---
title: "Execution"
url: "https://caper.network/wiki/governance/execution"
updated: 2026-08-28
license: CC-BY-4.0
license_url: "https://creativecommons.org/licenses/by/4.0/"
---

# Execution

> **[May be outdated]** This article documents Caper's ballot-based governance, which is no longer deployed. Verified on-chain 28 August 2026: the live CaperMain component (component_tdx_2_1cqz0ahm…, resolved from the CaperRegistry) exposes no vote, finalize_proposal, release_ballots or veto call. Proposals now resolve optimistically on price — a proposal passes if the spot price at resolution is at or above a seven-day TWAP baseline locked at creation, over a 48-hour window, and trading is the only way to express dissent. The mechanics that are live are summarised on the FAQ at /wiki/help/faq. This page is pending rewrite and is kept meanwhile as a record of the retired system.

|  |  |
| --- | --- |
| **Topic** | Carrying out a passed proposal |
| **Timeline** | Voting closes → execution delay (~10 min, ballots still arriving) → release ballots (3 per transaction) → market-veto tail (6 h) → finalize → execute |
| **Executive action kinds** | PAYOUT · INVEST · DIVEST · VOTE · UPGRADE ($CAPER only) |
| **Replay guards** | Two once-only flags: `settled`, then `executed` |
| **Market veto** | Finalize cancels a passed tally if the curve's time-weighted price from voting close to finalize sits >30% below the voting-window mean |
| **Settlement gas** | Treasury-reimbursed, up to 5 XRD per call against a 500 XRD per-proposal allowance |
| **Live logic tier** | [CaperMain on Stokenet](https://stokenet-dashboard.radixdlt.com/component/component_tdx_2_1cr0wpsnnfk3rkxzkn9e39n2su5sgd9nctfcxh4rmt9dtpre2ngalht), resolved from the [registry](https://stokenet-dashboard.radixdlt.com/component/component_tdx_2_1crd7wmgsa465q4ceqnlcwh9pc8pmsxhdthat86379axx74cse5ml4d)'s `current_main`; live since 25 August 2026, carrying the ballot-era release/finalize path and the market veto |
| **Related** | [Proposals](/wiki/governance/proposals), [Voting](/wiki/governance/voting), [Voting mechanisms](/wiki/governance/voting-mechanisms), [Proposal lifecycle](/wiki/dao-governance/concepts/voting/proposal-lifecycle) |

A proposal's voting window closes on a clock, and an **execution delay** of the same length runs after it: ten minutes each on the [live contract](https://stokenet-dashboard.radixdlt.com/component/component_tdx_2_1cr0wpsnnfk3rkxzkn9e39n2su5sgd9nctfcxh4rmt9dtpre2ngalht) today. Neither is a lever anyone can pull: both are written once when a logic component is instantiated and never assigned again, so moving either takes a whole new logic tier and a move of the registry's `current_main`. Ballots keep arriving through that delay. A member signs their ballot as a pre-authorization subintent during the voting window, and the settlement assembler packs those signatures into batched transactions submitted afterwards, so the timelock and the ballot-collection phase are the same window. Nothing can execute inside it, which is the role a timelock plays in [mainstream DAO governance](/wiki/dao-governance/concepts/voting/proposal-lifecycle).

Once the delay expires, resolution happens in public steps anyone can run. **Release** hands the ballots to the immortal record a few at a time: each one writes its audit blob, banks the voter's escrowed vote fee, mints their soulbound vote token, and unlocks the stake that backed it. **Finalize** then freezes the winner from accumulators the ballot box has been summing since the first ballot landed – but not the moment release finishes: the live contract holds it back a further six hours, the objection window in which the caper's own market can cancel a passed tally. Finally, anyone submits the transaction that **executes** the winning option.

## Release, finalize, execute – once-only guards

Resolution is guarded at every step: the release cursor only moves forward, each ballot is released exactly once, and a proposal can never be finalized twice or executed twice.

**Release.** Anyone can hand the next batch of ballots from the caper's ballot box to the immortal core. Batch size ships at **three ballots per transaction**, and that number is measured rather than chosen: profiled in a local ledger simulator on 15 August 2026, the release path costs roughly 34.3M cost units of base plus 12.09M per voter, so three fit inside 80% of the engine's per-transaction ceiling at a fee of about 4.1 XRD, under the 5 XRD the treasury reimburses. A five-ballot crank measures about 5.6 XRD on the same harness and tips past that ceiling, leaving the settler to eat the overflow – which is why the chunk is three rather than five (`RELEASE_CHUNK`, `contracts/logic/src/lib.rs`; `profile_release_scaling` and `profile_settlement_gas_is_reimbursed_by_treasury`, `contracts/logic/tests/profile.rs`). Both are simulator measurements rather than observations of the deployed component. Release refuses to start while casting is still open and refuses to run after a proposal has settled, so the audit record is always written before the decision is frozen. A voter whose account refuses deposits gets a single-ballot fallback that lands the record and the fee but parks the token mint for them to claim later, rather than poisoning every ballot batched alongside it.

**Finalize.** Finalizing reads the finished count and freezes the winner. It is O(1) in the electorate: there is no tally pass, because each ballot's Borda contribution was folded into the running accumulators the moment it was cast. The option with the greatest summed weight wins only if its share of the total weight _reached_ the `1.5 / N` supermajority – the comparison is `>=`, so a share landing exactly on the bar passes – **and** it is not the no-execution option, **and** the market veto described below did not fire; otherwise the proposal finalizes with no winner. Every cast ballot must have been released first, and the immortal core enforces the same ordering from the other side by refusing new ballots once a settlement is recorded. This step flips a `settled` flag and can run only once.

**Execute.** With a winner frozen, execution flips a separate `executed` flag, the guard that stops the same winning action from being replayed. A settled proposal that did not pass has no winner to execute, and a [legislative proposal](/wiki/governance/proposals)'s winner carries no on-chain action at all, so only executive winners reach this step.

## The market veto

Clearing the supermajority is not the last gate. Since 25 August 2026 the live logic tier carries an **optimistic market veto**: a proposal whose tally passed is cancelled at finalize if the caper's own [bonding-curve](/wiki/markets/bonding-curve) price fell far enough while the decision was pending. The tally answers what the electorate that showed up wanted; the veto asks what everyone else did with their money afterwards.

Two windows are compared, and both are time-weighted rather than sampled. The **baseline** runs from voting open to voting close; the **objection window** runs from voting close to the moment finalize is called. The immortal state tier integrates circulation exactly over each one – a caper's curve price is a step function that moves only at the sites that mint or burn tokens, so one accumulator advance per trade is the whole average, with no sampling and no ring buffer. Each window's mean circulation is then converted into a price, which is sound precisely because the curve's spot price is strictly monotone in circulation, so a threshold set in price space transfers. The veto fires when the objection-window price sits more than **30%** below the baseline – `veto_drop_threshold`, read as `0.3` off the live logic component on 26 August 2026.

It is deliberately one-sided, and that is the design rather than an accident of implementation. On a bonding curve, pushing the price down means selling float you actually hold, while holding it up takes only outside XRD that anyone can bring – so objection is expensive and honest, and support is rentable. Counting only the downward move means _silence executes_: a caper nobody traded during the window, and the [$XRD](/wiki/foundations/what-is-a-caper) caper (which has no curve at all, and is excluded by name), both settle exactly as they did before the veto existed. What the veto adds is a way for holders who never voted to stop a decision, at the price of selling into it.

The gate carries a clock of its own. `finalize_proposal` refuses to run until **six hours** after the execution delay closes – `veto_tail_seconds`, `21600` on the live contract – and that tail is what guarantees the market time to answer. Casting stays open right through the execution delay, so without it a last-second ballot could flip a tally with no window left to react. Release is _not_ held back: ballots can be released as soon as casting closes, so on a large proposal the release cranks and the objection window run alongside each other. Both parameters are logic-tier policy, rewritten together by `update_veto` and bounded to `[0, 1]`; a threshold of `1` disables the veto outright, since a price can never fall below zero.

A vetoed proposal is recorded as vetoed rather than quietly dropped. The settlement blob gained a second version carrying a `vetoed` flag beside the option weights, the `ProposalTallied` event appends it, and the interface labels that outcome _Market Vetoed_ – distinct from a proposal that simply never reached `1.5 / N`. (`finalize_proposal`, `contracts/logic/src/lib.rs`; `curve_time_averages`, `contracts/core/src/caper_dao.rs`.)

What it answers is turnout suppression rather than plutocracy. A caper's bar is `1.5 / N` of the weight actually cast, not of supply, and there is no [quorum](/wiki/dao-governance/concepts/voting/quorum-and-threshold-design) to fail – so a hostile proposal that thins the electorate, by timing or by noise or by anything that keeps stake unescrowed, faces only a ratio of whoever turned up. The veto puts a second constituency behind the first. Not a larger electorate, but the one thing a low-turnout attacker cannot manufacture: the price the caper's own holders will still accept.

## Custody replaced the clamp

Until 16 August 2026 this page described a different apparatus, and the difference is worth recording because the old one was a real design that a live upgrade retired. A ballot used to be credited the smaller of what the voter held when they voted and what they held when the tally ran; anyone could permissionlessly re-clamp a counted ballot downward, and a tally that was not taken in a single pass had to sit out a one-hour challenge window before it could settle. Snapshots, caps, clamping and the challenge window are all gone.

What replaced them is physical custody. A member stakes governance tokens into the caper's ballot box before voting; a ballot's weight term is that vaulted stake, and casting refcount-locks it. The vault refuses to return a stake while any ballot it backs is still unreleased, so the same tokens can never back two counted ballots and a voter cannot sell what they voted with. Double-vote prevention became a property of where the tokens are rather than a rule about what the tally re-reads, which is why the re-clamp and the challenge window had nothing left to do.

The swap itself went through the mechanism this page describes at the bottom. Proposal `P-CAPER-7CD71F` on the [$CAPER](/wiki/foundations/what-is-a-caper) caper carried an UPGRADE option naming the new logic component, and its [execution transaction](https://stokenet-dashboard.radixdlt.com/transaction/txid_tdx_2_1gyn66jhux2qcx7qgrfyt4e7m0pkq8yhymauqn6sgh3ghuk7culasxshjz3) called `execute_proposal_upgrade` and `execute_proposal_upgrade_activate` back to back on 16 August 2026 at 09:27:54 UTC. That transaction, and the component it activated, belong to a deployment Stokenet has since replaced twice over – and the way that citation ages is worth recording too. The registry it ran against, [`component_tdx_2_1czd3cm2…`](https://stokenet-dashboard.radixdlt.com/component/component_tdx_2_1czd3cm28fkq6f08c9y52w0tw56p4k6dz5h3uf76jrmpgm8a8ashuey), still answers, still holds real state, and still names that retired component as its own `current_main`: the whole citation self-confirms, so nothing about following it looks wrong. The live root is the registry in the infobox above, and resolving `current_main` there is the only thing that tells the two apart. Both calls fitted in one manifest because the registry carries a zero upgrade delay, then and now; on a network with a non-zero delay the propose and the activate are separated by it.

## Who pays to settle

Every step after the cast window closes is permissionless, and permissionless work still costs gas. A large proposal's release path is not one transaction but hundreds. At three ballots a transaction, a thousand-ballot proposal is roughly 334 release calls plus one finalize, and none of it is contingent on the proposal passing. Leaving that bill with whoever happens to click Settle taxes one member for a public good the whole caper consumes.

That is the default almost everywhere else. In [OpenZeppelin's Governor](https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/governance/Governor.sol) and in [Compound's Governor Bravo](https://raw.githubusercontent.com/compound-finance/compound-protocol/master/contracts/Governance/GovernorBravoDelegate.sol), `execute` is an ordinary `payable` function with no access control: anyone may call it once the proposal is queued, and the caller pays the gas from their own pocket with nothing coming back. In practice the proposer, a delegate or a bot absorbs it, and the cost appears in none of the governance ledgers surveyed in [DAO governance operating costs](/wiki/dao-governance/concepts/analysis/dao-governance-operating-costs). What that unpriced work costs when a DAO buys it from an automation vendor instead, and why it stays invisible when it does not, is on [who pays to execute a passed proposal](/wiki/dao-governance/concepts/analysis/proposal-execution-costs).

A caper reimburses it instead. Each call draws up to **5 XRD** from the treasury against a **per-proposal allowance of 500 XRD**. The two figures match today, but not because the allowance was sized to the fee – the allowance is a contract constant, and it is the fee that is pinned to it. `update_fees` asserts `proposal_fee >= SETTLEMENT_GAS_BUDGET`, refusing any schedule that fails "proposal_fee must cover the settlement-gas budget", so the configured fee can be raised above the allowance but never cut below it. That floor, rather than a coincidence of defaults, is what guarantees a proposal funds its own settlement and can never cost the treasury more than it contributed. The allowance is one pool shared by the whole path: the batched cast transactions draw on it too, at up to 8 XRD each. At 5 XRD a release call it covers about a hundred of them, so a proposal past roughly three hundred ballots exhausts it and the remaining callers pay their own way. That degrades rather than bricks, and the vote fees banked at release, 100 XRD a ballot on the live contract, return far more to the treasury than the allowance can ever spend.

The draw is a **contingent** fee lock, and that is load-bearing rather than a detail. A plain `lock_fee` credits the fee reserve immediately and survives an aborted transaction; [a contingent lock never touches that balance](https://raw.githubusercontent.com/radixdlt/radixdlt-scrypto/main/radix-engine/src/system/system_modules/costing/fee_reserve.rs) and is charged only when the transaction commits. Without it, anyone could call a settle-path method, abort the transaction, and drain the treasury while the allowance counter rolled back every time. Because payment and allowance now move together, the worst a spammer gets is an allowance draining at the same rate real work would drain it.

The draw rides each call's proposal read, so a release that finds nothing left to release still pays the keeper who ran it: honest work that happens to find nothing is still work. The allowance ledger itself lives in the immortal state tier beside the funds, while the budget and the per-call ceiling are logic-tier policy, so an [UPGRADE](/wiki/governance/proposals) can retune what settlement costs without touching the record of what has already been spent.

## The executive actions

**PAYOUT** and **INVEST** each execute in a single call. PAYOUT transfers the chosen currency and amount out of the treasury to the stored, voted-on recipient — by way of an [account locker](https://docs.radixdlt.com/docs/locker) rather than a direct deposit, so a recipient whose account rejects the resource cannot abort the execution; the payment simply waits to be claimed. That, and the rest of the treasury's plumbing, is covered under [raising funds](/wiki/markets/raising-funds). INVEST runs its whole withdraw → [buy](/wiki/markets/bonding-curve) → deposit inside the contract's own call frame, so the treasury XRD it spends never touches the transaction worktop — a public executor cannot take part of the withdrawal and pocket the rest. The execution step asserts the bucket handed back is the target caper's token, in a positive amount, before it lands in the treasury.

**VOTE** runs in two transactions and several manifest instructions, because it must re-enter a separate component mid-transaction. A preliminary call escrows the treasury's stake in the target caper's ballot box, in its own transaction so the execution itself stays under the engine's cost-unit ceiling, and the execution refuses to burn its once-only guard if that stake is not there: an unstaked treasury ballot would close gracefully to a silent zero. The execution proper then hands the manifest the target token, the XRD vote fee, and the treasury admin badge; the manifest presents that badge as a proof to authorise the treasury's vote into the target caper's proposal, routed through a fresh top-level frame; a final call returns the badge to its vault. Because the whole sequence is one atomic transaction, the admin badge never survives a transaction boundary outside the vault — if any step fails, the entire execution rolls back.

**DIVEST** is INVEST run backwards, and it is the newest of the five. Execution withdraws the voted-on amount of the target caper's token from this treasury, sells it into that caper's [curve](/wiki/markets/bonding-curve), and deposits the XRD — again entirely inside the contract's own call frame, so the tokens never touch the worktop. Two guards ride it. The withdrawal aborts outright if the treasury's holding has fallen below the amount that passed, because executing a smaller trade than the one that was voted on would break the frozen-winner contract; and the sell asserts the bucket handed over is the target caper's own resource, so a stale address aborts rather than selling the wrong thing. The $XRD caper is excluded by name — it has no curve to sell into. Where INVEST fuses its guard and its withdrawal into one state-tier call, DIVEST composes two generic ones the immortal tier already exposes, paying an extra cross-package hop to avoid redeploying that tier; the whole action therefore shipped through the ordinary UPGRADE path with no per-caper migration. Its sell leg emits the same event an ordinary sale does, so it appears on the target caper's [trade feed](/wiki/markets/trading) with no divest-specific arithmetic anywhere off-chain.

**UPGRADE** is the one action reserved for the [$CAPER](/wiki/foundations/what-is-a-caper) caper itself. Its winning option swaps the platform's live logic tier through the registry, in a two-phase propose-then-activate sequence — this is the mechanism by which the whole system upgrades itself under governance rather than under an admin key.

## Where each action runs — state tier vs logic tier

A [caper](/wiki/foundations/what-is-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 voted-on recipient is a `Global` that only stays invokable as a stable substate reference — it cannot be handed to the logic tier and called there. The state tier therefore performs the transfer to the stored recipient itself, and no executor can redirect it. **INVEST, DIVEST, VOTE and UPGRADE carry no account reference**, so the logic tier drives them, reading back only the frozen typed option from state. That division is why an upgrade can replace the entire logic tier without ever putting the treasury or the proposal ledger at risk.

## Hand-rolling an executor manifest

If you ever 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.

## Further reading

[The Binding Vote](/blog/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.
