Advanced level
Most DeFi contracts can be changed. A proxy holds the funds and forward every call to an implementation contract whose address sits in a known storage slot, so whoever controls that slot controls the code. The questions that matter are who holds that power, how many keys it takes, and how long the delay is before a change takes effect. A timelock only protects you if it is longer than the time you need to exit.
Every protocol has a "who" and a "how fast"
Few DeFi protocols are entirely unchangeable, and for good reasons: bugs need fixing, parameters need tuning, markets need listing. So every serious protocol has powers held by someone. Your job as a user is to know, for each power, three things: who holds it, how many keys it takes, and how long between the decision and the effect.
| Power | Typical holder | What it can do to you |
|---|---|---|
| Upgrade the code | Governance behind a timelock, or a multisig | Anything at all, including moving deposits |
| Change parameters | Governance, risk stewards within bounds | Liquidate you by lowering a threshold; change your rate |
| Change the oracle | Governance or an admin | Reprice your collateral |
| Pause or freeze | A guardian multisig | Stop withdrawals or new borrowing for a while |
| Mint, freeze or blacklist | The token issuer | Dilute holders; freeze an address |
None of these is bad in itself. A fast pause can save a protocol during an exploit; a slow upgrade gives users time to leave. The risk is in the combination: a powerful role, few keys, no delay.
How proxies make contracts upgradeable
Deployed bytecode cannot change. Upgradeability is an indirection: users talk to a proxy that
holds all the storage and funds, and the proxy forward each call with delegatecall to an
implementation contract. Delegatecall runs the implementation’s code against the
proxy’s storage, so replacing one stored address swaps the logic while every balance stays put.
Transparent proxy
The upgrade functions live in the proxy. Calls from the admin can only upgrade; calls from everyone else always go to the implementation. OpenZeppelin’s version routes admin calls through a separate ProxyAdmin contract.
UUPS
The proxy is minimal and the upgrade function lives in the implementation, named after ERC-1822 (a proposal marked stagnant, though the pattern is widely used). Cheaper, but an implementation without a working upgrade function freezes the contract forever.
Beacon
Many proxies ask one beacon contract for their implementation. One transaction on the beacon upgrades every proxy at once, which is efficient and concentrates power accordingly.
OpenZeppelin’s proxy documentation compares the three in detail. Whatever the pattern, the practical question is identical: where is the implementation address stored, and who can write to it?
EIP-1967: where the implementation address lives
Early proxies each stored the implementation wherever they liked, which made them hard to inspect and prone to storage collisions with the implementation’s own variables. ERC-1967, created in 2019 and now final, standardizes the slots as hashes of fixed labels minus one, so no normal Solidity variable can land there and the preimage of the slot is unknown:
# EIP-1967 slots are hashes minus one (Python, pycryptodome)
from Crypto.Hash import keccak
def slot(label):
h = keccak.new(digest_bits=256); h.update(label.encode())
return hex(int(h.hexdigest(), 16) - 1)
slot("eip1967.proxy.implementation")
# 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
slot("eip1967.proxy.admin")
# 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
slot("eip1967.proxy.beacon")
# 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50The standard also defines events (Upgraded, AdminChanged,
BeaconUpgraded), which is why an explorer can show a full upgrade history and a "Read as Proxy"
tab. You can read the slot yourself on any contract:
# Read the implementation slot of any contract over JSON-RPC
curl -s -X POST "$RPC" -H 'content-type: application/json' --data '{
"jsonrpc": "2.0", "id": 1, "method": "eth_getStorageAt",
"params": ["<contract address>",
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"latest"] }'
# The last 20 bytes of the result are the implementation address.
# All zeros means "not an EIP-1967 proxy", which is not the same as "not upgradeable".Two real cases show the limits of that check, both read in September 2026. Aave v3’s Pool on Ethereum stores its implementation in the EIP-1967 slot, but its admin slot is empty, because the admin is baked into the proxy’s bytecode as an immutable. And USDC’s proxy predates the standard: its EIP-1967 slot is empty and the implementation lives in an older OpenZeppelin (zeppelinos) slot. An empty slot tells you where not to look, nothing more.
Owners, admins, pausers and guardians
Beyond upgrades, contracts gate functions behind roles: an owner, an access control role, or a
named guardian. Well designed systems make fast powers narrow and broad powers slow. Aave is a useful example:
in April 2026, after an attacker deposited stolen rsETH as collateral, its Protocol Guardian began freezing rsETH
markets across every deployment within about 90 minutes of the exploit, and a risk steward adjusted interest rate
curves within preset bounds. Upgrading the protocol itself goes through governance.
Look for the same structure everywhere, including in your dependencies. The Chainlink ETH / USD feed proxy on Ethereum, which many protocols price collateral with, was owned by a 4 of 9 multisig as of September 2026. That is part of the trust you accept when a protocol reads it (see oracles).
Multisig thresholds and signer identity
An m of n multisig needs m signatures to act. It also has a second number people forget: how many keyholders it takes to block an action, which is n − m + 1.
| Setup | Keys to act (or steal) | Keys to block | Threshold |
|---|---|---|---|
| 2 of 3 | 2 | 2 | 66.7% |
| 3 of 5 | 3 | 3 | 60.0% |
| 4 of 9 | 4 | 6 | 44.4% |
| 5 of 9 | 5 | 5 | 55.6% |
| 9 of 12 | 9 | 4 | 75.0% |
The numbers are necessary and nowhere near sufficient. What matters is whether the keys are independent. The Ronin bridge required 5 of 9 validator signatures, but one organization controlled four validators and still held permission to sign for a fifth, so one compromise was enough (the Ronin post-mortem has the details). In the Bybit theft of February 2025, every signer approved what their interface showed as a routine transfer, and the transaction they actually signed replaced the wallet contract’s implementation. Same keys, same interface, same failure. The multisig guide covers setting one up; for protocols, ask who the signers are, whether they are publicly named, and whether they verify on independent hardware.
Timelocks and the exit window
A timelock is a contract that holds admin power and enforces a delay: an action is queued, becomes executable only after the delay, and expires if not executed within a grace period. Compound’s Timelock, which administers Compound v2 and the Compound III markets on Ethereum (markets on other chains take bridged proposals through their own local timelocks), enforces a minimum delay of 2 days, a maximum of 30 and a 14 day grace period; its delay was set to 2 days as of September 2026. Governance voting adds more days before anything is even queued.
The second term is where people fool themselves. Closing a leveraged loan, unwinding a liquidity position, or withdrawing from an optimistic rollup through its canonical bridge (about seven days) all take time. A 2 day timelock on a rollup bridge whose withdrawals take 7 days gives users no exit window at all: the upgrade lands while their funds are still in the pipe. A delay protects you only if it is longer than your exit, and only if someone is watching the queue. Services and bots that alert on queued transactions are what make a timelock useful in practice.
Immutable contracts and their tradeoffs
The alternative is to give up upgrades. Morpho’s core lending contract is not a proxy: its owner can approve new interest rate models and liquidation LTVs and set a fee of at most 25% of interest, but no one can change a market’s oracle, collateral or parameters once it exists. Many protocols take the same route for their core and keep upgradeability only in the periphery.
Immutable core
No one can change the rules
- No admin key to steal, bribe or coerce
- What you audit is what runs, forever
- A bug cannot be patched; users must migrate
- Parameters cannot adapt to new markets
Upgradeable
Governance can change the rules
- Bugs can be fixed before they are exploited
- Risk parameters can respond to markets
- Every user trusts the upgrade path
- A compromised key can rewrite everything
Rollups: the L2BEAT stages
Layer 2s are the largest upgradeable contracts most users touch, because the canonical bridge holds the funds. L2BEAT’s stages framework grades how far control has moved from people to code. As published on its stages page in September 2026:
| Stage | Core requirement |
|---|---|
| Stage 0 | Calls itself a rollup, posts state roots and data on L1, has open source software that can rebuild the state, and has a proper proof system (for fraud proofs, at least 5 external actors able to submit them) |
| Stage 1 | The only way (other than bugs) to indefinitely block a withdrawal or push an invalid one is to compromise at least 75% of the Security Council. Upgrades by anyone else must give users at least a 7 day exit window |
| Stage 2 | Permissionless fraud proofs, at least 30 days to exit before unwanted upgrades, and a Security Council limited to acting on bugs detectable onchain |
L2BEAT also expects a Security Council of at least 8 publicly announced members, at least half from outside the core team, and since a January 2025 update it applies a high level guiding principle to Stage 1 that also bears on how short an optimistic rollup’s challenge period can be. As of September 2026 its listings put Base, Arbitrum One and OP Mainnet at Stage 1, while the few Stage 2 projects held little value. Two caveats from L2BEAT itself: stages measure decentralization, not freedom from bugs, and the requirements evolve (a December 2025 proposal would require the Stage 1 property to hold even if the Security Council went permanently inactive). The deeper mechanics are in rollup architecture, and the chains themselves on the layer 2 pages.
Checking all of this on a block explorer
Is it a proxy?
Open the contract on an explorer. A "Read as Proxy" tab or an implementation link means yes. Confirm by reading the EIP-1967 slot; if it is empty, check the docs for an older pattern before concluding anything.
Who is the admin or owner?
Call
owner(), or read the admin slot, or find the ProxyAdmin. Follow the address: is it a person’s wallet, a multisig, a timelock or a governor?If it is a multisig, read it
For a Safe,
getThreshold()andgetOwners()give the m and n. Then look for who the signers are in the protocol’s docs or governance forum.If it is a timelock, read the delay
getMinDelay()on an OpenZeppelin TimelockController,delay()on a Compound style Timelock. Then check which roles can propose, execute or bypass it.Read the history
Filter the contract’s events for
Upgradedand admin changes. How often has the code changed, and did each change go through the delay? The guide on reading a transaction shows how to decode what each one did.
Common questions
What is a proxy contract?
A contract that holds the storage and funds but forward every call to a separate implementation contract using delegatecall. Changing the stored implementation address changes the logic without moving any funds, which is how most DeFi protocols upgrade.
Does an empty EIP-1967 slot mean a contract cannot be upgraded?
No. It means the contract is not a standard EIP-1967 proxy. Older proxies such as USDC’s use a different slot, and some contracts gate behavior behind roles without being proxies at all. Check the docs and the verified source.
How long should a timelock be?
Longer than the time users need to exit, with a margin for noticing. For simple deposits a few days can be enough. For positions that take a week to unwind, such as optimistic rollup withdrawals, a short timelock offers little real protection.
What is the difference between Stage 0, 1 and 2 on L2BEAT?
Stage 0 rollups are fully controllable by their operators. Stage 1 rollups can only be overridden by a Security Council supermajority, and other upgrades give a 7 day exit window. Stage 2 rollups have permissionless proofs, a 30 day exit window, and a Security Council limited to onchain detectable bugs.
Is an immutable protocol always safer?
It removes key risk, but a bug can never be patched, only escaped by migrating. Many teams choose an immutable core with upgradeable peripheral contracts to get most of both benefits.
Where to go next
Next: Reading a transaction like an analyst
Guide 13 of 16 in the Advanced level. Your place is kept on this device, so you can pick up exactly here next time.