History – Typed vs arbitrary proposal execution

Back
v1.8.7 · currentLink: Aragon's plugin-upgrade guide 404s since docs.aragon.org was restructured; repointed to its 13 July 2026 Wayback capture–
1 block modified
  • Modified – Text at Block 7

    The enumeration is an ABI, so …sting installations” (https://web.archive.org/web/20260713024235/https://docs.aragon.org/osx-contracts/…coding space it does not need.

v1.8.6Em dashes become spaced en dashes (VOICE.md); dashes inside quotes and source titles kept–
4 blocks modified
  • Modified – Text at Block 1 → Block 1

    Topic Whether a passed proposa…ameworks/daohaus) multi-send —– a proposal carries raw callda…ailure Tornado Cash, May 2023 —– a proposal whose code changed…curity-and-governance-attacks)

  • Modified – Text at Block 3

    What arbitrary execution actua…it is work almost nobody does —– decoding calldata against an …n means changing the contract.

  • Modified – Text at Block 4

    The failure mode: a proposal t…s. When the proposal executed —– via delegatecall, so in governance's own storage context —– it minted the attacker 1.2 mi…aid and what its calldata did —– the gap that arbitrary execut…no voting parameter can close.

  • Modified – Text at Block 6

    Why typed designs are rarer th…ction vocabulary genuinely is —– and, if it is narrow, whether…t assumptions can ever become.

v1.8.5Split paragraphs over 150 words at sentence boundaries; wording unchanged.–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …_1cq6jcm9… after 15 September.The 7 September redeploy retun… the method surface unchanged.The only methods to go were tw… naming the long-retired VOTE.Closing the gap took what a ty…an a title they have to trust.

