Upgrades Plugins

Integrate upgrades into your existing workflow. Plugins for Hardhat and Truffle to deploy and manage upgradeable contracts on Ethereum.

  • Deploy upgradeable contracts.

  • Upgrade deployed contracts.

  • Manage proxy admin rights.

  • Easily use in tests.

Upgrades Plugins are only a part of a comprehensive set of OpenZeppelin tools for deploying and securing upgradeable smart contracts. Check out the full list of resources.

Overview

Installation

Hardhat install

$ npm install --save-dev @openzeppelin/hardhat-upgrades @nomicfoundation/hardhat-ethers ethers

This installs our Hardhat plugin along with the necessary peer dependencies.

You also need to load it in your Hardhat config file:

// hardhat.config.js
require('@openzeppelin/hardhat-upgrades');
// hardhat.config.ts
import '@openzeppelin/hardhat-upgrades';

Truffle install

$ npm install --save-dev @openzeppelin/truffle-upgrades

Usage

See the documentation for using Truffle Upgrades and Hardhat Upgrades, or take a look at the sample code snippets below.

Hardhat usage

Hardhat users will be able to write scripts that use the plugin to deploy or upgrade a contract, and manage proxy admin rights.

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

async function main() {
  // Deploying
  const Box = await ethers.getContractFactory("Box");
  const instance = await upgrades.deployProxy(Box, [42]);
  await instance.waitForDeployment();

  // Upgrading
  const BoxV2 = await ethers.getContractFactory("BoxV2");
  const upgraded = await upgrades.upgradeProxy(await instance.getAddress(), BoxV2);
}

main();

Truffle usage

Truffle users will be able to write migrations that use the plugin to deploy or upgrade a contract, or manage proxy admin rights.

const { deployProxy, upgradeProxy } = require('@openzeppelin/truffle-upgrades');

const Box = artifacts.require('Box');
const BoxV2 = artifacts.require('BoxV2');

module.exports = async function (deployer) {
  const instance = await deployProxy(Box, [42], { deployer });
  const upgraded = await upgradeProxy(instance.address, BoxV2, { deployer });
}

Test usage

Whether you’re using Hardhat or Truffle, you can use the plugin in your tests to ensure everything works as expected.

it('works before and after upgrading', async function () {
  const instance = await upgrades.deployProxy(Box, [42]);
  assert.strictEqual(await instance.retrieve(), 42);

  await upgrades.upgradeProxy(instance, BoxV2);
  assert.strictEqual(await instance.retrieve(), 42);
});

How the plugins work

Both plugins provide functions which take care of managing upgradeable deployments of your contracts.

For example, deployProxy does the following:

  1. Validate that the implementation is upgrade safe.

  2. Deploy a proxy admin for your project (if needed).

  3. Deploy the implementation contract.

  4. Create and initialize the proxy contract.

And when you call upgradeProxy:

  1. Validate that the new implementation is upgrade safe and is compatible with the previous one.

  2. Check if there is an implementation contract deployed with the same bytecode, and deploy one if not.

  3. Upgrade the proxy to use the new implementation contract.

The plugins will keep track of all the implementation contracts you have deployed in an .openzeppelin folder in the project root, as well as the proxy admin. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as .openzeppelin/unknown-*.json).

Note: the format of the files within the .openzeppelin folder is not compatible with those of the OpenZeppelin CLI. If you want to use the Upgrades Plugins for an existing OpenZeppelin CLI project, you can migrate using the guide.

Proxy patterns

The plugins support the UUPS, transparent, and beacon proxy patterns. UUPS and transparent proxies are upgraded individually, whereas any number of beacon proxies can be upgraded atomically at the same time by upgrading the beacon that they point to. For more details on the different proxy patterns available, see the documentation for Proxies.

For UUPS and transparent proxies, use deployProxy and upgradeProxy as shown above. For beacon proxies, use deployBeacon, deployBeaconProxy, and upgradeBeacon. See the documentation for Hardhat Upgrades and Truffle Upgrades for examples.

Managing ownership

Transparent proxies define an admin address which has the rights to upgrade them. By default, the admin is a proxy admin contract deployed behind the scenes. You can change the admin of a proxy by calling the admin.changeProxyAdmin function in the plugin. Keep in mind that the admin of a proxy can only upgrade it, but not interact with the implementation contract. Read Transparent Proxies and Function Clashes for more info on this restriction.

The proxy admin contract also defines an owner address which has the rights to operate it. By default, this address is the externally owned account used during deployment. You can change the proxy admin owner by calling the admin.transferProxyAdminOwnership function in the plugin. Note that changing the proxy admin owner effectively transfers the power to upgrade any proxy in your whole project to the new owner, so use with care. Refer to each plugin documentation for more details on the admin functions.

UUPS and beacon proxies do not use admin addresses. UUPS proxies rely on an _authorizeUpgrade function to be overridden to include access restriction to the upgrade mechanism, whereas beacon proxies are upgradable only by the owner of their corresponding beacon.

Once you have transferred the rights to upgrade a proxy or beacon to another address, you can still use your local setup to validate and deploy the implementation contract. The plugins include a prepareUpgrade function that will validate that the new implementation is upgrade-safe and compatible with the previous one, and deploy it using your local Ethereum account. You can then execute the upgrade itself from the admin or owner address. You can also use the proposeUpgrade function to automatically set up the upgrade in Defender Admin.