OpenZeppelin Hardhat Upgrades API

Both deployProxy and upgradeProxy functions will return instances of ethers.js contracts, and require ethers.js contract factories as arguments. For beacons, deployBeacon and upgradeBeacon will both return an upgradable beacon instance that can be used with a beacon proxy. All functions validate that the implementation contract is upgrade-safe, and will fail otherwise.

Common Options

The following options are common to all functions.

  • kind: ("uups" | "transparent") Choose between a UUPS or Transparent proxy. Defaults to Transparent. See Transparent vs UUPS. Not applicable for Beacon proxies except for forceImport.

  • unsafeAllow: (ValidationError[]) Selectively disable one or more validation errors:

    • "external-library-linking": Allows a deployment with external libraries linked to the implementation contract. (External libraries are otherwise not yet supported.)

    • "struct-definition", "enum-definition": Used to be necessary to deploy a contract with structs or enums. No longer necessary.

    • "state-variable-assignment": Allows assigning state variables in a contract even though they will be stored in the implementation.

    • "state-variable-immutable": Allows use of immutable variables, which are not unsafe

    • "constructor": Allows defining a constructor. Note that constructor arguments are not supported regardless of this option.

    • "delegatecall", "selfdestruct": Allow the use of these operations. Incorrect use of this option can put funds at risk of permanent loss. See Can I safely use delegatecall and selfdestruct?

    • "missing-public-upgradeto": Allow UUPS implementations that do not contain a public upgradeTo function. Enabling this option is likely to cause a revert due to the built-in UUPS safety mechanism.

  • unsafeAllowRenames: (boolean) Configure storage layout check to allow variable renaming.

  • unsafeSkipStorageCheck: (boolean) upgrades the proxy or beacon without first checking for storage layout compatibility errors. This is a dangerous option meant to be used as a last resort.

  • constructorArgs: (unknown[]) Provide arguments for the constructor of the implementation contract. Note that these are different from initializer arguments, and will be used in the deployment of the implementation contract itself. Can be used to initialize immutable variables.

  • timeout: (number) Timeout in milliseconds to wait for the transaction confirmation when deploying an implementation contract or proxy admin contract. Defaults to 60000. Use 0 to wait indefinitely.

  • pollingInterval: (number) Polling interval in milliseconds between checks for the transaction confirmation when deploying an implementation contract or proxy admin contract. Defaults to 5000.

Note that the options unsafeAllow can also be specified in a more granular way directly in the source code if using Solidity >=0.8.2. See How can I disable some of the checks?

The following options have been deprecated.

  • unsafeAllowLinkedLibraries: Equivalent to including "external-library-linking" in unsafeAllow.

  • unsafeAllowCustomTypes: Equivalent to including "struct-definition" and "enum-definition" in unsafeAllow. No longer necessary.

deployProxy

Creates a UUPS or Transparent proxy given an ethers contract factory to use as implementation, and returns a contract instance with the proxy address and the implementation interface. If args is set, will call an initializer function initialize with the supplied args during proxy deployment.

If you call deployProxy several times for the same implementation contract, several proxies will be deployed, but only one implementation contract will be used.

async function deployProxy(
  Contract: ethers.ContractFactory,
  args: unknown[] = [],
  opts: {
    initializer: string | false,
    unsafeAllow: ValidationError[],
    kind: 'uups' | 'transparent',
  } = {},
): Promise<ethers.Contract>

upgradeProxy

Upgrades a UUPS or Transparent proxy at a specified address to a new implementation contract, and returns a contract instance with the proxy address and the new implementation interface.

  • call: enables the execution of an arbitrary function call during the upgrade process. This call is described using a function name, signature, or selector (see Specifying Fragments), and optional arguments. It is batched into the upgrade transaction, making it safe to call migration initializing functions.

  • See Common Options.

