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:
| Element | Purpose | Required |
|---|---|---|
| Type aliases | Named synonyms for types | No |
| Records | Product types (structs) | No |
| Enums | Sum types (tagged unions) | No |
| Constants | Compile-time fixed values | No |
| Macros | State-aware helper expressions | No |
routes { } | Named entry points | Yes |
| Members | State variables with transforms | Yes |
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.