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

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:

  1. var capture — a synchronous return from an external call that later transforms or guards depend on.
  2. 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

NeedPattern
Read a remote view, then gate local writesfetch: with var x = … ~> dest, then act where …
Optimistic transfer, then callback, then checkThree phases (see Uniswap V2 swap)
Effect between two groups of transformsExplicit 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

  1. Phases and their order live exclusively in the route body (tag: […]).
  2. In a phased route, every participating transform must carry a phase tag.
  3. In an unphased route, transforms must not carry phase tags.
  4. Untagged actions are illegal inside a phased body.
SituationError
Transform tag missing from the routeunknown phase
Phased route, transform without a tagtransform must specify a phase
Unphased route, transform with a tagroute has no phases
Untagged action in a phased bodyuntagged action

Cross-phase where / var rules are covered in Execution Model.