Phased Routes
Most routes are unphased: every member transform runs as one atomic
step, then every effect (~>, deploy, emit, …) runs afterward. That
covers the common case and matches the checks–effects–interactions (CEI)
discipline on EVM.
Reach for named phases only when you need something the unphased model cannot express:
varcapture — a synchronous return from an external call that later transforms or guards depend on.- Interleaving — an effect must run between state updates (or between other effects) rather than after all of them.
Otherwise keep the route unphased. Fewer phases are easier to read and audit.
Syntax
A phased route body is a sequence of tag: [ … ] blocks. Phases run in
declaration order:
castVote(proposal_id: U256, support: u8) => [
read: [
var weight = getVotes(msg::sender) ~> m_token;
]
tally where weight > 0 : throw 100 : [
]
]
Member transforms that participate in a phased route must name the same phase tag:
m_for: HashMap<U256, U256> {
in castVote(proposal_id, support) =>
tally: if support == 1 {
m_for.update(proposal_id, m_for[proposal_id] + weight)
} else {
m_for
}
}
Prefer unphased on EVM
For an unphased route the EVM backend flushes all member SSTOREs before any external call. You get CEI without writing phases:
transfer(to: address, amount: U256)
where amount > 0 : throw 1
=> [
emit Transfer(msg::sender, to, amount);
]
Balances update in the member transforms; emit (and any ~>) runs after
storage is consistent. Prefer this shape whenever you do not need a
captured return value mid-route.
When phases are the right tool
| Need | Pattern |
|---|---|
| Read a remote view, then gate local writes | fetch: with var x = … ~> dest, then act where … |
| Optimistic transfer, then callback, then check | Three phases (see Uniswap V2 swap) |
| Effect between two groups of transforms | Explicit empty phases (tag: []) so transforms can attach |
A phase that only applies transforms still appears in the route body as
tag: []. The route body is the source of truth for phase names and order;
transforms may only reference tags declared there.
Declaration rules
- Phases and their order live exclusively in the route body (
tag: […]). - In a phased route, every participating transform must carry a phase tag.
- In an unphased route, transforms must not carry phase tags.
- Untagged actions are illegal inside a phased body.
| Situation | Error |
|---|---|
| Transform tag missing from the route | unknown phase |
| Phased route, transform without a tag | transform must specify a phase |
| Unphased route, transform with a tag | route has no phases |
| Untagged action in a phased body | untagged action |
Cross-phase where / var rules are covered in
Execution Model.