Init Routes
Init routes run once when an entity instance is created. They supply the arguments that member transforms use for initial state. An entity may have at most one init route.
Syntax
Init routes are declared with the init keyword:
#[factory_only]
init setup(owner: address, name: String) => [
// initialization actions (optional)
]
A route literally named constructor (without the init keyword) is also
treated as the init route.
On EVM, entities are always deployed via CREATE2 through the generated
CambrianFactory, so the init route carries the #[factory_only] attribute:
the generated initialize() accepts only calls from the factory. Leaving it
out is an error in EVM project builds and a warning in single-file EVM
builds.
Purpose
The init route is invoked exactly once at instance creation. Its primary role is to provide initial values for members through their transforms:
entity Token {
routes {
#[factory_only]
init setup(
name: String,
symbol: String,
decimals: u8,
owner: address
) => []
}
m_name: String {
in setup(name, _, _, _) => name
}
m_symbol: String {
in setup(_, symbol, _, _) => symbol
}
m_decimals: u8 {
in setup(_, _, decimals, _) => decimals
}
m_owner: address {
in setup(_, _, _, owner) => owner
}
m_total_supply: U256 {
in setup(_, _, _, _) => 0
}
}
Parameter Passing
Init route parameters are passed at deployment time. The deploying entity (or off-chain client) provides these values as part of the deployment transaction — identity values first, then the init arguments:
// In another entity's route body, deploying the Token above:
deploy Token("MyToken", "MTK", 18, msg::sender)
Do not read msg::sender inside the init route itself: on EVM the caller of
initialize() is the factory, not the account that requested the deployment.
Pass the owner (or mint recipient) as an explicit parameter, as owner is
above. Reading msg::sender there is diagnosed like a missing
#[factory_only]: an error in EVM project builds, a warning in single-file
EVM builds.
Init Actions
While the init route’s action list is often empty ([]), it can contain
actions that should execute at deployment:
#[factory_only]
init setup(owner: address, treasury: address) => [
emit Initialized(owner, treasury);
]
Member Initialization
Every member that needs an initial value must include a transform referencing the init route. Members without an init transform start at their type’s default value (typically zero or empty).
The parameter pattern in a member transform for an init route uses positional
matching with _ for unused parameters:
m_owner: address {
in setup(_, _, _, owner) => owner
}
m_paused: bool {
in setup(_, _, _, _) => false
}
Complete Example
A vault entity with an init route:
entity Vault {
const MIN_DEPOSIT: U256 = 100
routes {
#[factory_only]
init create(
owner: address,
guardian: address,
daily_limit: U256
) => []
deposit()
where msg::value >= MIN_DEPOSIT : throw 100
=> []
withdraw(amount: U256)
where msg::sender == m_owner : throw 101
&& m_balance >= amount : throw 102
=> [
~> msg::sender with { value: amount }
]
}
m_owner: address {
in create(owner, _, _) => owner
}
m_guardian: address {
in create(_, guardian, _) => guardian
}
m_daily_limit: U256 {
in create(_, _, daily_limit) => daily_limit
}
m_balance: U256 {
in create(_, _, _) => 0
in deposit() => m_balance + msg::value
in withdraw(amount) => m_balance - amount
}
}
Constraints
- An entity can have at most one init route.
- Init routes cannot be called after deployment — they execute exactly once.
- Init routes can include
whereclauses to validate constructor arguments. - On EVM, construction is split into a factory-guarded
initialize(); see Deterministic Addresses.