Timelock Action-Bundle Commitment

  • Name: timelock action-bundle commitment
  • Category: governance reference / timelock design checklist / delayed-privileged-execution safety note / authorization-design reference
  • Summary: Healthy timelocks and proposal systems should not treat this proposal address, this executor, or this queued operation id as the real thing being trusted. The real trust object is one exact action bundle: ordered targets, values, calldata, execution primitive, dependency / predecessor constraints, timing parameters, and any code identity that must still hold at execution time. This note exists to make that positive design target explicit. The sharper split worth preserving is that the delayed payload is exact and reviewable is still not the same claim as the constitution will remain reachable long enough for that delayed payload to matter; healthy governance needs both. If a governance system cannot say exactly what machine-readable effect set it has frozen for later execution, then the timelock is mostly delaying ambiguity rather than securing root authority.
  • Why this note exists:
    • Many governance incidents do not begin when execution fires. They begin earlier, when the system freezes the wrong object for later root-level execution.
    • A timelock only helps if the thing being delayed is already the exact thing the protocol means to execute later.
    • Exact delayed payloads are only half the governance story. If threshold arithmetic, proposer eligibility, or emergency-repair survivability can die before the delay clears, then a well-committed action bundle still may never land.
    • The useful design question is not just is there a delay? but what exact effect set did the delay make immutable?

What this note means by action-bundle commitment

  • An action bundle is the full ordered set of privileged effects a proposal is allowed to produce when execution time arrives.
  • A healthy timelock commits that bundle in a way both humans and contracts can refer to unambiguously later.
  • The commitment should survive the whole path from proposal creation through queueing, delay, execution, and postmortem review.
  • The point is not abstract purity. It is preventing reviewed one thing, executed another failures in governance and admin flows.

The six joints a healthy timelock commitment should keep fused

  1. Ordered target set

    • The system should know exactly which contracts will be called, in what order.
    • Some later delegatecall through this proposal contract is not the same thing as an action list.
    • Ordering matters because state created by one step often becomes authority for the next.
  2. Exact calldata and value transfer

    • The committed object should include the full calldata bytes and any ETH / native-token value for each call.
    • Human-readable descriptions are useful, but they are not the security object unless the protocol also commits the exact bytes they are describing.
  3. Execution primitive

    • call, delegatecall, module install, proxy upgrade, and timelock executor dispatch are different authority shapes.
    • A healthy commitment records not only who gets touched but how those touches are allowed to happen.
  4. Dependency and timing constraints

    • Predecessor links, salt, eta, minimum delay, and any cancellation / expiration rules belong inside the trusted object.
    • A timelock should be able to answer whether this exact bundle was the one queued for this exact window under these exact prerequisites.
  5. Executable code identity when code is part of the plan

    • If the bundle relies on an external proposal contract, proxy implementation, module, or plugin, code identity belongs inside the commitment too.
    • Otherwise the system has only delayed a pointer to mutable behavior.
    • This is where action-bundle commitment meets execution-envelope-identity-pinning.
  6. Machine-checkable effect freeze

    • The final object should be stable enough that execution-time code can verify this is still the same bundle that governance queued.
    • If the timelock cannot distinguish one effect set from a later-mutated cousin, the delay is not really protecting the privileged action.

What sound design looks like

  • Queue effects, not shells

    • The healthiest governance systems queue concrete action lists rather than approving a generic executor contract and trusting later behavior.
    • execute(targets[], values[], calldatas[]) is usually a safer trust shape than delegatecall this proposal address later.
  • One hash should correspond to one executable story

    • A queued operation id or proposal hash should map to one exact privileged narrative a reviewer can reconstruct: who gets called, with what bytes, in what order, after what delay.
    • If multiple materially different executions can hide behind the same governance-facing object, the commitment is too weak.
  • Mutable executors should be pinned or revalidated

    • When governance must rely on an external executable contract, the system should pin runtime / implementation hash or re-check it at execution time.
    • Bare address approval is not enough once time exists between review and execution.
  • Timelock delays should protect humans and automation equally

    • Monitors, veto paths, and reviewers should be able to inspect the same machine-readable bundle the executor will later honor.
    • If defenders can only see prose while the contract honors opaque bytecode or mutable indirection, the timelock has split the security object.
  • Admin changes above the timelock should themselves be timelocked

    • If a proxy admin, module registry, or proposal factory can still mutate what the queued bundle means, that admin path belongs inside the same delayed review surface.
    • Otherwise the protocol quietly lets a second authority rewrite the first authority’s object.
  • The constitution has to survive the delay window

