Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
48b59e9
chore(plugin): initial changes for hardhat 3
afa7789 Oct 16, 2025
37b710a
chore(plugin): second change on tsconfig.json to be able to avoid tse…
afa7789 Oct 16, 2025
70993d7
chore(dev): upgrading files to use hardhat3.
afa7789 Oct 20, 2025
9d6d41e
chore: lint fix
afa7789 Oct 20, 2025
2516303
chore(plugin): update imports to use .js extensions and adjust tsconf…
afa7789 Oct 21, 2025
7fe4ec6
chore(plugin): refactor plugin structure with hooks and update type e…
afa7789 Oct 21, 2025
bc0de37
chore(plugin): refactor hooks to improve user config resolution and e…
afa7789 Oct 22, 2025
13d2465
chore(plugin): enhance validation handling and namespaced storage lay…
afa7789 Oct 22, 2025
e579a89
chore(plugin): update dependencies handling and enhance type extensio…
afa7789 Oct 22, 2025
6e4c825
chore(plugin): lint fixes.
afa7789 Oct 22, 2025
3fde741
chore(plugin): update import statements to use ES module syntax for J…
afa7789 Oct 23, 2025
0709925
chore(plugin): update Hardhat configuration and fix plugin integratio…
afa7789 Oct 28, 2025
71c16f8
chore(logs): debugging
afa7789 Oct 28, 2025
f5266f5
chore(provider): adding one more log which should help.
afa7789 Oct 28, 2025
49726a7
chore(call): enhance logging for callOptionalSignature and add implem…
afa7789 Oct 28, 2025
fd2af91
chore(test): migrate to ES module syntax and update upgrades import
afa7789 Oct 28, 2025
0a71170
feat: refactor deploy and upgrade functions to accept NetworkConnection
afa7789 Oct 28, 2025
6858807
refactor(tests): migrate from CommonJS to ES modules in Hardhat tests
afa7789 Oct 28, 2025
1a7e042
chore(tests): update imports and initialization in Hardhat tests
afa7789 Oct 29, 2025
78f3699
chore: clean up TODO comments and remove unnecessary code in deploy, …
afa7789 Oct 29, 2025
db81b33
feat: add warning for locked validations cache during compilation
afa7789 Oct 29, 2025
33f86a3
refactor(tests): replace proxyquire with esmock for module mocking
afa7789 Oct 29, 2025
0f67ffe
refactor(tests): replace network provider calls with ethers.provider …
afa7789 Oct 29, 2025
fe45e60
refactor(tests): replace hre.ethers with ethers for consistency in co…
afa7789 Oct 29, 2025
c4f2a85
refactor(tests): migrate mockDeploy function to a new file and update…
afa7789 Oct 29, 2025
acb0b57
feat(tests): enhance test script to separate Solidity and JavaScript …
afa7789 Nov 3, 2025
148f07a
feat(tests): add WithConstructor and WithConstructorArray contracts, …
afa7789 Nov 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/plugin-hardhat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changelog

## 3.10.0 (2025-XX-YY)
- Update hardhat, hardhat-ethers and hardhat-verify to support to hardhat v3

## 3.9.1 (2025-06-30)

Expand Down
43 changes: 43 additions & 0 deletions packages/plugin-hardhat/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { HardhatUserConfig } from 'hardhat/config';
import type { SolcUserConfig } from 'hardhat/types/config';
import hardhatVerify from '@nomicfoundation/hardhat-verify';
import hardhatEthers from '@nomicfoundation/hardhat-ethers';

const override: SolcUserConfig = {
version: '0.8.10',
settings: {
optimizer: {
enabled: true,
},
},
};

const config: HardhatUserConfig = {
plugins: [hardhatVerify, hardhatEthers],
solidity: {
compilers: [
{
version: '0.8.29',
},
{
version: '0.8.9',
},
{
version: '0.7.6',
},
{
version: '0.6.12',
},
{
version: '0.5.17',
},
],
overrides: {
'contracts/GapV1.sol': override,
'contracts/GapV2.sol': override,
'contracts/GapV2_Bad.sol': override,
},
},
};

