facebook

Smart Contract Upgradability | Proxy Patterns in Solidity

Posted By : Sarthak

Oct 29, 2024

Once deployed, smart contracts cannot be changed or tampered with since they are immutable. However, a contemporary method of smart contract development that can be upgraded is the Ethereum blockchain's Universal Upgradeable Proxy Standard (UUPS). By making the upgrading process easier and improving gas efficiency, it overcomes some drawbacks of earlier proxy patterns, most notably the Transparent Proxy Pattern.

 

UUPS consists of two main components: the proxy and implementation contracts.

 

Smart Contract Upgradability | Proxy Patterns in Solidity

 

a)Proxy Contract

 

  • Maintains a specific storage slot for the address of the implementation contract.
  • Users interact with the proxy rather than the implementation directly. This ensures that state and logic remain consistent across upgrades

 

b) Implementation Contract

 

`When deploying a UUPS setup, it's essential to initialize the implementation through the proxy to ensure that state variables are stored correctly in the proxy's storage rather than in the implementation's storage, which is essential for maintaining the integrity and upgradeability of the contract.

All the versions of the implementation contract share the same storage space so that`s why sequencing matters while initializing variables.

 

In the UUPS pattern, constructors are generally not used due to the proxy design.

 

  • Reasons for Not Using Constructors

     

Storage Separation

 

The implementation contract does not directly manage state variables; instead, these variables are stored in the proxy's storage. Since constructors are executed during contract deployment and would initialize state variables in the implementation contract, using them would lead to incorrect storage allocation and could result in state variables being stored in the implementation rather than the proxy.

 

Also, Check | How to Create a Simple Supply Chain Smart Contract

 

Initialization Function 

 

These contracts utilize an initializer function that is called after deployment. This function is designed to set up state variables and can include security mechanisms to ensure it is only called once, preventing re-initialization attacks.

 

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';

contract Version1 is
    Initializable,
    ERC20Upgradeable,
    UUPSUpgradeable,
    OwnableUpgradeable
{
    uint256 public value;

    // Initializer function to replace constructor
    function initialize() public initializer {
        __ERC20_init('Mars', 'MARS');
        __Ownable_init(_msgSender()); // Pass the owner address here
        value = 5;
        __UUPSUpgradeable_init();
        _mint(msg.sender, 10000000 * 10 ** decimals());
    }

    // Upgradeable authorization for upgrades (only owner can upgrade)
    function _authorizeUpgrade(
        address newImplementation
    ) internal override onlyOwner {}

    function getValue() public view returns (uint256) {
        return value;
    }
}

contract Version2 is Version1 {
    function version() public pure returns (string memory) {
        return 'V2';
    }
}

contract Version3 is Version1 {
    function version() public pure returns (string memory) {
        return 'V3';
    }
}

 

For the above contract we are upgrading the contract versions from 'V`1' to 'V3', below are the test cases for the proxy contract.

 

Also, Explore | How to Write and Deploy Modular Smart Contracts

 

const {
  loadFixture,
} = require('@nomicfoundation/hardhat-toolbox/network-helpers');
const hardhat = require('hardhat');
const assert = require('assert');

describe('Proxy', function () {

  describe('Deployment', async function () {
    async function deployOneYearLockFixture() {
      const contract = await hardhat.ethers.getContractFactory('Version1');
      const contractVersion2 = await hardhat.ethers.getContractFactory('Version2');
      const contractVersion3 = await hardhat.ethers.getContractFactory('Version3');
      const proxyContract = await hardhat.upgrades.deployProxy(contract, { kind: 'uups' })
      return {  proxyContract, contractVersion3, contractVersion2 }
    }

    describe('Versions', function () {
      it('Should set the right output', async function () {
        const { contractVersion3, proxyContract, contractVersion2 } = await loadFixture(deployOneYearLockFixture);
        assert(await proxyContract.name() == 'Mars')
        assert(await proxyContract.getValue() == 5n)
        const contractV2 = await hardhat.upgrades.upgradeProxy(proxyContract, contractVersion2)

        assert(await contractV2.getValue() == 5n)
        assert(await contractV2.version() == 'V2')


        const contractV3 = await hardhat.upgrades.upgradeProxy(proxyContract, contractVersion3)

        assert(await contractV3.getValue() == 5n)
        assert(await contractV3.version() == 'V3')
      });
    });
  })
})

 

Use the following command to run and verify the test cases for the proxy contract

 


- npx hardhat test

 

Also, Read | How to Create Play-to-Earn Gaming Smart Contracts


Conclusion


In conclusion, the Universal Upgradeable Proxy Standard (UUPS) provides a robust framework for developing upgradeable smart contracts on Ethereum. By leveraging a proxy architecture that separates logic from state, it allows for efficient upgrades while maintaining critical aspects of security and decentralization inherent to blockchain technology. As smart contract developers continue to navigate the complexities of smart contract deployment and management, UUPS stands out as a preferred method for ensuring that decentralized applications can evolve over time without compromising their foundational integrity.

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

December 17, 2024 at 07:01 pm

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Telegram Button
Youtube Button

Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text