Healthy architectural shapes

1. Native calldata-list timelocks

  • The cleanest shape is a timelock that queues exact targets, values, calldatas, predecessor, and salt directly.
  • The queue hash is then already the effect commitment, not a label for some other contract to interpret later.
  • This is the simplest way to make review-time and execution-time objects stay identical.

2. Proposal systems that freeze execution payloads before voting closes

  • If governance discussion needs richer proposal composition, the protocol should still freeze the final executable bundle before the decision point.
  • Voters should not be authorizing a proposal shell whose concrete effects are still meaningfully mutable.
  • The important boundary is when the machine-readable privileged action becomes immutable, not when the UI says proposal submitted.

3. Codehash-pinned proposal executors

  • If a protocol truly needs external proposal contracts or modules, the timelock should bind queueing and execution to code identity as well as address.
  • That can mean runtime hash checks, implementation hash checks, or another verifiable code-identity guard.
  • Without that extra pinning, the system is still trusting a mutable shell.

4. Two-stage governance where the first stage approves policy and the second stage approves bytes

  • Some systems need open-ended deliberation first and exact technical action approval second.
  • That can still be safe if the byte-level action bundle gets its own explicit freeze and delay before execution.
  • The important design move is that no root-level call path stays materially underspecified when the clock starts.

Red flags that usually mean the timelock is freezing the wrong object

  • The proposal contract was audited used as a substitute for committing exact later calldata
  • The address is queued with no statement about runtime hash, implementation pointer, or delegatecall behavior
  • Users can always inspect the proposal on the UI when the executor actually honors opaque bytes or mutable module behavior
  • The timelock operation id is enough even though materially different executions can still be reached through the same queued shell
  • We can upgrade the executor separately as if that did not change the meaning of the queued bundle
  • The delay gives us time to react when defenders cannot machine-check what exact root action is scheduled
  • The action bundle is now precise, so governance is safe even though the governor’s own denominator, proposer-admission rules, or emergency-repair lane may still fail before the delay expires

Design questions worth asking first

  • What exact machine-readable object did governance freeze for later execution?
  • Can a reviewer reconstruct the full ordered action set from the onchain commitment alone?
  • Does the queue hash or operation id bind calldata, value, order, and dependency edges tightly enough that another materially different effect set cannot hide behind it?
  • If external proposal contracts or modules are involved, what proves the same code will still be there at execution time?
  • Which admin or upgrade paths can still change the meaning of the queued bundle after governance approval?
  • Can the governor’s own threshold arithmetic, proposer eligibility, cancellation rights, or emergency-repair lane survive until execution time, or can adjacent economic state kill constitutional reachability before the exact delayed bundle matters?
  • Can monitors and veto mechanisms inspect the same object the executor will later honor, or only a lossy description of it?

Failure-prevention checklist

  • Queue one exact action bundle rather than a shell: ordered targets, values, calldata bytes, execution primitive, predecessor or dependency edges, salt / nonce, and the timing window that governs when this bundle is executable.
  • Make the queue hash or operation id derive from that full effect object. If materially different privileged stories can still hide behind the same queued label, the timelock has authenticated the wrong thing.
  • Keep delegatecall out of proposal-controlled executors unless the callable surface is tiny, pinned, and machine-verifiable. If the proposal runs in timelock or governor storage, it is root code.
  • Pin code identity whenever the delayed bundle depends on proposal contracts, modules, libraries, or proxy implementations. Runtime hash, implementation hash, or another equally stable code commitment should be part of the delayed object.
  • Revalidate execution-time assumptions before dispatch: expected code hash, expected implementation pointer, expected target set, expected value transfers, and expected call primitive should still match the queued bundle.
  • Derive human review views from the committed bytes. Forum posts, UI summaries, and governance descriptions should explain the same object the executor will later honor, not a parallel softer story.
  • Treat proposal factories, module registries, plugin managers, and proxy admins as part of the same authorization object when they can still change what the delayed bundle means after approval.
  • Simulate or fork-trace the final queued object, not only source repositories or pre-queue drafts. Review should answer what exact privileged writes and transfers can this delayed bundle still produce now?
  • Give defenders a real cancellation, veto, or pause path when post-queue drift is detected. A timelock helps only if the system can still act on discovered ambiguity before execution lands.
  • Check constitutional half-life as well as payload precision: the protocol should know whether proposal thresholds, quorum base, proposer eligibility, and emergency-repair survivability remain intact for the whole delay window rather than only at queue time.
  • Bound blast radius anyway: privilege tiering, staged upgrades, rate limits, and post-execution sanity checks should assume one under-committed delayed bundle will eventually get through.

