# How to Define Public View Methods in GenLayer Smart Contracts

> Learn to define public view methods in GenLayer contracts. Use the @gl.public.view decorator on Python functions to read contract state efficiently. Explore the GenLayer project boilerplate.

- Repository: [GenLayer Labs/genlayer-project-boilerplate](https://github.com/genlayerlabs/genlayer-project-boilerplate)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Define public view methods in GenLayer by importing the SDK and applying the `@gl.public.view` decorator to Python functions that read contract state without modifying it.**

GenLayer smart contracts are written in Python and deployed to a blockchain environment that requires explicit decorators to expose callable methods. The `genlayer-project-boilerplate` repository demonstrates how to implement read-only query functions that access persistent storage while maintaining the immutability guarantees required by the virtual machine.

## Understanding Public View Methods in GenLayer

A **public view method** is a read-only entry point that queries contract state without triggering state transitions. Unlike write methods that modify storage and consume gas for computation, view methods are pure operations that return serialized data to callers without requiring transaction fees.

In the GenLayer SDK, these methods serve as the primary interface for frontend applications, external contracts, and off-chain scripts to inspect contract data. The GenLayer VM enforces purity at runtime—any attempt to modify state or invoke non-deterministic operations inside a view method results in an execution error.

## The @gl.public.view Decorator Syntax

To declare a view method, apply the `@gl.public.view` decorator directly above the method definition. The decorator signals to the compiler and VM that the function should be exposed in the contract's ABI as a `view` function and allows read access to storage variables.

```python
from genlayer import *

class ExampleContract(gl.Contract):
    data: TreeMap[str, u256]

    @gl.public.view
    def read_data(self, key: str) -> u256:
        """Read from storage and return a value."""
        return self.data.get(key, 0)

```

**Key requirements for view methods:**

- **Import requirement**: Must import `from genlayer import *` to access the decorator and types
- **Return serialization**: Return values must be JSON-serializable (`dict`, `list`, `int`, `str`, or `bool`)
- **Storage access**: Can read from storage types like `TreeMap`, `DynArray`, and persistent class attributes
- **Purity enforcement**: Cannot reassign storage variables or call `gl.nondet.*` functions

## Basic View Method Implementation

The simplest view methods return primitive values stored in contract attributes. These methods provide transparency into contract state without exposing the raw storage structure.

```python
from genlayer import *

class Counter(gl.Contract):
    value: u256

    def __init__(self):
        self.value = 0

    @gl.public.write
    def increment(self):
        self.value += 1

    @gl.public.view
    def get_value(self) -> u256:
        """Return the current counter value."""
        return self.value

```

In this example, `get_value` qualifies as a view method because it only reads `self.value` without modification. The method returns a `u256` integer that the GenLayer VM automatically serializes for the caller.

## Returning Complex Data Structures

When contracts store dataclasses or nested mappings, view methods must format data into serializable Python dictionaries. The GenLayer VM does not automatically serialize custom objects, so explicit conversion is required.

```python
from genlayer import *
from dataclasses import dataclass

@allow_storage
@dataclass
class Profile:
    name: str
    age: u256

class UserDirectory(gl.Contract):
    profiles: TreeMap[Address, Profile]

    def __init__(self):
        self.profiles = TreeMap()

    @gl.public.view
    def get_profile(self, addr: str) -> dict:
        """Return profile data as a serializable dictionary."""
        p = self.profiles.get(Address(addr), None)
        if p is None:
            return {}
        return {"name": p.name, "age": int(p.age)}

```

This pattern ensures that complex storage types are converted to JSON-compatible structures before returning to the caller.

## Querying Mappings with Address Keys

A common pattern in GenLayer contracts involves iterating over `TreeMap` structures that use `Address` types as keys. Since JSON object keys must be strings, view methods should convert `Address` objects to hex string representations using the `.as_hex` property.

```python
@gl.public.view
def get_bets(self) -> dict:
    """Expose all bets with address hex strings as keys."""
    return {k.as_hex: v for k, v in self.bets.items()}

```

This approach converts non-serializable `Address` keys into hex strings while preserving the mapping structure for external consumers.

## Real-World Example from FootballBets

The `genlayer-project-boilerplate` repository implements multiple view methods in **[`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py)** (lines 109-119) that demonstrate production-ready patterns for state querying.

According to the source code, the `FootballBets` contract defines three public view methods:

- **`get_bets`** – Returns the complete `TreeMap` of bets indexed by address
- **`get_points`** – Returns the points mapping for all participants  
- **`get_player_points`** – Returns the point total for a specific address as an integer

These methods use the `@gl.public.view` decorator and return dictionaries or integers derived from the contract's `TreeMap` storage without modifying `self.bets` or `self.points`. The implementation at lines 109-119 shows the exact syntax for exposing contract state while maintaining read-only guarantees.

```python

# From contracts/football_bets.py, lines 109-119

@gl.public.view
def get_bets(self) -> dict:
    return {k.as_hex: v for k, v in self.bets.items()}

@gl.public.view  
def get_points(self) -> dict:
    return {k.as_hex: v for k, v in self.points.items()}

@gl.public.view
def get_player_points(self, addr: str) -> u256:
    return self.points.get(Address(addr), 0)

```

## State Access Rules and Restrictions

The GenLayer VM enforces strict purity constraints on view methods during execution. Understanding these boundaries prevents runtime errors when deploying contracts.

**Allowed operations:**
- Reading from `TreeMap`, `DynArray`, and persistent class attributes
- Constructing and returning `dict`, `list`, `int`, `str`, or `bool` values
- Calling other view methods within the same contract

**Prohibited operations:**
- Assigning values to storage variables (e.g., `self.bets[addr] = value`)
- Invoking `gl.nondet.*` functions for non-deterministic outcomes
- Calling write methods or external contracts that modify state
- Performing I/O operations or accessing external APIs

Attempting any prohibited operation triggers a runtime validation error that reverts the view call.

## Summary

- **Use `@gl.public.view`** to mark Python methods as read-only entry points in GenLayer contracts from the `genlayer-project-boilerplate` repository.
- **Return JSON-serializable types** (`dict`, `list`, `int`, `str`) when exposing complex storage structures like `TreeMap` or dataclasses.
- **Reference storage directly** via `self.attribute` notation to read contract state without gas fees.
- **Avoid state mutations** and `gl.nondet.*` calls inside view methods, as the VM enforces purity at runtime.
- **Convert Address keys** to hex strings using `.as_hex` when returning mapping data to ensure JSON compatibility.

## Frequently Asked Questions

### What happens if I modify state inside a @gl.public.view method?

The GenLayer VM detects state modifications during execution and raises a runtime error that reverts the view call. View methods must remain pure—any assignment to storage variables like `self.data[key] = value` or calls to write methods will fail validation, preventing the method from completing successfully.

### Can view methods call other methods within the same contract?

View methods can safely call other methods decorated with `@gl.public.view`, but cannot invoke methods marked with `@gl.public.write` or any function that performs state modifications. The purity constraint applies recursively—any operation invoked by a view method must also adhere to read-only restrictions.

### How do I return nested Mapping data from a view method?

Iterate over the `TreeMap` or storage container and construct a Python `dict` with serializable keys and values. When using `Address` types as keys, convert them to hex strings using `address.as_hex` because JSON requires string keys. The `FootballBets` contract in [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) demonstrates this pattern by returning `{k.as_hex: v for k, v in self.bets.items()}`.

### Do view methods consume gas when called?

No, view methods do not consume gas because they do not modify blockchain state or require transaction processing. External callers can query these methods through the contract ABI without submitting a transaction, making them ideal for frontend applications that need to display contract data efficiently.