# How to Perform Hex Conversion in GenLayer Contracts: Address API Guide

> Learn how to perform hex conversion in GenLayer contracts using the Address API. Easily convert between raw bytes and EIP-55 checksummed hex strings with the Address constructor and as_hex property.

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

---

**GenLayer contracts use the `Address` type to handle wallet addresses, providing the `Address(hex_str)` constructor and `addr.as_hex` property to convert between raw bytes and EIP-55-checksummed hex strings.**

The **GenLayer Project Boilerplate** demonstrates the standard patterns for hex conversion when working with contract addresses. Understanding these patterns is essential for passing addresses between contracts, returning values to UIs, and writing reliable tests.

## Understanding the Address Type

GenLayer's native `Address` type stores wallet addresses internally as 20-byte values. The type is defined in the platform's Python runtime and exposed through the `genlayer` package.

### Constructor Options

You can create an `Address` instance from two input formats:

| Input | Constructor Call | Result |
|-------|-----------------|--------|
| Hex string | `Address("0x1234...")` | Parsed and validated `Address` |
| Raw bytes | `Address(b'\x12\x34...')` | 20-byte `Address` instance |

In [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py), the `get_points` method accepts an `Address` parameter directly. The implementation wraps incoming values with `Address(player_address)` to ensure proper typing:

```python
@gl.public.view
def get_points(self, player_address: Address) -> u256:
    # Access stored points using the Address instance directly

    return self.points.get(Address(player_address), 0)

```

This pattern handles both hex string and raw byte inputs gracefully.

## Converting Address to Hex String

The **`addr.as_hex`** property returns the EIP-55-checksummed hexadecimal representation with the `0x` prefix. This is the correct method for producing human-readable addresses.

In [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) at lines 111-115, the contract returns a dictionary with hex keys for UI consumption:

```python
@gl.public.view
def get_bets(self) -> dict:
    # Return a mapping where keys are hex strings for UI consumption

    return {k.as_hex: v for k, v in self.bets.items()}

```

### Critical Gotcha: Avoid `str(addr)`

The `str()` built-in does **not** produce a hex string. As documented in [`tests/direct/test_patterns.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_patterns.py) at lines 189-195, `str(addr)` yields the raw bytes representation (e.g., `b'\x12\xab...'`), which is not valid for address comparison or display.

To obtain hex output, always use:

- **`addr.as_hex`** — checksummed hex string with `0x` prefix
- **`"0x" + addr.hex()`** — manual construction (equivalent to above)

## Converting Address to Raw Bytes

For low-level operations, extract the underlying 20-byte value using either:

- **`bytes(addr)`** — Python's `bytes()` constructor
- **`addr.bytes`** — direct property access

These yield identical results suitable for cryptographic operations or storage in byte-oriented data structures.

## Helper Utilities for Testing

The test suite in [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) provides a robust `to_hex` helper function used across direct-mode tests:

```python
def to_hex(addr_bytes):
    """Convert address bytes to a checksummed hex string matching contract output."""
    if hasattr(addr_bytes, "as_hex"):
        return addr_bytes.as_hex        # Already an Address object

    return Address(addr_bytes).as_hex   # Raw bytes → Address → hex

```

This helper normalizes both `Address` objects and raw bytes to consistent hex strings. Tests in [`tests/direct/test_views.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_views.py) at lines 5-6 demonstrate its usage for assertions against contract-returned values:

```python

# Example test assertion pattern

expected_hex = to_hex(direct_alice)
assert contract_result == expected_hex

```

## Complete Working Example

Here's a full contract demonstrating input and output hex conversion patterns:

```python
import genlayer as gl
from genlayer.types import Address, u256, TreeMap

class TokenTracker(gl.Contract):
    balances: TreeMap[Address, u256]
    
    @gl.public.write
    def register(self, user_hex: str) -> None:
        """Accept hex string, store as Address."""
        addr = Address(user_hex)
        self.balances[addr] = 0
    
    @gl.public.view
    def get_balance_hex(self, user_hex: str) -> u256:
        """Accept hex string, return balance."""
        addr = Address(user_hex)
        return self.balances.get(addr, 0)
    
    @gl.public.view
    def list_holders(self) -> list[str]:
        """Return all holder addresses as hex strings."""
        return [addr.as_hex for addr in self.balances.keys()]

```

## Quick Reference Table

| Operation | Method | Returns |
|-----------|--------|---------|
| Hex string → Address | `Address("0x...")` | `Address` instance |
| Raw bytes → Address | `Address(b'...')` | `Address` instance |
| Address → hex string | `addr.as_hex` | `"0x..."` string |
| Address → raw bytes | `bytes(addr)` or `addr.bytes` | 20-byte `bytes` |
| Address → `str` (avoid) | `str(addr)` | `b'...'` representation |

## Summary

- **Use `Address(hex_str)`** to convert incoming hex strings to the native type.
- **Use `addr.as_hex`** to output checksummed hex strings for UIs and APIs.
- **Never use `str(addr)`** for hex conversion—it returns raw bytes representation.
- **Reference [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py)** for the `to_hex` helper pattern in your own test suites.
- **Follow [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py)** for production-ready address handling patterns.

## Frequently Asked Questions

### How do I convert a user's wallet address from a frontend string to a GenLayer Address?

Use the `Address` constructor directly: `addr = Address("0x742d35Cc...")`. The constructor accepts any valid EIP-55-checksummed hex string and returns a properly typed `Address` instance ready for storage or contract calls.

### Why does `str(addr)` not return a hex string in GenLayer contracts?

GenLayer's `Address` type inherits Python's default object string representation, which displays the underlying raw bytes as a `bytes` literal (e.g., `b'\x12\xab...'`). This design prevents accidental hex conversion without explicit checksum validation. Always use `addr.as_hex` for intentional hex output.

### How can I test that my contract returns the correct address?

Use the `to_hex` helper from [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) or replicate its pattern: check if the value has `.as_hex`, otherwise wrap with `Address()` first. This normalizes both `Address` objects and raw bytes to comparable hex strings in your assertions.

### Does GenLayer support lowercase or non-checksummed hex strings?

The `Address` constructor accepts standard hex strings and normalizes them to EIP-55 checksum format. Outputs via `addr.as_hex` are always properly checksummed, ensuring consistency across contract interactions and reducing the risk of address confusion errors.