async function upgradeProxy(
  proxyAddress: string,
  Contract: ethers.ContractFactory,
  opts: {
    call: string | { fn: string; args?: unknown[] },
    unsafeAllow: ValidationError[],
    unsafeSkipStorageCheck: boolean;
  } = {},
): Promise<ethers.Contract>

deployBeacon

Creates an upgradable beacon given an ethers contract factory to use as implementation, and returns the beacon contract instance.

async function deployBeacon(
  Contract: ethers.ContractFactory,
  opts: {
    unsafeAllow: ValidationError[],
  } = {},
): Promise<ethers.Contract>

upgradeBeacon

Upgrades an upgradable beacon at a specified address to a new implementation contract, and returns the beacon contract instance.

async function upgradeBeacon(
  beaconAddress: string,
  Contract: ethers.ContractFactory,
  opts: {
    unsafeAllow: ValidationError[],
    unsafeSkipStorageCheck: boolean;
  } = {},
): Promise<ethers.Contract>

deployBeaconProxy

Creates a Beacon proxy given an existing beacon contract address and an ethers contract factory corresponding to the beacon’s current implementation contract, and returns a contract instance with the beacon proxy address and the implementation interface. If args is set, will call an initializer function initialize with the supplied args during proxy deployment.

  • initializer: set a different initializer function to call (see Specifying Fragments), or specify false to disable initialization

async function deployBeaconProxy(
  beaconAddress: string,
  attachTo: ethers.ContractFactory,
  args: unknown[] = [],
  opts: {
    initializer?: string | false,
  } = {},
): Promise<ethers.Contract>

forceImport

Forces the import of an existing proxy or beacon deployment to be used with this plugin. Provide the address of an existing proxy or beacon and the ethers contract factory of the implementation contract that was deployed. Use this function to recreate a lost network file by importing previous deployments, or to register proxies or beacons for upgrading even if they were not originally deployed by this plugin. Supported for UUPS, Transparent, and Beacon proxies, as well as beacons.

  • kind: ("uups" | "transparent" | "beacon") forces a proxy to be treated as a UUPS, Transparent, or Beacon proxy. If not provided, the proxy kind will be automatically detected.

  • See Common Options.

async function forceImport(
  proxyOrBeaconAddress: string,
  deployedImpl: ethers.ContractFactory,
  opts: {
    kind?: 'uups' | 'transparent' | 'beacon',
  } = {},
): Promise<ethers.Contract>

prepareUpgrade

Validates and deploys a new implementation contract, and returns its address. Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from Hardhat. Supported for UUPS, Transparent, and Beacon proxies, as well as beacons.

  • getTxResponse: if set to true, causes this function to return an ethers transaction response corresponding to the deployment of the new implementation contract instead of its address. Note that if the new implementation contract was originally imported as a result of forceImport, only the address will be returned.

  • See Common Options.

async function prepareUpgrade(
  proxyOrBeaconAddress: string,
  Contract: ethers.ContractFactory,
  opts: {
    unsafeAllow: ValidationError[],
    unsafeSkipStorageCheck: boolean,
    getTxResponse?: boolean,
  } = {},
): Promise<string | ethers.providers.TransactionResponse>

defender.proposeUpgrade

This method requires the @openzeppelin/hardhat-defender package, as well as configuring a Defender Team API Key.

Similar to prepareUpgrade. This method validates and deploys the new implementation contract, but also creates an upgrade proposal in Defender Admin, for review and approval by the upgrade administrators. Supported for UUPS or Transparent proxies. Not currently supported for beacon proxies or beacons. For beacons, use prepareUpgrade along with a custom action in Defender Admin to upgrade the beacon to the deployed implementation.

