Execution is what turns a passed proposal into a state change. It is the tail of a two-phase lifecycle: a ranked ballot decides what the caper should do, and the market then gets a window to veto it before it happens. Not every passed proposal has that tail: a ballot won by a DEBATE option ends at the tally, and nothing on this page applies to it. Execution proper is the last two permissionless calls of that sequence – resolve, then execute – made by anyone at all, with nothing about either depending on who makes it.
Resolve settles the market's verdict on a proposal the ballot already passed. trigger_proposal is what opens that window: it asserts the legislative pass, collects the execution fee the ballot's second question assigned, locks the caper's trailing time-weighted average price as the baseline, and stamps a window end one market window out, aligned up to the state tier's minute grid. Once that window closes, resolve_proposal reads the price-time integral at that recorded window end – not at the moment of the crank – and passes iff the window's average is at or above the baseline.
Integrating rather than sampling is the load-bearing choice. A spot read at the crank could be bought for one block; a window average has to be displaced and then held for a real share of the window, so an atomic buy → resolve → sell round-trip buys no verdict. Anchoring at the stored window end rather than at now also makes a late crank read the same number a prompt one does.
The integral is exact, not sampled. The state tier keeps a running price-time accumulator: at the tail of each of the three operations that change circulation – a buy, a sell, a migration – it adds the price in force multiplied by the seconds it held, and it writes one checkpoint of that total at the start of every minute that sees a trade (contracts/core/src/caper_dao.rs). Window ends and baseline starts sit on that minute grid, so the integral at each is read exactly – from state if nothing has traded since, otherwise back from the first checkpoint after it – and a burst of trades inside one minute writes one checkpoint rather than one per trade. The checkpoint ring is finite: CHECKPOINT_CAP entries, eight days of traded minutes, since a minute with no trade writes nothing, and the trailing baseline's length is pinned under it when the logic is compiled. Only a resolve cranked more than that many traded minutes after its window end moves the anchor, to the oldest retained checkpoint, and extends the window to it – deterministically, where Uniswap v3 reverts instead (price oracles in governance). Inside that bound the verdict is fixed at the window end however late the crank lands, so a prompt crank buys convenience and not correctness – which is much of why the platform stopped paying anybody to be prompt.
The settlement written to state carries the baseline, the window average and the verdict, so the decision can be audited afterwards without re-deriving anything.
Execute performs the action, and only if the settlement says passed. There is one entry point per action kind rather than a general one; the contract will not let you call the wrong one, because each asserts the stored action's kind before it does anything.
Settlement has no deadline; execution does. resolve_proposal stamps an execute_by of seven days from the moment it records the verdict, and the shared execution prologue refuses every entry point past it, so a passed action nobody carries out lapses instead of lying armed. The deadline arrived with the redeploy of 11 September 2026 and answers a specific failure: an action larger than its treasury can cover aborts on the shortfall and stays armed, so before the clock a payout voted through in a window measured in minutes could sit dormant for years and then fire the moment the treasury happened to fill. A failed proposal is simply never executable. Since 5 September 2026 there is a third end state: a proposal whose winning option was a DEBATE position has passed and is never executable, because it never named an action to execute. trigger_proposal asserts the winning option's kind is not 6 and aborts if it is, so such a proposal cannot park in a phase it could never leave.
Once-only, in one substate write
Four flags guard the path, and each flips exactly once. tallied flips when the ballot count is frozen, so a proposal cannot be counted twice or voted on afterwards. triggered flips when the optimistic window opens, which is also what fixes the baseline – a second trigger cannot re-freeze it at a more convenient price. settled flips when the market's verdict is recorded, so the window average cannot be re-integrated later. executed flips in the shared prologue every execution entry point runs, so a passed proposal performs its action once and never again.
The prologue is worth describing precisely, because it carries the whole once-only guarantee. In a single open of the proposal's substate it asserts the proposal is settled, asserts it passed, asserts it has not already executed, asserts the seven-day execution window has not closed, flips executed, and binds the frozen action. If any of those four assertions fails, or the action itself aborts later in the transaction, the flag flip rolls back with it — there is no state in which a caper has spent its one execution on an action that did not happen.
Who pays to settle
Whoever makes the call. Both steps are permissionless work that costs a network fee, and since 11 September 2026 the caller carries it: there is no treasury reimbursement anywhere on the settle path, and the deployed contract has no gas parameter on any crank entry point.
There was one until then, and why it went is worth stating, because it is a general lesson about paying for public goods on a ledger rather than a detail of this one. The caper's treasury used to lock up to 5 XRD contingently against each settle-path call, against a 500 XRD allowance per proposal sized to match the flat creation fee, so a proposal funded its own settlement out of what it had banked. The flaw was not the size of the subsidy but its scope. The Radix Engine consumes locked fees in reverse lock order – locked_fees.iter().cloned().rev() – so the treasury's lock was always drawn before the caller's, and the subsidy therefore paid the first 5 XRD of the caller's whole transaction rather than the first 5 XRD of the crank. Any manifest carrying one successful settle-path call had its opening cost met by a treasury it did not own, whatever else that manifest did, and crystallize_ballots will accept a chunk of a single ballot. Bounded by the proposer's own fee, so it was fee recycling rather than a drain – but it was somebody else's fee.
The creation fee kept the shape the allowance gave it. It is flat – 500 XRD banked when the proposal is raised, kept whether it passes or fails – and the 10% execution charge on the winning action is a separate, later leg that rides the trigger. It is not a floor under a percentage: an earlier deployment did size the creation fee as the greater of 500 XRD and 10% of the action, and material describing it that way, including some of Caper's own, is describing the retired shape.
The executive actions
- PAYOUT transfers the stored currency and amount out of the treasury to the stored recipient, by way of an account locker rather than a direct deposit – a recipient whose account rejects the resource cannot abort the execution; the payment waits to be claimed. The transfer floors the amount to the currency's divisibility and reports what it actually moved, so the event can never overstate the payment.
- INVEST runs its whole withdraw → buy → deposit inside the contract's own call frame. The treasury's XRD never reaches the transaction worktop, which closes the partial-withdraw skim a worktop hop would open, and the tokens bought are asserted to be the target caper's own before they are banked.
- DIVEST is the same trade reversed: withdraw the stored amount of the target's token, sell it into that caper's curve, bank the XRD. It aborts if the treasury's holding has fallen below the proposed amount since settlement – executing a smaller trade than the one that passed would break the frozen-action contract. The sell is emitted onto the target caper's ordinary trade feed, so an indexer needs no divest-specific arithmetic.
- METADATA rewrites the mutable metadata of the caper's own token. The target was pinned to that exact resource at creation, so the shared metadata badge cannot be aimed at a sibling resource such as the vote token or the founder badge.
- UPGRADE is the only two-leg action. The first call nominates the candidate logic component to the registry; the second activates it once the registry's timelock has elapsed. On a zero-delay deployment both legs can ride one transaction. Reserved to $CAPER, because one activation changes the logic every caper on the platform runs.
Neither INVEST nor DIVEST mints vote tokens. They are the treasury's trades, not a member's, and a treasury is not a member.
Where each action runs — state tier vs logic tier
A caper's contract is split into an immortal state tier that holds the treasury and the durable proposal record, and a swappable logic tier that carries the math and the executive plumbing. Which tier performs a winner depends on what the action touches.
PAYOUT executes inside the state tier, because the stored recipient is a global account reference that only stays invokable as a stable substate reference – it cannot be handed out to the logic tier and called there. Every other kind binds its frozen action in the state tier and is then driven by the logic tier, which is where the curve math and the registry calls live. The split is why a logic upgrade can change how an action executes without migrating a single caper's treasury or proposal history.
Hand-rolling an executor manifest
If you build an executor manifest by hand, end it with a try_deposit_*_or_abort: the Radix Engine's General transaction classifier only whitelists the _or_abort deposit variants, and the _or_refund forms make the wallet display a “Complex transaction” warning instead of a clean summary.
Three eras, and what each one changed
This path has been rebuilt twice in a month, and the older material still circulating describes one of the two retired shapes, so it is worth dating all three.
- To 26 August 2026 — escrowed ballots. Members staked governance tokens into a per-caper ballot box. Ballots were released from it in batches, folded into a Borda tally, checked against the same
1.5 / Nsupermajority the live system uses – three-quarters of the2 / Nceiling a Borda leader can take at any ballot size, then subjected to a market veto that cancelled a passed tally if the curve price had fallen far enough while the decision was pending. Release, finalize and execute were three guarded steps, and a thousand-ballot proposal took hundreds of transactions to settle. - 26–28 August 2026 — price only. The ballots were removed outright. A proposal named one action, froze a baseline at creation, and passed forty-eight hours later on a single price comparison. There was no vote to cast and no quorum to reach.
- From 29 August 2026 — both, in sequence. The ranked ballot returned in front of the optimistic phase rather than instead of it. A ballot decides which of up to five options wins and whether the treasury or the proposer pays to execute it; the market then vetoes, or does not, over the market window that follows. Neither phase can carry an action alone: a ballot that passes is only a mandate to open the window, and a window that closes above its baseline executes nothing unless a ballot named a winner first. That symmetry has had one exception since 5 September 2026: a DEBATE option carries no action, so a ballot it wins is carried by the first phase alone and never reaches the second. The redeploy of 7 September 2026 kept both phases and changed the measurement underneath the second: an exact price-time integral read through minute checkpoints replaced a 200-entry ring of per-trade samples, and the trailing baseline was retuned to a shorter window. The redeploy of 11 September 2026 left both phases alone and changed two things on this page instead – it put a seven-day clock on a passed action, and it removed the settle-path gas subsidy outright.
The escrow is the piece that did not come back. A ballot is now a plain wallet transaction – the contract reads the voter's balance rather than taking custody of it – and what makes moving tokens between casting and counting unprofitable is a clamp at settlement, not a lock-up.
Further reading
The Binding Vote – why splitting the count from the deed, and leaving no executor to trust, is the thing an advisory vote and a multisig both fail to do. It argues from the ballot era and is kept as written.