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
strparameters and convert internally withAddress(), 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_addressdirectly — This property already returns anAddressinstance; no conversion needed. -
Return
as_hexfor external callers — ABIs and UIs expect string addresses, so convert before returning from@gl.public.viewmethods. -
Never store raw bytes in maps — Always wrap with
Addressto ensure consistent hashing and equality semantics. -
Test with
create_address()but wrap the result — The testing helper returns bytes; explicitAddress()construction is required.
Summary
-
Construction: Create
Addressobjects from hex strings ("0x...") or 20-byte raw bytes usingAddress(value). -
Storage: Use
TreeMap[Address, T]for per-user state with automatic hashing and equality. -
Conversion: Access
.as_hexfor canonical string representation suitable for APIs and display. -
Testing: Wrap
create_address()output withAddress(); the helper returns raw bytes, not an instance. -
Pattern: Accept strings in public methods, convert internally, use
Addressinternally, returnas_hexexternally.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →