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

> Master GenLayer Address types. Learn to create and use 20-byte blockchain addresses as TreeMap keys in this comprehensive guide.

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

---

**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`:

```python
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)](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:

```python
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)](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.

```python
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)](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py#L23), address-keyed maps are declared as:

```python
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:

```python
@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:

```python
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)](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_patterns.py) validates `Address` behavior:

```python
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)](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/PatternTest.py#L85) shows the recommended signature pattern:

```python
@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)](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)](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)](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.