From 1a8e8a20fbd5e8d2fed09e0e72d00f9a31d3a185 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 15:59:16 -0400 Subject: [PATCH 1/8] Split hardhat-upgrades docs into current (HH3) and legacy (HH2) --- .../upgrades-plugins/api-hardhat-upgrades.mdx | 4 + .../hardhat-2/api-hardhat-upgrades.mdx | 697 ++++++++++++++++++ .../hardhat-2/hardhat-upgrades.mdx | 161 ++++ content/upgrades-plugins/hardhat-upgrades.mdx | 4 + src/navigation/ethereum-evm.json | 22 +- 5 files changed, 885 insertions(+), 3 deletions(-) create mode 100644 content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx create mode 100644 content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx diff --git a/content/upgrades-plugins/api-hardhat-upgrades.mdx b/content/upgrades-plugins/api-hardhat-upgrades.mdx index 326439ae..191da6c1 100644 --- a/content/upgrades-plugins/api-hardhat-upgrades.mdx +++ b/content/upgrades-plugins/api-hardhat-upgrades.mdx @@ -2,6 +2,10 @@ title: OpenZeppelin Hardhat Upgrades API --- + +This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat 2 (legacy) API docs](/upgrades-plugins/hardhat-2/api-hardhat-upgrades). + + Both `deployProxy` and `upgradeProxy` functions will return instances of [ethers.js contracts](https://docs.ethers.io/v5/api/contract/contract), and require [ethers.js contract factories](https://docs.ethers.io/v5/api/contract/contract-factory) as arguments. For [beacons](/contracts/5.x/api/proxy#beacon), `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 diff --git a/content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx new file mode 100644 index 00000000..bcf3e7bc --- /dev/null +++ b/content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx @@ -0,0 +1,697 @@ +--- +title: OpenZeppelin Hardhat Upgrades API +--- + + +You're viewing the Hardhat 2 (legacy) documentation. The latest documentation is available for [Hardhat 3](/upgrades-plugins/api-hardhat-upgrades). + + +Both `deployProxy` and `upgradeProxy` functions will return instances of [ethers.js contracts](https://docs.ethers.io/v5/api/contract/contract), and require [ethers.js contract factories](https://docs.ethers.io/v5/api/contract/contract-factory) as arguments. For [beacons](/contracts/5.x/api/proxy#beacon), `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()` and `upgradeProxy()` only support the values `"uups" | "transparent"`. Defaults to `"transparent"`. See [Transparent vs UUPS](/contracts/5.x/api/proxy#transparent-vs-uups). +* `unsafeAllow`: (`ValidationError[]`) Selectively disable one or more validation errors or warnings: + * `"external-library-linking"`: Allows a deployment with external libraries linked to the implementation contract. (External libraries are otherwise [not yet supported](/upgrades-plugins/faq#why-cant-i-use-external-libraries).) + * `"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. See `constructorArgs`. + * `"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`?](/upgrades-plugins/faq#can-i-safely-use-delegatecall-and-selfdestruct) + * `"missing-public-upgradeto"`: Allow UUPS implementations that do not contain a public `upgradeTo` or `upgradeToAndCall` 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?](/upgrades-plugins/faq#how-can-i-use-internal-functions-in-storage-variables) + * `"missing-initializer"`: Allows implementations where an initializer function is not detected. + * `"missing-initializer-call"`: Allows implementations where a parent initializer is not called from the child initializer. + * `"duplicate-initializer-call"`: Allows implementations where a parent initializer is called more than once from the child initializer. + * `"incorrect-initializer-order"`: Allows implementations where parent initializers are not called in linearized order. **Note**: This condition shows a warning by default, and setting this option will silence the warning. +* `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/hardhat-upgrades@3.0.0` +* `unsafeSkipProxyAdminCheck`: (`boolean`) Skips checking the `initialOwner` option when deploying a transparent proxy. When deploying a transparent proxy, the `initialOwner` 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 the `initialOwner` is not a ProxyAdmin contract. + * **Since:** `@openzeppelin/hardhat-upgrades@3.4.0` +* `timeout`: (`number`) Timeout in milliseconds to wait for the transaction confirmation when deploying an implementation 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. Defaults to `5000`. +* `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 the `salt` 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](https://docs.ethers.org/v6/api/contract/#Overrides) object to override transaction parameters, such as `gasLimit` and `gasPrice`. Applies to all transactions sent by a function with this option, even if the function sends multiple transactions. For OpenZeppelin Defender deployments, only the `gasLimit`, `gasPrice`, `maxFeePerGas`, and `maxPriorityFeePerGas` parameters are supported. +* `useDefenderDeploy`: (`boolean`) Deploy contracts using OpenZeppelin Defender instead of ethers.js. See [Using with OpenZeppelin Defender](/upgrades-plugins/defender-deploy). +* `verifySourceCode`: (`boolean`) When using OpenZeppelin Defender deployments, whether to verify source code on block explorers. Defaults to `true`. +* `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 affects `msg.sender` behavior. See [Caveats](/defender/tutorial/deploy#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](/defender/module/deploy#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](https://github.com/OpenZeppelin/openzeppelin-upgrades/blob/master/packages/plugin-hardhat/src/utils/factories.ts) for the default contract factory for each kind of proxy. + * **Since:** `@openzeppelin/hardhat-upgrades@3.7.0` +* `deployFunction`: (`(hre, opts, factory, ...args) => Promise`) Customizes the function used to deploy the proxy. Can be used along with the `proxyFactory` option to override constructor parameters for custom proxy deployments. See [deploy.ts](https://github.com/OpenZeppelin/openzeppelin-upgrades/blob/master/packages/plugin-hardhat/src/utils/deploy.ts) for the default deploy function. + * **Since:** `@openzeppelin/hardhat-upgrades@3.7.0` + +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?](/upgrades-plugins/faq#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. +* `useDeployedImplementation`: (`boolean`) Equivalent to setting `redeployImplementation` to `"never"`. + +## deployProxy + +```ts +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, + , +): Promise +``` + +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](https://docs.ethers.io/v5/api/utils/abi/interface/#Interface--specifying-fragments)), or specify `false` to disable initialization. + * additional options as described in [Common Options](#common-options). + +**Returns:** + +* a contract instance with the proxy address and the implementation interface. + +## upgradeProxy + +```ts +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 +``` + +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](https://docs.ethers.io/v5/api/utils/abi/interface/#Interface--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](#common-options). + +**Returns:** + +* a contract instance with the proxy address and the new implementation interface. + +## deployBeacon + +```ts +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 +``` + +Creates an [upgradable beacon](/contracts/5.x/api/proxy#UpgradeableBeacon) 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](#common-options). + +**Returns:** + +* the beacon contract instance. + +**Since:** + +* `@openzeppelin/hardhat-upgrades@1.13.0` + +## upgradeBeacon + +```ts +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 +``` + +Upgrades an [upgradable beacon](/contracts/5.x/api/proxy#UpgradeableBeacon) 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](#common-options). + +**Returns:** + +* the beacon contract instance. + +**Since**: + +* `@openzeppelin/hardhat-upgrades@1.13.0` + +## deployBeaconProxy + +```ts +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, + , +): Promise +``` + +Creates a [Beacon proxy](/contracts/5.x/api/proxy#BeaconProxy) 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](https://docs.ethers.io/v5/api/utils/abi/interface/#Interface--specifying-fragments)), or specify `false` to disable initialization. + * additional options as described in [Common Options](#common-options). + +**Returns:** + +* a contract instance with the beacon proxy address and the implementation interface. + +**Since:** + +* `@openzeppelin/hardhat-upgrades@1.13.0` + +## forceImport + +```ts +async function forceImport( + address: string, + deployedImpl: ethers.ContractFactory, + opts?: + kind?: 'uups' | 'transparent' | 'beacon', + , +): Promise +``` + +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](/upgrades-plugins/network-files) 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/hardhat-upgrades@1.15.0` + +## validateImplementation + +```ts +async function validateImplementation( + Contract: ethers.ContractFactory, + opts?: + unsafeAllow?: ValidationError[], + kind?: 'uups' | 'transparent' | 'beacon', + , +): Promise +``` + +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](#common-options). + +**Since:** + +* `@openzeppelin/hardhat-upgrades@1.20.0` + +## deployImplementation + +```ts +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 +``` + +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 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. + * additional options as described in [Common Options](#common-options). + +**Returns:** + +* the address or an ethers transaction response corresponding to the deployment of the implementation contract. + +**Since:** + +* `@openzeppelin/hardhat-upgrades@1.20.0` + +## validateUpgrade + +```ts +async function validateUpgrade( + referenceAddressOrContract: string | ethers.ContractFactory, + newContract: ethers.ContractFactory, + opts?: + unsafeAllow?: ValidationError[], + unsafeAllowRenames?: boolean, + unsafeSkipStorageCheck?: boolean, + kind?: 'uups' | 'transparent' | 'beacon', + , +): Promise +``` + +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](#common-options). + +**Since:** + +* `@openzeppelin/hardhat-upgrades@1.20.0` + +**Examples:** + +Validate upgrading an existing proxy to a new contract (replace `PROXY_ADDRESS` with the address of your proxy): +```ts +const ethers, upgrades = require('hardhat'); + +const BoxV2 = await ethers.getContractFactory('BoxV2'); +await upgrades.validateUpgrade(PROXY_ADDRESS, BoxV2); +``` + +Validate upgrading between two contract implementations: +```ts +const ethers, upgrades = require('hardhat'); + +const Box = await ethers.getContractFactory('Box'); +const BoxV2 = await ethers.getContractFactory('BoxV2'); +await upgrades.validateUpgrade(Box, BoxV2); +``` + +## prepareUpgrade + +```ts +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 +``` + +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 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. + * additional options as described in [Common Options](#common-options). + +**Returns:** + +* the address or an ethers transaction response corresponding to the deployment of the new implementation contract. + +## defender.deployContract + +```ts +async function deployContract( + Contract: ethers.ContractFactory, + args: unknown[] = [], + opts?: + unsafeAllowDeployContract?: boolean, + pollingInterval?: number, + , +): Promise +``` + +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](#deployimplementation) instead. + + +**Parameters:** + +* `Contract` - an ethers contract factory to use as the contract to deploy. +* `opts` - an object with options: + * `unsafeAllowDeployContract`: if set to `true`, allows the contract to be deployed even if it looks like an implementation contract. Defaults to `false`. + * `pollingInterval`: polling interval in milliseconds between checks for the transaction confirmation when calling `.waitForDeployment()` on the resulting contract instance. Defaults to `5000`. + +**Returns:** + +* the contract instance. + +**Since:** + +* `@openzeppelin/hardhat-upgrades@2.2.0` + +## defender.getDeployApprovalProcess + +```ts +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/hardhat-upgrades@2.5.0` + +## defender.getUpgradeApprovalProcess + +```ts +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/hardhat-upgrades@2.5.0` + +## defender.proposeUpgradeWithApproval + +```ts +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](#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/hardhat-upgrades@2.2.0` + +## admin.changeProxyAdmin + +```ts +async function changeProxyAdmin( + proxyAddress: string, + newAdmin: string, + signer?: ethers.Signer, + opts?: + txOverrides?: ethers.Overrides, + +): Promise +``` + +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](#common-options). + +## admin.transferProxyAdminOwnership + +```ts +async function transferProxyAdminOwnership( + proxyAddress: string, + newOwner: string, + signer?: ethers.Signer, + opts?: + silent?: boolean, + txOverrides?: ethers.Overrides, + +): Promise +``` + +Changes the owner of the proxy admin contract for a specific proxy. + + +The `proxyAddress` parameter is required since `@openzeppelin/hardhat-upgrades@3.0.0` + + +**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 to `true`, silences console logging about each proxy affected by the admin ownership transfer. + * additional options as described in [Common Options](#common-options). + +**Since:** + +* `@openzeppelin/hardhat-upgrades@3.0.0` + +## erc1967 + +```ts +async function erc1967.getImplementationAddress(proxyAddress: string): Promise; +async function erc1967.getBeaconAddress(proxyAddress: string): Promise; +async function erc1967.getAdminAddress(proxyAddress: string): Promise; +``` + +Functions in this module provide access to the [ERC1967](https://eips.ethereum.org/EIPS/eip-1967) variables of a proxy contract. + +**Parameters:** + +* `proxyAddress` - the proxy address. + +**Returns:** + +* the implementation, beacon, or admin address depending on the function called. + +## beacon + +```ts +async function beacon.getImplementationAddress(beaconAddress: string): Promise; +``` + +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/hardhat-upgrades@1.13.0` + +## silenceWarnings + +```ts +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](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-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](/contracts/5.x/api/proxy#ERC1967Proxy) or [TransparentUpgradeableProxy](/contracts/5.x/api/proxy#TransparentUpgradeableProxy) or [BeaconProxy](/contracts/5.x/api/proxy#BeaconProxy) (for UUPS, transparent, or beacon proxies, respectively) +* [ProxyAdmin](/contracts/5.x/api/proxy#ProxyAdmin) (with transparent proxies) +* [UpgradeableBeacon](/contracts/5.x/api/proxy#UpgradeableBeacon) (with beacon proxies) + +**Since:** + +* `@openzeppelin/hardhat-upgrades@2.0.0` + +**Usage:** + +To use this task, ensure you have hardhat-verify installed: +```sh +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`: +```js +require("@nomicfoundation/hardhat-verify"); +require("@openzeppelin/hardhat-upgrades"); +``` +Or if you are using TypeScript, import the plugins in `hardhat.config.ts`: +```ts +import "@nomicfoundation/hardhat-verify"; +import "@openzeppelin/hardhat-upgrades"; +``` + +Finally, follow [hardhat-verify’s usage documentation](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#usage) 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](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#using-programmatically): +```javascript +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](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#usage) or [subtask](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#using-programmatically). diff --git a/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx new file mode 100644 index 00000000..4d83452f --- /dev/null +++ b/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx @@ -0,0 +1,161 @@ +--- +title: Using with Hardhat +--- + + +You're viewing the Hardhat 2 (legacy) documentation. The latest documentation is available for [Hardhat 3](/upgrades-plugins/hardhat-upgrades). + + +This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Depends on `ethers.js`. + + +Check out the [step by step tutorial](https://forum.openzeppelin.com/t/openzeppelin-buidler-upgrades-step-by-step-tutorial/3580), showing from creating, testing and deploying, all the way through to upgrading with Gnosis Safe. + + +## Installation + +```console +$ npm install --save-dev @openzeppelin/hardhat-upgrades +$ npm install --save-dev @nomicfoundation/hardhat-ethers ethers # peer dependencies +``` + +And register the plugin in your [`hardhat.config.js`](https://hardhat.org/config): + +```js +require('@openzeppelin/hardhat-upgrades'); +``` + +## Usage in scripts + +### Proxies + +You can use this plugin in a [Hardhat script](https://hardhat.org/guides/scripts.html) to deploy an upgradeable instance of one of your contracts via the `deployProxy` function: + +```js +// scripts/create-box.js +const ethers, upgrades = require("hardhat"); + +async function main() + const Box = await ethers.getContractFactory("Box"); + const box = await upgrades.deployProxy(Box, [42]); + await box.waitForDeployment(); + console.log("Box deployed to:", await box.getAddress()); + + +main(); +``` + +This will automatically check that the `Box` contract is upgrade-safe, deploy an implementation contract for the `Box` contract (unless there is one already from a previous deployment), create a proxy (along with a proxy admin if needed), and initialize it by calling `initialize(42)`. + +Then, in another script, you can use the `upgradeProxy` function to upgrade the deployed instance to a new version. The new version can be a different contract (such as `BoxV2`), or you can just modify the existing `Box` contract and recompile it - the plugin will note it changed. + +```js +// scripts/upgrade-box.js +const ethers, upgrades = require("hardhat"); + +async function main() + const BoxV2 = await ethers.getContractFactory("BoxV2"); + const box = await upgrades.upgradeProxy(BOX_ADDRESS, BoxV2); + console.log("Box upgraded"); + + +main(); +``` + +> Note: While this plugin keeps track of all the implementation contracts you have deployed per network, in order to reuse them and validate storage compatibilities, it does _not_ keep track of the proxies you have deployed. This means that you will need to manually keep track of each deployment address, to supply those to the upgrade function when needed. + +The plugin will take care of comparing `BoxV2` to the previous one to ensure they are compatible for the upgrade, deploy the new `BoxV2` implementation contract (unless there is one already from a previous deployment), and upgrade the existing proxy to the new implementation. + +### Beacon proxies + +You can also use this plugin to deploy an upgradeable beacon for your contract with the `deployBeacon` function, then deploy one or more beacon proxies that point to it by using the `deployBeaconProxy` function. + +```js +// scripts/create-box.js +const ethers, upgrades = require("hardhat"); + +async function main() + const Box = await ethers.getContractFactory("Box"); + + const beacon = await upgrades.deployBeacon(Box); + await beacon.waitForDeployment(); + console.log("Beacon deployed to:", await beacon.getAddress()); + + const box = await upgrades.deployBeaconProxy(beacon, Box, [42]); + await box.waitForDeployment(); + console.log("Box deployed to:", await box.getAddress()); + + +main(); +``` + +Then, in another script, you can use the `upgradeBeacon` function to upgrade the beacon to a new version. When the beacon is upgraded, all of the beacon proxies that point to it will use the new contract implementation. + +```js +// scripts/upgrade-box.js +const ethers, upgrades = require("hardhat"); + +async function main() + const BoxV2 = await ethers.getContractFactory("BoxV2"); + + await upgrades.upgradeBeacon(BEACON_ADDRESS, BoxV2); + console.log("Beacon upgraded"); + + const box = BoxV2.attach(BOX_ADDRESS); + + +main(); +``` + +## Usage in tests + +You can also use the plugin’s functions from your Hardhat tests, in case you want to add tests for upgrading your contracts (which you should!). The API is the same as in scripts. + +### Proxies + +```js +const expect = require("chai"); + +describe("Box", function() + it('works', async () => { + const Box = await ethers.getContractFactory("Box"); + const BoxV2 = await ethers.getContractFactory("BoxV2"); + + const instance = await upgrades.deployProxy(Box, [42]); + const upgraded = await upgrades.upgradeProxy(await instance.getAddress(), BoxV2); + + const value = await upgraded.value(); + expect(value.toString()).to.equal('42'); + ); +}); +``` + +### Beacon proxies + +```js +const expect = require("chai"); + +describe("Box", function() + it('works', async () => { + const Box = await ethers.getContractFactory("Box"); + const BoxV2 = await ethers.getContractFactory("BoxV2"); + + const beacon = await upgrades.deployBeacon(Box); + const instance = await upgrades.deployBeaconProxy(beacon, Box, [42]); + + await upgrades.upgradeBeacon(beacon, BoxV2); + const upgraded = BoxV2.attach(await instance.getAddress()); + + const value = await upgraded.value(); + expect(value.toString()).to.equal('42'); + ); +}); +``` + +## Usage with Defender + +If you are using OpenZeppelin Defender, see [OpenZeppelin Defender with Hardhat](/upgrades-plugins/defender-deploy) for how to use it for deployments. + +## API + +See [Hardhat Upgrades API](/upgrades-plugins/api-hardhat-upgrades) for the full API documentation. diff --git a/content/upgrades-plugins/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-upgrades.mdx index 4fbe4f1b..d0d6b843 100644 --- a/content/upgrades-plugins/hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-upgrades.mdx @@ -2,6 +2,10 @@ title: Using with Hardhat --- + +This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat 2 (legacy) docs](/upgrades-plugins/hardhat-2/hardhat-upgrades). + + This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Depends on `ethers.js`. diff --git a/src/navigation/ethereum-evm.json b/src/navigation/ethereum-evm.json index 8ce1463b..a3f1f86f 100644 --- a/src/navigation/ethereum-evm.json +++ b/src/navigation/ethereum-evm.json @@ -659,7 +659,7 @@ "name": "Using with Hardhat", "index": { "type": "page", - "name": "Overview", + "name": "Hardhat 3", "url": "/upgrades-plugins/hardhat-upgrades" }, "children": [ @@ -667,6 +667,11 @@ "type": "page", "name": "Network Files", "url": "/upgrades-plugins/network-files" + }, + { + "type": "page", + "name": "Hardhat 2 (legacy)", + "url": "/upgrades-plugins/hardhat-2/hardhat-upgrades" } ] }, @@ -695,9 +700,20 @@ "name": "API Reference" }, { - "type": "page", + "type": "folder", "name": "Hardhat Upgrades API", - "url": "/upgrades-plugins/api-hardhat-upgrades" + "index": { + "type": "page", + "name": "Hardhat 3", + "url": "/upgrades-plugins/api-hardhat-upgrades" + }, + "children": [ + { + "type": "page", + "name": "Hardhat 2 (legacy)", + "url": "/upgrades-plugins/hardhat-2/api-hardhat-upgrades" + } + ] }, { "type": "folder", From 35d95e055e71bd1153e108483943ffc6d3425d5b Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 17:38:26 -0400 Subject: [PATCH 2/8] Update contents for Hardhat 3 --- .../upgrades-plugins/api-hardhat-upgrades.mdx | 72 +++-- content/upgrades-plugins/hardhat-upgrades.mdx | 245 +++++++++++++----- 2 files changed, 231 insertions(+), 86 deletions(-) diff --git a/content/upgrades-plugins/api-hardhat-upgrades.mdx b/content/upgrades-plugins/api-hardhat-upgrades.mdx index 191da6c1..1f86aec6 100644 --- a/content/upgrades-plugins/api-hardhat-upgrades.mdx +++ b/content/upgrades-plugins/api-hardhat-upgrades.mdx @@ -6,7 +6,25 @@ title: OpenZeppelin Hardhat Upgrades API This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat 2 (legacy) API docs](/upgrades-plugins/hardhat-2/api-hardhat-upgrades). -Both `deployProxy` and `upgradeProxy` functions will return instances of [ethers.js contracts](https://docs.ethers.io/v5/api/contract/contract), and require [ethers.js contract factories](https://docs.ethers.io/v5/api/contract/contract-factory) as arguments. For [beacons](/contracts/5.x/api/proxy#beacon), `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. +Both `deployProxy` and `upgradeProxy` functions will return instances of [ethers.js contracts](https://docs.ethers.org/v6/api/contract/#Contract), and require [ethers.js contract factories](https://docs.ethers.org/v6/api/contract/#ContractFactory) as arguments. For [beacons](/contracts/5.x/api/proxy#beacon), `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. + +## Setup + +In Hardhat 3, all of the functions below are accessed via an `upgradesApi` object created from the `upgrades` factory. Defender-specific functions (`defender.*`) are accessed via `defenderApi` from the `defender` factory. Both factories take a network connection as an argument; share one connection across your operations. + +```typescript +import hre from 'hardhat'; +import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'; + +const connection = await hre.network.connect(); +const { ethers } = connection; +const upgradesApi = await upgrades(hre, connection); + +// For Defender-specific functions: +const defenderApi = await defender(hre, connection); +``` + +`upgradesApi` exposes every top-level function documented below (e.g. `upgradesApi.deployProxy(...)`). `defenderApi` exposes the same functions plus the `defender.*` functions (e.g. `defenderApi.deployContract(...)`, `defenderApi.proposeUpgradeWithApproval(...)`). The `admin`, `erc1967`, and `beacon` namespaces are accessed as `upgradesApi.admin.*`, `upgradesApi.erc1967.*`, and `upgradesApi.beacon.*`. ## Common Options @@ -365,19 +383,29 @@ Validates a new implementation contract without deploying it and without actuall Validate upgrading an existing proxy to a new contract (replace `PROXY_ADDRESS` with the address of your proxy): ```ts -const ethers, upgrades = require('hardhat'); +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +const connection = await hre.network.connect(); +const { ethers } = connection; +const upgradesApi = await upgrades(hre, connection); const BoxV2 = await ethers.getContractFactory('BoxV2'); -await upgrades.validateUpgrade(PROXY_ADDRESS, BoxV2); +await upgradesApi.validateUpgrade(PROXY_ADDRESS, BoxV2); ``` Validate upgrading between two contract implementations: ```ts -const ethers, upgrades = require('hardhat'); +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +const connection = await hre.network.connect(); +const { ethers } = connection; +const upgradesApi = await upgrades(hre, connection); const Box = await ethers.getContractFactory('Box'); const BoxV2 = await ethers.getContractFactory('BoxV2'); -await upgrades.validateUpgrade(Box, BoxV2); +await upgradesApi.validateUpgrade(Box, BoxV2); ``` ## prepareUpgrade @@ -671,27 +699,25 @@ 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`: -```js -require("@nomicfoundation/hardhat-verify"); -require("@openzeppelin/hardhat-upgrades"); -``` -Or if you are using TypeScript, import the plugins in `hardhat.config.ts`: -```ts -import "@nomicfoundation/hardhat-verify"; -import "@openzeppelin/hardhat-upgrades"; +Then add both `@nomicfoundation/hardhat-verify` and `@openzeppelin/hardhat-upgrades` to the `plugins` array in your `hardhat.config.ts`. The upgrades plugin detects hardhat-verify and automatically extends its `verify` task: +```typescript +import { configVariable, defineConfig } from ‘hardhat/config’; +import hardhatVerify from ‘@nomicfoundation/hardhat-verify’; +import hardhatUpgrades from ‘@openzeppelin/hardhat-upgrades’; + +export default defineConfig({ + plugins: [hardhatVerify, hardhatUpgrades], + verify: { + etherscan: { + apiKey: configVariable(‘ETHERSCAN_API_KEY’), + }, + }, +}); ``` -Finally, follow [hardhat-verify’s usage documentation](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#usage) to configure your Etherscan API key and run the `verify` task from the command line with the proxy address: +Then 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](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#using-programmatically): -```javascript -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](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#usage) or [subtask](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#using-programmatically). +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](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#usage). diff --git a/content/upgrades-plugins/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-upgrades.mdx index d0d6b843..1039d42c 100644 --- a/content/upgrades-plugins/hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-upgrades.mdx @@ -6,10 +6,10 @@ title: Using with Hardhat This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat 2 (legacy) docs](/upgrades-plugins/hardhat-2/hardhat-upgrades). -This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Depends on `ethers.js`. +This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Requires `@nomicfoundation/hardhat-ethers`. -Check out the [step by step tutorial](https://forum.openzeppelin.com/t/openzeppelin-buidler-upgrades-step-by-step-tutorial/3580), showing from creating, testing and deploying, all the way through to upgrading with Gnosis Safe. +Migrating from Hardhat 2? See the [example projects](https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/plugin-hardhat/examples) for complete Transparent, UUPS, and Solidity-test examples using Hardhat 3. ## Installation @@ -19,47 +19,79 @@ $ npm install --save-dev @openzeppelin/hardhat-upgrades $ npm install --save-dev @nomicfoundation/hardhat-ethers ethers # peer dependencies ``` -And register the plugin in your [`hardhat.config.js`](https://hardhat.org/config): + +Hardhat 3 supports both ethers and viem. This plugin uses `@nomicfoundation/hardhat-ethers` internally and loads it automatically. If your project uses viem, install `@nomicfoundation/hardhat-viem` and add it to your config alongside this plugin. + + +Register the `@openzeppelin/hardhat-upgrades` plugin in your [`hardhat.config.ts`](https://hardhat.org/config/): -```js -require('@openzeppelin/hardhat-upgrades'); +```typescript +import { defineConfig } from 'hardhat/config'; +import hardhatUpgrades from '@openzeppelin/hardhat-upgrades'; + +export default defineConfig({ + plugins: [hardhatUpgrades], + // ... rest of config +}); ``` +If you use the `verify` task, also add `@nomicfoundation/hardhat-verify` to the `plugins` array and configure Hardhat's `verify.etherscan.apiKey` setting. + ## Usage in scripts +**Important:** Create a single network connection and share it across all operations. This ensures operations share the same context and state. + ### Proxies -You can use this plugin in a [Hardhat script](https://hardhat.org/guides/scripts.html) to deploy an upgradeable instance of one of your contracts via the `deployProxy` function: +You can use this plugin in a [Hardhat script](https://hardhat.org/docs/guides/writing-scripts) to deploy an upgradeable instance of one of your contracts via the `deployProxy` function: -```js -// scripts/create-box.js -const ethers, upgrades = require("hardhat"); +```typescript +// scripts/create-box.ts +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; -async function main() - const Box = await ethers.getContractFactory("Box"); - const box = await upgrades.deployProxy(Box, [42]); - await box.waitForDeployment(); - console.log("Box deployed to:", await box.getAddress()); +async function main() { + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); + const Box = await ethers.getContractFactory('Box'); + const box = await upgradesApi.deployProxy(Box, [42]); + await box.waitForDeployment(); + console.log('Box deployed to:', await box.getAddress()); +} -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); ``` This will automatically check that the `Box` contract is upgrade-safe, deploy an implementation contract for the `Box` contract (unless there is one already from a previous deployment), create a proxy (along with a proxy admin if needed), and initialize it by calling `initialize(42)`. -Then, in another script, you can use the `upgradeProxy` function to upgrade the deployed instance to a new version. The new version can be a different contract (such as `BoxV2`), or you can just modify the existing `Box` contract and recompile it - the plugin will note it changed. +Then, in another script, you can use the `upgradeProxy` function to upgrade the deployed instance to a new version. The new version can be a different contract (such as `BoxV2`), or you can just modify the existing `Box` contract and recompile it — the plugin will note it changed. -```js -// scripts/upgrade-box.js -const ethers, upgrades = require("hardhat"); +```typescript +// scripts/upgrade-box.ts +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; -async function main() - const BoxV2 = await ethers.getContractFactory("BoxV2"); - const box = await upgrades.upgradeProxy(BOX_ADDRESS, BoxV2); - console.log("Box upgraded"); +const PROXY_ADDRESS = '0x...'; // Replace with your proxy address +async function main() { + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); -main(); + const BoxV2 = await ethers.getContractFactory('BoxV2'); + await upgradesApi.upgradeProxy(PROXY_ADDRESS, BoxV2); + console.log('Box upgraded'); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); ``` > Note: While this plugin keeps track of all the implementation contracts you have deployed per network, in order to reuse them and validate storage compatibilities, it does _not_ keep track of the proxies you have deployed. This means that you will need to manually keep track of each deployment address, to supply those to the upgrade function when needed. @@ -70,88 +102,175 @@ The plugin will take care of comparing `BoxV2` to the previous one to ensure the You can also use this plugin to deploy an upgradeable beacon for your contract with the `deployBeacon` function, then deploy one or more beacon proxies that point to it by using the `deployBeaconProxy` function. -```js -// scripts/create-box.js -const ethers, upgrades = require("hardhat"); +```typescript +// scripts/create-box.ts +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +async function main() { + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); -async function main() - const Box = await ethers.getContractFactory("Box"); + const Box = await ethers.getContractFactory('Box'); - const beacon = await upgrades.deployBeacon(Box); + const beacon = await upgradesApi.deployBeacon(Box); await beacon.waitForDeployment(); - console.log("Beacon deployed to:", await beacon.getAddress()); + console.log('Beacon deployed to:', await beacon.getAddress()); - const box = await upgrades.deployBeaconProxy(beacon, Box, [42]); + const box = await upgradesApi.deployBeaconProxy(beacon, Box, [42]); await box.waitForDeployment(); - console.log("Box deployed to:", await box.getAddress()); + console.log('Box deployed to:', await box.getAddress()); +} - -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); ``` Then, in another script, you can use the `upgradeBeacon` function to upgrade the beacon to a new version. When the beacon is upgraded, all of the beacon proxies that point to it will use the new contract implementation. -```js -// scripts/upgrade-box.js -const ethers, upgrades = require("hardhat"); +```typescript +// scripts/upgrade-box.ts +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; -async function main() - const BoxV2 = await ethers.getContractFactory("BoxV2"); +const BEACON_ADDRESS = '0x...'; // Replace with your beacon address - await upgrades.upgradeBeacon(BEACON_ADDRESS, BoxV2); - console.log("Beacon upgraded"); +async function main() { + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); - const box = BoxV2.attach(BOX_ADDRESS); + const BoxV2 = await ethers.getContractFactory('BoxV2'); + await upgradesApi.upgradeBeacon(BEACON_ADDRESS, BoxV2); + console.log('Beacon upgraded'); +} - -main(); +main().catch(err => { + console.error(err); + process.exit(1); +}); ``` ## Usage in tests -You can also use the plugin’s functions from your Hardhat tests, in case you want to add tests for upgrading your contracts (which you should!). The API is the same as in scripts. +You can also use the plugin's functions from your Hardhat tests, in case you want to add tests for upgrading your contracts (which you should!). The API is the same as in scripts. + +**Important:** Share a single connection across all tests in a suite. Create the connection once in a `before` / `beforeAll` hook, or at module scope using ESM top-level `await`. ### Proxies -```js -const expect = require("chai"); +```typescript +import { expect } from 'chai'; +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +describe('Box', function () { + let upgradesApi; + let ethers; + + before(async () => { + const connection = await hre.network.connect(); + ({ ethers } = connection); + upgradesApi = await upgrades(hre, connection); + }); -describe("Box", function() it('works', async () => { - const Box = await ethers.getContractFactory("Box"); - const BoxV2 = await ethers.getContractFactory("BoxV2"); + const Box = await ethers.getContractFactory('Box'); + const BoxV2 = await ethers.getContractFactory('BoxV2'); - const instance = await upgrades.deployProxy(Box, [42]); - const upgraded = await upgrades.upgradeProxy(await instance.getAddress(), BoxV2); + const instance = await upgradesApi.deployProxy(Box, [42]); + const upgraded = await upgradesApi.upgradeProxy(await instance.getAddress(), BoxV2); const value = await upgraded.value(); expect(value.toString()).to.equal('42'); - ); + }); }); ``` ### Beacon proxies -```js -const expect = require("chai"); +```typescript +import { expect } from 'chai'; +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +describe('Box', function () { + let upgradesApi; + let ethers; + + before(async () => { + const connection = await hre.network.connect(); + ({ ethers } = connection); + upgradesApi = await upgrades(hre, connection); + }); -describe("Box", function() it('works', async () => { - const Box = await ethers.getContractFactory("Box"); - const BoxV2 = await ethers.getContractFactory("BoxV2"); + const Box = await ethers.getContractFactory('Box'); + const BoxV2 = await ethers.getContractFactory('BoxV2'); - const beacon = await upgrades.deployBeacon(Box); - const instance = await upgrades.deployBeaconProxy(beacon, Box, [42]); + const beacon = await upgradesApi.deployBeacon(Box); + const instance = await upgradesApi.deployBeaconProxy(beacon, Box, [42]); - await upgrades.upgradeBeacon(beacon, BoxV2); + await upgradesApi.upgradeBeacon(beacon, BoxV2); const upgraded = BoxV2.attach(await instance.getAddress()); const value = await upgraded.value(); expect(value.toString()).to.equal('42'); - ); + }); +}); +``` + +## Solidity tests + +Hardhat 3 supports writing tests in Solidity. You can use Solidity to test deployments, upgrades, and upgrade safety via the [`@openzeppelin/foundry-upgrades`](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades) Solidity library. + +For the Solidity API, see [`Upgrades.sol`](/upgrades-plugins/foundry/api/Upgrades) (deployment and upgrade functions) and [`Options.sol`](/upgrades-plugins/foundry/api/Options) (common options). + +This section is optional and is only needed if you want Solidity-based tests. + +Install: + +```console +$ npm install --save-dev @openzeppelin/foundry-upgrades +``` + +Configure your Hardhat config to enable Solidity tests and expose the artifacts directory to FFI: + +```typescript +import { defineConfig } from 'hardhat/config'; +import hardhatUpgrades, { proxyFilesToBuild } from '@openzeppelin/hardhat-upgrades'; + +if (!process.env.FOUNDRY_OUT) { + process.env.FOUNDRY_OUT = 'artifacts/contracts'; +} + +export default defineConfig({ + plugins: [hardhatUpgrades], + solidity: { + version: '0.8.28', + npmFilesToBuild: [...proxyFilesToBuild()], + }, + test: { + solidity: { + ffi: true, + fsPermissions: { + readDirectory: ['artifacts/contracts'], + }, + }, + }, }); ``` +Run `hardhat clean` before running your Solidity tests, or include the `--force` option when running `hardhat compile`. Any previous build artifacts must be cleared first to avoid duplicate contract definitions when the Solidity-based validation reads the build info: + +```console +$ npx hardhat compile --force +$ npx hardhat test solidity +``` + ## Usage with Defender If you are using OpenZeppelin Defender, see [OpenZeppelin Defender with Hardhat](/upgrades-plugins/defender-deploy) for how to use it for deployments. From a770b2c37c3a6b3ecb0ba5a25bf754d902ad8f8c Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 17:56:21 -0400 Subject: [PATCH 3/8] Add migrate from hardhat 2 guide --- content/upgrades-plugins/hardhat-upgrades.mdx | 2 +- .../migrate-from-hardhat-2.mdx | 225 ++++++++++++++++++ src/navigation/ethereum-evm.json | 5 + 3 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 content/upgrades-plugins/migrate-from-hardhat-2.mdx diff --git a/content/upgrades-plugins/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-upgrades.mdx index 1039d42c..22026f18 100644 --- a/content/upgrades-plugins/hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-upgrades.mdx @@ -9,7 +9,7 @@ This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat 2 (lega This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Requires `@nomicfoundation/hardhat-ethers`. -Migrating from Hardhat 2? See the [example projects](https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/plugin-hardhat/examples) for complete Transparent, UUPS, and Solidity-test examples using Hardhat 3. +Migrating from Hardhat 2? See the [Migration Guide](/upgrades-plugins/migrate-from-hardhat-2). ## Installation diff --git a/content/upgrades-plugins/migrate-from-hardhat-2.mdx b/content/upgrades-plugins/migrate-from-hardhat-2.mdx new file mode 100644 index 00000000..80d17786 --- /dev/null +++ b/content/upgrades-plugins/migrate-from-hardhat-2.mdx @@ -0,0 +1,225 @@ +--- +title: Migrating from Hardhat 2 +--- + + +**Prerequisite:** Migrate your Hardhat project to Hardhat 3 first. See the [official Hardhat 3 migration guide](https://hardhat.org/docs/migrate-from-hardhat2). + + +## Breaking Changes + +1. **No automatic `hre.upgrades`** - Must call factory function explicitly +2. **Factory functions are async** - Require `await` and network connection +3. **Import changes** - Import factory functions, not just the plugin +4. **Updated peerDependencies** - `hardhat` and `@nomicfoundation/hardhat-ethers` peer dependency versions have been updated for Hardhat 3. + +## Install Dependencies + +If upgrading from a previous version, ensure these packages are in your `devDependencies`: + +```bash +npm install --save-dev hardhat @nomicfoundation/hardhat-ethers +``` + + +**Using viem?** You can install both `@nomicfoundation/hardhat-ethers` and `@nomicfoundation/hardhat-viem`. The upgrades plugin uses ethers internally; your own scripts and tests can still use viem. + + +## Migration + +### Update Config + +In Hardhat 3, plugins must be explicitly added to the `plugins` array in your config: + +**Before (Hardhat 2):** +```typescript +import '@openzeppelin/hardhat-upgrades'; +``` + +**After (Hardhat 3):** +```typescript +import { defineConfig } from 'hardhat/config'; +import hardhatUpgrades from '@openzeppelin/hardhat-upgrades'; + +export default defineConfig({ + plugins: [hardhatUpgrades], + // ... rest of config +}); +``` + +If you use the `verify` task, also add `@nomicfoundation/hardhat-verify` and configure Hardhat's `verify.etherscan.apiKey` setting: + +```typescript +import { configVariable, defineConfig } from 'hardhat/config'; +import hardhatVerify from '@nomicfoundation/hardhat-verify'; +import hardhatUpgrades from '@openzeppelin/hardhat-upgrades'; + +export default defineConfig({ + plugins: [hardhatVerify, hardhatUpgrades], + verify: { + etherscan: { + apiKey: configVariable('ETHERSCAN_API_KEY'), + }, + }, +}); +``` + +### Update Imports + +**Before:** +```typescript +import '@openzeppelin/hardhat-upgrades'; +``` + +**After:** +```typescript +import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'; +``` + +All functions are now exported by the API. Import `upgrades` for standard functions, or `defender` for Defender-specific functions. + +### Update Usage + +**Before:** +```typescript +await hre.upgrades.deployProxy(MyContract, []); +``` + +**After:** +```typescript +const connection = await hre.network.connect(); +const upgradesApi = await upgrades(hre, connection); +await upgradesApi.deployProxy(MyContract, []); +``` + + +**Important:** +- Both `upgrades` and `defender` receive `hre` and `connection` as parameters: `upgrades(hre, connection)` or `defender(hre, connection)` +- Share the connection across multiple operations; do not create a new one each time +- In tests, create the connection once in a `before` block or use top-level await (ESM) + + +## Examples + +### Scripts + +```typescript +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +async function main() { + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); + + const MyContract = await ethers.getContractFactory('MyContract'); + const proxy = await upgradesApi.deployProxy(MyContract, []); + + const MyContractV2 = await ethers.getContractFactory('MyContractV2'); + await upgradesApi.upgradeProxy(proxy, MyContractV2); +} + +main(); +``` + +### Tasks + +```typescript +import { task } from 'hardhat/config'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +task('deploy', async (args, hre) => { + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); + const MyContract = await ethers.getContractFactory('MyContract'); + await upgradesApi.deployProxy(MyContract, []); +}); +``` + +### Tests + + +**Important:** In Hardhat 3, `ethers` comes from the connection, not `hre.ethers`. Share the connection across all tests in a suite. + + +**Before (Hardhat 2):** +```typescript +import hre from 'hardhat'; +import '@openzeppelin/hardhat-upgrades'; + +describe('MyContract', () => { + it('should deploy', async () => { + const MyContract = await hre.ethers.getContractFactory('MyContract'); + const proxy = await hre.upgrades.deployProxy(MyContract, []); + }); +}); +``` + +**After (Hardhat 3) - with `before` hook:** +```typescript +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +describe('MyContract', () => { + let upgradesApi; + let ethers; + + before(async () => { + const connection = await hre.network.connect(); + ({ ethers } = connection); + upgradesApi = await upgrades(hre, connection); + }); + + it('should deploy', async () => { + const MyContract = await ethers.getContractFactory('MyContract'); + const proxy = await upgradesApi.deployProxy(MyContract, []); + }); +}); +``` + +**After (Hardhat 3) - with ESM top-level await:** +```typescript +import hre from 'hardhat'; +import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'; + +const connection = await hre.network.connect(); +const { ethers } = connection; +const upgradesApi = await upgrades(hre, connection); +const defenderApi = await defender(hre, connection); + +describe('MyContract', () => { + it('should deploy', async () => { + const MyContract = await ethers.getContractFactory('MyContract'); + const proxy = await upgradesApi.deployProxy(MyContract, []); + }); +}); +``` + +Note: Both `upgrades` and `defender` receive `hre` and `connection` as parameters. + +## Verify Task (Optional) + +If your Hardhat config file's `verify.etherscan.apiKey` setting uses `configVariable('ETHERSCAN_API_KEY')`, set `ETHERSCAN_API_KEY` before running Hardhat (or use a provider such as `@nomicfoundation/hardhat-keystore`): + +```bash +ETHERSCAN_API_KEY=... npx hardhat verify --network mainnet PROXY_ADDRESS +``` + +The upgrades plugin extends hardhat-verify's `verify` task for proxy addresses. + +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 [Hardhat's documentation for verifying a contract](https://hardhat.org/docs/guides/smart-contract-verification#verifying-a-contract). + +For Solidity test setup in Hardhat 3 (including use of `@openzeppelin/foundry-upgrades`), see [Solidity tests](/upgrades-plugins/hardhat-upgrades#solidity-tests). + +## Checklist + +- Install `@nomicfoundation/hardhat-ethers` — required even if your project uses viem (install both if needed) +- Add `hardhatUpgrades` to `plugins` in `hardhat.config.ts` +- If using `verify`, add `hardhatVerify` to `plugins`, install `@nomicfoundation/hardhat-verify`, and configure Hardhat's `verify.etherscan.apiKey` setting +- Replace `import '@openzeppelin/hardhat-upgrades'` → `import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'` in scripts/tests +- Add `const connection = await hre.network.connect();` (share connection across operations, don't create new ones) +- Replace `hre.ethers` → `ethers` from connection (`const { ethers } = connection`) +- Replace `hre.upgrades.method()` → call methods from `const upgradesApi = await upgrades(hre, connection)` +- Replace `hre.defender.method()` → call methods from `const defenderApi = await defender(hre, connection)` +- Update all scripts, tasks, and tests diff --git a/src/navigation/ethereum-evm.json b/src/navigation/ethereum-evm.json index a3f1f86f..3d7af231 100644 --- a/src/navigation/ethereum-evm.json +++ b/src/navigation/ethereum-evm.json @@ -668,6 +668,11 @@ "name": "Network Files", "url": "/upgrades-plugins/network-files" }, + { + "type": "page", + "name": "Migrating from Hardhat 2", + "url": "/upgrades-plugins/migrate-from-hardhat-2" + }, { "type": "page", "name": "Hardhat 2 (legacy)", From f91791c0442b14d0738c47ad2817105b3d9a9f1e Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 18:13:31 -0400 Subject: [PATCH 4/8] Update other pages --- content/upgrades-plugins/defender-deploy.mdx | 4 ++++ content/upgrades-plugins/faq.mdx | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/content/upgrades-plugins/defender-deploy.mdx b/content/upgrades-plugins/defender-deploy.mdx index d80b6a4a..e051d2f1 100644 --- a/content/upgrades-plugins/defender-deploy.mdx +++ b/content/upgrades-plugins/defender-deploy.mdx @@ -2,6 +2,10 @@ title: OpenZeppelin Defender with Hardhat --- + +This page describes usage with Hardhat 2. If you are on Hardhat 3, apply the patterns from [Using with Hardhat](/upgrades-plugins/hardhat-upgrades) (ESM, `defineConfig`, and the `defender(hre, connection)` factory) to the examples below. + + The Hardhat Upgrades package can use [OpenZeppelin Defender](/defender) for deployments instead of ethers.js, which allows for features such as gas pricing estimation, resubmissions, and automated bytecode and source code verification. ## Configuration diff --git a/content/upgrades-plugins/faq.mdx b/content/upgrades-plugins/faq.mdx index 34a99fe3..a530e1c2 100644 --- a/content/upgrades-plugins/faq.mdx +++ b/content/upgrades-plugins/faq.mdx @@ -268,10 +268,10 @@ contract V2 is V1 { Then when upgrading, call the reinitializer function as part of the upgrade process, for example in Hardhat: ```ts -await upgrades.upgradeProxy(PROXY_ADDRESS, ContractFactoryV2, +await upgradesApi.upgradeProxy(PROXY_ADDRESS, ContractFactoryV2, { call: 'initializeV2', unsafeAllow: ['internal-function-storage'] -); +}); ``` or in Foundry: ```solidity From e8341127b75ddc74d9742332541598bb2ae80f5f Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 20:55:57 -0400 Subject: [PATCH 5/8] Add solidity proxy note --- content/upgrades-plugins/hardhat-upgrades.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/upgrades-plugins/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-upgrades.mdx index 22026f18..d6148e10 100644 --- a/content/upgrades-plugins/hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-upgrades.mdx @@ -231,6 +231,10 @@ For the Solidity API, see [`Upgrades.sol`](/upgrades-plugins/foundry/api/Upgrade This section is optional and is only needed if you want Solidity-based tests. + +**Proxy source:** In Solidity tests, proxies are compiled from the `@openzeppelin/contracts` version installed in your project (via `proxyFilesToBuild()` + Hardhat 3's `npmFilesToBuild`). In scripts and JavaScript/TypeScript tests, proxies come from precompiled bytecode bundled with the plugin. Because the two paths rely on independent sources and compilation settings, the resulting proxy bytecode may not be identical. + + Install: ```console From 0e78da9b0128547adb37a0858ff90f5ef3a1829b Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 23 Apr 2026 15:26:28 -0400 Subject: [PATCH 6/8] Update nav --- src/navigation/ethereum-evm.json | 42 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/navigation/ethereum-evm.json b/src/navigation/ethereum-evm.json index 3d7af231..be9975cc 100644 --- a/src/navigation/ethereum-evm.json +++ b/src/navigation/ethereum-evm.json @@ -659,7 +659,7 @@ "name": "Using with Hardhat", "index": { "type": "page", - "name": "Hardhat 3", + "name": "Overview", "url": "/upgrades-plugins/hardhat-upgrades" }, "children": [ @@ -672,11 +672,6 @@ "type": "page", "name": "Migrating from Hardhat 2", "url": "/upgrades-plugins/migrate-from-hardhat-2" - }, - { - "type": "page", - "name": "Hardhat 2 (legacy)", - "url": "/upgrades-plugins/hardhat-2/hardhat-upgrades" } ] }, @@ -705,20 +700,9 @@ "name": "API Reference" }, { - "type": "folder", + "type": "page", "name": "Hardhat Upgrades API", - "index": { - "type": "page", - "name": "Hardhat 3", - "url": "/upgrades-plugins/api-hardhat-upgrades" - }, - "children": [ - { - "type": "page", - "name": "Hardhat 2 (legacy)", - "url": "/upgrades-plugins/hardhat-2/api-hardhat-upgrades" - } - ] + "url": "/upgrades-plugins/api-hardhat-upgrades" }, { "type": "folder", @@ -755,6 +739,26 @@ "type": "page", "name": "Upgrades Core & CLI", "url": "/upgrades-plugins/api-core" + }, + { + "type": "separator", + "name": "Previous Versions" + }, + { + "type": "folder", + "name": "Hardhat 2", + "children": [ + { + "type": "page", + "name": "Using with Hardhat", + "url": "/upgrades-plugins/hardhat-2/hardhat-upgrades" + }, + { + "type": "page", + "name": "Hardhat Upgrades API", + "url": "/upgrades-plugins/hardhat-2/api-hardhat-upgrades" + } + ] } ] }, From f647ce1111343808f72c1fda526729b076015a53 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 23 Apr 2026 16:08:50 -0400 Subject: [PATCH 7/8] Update callouts --- content/upgrades-plugins/api-hardhat-upgrades.mdx | 2 +- content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx | 2 +- content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx | 2 +- content/upgrades-plugins/hardhat-upgrades.mdx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/upgrades-plugins/api-hardhat-upgrades.mdx b/content/upgrades-plugins/api-hardhat-upgrades.mdx index 1f86aec6..6606693b 100644 --- a/content/upgrades-plugins/api-hardhat-upgrades.mdx +++ b/content/upgrades-plugins/api-hardhat-upgrades.mdx @@ -3,7 +3,7 @@ title: OpenZeppelin Hardhat Upgrades API --- -This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat 2 (legacy) API docs](/upgrades-plugins/hardhat-2/api-hardhat-upgrades). +This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat Upgrades API docs (Hardhat 2)](/upgrades-plugins/hardhat-2/api-hardhat-upgrades). Both `deployProxy` and `upgradeProxy` functions will return instances of [ethers.js contracts](https://docs.ethers.org/v6/api/contract/#Contract), and require [ethers.js contract factories](https://docs.ethers.org/v6/api/contract/#ContractFactory) as arguments. For [beacons](/contracts/5.x/api/proxy#beacon), `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. diff --git a/content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx index bcf3e7bc..0cd53fcf 100644 --- a/content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-2/api-hardhat-upgrades.mdx @@ -3,7 +3,7 @@ title: OpenZeppelin Hardhat Upgrades API --- -You're viewing the Hardhat 2 (legacy) documentation. The latest documentation is available for [Hardhat 3](/upgrades-plugins/api-hardhat-upgrades). +You're viewing the documentation for Hardhat 2. The latest documentation is available for [Hardhat 3](/upgrades-plugins/api-hardhat-upgrades). Both `deployProxy` and `upgradeProxy` functions will return instances of [ethers.js contracts](https://docs.ethers.io/v5/api/contract/contract), and require [ethers.js contract factories](https://docs.ethers.io/v5/api/contract/contract-factory) as arguments. For [beacons](/contracts/5.x/api/proxy#beacon), `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. diff --git a/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx index 4d83452f..2c720af1 100644 --- a/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx @@ -3,7 +3,7 @@ title: Using with Hardhat --- -You're viewing the Hardhat 2 (legacy) documentation. The latest documentation is available for [Hardhat 3](/upgrades-plugins/hardhat-upgrades). +You're viewing the documentation for Hardhat 2. The latest documentation is available for [Hardhat 3](/upgrades-plugins/hardhat-upgrades). This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Depends on `ethers.js`. diff --git a/content/upgrades-plugins/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-upgrades.mdx index d6148e10..1815afc6 100644 --- a/content/upgrades-plugins/hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-upgrades.mdx @@ -3,7 +3,7 @@ title: Using with Hardhat --- -This is the documentation for Hardhat 3. For Hardhat 2, see the [Hardhat 2 (legacy) docs](/upgrades-plugins/hardhat-2/hardhat-upgrades). +This is the documentation for Hardhat 3. For Hardhat 2, see [Using with Hardhat (Hardhat 2)](/upgrades-plugins/hardhat-2/hardhat-upgrades). This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Requires `@nomicfoundation/hardhat-ethers`. From 4e2329f6cdc023fc3b77826e06acc6e3221e45df Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 23 Apr 2026 16:15:44 -0400 Subject: [PATCH 8/8] Update hardhat-upgrades installation command to specify version 3.9.1 --- content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx b/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx index 2c720af1..a8c4cd3e 100644 --- a/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx +++ b/content/upgrades-plugins/hardhat-2/hardhat-upgrades.mdx @@ -15,7 +15,7 @@ Check out the [step by step tutorial](https://forum.openzeppelin.com/t/openzeppe ## Installation ```console -$ npm install --save-dev @openzeppelin/hardhat-upgrades +$ npm install --save-dev @openzeppelin/hardhat-upgrades@^3.9.1 $ npm install --save-dev @nomicfoundation/hardhat-ethers ethers # peer dependencies ```