new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Smart Contract Auditor · all subjects

Upgradeability and proxies

3 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

What is a storage collision in upgradeable contracts?

Proxy and implementation share one storage layout (the proxy's). If the new implementation declares variables in a different order or inserts a new one in the middle, every existing slot is reinterpreted — an address becomes a balance, an admin becomes a token. Real incident class: the Audius governance takeover (2022) traced to proxy/initialization misconfiguration, letting the attacker re-initialize and seize admin. Rules: append-only variables in upgradeable contracts; never reorder or change types; use storage gaps (`uint256[50] private __gap`) in base contracts meant for inheritance; namespaced storage (ERC-7201, `keccak256` "diamond-style" slots) for libraries. Detection signals: diff the flattened storage layout between versions — `openzeppelin-upgrades` plugins do this automatically (`validateUpgrade`, `storageLayout` in build info); manual audits should dump `solc --storage-layout` for both versions and compare. Also flag `delegatecall` to a user-supplied address anywhere — that plus storage control is instant full compromise.

How dangerous is delegatecall to a user-supplied address?

Critical, unconditionally — it's the single most dangerous pattern in Solidity. `delegatecall` executes foreign code in YOUR storage context: the callee can rewrite any slot, including the implementation pointer or owner. Parity multisig hack #2 (November 2017, ~$150M frozen forever): the library contract had an unprotected `initWallet`; the attacker initialized themselves as owner of the shared library and called `kill` (`selfdestruct`), bricking every wallet that `delegatecall`ed into it. Detection signals: `delegatecall` where the target comes from calldata, storage writable by non-admins, or a registry that can be updated without timelock; also library contracts with `selfdestruct` anywhere reachable. Fixes: hardcode/whitelist delegatecall targets; never expose a public function that delegatecalls arbitrary addresses; on the target side, no `selfdestruct` and locked initializers. In reports, one foundry test that overwrites slot 0 through the delegatecall is enough to prove critical severity.

What should I check in the proxy admin and upgrade flow itself?

Beyond code: (1) Who is `ProxyAdmin`/owner — EOA, multisig, or governor? EOA + instant upgrade = flag as centralization risk with concrete impact ("owner can replace implementation and drain all user funds in one transaction"). (2) Timelock: is there a delay between proposing and executing an upgrade so users can exit? (3) The upgrade PATH: does `upgradeToAndCall` let the admin atomically call `initialize` on the new implementation with attacker-chosen args? (4) Selector clashing in transparent proxies: a proxy function and implementation function with the same 4-byte selector cause silent misrouting. (5) For UUPS: the upgrade function lives in the IMPLEMENTATION — verify `upgradeToAndCall` is role-gated AND exists in the new implementation, or the proxy is bricked; simulate the upgrade in a fork test and assert balances/roles survive. Tooling: `@openzeppelin/upgrades-core` `validateUpgrade` catches layout breaks and unsafe `delegatecall`/`selfdestruct` — run it in CI, as of early 2026 it's the standard gate.

Give your agent this brain