export default config;
22 changes: 8 additions & 14 deletions packages/plugin-hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
"name": "@openzeppelin/hardhat-upgrades",
"version": "3.9.1",
"description": "",
"type": "module",
"repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades",
"license": "MIT",
"main": "dist/index.js",
"exports": {
".": "./dist/index.js",
"./package.json": "./package.json"
},
"files": [
"/dist",
"/src"
Expand All @@ -20,14 +25,14 @@
"test:watch": "fgbg 'bash scripts/test.sh --watch' 'tsc -b --watch' --"
},
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.6",
"@nomicfoundation/hardhat-verify": "^2.0.14",
"@nomicfoundation/hardhat-ethers": "^4.0.2",
"@nomicfoundation/hardhat-verify": "^3.0.3",
"@openzeppelin/contracts": "5.3.0",
"@openzeppelin/contracts-upgradeable": "5.3.0",
"@types/mocha": "^7.0.2",
"ava": "^6.0.0",
"fgbg": "^0.1.4",
"hardhat": "^2.24.1",
"hardhat": "^3.0.7",
"promisified": "^0.5.0",
"proxyquire": "^2.1.3",
"rimraf": "^5.0.0",
Expand All @@ -43,16 +48,5 @@
"ethereumjs-util": "^7.1.5",
"proper-lockfile": "^4.1.1",
"undici": "^6.11.1"
},
"peerDependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.6",
"@nomicfoundation/hardhat-verify": "^2.0.14",
"ethers": "^6.6.0",
"hardhat": "^2.24.1"
},
"peerDependenciesMeta": {
"@nomicfoundation/hardhat-verify": {
"optional": true
}
}
}
21 changes: 15 additions & 6 deletions packages/plugin-hardhat/src/admin.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import chalk from 'chalk';
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre';
import type { EthereumProvider } from 'hardhat/types/providers';

import { Manifest, getAdminAddress } from '@openzeppelin/upgrades-core';
import { Contract, Signer } from 'ethers';
import { EthersDeployOptions, attachProxyAdminV4 } from './utils';
import { disableDefender } from './defender/utils';
import { EthersDeployOptions, attachProxyAdminV4 } from './utils/index.js';
import { disableDefender } from './defender/utils.js';

const SUCCESS_CHECK = chalk.green('✔') + ' ';

