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 deploy and upgrade functions validate that the implementation contract is upgrade-safe, and will fail otherwise.
Common Options
The following options are common to some functions.
-
kind
: ("uups" | "transparent" | "beacon"
) The kind of proxy to deploy, upgrade or import, or the kind of proxy that the implementation will be used with.deployProxy()
andupgradeProxy()
only support the values"uups" | "transparent"
. Defaults to"transparent"
. See Transparent vs UUPS. -
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. SeeconstructorArgs
. -
"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 usedelegatecall
andselfdestruct
? -
"missing-public-upgradeto"
: Allow UUPS implementations that do not contain a publicupgradeTo
orupgradeToAndCall
function. Enabling this option is likely to cause a revert due to the built-in UUPS safety mechanism. -
"internal-function-storage"
: Allow internal functions in storage variables. Internal functions are code pointers which will no longer be valid after an upgrade, so they must be reassigned during upgrades. See How can I use internal functions in storage variables?
-
-
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. -
initialOwner
: (string
) the address to set as the initial owner of a transparent proxy’s admin or initial owner of a beacon. Defaults to the externally owned account that is deploying the transparent proxy or beacon. Not supported for UUPS proxies.-
Since:
@openzeppelin/[email protected]
-
-
unsafeSkipProxyAdminCheck
: (boolean
) Skips checking theinitialOwner
option when deploying a transparent proxy. When deploying a transparent proxy, theinitialOwner
must be the address of an EOA or a contract that can call functions on a ProxyAdmin. It must not be a ProxyAdmin contract itself. Use this if you encounter an error due to this check and are sure that theinitialOwner
is not a ProxyAdmin contract.-
Since:
@openzeppelin/[email protected]
-
-
timeout
: (number
) Timeout in milliseconds to wait for the transaction confirmation when deploying an implementation contract. Defaults to60000
. Use0
to wait indefinitely. -
pollingInterval
: (number
) Polling interval in milliseconds between checks for the transaction confirmation when deploying an implementation contract. Defaults to5000
. -
redeployImplementation
: ("always" | "never" | "onchange"
) Determines whether the implementation contract will be redeployed. Defaults to"onchange"
.-
If set to
"always"
, the implementation contract is always redeployed even if it was previously deployed with the same bytecode. This can be used with thesalt
option when deploying a proxy through OpenZeppelin Defender to ensure that the implementation contract is deployed with the same salt as the proxy. -
If set to
"never"
, the implementation contract is never redeployed. If the implementation contract was not previously deployed or is not found in the network file, an error will be thrown. -
If set to
"onchange"
, the implementation contract is redeployed only if the bytecode has changed from previous deployments.
-
-
txOverrides
: (ethers.Overrides
) An ethers.js Overrides object to override transaction parameters, such asgasLimit
andgasPrice
. Applies to all transactions sent by a function with this option, even if the function sends multiple transactions. For OpenZeppelin Defender deployments, only thegasLimit
,gasPrice
,maxFeePerGas
, andmaxPriorityFeePerGas
parameters are supported. -
useDefenderDeploy
: (boolean
) Deploy contracts using OpenZeppelin Defender instead of ethers.js. See Using with OpenZeppelin Defender. -
verifySourceCode
: (boolean
) When using OpenZeppelin Defender deployments, whether to verify source code on block explorers. Defaults totrue
. -
relayerId
: (string
) When using OpenZeppelin Defender deployments, the ID of the relayer to use for the deployment. Defaults to the relayer configured for your deployment environment on Defender. -
salt
: (string
) When using OpenZeppelin Defender deployments, if this is not set, deployments will be performed using the CREATE opcode. If this is set, deployments will be performed using the CREATE2 opcode with the provided salt. Note that deployments using a Safe are done using CREATE2 and require a salt. Warning: CREATE2 affectsmsg.sender
behavior. See Caveats for more information. -
metadata
: ({ commitHash?: string; tag?: string; [k: string]: any; }
) When using OpenZeppelin Defender deployments, you can use this to identify, tag, or classify deployments. See Metadata. -
proxyFactory
: (ethers.ContractFactory
) Customizes the ethers contract factory to use for deploying the proxy, allowing a custom proxy contract to be deployed. See factories.ts for the default contract factory for each kind of proxy.-
Since:
@openzeppelin/[email protected]
-
-
deployFunction
: ((hre, opts, factory, …args) ⇒ Promise<EthersOrDefenderDeployment>
) Customizes the function used to deploy the proxy. Can be used along with theproxyFactory
option to override constructor parameters for custom proxy deployments. See deploy.ts for the default deploy function.-
Since:
@openzeppelin/[email protected]
-
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"
inunsafeAllow
. -
unsafeAllowCustomTypes
: Equivalent to including"struct-definition"
and"enum-definition"
inunsafeAllow
. No longer necessary. -
useDeployedImplementation
: (boolean
) Equivalent to settingredeployImplementation
to"never"
.
deployProxy
async function deployProxy(
Contract: ethers.ContractFactory,
args: unknown[] = [],
opts?: {
initializer?: string | false,
unsafeAllow?: ValidationError[],
constructorArgs?: unknown[],
initialOwner?: string,
unsafeSkipProxyAdminCheck?: boolean,
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
txOverrides?: ethers.Overrides,
kind?: 'uups' | 'transparent',
useDefenderDeploy?: boolean,
proxyFactory?: ethers.ContractFactory,
deployFunction?: () => Promise<EthersOrDefenderDeployment>,
},
): Promise<ethers.Contract>
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.
Parameters:
-
Contract
- an ethers contract factory to use as the implementation. -
args
- arguments for the initializer function. -
opts
- an object with options:-
initializer
: set a different initializer function to call (see Specifying Fragments), or specifyfalse
to disable initialization. -
additional options as described in Common Options.
-
Returns:
-
a contract instance with the proxy address and the implementation interface.
upgradeProxy
async function upgradeProxy(
proxy: string | ethers.Contract,
Contract: ethers.ContractFactory,
opts?: {
call?: string | { fn: string; args?: unknown[] },
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
constructorArgs?: unknown[],
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
txOverrides?: ethers.Overrides,
kind?: 'uups' | 'transparent',
},
): Promise<ethers.Contract>
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.
Parameters:
-
proxy
- the proxy address or proxy contract instance. -
Contract
- an ethers contract factory to use as the new implementation. -
opts
- an object with options:-
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. -
additional options as described in Common Options.
-
Returns:
-
a contract instance with the proxy address and the new implementation interface.
deployBeacon
async function deployBeacon(
Contract: ethers.ContractFactory,
opts?: {
unsafeAllow?: ValidationError[],
constructorArgs?: unknown[],
initialOwner?: string,
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
txOverrides?: ethers.Overrides,
},
): Promise<ethers.Contract>
Creates an upgradable beacon given an ethers contract factory to use as implementation, and returns the beacon contract instance.
Parameters:
-
Contract
- an ethers contract factory to use as the implementation. -
opts
- an object with options:-
additional options as described in Common Options.
-
Returns:
-
the beacon contract instance.
Since:
-
@openzeppelin/[email protected]
upgradeBeacon
async function upgradeBeacon(
beacon: string | ethers.Contract,
Contract: ethers.ContractFactory,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
constructorArgs?: unknown[],
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
txOverrides?: ethers.Overrides,
},
): Promise<ethers.Contract>
Upgrades an upgradable beacon at a specified address to a new implementation contract, and returns the beacon contract instance.
Parameters:
-
beacon
- the beacon address or beacon contract instance. -
Contract
- an ethers contract factory to use as the new implementation. -
opts
- an object with options:-
additional options as described in Common Options.
-
Returns:
-
the beacon contract instance.
Since:
-
@openzeppelin/[email protected]
deployBeaconProxy
async function deployBeaconProxy(
beacon: string | ethers.Contract,
attachTo: ethers.ContractFactory,
args: unknown[] = [],
opts?: {
initializer?: string | false,
txOverrides?: ethers.Overrides,
useDefenderDeploy?: boolean,
proxyFactory?: ethers.ContractFactory,
deployFunction?: () => Promise<EthersOrDefenderDeployment>,
},
): Promise<ethers.Contract>
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.
Parameters:
-
beacon
- the beacon address or beacon contract instance. -
attachTo
- an ethers contract factory corresponding to the beacon’s current implementation contract. -
args
- arguments for the initializer function. -
opts
- an object with options:-
initializer
: set a different initializer function to call (see Specifying Fragments), or specifyfalse
to disable initialization. -
additional options as described in Common Options.
-
Returns:
-
a contract instance with the beacon proxy address and the implementation interface.
Since:
-
@openzeppelin/[email protected]
forceImport
async function forceImport(
address: string,
deployedImpl: ethers.ContractFactory,
opts?: {
kind?: 'uups' | 'transparent' | 'beacon',
},
): Promise<ethers.Contract>
Forces the import of an existing proxy, beacon, or implementation contract deployment to be used with this plugin. Provide the address of an existing proxy, beacon or implementation, along with the ethers contract factory of the implementation contract that was deployed.
When importing a proxy or beacon, the deployedImpl argument must be the contract factory of the current implementation contract version that is being used, not the version that you are planning to upgrade to.
|
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 and implementation contracts.
Parameters:
-
address
- the address of an existing proxy, beacon or implementation. -
deployedImpl
- the ethers contract factory of the implementation contract that was deployed. -
opts
- an object with options:-
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.
-
Returns:
-
a contract instance representing the imported proxy, beacon or implementation.
Since
-
@openzeppelin/[email protected]
validateImplementation
async function validateImplementation(
Contract: ethers.ContractFactory,
opts?: {
unsafeAllow?: ValidationError[],
kind?: 'uups' | 'transparent' | 'beacon',
},
): Promise<void>
Validates an implementation contract without deploying it.
Parameters:
-
Contract
- the ethers contract factory of the implementation contract. -
opts
- an object with options:-
additional options as described in Common Options.
-
Since:
-
@openzeppelin/[email protected]
deployImplementation
async function deployImplementation(
Contract: ethers.ContractFactory,
opts?: {
unsafeAllow?: ValidationError[],
constructorArgs?: unknown[],
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
txOverrides?: ethers.Overrides,
getTxResponse?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
useDefenderDeploy?: boolean,
},
): Promise<string | ethers.providers.TransactionResponse>
Validates and deploys an implementation contract, and returns its address.
Parameters:
-
Contract
- an ethers contract factory to use as the implementation. -
opts
- an object with options:-
getTxResponse
: if set totrue
, 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 offorceImport
, only the address will be returned. -
additional options as described in Common Options.
-
Returns:
-
the address or an ethers transaction response corresponding to the deployment of the implementation contract.
Since:
-
@openzeppelin/[email protected]
validateUpgrade
async function validateUpgrade(
referenceAddressOrContract: string | ethers.ContractFactory,
newContract: ethers.ContractFactory,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
},
): Promise<void>
Validates a new implementation contract without deploying it and without actually upgrading to it. Compares the current implementation contract to the new implementation contract to check for storage layout compatibility errors. If referenceAddressOrContract
is the current implementation address, the kind
option is required.
Parameters:
-
referenceAddressOrContract
- a proxy or beacon address that uses the current implementation, or an address or ethers contract factory corresponding to the current implementation. -
newContract
- the new implementation contract. -
opts
- an object with options:-
additional options as described in Common Options.
-
Since:
-
@openzeppelin/[email protected]
Examples:
Validate upgrading an existing proxy to a new contract (replace PROXY_ADDRESS
with the address of your proxy):
const { ethers, upgrades } = require('hardhat');
const BoxV2 = await ethers.getContractFactory('BoxV2');
await upgrades.validateUpgrade(PROXY_ADDRESS, BoxV2);
Validate upgrading between two contract implementations:
const { ethers, upgrades } = require('hardhat');
const Box = await ethers.getContractFactory('Box');
const BoxV2 = await ethers.getContractFactory('BoxV2');
await upgrades.validateUpgrade(Box, BoxV2);
prepareUpgrade
async function prepareUpgrade(
referenceAddressOrContract: string | ethers.Contract,
Contract: ethers.ContractFactory,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
constructorArgs?: unknown[],
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
txOverrides?: ethers.Overrides,
getTxResponse?: boolean,
kind?: 'uups' | 'transparent' | 'beacon',
useDefenderDeploy?: boolean,
},
): Promise<string | ethers.providers.TransactionResponse>
Validates and deploys a new implementation contract, and returns its address. If referenceAddressOrContract
is the current implementation address, the kind
option is required. Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from Hardhat.
Parameters:
-
referenceAddressOrContract
- the proxy or beacon or implementation address or contract instance. -
Contract
- the new implementation contract. -
opts
- an object with options:-
getTxResponse
: if set totrue
, 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 offorceImport
, only the address will be returned. -
additional options as described in Common Options.
-
Returns:
-
the address or an ethers transaction response corresponding to the deployment of the new implementation contract.
defender.deployContract
async function deployContract(
Contract: ethers.ContractFactory,
args: unknown[] = [],
opts?: {
unsafeAllowDeployContract?: boolean,
pollingInterval?: number,
},
): Promise<ethers.Contract>
Deploys a non-upgradeable contract using OpenZeppelin Defender, and returns a contract instance. Throws an error if the contract looks like an implementation contract.
Do not use this function to deploy implementations of upgradeable contracts, because upgrade safety validations are not performed with this function. For implementation contracts, use deployImplementation instead. |
Parameters:
-
Contract
- an ethers contract factory to use as the contract to deploy. -
opts
- an object with options:-
unsafeAllowDeployContract
: if set totrue
, allows the contract to be deployed even if it looks like an implementation contract. Defaults tofalse
. -
pollingInterval
: polling interval in milliseconds between checks for the transaction confirmation when calling.waitForDeployment()
on the resulting contract instance. Defaults to5000
.
-
Returns:
-
the contract instance.
Since:
-
@openzeppelin/[email protected]
defender.getDeployApprovalProcess
async function getDeployApprovalProcess(
): Promise<{
approvalProcessId: string,
address?: string,
viaType?: 'EOA' | 'Contract' | 'Multisig' | 'Safe' | 'Gnosis Multisig' | 'Relayer' | 'Unknown' | 'Timelock Controller' | 'ERC20' | 'Governor' | 'Fireblocks',
}>
Gets the default deploy approval process configured for your deployment environment on OpenZeppelin Defender.
Returns:
-
an object with the default deploy approval process ID and the associated address, such as a Relayer, EOA, or multisig wallet address.
Since:
-
@openzeppelin/[email protected]
defender.getUpgradeApprovalProcess
async function getUpgradeApprovalProcess(
): Promise<{
approvalProcessId: string,
address?: string,
viaType?: 'EOA' | 'Contract' | 'Multisig' | 'Safe' | 'Gnosis Multisig' | 'Relayer' | 'Unknown' | 'Timelock Controller' | 'ERC20' | 'Governor' | 'Fireblocks',
}>
Gets the default upgrade approval process configured for your deployment environment on OpenZeppelin Defender. For example, this is useful for determining the default multisig wallet that you can use in your scripts to assign as the owner of your proxy.
Returns:
-
an object with the default upgrade approval process ID and the associated address, such as a multisig or governor contract address.
Since:
-
@openzeppelin/[email protected]
defender.proposeUpgradeWithApproval
async function proposeUpgradeWithApproval(
proxyAddress: string,
ImplFactory: ContractFactory,
opts?: {
unsafeAllow?: ValidationError[],
unsafeAllowRenames?: boolean,
unsafeSkipStorageCheck?: boolean,
constructorArgs?: unknown[],
timeout?: number,
pollingInterval?: number,
redeployImplementation?: 'always' | 'never' | 'onchange',
kind?: 'uups' | 'transparent' | 'beacon',
useDefenderDeploy?: boolean,
approvalProcessId?: string,
},
): Promise<{
proposalId: string,
url: string,
txResponse?: ethers.providers.TransactionResponse,
}>
Proposes an upgrade using an upgrade approval process on OpenZeppelin Defender.
Similar to prepareUpgrade
. This method validates and deploys the new implementation contract, but also proposes an upgrade using an upgrade approval process on OpenZeppelin Defender. Supported for UUPS or Transparent proxies. Not currently supported for beacon proxies or beacons. For beacons, use prepareUpgrade
along with a transaction proposal on Defender to upgrade the beacon to the deployed implementation.
Parameters:
-
proxyAddress
- the proxy address. -
ImplFactory
- the new implementation contract. -
opts
- an object with options:-
approvalProcessId
: The ID of the upgrade approval process. Defaults to the upgrade approval process configured for your deployment environment on Defender. -
additional options as described in Common Options.
-
Returns:
-
an object with the Defender proposal ID, the URL of the proposal in Safe App if applicable, 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.
Since:
-
@openzeppelin/[email protected]
admin.changeProxyAdmin
async function changeProxyAdmin(
proxyAddress: string,
newAdmin: string,
signer?: ethers.Signer,
opts?: {
txOverrides?: ethers.Overrides,
}
): Promise<void>
Changes the admin for a specific proxy.
This function is not supported with admins or proxies from OpenZeppelin Contracts 5.x. |
Parameters:
-
proxyAddress
- the address of the proxy to change. -
newAdmin
- the new admin address. -
signer
- the signer to use for the transaction. -
opts
- an object with options:-
additional options as described in Common Options.
-
admin.transferProxyAdminOwnership
async function transferProxyAdminOwnership(
proxyAddress: string,
newOwner: string,
signer?: ethers.Signer,
opts?: {
silent?: boolean,
txOverrides?: ethers.Overrides,
}
): Promise<void>
Changes the owner of the proxy admin contract for a specific proxy.
The proxyAddress parameter is required since @openzeppelin/[email protected]
|
Parameters:
-
proxyAddress
- the address of the proxy whose admin ownership is to be transferred. -
newOwner
- the new owner address for the proxy admin contract. -
signer
- the signer to use for the transaction. -
opts
- an object with options:-
silent
: if set totrue
, silences console logging about each proxy affected by the admin ownership transfer. -
additional options as described in Common Options.
-
Since:
-
@openzeppelin/[email protected]
erc1967
async function erc1967.getImplementationAddress(proxyAddress: string): Promise<string>;
async function erc1967.getBeaconAddress(proxyAddress: string): Promise<string>;
async function erc1967.getAdminAddress(proxyAddress: string): Promise<string>;
Functions in this module provide access to the ERC1967 variables of a proxy contract.
Parameters:
-
proxyAddress
- the proxy address.
Returns:
-
the implementation, beacon, or admin address depending on the function called.
beacon
async function beacon.getImplementationAddress(beaconAddress: string): Promise<string>;
This module provides a convenience function to get the implementation address from a beacon contract.
Parameters:
-
beaconAddress
- the beacon address.
Returns:
-
the implementation address.
Since:
-
@openzeppelin/[email protected]
silenceWarnings
function silenceWarnings()
This function is useful for tests, but its use in production deployment scripts is discouraged. |
Silences all subsequent warnings about the use of unsafe flags. Prints a last warning before doing so.
verify
Extends hardhat-verify's verify
task to completely verify a proxy on Etherscan. This supports verifying proxy contracts that were deployed by the Hardhat Upgrades plugin.
The arguments are the same as for hardhat-verify’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-verify will be run on the address instead.
The following contracts will be verified when you run this task on your proxy address:
-
Your implementation contract
-
ERC1967Proxy or TransparentUpgradeableProxy or BeaconProxy (for UUPS, transparent, or beacon proxies, respectively)
-
ProxyAdmin (with transparent proxies)
-
UpgradeableBeacon (with beacon proxies)
Since:
-
@openzeppelin/[email protected]
Usage:
To use this task, ensure you have hardhat-verify installed:
npm install --save-dev @nomicfoundation/hardhat-verify
Then import the @nomicfoundation/hardhat-verify
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("@nomicfoundation/hardhat-verify");
require("@openzeppelin/hardhat-upgrades");
Or if you are using TypeScript, import the plugins in hardhat.config.ts
:
import "@nomicfoundation/hardhat-verify";
import "@openzeppelin/hardhat-upgrades";
Finally, follow hardhat-verify’s usage documentation to configure your Etherscan API key and run the verify
task from the command line with the proxy address:
npx hardhat verify --network mainnet PROXY_ADDRESS
or programmatically using the verify:verify
subtask:
await hre.run("verify:verify", { address: PROXY_ADDRESS, });
Note that you do not need to include constructor arguments when verifying 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 according to the usage information for the task or subtask.