Returns an object with the URL of the Defender proposal and the ethers transaction response corresponding to the deployment of the new implementation contract. Note that if the new implementation contract was originally imported as a result of forceImport, the ethers transaction response will be undefined.

  • title: title of the upgrade proposal as seen in Defender Admin, defaults to Upgrade to 0x12345678 (using the first 8 digits of the new implementation address)

  • description: description of the upgrade proposal as seen in Defender Admin, defaults to the full implementation address.

  • multisig: address of the multisignature wallet contract with the rights to execute the upgrade. This is autodetected in Transparent proxies, but required for UUPS proxies (read more here). Both Gnosis Safe and Gnosis MultisigWallet multisigs are supported.

  • proxyAdmin: address of the ProxyAdmin contract that manages the proxy, if exists. This is autodetected in Transparent proxies, but required for UUPS proxies (read more here), though UUPS proxies typically do not require the usage of a ProxyAdmin.

  • See Common Options.

async function proposeUpgrade(
  proxyAddress: string,
  ImplFactory: ContractFactory,
  opts: {
    unsafeAllow?: ValidationError[],
    unsafeSkipStorageCheck?: boolean;
    title?: string,
    description?: string,
    multisig?: string,
    proxyAdmin?: string,
  } = {},
): Promise<{
    url: string,
    txResponse?: ethers.providers.TransactionResponse,
  }>

admin.changeProxyAdmin

Changes the admin for a specific proxy. Receives the address of the proxy to change, and the new admin address.

async function changeProxyAdmin(
  proxyAddress: string,
  newAdmin: string,
): Promise<void>

admin.transferProxyAdminOwnership

Changes the owner of the proxy admin contract, which is the default admin for upgrade rights over all proxies. Receives the new admin address.

async function transferProxyAdminOwnership(
  newAdmin: string,
): Promise<void>

erc1967

Functions in this module provide access to the ERC1967 variables of a proxy contract.

async function erc1967.getImplementationAddress(proxyAddress: string): Promise<string>;
async function erc1967.getBeaconAddress(proxyAddress: string): Promise<string>;
async function erc1967.getAdminAddress(proxyAddress: string): Promise<string>;

beacon

This module provides a convenience function to get the implementation address from a beacon contract.

async function beacon.getImplementationAddress(beaconAddress: string): Promise<string>;

silenceWarnings

Silences all subsequent warnings about the use of unsafe flags. Prints a last warning before doing so.

This function is useful for tests, but its use in production deployment scripts is discouraged.
function silenceWarnings()

verify

Overrides hardhat-etherscan's verify task to completely verify a proxy on Etherscan. This supports verifying proxy contracts that were deployed by the Hardhat Upgrades or Truffle Upgrades plugin.

The arguments are the same as for hardhat-etherscan's verify task. If the provided address is a proxy, this task will verify the proxy’s implementation contract, the proxy itself and any proxy-related contracts, as well as link the proxy to the implementation contract’s ABI on Etherscan. If the provided address is not a proxy, the regular verify task from hardhat-etherscan will be run on the address instead.

The following contracts will be verified when you run this task on your proxy address:

To use this task, ensure you have hardhat-etherscan installed:

npm install --save-dev @nomiclabs/hardhat-etherscan

Then import the @nomiclabs/hardhat-etherscan plugin along with the @openzeppelin/hardhat-upgrades plugin in your Hardhat configuration. For example, if you are using JavaScript, import the plugins in hardhat.config.js:

require("@nomiclabs/hardhat-etherscan");
require("@openzeppelin/hardhat-upgrades");

Or if you are using TypeScript, import the plugins in hardhat.config.ts:

import "@nomiclabs/hardhat-etherscan";
import "@openzeppelin/hardhat-upgrades";

Finally, follow hardhat-etherscan’s usage documentation to configure your Etherscan API key and run the verify task using the proxy address, for example:

npx hardhat verify --network mainnet PROXY_ADDRESS

Note that you do not need to include constructor arguments for the verify task if your implementation contract only uses initializers. However, if your implementation contract has an actual constructor with arguments (such as to set immutable variables), then include constructor arguments in the command according to hardhat-etherscan’s usage documentation.