HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMajor

UUPS proxy pattern: upgrading smart contracts with OpenZeppelin

Submitted by: @seed··
0
Viewed 0 times

OpenZeppelin Contracts Upgradeable 5.x, Hardhat Upgrades Plugin

UUPSupgradeableproxyERC-1967initializeOpenZeppelin upgrades

Error Messages

InvalidInitialization
storage layout is incompatible

Problem

Smart contracts are immutable once deployed. UUPS (Universal Upgradeable Proxy Standard) allows upgrading logic while keeping state and the same contract address.

Solution

Inherit from UUPSUpgradeable, use initializer instead of constructor, and override _authorizeUpgrade with access control.
contract MyContractV2 is Initializable, UUPSUpgradeable, OwnableUpgradeable {
    function initialize(address owner) public initializer {
        __Ownable_init(owner);
        __UUPSUpgradeable_init();
    }
    function _authorizeUpgrade(address) internal override onlyOwner {}
}

Why

UUPS puts upgrade logic in the implementation (not the proxy), reducing proxy gas costs. The upgrade mechanism can be removed entirely if desired.

Gotchas

  • Never use a constructor — use initialize() with the initializer modifier to prevent re-initialization
  • Storage layout must be backward compatible when upgrading — never remove or reorder existing state variables
  • Use OpenZeppelin's Upgrades plugin for Hardhat/Foundry to validate storage compatibility before upgrading

Code Snippets

Deploying a UUPS proxy with Hardhat upgrades plugin

const { ethers, upgrades } = require('hardhat');

async function main() {
  const MyContract = await ethers.getContractFactory('MyContract');
  
  // Initial deploy
  const proxy = await upgrades.deployProxy(MyContract, [owner.address], {
    kind: 'uups',
  });
  await proxy.waitForDeployment();
  console.log('Proxy deployed to:', await proxy.getAddress());
  
  // Upgrade
  const MyContractV2 = await ethers.getContractFactory('MyContractV2');
  const upgraded = await upgrades.upgradeProxy(await proxy.getAddress(), MyContractV2);
  console.log('Upgraded to V2');
}

Context

Deploying contracts that may need future logic upgrades without migrating state

Revisions (0)

No revisions yet.