Audit prompts

  • What exact object does this timelock authenticate: ordered call bundle, proposal address, operation id, module install, or some mixture of those abstractions?
  • Can a reviewer reconstruct the full privileged effect set from onchain governor or timelock state alone, including order, calldata, value, execution primitive, and dependency edges?
  • Could the queued object resolve to different behavior at execution time through proxy upgrades, implementation-slot drift, module rebinding, factory deployment, or other post-approval mutability?
  • Does any governance-approved path still rely on delegatecall, plugin installation, or another primitive that quietly expands authority beyond the reviewed top-level calls?
  • Are proposal UIs and monitor feeds rendering the same committed bytes the executor will honor later, or only a description that omits nested calls, code identity, or privilege shape?
  • What exact execution-time checks prove that the queued bundle is still the same bundle governance reviewed, and where would a mismatch be caught before funds or upgrade authority move?
  • Which admin, upgrader, registry owner, factory, or deployer can still change the meaning of the queued bundle after approval but before execution?
  • If a proposal depends on an external executable contract, what proves the same code object is still present at execution time rather than only the same address label?
  • If a hidden branch were discovered halfway through the timelock window, who can stop execution and how quickly relative to the remaining delay?
  • Does the protocol know whether the governor itself will still be constitutionally reachable when the delay clears, or could rebases, supply drift, migration-side accounting, or other nearby state changes kill the repair path first?
  • Would this approval surface still look acceptable if reviewers had to sign off on the exact action bundle bytes rather than the current higher-level abstraction? If not, the delayed object is underspecified.

Lessons that should already be standard

  • A timelock on a shell is not a timelock on effects. Delaying this proposal, this executor, or this operation id only helps if those labels already map to one exact privileged action bundle.
  • Delegatecall turns governance review errors into root-authority errors. Proposal code executed inside governor or timelock storage should be treated with the same suspicion as a live upgrade.
  • Human-readable proposal descriptions are commentary, not commitments. The real security object is still bytes, execution primitive, code identity, and dependency constraints.
  • Code identity is part of delayed authorization whenever external executors, modules, or proxies remain in the path. Same address later is not the same thing as same privileged behavior later.
  • A healthy timelock narrows uncertainty over time. As a proposal moves from discussion to queue to execution, the set of possible privileged effects should shrink toward one machine-checkable story.
  • Proposal factories, module registries, and upgrade admins above the timelock are part of governance security, not peripheral tooling. They can rewrite what the delayed bundle means.
  • Monitors need one machine-checkable object to defend. If defenders must reconstruct the real effect set from forum context, source links, and mutable deployments, the protocol has already made delayed execution too social.
  • Exact delayed payload commitment is still not constitutional-liveness design. We know precisely what will execute later is weaker than the governor and emergency-repair lane will still exist later.
  • Governance safety is not only about who can vote; it is also about whether the thing being approved is precise enough that an honest vote corresponds to one exact future privileged effect set.

How this note complements the exploit corpus

Negative exemplar cluster: what weak action commitment looks like

  • Tornado Cash governance proposal-smuggling takeover (2023-05-20) preserves the sharpest timelock delayed a proposal address whose code identity did not stay fixed branch.
  • Furucombo preserves the adjacent approved address stayed stable while the executable meaning behind it drifted branch outside classic token-vote governance.
  • The common lesson is that delay by itself is not a commitment mechanism. A timelock only protects the protocol if the delayed object is already the exact thing later execution is allowed to do.

Internal linkages

Control surface