Upgrade Patterns

Transparent Proxies

Since all function calls in Solidity are identified by the function selector, there is a low but non-zero possibility of an implementation contract having a function that has the same 4-byte identifier as the proxy’s upgrade function. This could cause an admin to inadvertently upgrade a proxy to a random address while attempting to call a completely different function provided by the implementation.

This issue can be solved by setting up such that the admin can only call upgrade management functions, and all other users can only call functions of the implementation contract.

The transparent pattern has a downside: gas cost. Each call requires an additional read from storage to load the admin address. The contract itself is also relatively expensive to deploy compared to other proxies, at over 700k gas.

https://blog.openzeppelin.com/the-transparent-proxy-pattern/

Universal Upgradeable Proxy Standard (UUPS)

As an alternative to transparent proxies, EIP1822 defines the universal upgradeable proxy standard, or UUPS for short. This standard uses the same delegate call pattern, but places upgrade logic in the implementation contract instead of the proxy itself.

Since the proxy uses delegate calls, the implementation contract always writes to the proxy’s storage instead of its own. The implementation address itself is kept in the proxy’s storage. UUPS proposes that all implementation contracts extend from a base proxiable contract:

contract UUPSProxy {
    address implementation;
    
    fallback() external payable {
        implementation.delegatecall.value(msg.value)(msg.data);
    }
}
abstract contract UUPSProxiable {
    address implementation;
    address admin;
    
    function upgrade(address newImplementation) external {
        require(msg.sender == admin);
        implementation = newImplementation;
    }

Last updated