Upgradeability Patterns and Vulnerabilities
Smart contracts are immutable by default. Upgradeability is a deliberate, engineered escape hatch from that property — and like every escape hatch, it introduces its own attack surface. The same mechanism that lets a team patch a vulnerability also lets a compromised admin replace the entire system with malicious code. Auditing an upgradeable system means auditing the contracts as they are today and the upgrade machinery that determines what they can become tomorrow.
This chapter covers what every auditor needs to know about upgradeable smart contracts:
- Proxy patterns — Transparent, UUPS, Beacon, and Diamond (EIP-2535) — their mechanics, trade-offs, and characteristic failure modes.
- Storage layout and collisions — how proxy and implementation storage interact, why naïve upgrades corrupt state, and the patterns (storage gaps, namespaced storage / ERC-7201) that mitigate it.
- Initializer pitfalls — front-running, reinitialization, missing
_disableInitializers(), and the ways constructors leak through proxies. - Function and selector clashes — when proxy admin functions collide with implementation functions, hiding methods or worse.
- Governance and authorization of upgrades — who can upgrade, how fast, with what oversight, and what users can do if they disagree.
- An audit checklist for upgradeable systems — the questions that should be answered before signing off on any upgrade pathway.
Why Upgradeability Is Risky
A non-upgradeable contract has a fixed worst case: whatever bugs are in the deployed code. An upgradeable contract has a worst case bounded only by what the authorized upgrader can do, which in most live protocols is "deploy arbitrary code at the existing address." That means:
- The trust assumption of the entire protocol effectively reduces to the trust assumption of the upgrade key.
- Users who interacted with the protocol under one set of rules can find themselves subject to a different set of rules with a single transaction.
- Static analysis of the current implementation does not bound the behavior users will face — only an analysis of the upgrade authority does.
A team that argues "upgradeability is necessary because we move fast" is also arguing "our users trust us as much as they trust an EOA we control." That trade-off can be reasonable; it must always be made explicit.
A Spectrum, Not a Switch
Upgradeability is not binary. Real systems sit somewhere on a spectrum from fully immutable to fully mutable:
| Model | Who can change behavior | Typical use |
|---|---|---|
| Immutable | No one | Settlement layers, high-stakes vaults that prefer hard forks over patches |
| Parameterized | Governance, within bounded ranges | Fees, oracle sources, risk parameters |
| Modular / plugin | Governance, by swapping a single module | Strategies, hooks, payment processors |
| Upgradeable implementation | Governance, by replacing the whole logic | Most large DeFi protocols |
| Fully mutable | Any holder of an EOA key | Pre-launch testnets, prototypes |
Auditors should identify where the system sits on this spectrum for each subsystem, not just for the contract as a whole. A protocol may have an immutable core, parameterized risk modules, and an upgradeable peripheral — each of which has a different threat model and warrants different scrutiny.
What an Upgradeable Audit Examines
For every upgradeable contract in scope, an audit should answer the following at minimum:
- What is the proxy pattern, exactly? Transparent, UUPS, Beacon, Diamond, or a custom design? Where is the implementation address stored?
- Who can upgrade? What roles exist, what address holds them today, and what governance process gates their actions?
- What is the upgrade timelock? Is there one? Is it long enough for users to exit?
- Is the storage layout safe across upgrades? Has the team committed to a layout discipline (gaps, ERC-7201 namespaces)? Is there a CI check?
- Is the implementation contract safe on its own? Has
_disableInitializers()been called in the constructor? Are initializer functions properly guarded? - Can the upgrade be front-run? Is the upgrade transaction observable, and can it be sandwiched or pre-empted?
- What is the recovery story? If the upgrader key is lost or compromised, what happens?
The subsections that follow address each of these questions in detail, with the patterns and failure modes auditors should be ready to recognize.