# How to Handle Integer Types in GenLayer Contracts: A Complete Guide to u256 and i256

> Master integer types in GenLayer contracts. Learn to use u256 and i256 for state storage and ensure VM compliance with expert tips and guidance.

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

---

**GenLayer contracts require the `u256` and `i256` wrapper classes for all integer state storage, forcing developers to convert to native Python `int` for arithmetic operations and re-wrap results before assignment to maintain deterministic VM compliance.**

The `genlayerlabs/genlayer-project-boilerplate` repository demonstrates how GenLayer's deterministic virtual machine enforces strict storage typing for numeric values. Unlike standard Python, you cannot store raw integers directly in contract state—instead, you must handle integer types in GenLayer contracts using specialized 256-bit wrapper classes that ensure consistent, deterministic behavior across all execution nodes.

## Understanding u256 and i256 Storage Types

GenLayer provides two fixed-size, 256-bit numeric classes for integer arithmetic. Both types are **immutable wrappers** around Python’s native `int` and are the only permitted primitive storage types for numeric values in contract state.

**`u256`** (unsigned 256-bit integer)
- Values are always non-negative and wrap on overflow modulo 2²⁵⁶
- Use for counters, token balances, points, or any value that cannot be negative
- Instantiation: `u256(0)` or `u256(100)`

**`i256`** (signed 256-bit integer)
- Supports negative values and wraps on overflow modulo 2²⁵⁶
- Use for scores that may be negative, offsets, or calculations where subtraction can produce negative results
- Instantiation: `i256(-5)` or `i256(0)`

According to the [`CLAUDE.md`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/CLAUDE.md) file in the official boilerplate, these types are listed under *Storage types* and represent the complete set of integer primitives available to contract developers.

## Instantiating Integer Wrappers

You must explicitly construct wrapper instances using class constructors. Direct assignment of raw Python `int` values to state variables will be rejected by the GenLayer linter.

```python
from genlayer import gl

class Counter(gl.Contract):
    count: u256
    
    def __init__(self):
        # Correct: explicit wrapper construction

        self.count = u256(0)
        
        # Incorrect: raw int assignment fails linting

        # self.count = 0

```

The wrappers validate constraints at construction time—`u256` rejects negative inputs while `i256` accepts the full signed range.

## Performing Arithmetic Operations

Because `u256` and `i256` are immutable, you cannot perform in-place arithmetic. The required pattern is **unwrap-calculate-wrap**: convert the wrapper to a native `int`, perform the calculation, then construct a new wrapper with the result.

In [`contracts/PatternTest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/PatternTest.py), the canonical increment pattern appears in the `increment` method:

```python
@gl.public.write
def increment(self) -> u256:
    """Increase count by one using the unwrap-calculate-wrap pattern."""
    self.count = u256(int(self.count) + 1)
    return self.count

```

The same pattern applies to signed arithmetic:

```python

# Decrementing an i256 value

self.balance = i256(int(self.balance) - amount)

```

When returning values to callers, the VM automatically converts wrappers back to Python `int`. View methods should annotate return types as `int` (or keep the wrapper if preserving type information), as shown in the `get_count` method:

```python
@gl.public.view
def get_count(self) -> int:
    """Expose the counter as a plain Python int."""
    return int(self.count)

```

## Storing Integers in Contract State

The linter only accepts `u256` and `i256` inside built-in storage containers (`TreeMap`, `DynArray`, `Array`). You cannot store raw Python integers in contract state—attempting to do so produces validation errors during compilation.

The [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) file demonstrates a production pattern for user-scored points:

```python
class FootballBets(gl.Contract):
    points: TreeMap[Address, u256]

    @gl.public.write
    def award_point(self, player: Address) -> None:
        """Give a player one point."""
        # Initialize to zero if the address is unseen

        if player not in self.points:
            self.points[player] = u256(0)
        
        # Increment safely using conversion pattern

        current = int(self.points[player])
        self.points[player] = u256(current + 1)

```

This pattern ensures that all state storage remains deterministic and compatible with GenLayer's consensus mechanism.

## Handling Overflow and Determinism

Both `u256` and `i256` implement **wrapping arithmetic** modulo 2²⁵⁶. This behavior mirrors Solidity’s `uint256` and `int256` semantics, ensuring deterministic execution across all validator nodes regardless of underlying hardware.

When an operation exceeds the maximum representable value, it wraps around to the minimum:

```python

# u256 overflow example

max_val = u256(2**256 - 1)
result = u256(int(max_val) + 1)  # Wraps to u256(0)

# i256 overflow example

min_val = i256(-2**255)
result = i256(int(min_val) - 1)  # Wraps to i256(2**255 - 1)

```

The [`tests/direct/test_patterns.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_patterns.py) file contains unit tests verifying correct round-trip conversion and default initialization behavior for these types, ensuring your contracts behave identically in direct-mode testing and on-chain execution.

## Summary

- **Use wrappers exclusively**: Store only `u256` and `i256` instances in contract state, never raw Python `int` values.
- **Follow the conversion pattern**: Unwrap with `int(wrapper)`, calculate, then re-wrap with `u256(result)` or `i256(result)` before assignment.
- **Respect container constraints**: Place integer wrappers only inside approved storage containers like `TreeMap` or `DynArray`.
- **Expect wrapping overflow**: Arithmetic operations wrap modulo 2²⁵⁶, matching Ethereum Virtual Machine behavior for consistent cross-node execution.
- **Annotate view returns**: Return `int` from view methods for automatic conversion, or maintain wrappers if type preservation is required.

## Frequently Asked Questions

### Can I store raw Python int values in GenLayer contract state?

No. The GenLayer linter explicitly rejects raw Python integers in state declarations. You must declare state variables using `u256` for unsigned values or `i256` for signed values, then wrap all assignments using the class constructors (e.g., `self.value = u256(100)`). Attempting to store `int` types directly results in compilation failures.

### How do I handle negative numbers in GenLayer contracts?

Use the `i256` type for any value that may become negative. Unlike `u256`, which only accepts non-negative inputs, `i256` supports the full signed 256-bit integer range from -2²⁵⁵ to 2²⁵⁵-1. Construct negative values with `i256(-5)`, and always apply the unwrap-calculate-wrap pattern when performing arithmetic that might produce negative intermediate results.

### What happens when a u256 or i256 value overflows?

Both types implement wrapping overflow semantics identical to Solidity. When a value exceeds 2²⁵⁶-1 (for unsigned) or falls below -2²⁵⁵ (for signed), it wraps around modulo 2²⁵⁶. This deterministic behavior ensures that all nodes in the GenLayer network compute identical results regardless of hardware architecture, preventing consensus failures.

### Should view methods return u256 or int types?

View methods should typically annotate return types as `int`. The GenLayer VM automatically converts `u256` and `i256` wrappers to native Python integers when returning values to callers, making `int` the natural choice for external interfaces. However, you may return the wrapper type directly if you specifically want to preserve the type information in the contract's public API.