UUPS Proxy Example

Let us dive into the UUPS (Universal Upgradeable Proxy Standard) proxy pattern with a practical example. This will allow us to create upgradeable smart contracts while minimizing gas costs and maintaining a clear separation between your contract logic and storage.

What is UUPS Proxy?

UUPS is an upgradeable proxy pattern that leverages a single implementation contract and a proxy to delegate calls to the implementation. The proxy holds the state, while the implementation contract contains the logic. UUPS allows the implementation to be upgraded through a function call on the proxy itself, reducing gas costs compared to other patterns like Transparent Proxies.

Key Components

  1. Proxy Contract: This is the contract that users interact with. It holds the state and delegates calls to the implementation.

  2. Implementation Contract: This contains the actual logic of the contract. It can be upgraded by deploying a new version and pointing the proxy to the new implementation.

  3. Admin Functionality: The proxy needs to have a way to authorize upgrades.

UUPS Proxy Implementation

Step 1: Set Up Your Project

First, make sure you have a working environment with Node.js, Hardhat, and OpenZeppelin:

mkdir UUPSExample
cd UUPSExample
npm init -y
npm install --save-dev hardhat
npm install @openzeppelin/contracts

Step 2: Create the Implementation Contract

Create a file named MyContract.sol in the contracts folder:

Explanation:

  • The contract uses UUPSUpgradeable for the upgrade mechanism.

  • The initialize function sets the initial state.

  • The _authorizeUpgrade function restricts who can upgrade the contract to the owner.

Step 4: Create the Proxy Contract

With UUPS, the proxy is automatically handled by OpenZeppelin, so you don't need to create a separate proxy contract manually. Instead, you will deploy the implementation contract and interact with it via the proxy mechanism.

Step 5: Write the Deployment Script

In the scripts folder, create a file named deploy.js:

Step 6: Deploy the Contract

Run the deployment script:

Step 7: Upgrade the Contract

Let’s say you want to add new functionality in the future. Create a new version of your contract. Create MyContractV2.sol:

Explanation:

  • This version extends MyContract and adds a new function incrementValue.

Step 8: Deploy the New Implementation

In the scripts folder, create a new script named upgrade.js:

Step 9: Upgrade Your Contract

Run the upgrade script:

Step 10: Interact with the Upgraded Contract

You can now call the new incrementValue function on the upgraded contract. Create a script named interact.js:

Run the interaction script:

You’ve successfully implemented a UUPS proxy pattern for upgradeable contracts! Here’s a recap of what we did:

  1. Created an implementation contract with upgradeable functionality.

  2. Deployed the contract to the network.

  3. Added a new version of the contract with additional features.

  4. Upgraded the contract to the new implementation.

  5. Interacted with the upgraded contract to demonstrate its new capabilities.

This UUPS pattern provides a flexible way to maintain and upgrade your smart contracts while optimizing for gas costs, making it an ideal choice for modern Ethereum development.

Last updated