What Are the Allowed Storage Types in GenLayer Intelligent Contracts?

GenLayer intelligent contracts support five deterministic storage types: TreeMap, DynArray, Array, u256/i256, and @allow_storage-decorated custom classes.

These types ensure that smart contract state can be reproduced identically across all validator nodes in the GenLayer network. The genlayer-project-boilerplate repository documents these constraints in CLAUDE.md and enforces them through the GenVM linter, which blocks nondeterministic Python data structures like standard dict and list.

Core Storage Types for GenLayer Contracts

GenLayer's storage system replaces Python's native collections with deterministic alternatives. Each type is optimized for specific access patterns while maintaining the reproducibility required for consensus.

TreeMap: Deterministic Ordered Mapping

TreeMap[Key, Value] provides O(log n) lookups with guaranteed insertion order. Unlike Python's dict, this structure produces identical iteration order on every node.

Use TreeMap for any key-value data that persists across blocks:

from genlayer import *

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

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

    @gl.public.write
    def transfer(self, to: Address, amount: u256):
        sender = gl.message.sender_address
        self.balances[sender] -= amount
        self.balances[to] = self.balances.get(to, u256(0)) + amount

DynArray: Growable Deterministic Lists

DynArray[Element] supports dynamic sizing with predictable ordering. Use this when your collection needs to expand or contract during contract execution.

Typical applications include participant registries and pending transaction queues:

from genlayer import *

class Registry(gl.Contract):
    participants: DynArray[Address]

    def __init__(self):
        self.participants = DynArray()

    @gl.public.write
    def join(self):
        self.participants.append(gl.message.sender_address)

Array: Fixed-Size Collections

Array[Element] requires compile-time length knowledge. This constraint enables memory optimizations and guarantees stable indexing.

Best for leaderboards, round-robin slots, or other bounded collections:

from genlayer import *

class Leaderboard(gl.Contract):
    top_scores: Array[u256]  # Exactly 10 entries

    def __init__(self):
        self.top_scores = Array([u256(0)] * 10)

u256 and i256: Native Integer Types

These 256-bit integers replace Python's arbitrary-precision int for deterministic arithmetic. u256 handles unsigned values; i256 handles signed.

Always use these types for counters, balances, timestamps, and mathematical operations:

from genlayer import *

class Counter(gl.Contract):
    value: u256
    signed_delta: i256

    def __init__(self):
        self.value = u256(0)
        self.signed_delta = i256(-5)

Creating Custom Storage Types with @allow_storage

The @allow_storage decorator extends the type system to user-defined classes. Decorated classes must contain only allowed storage primitives internally.

This pattern appears in contracts/football_bets.py from the boilerplate repository:

from genlayer import *

@allow_storage
class Bet:
    amount: u256
    choice: str
    timestamp: u256

class FootballBets(gl.Contract):
    bets: TreeMap[Address, TreeMap[str, Bet]]
    points: TreeMap[Address, u256]

    def __init__(self):
        self.bets = TreeMap()
        self.points = TreeMap()

    @gl.public.write
    def place_bet(self, match_id: str, amount: u256, choice: str):
        user = gl.message.sender_address
        user_bets = self.bets.get(user, TreeMap())
        user_bets[match_id] = Bet(
            amount=amount,
            choice=choice,
            timestamp=gl.vm.now()
        )
        self.bets[user] = user_bets
        self.points[user] = self.points.get(user, u256(0)) + u256(1)

The nested TreeMap[str, Bet] structure enables efficient per-user, per-match bet tracking while maintaining full determinism.

Workarounds for Unsupported Patterns

Standard Python list cannot be stored directly in a TreeMap. The boilerplate demonstrates a JSON serialization pattern in contracts/PatternTest.py:

import json
from genlayer import *

class ListWrapper(gl.Contract):
    data: TreeMap[str, str]  # JSON-encoded lists

    def __init__(self):
        self.data = TreeMap()

    @gl.public.write
    def store_list(self, key: str, values: list[str]):
        self.data[key] = json.dumps(values)

    @gl.public.view
    def retrieve_list(self, key: str) -> list[str]:
        return json.loads(self.data.get(key, "[]"))

This approach trades some gas efficiency for flexibility when true nested collections are required.

Complete Storage Type Reference

Type Mutability Use Case Source Reference
TreeMap[K,V] Mutable Mappings with ordered iteration CLAUDE.md line 72
DynArray[T] Mutable Variable-length sequences CLAUDE.md line 72
Array[T] Fixed-size Compile-time bounded collections CLAUDE.md line 72
u256 / i256 Value types All numeric computation CLAUDE.md line 72
@allow_storage classes Depends on fields Domain-specific structs contracts/football_bets.py

Summary

  • Five storage types are permitted in GenLayer intelligent contracts: TreeMap, DynArray, Array, u256/i256, and @allow_storage classes.
  • Native Python containers (dict, list, set) are rejected by the GenVM linter to ensure deterministic execution.
  • Custom structs require the @allow_storage decorator and may only contain other allowed types.
  • Nested structures like TreeMap[Address, TreeMap[str, Bet]] are fully supported as shown in contracts/football_bets.py.
  • Serialization workarounds enable storing complex data when direct container nesting is insufficient.

Frequently Asked Questions

What happens if I use a Python list in a GenLayer contract?

The GenVM linter will reject the contract during compilation. Standard Python list produces nondeterministic memory layouts that could cause consensus failures across validator nodes. Use DynArray for dynamic collections or Array for fixed-size needs.

Can I nest multiple storage types together?

Yes. The FootballBets example in contracts/football_bets.py demonstrates TreeMap[Address, TreeMap[str, Bet]] with a custom @allow_storage class at the deepest level. Arbitrary nesting of allowed types is permitted.

How do I store a variable-length list inside a TreeMap?

GenLayer does not support direct TreeMap[K, list[V]] storage. Serialize the list to JSON (or another deterministic format) and store as TreeMap[K, str] instead, as shown in contracts/PatternTest.py. Deserialize when reading.

Is there a size limit for DynArray or TreeMap?

The analysis does not specify explicit size limits. Practical limits are determined by gas costs for operations and the deterministic execution timeout in GenVM. For unbounded growth, consider pagination patterns or off-chain data with on-chain hashes.

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 →