Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Identity Members

Identity members are immutable fields fixed at deploy time. On EVM they participate in the CREATE2 address of the instance: same identity args ⇒ same address everywhere in the project.

Declaration

entity Locker {
    identity m_id: u64

    routes {
        #[factory_only]
        constructor() => []
        // …
    }
}

An entity may declare several identity members; all contribute to the address, in declaration order:

entity UniswapV2Pair {
    identity m_token0: address
    identity m_token1: address
}

Rules

PropertyOrdinary memberIdentity member
Mutable after deployYes (transforms)No
Default valueAllowedForbidden
TransformsAllowedForbidden
Address arityDoes not countCounts toward Entity.address(…)

Identity values are supplied as constructor / deploy arguments (together with any non-identity init parameters). They are never rewritten by routes.

Address arity

Entity.address(args…) takes exactly as many arguments as the entity has identity members — same count and types, in declaration order:

Locker.address(locker_id)                    // one identity
UniswapV2Pair.address(token0, token1)        // two identities

from Entity(args) on EVM uses the same arity (rule V33). Mismatched arity is a compile-time error (V32 for deploy, V33 for from).

Singletons

An entity with no identity members is a singleton: one predictable address per project, and Entity.address() takes no arguments:

pure fn min_addr(a: address, b: address) -> address {
    if a < b { a } else { b }
}

pure fn max_addr(a: address, b: address) -> address {
    if a < b { b } else { a }
}

entity UniswapV2Pair {
    identity m_token0: address
    identity m_token1: address

    routes {
        #[factory_only]
        constructor() => []
        sync() => []
    }

    m_syncs: u64 {
        in sync() => m_syncs + 1
    }
}

entity UniswapV2Factory {
    // no identity — singleton
    routes {
        #[factory_only]
        constructor(fee_to_setter: address) => []

        createPair(tokenA: address, tokenB: address)
            where tokenA != tokenB : throw 1
        => [
            deploy UniswapV2Pair(min_addr(tokenA, tokenB), max_addr(tokenA, tokenB))
        ]

        view pairFor(tokenA: address, tokenB: address) -> address => [
            return(UniswapV2Pair.address(min_addr(tokenA, tokenB), max_addr(tokenA, tokenB)))
        ]
    }

    m_fee_to_setter: address {
        in constructor(fee_to_setter) => fee_to_setter
    }
}

Elsewhere, the factory’s own address is UniswapV2Factory.address().

Governor and TimelockController in examples/governor/ are singletons the same way.

When to use identity

Use identity when distinct instances must be content-addressed — token ids, pair keys (token0, token1), shard indices. If the value can change after deployment or should not affect the address, use an ordinary member instead.

See Deterministic Addresses for CREATE2 / CambrianFactory details.