Upgradeable Proxies Explained in Detail
An upgradeable proxy is a contract that stores state and delegates execution to a separate implementation contract. Users call the proxy address. The proxy uses delegatecall, so implementation code runs against the proxy storage.
Smart contract example
A token proxy points to TokenV1. Later, governance upgrades the proxy to TokenV2.
The token balances remain in the proxy storage. Only the implementation address changes.
fallback() external payable {
address impl = implementation;
assembly {
calldatacopy(0, 0, calldatasize())
let ok := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch ok
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
Upgradeable Proxies in Auditing
Upgradeability changes the trust model: safe logic today can be replaced with unsafe logic later.
Auditors need to review who can upgrade, how upgrades are validated, whether initializers are safe, and whether storage layout remains compatible.
Red flags in code
-
Upgrade function callable by weak or unclear authority.
-
Proxy admin is an EOA with no timelock or multisig.
-
Implementation contains unsafe
delegatecall. -
Proxy and implementation use custom storage slots without clear EIP-1967 compatibility.
-
No event emitted on upgrades.
-
Users interact directly with the implementation contract.
How to test or review it
-
Check the proxy type first: transparent, UUPS, beacon, or custom.
-
Verify the implementation slot, admin slot, upgrade authorization, and initialization path.
-
Compare storage layouts across all versions and check for storage collision.
-
Test that unauthorized users cannot upgrade.
-
Test that the implementation cannot be initialized directly.
Keep learning this topic
Delegatecall
Delegatecall executes code from another contract while reading and writing the caller's storage, preserving the original caller context.
Initializer Function
An initializer is a one-time setup function used instead of a constructor when a smart contract is deployed behind an upgradeable proxy.
Storage Collision
A storage collision happens when two variables or contracts use the same storage slot, corrupting state in upgradeable or delegatecall-based systems.
Proxy Initialization
Proxy initialization is the setup step that assigns initial state for an upgradeable proxy, usually through an initializer function instead of a constructor.
Access Control Attacks
Access control attacks in Solidity: broken authorization patterns, privilege escalation paths, and secure role and ownership design.
Delegatecall & Call Injection Attacks
Delegatecall and call injection attacks in Solidity: storage collision exploits, proxy vulnerabilities like Parity, and secure upgrade patterns.
Smart Contract Audit Checklist
Use this SCH tool to turn the concept into practical audit work.
Practice this in real audit scenarios
Definitions help, but auditors need reps. SCH turns concepts like Upgradeable Proxy into exploit labs, code review habits, and report-writing practice.
Start the free trial or see the full smart contract auditing course.