Multi-Entity Messaging
A minimal two-entity system with deterministic CREATE2 addresses,
deploy, typed sends, and from Entity(args) authentication. Source:
contracts/det_guardian.cam, contracts/det_locker.cam, and
contracts/det_messaging.yaml.
Project
name: det-messaging
target: evm
output_dir: build/
sources:
- det_guardian.cam
- det_locker.cam
cambrian-transpiler --project contracts/det_messaging.yaml
Both entities declare identity m_id: u64, so
Guardian.address(id) and Locker.address(id) are stable CREATE2
predictions. The auto-generated CambrianFactory deploys them.
Architecture
Guardian(id) Locker(id)
+------------------+ +------------------+
| spawnLocker(id) | --deploy--> | (new instance) |
| ping(locker_id) | ----~>----> | acceptPing() |
| | | from Guardian(m_id)
+------------------+ +------------------+
Matching identity values: a locker with m_id = 7 only accepts
acceptPing from Guardian(7).
Guardian
entity Guardian {
identity m_id: u64
routes {
constructor() => []
ping(locker_id: u64) => [
acceptPing() ~> Locker.address(locker_id)
]
spawnLocker(locker_id: u64) => [
deploy Locker(locker_id)
]
getPings() -> u64 => [
return(m_pings_sent)
]
}
m_pings_sent: u64 {
in constructor() => 0
in ping(_) => m_pings_sent + 1
}
}
deploy Locker(locker_id)— factory CREATE2; address equalsLocker.address(locker_id).acceptPing() ~> Locker.address(…)— typed send to the predicted address (no stored handle required).
Locker
entity Locker {
identity m_id: u64
routes {
constructor() => []
acceptPing()
from Guardian(m_id)
=> []
getPingCount() -> u64 => [
return(m_ping_count)
]
}
m_ping_count: u64 {
in constructor() => 0
in acceptPing() => m_ping_count + 1
}
}
from Guardian(m_id) lowers to
require(msg.sender == CREATE2(Guardian, m_id)) on EVM —
sender type and identity in one clause.
Patterns
| Pattern | Example |
|---|---|
| Shared identity key | Same m_id on both entities |
| Predict without storing | Locker.address(locker_id) |
| Child deploy | deploy Locker(locker_id) |
| Authenticate peer | from Guardian(m_id) |
Larger systems use the same idioms: Governor holds
Address<ERC20Votes> / Address<TimelockController> and Uniswap’s
factory does deploy UniswapV2Pair(t0, t1) then records
UniswapV2Pair.address(t0, t1). See Governor and
Uniswap V2.
Background: Identity Members, Deterministic Addresses, Multi-File Projects.