v1.8.4The registry resolves to CaperMain cqma5fh since the logic upgrade of 30 September 2026–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …make. Read on-chain again on 2530 September 2026: the CaperMain… to today ( component_tdx_2_1cr40xf3qma5fh… (https://dashboard.radixscan…o/component/component_tdx_2_1cr40xf3hk3wauqz7pjnvvm067a5hu9ss52wwawdzuz8uxqlj0szvu9) , published bqma5fhxglr6z6s6ym9y73zm4r9lnuzq474ent08xtsplmdufn8npm) , the logic upgrade of 30 September 2026 on the registry the full redeploy of 25 September 2026 published) exposes twenty-one methods a…an a title they have to trust.

v1.8.3Sweep #472: the redeploy list stopped at 18 Sep. Adds the 21 Sep redeploy (CaperMain czmc8h0, which introduced exit_open) and the 25 Sep one (trade fee replaced by the treasury's share of the skim, commit 012387e), both read on-ledger 26 Sep with 21 methods + init; and 'none of the twenty' -> 'twenty-one', matching the count the page already gives.–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …per and the trade fee on sells; the 21 September redeploy added exit_open ( component_tdx_2_1czmc8h0… (https://dashboard.radixscan.io/component/component_tdx_2_1czmc8h097upkzdpptzjz7k00fh8msjtq7m634zd74g7qxyhuxrck0u) ), which records a member's vote-token balance so that exit can only burn tokens that account gave up; and the 25 September redeploy replaced the trade fee with the treasury's share of the skim, with the method surface unchanged. The only methods to go were …grade – and none of the twenty-one is a generic call entry point…an a title they have to trust.

v1.8.2CaperMain has twenty-one public methods since exit_open; cite the 25 September 2026 deployment–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …make. Read on-chain again on 1925 September 2026: the CaperMain…rRegistry ( component_tdx_2_1crf8dl07pndqmgr… (https://dashboard.radixscan…o/component/component_tdx_2_1crf8dl07k744cd0ekefyxf450r93jjwe9ewl6shl4zqr8vngjmnnxgpndqmgr5n6fsf6kausxlyllm4rpuc2zwhn94adws7zegkenqsad8z) ) resolves to today ( component_tdx_2_1czqsy8nr40xf3… (https://dashboard.radixscan…o/component/component_tdx_2_1czqsy8ncz6mcfp43mmnus39kk2l73f8x7wrxjwnzmth93aruhv43pmr40xf3hk3wauqz7pjnvvm067a5hu9ss52wwawdzuz8uxqlj0szvu9) , published by the second logic upgradefull redeploy of 1925 September 2026) exposes twenty-one methods and one instantiation function; exit_open, added on 21 September, is the twenty-first. From 5 September to the rede…an a title they have to trust.

v1.8.1The CaperMain the registry resolves to is the 22:03Z one–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A … to today ( component_tdx_2_1cqadjtvzqsy8n… (https://dashboard.radixscan…o/component/component_tdx_2_1cqadjtv57kjsswhzftulwh6cek2yvwsdgddgvv87230z8qmq0wav20zqsy8ncz6mcfp43mmnus39kk2l73f8x7wrxjwnzmth93aruhv43pm) , published by the second logic upgrade of 19 September …an a title they have to trust.

v1.8.0Add Agora's timelock-context simulation and its Pretty view of proposal actions–
1 block modified
  • Modified – Text at Block 5

    Narrowing the surface without …lamorous positioning suggests. Agora (/wiki/dao-governance/tooling/voting/agora) 's version shows why the setup matters. Since April 2025 it runs a proposal inside its full governor and timelock context, following ScopeLift's Seatbelt (https://github.com/Uniswap/governance-seatbelt) , because calldata simulated on its own can report success for a call that would fail ( Agora (https://www.agora.xyz/blogs/7-context-failures) ). Its Pretty view, added in March 2026, describes each action in plain language, such as which contract's owner a call sets, but only for function selectors and contract addresses on a curated list; anything else is shown as decoded or raw calldata ( Agora (https://www.agora.xyz/blogs/10-pretty-view-proposal-actions) ). - Procedural checks. DAOhaus…hat the answer is not obvious.

v1.7.0Add Aragon's function-selector permissions and Geo's typed fast path–
1 block modified
  • Modified – Text at Block 5

    Narrowing the surface without …pted is enumerated in advance. Aragon (/wiki/dao-governance/tooling/frameworks/aragon) added the same control to its app in August 2025: each governance process can be granted permission to call only chosen functions, each named by a contract address and a function selector, the four bytes at the start of a call that identify which function it invokes, with no change to the target contracts ( Aragon (https://blog.aragon.org/new-in-the-aragon-app-permissions-based-access-control/) ). - A typed fast path. Geo (/wiki/dao-governance/tooling/frameworks/geo) 's second-generation space contracts, specified in October 2025, split the difference by speed. Editors can pass a single action from a short list (adding or removing a member, publishing an edit, flagging or unflagging content) as soon as enough of them vote yes, while anything else, including a change to who the editors are, goes through a slower majority vote that can carry several actions ( Geo (https://github.com/geobrowser/geo-contracts-foundry/blob/dev/docs/2026_03-internal-review/rfcs/2025_10_22-geo-singleton-architecture.md) ). The fast path is typed; the slow path stays general. - Simulation. Front-ends inc…hat the answer is not obvious.

v1.6.0Collateralization skim: a 100% peak falling to 1% by 10% of the supply sold and a 0.5% floor (logic upgrade of 19 September 2026), replacing a 5% peak that fell as peak/√(1 + s/0.0225). Also: CaperMain exposes twenty methods, not twenty-two (dao and treasury went on 18 September), and the 18 September genesis redeploy is recorded–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …ake. Read on-chain again on 189 September 2026: the CaperMain…Registry ( component_tdx_2_1cr65qc2f8dl07… (https://dashboard.radixscan…/component/component_tdx_2_1cr65qc2r33ky36j49lp3mdee7x45pzewr8dp0q6z2ya57mkajmtch9f8dl07k744cd0ekefyxf450r93jjwe9ewl6shl4zqr8vngjmnnxg) ) resolves to today ( component_tdx_2_1cr3qm57qadjtv… (https://dashboard.radixscan…o/component/component_tdx_2_1cr3qm57h5fld30uq8ufefx60ycfuucyu5m7v4zkl9pwkca6d6g7zggqadjtv57kjsswhzftulwh6cek2yvwsdgddgvv87230z8qmq0wav20) , published by the logic upgrade of 189 September 2026) exposes twenty-two methods and one instantiation function – the same count as component_tdx_2_1cq6jcm9… after the redeploy of 1. From 5 September to the redeploy of 18 September every component carried twenty-two: component_tdx_2_1czm6lcn… on 5 September, component_tdx_2_1cq64v5twr8psn5… after the redeploy of 117 September, component_tdx_2_1cr8psn5q64v5tw… after 711 September and component_tdx_2_1czm6lcn… on 5 September. None of those redeploys moved a methodq6jcm9… after 15 September. The 7 September oneredeploy retuned the governance window…nder's skim; the 18 September upgrade changed only the shape of that skim. None added or removredeploy removed the $XRD caper and the trade fee on sells. The only methods to go were two address reads, dao and treasury, dropped by the logic upgrade on the afternoon of 18 September; that upgrade and the one of 19 September 2026 otherwise changed only how the skim is priced and its shape. None added a method. Six of the method…grade – and none of the twenty-two is a generic call entry point…an a title they have to trust.

v1.5.5Sweep #440: the registry now resolves to the CaperMain the 18 Sep 2026 logic upgrade published; method count unchanged–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …ake. Read on-chain again on 178 September 2026: the CaperMain… to today ( component_tdx_2_1cq6jcm9r3qm57… (https://dashboard.radixscan…o/component/component_tdx_2_1cq6jcm9hd6uakguta74dvv63qa2p4seap47htt0px8rftecq2wd3nar3qm57h5fld30uq8ufefx60ycfuucyu5m7v4zkl9pwkca6d6g7zgg) , published by the redeploylogic upgrade of 158 September 2026) exposes twent…n function – the same count as component_tdx_2_1cq6jcm9… after the redeploy of 15 September, component_tdx_2_1cq64v5tw… af…e trade fee and the founder's skim; the 18 September upgrade changed only the shape of that skim. None added or removed a …an a title they have to trust.

v1.5.4Sweep #437: re-pin the method count to the CaperMain published 15 Sep 2026 (cq6jcm9, registry cr65qc2), read 17 Sep–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …ake. Read on-chain again on 137 September 2026: the CaperMain the live CaperRegistry (component_tdx_2_1cr7l4u0c…65qc2… (https://dashboard.radixscan.io/component/component_tdx_2_1cr65qc2r33ky36j49lp3mdee7x45pzewr8dp0q6z2ya57mkajmtch9) ) resolves to today (component_tdx_2_1cq64v5tw…jcm9… (https://dashboard.radixscan.io/component/component_tdx_2_1cq6jcm9hd6uakguta74dvv63qa2p4seap47htt0px8rftecq2wd3na) , published by the genesis redeploy of 115 September 2026) exposes twent…me count as component_tdx_2_1cr8psn5q64v5tw… after the redeploy of 11 September, component_tdx_2_1cr8psn5… after 7 September and component_tdx_2_1czm6lcn… on 5 September. Neitherone of those redeploys moved a method: the first. The 7 September one retuned the governance windows and the price measurement, and the sec; the 11 September onde changed what existing methods…es amounts rather than buckets – without adding or removing one; the 15 September one changed how buys, sells and exits charge the trade fee and the founder's skim. None added or removed a method. Six of the methods are execu…an a title they have to trust.

v1.5.3Sweep #412 (dao-governance/concepts): corrected for the 11 September 2026 redeploy – v is minted only by a ranked ballot (trade-side mint removed), the settlement-gas subsidy is gone so each crank pays its own fee, exit takes amounts withdrawn from the member's account, and the first ballot's snapshot counts its own mint. Verified at contracts/logic/src/lib.rs (VOTE_MINT, vote, exit, SINGLE_PASS_MAX_VOTERS, EXECUTION_WINDOW_SECONDS; source last moved bd5892a) and the live CaperMain component_tdx_2_1cq64v5tw…jjflhcr. 2 span(s) on this page.–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …make. Read on-chain again on 713 September 2026, after that morning’s redeploy: the CaperMain the registry resolves to today (component_tdx_2_1cr8psn5…, resolved from the CaperRegistry at component_tdx_2_1cqtsx3p… that the site’s own client bundle names) exposes twenty-two methods and one instantiation function, exactly as its predecessor: the CaperMain the live CaperRegistry (component_tdx_2_1cr7l4u0c…) resolves to today (component_tdx_2_1cq64v5tw…, published by the genesis redeploy of 11 September 2026) exposes twenty-two methods and one instantiation function – the same count as component_tdx_2_1cr8psn5… after the redeploy of 7 September and component_tdx_2_1czm6lcn… did on 5 September – . Neither redeploy moved a method: the first retuned the governance windows…nd the price measurement, and moved no methodthe second changed what existing methods do – exit, for one, now takes amounts rather than buckets – without adding or removing one. Six of the methods are execu…move when the enumeration did: DEBATE, the kind added that morningon 5 September 2026, executes nothing, so it neede…an a title they have to trust.

v1.5.2Sweep #373: verification stamps re-pointed at the component the registry resolves to today (component_tdx_2_1cr8psn5…, package pkdy3km, commit 3180b5b) – the superseded component still answers as a CaperMain, so the address alone was never the check; plus the execute_proposal_* method count (six for five kinds), the UPGRADE two-leg execution, and the drill-tuning caveat narrowed off the trailing baseline–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …w how to make. Read on-chain again on 57 September 2026: the live CaperMain component, after that morning’s redeploy: the CaperMain the registry resolves to today (component_tdx_2_1czm6lcnr8psn5…, resolved from the CaperRegi…and one instantiation function, exactly as its predecessor component_tdx_2_1czm6lcn… did on 5 September – the redeploy retuned the governance windows and the price measurement, and moved no method. Six of the methods are execu…an a title they have to trust.

v1.5.1Sweep #372: window/baseline/verdict prose settled against the 7 Sep 2026 redeploy – exact price-time integral with minute checkpoints, trailing baseline as a logic constant, market window as drill tuning; no durations in prose–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …earing that ballot only opens a 48-hourthe optimistic market window; the winning action exe…olds at or above the trailing seven-day average locked whenat the window openedtrigger ( optimistic governance (/wik…an a title they have to trust.

v1.5.0The enumeration this page argued about narrowing has now widened: DEBATE (kind 6) deployed 2026-09-05 09:36Z. Re-read the live component, correct the five-kind and 0|1|3|4|5 claims, and price the widening against the arbitrary-execution alternative–
1 block modified
  • Modified – Text at Block 8

    How Caper approaches this A …he time of writing recognises exactly fivsix, five of which execute: pay an address from the tre…hat every caper runs (UPGRADE) – plus a sixth that executes nothing at all, DEBATE, a ranked position in an argument that is terminal at the tally. There is no calldata field. …ow to make. Read on-chain on 30 August5 September 2026: the live CaperMain component (component_tdx_2_1cpz5lthzm6lcn…, resolved from the CaperRegi…_tdx_2_1cqtsx3p… that the site'’s own client bundle names) exp…thods are executors – one per executable kind, plus the second leg of t…call entry point of any shape. That count is worth stating because it did not move when the enumeration did: the kind added that morning executes nothing, so it needed no executor, and a diff of the method surface would not have shown the change at all. The consequences run both wa…on rule. Its enumeration has already narrowed oncenow moved in both directions, which is where the versionin… 1 INVEST, 3 UPGRADE, 4 DIVEST and, 5 METADATA and 6 DEBATE: the gap at 2 was VOTE, an ac…et one caper cast its treasury'’s ballot inside another, and i…d the creation-time whitelist accepts only– 0 | 1 | 3 | 4 | 5 wh| 6 – stilel naminges VOTE in its abort message –, so a proposal built against t…anently unusable discriminant. The widening is the more instructive half, because it puts a price on the ceiling described above. DEBATE was written into the contract source on 30 August 2026 and could not be used for six days. The app half shipped first, so the create-proposal form, the published OpenAPI document (https://caper.network/.well-known/openapi.json) and the MCP tool manifest all advertised a kind the deployed component refused, and a proposer who picked it got an abort naming the long-retired VOTE. Closing the gap took what a typed design always requires and an arbitrary-execution design never does: a newly published logic package and a governed swap of the registry’s current_main, both completed on the morning of 5 September 2026. A Governor (/wiki/dao-governance/tooling/voting/openzeppelin-governor) DAO wanting the same thing – a proposal that is voted on and deliberately executes nothing – expresses it with an empty targets array and deploys nothing. That is the trade in one worked example: legibility bought at the price of a contract release, and a six-day window in which the interface and the ledger disagreed about what a proposal was allowed to say. One thing this page's axis d… a “Do nothing”, at least one executablean answer other than it – and clearing that ballot on…an a title they have to trust.

v1.4.1Point the Aragon OSx citation at its canonical trailing-slash URL; the bare path answers 308.–
1 block modified
  • Modified – Text at Block 7

    The enumeration is an ABI, so …-develop-plugin/upgrade-plugin/) , and updates across release…coding space it does not need.

v1.4.0Add the versioning axis a typed proposal system has and arbitrary calldata does not: a kind set is an ABI, recycling a freed discriminant makes an old-client proposal decode into the wrong action silently, and the safe handling is to fail at creation. Cites Aragon OSx release/build semantics, and records Caper's own retired kind 2 (VOTE) against the deployed contract's creation whitelist.–
1 block added, 1 block modified, 1 block reordered
  • Moved – Text at Block 8
  • Modified – Text at Block 8

    How Caper approaches this A …to the same resolution rule. Its enumeration has already narrowed once, which is where the versioning question above stops being hypothetical. The discriminants run 0 PAYOUT, 1 INVEST, 3 UPGRADE, 4 DIVEST and 5 METADATA: the gap at 2 was VOTE, an action that let one caper cast its treasury's ballot inside another, and it died with the escrow voting system it belonged to. The number was retired rather than recycled, and the creation-time whitelist accepts only 0 | 1 | 3 | 4 | 5 while naming VOTE in its abort message – so a proposal built against the old interface fails at creation with a message that says why, instead of decoding into some later kind. That is the loud-and-early handling, chosen at the cost of a permanently unusable discriminant. One thing this page's axis doe…an a title they have to trust.

  • Added – Text at Block 7

    The enumeration is an ABI, so it has a version The generality trade-off above is the one this axis is usually argued on. There is a second, quieter one that only typed designs face: a fixed set of action kinds is an interface, and interfaces change. Arbitrary calldata has no version to break – a Governor proposal is bytes, and bytes mean whatever the target contract makes of them. A typed proposal is a discriminator plus typed fields, so retiring or renumbering a kind is a breaking change to every client that builds proposals, every indexer that reads them, and every archived proposal that already carries the old number. The dangerous handling is silent reinterpretation. If a removed discriminant is recycled for a new action, a proposal encoded by an old client still decodes cleanly – into the wrong action. Nothing aborts, nothing looks malformed, and the mismatch surfaces only at execution, which is precisely the gap between stated intent and performed action that typing was adopted to close. The safe handling is the opposite: make the stale encoding fail loudly and early, at creation rather than at execution. Frameworks solve this a layer up, at the plugin rather than the action. Aragon OSx (/wiki/dao-governance/tooling/frameworks/aragon) versions each plugin as a release and a build, and its documentation draws the line in exactly these terms: “Releases contain breaking changes, which are incompatible with preexisting installations” (https://docs.aragon.org/osx-contracts/1.x/guide-develop-plugin/upgrade-plugin) , and updates across releases are not possible at all – a DAO must uninstall the old plugin and install the new one, while builds are reserved for compatible upgrades. That is the same instinct expressed as deployment policy: an incompatible interface change is made impossible to apply by accident. The practical question for anyone evaluating a typed DAO is therefore not only how wide its action vocabulary is, but what its enumeration does when it narrows. A design that reuses freed discriminants is trading a legibility guarantee it advertised for a byte of encoding space it does not need.

v1.3.0Re-read the live CaperMain (component_tdx_2_1cpz5lth..., 22 methods, 6 executors) and replace the retired-ballots paragraph with the deployed ranked-ballot plus optimistic-window shape–
1 block modified
  • Modified – Text at Block 7

    How Caper approaches this A …a kind discriminator, and the contractlogic package live at the time of writing recognises exactly five: pay …r runs (UPGRADE). There is no sixth slot and no calldata field. A proposal can…ow to make. Read on-chain on 2830 August 2026: the live CaperMa… component (component_tdx_2_1cqtt9nzpz5lth…, resolved from the CaperRegistry at component_tdx_2_1cqtsx3p… that the site's own client bundle names) exposes eighteen methods, six of themtwenty-two methods and one instantiation function. Six of the methods are executors – one per kind, plu…nd leg of the upgrade – and none of the twenty-two is a generic call entry point of a… ceiling: adding a kind needs the state-holding contract's cooperationa new logic package, not merely a passed proposal…e's axis does not anticipate: Ca caper retired ballots in August 2026, so a's proposal ihas nto longerbe legible twice, to voters –and thern to the mare none. It resolves on price instead, passing only if the caper's spot price at the close of a 48-hourket. It is a ranked ballot over two to five options – at least one a “Do nothing”, at least one executable – and clearing that ballot only opens a 48-hour optimistic window; the winning action executes if the token's time-weighted price across the window iholds at or above a seven-day time-weightedthe trailing seven-day average locked when the proposal was raiswindow opened ( optimistic governance (/w…amily). Typing therefore does moreits work here than in a Governor DAO, not less: with no tally standing between a proposal's title and its payload, the enumeration is the only thing that doeson both readers: a voter ranks options whose payloads the enumeration has already constrained, and a holder who wants to veto one is selling against an action they can read rather than a title they have to trust.

v1.2.0Repoint the cited deployment at the 28 Aug 2026 full redeploy: CaperMain component_tdx_2_1cqtt9nz…, CaperRegistry component_tdx_2_1cpqacn3…. The claims are unchanged and still hold — the v-minting core interface change forced a new immortal core, so every prior address moved.–
1 block modified
  • Modified – Text at Block 7

    How Caper approaches this A … component (component_tdx_2_1crmlq2fqtt9nz…, resolved from the CaperRegi…n is the only thing that does.

v1.1.0Caper note corrected against the deployed contract: the fifth action kind is METADATA, not caper-as-voter (kind 2 retired); six executors read on-chain 28 Aug 2026 off current_main component_tdx_2_1crmlq2f…; proposals resolve on price, not a tally.–
1 block modified
  • Modified – Text at Block 7

    How Caper approaches this A …(/wiki/governance/proposals) option carries a kind discriminator, …y an address from the treasury (PAYOUT), buy another caper's token (INVEST), sell one back down its curve, cast a vote inside ano (DIVEST), rewrite the mutable metadata of ther caper's own token (METADATA), or —– on the platform's own caper only —– swap the shared logic tier that every caper runs (UPGRADE). There is no sixth slot and n…ot already know how to make. Read on-chain on 28 August 2026: the live CaperMain component (component_tdx_2_1crmlq2f…, resolved from the CaperRegistry that the site's own client bundle names) exposes eighteen methods, six of them executors – one per kind, plus the second leg of the upgrade – and no generic call entry point of any shape. The consequences run both ways…and both are worth stating. A votreader can readtell what an option proposal does from its kind and its ty…, so the Tornado failure mode —– a description that does not match the payload —– has no room to occur. But the…really is a ceiling: adding a new kind requireneeds the state-holding contract's…peration, not merely a passed voteproposal. Caper's answer to that is the fifth kind, an upgrade of the sharedUPGRADE kind, which swaps the logic tier, which and is itself a proposal subject to the same voting rules. The action vocabulary is fixed, but the process for widening it is inside governance rather than outside it. See executionresolution rule. One thing this page's axis does not anticipate: Caper retired ballots in August 2026, so a proposal is no longer legible to voters – there are none. It resolves on price instead, passing only if the caper's spot price at the close of a 48-hour window is at or above a seven-day time-weighted average locked when the proposal was raised ( optimistic governance (/wiki/dao-governance/execution) for how a winning option is settledconcepts/voting/optimistic-governance) is the wider family). Typing therefore does more work here than in a Governor DAO, not less: with no tally standing between a proposal's title and its payload, the enumeration is the only thing that does.

v1.0.0New concept page: typed vs arbitrary proposal execution — the axis separating Governor-style calldata proposals from enumerated action kinds. Sourced from Governor.sol / GovernorBravoDelegate.sol, the Zodiac Roles Modifier, DAOhaus's agent vote-decision flow, and Halborn on the May 2023 Tornado Cash governance attack.–
7 blocks added
  • Added – Text at Block 1 → Block 1

    Topic Whether a passed proposal may run any call, or only one of a fixed set of actions The arbitrary end OpenZeppelin Governor (/wiki/dao-governance/tooling/voting/openzeppelin-governor) · Compound Governor Bravo · Baal (/wiki/dao-governance/tooling/frameworks/daohaus) multi-send — a proposal carries raw calldata The typed end Enumerated action kinds the contract itself understands; anything outside the enum is not expressible Core trade-off Generality (any future action, no upgrade needed) against legibility (a voter can tell what the proposal does) Mitigations in practice Timelocks (/wiki/dao-governance/concepts/voting/governance-timelocks) · Zodiac Roles (/wiki/dao-governance/tooling/frameworks/zodiac) scoping · simulation before the vote Worked failure Tornado Cash, May 2023 — a proposal whose code changed after it passed Related The proposal lifecycle (/wiki/dao-governance/concepts/voting/proposal-lifecycle) · Governance attacks (/wiki/dao-governance/concepts/analysis/dao-security-and-governance-attacks)

  • Added – Text at Block 2

    Every DAO framework has to answer one question about its proposals: what is a passed vote allowed to do? There are two families of answer. In an arbitrary-execution design, a proposal carries raw transaction data and the governance contract will execute whatever that data says. In a typed design, a proposal selects from a fixed list of action kinds that the contract itself understands, and nothing outside that list can be expressed at all. This axis is rarely stated explicitly, but it decides more about a DAO's real risk profile than quorum or threshold settings do. It determines whether a voter can know what they are voting for by reading the ballot, and it is the axis on which the most expensive governance failures (/wiki/dao-governance/concepts/analysis/dao-security-and-governance-attacks) turn.

  • Added – Text at Block 3

    What arbitrary execution actually looks like The dominant Ethereum pattern is arbitrary. OpenZeppelin's Governor (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/governance/Governor.sol) declares its entry point as propose(address[] targets, uint256[] values, bytes[] calldatas, string description). The first three arrays are the proposal; the fourth argument, the human-readable description, is the only part a voter reads and the only part the contract does not enforce. Compound's Governor Bravo (https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/GovernorBravoDelegate.sol) takes the same shape with an extra signatures array and caps a proposal at ten actions (proposalMaxOperations = 10). Nothing in either contract relates the description to the calldata. A proposal titled “Fund the grants program for Q3” and a proposal that drains the treasury are, to the governance contract, the same kind of object: a list of addresses, values and byte strings. Verifying that the two agree is off-chain social work, and it is work almost nobody does — decoding calldata against an unverified target contract is a skill most delegates do not have and most voters do not attempt. The upside is real and is why the pattern won. An arbitrary-execution DAO can do things its designers never anticipated: call a protocol that did not exist when the governance contract shipped, adopt a new standard, or wind itself down. A typed DAO can only ever do what its enumeration allows, and widening the enumeration means changing the contract.

  • Added – Text at Block 4

    The failure mode: a proposal that changed after it passed In May 2023 Tornado Cash lost governance control of itself to a proposal that looked like an earlier, legitimate one. Per Halborn's analysis (https://www.halborn.com/blog/post/explained-the-tornado-cash-hack-may-2023) , the attacker submitted a proposal that claimed to implement the same logic as a previously approved proposal, but whose deployed contract carried extra self-destruct logic. Voters, comparing it to the version they already trusted, passed it. The attacker then destroyed the proposal contract and redeployed different code at the same address. When the proposal executed — via delegatecall, so in governance's own storage context — it minted the attacker 1.2 million votes against roughly 70,000 legitimate ones, and about 483,000 TORN (~$2.17 million) was drained. The instructive part is not the self-destruct trick. It is that every governance rule worked correctly. Quorum was met, the vote was honest, the timelock elapsed, and the contract executed exactly the bytes it was given. The failure was entirely in the gap between what the proposal said and what its calldata did — the gap that arbitrary execution creates by design and that no voting parameter can close.

  • Added – Text at Block 5

    Narrowing the surface without giving up generality Most of the tooling built around Governor over the past few years is, read charitably, an attempt to recover legibility that arbitrary execution gave away. - Timelocks (/wiki/dao-governance/concepts/voting/governance-timelocks) do not make a proposal legible; they buy time for someone else to notice. They convert an execution problem into a monitoring problem, which is an improvement only if somebody is monitoring. - Scoped permissions. Zodiac's Roles Modifier (/wiki/dao-governance/tooling/frameworks/zodiac) sits between a module and a Safe (/wiki/dao-governance/tooling/treasury/safe) and, in its own description (https://github.com/gnosisguild/zodiac-modifier-roles) , lets holders of a role “unilaterally make calls to any approved addresses, approved functions, and using approved parameters.” That is typing applied at the permission layer rather than the proposal layer: the calldata stays arbitrary, but the set of calls that will be accepted is enumerated in advance. - Simulation. Front-ends increasingly simulate a proposal's calls against forked state and show the resulting balance changes. This is the closest thing to making calldata readable, and it is why proposal tooling and governance data (/wiki/dao-governance/tooling/analytics/boardroom) services matter more than their unglamorous positioning suggests. - Procedural checks. DAOhaus's published vote-decision flow (https://github.com/HausDAO/moloch-skills/blob/main/VOTE_DECISION_FLOW.md) for autonomous agent members is the clearest written statement of the problem: it classifies every proposal into one of six buckets, of which the last is plainly “arbitrary execution”, and requires the agent to ask “does action data match proposal text?” before voting. When a procedure has to name that question explicitly, the design has already conceded that the answer is not obvious.

  • Added – Text at Block 6

    Why typed designs are rarer than they should be Typing a proposal system has an obvious cost: the enumeration is a ceiling. Adding a genuinely new action means changing the contract that holds the state, which is exactly the kind of change a DAO is slowest at making and most reluctant to authorise. Frameworks that expect to serve arbitrary future protocols therefore default to arbitrary calldata, and the burden of understanding a proposal shifts to the voter. The counter-argument is that most DAOs do not need generality. A treasury that in practice only pays contributors, buys assets, sells assets and votes in other organizations has a small action vocabulary, and paying for unlimited expressiveness it never uses buys nothing but attack surface. The right question is not “which design is safer” but how wide the DAO's action vocabulary genuinely is — and, if it is narrow, whether the framework will let it say so on-chain. Most will not. This is one of the places where framework choice, made once and early, quietly sets a ceiling on how minimal (/wiki/dao-governance/concepts/fundamentals/governance-minimization) a DAO's trust assumptions can ever become.

  • Added – Text at Block 7

    How Caper approaches this A caper (/wiki/foundations/what-is-a-caper) sits at the typed end, and deliberately. An executive proposal (/wiki/governance/proposals) option carries a kind discriminator, and the contract recognises exactly five: pay an address from the treasury, buy another caper's token, sell one back down its curve, cast a vote inside another caper, or — on the platform's own caper only — swap the shared logic every caper runs. There is no sixth slot and no calldata field. A proposal cannot encode a call the contract does not already know how to make. The consequences run both ways, and both are worth stating. A voter can read what an option does from its kind and its typed fields, so the Tornado failure mode — a description that does not match the payload — has no room to occur. But the enumeration really is a ceiling: adding a new kind requires the state-holding contract's cooperation, not merely a passed vote. Caper's answer to that is the fifth kind, an upgrade of the shared logic tier, which is itself a proposal subject to the same voting rules. The action vocabulary is fixed, but the process for widening it is inside governance rather than outside it. See execution (/wiki/governance/execution) for how a winning option is settled.