An upgrade timelock protects users only when every implementation change must pass through it. A multisig helps distribute approval, but its threshold says little about an enabled module, an old administrator or an emergency role that can replace the code directly. Start by mapping those paths. Give routine upgrades a reviewed, delayed route and give incident responders a narrower power to stop specific operations. Then prove that neither route can silently acquire the other's authority.
This guide proposes a control matrix for EVM protocols using upgradeable contracts. Its thresholds and timing examples are hypothetical design inputs, not universal recommendations or reported client results. AI assisted the drafting and the conceptual cover; the acceptance exercises below are proposed tests, not a report of tests run against a deployed protocol.
Put every privileged action in one control matrix
Treat authority as a relationship between an actor, a target and a permitted state transition. A role name is insufficient: an emergency operator who can grant an upgrader role possesses indirect upgrade power. A reviewer needs both the direct call and the administrative path that can make the call possible.
For an example vault, assume a governance multisig proposes routine changes, a timelock owns upgrade authority and a separate guardian can pause new deposits. The application must actually support that pause scope. Unpausing follows the normal governance route. A cancellation role can stop queued operations, subject to the limits discussed below.
| Privileged action | Actor and target | Delay | Veto | Emitted evidence | Recovery path |
|---|---|---|---|---|---|
| Replace implementation | Governance proposes; timelock calls upgrade gate | Configured upgrade delay | Canceller before execution | Scheduled operation, execution, implementation event | Corrected upgrade or tested migration |
| Pause deposits | Guardian calls scoped pause function | Immediate after authorization | No pre-execution veto | Application pause event | Governed unpause after verification |
| Unpause deposits | Timelock calls scoped unpause function | Chosen recovery delay | Canceller before execution | Scheduled operation and unpause event | Pause again if the fault returns |
| Rotate application roles | Timelock calls the relevant role administrator | Administrative delay | Canceller before execution | Role grant and revocation events | Previously documented role-transfer route |
| Change timelock delay | Timelock calls itself | Existing minimum applies to scheduling | Canceller before execution | Scheduled operation and delay-change event | Another delayed configuration change |
| Change multisig owners or threshold | Authorized Safe transaction changes the Safe | No extra delay assumed | Only constraints actually installed | Owner and threshold events | Tested surviving-owner or recovery route |
| Move assets during recovery | Separately authorized, narrowly scoped recovery function | Explicit policy for that action | Depends on implemented route | Recipient, asset and amount evidence | Reconciliation; no automatic reversal |
Replace each actor label with a chain-specific address and each delay with the deployed value. Record absent controls as absent. If an asset-recovery function does not exist, its row describes a design question rather than an available escape route. The matrix should expose those differences before a production incident makes them urgent.
For a team confronting undocumented administrative paths, Pharos Production provides smart-contract engineering; its published upgrade process includes documented procedures and fork rehearsals. That process is relevant to making an authority map executable. The matrix here is a proposed review artifact, with no claim that a particular customer deployment already satisfies it.
Illustrative authority paths. Diagram constructed with code and AI assistance; actual contracts must enforce the separation.
Trace the upgrade gate to its actual owner
Begin at the address users call. Resolve the implementation and identify the proxy pattern from verified code and deployment evidence. Then follow the authorization check outward until it reaches the accounts or governance mechanism that can satisfy it. Stop only when every branch has an identified controller.
In OpenZeppelin's transparent pattern, inspect the associated ProxyAdmin and its owner. For UUPS, inspect the implementation's _authorizeUpgrade logic and the state that makes that check pass through the proxy. For a beacon arrangement, inspect the beacon's controller and the full set of dependent proxies. The OpenZeppelin proxy reference documents these distinct mechanisms; a familiar address label does not establish which one a deployment uses.
Imagine the timelock controls a ProxyAdmin, while an application administrator can change the contract that authorizes upgrades. The first observation looks reassuring. The second may create another route to the same power. Draw both edges, including any ability to change role administrators, replace a registry or execute arbitrary calls.
ERC-1967 specifies implementation, beacon and admin storage slots and recommends events for changes. Use those as inspection aids, then compare them with the actual contract. Custom proxies and version-specific designs require their own interpretation. A storage read is evidence about that slot at one block, not a complete account of future authority.
For each network, retain the inspected block number and hash. The same application name on two chains can hide different guardians, signer sets or delay settings. An authority diagram without a network and observation point cannot show which deployment the reviewer accepted.
Also inspect the powers that leave the implementation address unchanged. A controller might replace an oracle, alter a withdrawal recipient or raise a minting allowance. Those actions can change the system's economic behavior without invoking an upgrade function. Put them in an adjacent parameter-authority inventory and decide whether they require the same review window. Otherwise, a narrowly correct upgrade policy can coexist with an immediately callable action that creates comparable harm.
Make the timelock bind a specific operation
A delay has value when observers can identify the exact change awaiting execution. Record the target, value, calldata, predecessor and salt used to derive the operation identifier. Decode nested calls, including the implementation address and any initializer data. Link the readable explanation to those bytes so an edited description cannot silently redefine approval.
OpenZeppelin's TimelockController API distinguishes scheduling from execution and cancellation. Execution requires a ready operation and, when specified, a completed predecessor. The contract's minimum delay is a floor; an operation can be scheduled with a longer delay. Granting executor access to the zero address allows anyone to execute an eligible operation. It does not authorize them to invent an unscheduled payload.
The example matrix assumes governed timelock administration. Check the actual admin set, including deployment-time privileges. A separate administrator may be able to change role membership outside the intended governance procedure. OpenZeppelin describes self-administration and its liveness trade-offs in its access-control guide. Remove bootstrap authority only after the permanent proposal and execution routes have been verified.
Suppose a reviewed upgrade is ready but the execution transaction fails because the migration encounters an unexpected account state. A ready timestamp does not make a failing payload correct. Preserve the failure evidence, determine whether the same operation remains executable and cancel it when the plan changes. Replacing calldata requires a newly identified operation and the applicable review cycle.
Keep a separate operational expiry for review evidence. A queued operation may still be technically executable after its simulation or approval context becomes stale. If the contract has no maximum execution window, an internal document cannot enforce one against another authorized executor. Cancel stale operations on-chain or implement the required expiry constraint before relying on it.
Size the delay around an available response
Choosing forty-eight hours because another protocol uses it leaves the central question unanswered: what can an affected person accomplish during that interval? Write down who detects a proposal, who interprets it and what meaningful action remains available before execution.
Consider a hypothetical delay of forty-eight hours. Detection takes six hours, technical review takes twelve and an allowed withdrawal takes thirty-six. A sequential response needs fifty-four hours before any contingency margin. That configuration cannot support the claimed exit window. The arithmetic describes this scenario only; real response paths may overlap or face additional constraints.
Include weekends, signer availability and the dependencies of the exit transaction. If withdrawals require an operator signature, that operator must be part of the response model. If pausing deposits also blocks withdrawals, the emergency setting changes the meaning of the timelock. Show that consequence in the incident runbook and user-facing control description.
Different changes can justify different waiting periods. A routine parameter adjustment and a replacement implementation need not share the same risk assessment. However, a policy document cannot create per-action enforcement by itself. Explain which delays the deployed contracts enforce and which longer waits remain a governance commitment.
An emergency may require stopping exposure faster than an implementation review can finish. A predesigned pause function can provide that response while a correction follows the upgrade path. When the exploit is outside the pause scope, describe the residual exposure explicitly. Making the delay smaller after an incident begins is not a substitute for having designed an effective containment action beforehand.
Review the multisig as a system of permissions
A three-of-five threshold means three valid owner approvals are needed for the ordinary owner transaction path. It does not prove that three independent people reviewed the change. Two keys held by one person, a shared recovery account or a common signing workstation can reduce the practical independence of the set.
Assign each signer a concrete verification task. One can reproduce the target and calldata, another can inspect the migration evidence and another can compare the live authority state with the reviewed snapshot. The assignments do not replace complete understanding, but they make three identical clicks less likely to pass for three distinct checks.
Safe's smart-account concepts describe owner transactions and module transactions as separate paths. Enabled modules can execute through the Safe under their own authorization logic. That makes the module inventory part of the authority review, even when a dashboard prominently displays the owner threshold.
The Safe documentation states:
A malicious module can take over a Safe.
Attribution: Safe, Safe Modules. This warning belongs beside the signer review because adding a module can change what the owner threshold actually protects.
For every enabled module, identify its controller, permitted targets and ability to change its own policy. Inspect guards and recovery mechanisms as well. Safe's guard documentation warns that a broken or malicious guard can block transaction execution. Verify which transaction paths a particular guard covers; do not infer that one guard constrains every module route.
The smart-contract upgrade procedures described by Pharos Production include timelocks, multisigs and fork rehearsals. For this review, the useful application is to rehearse the complete authority chain, including account extensions, rather than testing only the final upgrade function. No signer count makes that rehearsal unnecessary.
Give emergency controls a narrow state transition
Define an emergency action by the exposure it stops. Pausing new deposits can prevent additional funds entering a vulnerable path. Disabling a particular borrowing route can contain a pricing problem. Neither description establishes what happens to existing positions, withdrawals or integrations that call another entry point.
Write the allowed transition precisely: normal deposits become disabled, while a separately verified withdrawal path remains available. Enumerate the selectors affected by that transition and the paths that remain active. If the contract cannot enforce this distinction, adjust the claim and evaluate the consequences of its actual global pause behavior.
The guardian should not inherit arbitrary execution, implementation replacement or role-administration power merely because incident response is urgent. A narrow pause role reduces the set of harmful changes that a compromised guardian can make. It can still cause an outage, so the architecture also needs a way to remove or replace it.
Unpause deserves its own acceptance condition. The team should establish which defect was corrected, which state was inspected and which operations were rehearsed before reopening. A separate unpause route can reduce the chance that the same compromised emergency key immediately restores the vulnerable path.
Some protocols deliberately give an emergency council upgrade authority. Document that as a second upgrade path, with its own authorization requirements and complete blast radius. Calling it an emergency control does not make its implementation-changing power smaller. Users and reviewers need to know whether it can bypass the routine delay.
Avoid implying that the pause flag blocks every administrative action. An upgrade function, another contract or an alternate routing path may remain callable. Test the relevant behavior through each public entry point. The operational question is what stops in the deployed system, including the operations the team intended to preserve.
A pause can also shift risk to another participant. An integration may keep accepting deposits while its downstream vault rejects them, leaving funds waiting in an intermediary. Map that intermediate state, its refund path and the party responsible for communicating the interruption. The contract event reports that a switch changed; it does not prove the surrounding product has stopped offering the affected operation.
Separate migration safety from authorization
An authorized upgrade can still corrupt state. Review the change to storage and initialization independently of the decision about who may execute it. OpenZeppelin's upgradeable-contract guidance explains storage-layout restrictions and initializer handling. Pin the dependency version used by the project and apply the matching validation workflow.
Bind the release packet to the implementation address, deployed runtime code and initialization payload. Include the compiler configuration and relevant linked libraries. A source commit is useful provenance, but it cannot alone demonstrate that the address in the scheduled transaction contains the reviewed build.
Use a rehearsal state that includes awkward accounts. For a vault, that might mean outstanding withdrawal requests, accrued fees and positions near a limit. Check value-preservation and access-control properties after migration. Include at least one user flow through the proxy, since calling the implementation directly does not exercise the same storage context.
Suppose the new implementation appends a field and initializes it successfully, then later user transactions populate new state. Returning the implementation pointer to the previous version does not reverse those writes. A rollback plan therefore needs a state-compatibility argument and an explicit treatment of activity that occurred after the upgrade.
When that argument cannot be made, recovery may require a forward fix or a separately authorized migration. Record the decision before execution. An old implementation address in a runbook is only a potential target; it is not evidence that restoring it will preserve balances or restore the intended permissions.
Rehearse lost keys and hostile cancellation
Availability failures belong in the authority design. If a governance multisig loses enough owners to miss its threshold, the team may be unable to schedule the very transaction that would restore control. If a self-administered timelock has no usable proposer, waiting longer will not manufacture one.
Distinguish owner rotation inside a Safe from replacing that Safe as a timelock role holder. They involve different authorization paths. A surviving Safe quorum may rotate an owner using the Safe's controls. Replacing an unavailable proposer requires another route that already has sufficient authority in the timelock. The matrix must show which route exists.
A canceller introduces a different liveness risk. An actor able to cancel pending governance operations may also cancel an operation intended to remove that actor's cancellation privilege. A delay therefore does not guarantee that a hostile canceller can eventually be removed. Do not put that promise in the recovery column unless the deployed design actually supports it.
Resolve the trade-off explicitly. Some systems accept that veto power as part of their governance model. Others constrain cancellation or establish a separate, carefully bounded recovery mechanism. Every extra mechanism adds another authority path to inspect. A secret recovery key cannot count as both undisclosed and part of a transparent trust model.
Run the exercise with one unavailable signer and then with a compromised signer. Record what still works, who can interrupt a queued operation and whether the recovery route can itself be blocked. An exercise that ends with contacting an unnamed administrator has discovered missing governance, not completed recovery.
Rehearse voluntary retirement too. Removing upgrade authority can eliminate one future change path, but it may also eliminate the only route to correct a defect. Before that irreversible governance decision, identify which emergency powers remain and whether they can be retired separately. A permanently paused contract with no usable recovery function has very different consequences from an immutable contract whose normal user operations remain available.
Bind monitoring to the reviewed transaction
Prepare a compact operation record before gathering approvals. It should identify the chain, proxy and upgrade gate, then bind the implementation and calldata to the proposed state transition. Keep the observed authority configuration beside it so a reviewer can notice a changed signer set or a newly enabled module.
The minimum useful record contains the following fields:
- Network identifier, inspected block number and block hash.
- Proxy address, current implementation and actual upgrade controller.
- Proposed implementation, runtime-code hash and initialization calldata.
- Timelock operation identifier, predecessor, salt and earliest execution time.
- Proposer, executor, canceller, relevant role administrators and multisig configuration.
- Rehearsal inputs, expected postconditions and accepted recovery limitations.
Monitor configuration changes as well as proposal events. Owner rotation, threshold changes, role grants and module enablement can invalidate an earlier review without changing the upgrade calldata. Record what invalidates approval and who is responsible for canceling or replacing the queued operation when that happens.
After execution, compare both emitted events and resulting state with the expected change. A transaction receipt alone does not show that every intended postcondition holds. Read the implementation reference again, inspect critical roles and exercise the representative user path at an appropriate confirmation point for that network.
Keep failed and canceled attempts in the same history. They explain why a replacement operation exists and prevent a later operator from reviving an obsolete plan. Monitoring should lead to a defined response: notification, cancellation, containment or renewed review. A dashboard that only records the bad event after execution provides a different control from one that enables intervention beforehand.
Decide who receives an alert when the normal responder is unavailable. Store a plain-language interpretation alongside decoded transaction data so the fallback operator can distinguish a routine owner rotation from a newly opened execution path. That handover is part of making the delay usable.
Accept the design through adversarial questions
Review the matrix with the people expected to operate it. Start with a direct call from each privileged account and ask whether that call can change the implementation without the recorded delay. Repeat the question for role administration, Safe modules and any contract allowed to forward arbitrary calls.
Next, examine timing boundaries. An attempt before readiness should fail. An executed operation should not execute again as the same completed operation. A canceled operation should not remain immediately executable under its canceled schedule. Verify rescheduling behavior against the chosen implementation and check that monitoring distinguishes the replacement from its predecessor.
Challenge the emergency boundary separately. Prove the guardian can stop the intended exposure and cannot perform an unrelated administrative transition. Test that preserved withdrawals really remain possible in the paused state. Then exercise the approved unpause route with the corrected implementation and the actual authorization configuration.
Finally, remove an expected dependency from the rehearsal: a signer, a proposer, an execution service or the usual interface. Identify which operations remain available through documented tools and which become impossible. Preserve impossible outcomes as constraints requiring a design decision. They are more useful than a successful demonstration that quietly assumes every privileged actor is online.
The acceptance result should be a versioned matrix tied to a release and a deployment snapshot. Each recovery claim needs supporting evidence; each exception needs a named decision owner. That gives the next engineer a concrete basis for deciding whether an upgrade may proceed when the people, permissions or operating conditions have changed.
More insights to read
- Five Smart Contract Upgrade Patterns Compared
- Smart Contracts. Their Potential and Real Limitations. Part 1
- Upgradeable Solidity Smart Contracts. Part 1: Versioning
- Smart Contracts. Their Potential and Real Limitations. Part 2
- Web3. Smart Contracts. Oracles. Part 1
About the author
Dmytro Nasyrov. Photo supplied by the author.
Written by Dmytro Nasyrov PhD, software architect with 24 years of production experience. Dmytro is the founder and CTO of Pharos Production. He works on production software architecture for FinTech, AI, Web3 and blockchain systems.


Top comments (0)