How to Work with Address Types in GenLayer Contracts: A Complete Guide

The Address class in GenLayer represents 20-byte blockchain addresses that can be created from hex strings or raw bytes and used directly as keys in TreeMap collections.

GenLayer smart contracts handle address types through the Address class provided by the genlayer package. This specialized type enables type-safe storage of user identities, supports efficient map-based lookups, and integrates seamlessly with Python's built-in data structures through custom equality and hashing implementations.

Creating Address Instances

The Address constructor accepts two input formats according to the genlayer implementation.

From Hex Strings

Pass a canonical "0x..." string to create an Address:

from genlayer import Address

addr = Address("0x742d35cc6a5a4b4f5b6e7f8a9b0c1d2e3f4a5b6c")
print(addr.as_hex)  # 0x742d35cc6a5a4b4f5b6e7f8a9b0c1d2e3f4a5b6c

This pattern appears in [contracts/football_bets.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py#L119) where the get_player_points method converts a string argument to an Address before map lookup.

From Raw Bytes

Pass a 20-byte bytes object for low-level construction:

raw_bytes = b'\x74\x2d\x35\xcc...'  # 20 bytes exactly

addr = Address(raw_bytes)

The test suite in [tests/direct/test_patterns.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_patterns.py#L189) demonstrates this pattern and notes a critical distinction: create_address() returns raw bytes, not an Address object, requiring explicit wrapping.

from genlayer import create_address, Address

raw = create_address()      # returns bytes, not Address

wrapped = Address(raw)      # convert to Address instance

Using Address as Map Keys

The primary use case for Address in GenLayer contracts is as a key type for TreeMap collections, enabling per-user state storage.

Declaring Address-Keyed Maps

In [contracts/football_bets.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py#L23), address-keyed maps are declared as:

from genlayer import *

class FootballBets(gl.Contract):
    bets: TreeMap[Address, List[Bet]]      # stores bets per player

    points: TreeMap[Address, u256]          # stores points per player

The Address type parameter ensures type safety and enables the TreeMap to use efficient hashing for O(log n) lookups.

Reading and Writing Address-Keyed Data

Access patterns support both existing Address instances and on-the-fly construction:

@gl.public.write
def place_bet(self, match_id: u256, team: str, amount: u256):
    sender = gl.message.sender_address      # already an Address instance

    self.bets.get_or_insert_default(sender, List())

@gl.public.view
def get_points(self, player: str) -> u256:
    # Construct Address from string parameter for lookup

    addr = Address(player)
    return self.points.get(addr, u256(0))

Complete Contract Example

This implementation demonstrates production patterns from the genlayer-project-boilerplate repository:

from genlayer import *

class TokenLedger(gl.Contract):
    balances: TreeMap[Address, u256]
    allowances: TreeMap[Address, TreeMap[Address, u256]]

    def __init__(self):
        self.balances = TreeMap()
        self.allowances = TreeMap()

    @gl.public.write
    def transfer(self, to: str, amount: u256) -> bool:
        sender = gl.message.sender_address
        recipient = Address(to)
        
        sender_bal = self.balances.get(sender, u256(0))
        assert sender_bal >= amount, "Insufficient balance"
        
        self.balances[sender] = sender_bal - amount
        self.balances[recipient] = self.balances.get(recipient, u256(0)) + amount
        return True

    @gl.public.view
    def balance_of(self, account: str) -> u256:
        return self.balances.get(Address(account), u256(0))

    @gl.public.view
    def get_all_balances(self) -> dict:
        # Return human-readable addresses for external consumption

        return {addr.as_hex: int(bal) for addr, bal in self.balances.items()}

Testing Address Operations

The repository's test suite in [tests/direct/test_patterns.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_patterns.py) validates Address behavior:

def test_address_construction(direct_vm):
    # From hex string

    addr_str = Address("0x1234567890123456789012345678901234567890")
    assert addr_str.as_hex == "0x1234567890123456789012345678901234567890"
    
    # From raw bytes

    raw = bytes.fromhex("1234567890123456789012345678901234567890")
    addr_bytes = Address(raw)
    assert addr_bytes.as_hex == addr_str.as_hex
    
    # Equality and hashing work correctly

    assert addr_str == addr_bytes
    assert len({addr_str, addr_bytes}) == 1  # same set entry

A helper in [contracts/PatternTest.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/PatternTest.py#L85) shows the recommended signature pattern:

@gl.public.view
def process_address(self, input_addr: str) -> str:
    """Accept string, validate/convert to Address, return canonical form."""
    addr = Address(input_addr)
    return addr.as_hex

Address Properties and Methods

Property/Method Purpose Example Output
as_hex Canonical string representation "0x742d35cc6a5a4b4f5b6e7f8a9b0c1d2e3f4a5b6c"
__eq__ Value equality for comparisons addr1 == addr2 → bool
__hash__ Hash support for dict, set, TreeMap hash(addr) → int

Best Practices for Address Types in GenLayer

  • Accept strings at API boundaries — Contract methods should accept str parameters and convert internally with Address(), matching the pattern in [contracts/football_bets.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py#L119).

  • Use gl.message.sender_address directly — This property already returns an Address instance; no conversion needed.

  • Return as_hex for external callers — ABIs and UIs expect string addresses, so convert before returning from @gl.public.view methods.

  • Never store raw bytes in maps — Always wrap with Address to ensure consistent hashing and equality semantics.

  • Test with create_address() but wrap the result — The testing helper returns bytes; explicit Address() construction is required.

Summary

  • Construction: Create Address objects from hex strings ("0x...") or 20-byte raw bytes using Address(value).

  • Storage: Use TreeMap[Address, T] for per-user state with automatic hashing and equality.

  • Conversion: Access .as_hex for canonical string representation suitable for APIs and display.

  • Testing: Wrap create_address() output with Address(); the helper returns raw bytes, not an instance.

  • Pattern: Accept strings in public methods, convert internally, use Address internally, return as_hex externally.

Frequently Asked Questions

How do I convert a string address to an Address object in GenLayer?

Pass the hex string directly to the Address constructor: addr = Address("0x742d35cc..."). This pattern is used in [contracts/football_bets.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) for method parameters. The constructor validates the format and creates a properly typed instance.

Can I use Address objects as dictionary keys in GenLayer contracts?

Yes. The Address class implements __hash__ and __eq__, making it compatible with Python dict, set, and the TreeMap type used in contract storage. This enables patterns like TreeMap[Address, u256] for user balances.

What is the difference between create_address() and Address in GenLayer tests?

create_address() returns a raw 20-byte bytes object, while Address is a class that wraps such bytes. In tests, you must wrap: addr = Address(create_address()). The [tests/direct/test_patterns.py](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_patterns.py) file demonstrates this distinction explicitly.

How do I get the caller's address in a GenLayer contract method?

Use gl.message.sender_address, which returns an Address instance directly. No conversion is needed. Store this value directly in TreeMap keys or compare against other Address objects.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →