UUPS Proxy Explained in Detail
A UUPS proxy is an upgradeable proxy pattern where the proxy delegates calls to an implementation, but the implementation contains the upgrade function and authorization logic.
This differs from a transparent proxy, where admin and upgrade logic live in the proxy or proxy admin contract. In UUPS, a bad implementation can break or expose upgrades because the implementation controls the upgrade path.
Smart contract example
The critical function is usually _authorizeUpgrade:
contract VaultV2 is UUPSUpgradeable, OwnableUpgradeable {
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
If onlyOwner is missing, the proxy owner was never initialized, or the wrong account controls ownership, an attacker can upgrade the proxy.
UUPS Proxy in Auditing
UUPS combines delegatecall, proxy storage, initialization, and access control. A single mistake can produce permanent takeover, bricked upgrades, or storage collision.
Auditors focus on who can upgrade, whether the implementation is initialized or locked correctly, and whether each new implementation preserves storage layout and UUPS compatibility.
Red flags in code
-
_authorizeUpgradeis empty, weak, or tied to uninitialized ownership. -
Implementation contract can be initialized by anyone.
-
Upgrade functions remain callable directly on the implementation or through a proxy path that was not intended to expose upgrades.
-
New implementation removes UUPS compatibility or changes storage layout unsafely.
-
UUPS implementation is used behind a transparent proxy without reviewing mixed-pattern behavior.
How to test or review it
-
Attempt upgrades as owner, non-owner, proxy admin, and random contract callers.
-
Verify the implementation contract calls
_disableInitializers()when appropriate. -
Compare storage layouts before and after every upgrade.
-
Verify
_authorizeUpgradeis tested through the proxy, not only by calling the implementation contract. -
Review upgrade authority alongside multisig and timelock controls.