Expand Down Expand Up @@ -34,7 +36,10 @@ export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment, defenderMod
) {
disableDefender(hre, defenderModule, {}, changeProxyAdmin.name);

const proxyAdminAddress = await getAdminAddress(hre.network.provider, proxyAddress);
const { ethers } = await hre.network.connect();
const provider = ethers.provider as unknown as EthereumProvider;

const proxyAdminAddress = await getAdminAddress(provider, proxyAddress);
// Only compatible with v4 admins
const admin = await attachProxyAdminV4(hre, proxyAdminAddress, signer);

Expand All @@ -55,15 +60,19 @@ export function makeTransferProxyAdminOwnership(
) {
disableDefender(hre, defenderModule, {}, transferProxyAdminOwnership.name);

const proxyAdminAddress = await getAdminAddress(hre.network.provider, proxyAddress);
const { ethers } = await hre.network.connect();
const provider = ethers.provider as unknown as EthereumProvider;

const proxyAdminAddress = await getAdminAddress(provider, proxyAddress);
// Compatible with both v4 and v5 admins since they both have transferOwnership
const admin = await attachProxyAdminV4(hre, proxyAdminAddress, signer);

const overrides = opts.txOverrides ? [opts.txOverrides] : [];
await admin.transferOwnership(newOwner, ...overrides);

if (!opts.silent) {
const { provider } = hre.network;
const { ethers } = await hre.network.connect();
const provider = ethers.provider;
const manifest = await Manifest.forNetwork(provider);
const { proxies } = await manifest.read();
const adminAddress = await admin.getAddress();
Expand Down
5 changes: 3 additions & 2 deletions packages/plugin-hardhat/src/defender/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { DeployClient } from '@openzeppelin/defender-sdk-deploy-client';
import { NetworkClient } from '@openzeppelin/defender-sdk-network-client';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { getDefenderApiKey } from './utils';
import { HardhatRuntimeEnvironment } from 'hardhat/types/hre';

import { getDefenderApiKey } from './utils.js';

export function getNetworkClient(hre: HardhatRuntimeEnvironment): NetworkClient {
return new NetworkClient(getDefenderApiKey(hre));
Expand Down
37 changes: 16 additions & 21 deletions packages/plugin-hardhat/src/defender/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// TODO: figure out, the
import type { ethers, ContractFactory } from 'ethers';
import { CompilerInput, CompilerOutputContract, HardhatRuntimeEnvironment } from 'hardhat/types';
import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre';
import type { CompilerInput, CompilerOutputContract } from 'hardhat/types/solidity';

import { parseFullyQualifiedName } from 'hardhat/utils/contract-names';

Expand All @@ -18,30 +20,20 @@ import BeaconProxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/con
import UpgradeableBeacon from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json';
import TransparentUpgradeableProxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json';

import { getNetwork, parseTxOverrides } from './utils';
import { DefenderDeployOptions, UpgradeOptions, EthersDeployOptions, DefenderDeployment } from '../utils';
import debug from '../utils/debug';
import { getDeployData } from '../utils/deploy-impl';
import { getNetwork, parseTxOverrides } from './utils.js';
import { DefenderDeployOptions, UpgradeOptions, EthersDeployOptions, DefenderDeployment } from '../utils/index.js';
import debug from '../utils/debug.js';
import { getDeployData } from '../utils/deploy-impl.js';
import { ContractSourceNotFoundError } from '@openzeppelin/upgrades-core';
import { getDeployClient } from './client';
import { getDeployClient } from './client.js';
import { getCombinedBuildInfo, type CombinedBuildInfo } from '../utils/artifacts.js';

const deployableProxyContracts = [ERC1967Proxy, BeaconProxy, UpgradeableBeacon, TransparentUpgradeableProxy];

interface ReducedBuildInfo {
_format: string;
id: string;
solcVersion: string;
solcLongVersion: string;
input: CompilerInput;
output: {
contracts: any;
};
}

interface ContractInfo {
sourceName: string;
contractName: string;
buildInfo: ReducedBuildInfo;
buildInfo: CombinedBuildInfo;
libraries?: DeployRequestLibraries;
constructorBytecode: string;
}
Expand Down Expand Up @@ -148,8 +140,10 @@ export async function defenderDeploy(
}
}

const txResponse = (await hre.ethers.provider.getTransaction(deploymentResponse.txHash)) ?? undefined;
const checksumAddress = hre.ethers.getAddress(deploymentResponse.address);
const { ethers } = await hre.network.connect();

const txResponse = (await ethers.provider.getTransaction(deploymentResponse.txHash)) ?? undefined;
const checksumAddress = ethers.getAddress(deploymentResponse.address);
return {
address: checksumAddress,
txHash: deploymentResponse.txHash,
Expand Down Expand Up @@ -212,7 +206,8 @@ async function getContractInfo(

const { sourceName, contractName } = parseFullyQualifiedName(fullContractName);
// Get the build-info file corresponding to the fully qualified contract name
const buildInfo = await hre.artifacts.getBuildInfo(fullContractName);
const buildInfo = await getCombinedBuildInfo(hre.artifacts, fullContractName);

if (buildInfo === undefined) {
throw new UpgradesError(
`Could not get Hardhat compilation artifact for contract ${fullContractName}`,
Expand Down
6 changes: 3 additions & 3 deletions packages/plugin-hardhat/src/defender/get-approval-process.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { HardhatRuntimeEnvironment } from 'hardhat/types/hre';

import { getNetwork } from './utils';
import { getDeployClient } from './client';
import { getNetwork } from './utils.js';
import { getDeployClient } from './client.js';
import { ApprovalProcessResponse } from '@openzeppelin/defender-sdk-deploy-client';

export interface ApprovalProcess {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {
isTransparentProxy,
} from '@openzeppelin/upgrades-core';
import { ContractFactory, ethers } from 'ethers';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { DefenderDeployOptions, UpgradeOptions } from '../utils';
import { getNetwork, enableDefender } from './utils';
import { deployImplForUpgrade } from '../prepare-upgrade';
import { getDeployClient } from './client';
import { HardhatRuntimeEnvironment } from 'hardhat/types/hre';
import { EthereumProvider } from 'hardhat/types/providers';

import { DefenderDeployOptions, UpgradeOptions } from '../utils/index.js';
import { getNetwork, enableDefender } from './utils.js';
import { deployImplForUpgrade } from '../prepare-upgrade.js';
import { getDeployClient } from './client.js';

export interface UpgradeProposalResponse {
proposalId: string;
Expand All @@ -37,23 +39,25 @@ export function makeProposeUpgradeWithApproval(

const client = getDeployClient(hre);
const network = await getNetwork(hre);
const { ethers } = await hre.network.connect();
const provider = ethers.provider as unknown as EthereumProvider;

if (await isBeaconProxy(hre.network.provider, proxyAddress)) {
if (await isBeaconProxy(provider, proxyAddress)) {
throw new Error(`Beacon proxy is not currently supported with defender.proposeUpgradeWithApproval()`);
} else {
// try getting the implementation address so that it will give an error if it's not a transparent/uups proxy
await getImplementationAddress(hre.network.provider, proxyAddress);
await getImplementationAddress(provider, proxyAddress);
}

let proxyAdmin = undefined;
if (await isTransparentProxy(hre.network.provider, proxyAddress)) {
if (await isTransparentProxy(provider, proxyAddress)) {
// use the erc1967 admin address as the proxy admin
proxyAdmin = await getAdminAddress(hre.network.provider, proxyAddress);
proxyAdmin = await getAdminAddress(provider, proxyAddress);
}

const implFactory =
typeof contractNameOrImplFactory === 'string'
? await hre.ethers.getContractFactory(contractNameOrImplFactory)
? await ethers.getContractFactory(contractNameOrImplFactory)
: contractNameOrImplFactory;
const abi = implFactory.interface.formatJson();

Expand Down
19 changes: 11 additions & 8 deletions packages/plugin-hardhat/src/defender/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { HardhatRuntimeEnvironment } from 'hardhat/types/hre';

import {
getChainId,
hasCode,
Expand All @@ -11,13 +12,13 @@ import {
import { Network, fromChainId } from '@openzeppelin/defender-sdk-base-client';
import { TxOverrides } from '@openzeppelin/defender-sdk-deploy-client';

import { HardhatDefenderConfig } from '../type-extensions';
import { DefenderDeploy } from '../utils';
import debug from '../utils/debug';
import { HardhatDefenderConfig } from '../type-extensions.js';
import { DefenderDeploy } from '../utils/index.js';
import debug from '../utils/debug.js';
import { Overrides } from 'ethers';

import { promisify } from 'util';
import { getDeployClient, getNetworkClient } from './client';
import { getDeployClient, getNetworkClient } from './client.js';
const sleep = promisify(setTimeout);

export function getDefenderApiKey(hre: HardhatRuntimeEnvironment): HardhatDefenderConfig {
Expand All @@ -32,8 +33,9 @@ export function getDefenderApiKey(hre: HardhatRuntimeEnvironment): HardhatDefend
}

export async function getNetwork(hre: HardhatRuntimeEnvironment): Promise<Network> {
const { provider } = hre.network;
const chainId = hre.network.config.chainId ?? (await getChainId(provider));
const { networkConfig, ethers } = await hre.network.connect();
const provider = ethers.provider;
const chainId = networkConfig.chainId ?? (await getChainId(provider));

const networkNames = await getNetworkNames(chainId, hre);

Expand Down Expand Up @@ -181,10 +183,11 @@ export async function waitForDeployment(
): Promise<string | undefined> {
const pollInterval = opts.pollingInterval ?? 5e3;
let lastKnownTxHash: string | undefined;
const { ethers } = await hre.network.connect();

// eslint-disable-next-line no-constant-condition
while (true) {
if (await hasCode(hre.ethers.provider, address)) {
if (await hasCode(ethers.provider, address)) {
debug('code in target address found', address);
break;
}
Expand Down
13 changes: 7 additions & 6 deletions packages/plugin-hardhat/src/deploy-beacon-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre';
import { ContractFactory } from 'ethers';

import {
Expand All @@ -21,10 +21,10 @@ import {
getContractAddress,
getInitializerData,
getSigner,
} from './utils';
import { enableDefender } from './defender/utils';
import { getContractInstance } from './utils/contract-instance';
import { ContractTypeOfFactory } from './type-extensions';
} from './utils/index.js';
import { enableDefender } from './defender/utils.js';
import { getContractInstance } from './utils/contract-instance.js';
import { ContractTypeOfFactory } from './type-extensions.js';

export interface DeployBeaconProxyFunction {
<F extends ContractFactory>(
Expand Down Expand Up @@ -63,7 +63,8 @@ export function makeDeployBeaconProxy(

opts = enableDefender(hre, defenderModule, opts);

const { provider } = hre.network;
const { ethers } = await hre.network.connect();
const provider = ethers.provider;
const manifest = await Manifest.forNetwork(provider);

if (opts.kind !== undefined && opts.kind !== 'beacon') {
Expand Down
10 changes: 5 additions & 5 deletions packages/plugin-hardhat/src/deploy-beacon.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre';
import type { ContractFactory, Contract } from 'ethers';

import { Deployment } from '@openzeppelin/upgrades-core';

import { DeployBeaconOptions, deploy, DeployTransaction, getUpgradeableBeaconFactory, deployBeaconImpl } from './utils';
import { disableDefender } from './defender/utils';
import { attach, getSigner } from './utils/ethers';
import { getInitialOwner } from './utils/initial-owner';
import { DeployBeaconOptions, deploy, DeployTransaction, getUpgradeableBeaconFactory, deployBeaconImpl } from './utils/index.js';
import { disableDefender } from './defender/utils.js';
import { attach, getSigner } from './utils/ethers.js';
import { getInitialOwner } from './utils/initial-owner.js';

export interface DeployBeaconFunction {
(ImplFactory: ContractFactory, opts?: DeployBeaconOptions): Promise<Contract>;
Expand Down
Loading
Loading