The UUPS proxy pattern is a design for making Ethereum smart contracts upgradeable, defined in EIP-1822. Unlike the traditional Transparent Proxy pattern, which stores upgrade logic in the proxy, a UUPS proxy delegates all calls to an implementation contract that also contains the upgradeTo(address) function. This makes the proxy contract itself simpler and more gas-efficient, as it contains minimal code—primarily just a fallback function for delegation. The key security consideration is that the implementation contract must be carefully designed to protect its own upgrade function.
UUPS Proxy
What is a UUPS Proxy?
A UUPS (Universal Upgradeable Proxy Standard) is a smart contract upgrade pattern where the upgrade logic is embedded directly within the implementation contract itself, rather than in a separate proxy contract.
A major advantage of the UUPS pattern is reduced gas cost for deployment and subsequent transactions. Since the proxy contract is extremely lightweight, users pay less gas when interacting with it. However, this design introduces a critical responsibility: the upgrade authorization mechanism (e.g., ownership, multi-signature) and the upgradeTo function logic must be implemented and secured within the logic contract. If this logic is flawed or removed in a subsequent upgrade, the contract can become permanently frozen, unable to upgrade further—a state known as "bricking."
Developers typically use UUPS proxies via libraries like OpenZeppelin's UUPSUpgradeable, which provides a secure, audited base implementation. The upgrade process involves deploying a new version of the logic contract and then calling the upgradeTo function on the proxy, pointing it to the new address. This pattern is widely used in DeFi protocols and DAO treasuries where the ability to fix bugs or add features is essential, but operational efficiency is also a priority. It represents a more modern and gas-optimized evolution from earlier proxy patterns.
Etymology and Origin
The UUPS proxy pattern emerged as a gas-optimized evolution of the established transparent proxy standard, directly addressing its inherent overhead in upgrade mechanisms.
The term UUPS is an acronym for Universal Upgradeable Proxy Standard. It was formally introduced in Ethereum Improvement Proposal EIP-1822, authored by Gabriel Barros and Patrick Gallagher. The proposal's core innovation was to relocate the upgrade logic from a dedicated proxy contract into the implementation contract itself. This architectural shift was a direct response to the gas inefficiencies identified in the earlier Transparent Proxy pattern, which required a separate proxy admin contract to manage upgrades.
The etymology of "universal" signifies its intended generality; the standard was designed to be a reusable, non-opinionated template for any upgradeable contract system. Unlike its predecessor, a UUPS proxy does not contain upgrade logic in its bytecode. Instead, it delegates all calls, including upgrade calls, to the implementation contract, which must contain a function (e.g., upgradeTo(address)) authorized to update the proxy's stored implementation address. This design makes the proxy itself a simpler, more minimal contract.
The origin of the pattern is deeply rooted in the ongoing quest for gas optimization on the Ethereum blockchain. By eliminating the need for a separate admin contract and the associated storage slot checks on every function call, UUPS proxies reduce baseline transaction costs. This optimization became particularly critical for user-facing operations in decentralized applications (dApps), where saving even small amounts of gas per transaction leads to significant aggregate savings and a better user experience.
The adoption of UUPS was accelerated by its integration into major smart contract development frameworks like OpenZeppelin Contracts. Their libraries provided audited, standard implementations, making the pattern accessible and secure for developers. This institutional support helped cement UUPS as a mainstream choice for upgradeable contracts, especially for new projects where the reduced deployment and transaction costs were a primary consideration over the transparent proxy's alternative security model.
Key Features of UUPS Proxies
UUPS (Universal Upgradeable Proxy Standard) is a proxy pattern where the upgrade logic is embedded within the implementation contract itself, rather than a separate proxy admin contract.
Self-Contained Upgrade Logic
In a UUPS proxy, the upgrade authorization and execution functions (like upgradeTo) are part of the implementation contract's logic. This contrasts with the Transparent Proxy pattern, which uses an external ProxyAdmin contract. The proxy delegates all calls, including upgrades, to the implementation.
Gas Efficiency
UUPS proxies are more gas-efficient for users because they avoid the storage slot read required by the Transparent Proxy pattern to check if the caller is the admin. Direct calls from non-admin users skip this check, resulting in lower transaction costs for regular operations.
Implementation Contract Responsibility
The implementation contract must inherit and properly implement the upgrade functionality, typically from a standard interface like IERC1822Proxiable. Developers must ensure the new implementation contract includes the upgrade function in every subsequent version, or the proxy becomes permanently frozen.
Proxy Storage Layout
The proxy contract stores only one critical piece of data: the address of the current implementation contract (e.g., in the _IMPLEMENTATION_SLOT defined by EIP-1967). All other state variables are defined and stored in the context of the implementation contract's storage layout, which must be preserved across upgrades.
Initialization vs Constructor
Because the proxy uses delegatecall, constructor code in the implementation is not executed upon linking. Instead, an initializer function must be used to set up the contract's initial state. This function must be protected (e.g., with initializer modifier) to prevent re-initialization.
Comparison to Transparent Proxies
- UUPS: Upgrade logic in implementation. More gas-efficient for users. Risk of irreparable proxy if upgrade function is omitted.
- Transparent: Upgrade logic in separate ProxyAdmin. Slightly more gas for users. Clear separation of concerns and admin management.
How UUPS Proxies Work
An explanation of the Universal Upgradeable Proxy Standard (UUPS), a design pattern that separates a contract's logic from its storage to enable seamless upgrades on-chain.
A Universal Upgradeable Proxy Standard (UUPS) proxy is a smart contract architecture that delegates all function calls, except for a predefined initialization, to a separate logic contract (also called an implementation). This delegation is performed via the low-level delegatecall opcode, which executes the logic contract's code in the context of the proxy's storage. The proxy contract itself holds only two critical pieces of data: the address of the current logic contract and, optionally, an admin address. This separation is the core mechanism that allows the logic of a decentralized application to be upgraded by simply pointing the proxy to a new implementation address, while preserving all existing user data and state.
The upgrade mechanism itself is embedded directly within the logic contract, not the proxy. This is a key distinction from the older Transparent Proxy pattern. In a UUPS design, the logic contract contains the function to update the proxy's stored implementation address (e.g., upgradeTo(address newImplementation)). Consequently, the upgrade authorization logic—such as access controls managed by Ownable or AccessControl—resides in the logic being executed. This design makes UUPS proxies more gas-efficient for end-users, as the proxy avoids a check on every call to see if the caller is the admin, a check required in the Transparent Proxy model.
A crucial security consideration for UUPS proxies is ensuring the implementation contract is itself not vulnerable. Since the upgrade function lives in the implementation, a flawed or malicious upgrade could point the proxy to a broken or harmful contract. Therefore, rigorous testing and auditing of the implementation's upgrade functionality is paramount. Furthermore, developers must be cautious not to accidentally leave a vulnerability in the implementation that allows an unauthorized party to call the upgradeTo function, which would compromise the entire system. Proper use of inheritance from audited libraries like OpenZeppelin's UUPSUpgradeable is a standard practice to mitigate these risks.
The initialization of a UUPS proxy system requires careful handling to prevent hijacking. Unlike regular constructors, a special initializer function must be used to set up the initial state of the proxy. This function, typically named initialize, should be protected so it can only be called once. This prevents a malicious actor from re-initializing the contract with their own parameters. It is common practice to combine UUPS with contract initialization libraries that enforce this protection. The deployment flow involves first deploying the logic contract, then deploying the proxy (pointing to that logic), and finally calling the initialize function on the proxy address to set up ownership and initial variables.
In practice, UUPS is widely adopted in the Ethereum ecosystem due to its gas efficiency and flexibility. Major protocols and standards, including many ERC-20 and ERC-721 token implementations, utilize UUPS for their upgradeable versions. Tools like Hardhat and Foundry have plugins to streamline the deployment and verification of UUPS-based upgradeable contracts. When compared to other patterns, UUPS is often preferred for new projects where the logic contract can be designed with upgradeability in mind from the start, as it reduces overhead for users and centralizes upgrade logic in a single, auditable code location.
Code Example: UUPS Implementation
A hands-on walkthrough of implementing a UUPS (Universal Upgradeable Proxy Standard) pattern in Solidity, demonstrating the separation of proxy and logic contracts.
The core of a UUPS implementation involves two primary contracts: the proxy and the logic contract. The proxy is a minimal contract that delegates all calls, via delegatecall, to the logic contract's address stored in its state. The logic contract contains the actual business logic and, crucially, must include the upgrade function itself. This is a key architectural difference from the Transparent Proxy pattern, where upgrade logic resides in a separate proxy admin contract. A basic proxy contract might store the logic address in a variable like implementation and use a fallback function to forward calls.
The logic contract must inherit from and implement a UUPS-compliant upgrade mechanism, typically defined in an interface like IERC1822Proxiable. The most critical function is upgradeTo(address newImplementation), which contains the authorization logic (e.g., onlyOwner) and updates the proxy's stored implementation address. This function is called through the proxy, meaning the upgrade executes in the proxy's storage context. Developers must ensure the new logic contract is also UUPS-compliant to preserve upgradeability. A common practice is to use an abstract base contract, such as OpenZeppelin's UUPSUpgradeable, which provides the internal _upgradeToAndCall function and includes a safety mechanism to prevent the implementation contract from being initialized post-deployment.
A complete implementation requires careful attention to initialization and storage layout. Since constructors are not used in upgradeable contracts, an initializer function (e.g., initialize) protected by an initializer modifier must set up the contract's initial state. It is vital that this function can only be called once. Furthermore, when writing new versions of the logic contract, you must follow storage collision rules: never change the order of existing state variables, only append new ones. Failing to do so will corrupt the proxy's storage. Tools like OpenZeppelin Upgrades Plugins can automate safety checks for these patterns.
Deploying a UUPS system involves a specific sequence. First, you deploy the initial version of the logic contract (V1). Next, you deploy the proxy contract, passing the V1 logic address to its constructor or initializer. All future interactions are made with the proxy's address. To upgrade, you deploy the new logic contract (V2) and then call proxy.upgradeTo(V2_address). This call is routed through the proxy to V1's upgradeTo function, which authorizes the caller and updates the proxy's stored implementation pointer. After this single transaction, all subsequent calls to the proxy will execute code from V2, while preserving the proxy's address and storage.
UUPS vs. Transparent Proxy Comparison
A technical comparison of the two primary patterns for upgradeable smart contracts on EVM chains, focusing on implementation details, gas costs, and security considerations.
| Feature / Metric | Transparent Proxy Pattern | UUPS (Universal Upgradeable Proxy Standard) |
|---|---|---|
Proxy Logic Location | Separate ProxyAdmin contract | Implementation contract itself |
Upgrade Authorization | ProxyAdmin owner | Implementation contract logic |
Gas Cost for User Calls | Higher (~+5-10k gas) | Lower (Near base contract cost) |
Proxy Contract Size | Larger | Smaller |
Upgrade Function Location | External ProxyAdmin | Internal |
Implementation Self-Destruct Risk | Not applicable | Critical (if not secured) |
EIP Standard | EIP-1967 (Storage slots) | EIP-1822 / EIP-1967 |
Initialization Pattern | Separate initializer function | Separate initializer function |
Security Considerations and Risks
While the UUPS (Universal Upgradeable Proxy Standard) pattern offers a gas-efficient upgrade mechanism, its security model introduces unique risks that developers and auditors must rigorously assess.
Implementation Contract Self-Destruct
The most critical risk in UUPS is that the upgrade logic resides in the implementation contract itself. If a malicious or flawed upgrade function is called, it could self-destruct the implementation, permanently breaking all proxies pointing to it and locking all user funds. This is a key difference from the more common Transparent Proxy pattern, where the proxy holds the upgrade logic.
Initialization and Reinitialization Attacks
UUPS proxies rely on an initializer function instead of a constructor. Critical risks include:
- Missing initializer: Forgetting to call it leaves the contract uninitialized and vulnerable.
- Reinitialization: Failing to use an
initializermodifier or guard can allow an attacker to reset contract state. - Front-running: Initialization transactions can be front-run before the proxy is transferred to its rightful owner.
Storage Collision Hazards
Both the proxy and implementation share the same storage layout. An upgrade that modifies the storage variables in the new implementation can cause catastrophic storage collisions. If variable slots are rearranged or types changed, the new logic will read corrupt data from the proxy's existing storage, leading to fund loss or contract failure.
Governance and Access Control
The upgradeTo function must be protected by robust access control, typically only allowing a governance contract or multi-signature wallet. A compromised private key or flawed governance mechanism can lead to a malicious upgrade. The 2021 Audius exploit, where a malicious proposal upgraded a contract to steal tokens, highlights this risk.
Proxy-Implementation Function Clash
The proxy's fallback function delegates all calls to the implementation. A risk arises if the implementation defines a function with the same selector as the proxy's core functions (like upgradeTo or owner). This can cause unintended behavior or lock the ability to upgrade. Careful interface design and the use of the ERC1967Utils library mitigate this.
Verification and Transparency
Because users interact with the proxy address, the verified source code on block explorers points to the proxy, not the active implementation. This requires:
- Verifying the implementation contract separately and monitoring its address.
- Ensuring transparency for users about the current implementation and upgrade history.
- Using EIP-1967 standard storage slots so tools can reliably find the current implementation.
Ecosystem Usage and Examples
UUPS (Universal Upgradeable Proxy Standard) is a prevalent pattern for creating upgradeable smart contracts, enabling logic updates while preserving contract state and address. This section explores its key implementations and real-world applications.
The Upgrade Mechanism
In a UUPS proxy, the upgrade logic is embedded directly in the implementation contract, not the proxy. To upgrade, a privileged account calls a function (e.g., upgradeTo(address newImplementation)) on the proxy, which delegates the call to the current implementation's upgrade function. This design makes the proxy contract simpler and more gas-efficient, as the proxy itself contains no upgrade logic.
Key Security Consideration: Initializers
Because constructors are not used in upgradeable contracts, UUPS implementations rely on initializer functions. These functions, often named initialize, set the initial state and must be protected to prevent re-initialization attacks. Best practices include:
- Using the
initializermodifier from OpenZeppelin. - Explicitly setting the initializer version to prevent clashes in future upgrades.
- Ensuring the deployer calls the initializer immediately after deployment.
Comparison with Transparent Proxy Pattern
UUPS differs from the older Transparent Proxy pattern in a critical way:
- Transparent Proxy: Upgrade logic is in the Proxy Admin contract. The proxy uses
delegatecallfor user logic and a direct call for admin functions. - UUPS Proxy: Upgrade logic is in the implementation. This reduces gas costs for users and simplifies the system architecture, but places the responsibility for maintaining upgradeability on the implementation logic itself.
Real-World Adoption: Uniswap v4
A prominent example of UUPS adoption is Uniswap v4. Its core hook contracts are designed to be upgradeable using the UUPS pattern. This allows hook developers to deploy initial versions and later push security patches or feature upgrades without requiring liquidity providers to migrate their positions to a new contract address, ensuring continuity and reducing friction.
The ERC-1822 and ERC-1967 Standards
UUPS builds upon formal Ethereum standards:
- ERC-1822: The original Universal Upgradeable Proxy Standard proposal that defined the pattern.
- ERC-1967: The standard that defines specific storage slots for the implementation address (
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc) and the admin address. This prevents storage collisions and allows tools to reliably discover a proxy's implementation.
Common Misconceptions About UUPS
Universal Upgradeable Proxy Standard (UUPS) patterns are widely used but often misunderstood. This section clarifies key technical distinctions and operational details to prevent critical implementation errors.
Yes, a UUPS proxy is more gas-efficient for end-users because it eliminates the proxy admin overhead from every transaction. In the Transparent Proxy pattern, every call from a regular user must pass through a delegatecall check in the proxy to determine if the caller is the admin, incurring a constant gas cost. The UUPS pattern moves the upgrade logic into the implementation contract itself. This means routine function calls avoid the admin check, resulting in lower gas costs. However, the upgrade transaction itself may be slightly more expensive as it executes logic within the implementation.
Frequently Asked Questions (FAQ)
A UUPS (Universal Upgradeable Proxy Standard) is a smart contract upgrade pattern where the upgrade logic resides in the implementation contract itself, not the proxy. This FAQ addresses common developer questions about its design, security, and use.
A UUPS (Universal Upgradeable Proxy Standard) is a smart contract upgrade pattern where the logic for upgrading the implementation address is built into the implementation contract itself, rather than the proxy. The proxy contract uses delegatecall to execute all logic from the implementation, storing only the implementation address and any proxy-specific state. To upgrade, a new implementation contract is deployed, and a function call (like upgradeTo(address newImplementation)) is made through the proxy, which delegates to the old implementation's upgrade logic to update the stored address. This design makes the proxy contract simpler and more gas-efficient for regular operations.
Further Reading and Resources
Deepen your understanding of UUPS upgrade patterns with official documentation, community tools, and security resources.
Get In Touch
today.
Our experts will offer a free quote and a 30min call to discuss your project.