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

Entities

An entity is the core organizational unit in Cambrian: a named unit of persistent state plus the routes that read and update it. Every Cambrian program contains at least one entity. Backends differ in how an entity is hosted (on-chain instance, Lean world model, in-process library), but the .cam shape is the same.

Declaration

An entity is declared with the entity keyword, a name, and a brace-delimited body (names are ordinary identifiers; PascalCase is conventional, not required):

entity Token {
    // type aliases, records, enums, constants, macros
    // routes { ... }
    // member declarations (same level as routes)
}

What an Entity Contains

An entity body can include any of the following, in any order:

ElementPurposeRequired
Type aliasesNamed synonyms for typesNo
RecordsProduct types (structs)No
EnumsSum types (tagged unions)No
ConstantsCompile-time fixed valuesNo
MacrosState-aware helper expressionsNo
routes { }Named entry pointsYes
MembersState variables with transformsYes

A minimal entity needs at least one route and typically at least one member:

entity Counter {
    routes {
        increment() => []
    }

    m_count: u64 {
        in increment() => m_count + 1
    }
}

What an entity is at runtime

Across targets, an entity is an independent unit that:

  • Owns its own persistent state (member declarations in the entity body).
  • Exposes behaviour through its routes.
  • Can send messages to other entities with ~>.

On EVM it is typically deployed at an address; on Lean it appears as state and route transitions in a world model.

Entity-Level Definitions

Type Aliases

entity Token {
    type Amount = U256;
    type AccountId = address;
}

Records

entity Marketplace {
    record Listing {
        seller: address,
        price: U256,
        active: bool
    }
}

Enums

entity Governance {
    enum ProposalStatus {
        Pending,
        Approved,
        Rejected
    }
}

Constants

entity Vault {
    const MIN_DEPOSIT: U256 = 1_000_000;
    const MAX_SIGNERS: u64 = 10;
}

Macros

entity Wallet {
    macro is_owner() -> bool = {
        msg::sender == m_owner
    }

    macro require_funded(amount: U256) -> bool = {
        m_balance >= amount
    }
}

Multiple Entities

A .cam file can define multiple entities. This is the usual approach when several stateful units communicate:

entity Broker {
    routes {
        place_order(item_id: u64, quantity: u64) => [
            Shop::fulfill(item_id, quantity) ~> m_shop
        ]
    }

    m_shop: address {
        in init(shop) => shop
    }
}

entity Shop {
    routes {
        fulfill(item_id: u64, quantity: u64) => [
            Ledger::record_sale(item_id, quantity, msg::sender) ~> m_ledger
        ]
    }

    m_ledger: address {
        in init(_, ledger) => ledger
    }
}

entity Ledger {
    routes {
        record_sale(item_id: u64, quantity: u64, buyer: address) => []
    }

    m_sales: Vec<(u64, u64, address)> {
        in record_sale(item_id, quantity, buyer) =>
            m_sales.push((item_id, quantity, buyer))
    }
}

When entities are defined in the same file, the compiler can verify that messages sent between them match the declared routes.

Entity vs. Pure Functions

Code that does not need state access should be placed in pure functions outside any entity. Pure functions are available to all entities in the same file:

pure fn compute_fee(amount: U256, bps: u64) -> U256 {
    amount * (bps as U256) / 10000
}

entity Token {
    routes {
        transfer(to: address, amount: U256) => [
            let fee = compute_fee(amount, 30);
            // ...
        ]
    }
    // ...
}

See Pure Functions for details.