Solidity

Upgradeable Proxy

An upgradeable proxy is a smart contract pattern where users call a stable proxy address while execution is delegated to replaceable implementation logic.

The address stays the same, but the code behind it can change.

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.

Sources