From 972cfb2fa7e46185c7c58fb63ea62c9f9488117a Mon Sep 17 00:00:00 2001 From: Arthur Abeilice Date: Tue, 20 Jan 2026 17:10:56 -0300 Subject: [PATCH 01/21] core upgrade changes to be able to support hardhat3, in CI/CD (#1203) --- packages/core/CHANGELOG.md | 6 + packages/core/package.json | 2 +- .../core/src/cli/validate/build-info-file.ts | 150 +++++++++++++++--- packages/core/src/impl-address.ts | 9 +- 4 files changed, 141 insertions(+), 26 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index d8cda2ab4..0e800f1df 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.0.0-alpha.0 + +- Support Hardhat 3 build-info file format for CLI validation ([#1203](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1203)) + +> **Note** ⚠️ This version is still in testing and should not be used in production. + ## 1.44.2 (2025-11-03) diff --git a/packages/core/package.json b/packages/core/package.json index e2335e1ba..0ad593c2e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/upgrades-core", - "version": "1.44.2", + "version": "2.0.0-alpha.0", "description": "", "repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades", "license": "MIT", diff --git a/packages/core/src/cli/validate/build-info-file.ts b/packages/core/src/cli/validate/build-info-file.ts index 5922397bd..a3025419a 100644 --- a/packages/core/src/cli/validate/build-info-file.ts +++ b/packages/core/src/cli/validate/build-info-file.ts @@ -129,7 +129,7 @@ async function exists(dir: string) { async function getJsonFiles(dir: string): Promise { const files = await fs.readdir(dir); - const jsonFiles = files.filter(file => file.endsWith('.json')); + const jsonFiles = files.filter(file => file.endsWith('.json') && !file.endsWith('.output.json')); return jsonFiles.map(file => path.join(dir, file)); } @@ -137,44 +137,139 @@ async function readBuildInfo(buildInfoFilePaths: string[], dirShortName: string) const buildInfoFiles: BuildInfoFile[] = []; for (const buildInfoFilePath of buildInfoFilePaths) { - const buildInfoJson = await readJSON(buildInfoFilePath); - if ( - buildInfoJson.input === undefined || - buildInfoJson.output === undefined || - buildInfoJson.solcVersion === undefined - ) { - throw new ValidateCommandError( - `Build info file ${buildInfoFilePath} must contain Solidity compiler input, output, and solcVersion.`, - ); - } else { - checkOutputSelection(buildInfoJson, buildInfoFilePath); + const buildInfo = await loadBuildInfo(buildInfoFilePath); + + checkOutputSelection(buildInfo.input, buildInfoFilePath); + + buildInfoFiles.push({ + input: buildInfo.input, + output: buildInfo.output, + solcVersion: buildInfo.solcVersion, + dirShortName, + }); + } + return buildInfoFiles; +} + +async function loadBuildInfo(buildInfoFilePath: string): Promise<{ + input: SolcInput; + output: SolcOutput; + solcVersion: string; +}> { + const buildInfoJson = await readJSON(buildInfoFilePath); + + if ( + buildInfoJson.input !== undefined && + buildInfoJson.output !== undefined && + buildInfoJson.solcVersion !== undefined + ) { + return { + input: buildInfoJson.input, + output: buildInfoJson.output, + solcVersion: buildInfoJson.solcVersion, + }; + } + + const format = buildInfoJson._format as string | undefined; + + if ( + typeof format === 'string' && + (format.startsWith('hh3-sol-build-info') || format.startsWith('hh-sol-build-info')) + ) { + if (buildInfoJson.input !== undefined && buildInfoJson.solcVersion !== undefined) { + let inputData: SolcInput = buildInfoJson.input; + let outputData: SolcOutput | undefined = buildInfoJson.output; + + if (outputData === undefined) { + const { dir, name } = path.parse(buildInfoFilePath); + const outputFilePath = path.join(dir, `${name}.output.json`); - buildInfoFiles.push({ - input: buildInfoJson.input, - output: buildInfoJson.output, + try { + const outputJson = await readJSON(outputFilePath); + outputData = outputJson.output ?? outputJson; + } catch (error) { + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} does not contain output, and output file ${outputFilePath} could not be read.`, + ); + } + + if (outputData === undefined) { + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} does not contain output, and output file ${outputFilePath} is missing Solidity compiler output.`, + ); + } + } + + const userSourceNameMap: Record | undefined = buildInfoJson.userSourceNameMap; + if (userSourceNameMap !== undefined) { + const canonicalToUser: Record = {}; + for (const [userSource, canonicalSource] of Object.entries(userSourceNameMap)) { + canonicalToUser[canonicalSource] = userSource; + } + + if (inputData.sources !== undefined) { + inputData = { + ...inputData, + sources: remapKeys(inputData.sources, canonicalToUser), + }; + } + + if (outputData.sources !== undefined) { + outputData = { + ...outputData, + sources: remapKeys(outputData.sources, canonicalToUser), + contracts: + outputData.contracts !== undefined + ? remapKeys(outputData.contracts, canonicalToUser) + : outputData.contracts, + }; + } else if (outputData.contracts !== undefined) { + outputData = { + ...outputData, + contracts: remapKeys(outputData.contracts, canonicalToUser), + }; + } + } + + return { + input: inputData, + output: outputData, solcVersion: buildInfoJson.solcVersion, - dirShortName: dirShortName, - }); + }; } } - return buildInfoFiles; + + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} must contain Solidity compiler input, output, and solcVersion. Got format: ${format ?? 'unknown'}.`, + ); } /** * Gives an error if there is empty output selection for any contract, or a contract does not have storage layout. */ -function checkOutputSelection(buildInfoJson: any, buildInfoFilePath: string) { - const o = buildInfoJson.input.settings?.outputSelection; +function checkOutputSelection(input: SolcInput, buildInfoFilePath: string) { + const outputSelection = input.settings?.outputSelection; - return Object.keys(o).forEach((item: any) => { - if ((o[item][''] === undefined || o[item][''].length === 0) && o[item]['*'].length === 0) { + if (outputSelection === undefined) { + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} does not define compiler outputSelection.`, + () => STORAGE_LAYOUT_HELP, + ); + } + + Object.keys(outputSelection).forEach(item => { + const selection = outputSelection[item] ?? {}; + const unnamed = Array.isArray(selection['']) ? selection[''] : []; + const wildcard = Array.isArray(selection['*']) ? selection['*'] : []; + + if (unnamed.length === 0 && wildcard.length === 0) { // No outputs at all for this contract e.g. if there were no changes since the last compile in Foundry. // This is not supported for now, since it leads to AST nodes that reference node ids in other build-info files. throw new ValidateCommandError( `Build info file ${buildInfoFilePath} is not from a full compilation.`, () => PARTIAL_COMPILE_HELP, ); - } else if (!o[item]['*'].includes('storageLayout')) { + } else if (!wildcard.includes('storageLayout')) { throw new ValidateCommandError( `Build info file ${buildInfoFilePath} does not contain storage layout for all contracts.`, () => STORAGE_LAYOUT_HELP, @@ -186,3 +281,12 @@ function checkOutputSelection(buildInfoJson: any, buildInfoFilePath: string) { async function readJSON(path: string) { return JSON.parse(await fs.readFile(path, 'utf8')); } + +function remapKeys(original: Record, canonicalToUser: Record): Record { + const remapped: Record = {}; + for (const [key, value] of Object.entries(original)) { + const mappedKey = canonicalToUser[key] ?? key; + remapped[mappedKey] = value; + } + return remapped; +} diff --git a/packages/core/src/impl-address.ts b/packages/core/src/impl-address.ts index 8c2b2291f..cbdbd426d 100644 --- a/packages/core/src/impl-address.ts +++ b/packages/core/src/impl-address.ts @@ -20,13 +20,18 @@ export class InvalidBeacon extends UpgradesError {} * @throws {InvalidBeacon} If the implementation() function could not be called or does not return an address. */ export async function getImplementationAddressFromBeacon( - provider: EthereumProvider, + provider: EthereumProvider, // v2 may differ from v3 beaconAddress: string, ): Promise { const impl = await callOptionalSignature(provider, beaconAddress, 'implementation()'); + let parsedImplAddress; if (impl !== undefined) { - parsedImplAddress = parseAddress(impl); + try { + parsedImplAddress = parseAddress(impl); + } catch (parseErr) { + throw new InvalidBeacon(`Contract at ${beaconAddress} doesn't look like a beacon`); + } } if (parsedImplAddress === undefined) { From 59dcd33d92993e37fe063c7e690317be2e3067b4 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 20 Jan 2026 16:04:58 -0500 Subject: [PATCH 02/21] Bump minor instead of major version for core --- packages/core/CHANGELOG.md | 2 +- packages/core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 0e800f1df..bcdbdfdc3 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2.0.0-alpha.0 +## 1.45.0-alpha.0 (2026-01-20) - Support Hardhat 3 build-info file format for CLI validation ([#1203](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1203)) diff --git a/packages/core/package.json b/packages/core/package.json index 0ad593c2e..4fb48ca49 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/upgrades-core", - "version": "2.0.0-alpha.0", + "version": "1.45.0-alpha.0", "description": "", "repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades", "license": "MIT", From 09245951f37d823b0100b4164992ebb4e9f4d418 Mon Sep 17 00:00:00 2001 From: Arthur Abeilice Date: Mon, 2 Mar 2026 18:20:49 -0300 Subject: [PATCH 03/21] Migrate hardhat-upgrades plugin to use hardhat v3 (#1194) Co-authored-by: Eric Lau --- .eslintrc.js | 16 +- .github/actions/setup/action.yml | 12 +- .github/workflows/changeset.yml | 1 + .github/workflows/checks.yml | 15 +- .github/workflows/publish.yml | 1 + .github/workflows/version.yml | 1 + .node-version | 2 +- README.md | 5 + package.json | 2 +- packages/core/CHANGELOG.md | 7 +- packages/core/package.json | 2 +- .../src/cli/validate/build-info-file.test.ts | 57 +- .../core/src/cli/validate/build-info-file.ts | 148 +- packages/plugin-hardhat/CHANGELOG.md | 31 + packages/plugin-hardhat/MIGRATION.md | 217 ++ packages/plugin-hardhat/README.md | 171 +- packages/plugin-hardhat/ava.config.js | 2 +- .../{Constructor.sol => WithConstructor.sol} | 0 .../foundry/GreeterInitializable.sol | 19 + .../contracts/foundry/GreeterProxiable.sol | 23 + .../contracts/foundry/GreeterV2.sol | 28 + .../contracts/foundry/GreeterV2Proxiable.sol | 30 + .../contracts/foundry/HasOwner.sol | 40 + .../contracts/foundry/MyContractFile.sol | 8 + .../contracts/foundry/NoLicense.sol | 3 + .../contracts/foundry/Unlicensed.sol | 4 + .../contracts/foundry/UnrecognizedLicense.sol | 4 + .../foundry/UpgradeInterfaceVersions.sol | 22 + .../contracts/foundry/Validations.sol | 90 + .../contracts/foundry/WithConstructor.sol | 30 + .../examples/BoxSolidityTests/.gitignore | 5 + .../BoxSolidityTests/contracts/Box.sol | 25 + .../BoxSolidityTests/contracts/BoxV2.sol | 30 + .../BoxSolidityTests/hardhat.config.ts | 29 + .../examples/BoxSolidityTests/package.json | 20 + .../test/solidity/Upgrades.t.sol | 32 + .../examples/BoxSolidityTests/tsconfig.json | 13 + .../examples/BoxTransparent/.gitignore | 5 + .../examples/BoxTransparent/ava.config.js | 6 + .../examples/BoxTransparent/contracts/Box.sol | 25 + .../BoxTransparent/contracts/BoxV2.sol | 29 + .../examples/BoxTransparent/hardhat.config.ts | 7 + .../examples/BoxTransparent/package.json | 21 + .../BoxTransparent/scripts/1-deploy.ts | 21 + .../BoxTransparent/scripts/2-upgrade.ts | 26 + .../BoxTransparent/test/upgrade.test.ts | 29 + .../examples/BoxTransparent/tsconfig.json | 13 + .../examples/BoxUUPS/.gitignore | 5 + .../examples/BoxUUPS/ava.config.js | 6 + .../examples/BoxUUPS/contracts/Box.sol | 31 + .../examples/BoxUUPS/contracts/BoxV2.sol | 35 + .../examples/BoxUUPS/hardhat.config.ts | 7 + .../examples/BoxUUPS/package.json | 21 + .../examples/BoxUUPS/scripts/1-deploy.ts | 22 + .../examples/BoxUUPS/scripts/2-upgrade.ts | 26 + .../examples/BoxUUPS/test/upgrade.test.ts | 30 + .../examples/BoxUUPS/tsconfig.json | 13 + packages/plugin-hardhat/examples/README.md | 40 + packages/plugin-hardhat/hardhat.config.js | 37 - packages/plugin-hardhat/hardhat.config.ts | 72 + packages/plugin-hardhat/package.json | 51 +- .../assert-upgrades-core-resolution.sh | 69 + packages/plugin-hardhat/scripts/test.sh | 42 +- packages/plugin-hardhat/src/admin.ts | 31 +- .../plugin-hardhat/src/defender/client.ts | 5 +- .../plugin-hardhat/src/defender/deploy.ts | 85 +- .../src/defender/get-approval-process.ts | 29 +- .../defender/propose-upgrade-with-approval.ts | 44 +- packages/plugin-hardhat/src/defender/utils.ts | 26 +- .../plugin-hardhat/src/deploy-beacon-proxy.ts | 22 +- packages/plugin-hardhat/src/deploy-beacon.ts | 27 +- .../plugin-hardhat/src/deploy-contract.ts | 25 +- .../src/deploy-implementation.ts | 12 +- packages/plugin-hardhat/src/deploy-proxy.ts | 29 +- packages/plugin-hardhat/src/force-import.ts | 31 +- packages/plugin-hardhat/src/hooks/config.ts | 39 + packages/plugin-hardhat/src/hooks/solidity.ts | 337 +++ packages/plugin-hardhat/src/index.ts | 312 +-- .../plugin-hardhat/src/prepare-upgrade.ts | 33 +- .../src/test-utils/mock-deploy.ts | 35 + .../plugin-hardhat/src/type-extensions.ts | 27 +- packages/plugin-hardhat/src/types.ts | 52 + packages/plugin-hardhat/src/upgrade-beacon.ts | 18 +- packages/plugin-hardhat/src/upgrade-proxy.ts | 30 +- .../plugin-hardhat/src/utils/artifacts.ts | 130 + .../plugin-hardhat/src/utils/attach-abi.ts | 33 +- .../src/utils/contract-instance.ts | 14 +- .../plugin-hardhat/src/utils/deploy-impl.ts | 60 +- packages/plugin-hardhat/src/utils/deploy.ts | 8 +- .../plugin-hardhat/src/utils/etherscan-api.ts | 107 +- .../plugin-hardhat/src/utils/factories.ts | 32 +- packages/plugin-hardhat/src/utils/factory.ts | 180 ++ packages/plugin-hardhat/src/utils/index.ts | 24 +- .../plugin-hardhat/src/utils/initial-owner.ts | 2 +- .../src/utils/npmFilesToBuild.ts | 20 + packages/plugin-hardhat/src/utils/options.ts | 2 +- .../src/utils/simulate-deploy.ts | 13 +- .../plugin-hardhat/src/utils/validate-impl.ts | 4 +- .../plugin-hardhat/src/utils/validations.ts | 2 +- .../src/validate-implementation.ts | 17 +- .../plugin-hardhat/src/validate-upgrade.ts | 24 +- packages/plugin-hardhat/src/verify-plugin.ts | 16 + .../src/verify-proxy-task-action.ts | 8 + packages/plugin-hardhat/src/verify-proxy.ts | 537 ++-- .../test/beacon-000-externally-deployed.js | 28 +- .../test/beacon-001-externally-upgraded.js | 18 +- .../plugin-hardhat/test/beacon-constructor.js | 34 +- .../test/beacon-custom-beacon-proxy.js | 20 +- .../test/beacon-deploy-overload.js | 16 +- .../test/beacon-deploy-validation.js | 16 +- .../test/beacon-happy-path-with-addresses.js | 18 +- .../test/beacon-happy-path-with-enums.js | 17 +- .../test/beacon-happy-path-with-library.js | 16 +- .../test/beacon-happy-path-with-structs.js | 17 +- .../plugin-hardhat/test/beacon-happy-path.js | 18 +- .../test/beacon-initial-owner.js | 16 +- .../test/beacon-initializers.js | 16 +- .../test/beacon-linked-libraries.js | 19 +- .../test/beacon-upgrade-block-proxy.js | 26 +- .../test/beacon-upgrade-storage.js | 15 +- .../test/beacon-upgrade-validation.js | 25 +- .../plugin-hardhat/test/beacon-with-call.js | 17 +- packages/plugin-hardhat/test/constructor.js | 33 +- .../test/defender-contract-instance.js | 47 +- .../test/defender-deploy-contract.js | 74 +- .../test/defender-deploy-implementation.js | 46 +- .../test/defender-deploy-proxy.js | 122 +- .../plugin-hardhat/test/defender-deploy.js | 241 +- .../test/defender-get-approval-process.js | 25 +- .../test/defender-hardhat-setup.js | 11 +- .../plugin-hardhat/test/defender-utils.js | 99 +- .../test/implementation-functions.js | 38 +- packages/plugin-hardhat/test/import-v4.js | 22 +- packages/plugin-hardhat/test/import-v5.js | 25 +- .../test/import-with-deploy-v4.js | 21 +- .../test/import-with-deploy-v5.js | 27 +- .../plugin-hardhat/test/infer-proxy-kind.js | 18 +- packages/plugin-hardhat/test/namespaced.js | 15 +- packages/plugin-hardhat/test/namespaced.js.md | 10 +- .../plugin-hardhat/test/namespaced.js.snap | Bin 621 -> 617 bytes .../test/prepare-upgrade-txresponse.js | 21 +- .../propose-upgrade-with-approval-beacon.js | 40 +- ...opose-upgrade-with-approval-transparent.js | 59 +- ...opose-upgrade-with-approval-unsafeAllow.js | 58 +- ...pose-upgrade-with-approval-use-deployed.js | 58 +- .../propose-upgrade-with-approval-uups.js | 62 +- .../test/redeploy-implementation-always.js | 17 +- .../test/redeploy-implementation-never.js | 17 +- .../test/redeploy-implementation-onchange.js | 17 +- .../test/solidity-overrides-storage.js | 15 +- .../test/solidity/Upgrades.t.sol | 384 +++ packages/plugin-hardhat/test/timeout.js | 70 +- .../test/transparent-admin-initial-owner.js | 29 +- ...sparent-admin-unknown-upgrade-interface.js | 27 +- .../test/transparent-deploy-overload.js | 15 +- .../test/transparent-deploy-validation.js | 15 +- .../test/transparent-happy-path-with-call.js | 18 +- .../test/transparent-happy-path-with-enums.js | 16 +- .../transparent-happy-path-with-library.js | 15 +- .../transparent-happy-path-with-structs.js | 16 +- .../test/transparent-happy-path.js | 17 +- .../test/transparent-initializers.js | 15 +- .../test/transparent-linked-libraries.js | 19 +- .../test/transparent-multi-compiler.js | 20 +- ...ent-transfer-admin-ownership-happy-path.js | 18 +- ...sparent-transfer-admin-ownership-signer.js | 21 +- ...t-transfer-admin-ownership-wrong-signer.js | 17 +- .../test/transparent-upgrade-storage.js | 15 +- .../test/transparent-upgrade-validation.js | 15 +- ...arent-v4-change-admin-different-address.js | 21 +- .../transparent-v4-change-admin-happy-path.js | 18 +- ...nt-v4-transfer-admin-ownership-multiple.js | 20 +- .../transparent-v5-with-v4-manifest-admin.js | 24 +- packages/plugin-hardhat/test/tx-overrides.js | 20 +- .../test/use-deployed-implementation.js | 17 +- .../plugin-hardhat/test/uups-custom-proxy.js | 22 +- .../test/uups-deploy-overload.js | 15 +- .../test/uups-deploy-validation.js | 15 +- .../plugin-hardhat/test/uups-happy-path-v4.js | 15 +- .../test/uups-happy-path-with-call-v4.js | 15 +- .../test/uups-happy-path-with-call.js | 21 +- .../test/uups-happy-path-with-enums.js | 16 +- .../test/uups-happy-path-with-library.js | 15 +- .../test/uups-happy-path-with-structs.js | 16 +- .../plugin-hardhat/test/uups-happy-path.js | 22 +- .../plugin-hardhat/test/uups-initial-owner.js | 27 +- .../plugin-hardhat/test/uups-initializers.js | 15 +- .../test/uups-linked-libraries.js | 19 +- .../test/uups-multi-compiler.js | 22 +- .../test/uups-unknown-upgrade-interface.js | 57 +- .../test/uups-upgrade-storage.js | 23 +- .../test/uups-upgrade-validation.js | 18 +- packages/plugin-hardhat/tsconfig.json | 19 +- submodules/openzeppelin-foundry-upgrades | 2 +- yarn.lock | 2451 +++++++---------- 195 files changed, 6349 insertions(+), 3264 deletions(-) create mode 100644 packages/plugin-hardhat/MIGRATION.md rename packages/plugin-hardhat/contracts/{Constructor.sol => WithConstructor.sol} (100%) create mode 100644 packages/plugin-hardhat/contracts/foundry/GreeterInitializable.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/GreeterProxiable.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/GreeterV2.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/GreeterV2Proxiable.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/HasOwner.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/MyContractFile.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/NoLicense.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/Unlicensed.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/UnrecognizedLicense.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/UpgradeInterfaceVersions.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/Validations.sol create mode 100644 packages/plugin-hardhat/contracts/foundry/WithConstructor.sol create mode 100644 packages/plugin-hardhat/examples/BoxSolidityTests/.gitignore create mode 100644 packages/plugin-hardhat/examples/BoxSolidityTests/contracts/Box.sol create mode 100644 packages/plugin-hardhat/examples/BoxSolidityTests/contracts/BoxV2.sol create mode 100644 packages/plugin-hardhat/examples/BoxSolidityTests/hardhat.config.ts create mode 100644 packages/plugin-hardhat/examples/BoxSolidityTests/package.json create mode 100644 packages/plugin-hardhat/examples/BoxSolidityTests/test/solidity/Upgrades.t.sol create mode 100644 packages/plugin-hardhat/examples/BoxSolidityTests/tsconfig.json create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/.gitignore create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/ava.config.js create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/contracts/Box.sol create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/contracts/BoxV2.sol create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/hardhat.config.ts create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/package.json create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/scripts/1-deploy.ts create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/scripts/2-upgrade.ts create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/test/upgrade.test.ts create mode 100644 packages/plugin-hardhat/examples/BoxTransparent/tsconfig.json create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/.gitignore create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/ava.config.js create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/contracts/Box.sol create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/contracts/BoxV2.sol create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/hardhat.config.ts create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/package.json create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/scripts/1-deploy.ts create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/scripts/2-upgrade.ts create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/test/upgrade.test.ts create mode 100644 packages/plugin-hardhat/examples/BoxUUPS/tsconfig.json create mode 100644 packages/plugin-hardhat/examples/README.md delete mode 100644 packages/plugin-hardhat/hardhat.config.js create mode 100644 packages/plugin-hardhat/hardhat.config.ts create mode 100755 packages/plugin-hardhat/scripts/assert-upgrades-core-resolution.sh create mode 100644 packages/plugin-hardhat/src/hooks/config.ts create mode 100644 packages/plugin-hardhat/src/hooks/solidity.ts create mode 100644 packages/plugin-hardhat/src/test-utils/mock-deploy.ts create mode 100644 packages/plugin-hardhat/src/types.ts create mode 100644 packages/plugin-hardhat/src/utils/artifacts.ts create mode 100644 packages/plugin-hardhat/src/utils/factory.ts create mode 100644 packages/plugin-hardhat/src/utils/npmFilesToBuild.ts create mode 100644 packages/plugin-hardhat/src/verify-plugin.ts create mode 100644 packages/plugin-hardhat/src/verify-proxy-task-action.ts create mode 100644 packages/plugin-hardhat/test/solidity/Upgrades.t.sol diff --git a/.eslintrc.js b/.eslintrc.js index 038626645..e35b391a4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { root: true, parserOptions: { - ecmaVersion: 2020, + ecmaVersion: 2022, }, extends: ['eslint:recommended', 'plugin:prettier/recommended'], env: { @@ -15,7 +15,13 @@ module.exports = { 'unicorn/no-array-reduce': 'warn', 'no-plusplus': ['warn', { allowForLoopAfterthoughts: true }], }, - ignorePatterns: ['submodules'], + ignorePatterns: [ + 'submodules', + // ESLint 8.x doesn't support ES2025 import attributes syntax + // uses import attributes (import ... with { type: 'json' }) + // which is valid ES2025 syntax but ESLint parser doesn't support it yet + 'packages/plugin-hardhat/test/*.js', + ], overrides: [ { files: ['*.ts'], @@ -29,10 +35,14 @@ module.exports = { }, }, { - files: ['ava.config.js'], + files: ['ava.config.js', 'packages/plugin-hardhat/test/*.js', 'packages/plugin-hardhat/src/*.js'], parserOptions: { + ecmaVersion: 'latest', sourceType: 'module', }, + rules: { + 'no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + }, }, { files: ['packages/plugin-truffle/**'], diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 0d40c85fd..ce82d9c60 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -5,9 +5,19 @@ runs: steps: - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x cache: yarn + - name: Cache Hardhat solc compilers + uses: actions/cache@v4 + with: + path: | + ~/.cache/hardhat/compilers-v3 + ~/.cache/hardhat-nodejs/compilers-v3 + key: hardhat-compilers-${{ runner.os }}-${{ hashFiles('**/package.json', '**/yarn.lock') }} + restore-keys: | + hardhat-compilers-${{ runner.os }}- + - name: Install dependencies run: yarn --frozen-lockfile --prefer-offline shell: bash diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index 8d55d1b1e..8b0ba6adb 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -22,6 +22,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 with: fetch-depth: 0 # Include history so Changesets finds merge-base + submodules: recursive - name: Set up environment uses: ./.github/actions/setup - name: Check changeset diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index d4fb57334..6107e3f66 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -17,6 +17,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Set up environment uses: ./.github/actions/setup @@ -35,10 +37,16 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Set up environment uses: ./.github/actions/setup + - name: Assert upgrades-core resolution (plugin-hardhat) + if: matrix.package == 'plugin-hardhat' + run: bash packages/plugin-hardhat/scripts/assert-upgrades-core-resolution.sh + - name: Run tests run: yarn --cwd "packages/${{matrix.package}}" run test @@ -47,10 +55,15 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive - name: Set up environment uses: ./.github/actions/setup + - name: Assert upgrades-core resolution + run: bash packages/plugin-hardhat/scripts/assert-upgrades-core-resolution.sh + - name: Run coverage run: yarn coverage @@ -114,4 +127,4 @@ jobs: if: steps.create_pull_request.outcome == 'failure' run: | git push origin --delete $BRANCH_NAME - \ No newline at end of file + diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 327fbfeaf..51f887ea8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,6 +18,7 @@ jobs: with: fetch-depth: 0 # To get all tags ref: ${{ github.ref }} + submodules: recursive - name: Set up environment uses: ./.github/actions/setup - name: Create Prepare Release PR or Publish diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index eff55f032..14772f48c 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -21,6 +21,7 @@ jobs: with: fetch-depth: 0 # To get all tags ref: ${{ github.ref }} + submodules: recursive - name: Set up environment uses: ./.github/actions/setup - name: Create Prepare Release PR diff --git a/.node-version b/.node-version index 209e3ef4b..2bd5a0a98 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20 +22 diff --git a/README.md b/README.md index 462a419fa..16831f4c1 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,11 @@ [![Checks](https://github.com/OpenZeppelin/openzeppelin-upgrades/actions/workflows/checks.yml/badge.svg)](https://github.com/OpenZeppelin/openzeppelin-upgrades/actions/workflows/checks.yml) [![License](https://img.shields.io/github/license/OpenZeppelin/openzeppelin-upgrades)](https://github.com/OpenZeppelin/openzeppelin-upgrades/blob/master/LICENSE) +> [!NOTE] +> This branch contains support for Hardhat 3. This version is still in testing. Do not use it to deploy or upgrade production deployments. For details and migration instructions, see: +> - [Hardhat Upgrades for Hardhat 3](./packages/plugin-hardhat/README.md) +> - [Hardhat 2 to 3 migration guide](./packages/plugin-hardhat/MIGRATION.md) + **Integrate upgrades into your existing workflow.** Plugins for [Hardhat](https://hardhat.org/) and [Foundry](https://book.getfoundry.sh/) to deploy and manage upgradeable contracts on Ethereum. - Deploy upgradeable contracts. diff --git a/package.json b/package.json index add732190..be4a49b49 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-unicorn": "^51.0.0", - "ethers": "^6.8.1", + "ethers": "^6.14.0", "nyc": "^17.0.0", "prettier": "^3.0.0", "typescript": "^5.0.0", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index bcdbdfdc3..40fa84500 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,12 +1,17 @@ # Changelog +## 1.45.0-alpha.1 (2026-03-02) + +- Improve support for validating build-info files in CLI for Hardhat 3, Hardhat 2, and Foundry. ([#1194](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1194)) + +> **Note** ⚠️ This version is still in testing and should not be used in production. + ## 1.45.0-alpha.0 (2026-01-20) - Support Hardhat 3 build-info file format for CLI validation ([#1203](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1203)) > **Note** ⚠️ This version is still in testing and should not be used in production. - ## 1.44.2 (2025-11-03) - Add Celo Sepolia network to manifest file names. ([#1189](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1189)) diff --git a/packages/core/package.json b/packages/core/package.json index 4fb48ca49..b0622ac7e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/upgrades-core", - "version": "1.45.0-alpha.0", + "version": "1.45.0-alpha.1", "description": "", "repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades", "license": "MIT", diff --git a/packages/core/src/cli/validate/build-info-file.test.ts b/packages/core/src/cli/validate/build-info-file.test.ts index 7edd637d1..d30246234 100644 --- a/packages/core/src/cli/validate/build-info-file.test.ts +++ b/packages/core/src/cli/validate/build-info-file.test.ts @@ -357,12 +357,67 @@ test.serial('get build info files - override with custom absolute path', async t assertBuildInfoFiles(t, buildInfoFiles); }); -test.serial('invalid build info file', async t => { +test.serial('generic invalid build info file', async t => { await fs.mkdir('invalid-build-info', { recursive: true }); await fs.writeFile('invalid-build-info/invalid.json', JSON.stringify({ output: {} })); const error = await t.throwsAsync(getBuildInfoFiles('invalid-build-info')); + t.true(error?.message.includes('must include Solidity compiler input, output, and solcVersion')); +}); + +test.serial('Foundry format (ethers-rs) with missing output suggests forge clean && forge build', async t => { + await fs.mkdir('foundry-format-missing', { recursive: true }); + + await fs.writeFile( + 'foundry-format-missing/build.json', + JSON.stringify({ + _format: 'ethers-rs-sol-build-info-1', + input: BUILD_INFO.input, + solcVersion: '0.8.31', + // output missing + }), + ); + const error = await t.throwsAsync(getBuildInfoFiles('foundry-format-missing')); t.true(error?.message.includes('must contain Solidity compiler input, output, and solcVersion')); + t.true(error?.message.includes('ethers-rs-sol-build-info')); + t.true(error?.message.includes('forge clean && forge build')); +}); + +test.serial('Hardhat 3 format with missing .output.json suggests hardhat compile', async t => { + await fs.mkdir('hh3-format-missing-output', { recursive: true }); + + await fs.writeFile( + 'hh3-format-missing-output/solc-0_8_0-abc123.json', + JSON.stringify({ + _format: 'hh3-sol-build-info-1', + input: BUILD_INFO.input, + solcVersion: '0.8.9', + // output in separate .output.json which we do not create + }), + ); + const error = await t.throwsAsync(getBuildInfoFiles('hh3-format-missing-output')); + t.true(error?.message.includes('could not be read') || error?.message.includes('missing Solidity compiler output')); + t.true(error?.message.includes('hardhat compile')); +}); + +test.serial('Hardhat 2 format with missing output falls back to generic help', async t => { + await fs.mkdir('invalid-hh2-build-info', { recursive: true }); + + // Missing output but has an hh2-style _format with input and solcVersion. + await fs.writeFile( + 'invalid-hh2-build-info/invalid.json', + JSON.stringify({ + _format: 'hh-sol-build-info-1', + input: BUILD_INFO.input, + solcVersion: '0.8.9', + // output missing + }), + ); + + const error = await t.throwsAsync(getBuildInfoFiles('invalid-hh2-build-info')); + t.true(error?.message.includes('must include Solidity compiler input, output, and solcVersion')); + t.true(error?.message.includes('invalid.json')); + t.false(error?.message.includes('Hardhat 3')); }); test.serial('dir does not exist', async t => { diff --git a/packages/core/src/cli/validate/build-info-file.ts b/packages/core/src/cli/validate/build-info-file.ts index a3025419a..faa50f617 100644 --- a/packages/core/src/cli/validate/build-info-file.ts +++ b/packages/core/src/cli/validate/build-info-file.ts @@ -151,6 +151,23 @@ async function readBuildInfo(buildInfoFilePaths: string[], dirShortName: string) return buildInfoFiles; } +const FOUNDRY_BUILD_INFO_HELP = `\ +Foundry build-info files must contain Solidity compiler input, output, and solcVersion. +Ensure foundry.toml has: + [profile.default] + build_info = true + extra_output = ["storageLayout"] +Then run: ${FOUNDRY_COMPILE_COMMAND}`; + +const HH3_BUILD_INFO_HELP = `\ +Hardhat 3's build-info uses a main .json file (input, solcVersion) and a separate .output.json (output). +Ensure you compile with Hardhat 3 so that artifacts/build-info/ contains both the main files and the .output.json files. +Run: ${HARDHAT_COMPILE_COMMAND}`; + +const GENERIC_BUILD_INFO_HELP = `\ +If using Foundry, ensure foundry.toml has build_info = true and extra_output = ["storageLayout"], then compile with '${FOUNDRY_COMPILE_COMMAND}'. +If using Hardhat, compile with '${HARDHAT_COMPILE_COMMAND}'.`; + async function loadBuildInfo(buildInfoFilePath: string): Promise<{ input: SolcInput; output: SolcOutput; @@ -172,75 +189,88 @@ async function loadBuildInfo(buildInfoFilePath: string): Promise<{ const format = buildInfoJson._format as string | undefined; - if ( - typeof format === 'string' && - (format.startsWith('hh3-sol-build-info') || format.startsWith('hh-sol-build-info')) - ) { - if (buildInfoJson.input !== undefined && buildInfoJson.solcVersion !== undefined) { - let inputData: SolcInput = buildInfoJson.input; - let outputData: SolcOutput | undefined = buildInfoJson.output; + // Foundry (ethers-rs-sol-build-info-1) normally has input, output, solcVersion at top level. + // If we reach here with Foundry format, a field is missing — suggest recompiling. + if (typeof format === 'string' && format.startsWith('ethers-rs-sol-build-info')) { + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} must contain Solidity compiler input, output, and solcVersion. Got format: ${format}.`, + () => FOUNDRY_BUILD_INFO_HELP, + ); + } + + if (typeof format === 'string' && format.startsWith('hh3-sol-build-info')) { + if (buildInfoJson.input === undefined || buildInfoJson.solcVersion === undefined) { + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} (Hardhat 3 format) must contain input and solcVersion. Got format: ${format}.`, + () => HH3_BUILD_INFO_HELP, + ); + } + let inputData: SolcInput = buildInfoJson.input; + let outputData: SolcOutput | undefined = buildInfoJson.output; + + if (outputData === undefined) { + const { dir, name } = path.parse(buildInfoFilePath); + const outputFilePath = path.join(dir, `${name}.output.json`); + + try { + const outputJson = await readJSON(outputFilePath); + outputData = outputJson.output ?? outputJson; + } catch (error) { + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} does not contain output, and output file ${outputFilePath} could not be read.`, + () => HH3_BUILD_INFO_HELP, + ); + } if (outputData === undefined) { - const { dir, name } = path.parse(buildInfoFilePath); - const outputFilePath = path.join(dir, `${name}.output.json`); - - try { - const outputJson = await readJSON(outputFilePath); - outputData = outputJson.output ?? outputJson; - } catch (error) { - throw new ValidateCommandError( - `Build info file ${buildInfoFilePath} does not contain output, and output file ${outputFilePath} could not be read.`, - ); - } - - if (outputData === undefined) { - throw new ValidateCommandError( - `Build info file ${buildInfoFilePath} does not contain output, and output file ${outputFilePath} is missing Solidity compiler output.`, - ); - } + throw new ValidateCommandError( + `Build info file ${buildInfoFilePath} does not contain output, and output file ${outputFilePath} is missing Solidity compiler output.`, + () => HH3_BUILD_INFO_HELP, + ); + } + } + + const userSourceNameMap: Record | undefined = buildInfoJson.userSourceNameMap; + if (userSourceNameMap !== undefined) { + const canonicalToUser: Record = {}; + for (const [userSource, canonicalSource] of Object.entries(userSourceNameMap)) { + canonicalToUser[canonicalSource] = userSource; } - const userSourceNameMap: Record | undefined = buildInfoJson.userSourceNameMap; - if (userSourceNameMap !== undefined) { - const canonicalToUser: Record = {}; - for (const [userSource, canonicalSource] of Object.entries(userSourceNameMap)) { - canonicalToUser[canonicalSource] = userSource; - } - - if (inputData.sources !== undefined) { - inputData = { - ...inputData, - sources: remapKeys(inputData.sources, canonicalToUser), - }; - } - - if (outputData.sources !== undefined) { - outputData = { - ...outputData, - sources: remapKeys(outputData.sources, canonicalToUser), - contracts: - outputData.contracts !== undefined - ? remapKeys(outputData.contracts, canonicalToUser) - : outputData.contracts, - }; - } else if (outputData.contracts !== undefined) { - outputData = { - ...outputData, - contracts: remapKeys(outputData.contracts, canonicalToUser), - }; - } + if (inputData.sources !== undefined) { + inputData = { + ...inputData, + sources: remapKeys(inputData.sources, canonicalToUser), + }; } - return { - input: inputData, - output: outputData, - solcVersion: buildInfoJson.solcVersion, - }; + if (outputData.sources !== undefined) { + outputData = { + ...outputData, + sources: remapKeys(outputData.sources, canonicalToUser), + contracts: + outputData.contracts !== undefined + ? remapKeys(outputData.contracts, canonicalToUser) + : outputData.contracts, + }; + } else if (outputData.contracts !== undefined) { + outputData = { + ...outputData, + contracts: remapKeys(outputData.contracts, canonicalToUser), + }; + } } + + return { + input: inputData, + output: outputData, + solcVersion: buildInfoJson.solcVersion, + }; } throw new ValidateCommandError( - `Build info file ${buildInfoFilePath} must contain Solidity compiler input, output, and solcVersion. Got format: ${format ?? 'unknown'}.`, + `Build info from ${buildInfoFilePath} must include Solidity compiler input, output, and solcVersion. Got format: ${format ?? 'unknown'}.`, + () => GENERIC_BUILD_INFO_HELP, ); } diff --git a/packages/plugin-hardhat/CHANGELOG.md b/packages/plugin-hardhat/CHANGELOG.md index a7c55e750..2b2062182 100644 --- a/packages/plugin-hardhat/CHANGELOG.md +++ b/packages/plugin-hardhat/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## 4.0.0-alpha.0 (2026-03-02) + +> **Note** ⚠️ This version is still in testing. Do not use it to deploy or upgrade production deployments. + +- Migrate to Hardhat 3 with ESM module structure and plugin hooks architecture. ([#1194](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1194)) + + ### Breaking Changes + + - **Requires Hardhat 3**: Minimum `hardhat@^3.0.0` required + - **ESM-only**: Package converted to ESM, CommonJS no longer supported + - **API Changes**: + - No automatic `hre.upgrades` - must call factory function explicitly + - Factory functions are async: `await upgrades(hre, connection)` + - Network connection must be explicitly created: `await hre.network.connect()` + - `ethers` now comes from connection, not `hre.ethers` + - **Import Changes**: Import factory functions instead of side-effect import + - Before: `import '@openzeppelin/hardhat-upgrades'` + - After: `import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'` + + ### Usage and Migration + + See the [README](./README.md) for Hardhat 3 usage, the [examples](./examples/README.md) directory for sample projects, and the [Migration Guide](./MIGRATION.md) for Hardhat 2 to 3 migration steps. + + ### Changes + + - Migrated from `extendEnvironment` to Hardhat 3's `HardhatPlugin` with `hookHandlers` + - Converted package to ESM + - Etherscan verification requires `@nomicfoundation/hardhat-verify@^3.0.10` (optional peer dependency). + - Support [Solidity tests in Hardhat 3](./README.md#solidity-tests-hardhat-3) with `@openzeppelin/foundry-upgrades`. + - Added example projects to work with Hardhat 3 + ## 3.9.1 (2025-06-30) diff --git a/packages/plugin-hardhat/MIGRATION.md b/packages/plugin-hardhat/MIGRATION.md new file mode 100644 index 000000000..aba8fbc61 --- /dev/null +++ b/packages/plugin-hardhat/MIGRATION.md @@ -0,0 +1,217 @@ +# Migration Guide: Hardhat 2 to Hardhat 3 + +> **Prerequisite:** Migrate your Hardhat project to Hardhat 3 first. See the [official Hardhat 3 migration guide](https://hardhat.org/docs/migrate-from-hardhat2). +> +> **Note** ⚠️ This version is still in testing. Do not use it to deploy or upgrade production deployments. + +## 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 the [README section on Solidity tests](./README.md#solidity-tests-hardhat-3). + +## 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/packages/plugin-hardhat/README.md b/packages/plugin-hardhat/README.md index baef16d6c..994be226d 100644 --- a/packages/plugin-hardhat/README.md +++ b/packages/plugin-hardhat/README.md @@ -3,40 +3,60 @@ [![Docs](https://img.shields.io/badge/docs-%F0%9F%93%84-blue)](https://docs.openzeppelin.com/upgrades-plugins/hardhat-upgrades) [![NPM Package](https://img.shields.io/npm/v/@openzeppelin/hardhat-upgrades.svg)](https://www.npmjs.org/package/@openzeppelin/hardhat-upgrades) -**Hardhat plugin for deploying and managing upgradeable contracts.** This package adds functions to your Hardhat scripts so you can deploy and upgrade proxies for your contracts. Depends on `ethers.js`. +**Hardhat plugin for deploying and managing upgradeable contracts.** 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 [Migration Guide](./MIGRATION.md) for breaking changes and how to update your code, or see the [example projects](./examples/) for complete transparent and UUPS proxy examples using Hardhat 3. +> +> **Note** ⚠️ This version is still in testing. Do not use it to deploy or upgrade production deployments. ## Installation ``` -npm install --save-dev @openzeppelin/hardhat-upgrades +npm install --save-dev @openzeppelin/hardhat-upgrades@next npm install --save-dev @nomicfoundation/hardhat-ethers ethers # peer dependencies ``` -And register the plugin in your [`hardhat.config.js`](https://hardhat.org/config/): +> **Note:** 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. -```js -// Javascript -require('@openzeppelin/hardhat-upgrades'); +Register the `@openzeppelin/hardhat-upgrades` plugin in your [`hardhat.config.ts`](https://hardhat.org/config/): + +```typescript +import { defineConfig } from 'hardhat/config'; +import hardhatUpgrades from '@openzeppelin/hardhat-upgrades'; -// Typescript -import '@openzeppelin/hardhat-upgrades'; +export default defineConfig({ + plugins: [hardhatUpgrades], + // ... rest of config +}); ``` ## 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: ```js // scripts/create-box.js -const { ethers, upgrades } = require("hardhat"); +import hre from "hardhat"; +import { upgrades } from "@openzeppelin/hardhat-upgrades"; async function main() { + // Create connection once and reuse for all operations + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); + const Box = await ethers.getContractFactory("Box"); - const box = await upgrades.deployProxy(Box, [42]); + const box = await upgradesApi.deployProxy(Box, [42]); await box.waitForDeployment(); console.log("Box deployed to:", await box.getAddress()); + + // Reuse the same connection for upgrades + const BoxV2 = await ethers.getContractFactory("BoxV2"); + await upgradesApi.upgradeProxy(await box.getAddress(), BoxV2); } main(); @@ -48,11 +68,16 @@ Then, in another script, you can use the `upgradeProxy` function to upgrade the ```js // scripts/upgrade-box.js -const { ethers, upgrades } = require("hardhat"); +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 BoxV2 = await ethers.getContractFactory("BoxV2"); - const box = await upgrades.upgradeProxy(BOX_ADDRESS, BoxV2); + await upgradesApi.upgradeProxy(BOX_ADDRESS, BoxV2); console.log("Box upgraded"); } @@ -69,16 +94,21 @@ You can also use this plugin to deploy an upgradeable beacon for your contract w ```js // scripts/create-box.js -const { ethers, upgrades } = require("hardhat"); +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 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()); - 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()); } @@ -90,12 +120,16 @@ Then, in another script, you can use the `upgradeBeacon` function to upgrade the ```js // scripts/upgrade-box.js -const { ethers, upgrades } = require("hardhat"); +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 BoxV2 = await ethers.getContractFactory("BoxV2"); - - await upgrades.upgradeBeacon(BEACON_ADDRESS, BoxV2); + await upgradesApi.upgradeBeacon(BEACON_ADDRESS, BoxV2); console.log("Beacon upgraded"); const box = BoxV2.attach(BOX_ADDRESS); @@ -108,18 +142,31 @@ main(); 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` block or use top-level await (ESM) to ensure all operations share the same context. + ### Proxies ```js -const { expect } = require("chai"); +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); + }); + 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 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'); @@ -130,17 +177,28 @@ describe("Box", function() { ### Beacon proxies ```js -const { expect } = require("chai"); +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); + }); + 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]); + 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(); @@ -149,6 +207,67 @@ describe("Box", function() { }); ``` +### Solidity tests (Hardhat 3) + +You can also write Solidity tests in Hardhat 3 and perform proxy deployments, upgrades, and upgrade safety validations directly from Solidity via `@openzeppelin/foundry-upgrades`. + +This is optional and only needed if you want Solidity-based tests. + +Install: + +```bash +npm install --save-dev @openzeppelin/foundry-upgrades@next +``` + +Configure your Hardhat config for Solidity tests: + +```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 `npx hardhat clean` or `npx hardhat compile --force` before running your Solidity tests: + +```bash +npx hardhat compile --force +npx hardhat test solidity +``` + +## TypeScript Support + +Full TypeScript support is included. Import the factory functions with type safety: + +```typescript +import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'; +import type { HardhatUpgrades, DefenderHardhatUpgrades } from '@openzeppelin/hardhat-upgrades'; + +task('deploy', async (args, hre) => { + const connection = await hre.network.connect(); + const upgradesApi: HardhatUpgrades = await upgrades(hre, connection); + // ... +}); +``` + ## Learn more * Refer to the [API documentation](https://docs.openzeppelin.com/upgrades-plugins/api-hardhat-upgrades). * Also see the [main documentation](https://docs.openzeppelin.com/upgrades-plugins) for more info. diff --git a/packages/plugin-hardhat/ava.config.js b/packages/plugin-hardhat/ava.config.js index 00dde5cfc..edf09641f 100644 --- a/packages/plugin-hardhat/ava.config.js +++ b/packages/plugin-hardhat/ava.config.js @@ -1,4 +1,4 @@ -module.exports = { +export default { workerThreads: false, files: ['test/*.js'], watchMode: { diff --git a/packages/plugin-hardhat/contracts/Constructor.sol b/packages/plugin-hardhat/contracts/WithConstructor.sol similarity index 100% rename from packages/plugin-hardhat/contracts/Constructor.sol rename to packages/plugin-hardhat/contracts/WithConstructor.sol diff --git a/packages/plugin-hardhat/contracts/foundry/GreeterInitializable.sol b/packages/plugin-hardhat/contracts/foundry/GreeterInitializable.sol new file mode 100644 index 000000000..3052c3c1c --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/GreeterInitializable.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract GreeterInitializable is Initializable, OwnableUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); + greeting = _greeting; + } +} diff --git a/packages/plugin-hardhat/contracts/foundry/GreeterProxiable.sol b/packages/plugin-hardhat/contracts/foundry/GreeterProxiable.sol new file mode 100644 index 000000000..693768d56 --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/GreeterProxiable.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +contract GreeterProxiable is Initializable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); + greeting = _greeting; + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} +} diff --git a/packages/plugin-hardhat/contracts/foundry/GreeterV2.sol b/packages/plugin-hardhat/contracts/foundry/GreeterV2.sol new file mode 100644 index 000000000..787b2ba3c --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/GreeterV2.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/// @custom:oz-upgrades-from contracts/foundry/GreeterInitializable.sol:GreeterInitializable +contract GreeterV2 is Initializable, OwnableUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); + greeting = _greeting; + } + + function resetGreeting() public reinitializer(2) { + greeting = "resetted"; + } + + function setGreeting(string memory _greeting) public onlyOwner { + greeting = _greeting; + } +} diff --git a/packages/plugin-hardhat/contracts/foundry/GreeterV2Proxiable.sol b/packages/plugin-hardhat/contracts/foundry/GreeterV2Proxiable.sol new file mode 100644 index 000000000..72b1d1444 --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/GreeterV2Proxiable.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +// These contracts are for testing only, they are not safe for use in production. + +/// @custom:oz-upgrades-from contracts/foundry/GreeterProxiable.sol:GreeterProxiable +contract GreeterV2Proxiable is Initializable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + string public greeting; + + function initialize(address initialOwner, string memory _greeting) public initializer { + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); + greeting = _greeting; + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + + function resetGreeting() public reinitializer(2) { + greeting = "resetted"; + } +} diff --git a/packages/plugin-hardhat/contracts/foundry/HasOwner.sol b/packages/plugin-hardhat/contracts/foundry/HasOwner.sol new file mode 100644 index 000000000..7c98a8afd --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/HasOwner.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// These contracts are for testing only, they are not safe for use in production. + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +// Basic but pointless contract that has its own owner and can call ProxyAdmin functions +contract HasOwner is Ownable { + constructor(address initialOwner) Ownable(initialOwner) {} + + function upgradeAndCall( + ProxyAdmin admin, + ITransparentUpgradeableProxy proxy, + address implementation, + bytes memory data + ) public payable virtual onlyOwner { + admin.upgradeAndCall{value: msg.value}(proxy, implementation, data); + } +} + +contract NoGetter {} + +contract StringOwner { + string public owner; + + constructor(string memory initialOwner) { + owner = initialOwner; + } +} + +contract StateChanging { + bool public triggered; + + function owner() public { + triggered = true; + } +} diff --git a/packages/plugin-hardhat/contracts/foundry/MyContractFile.sol b/packages/plugin-hardhat/contracts/foundry/MyContractFile.sol new file mode 100644 index 000000000..9dedb14d3 --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/MyContractFile.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// This contract is for testing only. + +contract MyContractName { + +} diff --git a/packages/plugin-hardhat/contracts/foundry/NoLicense.sol b/packages/plugin-hardhat/contracts/foundry/NoLicense.sol new file mode 100644 index 000000000..ebef766cc --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/NoLicense.sol @@ -0,0 +1,3 @@ +pragma solidity ^0.8.20; + +contract NoLicense {} diff --git a/packages/plugin-hardhat/contracts/foundry/Unlicensed.sol b/packages/plugin-hardhat/contracts/foundry/Unlicensed.sol new file mode 100644 index 000000000..f73bb7396 --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/Unlicensed.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +contract Unlicensed {} diff --git a/packages/plugin-hardhat/contracts/foundry/UnrecognizedLicense.sol b/packages/plugin-hardhat/contracts/foundry/UnrecognizedLicense.sol new file mode 100644 index 000000000..34768fe65 --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/UnrecognizedLicense.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: UnrecognizedId +pragma solidity ^0.8.20; + +contract UnrecognizedLicense {} diff --git a/packages/plugin-hardhat/contracts/foundry/UpgradeInterfaceVersions.sol b/packages/plugin-hardhat/contracts/foundry/UpgradeInterfaceVersions.sol new file mode 100644 index 000000000..aedd50ca7 --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/UpgradeInterfaceVersions.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// These contracts are for testing only. + +contract UpgradeInterfaceVersionString { + string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; +} + +contract UpgradeInterfaceVersionNoGetter {} + +contract UpgradeInterfaceVersionEmpty { + string public constant UPGRADE_INTERFACE_VERSION = ""; +} + +contract UpgradeInterfaceVersionInteger { + uint256 public constant UPGRADE_INTERFACE_VERSION = 5; +} + +contract UpgradeInterfaceVersionVoid { + function UPGRADE_INTERFACE_VERSION() external pure {} +} diff --git a/packages/plugin-hardhat/contracts/foundry/Validations.sol b/packages/plugin-hardhat/contracts/foundry/Validations.sol new file mode 100644 index 000000000..bcc997059 --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/Validations.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// These contracts are for testing only, they are not safe for use in production. + +contract Unsafe { + function unsafe() public { + (bool s, ) = msg.sender.delegatecall(""); + s; + } +} + +contract LayoutV1 { + uint256 a; + uint256 b; +} + +contract LayoutV2_Bad { + uint256 a; + uint256 c; + uint256 b; +} + +/// @custom:oz-upgrades-from LayoutV1 +contract LayoutV2_Renamed { + uint256 _old_a; + uint256 b; +} + +/// @custom:oz-upgrades-from LayoutV1 +contract LayoutV2_UpgradesFrom_Bad { + uint256 a; + uint256 c; + uint256 b; +} + +contract NamespacedV1 { + /// @custom:storage-location erc7201:s + struct S { + uint256 a; + uint256 b; + } +} + +contract NamespacedV2_Bad { + /// @custom:storage-location erc7201:s + struct S { + uint256 a; + uint256 c; + uint256 b; + } +} + +/// @custom:oz-upgrades-from NamespacedV1 +contract NamespacedV2_UpgradesFrom_Bad { + /// @custom:storage-location erc7201:s + struct S { + uint256 a; + uint256 c; + uint256 b; + } +} + +contract NamespacedV2_Ok { + /// @custom:storage-location erc7201:s + struct S { + uint256 a; + uint256 b; + uint256 c; + } +} + +/// @custom:oz-upgrades-from NamespacedV1 +contract NamespacedV2_UpgradesFrom_Ok { + /// @custom:storage-location erc7201:s + struct S { + uint256 a; + uint256 b; + uint256 c; + } +} + +contract HasWarningAndError { + uint256 immutable x = 1; // allow `state-variable-immutable` using option to turn into a warning + + function unsafe() public { + (bool s, ) = msg.sender.delegatecall(""); + s; + } +} diff --git a/packages/plugin-hardhat/contracts/foundry/WithConstructor.sol b/packages/plugin-hardhat/contracts/foundry/WithConstructor.sol new file mode 100644 index 000000000..4d10f11ea --- /dev/null +++ b/packages/plugin-hardhat/contracts/foundry/WithConstructor.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// These contracts are for testing only, they are not safe for use in production. + +contract WithConstructor { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + uint256 public immutable a; + + uint256 public b; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(uint256 _a) { + a = _a; + } + + function initialize(uint256 _b) public { + b = _b; + } +} + +contract NoInitializer { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + uint256 public immutable a; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(uint256 _a) { + a = _a; + } +} diff --git a/packages/plugin-hardhat/examples/BoxSolidityTests/.gitignore b/packages/plugin-hardhat/examples/BoxSolidityTests/.gitignore new file mode 100644 index 000000000..c722b2186 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxSolidityTests/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +artifacts/ +cache/ +*.log +.env diff --git a/packages/plugin-hardhat/examples/BoxSolidityTests/contracts/Box.sol b/packages/plugin-hardhat/examples/BoxSolidityTests/contracts/Box.sol new file mode 100644 index 000000000..64732b690 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxSolidityTests/contracts/Box.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract Box is Initializable { + uint256 private _value; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(uint256 initialValue) public initializer { + _value = initialValue; + } + + function store(uint256 value) public { + _value = value; + } + + function retrieve() public view returns (uint256) { + return _value; + } +} diff --git a/packages/plugin-hardhat/examples/BoxSolidityTests/contracts/BoxV2.sol b/packages/plugin-hardhat/examples/BoxSolidityTests/contracts/BoxV2.sol new file mode 100644 index 000000000..7cc972914 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxSolidityTests/contracts/BoxV2.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/// @custom:oz-upgrades-from Box +contract BoxV2 is Initializable { + uint256 private _value; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(uint256 initialValue) public initializer { + _value = initialValue; + } + + function store(uint256 value) public { + _value = value; + } + + function retrieve() public view returns (uint256) { + return _value; + } + + function increment() public { + _value += 1; + } +} diff --git a/packages/plugin-hardhat/examples/BoxSolidityTests/hardhat.config.ts b/packages/plugin-hardhat/examples/BoxSolidityTests/hardhat.config.ts new file mode 100644 index 000000000..a70a5609d --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxSolidityTests/hardhat.config.ts @@ -0,0 +1,29 @@ +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()], + settings: { + evmVersion: 'cancun', + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + test: { + solidity: { + ffi: true, + fsPermissions: { + readDirectory: ['artifacts/contracts'], + }, + }, + }, +}); diff --git a/packages/plugin-hardhat/examples/BoxSolidityTests/package.json b/packages/plugin-hardhat/examples/BoxSolidityTests/package.json new file mode 100644 index 000000000..25d87d766 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxSolidityTests/package.json @@ -0,0 +1,20 @@ +{ + "name": "box-solidity-tests-example", + "version": "1.0.0", + "type": "module", + "scripts": { + "compile": "hardhat compile", + "test": "hardhat compile --force && hardhat test solidity" + }, + "devDependencies": { + "@nomicfoundation/hardhat-ethers": "^4.0.2", + "@openzeppelin/contracts": "^5.0.0", + "@openzeppelin/contracts-upgradeable": "^5.0.0", + "@openzeppelin/foundry-upgrades": "^0.4.1-alpha.0", + "@openzeppelin/hardhat-upgrades": "^4.0.0-alpha.0", + "forge-std": "github:foundry-rs/forge-std#v1.9.7", + "hardhat": "^3.0.0", + "tsx": "^4.7.0", + "typescript": "^5.0.0" + } +} diff --git a/packages/plugin-hardhat/examples/BoxSolidityTests/test/solidity/Upgrades.t.sol b/packages/plugin-hardhat/examples/BoxSolidityTests/test/solidity/Upgrades.t.sol new file mode 100644 index 000000000..1bfe0a551 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxSolidityTests/test/solidity/Upgrades.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import { Test } from "forge-std/Test.sol"; +import { Upgrades } from "@openzeppelin/foundry-upgrades/src/Upgrades.sol"; +import { Options } from "@openzeppelin/foundry-upgrades/src/Options.sol"; + +import { Box } from "../../contracts/Box.sol"; +import { BoxV2 } from "../../contracts/BoxV2.sol"; + +contract UpgradesTest is Test { + function testDeployAndUpgradeTransparentProxy() public { + address proxy = Upgrades.deployTransparentProxy( + "contracts/Box.sol:Box", + msg.sender, + abi.encodeCall(Box.initialize, (42)) + ); + Box box = Box(proxy); + assertEq(box.retrieve(), 42); + + Upgrades.upgradeProxy(proxy, "contracts/BoxV2.sol:BoxV2", "", msg.sender); + BoxV2 boxV2 = BoxV2(proxy); + + boxV2.increment(); + assertEq(boxV2.retrieve(), 43); + } + + function testValidateUpgrade() public { + Options memory opts; + Upgrades.validateUpgrade("contracts/BoxV2.sol:BoxV2", opts); + } +} diff --git a/packages/plugin-hardhat/examples/BoxSolidityTests/tsconfig.json b/packages/plugin-hardhat/examples/BoxSolidityTests/tsconfig.json new file mode 100644 index 000000000..2cbe50dbf --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxSolidityTests/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "lib": ["es2020"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "moduleResolution": "node16" + }, + "include": ["./hardhat.config.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/plugin-hardhat/examples/BoxTransparent/.gitignore b/packages/plugin-hardhat/examples/BoxTransparent/.gitignore new file mode 100644 index 000000000..c722b2186 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +artifacts/ +cache/ +*.log +.env diff --git a/packages/plugin-hardhat/examples/BoxTransparent/ava.config.js b/packages/plugin-hardhat/examples/BoxTransparent/ava.config.js new file mode 100644 index 000000000..4529a7372 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/ava.config.js @@ -0,0 +1,6 @@ +export default { + files: ['test/**/*.test.ts'], + extensions: { ts: 'module' }, + nodeArguments: ['--import', 'tsx'], + timeout: '60s', +}; diff --git a/packages/plugin-hardhat/examples/BoxTransparent/contracts/Box.sol b/packages/plugin-hardhat/examples/BoxTransparent/contracts/Box.sol new file mode 100644 index 000000000..64732b690 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/contracts/Box.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract Box is Initializable { + uint256 private _value; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(uint256 initialValue) public initializer { + _value = initialValue; + } + + function store(uint256 value) public { + _value = value; + } + + function retrieve() public view returns (uint256) { + return _value; + } +} diff --git a/packages/plugin-hardhat/examples/BoxTransparent/contracts/BoxV2.sol b/packages/plugin-hardhat/examples/BoxTransparent/contracts/BoxV2.sol new file mode 100644 index 000000000..6e230f782 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/contracts/BoxV2.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +contract BoxV2 is Initializable { + uint256 private _value; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(uint256 initialValue) public initializer { + _value = initialValue; + } + + function store(uint256 value) public { + _value = value; + } + + function retrieve() public view returns (uint256) { + return _value; + } + + function increment() public { + _value += 1; + } +} diff --git a/packages/plugin-hardhat/examples/BoxTransparent/hardhat.config.ts b/packages/plugin-hardhat/examples/BoxTransparent/hardhat.config.ts new file mode 100644 index 000000000..e4224813b --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/hardhat.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'hardhat/config'; +import hardhatUpgrades from '@openzeppelin/hardhat-upgrades'; + +export default defineConfig({ + plugins: [hardhatUpgrades], + solidity: '0.8.28', +}); diff --git a/packages/plugin-hardhat/examples/BoxTransparent/package.json b/packages/plugin-hardhat/examples/BoxTransparent/package.json new file mode 100644 index 000000000..82762eeb8 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/package.json @@ -0,0 +1,21 @@ +{ + "name": "box-transparent-example", + "version": "1.0.0", + "type": "module", + "scripts": { + "compile": "hardhat compile", + "test": "hardhat compile && ava", + "deploy": "hardhat run scripts/1-deploy.ts", + "upgrade": "hardhat run scripts/2-upgrade.ts" + }, + "devDependencies": { + "@nomicfoundation/hardhat-ethers": "^4.0.2", + "@openzeppelin/contracts-upgradeable": "^5.0.0", + "@openzeppelin/hardhat-upgrades": "@openzeppelin/hardhat-upgrades@4.0.0-alpha.0", + "ava": "^6.0.0", + "ethers": "^6.8.1", + "hardhat": "^3.0.0", + "tsx": "^4.7.0", + "typescript": "^5.0.0" + } +} diff --git a/packages/plugin-hardhat/examples/BoxTransparent/scripts/1-deploy.ts b/packages/plugin-hardhat/examples/BoxTransparent/scripts/1-deploy.ts new file mode 100644 index 000000000..707d38a0b --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/scripts/1-deploy.ts @@ -0,0 +1,21 @@ +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 Box = await ethers.getContractFactory('Box'); + const box = await upgradesApi.deployProxy(Box, [42], { kind: 'transparent' }); + await box.waitForDeployment(); + + const proxyAddress = await box.getAddress(); + + console.log('Proxy deployed to:', proxyAddress); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/packages/plugin-hardhat/examples/BoxTransparent/scripts/2-upgrade.ts b/packages/plugin-hardhat/examples/BoxTransparent/scripts/2-upgrade.ts new file mode 100644 index 000000000..6ab82833c --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/scripts/2-upgrade.ts @@ -0,0 +1,26 @@ +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +const PROXY_ADDRESS = '0x...'; // Replace with your proxy address from 1-deploy.ts + +async function main() { + if (PROXY_ADDRESS === '0x...') { + console.error('Please set PROXY_ADDRESS to your deployed proxy address'); + process.exit(1); + } + + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); + + const BoxV2 = await ethers.getContractFactory('BoxV2'); + const box = await upgradesApi.upgradeProxy(PROXY_ADDRESS, BoxV2); + await box.waitForDeployment(); + + console.log('Box upgraded at:', await box.getAddress()); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/packages/plugin-hardhat/examples/BoxTransparent/test/upgrade.test.ts b/packages/plugin-hardhat/examples/BoxTransparent/test/upgrade.test.ts new file mode 100644 index 000000000..89119377f --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/test/upgrade.test.ts @@ -0,0 +1,29 @@ +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +const connection = await hre.network.connect(); +const { ethers } = connection as any; +const upgradesApi = await upgrades(hre, connection); + +test.after.always(() => connection.close()); + +test('deploys Box and preserves state through upgrade', async t => { + const Box = await ethers.getContractFactory('Box'); + const BoxV2 = await ethers.getContractFactory('BoxV2'); + + const box = await upgradesApi.deployProxy(Box, [42], { kind: 'transparent' }); + t.is(await box.retrieve(), 42n); + + await box.store(100); + t.is(await box.retrieve(), 100n); + + const boxV2 = await upgradesApi.upgradeProxy(await box.getAddress(), BoxV2); + + // State is preserved after upgrade + t.is(await boxV2.retrieve(), 100n); + + // New function works + await boxV2.increment(); + t.is(await boxV2.retrieve(), 101n); +}); diff --git a/packages/plugin-hardhat/examples/BoxTransparent/tsconfig.json b/packages/plugin-hardhat/examples/BoxTransparent/tsconfig.json new file mode 100644 index 000000000..3582fb48a --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxTransparent/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "lib": ["es2020"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "moduleResolution": "node16" + }, + "include": ["./scripts", "./test", "./hardhat.config.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/plugin-hardhat/examples/BoxUUPS/.gitignore b/packages/plugin-hardhat/examples/BoxUUPS/.gitignore new file mode 100644 index 000000000..c722b2186 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +artifacts/ +cache/ +*.log +.env diff --git a/packages/plugin-hardhat/examples/BoxUUPS/ava.config.js b/packages/plugin-hardhat/examples/BoxUUPS/ava.config.js new file mode 100644 index 000000000..4529a7372 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/ava.config.js @@ -0,0 +1,6 @@ +export default { + files: ['test/**/*.test.ts'], + extensions: { ts: 'module' }, + nodeArguments: ['--import', 'tsx'], + timeout: '60s', +}; diff --git a/packages/plugin-hardhat/examples/BoxUUPS/contracts/Box.sol b/packages/plugin-hardhat/examples/BoxUUPS/contracts/Box.sol new file mode 100644 index 000000000..822582e5f --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/contracts/Box.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract Box is Initializable, OwnableUpgradeable, UUPSUpgradeable { + uint256 private _value; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(address initialOwner, uint256 initialValue) public initializer { + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); + _value = initialValue; + } + + function store(uint256 value) public { + _value = value; + } + + function retrieve() public view returns (uint256) { + return _value; + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} +} diff --git a/packages/plugin-hardhat/examples/BoxUUPS/contracts/BoxV2.sol b/packages/plugin-hardhat/examples/BoxUUPS/contracts/BoxV2.sol new file mode 100644 index 000000000..d16f5ff19 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/contracts/BoxV2.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract BoxV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable { + uint256 private _value; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(address initialOwner, uint256 initialValue) public initializer { + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); + _value = initialValue; + } + + function store(uint256 value) public { + _value = value; + } + + function retrieve() public view returns (uint256) { + return _value; + } + + function increment() public { + _value += 1; + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} +} diff --git a/packages/plugin-hardhat/examples/BoxUUPS/hardhat.config.ts b/packages/plugin-hardhat/examples/BoxUUPS/hardhat.config.ts new file mode 100644 index 000000000..e4224813b --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/hardhat.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'hardhat/config'; +import hardhatUpgrades from '@openzeppelin/hardhat-upgrades'; + +export default defineConfig({ + plugins: [hardhatUpgrades], + solidity: '0.8.28', +}); diff --git a/packages/plugin-hardhat/examples/BoxUUPS/package.json b/packages/plugin-hardhat/examples/BoxUUPS/package.json new file mode 100644 index 000000000..fe5f1f84a --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/package.json @@ -0,0 +1,21 @@ +{ + "name": "box-uups-example", + "version": "1.0.0", + "type": "module", + "scripts": { + "compile": "hardhat compile", + "test": "hardhat compile && ava", + "deploy": "hardhat run scripts/1-deploy.ts", + "upgrade": "hardhat run scripts/2-upgrade.ts" + }, + "devDependencies": { + "@nomicfoundation/hardhat-ethers": "^4.0.2", + "@openzeppelin/contracts-upgradeable": "^5.0.0", + "@openzeppelin/hardhat-upgrades": "@openzeppelin/hardhat-upgrades@4.0.0-alpha.0", + "ava": "^6.0.0", + "ethers": "^6.8.1", + "hardhat": "^3.0.0", + "tsx": "^4.7.0", + "typescript": "^5.0.0" + } +} diff --git a/packages/plugin-hardhat/examples/BoxUUPS/scripts/1-deploy.ts b/packages/plugin-hardhat/examples/BoxUUPS/scripts/1-deploy.ts new file mode 100644 index 000000000..5062fb4f5 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/scripts/1-deploy.ts @@ -0,0 +1,22 @@ +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 [deployer] = await ethers.getSigners(); + const Box = await ethers.getContractFactory('Box'); + const box = await upgradesApi.deployProxy(Box, [deployer.address, 42], { kind: 'uups' }); + await box.waitForDeployment(); + + const proxyAddress = await box.getAddress(); + + console.log('Proxy deployed to:', proxyAddress); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/packages/plugin-hardhat/examples/BoxUUPS/scripts/2-upgrade.ts b/packages/plugin-hardhat/examples/BoxUUPS/scripts/2-upgrade.ts new file mode 100644 index 000000000..3346ee5a9 --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/scripts/2-upgrade.ts @@ -0,0 +1,26 @@ +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +const PROXY_ADDRESS = '0x...'; // Replace with your proxy address from 1-deploy.ts + +async function main() { + if (PROXY_ADDRESS === '0x...') { + console.error('Please set PROXY_ADDRESS to your deployed proxy address'); + process.exit(1); + } + + const connection = await hre.network.connect(); + const { ethers } = connection; + const upgradesApi = await upgrades(hre, connection); + + const BoxV2 = await ethers.getContractFactory('BoxV2'); + const box = await upgradesApi.upgradeProxy(PROXY_ADDRESS, BoxV2, { kind: 'uups' }); + await box.waitForDeployment(); + + console.log('Box upgraded at:', await box.getAddress()); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/packages/plugin-hardhat/examples/BoxUUPS/test/upgrade.test.ts b/packages/plugin-hardhat/examples/BoxUUPS/test/upgrade.test.ts new file mode 100644 index 000000000..c540f120b --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/test/upgrade.test.ts @@ -0,0 +1,30 @@ +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades } from '@openzeppelin/hardhat-upgrades'; + +const connection = await hre.network.connect(); +const { ethers } = connection as any; +const upgradesApi = await upgrades(hre, connection); + +test.after.always(() => connection.close()); + +test('deploys Box and preserves state through upgrade', async t => { + const [owner] = await ethers.getSigners(); + const Box = await ethers.getContractFactory('Box'); + const BoxV2 = await ethers.getContractFactory('BoxV2'); + + const box = await upgradesApi.deployProxy(Box, [owner.address, 42], { kind: 'uups' }); + t.is(await box.retrieve(), 42n); + + await box.store(100); + t.is(await box.retrieve(), 100n); + + const boxV2 = await upgradesApi.upgradeProxy(await box.getAddress(), BoxV2); + + // State is preserved after upgrade + t.is(await boxV2.retrieve(), 100n); + + // New function works + await boxV2.increment(); + t.is(await boxV2.retrieve(), 101n); +}); diff --git a/packages/plugin-hardhat/examples/BoxUUPS/tsconfig.json b/packages/plugin-hardhat/examples/BoxUUPS/tsconfig.json new file mode 100644 index 000000000..3582fb48a --- /dev/null +++ b/packages/plugin-hardhat/examples/BoxUUPS/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "lib": ["es2020"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "moduleResolution": "node16" + }, + "include": ["./scripts", "./test", "./hardhat.config.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/plugin-hardhat/examples/README.md b/packages/plugin-hardhat/examples/README.md new file mode 100644 index 000000000..10bdc3811 --- /dev/null +++ b/packages/plugin-hardhat/examples/README.md @@ -0,0 +1,40 @@ +# Examples + +Self-contained example projects demonstrating the OpenZeppelin Hardhat Upgrades plugin with Hardhat 3. + +All use a simple `Box` contract (stores a value, V2 adds `increment`). + +## BoxTransparent + +Transparent proxy pattern. The ProxyAdmin contract controls upgrades. + +```bash +cd BoxTransparent +npm install +npm test +``` + +## BoxUUPS + +UUPS proxy pattern. The implementation contract controls upgrades via `_authorizeUpgrade`. + +```bash +cd BoxUUPS +npm install +npm test +``` + +## BoxSolidityTests + +Solidity test example using `hardhat test solidity` with `@openzeppelin/foundry-upgrades` (FFI enabled). + +```bash +cd BoxSolidityTests +npm install +npm test +``` + +## Resources + +- [OpenZeppelin Hardhat Upgrades readme](../README.md) +- [Hardhat 3 migration guide](../MIGRATION.md) diff --git a/packages/plugin-hardhat/hardhat.config.js b/packages/plugin-hardhat/hardhat.config.js deleted file mode 100644 index 1c6c746db..000000000 --- a/packages/plugin-hardhat/hardhat.config.js +++ /dev/null @@ -1,37 +0,0 @@ -require('@openzeppelin/hardhat-upgrades'); - -const override = { - version: '0.8.10', - settings: { - optimizer: { - enabled: true, - }, - }, -}; - -module.exports = { - 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, - }, - }, -}; diff --git a/packages/plugin-hardhat/hardhat.config.ts b/packages/plugin-hardhat/hardhat.config.ts new file mode 100644 index 000000000..6986973e2 --- /dev/null +++ b/packages/plugin-hardhat/hardhat.config.ts @@ -0,0 +1,72 @@ +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'; +import hardhatUpgrades from './dist/index.js'; +import { proxyFilesToBuild } from './dist/index.js'; + +import 'dotenv/config'; + +// Set FOUNDRY_OUT for testing (can be overridden by .env file or inline env var) +if (!process.env.FOUNDRY_OUT) { + process.env.FOUNDRY_OUT = 'artifacts/contracts'; +} +const override: SolcUserConfig = { + version: '0.8.10', + settings: { + optimizer: { + enabled: true, + }, + }, +}; + +const config: HardhatUserConfig = { + plugins: [hardhatVerify, hardhatEthers, hardhatUpgrades], + 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, + }, + npmFilesToBuild: [...proxyFilesToBuild(), '@openzeppelin/contracts/access/manager/AccessManager.sol'], + }, + paths: { + tests: { + solidity: './test/solidity', + }, + }, + test: { + solidity: { + ffi: true, + fsPermissions: { + readFile: ['./hardhat.config.ts', './hardhat.config.js', './artifacts/**/*.json'], + readDirectory: [ + './artifacts', + './artifacts/build-info', + './artifacts/contracts', + './artifacts/@openzeppelin', + './out', + ], + }, + }, + }, +}; + +export default config; diff --git a/packages/plugin-hardhat/package.json b/packages/plugin-hardhat/package.json index 3b75cde0e..1365c07f8 100644 --- a/packages/plugin-hardhat/package.json +++ b/packages/plugin-hardhat/package.json @@ -1,10 +1,19 @@ { "name": "@openzeppelin/hardhat-upgrades", - "version": "3.9.1", + "version": "4.0.0-alpha.0", "description": "", + "type": "module", "repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades", "license": "MIT", "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./package.json": "./package.json" + }, "files": [ "/dist", "/src" @@ -14,45 +23,49 @@ }, "scripts": { "clean": "rimraf dist *.tsbuildinfo", - "compile": "tsc -b", + "compile": "tsc -b && hardhat compile", "prepare": "yarn compile", "test": "tsc -b && bash scripts/test.sh", - "test:watch": "fgbg 'bash scripts/test.sh --watch' 'tsc -b --watch' --" + "test:watch": "fgbg 'bash scripts/test.sh --watch' 'tsc -b --watch' --", + "test:examples": "cd examples/BoxTransparent && npm install && npm test && cd ../BoxUUPS && npm install && npm test && cd ../BoxSolidityTests && npm install && npm test" }, "devDependencies": { - "@nomicfoundation/hardhat-ethers": "^3.0.6", - "@nomicfoundation/hardhat-verify": "^2.0.14", + "@nomicfoundation/hardhat-ethers": "^4.0.0", + "@nomicfoundation/hardhat-verify": "^3.0.10", "@openzeppelin/contracts": "5.3.0", "@openzeppelin/contracts-upgradeable": "5.3.0", + "@openzeppelin/foundry-upgrades": "0.4.1-alpha.0", "@types/mocha": "^7.0.2", "ava": "^6.0.0", + "dotenv": "^16.0.0", + "esmock": "^2.7.3", "fgbg": "^0.1.4", - "hardhat": "^2.24.1", + "forge-std": "github:foundry-rs/forge-std#v1.9.7", + "hardhat": "^3.1.5", "promisified": "^0.5.0", - "proxyquire": "^2.1.3", "rimraf": "^5.0.0", "sinon": "^20.0.0" }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^4.0.0", + "@nomicfoundation/hardhat-verify": "^3.0.10", + "ethers": "^6.14.0", + "hardhat": "^3.0.0" + }, + "peerDependenciesMeta": { + "@nomicfoundation/hardhat-verify": { + "optional": true + } + }, "dependencies": { "@openzeppelin/defender-sdk-base-client": "^2.1.0", "@openzeppelin/defender-sdk-deploy-client": "^2.1.0", "@openzeppelin/defender-sdk-network-client": "^2.1.0", - "@openzeppelin/upgrades-core": "^1.41.0", + "@openzeppelin/upgrades-core": "1.45.0-alpha.1", "chalk": "^4.1.0", "debug": "^4.1.1", "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 - } } } diff --git a/packages/plugin-hardhat/scripts/assert-upgrades-core-resolution.sh b/packages/plugin-hardhat/scripts/assert-upgrades-core-resolution.sh new file mode 100755 index 000000000..c499dde8e --- /dev/null +++ b/packages/plugin-hardhat/scripts/assert-upgrades-core-resolution.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Purpose: +# - Assert that the upgrades-core CLI used through Foundry Upgrades' npx command +# resolves to this repo's local workspace package, not an external/published one. +# +# Why this matters: +# - plugin-hardhat Solidity tests call into @openzeppelin/foundry-upgrades, which +# invokes `npx @openzeppelin/upgrades-core@ validate`. +# - If npx resolves to a published package instead of local workspace code, CI may +# pass while not actually testing unreleased local upgrades-core changes. +# +# What this script checks: +# 1) The UPGRADES_CORE version requested by Foundry Upgrades. +# 2) The workspace-resolved @openzeppelin/upgrades-core version/path. +# 3) The npx-resolved @openzeppelin/upgrades-core version/path for the requested version. +# 4) Assertion: npx path must equal workspace path. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PKG_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +ROOT_DIR="$(cd "$PKG_DIR/../.." && pwd)" + +VERSIONS_SOL="" +if [[ -f "$ROOT_DIR/node_modules/@openzeppelin/foundry-upgrades/src/internal/Versions.sol" ]]; then + VERSIONS_SOL="$ROOT_DIR/node_modules/@openzeppelin/foundry-upgrades/src/internal/Versions.sol" +elif [[ -f "$PKG_DIR/node_modules/@openzeppelin/foundry-upgrades/src/internal/Versions.sol" ]]; then + VERSIONS_SOL="$PKG_DIR/node_modules/@openzeppelin/foundry-upgrades/src/internal/Versions.sol" +else + echo "Could not find foundry-upgrades Versions.sol" >&2 + exit 1 +fi + +REQ_VERSION="$( + node -e "const fs=require('fs');const s=fs.readFileSync(process.argv[1],'utf8');const m=s.match(/UPGRADES_CORE\\s*=\\s*\"([^\"]+)\"/);if(!m){throw new Error('UPGRADES_CORE not found');}console.log(m[1]);" "$VERSIONS_SOL" +)" + +echo "Foundry requested upgrades-core version: $REQ_VERSION" +echo + +( + cd "$PKG_DIR" + node -e "console.log('Workspace upgrades-core:');console.log(' version:', require('@openzeppelin/upgrades-core/package.json').version);console.log(' path: ', require.resolve('@openzeppelin/upgrades-core/package.json'));" +) + +echo + +( + cd "$PKG_DIR" + NPX_RESOLUTION="$( + npx --yes --package "@openzeppelin/upgrades-core@$REQ_VERSION" node -e "const pkg=require('@openzeppelin/upgrades-core/package.json');const p=require.resolve('@openzeppelin/upgrades-core/package.json');console.log(JSON.stringify({version:pkg.version,path:p}));" + )" + + NPX_VERSION="$(node -e "const x=JSON.parse(process.argv[1]);console.log(x.version);" "$NPX_RESOLUTION")" + NPX_PATH="$(node -e "const x=JSON.parse(process.argv[1]);console.log(x.path);" "$NPX_RESOLUTION")" + WORKSPACE_PATH="$(node -e "console.log(require.resolve('@openzeppelin/upgrades-core/package.json'))")" + + echo "npx upgrades-core:" + echo " version: $NPX_VERSION" + echo " path: $NPX_PATH" + + if [[ "$NPX_PATH" != "$WORKSPACE_PATH" ]]; then + echo "Assertion failed: npx did not resolve to workspace @openzeppelin/upgrades-core." >&2 + echo "Expected: $WORKSPACE_PATH" >&2 + echo "Actual: $NPX_PATH" >&2 + exit 1 + fi +) diff --git a/packages/plugin-hardhat/scripts/test.sh b/packages/plugin-hardhat/scripts/test.sh index 73a008bee..51b326d9c 100644 --- a/packages/plugin-hardhat/scripts/test.sh +++ b/packages/plugin-hardhat/scripts/test.sh @@ -3,5 +3,45 @@ set -euo pipefail rimraf .openzeppelin + hardhat compile -ava "$@" + +# Separate .sol and .js test files, and collect other arguments (flags) +sol_tests=() +js_tests=() +other_args=() + +for arg in "$@"; do + if [[ "$arg" == *.sol ]]; then + sol_tests+=("$arg") + elif [[ "$arg" == *.js ]] || [[ "$arg" == *.ts ]]; then + js_tests+=("$arg") + else + # Collect flags and other arguments + other_args+=("$arg") + fi +done + +# Error if flags were provided but no test files +if [ $# -gt 0 ] && [ ${#sol_tests[@]} -eq 0 ] && [ ${#js_tests[@]} -eq 0 ]; then + echo "Error: Flags provided but no test files specified." >&2 + echo "Usage: $0 [test files...] [flags...]" >&2 + echo " Example: $0 test/beacon-happy-path.js --timeout=60s" >&2 + exit 1 +fi + +# Run Solidity tests if any +if [ ${#sol_tests[@]} -gt 0 ]; then + hardhat test solidity "${sol_tests[@]}" "${other_args[@]}" +fi + +# Run JavaScript tests if any +if [ ${#js_tests[@]} -gt 0 ]; then + ava "${js_tests[@]}" "${other_args[@]}" +fi + +# If no arguments, run all tests +if [ $# -eq 0 ]; then + ava + hardhat test solidity +fi \ No newline at end of file diff --git a/packages/plugin-hardhat/src/admin.ts b/packages/plugin-hardhat/src/admin.ts index 6b8abeb52..f94047d64 100644 --- a/packages/plugin-hardhat/src/admin.ts +++ b/packages/plugin-hardhat/src/admin.ts @@ -1,9 +1,12 @@ import chalk from 'chalk'; -import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; +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('✔') + ' '; @@ -25,7 +28,11 @@ export type TransferProxyAdminOwnershipFunction = ( ) => Promise; export type GetInstanceFunction = (signer?: Signer) => Promise; -export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment, defenderModule: boolean): ChangeAdminFunction { +export function makeChangeProxyAdmin( + hre: HardhatRuntimeEnvironment, + defenderModule: boolean, + connection: NetworkConnection, +): ChangeAdminFunction { return async function changeProxyAdmin( proxyAddress: string, newAdmin: string, @@ -34,9 +41,12 @@ export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment, defenderMod ) { disableDefender(hre, defenderModule, {}, changeProxyAdmin.name); - const proxyAdminAddress = await getAdminAddress(hre.network.provider, proxyAddress); + const { ethers } = connection; + 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); + const admin = await attachProxyAdminV4(connection, proxyAdminAddress, signer); const overrides = opts.txOverrides ? [opts.txOverrides] : []; await admin.changeProxyAdmin(proxyAddress, newAdmin, ...overrides); @@ -46,6 +56,7 @@ export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment, defenderMod export function makeTransferProxyAdminOwnership( hre: HardhatRuntimeEnvironment, defenderModule: boolean, + connection: NetworkConnection, ): TransferProxyAdminOwnershipFunction { return async function transferProxyAdminOwnership( proxyAddress: string, @@ -55,15 +66,17 @@ export function makeTransferProxyAdminOwnership( ) { disableDefender(hre, defenderModule, {}, transferProxyAdminOwnership.name); - const proxyAdminAddress = await getAdminAddress(hre.network.provider, proxyAddress); + const { ethers } = connection; + 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 admin = await attachProxyAdminV4(connection, proxyAdminAddress, signer); const overrides = opts.txOverrides ? [opts.txOverrides] : []; await admin.transferOwnership(newOwner, ...overrides); if (!opts.silent) { - const { provider } = hre.network; const manifest = await Manifest.forNetwork(provider); const { proxies } = await manifest.read(); const adminAddress = await admin.getAddress(); diff --git a/packages/plugin-hardhat/src/defender/client.ts b/packages/plugin-hardhat/src/defender/client.ts index d6feb1b07..8d3a92bed 100644 --- a/packages/plugin-hardhat/src/defender/client.ts +++ b/packages/plugin-hardhat/src/defender/client.ts @@ -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)); diff --git a/packages/plugin-hardhat/src/defender/deploy.ts b/packages/plugin-hardhat/src/defender/deploy.ts index fb1398000..509faeda1 100644 --- a/packages/plugin-hardhat/src/defender/deploy.ts +++ b/packages/plugin-hardhat/src/defender/deploy.ts @@ -1,5 +1,7 @@ import type { ethers, ContractFactory } from 'ethers'; -import { CompilerInput, CompilerOutputContract, HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; +import type { CompilerOutputContract } from 'hardhat/types/solidity'; import { parseFullyQualifiedName } from 'hardhat/utils/contract-names'; @@ -10,40 +12,44 @@ import { DeployRequestLibraries, } from '@openzeppelin/defender-sdk-deploy-client'; import { getContractNameAndRunValidation, UpgradesError } from '@openzeppelin/upgrades-core'; +import { createRequire } from 'node:module'; -import artifactsBuildInfo from '@openzeppelin/upgrades-core/artifacts/build-info-v5.json'; +const require = createRequire(import.meta.url); -import ERC1967Proxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'; -import BeaconProxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'; -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'; +const artifactsBuildInfo = require('@openzeppelin/upgrades-core/artifacts/build-info-v5.json'); +const ERC1967Proxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'); +const BeaconProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'); +const UpgradeableBeacon = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json'); +const TransparentUpgradeableProxy = require('@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; - }; +/** + * Removes the 'project/' prefix from a fully qualified name if present. + * In Hardhat 3, validation data uses 'project/' prefix for local contracts + * (e.g., "project/contracts/Greeter.sol:Greeter"), but the artifact system + * expects names without this prefix (e.g., "contracts/Greeter.sol:Greeter"). + */ +function removeProjectPrefix(fullyQualifiedName: string): string { + const prefix = 'project/'; + return fullyQualifiedName.startsWith(prefix) ? fullyQualifiedName.slice(prefix.length) : fullyQualifiedName; } interface ContractInfo { sourceName: string; contractName: string; - buildInfo: ReducedBuildInfo; + buildInfo: CombinedBuildInfo; libraries?: DeployRequestLibraries; constructorBytecode: string; + inputSourceName?: string; // Hardhat 3: source name with project/ prefix for buildInfo access } type CompilerOutputWithMetadata = CompilerOutputContract & { @@ -58,13 +64,16 @@ export async function defenderDeploy( opts: UpgradeOptions & EthersDeployOptions & DefenderDeployOptions, ...args: unknown[] ): Promise { + // Create connection if not available in context + const connection = await hre.network.connect(); + const client = getDeployClient(hre); // Override constructor arguments in options with the ones passed as arguments to this function. // The ones in the options are for implementation contracts only, while this function // can be used to deploy proxies as well. - const contractInfo = await getContractInfo(hre, factory, { ...opts, constructorArgs: args }); - const network = await getNetwork(hre); + const contractInfo = await getContractInfo(hre, factory, { ...opts, constructorArgs: args }, connection); + const network = await getNetwork(hre, connection); debug(`Network ${network}`); const verifySourceCode = opts.verifySourceCode ?? true; @@ -148,8 +157,10 @@ export async function defenderDeploy( } } - const txResponse = (await hre.ethers.provider.getTransaction(deploymentResponse.txHash)) ?? undefined; - const checksumAddress = hre.ethers.getAddress(deploymentResponse.address); + const { ethers } = connection; + + const txResponse = (await ethers.provider.getTransaction(deploymentResponse.txHash)) ?? undefined; + const checksumAddress = ethers.getAddress(deploymentResponse.address); return { address: checksumAddress, txHash: deploymentResponse.txHash, @@ -162,13 +173,14 @@ async function getContractInfo( hre: HardhatRuntimeEnvironment, factory: ethers.ContractFactory, opts: UpgradeOptions & Required>, + connection: NetworkConnection, ): Promise { let fullContractName, runValidation; let libraries: DeployRequestLibraries | undefined; let constructorBytecode: string; try { // Get fully qualified contract name and link references from validations - const deployData = await getDeployData(hre, factory, opts); + const deployData = await getDeployData(hre, factory, opts, connection); constructorBytecode = deployData.encodedArgs; [fullContractName, runValidation] = getContractNameAndRunValidation(deployData.validations, deployData.version); debug(`Contract ${fullContractName}`); @@ -210,9 +222,17 @@ async function getContractInfo( throw e; } - const { sourceName, contractName } = parseFullyQualifiedName(fullContractName); + // Remove 'project/' prefix for artifact resolution (Hardhat 3 compatibility) + const artifactName = removeProjectPrefix(fullContractName); + const { sourceName, contractName } = parseFullyQualifiedName(artifactName); + + // Read the artifact to get inputSourceName which is needed for buildInfo access + const artifact = await hre.artifacts.readArtifact(artifactName); + const inputSourceName = artifact.inputSourceName || sourceName; // fallback for Hardhat 2 + // 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, artifactName); + if (buildInfo === undefined) { throw new UpgradesError( `Could not get Hardhat compilation artifact for contract ${fullContractName}`, @@ -220,15 +240,17 @@ async function getContractInfo( ); } - return { sourceName, contractName, buildInfo, libraries, constructorBytecode }; + return { sourceName, contractName, buildInfo, libraries, constructorBytecode, inputSourceName }; } /** * Get the SPDX license identifier from the contract metadata without validating it. */ function getSpdxLicenseIdentifier(contractInfo: ContractInfo): string | undefined { + // Use inputSourceName for buildInfo access (Hardhat 3 has project/ prefix), fallback to sourceName + const buildInfoSourceName = contractInfo.inputSourceName || contractInfo.sourceName; const compilerOutput: CompilerOutputWithMetadata = - contractInfo.buildInfo.output.contracts[contractInfo.sourceName][contractInfo.contractName]; + contractInfo.buildInfo.output.contracts[buildInfoSourceName][contractInfo.contractName]; const metadataString = compilerOutput.metadata; if (metadataString === undefined) { @@ -240,7 +262,8 @@ function getSpdxLicenseIdentifier(contractInfo: ContractInfo): string | undefine const metadata = JSON.parse(metadataString); - return metadata.sources[contractInfo.sourceName].license; + // Metadata sources are also keyed by inputSourceName in Hardhat 3 + return metadata.sources[buildInfoSourceName].license; } /** diff --git a/packages/plugin-hardhat/src/defender/get-approval-process.ts b/packages/plugin-hardhat/src/defender/get-approval-process.ts index 70a08a5c9..643ab7719 100644 --- a/packages/plugin-hardhat/src/defender/get-approval-process.ts +++ b/packages/plugin-hardhat/src/defender/get-approval-process.ts @@ -1,7 +1,8 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; -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 { @@ -13,21 +14,31 @@ export interface ApprovalProcess { export type GetDeployApprovalProcessFunction = () => Promise; export type GetUpgradeApprovalProcessFunction = () => Promise; -export function makeGetDeployApprovalProcess(hre: HardhatRuntimeEnvironment): GetDeployApprovalProcessFunction { +export function makeGetDeployApprovalProcess( + hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, +): GetDeployApprovalProcessFunction { return async function getDeployApprovalProcess() { - return await getApprovalProcess(hre, 'deploy'); + return await getApprovalProcess(hre, 'deploy', connection); }; } -export function makeGetUpgradeApprovalProcess(hre: HardhatRuntimeEnvironment): GetUpgradeApprovalProcessFunction { +export function makeGetUpgradeApprovalProcess( + hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, +): GetUpgradeApprovalProcessFunction { return async function getUpgradeApprovalProcess() { - return await getApprovalProcess(hre, 'upgrade'); + return await getApprovalProcess(hre, 'upgrade', connection); }; } -async function getApprovalProcess(hre: HardhatRuntimeEnvironment, kind: 'deploy' | 'upgrade') { +async function getApprovalProcess( + hre: HardhatRuntimeEnvironment, + kind: 'deploy' | 'upgrade', + connection: NetworkConnection, +) { const client = getDeployClient(hre); - const network = await getNetwork(hre); + const network = await getNetwork(hre, connection); let response: ApprovalProcessResponse; switch (kind) { diff --git a/packages/plugin-hardhat/src/defender/propose-upgrade-with-approval.ts b/packages/plugin-hardhat/src/defender/propose-upgrade-with-approval.ts index df5475be6..e6233e533 100644 --- a/packages/plugin-hardhat/src/defender/propose-upgrade-with-approval.ts +++ b/packages/plugin-hardhat/src/defender/propose-upgrade-with-approval.ts @@ -1,4 +1,4 @@ -import '../type-extensions'; +import '../type-extensions.js'; import { getAdminAddress, getImplementationAddress, @@ -6,11 +6,14 @@ 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 type { NetworkConnection } from 'hardhat/types/network'; +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; @@ -31,36 +34,45 @@ export interface ProposalOptions extends UpgradeOptions, DefenderDeployOptions { export function makeProposeUpgradeWithApproval( hre: HardhatRuntimeEnvironment, defenderModule: boolean, + connection: NetworkConnection, ): ProposeUpgradeWithApprovalFunction { return async function proposeUpgradeWithApproval(proxyAddress, contractNameOrImplFactory, opts = {}) { opts = enableDefender(hre, defenderModule, opts); const client = getDeployClient(hre); - const network = await getNetwork(hre); + const network = await getNetwork(hre, connection); + const { ethers } = connection; + 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(); - const deployedImpl = await deployImplForUpgrade(hre, proxyAddress, implFactory, { - getTxResponse: true, - ...opts, - }); + const deployedImpl = await deployImplForUpgrade( + hre, + proxyAddress, + implFactory, + { + getTxResponse: true, + ...opts, + }, + connection, + ); const txResponse = deployedImpl.txResponse; const newImplementation = deployedImpl.impl; diff --git a/packages/plugin-hardhat/src/defender/utils.ts b/packages/plugin-hardhat/src/defender/utils.ts index 37d300220..4b0390ac0 100644 --- a/packages/plugin-hardhat/src/defender/utils.ts +++ b/packages/plugin-hardhat/src/defender/utils.ts @@ -1,4 +1,6 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; + import { getChainId, hasCode, @@ -11,13 +13,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 { @@ -31,9 +33,10 @@ export function getDefenderApiKey(hre: HardhatRuntimeEnvironment): HardhatDefend return cfg; } -export async function getNetwork(hre: HardhatRuntimeEnvironment): Promise { - const { provider } = hre.network; - const chainId = hre.network.config.chainId ?? (await getChainId(provider)); +export async function getNetwork(hre: HardhatRuntimeEnvironment, connection: NetworkConnection): Promise { + const { networkConfig, ethers } = connection; + const provider = ethers.provider; + const chainId = networkConfig.chainId ?? (await getChainId(provider)); const networkNames = await getNetworkNames(chainId, hre); @@ -156,6 +159,7 @@ export function disableDefender( export async function getRemoteDeployment( hre: HardhatRuntimeEnvironment, remoteDeploymentId: string, + _connection: NetworkConnection, ): Promise { const client = getDeployClient(hre); try { @@ -181,15 +185,17 @@ export async function waitForDeployment( ): Promise { const pollInterval = opts.pollingInterval ?? 5e3; let lastKnownTxHash: string | undefined; + const connection = await hre.network.connect(); + const { ethers } = connection; // 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; } - const response = await getRemoteDeployment(hre, remoteDeploymentId); + const response = await getRemoteDeployment(hre, remoteDeploymentId, connection); lastKnownTxHash = response?.txHash; const completed = await isDeploymentCompleted(address, remoteDeploymentId, response); if (completed) { diff --git a/packages/plugin-hardhat/src/deploy-beacon-proxy.ts b/packages/plugin-hardhat/src/deploy-beacon-proxy.ts index 9755adf5c..1a14fddc3 100644 --- a/packages/plugin-hardhat/src/deploy-beacon-proxy.ts +++ b/packages/plugin-hardhat/src/deploy-beacon-proxy.ts @@ -1,4 +1,5 @@ -import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; import { ContractFactory } from 'ethers'; import { @@ -21,10 +22,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 { ( @@ -43,6 +44,7 @@ export interface DeployBeaconProxyFunction { export function makeDeployBeaconProxy( hre: HardhatRuntimeEnvironment, defenderModule: boolean, + connection: NetworkConnection, ): DeployBeaconProxyFunction { return async function deployBeaconProxy( beacon: ContractAddressOrInstance, @@ -63,7 +65,8 @@ export function makeDeployBeaconProxy( opts = enableDefender(hre, defenderModule, opts); - const { provider } = hre.network; + const { ethers } = connection; + const provider = ethers.provider; const manifest = await Manifest.forNetwork(provider); if (opts.kind !== undefined && opts.kind !== 'beacon') { @@ -72,7 +75,9 @@ export function makeDeployBeaconProxy( opts.kind = 'beacon'; const beaconAddress = await getContractAddress(beacon); - if (!(await isBeacon(provider, beaconAddress))) { + + const isBeaconResult = await isBeacon(provider as any, beaconAddress); + if (!isBeaconResult) { throw new DeployBeaconProxyUnsupportedError(beaconAddress); } @@ -85,7 +90,8 @@ export function makeDeployBeaconProxy( ]); } - const BeaconProxyFactory = opts.proxyFactory || (await getBeaconProxyFactory(hre, getSigner(attachTo.runner))); + const BeaconProxyFactory = + opts.proxyFactory || (await getBeaconProxyFactory(connection, getSigner(attachTo.runner))); const proxyDeployment: Required & DeployTransaction & RemoteDeploymentId = Object.assign( { kind: opts.kind }, await (opts.deployFunction || deploy)(hre, opts, BeaconProxyFactory, beaconAddress, data), diff --git a/packages/plugin-hardhat/src/deploy-beacon.ts b/packages/plugin-hardhat/src/deploy-beacon.ts index be0b3270a..c555c063b 100644 --- a/packages/plugin-hardhat/src/deploy-beacon.ts +++ b/packages/plugin-hardhat/src/deploy-beacon.ts @@ -1,25 +1,36 @@ -import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; 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; } -export function makeDeployBeacon(hre: HardhatRuntimeEnvironment, defenderModule: boolean): DeployBeaconFunction { +export function makeDeployBeacon( + hre: HardhatRuntimeEnvironment, + defenderModule: boolean, + connection: NetworkConnection, +): DeployBeaconFunction { return async function deployBeacon(ImplFactory: ContractFactory, opts: DeployBeaconOptions = {}) { disableDefender(hre, defenderModule, opts, deployBeacon.name); - const { impl } = await deployBeaconImpl(hre, ImplFactory, opts); + const { impl } = await deployBeaconImpl(hre, ImplFactory, opts, undefined, connection); const signer = getSigner(ImplFactory.runner); - const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(hre, signer); + const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(connection, signer); const initialOwner = await getInitialOwner(opts, signer); diff --git a/packages/plugin-hardhat/src/deploy-contract.ts b/packages/plugin-hardhat/src/deploy-contract.ts index 93c3a746f..f321f8362 100644 --- a/packages/plugin-hardhat/src/deploy-contract.ts +++ b/packages/plugin-hardhat/src/deploy-contract.ts @@ -1,9 +1,11 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; + import type { ContractFactory } from 'ethers'; -import { deploy, DeployContractOptions, DeployTransaction, EthersOrDefenderDeployment } from './utils'; -import { DeployData, getDeployData } from './utils/deploy-impl'; -import { enableDefender } from './defender/utils'; +import { deploy, DeployContractOptions, DeployTransaction, EthersOrDefenderDeployment } from './utils/index.js'; +import { DeployData, getDeployData } from './utils/deploy-impl.js'; +import { enableDefender } from './defender/utils.js'; import { getContractNameAndRunValidation, inferProxyKind, @@ -12,8 +14,8 @@ import { Deployment, RemoteDeploymentId, } from '@openzeppelin/upgrades-core'; -import { getContractInstance } from './utils/contract-instance'; -import { ContractTypeOfFactory } from './type-extensions'; +import { getContractInstance } from './utils/contract-instance.js'; +import { ContractTypeOfFactory } from './type-extensions.js'; export interface DeployContractFunction { ( @@ -31,8 +33,9 @@ async function deployNonUpgradeableContract( hre: HardhatRuntimeEnvironment, Contract: ContractFactory, opts: DeployContractOptions, + connection: NetworkConnection, ): Promise & DeployTransaction & RemoteDeploymentId> { - const deployData = await getDeployData(hre, Contract, opts); + const deployData = await getDeployData(hre, Contract, opts, connection); if (!opts.unsafeAllowDeployContract) { assertNonUpgradeable(deployData); @@ -60,7 +63,11 @@ function assertNonUpgradeable(deployData: DeployData) { } } -export function makeDeployContract(hre: HardhatRuntimeEnvironment, defenderModule: boolean): DeployContractFunction { +export function makeDeployContract( + hre: HardhatRuntimeEnvironment, + defenderModule: boolean, + connection: NetworkConnection, +): DeployContractFunction { return async function deployContract( Contract: F, args: unknown[] | DeployContractOptions = [], @@ -84,7 +91,7 @@ export function makeDeployContract(hre: HardhatRuntimeEnvironment, defenderModul } opts.constructorArgs = args; - const deployment = await deployNonUpgradeableContract(hre, Contract, opts); + const deployment = await deployNonUpgradeableContract(hre, Contract, opts, connection); return getContractInstance(hre, Contract, opts, deployment); }; diff --git a/packages/plugin-hardhat/src/deploy-implementation.ts b/packages/plugin-hardhat/src/deploy-implementation.ts index e29adb0d9..ffc2dcee4 100644 --- a/packages/plugin-hardhat/src/deploy-implementation.ts +++ b/packages/plugin-hardhat/src/deploy-implementation.ts @@ -1,9 +1,10 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; import type { ContractFactory, ethers } from 'ethers'; -import { DeployImplementationOptions } from './utils'; -import { deployUpgradeableImpl } from './utils/deploy-impl'; -import { enableDefender } from './defender/utils'; +import { DeployImplementationOptions } from './utils/index.js'; +import { deployUpgradeableImpl } from './utils/deploy-impl.js'; +import { enableDefender } from './defender/utils.js'; export type DeployImplementationFunction = ( ImplFactory: ContractFactory, @@ -15,11 +16,12 @@ export type DeployImplementationResponse = string | ethers.TransactionResponse; export function makeDeployImplementation( hre: HardhatRuntimeEnvironment, defenderModule: boolean, + connection: NetworkConnection, ): DeployImplementationFunction { return async function deployImplementation(ImplFactory, opts: DeployImplementationOptions = {}) { opts = enableDefender(hre, defenderModule, opts); - const deployedImpl = await deployUpgradeableImpl(hre, ImplFactory, opts); + const deployedImpl = await deployUpgradeableImpl(hre, ImplFactory, opts, undefined, connection); if (opts.getTxResponse && deployedImpl.txResponse) { return deployedImpl.txResponse; diff --git a/packages/plugin-hardhat/src/deploy-proxy.ts b/packages/plugin-hardhat/src/deploy-proxy.ts index b912102c3..a9db96207 100644 --- a/packages/plugin-hardhat/src/deploy-proxy.ts +++ b/packages/plugin-hardhat/src/deploy-proxy.ts @@ -1,4 +1,5 @@ -import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; import type { ContractFactory } from 'ethers'; import { @@ -21,11 +22,11 @@ import { deployProxyImpl, getInitializerData, getSigner, -} from './utils'; -import { enableDefender } from './defender/utils'; -import { getContractInstance } from './utils/contract-instance'; -import { getInitialOwner } from './utils/initial-owner'; -import { ContractTypeOfFactory } from './type-extensions'; +} from './utils/index.js'; +import { enableDefender } from './defender/utils.js'; +import { getContractInstance } from './utils/contract-instance.js'; +import { getInitialOwner } from './utils/initial-owner.js'; +import { ContractTypeOfFactory } from './type-extensions.js'; export interface DeployFunction { ( @@ -36,7 +37,11 @@ export interface DeployFunction { (ImplFactory: F, opts?: DeployProxyOptions): Promise>; } -export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule: boolean): DeployFunction { +export function makeDeployProxy( + hre: HardhatRuntimeEnvironment, + defenderModule: boolean, + connection: NetworkConnection, +): DeployFunction { return async function deployProxy( ImplFactory: F, args: unknown[] | DeployProxyOptions = [], @@ -49,10 +54,12 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule: opts = enableDefender(hre, defenderModule, opts); - const { provider } = hre.network; + const { ethers } = connection; + const provider = ethers.provider; + const manifest = await Manifest.forNetwork(provider); - const { impl, kind } = await deployProxyImpl(hre, ImplFactory, opts); + const { impl, kind } = await deployProxyImpl(hre, ImplFactory, opts, undefined, connection); const contractInterface = ImplFactory.interface; const data = getInitializerData(contractInterface, args, opts.initializer); @@ -85,7 +92,7 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule: throw new InitialOwnerUnsupportedKindError(kind); } - const ProxyFactory = opts.proxyFactory || (await getProxyFactory(hre, signer)); + const ProxyFactory = opts.proxyFactory || (await getProxyFactory(connection, signer)); proxyDeployment = Object.assign({ kind }, await deployFn(hre, opts, ProxyFactory, impl, data)); break; } @@ -102,7 +109,7 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule: } const TransparentUpgradeableProxyFactory = - opts.proxyFactory || (await getTransparentUpgradeableProxyFactory(hre, signer)); + opts.proxyFactory || (await getTransparentUpgradeableProxyFactory(connection, signer)); proxyDeployment = Object.assign( { kind }, await deployFn(hre, opts, TransparentUpgradeableProxyFactory, impl, initialOwner, data), diff --git a/packages/plugin-hardhat/src/force-import.ts b/packages/plugin-hardhat/src/force-import.ts index 879fbf58a..5ccb07aea 100644 --- a/packages/plugin-hardhat/src/force-import.ts +++ b/packages/plugin-hardhat/src/force-import.ts @@ -1,4 +1,6 @@ -import type { EthereumProvider, HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; +import type { EthereumProvider } from 'hardhat/types/providers'; import type { ContractFactory, Contract } from 'ethers'; import { @@ -23,41 +25,42 @@ import { getContractAddress, getUpgradeableBeaconFactory, ForceImportOptions, -} from './utils'; -import { getDeployData } from './utils/deploy-impl'; -import { attach, getSigner } from './utils/ethers'; +} from './utils/index.js'; +import { getDeployData } from './utils/deploy-impl.js'; +import { attach, getSigner } from './utils/ethers.js'; export interface ForceImportFunction { (proxyAddress: string, ImplFactory: ContractFactory, opts?: ForceImportOptions): Promise; } -export function makeForceImport(hre: HardhatRuntimeEnvironment): ForceImportFunction { +export function makeForceImport(hre: HardhatRuntimeEnvironment, connection: NetworkConnection): ForceImportFunction { return async function forceImport( addressOrInstance: ContractAddressOrInstance, ImplFactory: ContractFactory, opts: ForceImportOptions = {}, ) { - const { provider } = hre.network; + const { ethers } = connection; + const provider = ethers.provider as unknown as EthereumProvider; const manifest = await Manifest.forNetwork(provider); const address = await getContractAddress(addressOrInstance); const implAddress = await getImplementationAddressFromProxy(provider, address); if (implAddress !== undefined) { - await importProxyToManifest(provider, hre, address, implAddress, ImplFactory, opts, manifest); + await importProxyToManifest(provider, hre, address, implAddress, ImplFactory, opts, manifest, connection); return attach(ImplFactory, address); } else if (await isBeacon(provider, address)) { const beaconImplAddress = await getImplementationAddressFromBeacon(provider, address); - await addImplToManifest(hre, beaconImplAddress, ImplFactory, opts); + await addImplToManifest(hre, beaconImplAddress, ImplFactory, opts, connection); - const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(hre, getSigner(ImplFactory.runner)); + const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(connection, getSigner(ImplFactory.runner)); return attach(UpgradeableBeaconFactory, address); } else { if (!(await hasCode(provider, address))) { throw new NoContractImportError(address); } - await addImplToManifest(hre, address, ImplFactory, opts); + await addImplToManifest(hre, address, ImplFactory, opts, connection); return attach(ImplFactory, address); } }; @@ -71,15 +74,16 @@ async function importProxyToManifest( ImplFactory: ContractFactory, opts: ForceImportOptions, manifest: Manifest, + connection: NetworkConnection, ) { - await addImplToManifest(hre, implAddress, ImplFactory, opts); + await addImplToManifest(hre, implAddress, ImplFactory, opts, connection); let importKind: ProxyDeployment['kind']; if (opts.kind === undefined) { if (await isBeaconProxy(provider, proxyAddress)) { importKind = 'beacon'; } else { - const deployData = await getDeployData(hre, ImplFactory, opts); + const deployData = await getDeployData(hre, ImplFactory, opts, connection); importKind = inferProxyKind(deployData.validations, deployData.version); } } else { @@ -98,8 +102,9 @@ async function addImplToManifest( implAddress: string, ImplFactory: ContractFactory, opts: ForceImportOptions, + connection: NetworkConnection, ) { - await simulateDeployImpl(hre, ImplFactory, opts, implAddress); + await simulateDeployImpl(hre, ImplFactory, opts, implAddress, connection); } async function assertNonEmptyAdminSlot(provider: EthereumProvider, proxyAddress: string) { diff --git a/packages/plugin-hardhat/src/hooks/config.ts b/packages/plugin-hardhat/src/hooks/config.ts new file mode 100644 index 000000000..db81bc3f1 --- /dev/null +++ b/packages/plugin-hardhat/src/hooks/config.ts @@ -0,0 +1,39 @@ +import type { ConfigHooks } from 'hardhat/types/hooks'; + +export default async (): Promise> => { + return { + async resolveUserConfig(userConfig, resolveConfigurationVariable, next) { + // First, let other plugins resolve the config + const config = await next(userConfig, resolveConfigurationVariable); + + // Iterate through all profiles + for (const profile of Object.values(config.solidity.profiles)) { + // Accumulate references to all the compiler settings, including overrides + const settings = []; + + for (const compiler of profile.compilers) { + compiler.settings ??= {}; + settings.push(compiler.settings); + } + + for (const compilerOverride of Object.values(profile.overrides)) { + compilerOverride.settings ??= {}; + settings.push(compilerOverride.settings); + } + + // Enable storage layout output for upgrade safety validations + for (const setting of settings) { + setting.outputSelection ??= {}; + setting.outputSelection['*'] ??= {}; + setting.outputSelection['*']['*'] ??= []; + + if (!setting.outputSelection['*']['*'].includes('storageLayout')) { + setting.outputSelection['*']['*'].push('storageLayout'); + } + } + } + + return config; + }, + }; +}; diff --git a/packages/plugin-hardhat/src/hooks/solidity.ts b/packages/plugin-hardhat/src/hooks/solidity.ts new file mode 100644 index 000000000..90b7df062 --- /dev/null +++ b/packages/plugin-hardhat/src/hooks/solidity.ts @@ -0,0 +1,337 @@ +import type { SolidityHooks, HookContext } from 'hardhat/types/hooks'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { SolcConfig } from 'hardhat/types/config'; +import type { CompilerInput, CompilerOutput, Compiler } from 'hardhat/types/solidity'; +import type { SolcInput, SolcOutput } from '@openzeppelin/upgrades-core'; + +// Helper to extract compile errors from output +function getNamespacedCompileErrors(output: CompilerOutput | undefined): string[] { + const errors: string[] = []; + if (output?.errors) { + for (const error of output.errors) { + if (error.severity === 'error') { + errors.push(error.formattedMessage ?? error.message); + } + } + } + return errors; +} + +let lockWarningShown = false; + +/** + * Converts Hardhat's CompilerInput to upgrades-core's SolcInput. + * The types are structurally compatible for the fields we use. + */ +function toSolcInput(input: CompilerInput): SolcInput { + return input as unknown as SolcInput; +} + +/** + * Converts Hardhat's CompilerOutput to upgrades-core's SolcOutput. + * The types are structurally compatible for the fields we use. + */ +function toSolcOutput(output: CompilerOutput): SolcOutput { + return output as unknown as SolcOutput; +} + +/** + * Converts upgrades-core's SolcInput to Hardhat's CompilerInput. + */ +function toCompilerInput(input: SolcInput): CompilerInput { + return input as unknown as CompilerInput; +} + +export default async (): Promise> => { + return { + async preprocessSolcInputBeforeBuilding(context, solcInput, next) { + const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import( + '../utils/validations.js' + ); + + try { + await readValidations(context as HardhatRuntimeEnvironment); + // Cache exists and is valid, continue normally + } catch (e: unknown) { + if (e instanceof ValidationsCacheOutdated) { + // Cache exists but is outdated + // TODO: when hardhat supports forcing recompilation, we should do it here + } else if (e instanceof ValidationsCacheNotFound) { + // Cache doesn't exist - that's fine, just proceed with compilation + } else if (typeof e === 'object' && e !== null && 'code' in e && e.code === 'ELOCKED') { + // Lock file is being held by another process - warn once and continue + if (!lockWarningShown) { + console.warn( + '\nWarning: Validations cache is locked by another process. Continuing without cache validation.', + ); + lockWarningShown = true; + } + } else { + throw e; + } + } + + return await next(context, solcInput); + }, + + /** + * Hook that intercepts solc invocation to run upgrade validations. + * This replaces the old workaround of reading build-info files in onCleanUpArtifacts. + */ + async invokeSolc( + context: HookContext, + compiler: Compiler, + solcInput: CompilerInput, + solcConfig: SolcConfig, + next: ( + nextContext: HookContext, + nextCompiler: Compiler, + nextSolcInput: CompilerInput, + nextSolcConfig: SolcConfig, + ) => Promise, + ): Promise { + const { validate, solcInputOutputDecoder, isNamespaceSupported, makeNamespacedInput, trySanitizeNatSpec } = + await import('@openzeppelin/upgrades-core'); + const { writeValidations } = await import('../utils/validations.js'); + const { isFullSolcOutput } = await import('../utils/is-full-solc-output.js'); + + // Run the original compilation + const output = await next(context, compiler, solcInput, solcConfig); + + // Convert to upgrades-core types for validation + const solcInputCore = toSolcInput(solcInput); + const solcOutputCore = toSolcOutput(output); + + // Only process full solc output (with contracts, sources, etc.) + if (!isFullSolcOutput(solcOutputCore)) { + return output; + } + + const solcVersion = compiler.version; + const decodeSrc = solcInputOutputDecoder(solcInputCore, solcOutputCore); + let namespacedOutput: SolcOutput | undefined = undefined; + + // Handle namespaced storage layouts + if (isNamespaceSupported(solcVersion)) { + try { + let namespacedInput = makeNamespacedInput(solcInputCore, solcOutputCore, solcVersion); + namespacedInput = await trySanitizeNatSpec(namespacedInput, solcVersion); + + // Run the namespaced compilation by calling next() with modified input + const namespacedResult = await next(context, compiler, toCompilerInput(namespacedInput), solcConfig); + + const namespacedCompileErrors = getNamespacedCompileErrors(namespacedResult); + + if (namespacedCompileErrors.length > 0) { + const msg = `Failed to compile modified contracts for namespaced storage layout validations:\n\n${namespacedCompileErrors.join('\n')}`; + const preamble = [ + 'Please report this at https://zpl.in/upgrades/report.', + 'If possible, include the source code for the contracts mentioned in the errors above.', + 'This step allows for advanced storage modifications such as tight variable packing when performing upgrades with namespaced storage layouts.', + ]; + + const namespacedErrorsSetting = ( + context.config as HardhatRuntimeEnvironment['config'] & { + namespacedCompileErrors?: 'error' | 'warn' | 'ignore'; + } + ).namespacedCompileErrors; + + switch (namespacedErrorsSetting) { + case undefined: + case 'error': { + const { UpgradesError } = await import('@openzeppelin/upgrades-core'); + const details = [ + ...preamble, + 'If you are not using namespaced storage, or if you do not anticipate making advanced modifications to namespaces during upgrades,', + "you can set namespacedCompileErrors: 'warn' or namespacedCompileErrors: 'ignore' in your hardhat config to convert this to a warning or to ignore this.", + ]; + throw new UpgradesError(msg, () => details.join('\n')); + } + case 'warn': { + const { logWarning } = await import('@openzeppelin/upgrades-core'); + const details = [ + ...preamble, + "you can set namespacedCompileErrors: 'ignore' in your hardhat config to ignore this.", + ]; + logWarning(msg, details); + break; + } + case 'ignore': + break; + } + namespacedOutput = undefined; + } else { + namespacedOutput = toSolcOutput(namespacedResult); + } + } catch (err: unknown) { + // If it's an UpgradesError, rethrow it + const { UpgradesError } = await import('@openzeppelin/upgrades-core'); + if (err instanceof UpgradesError) { + throw err; + } + // Otherwise, silently continue without namespaced output + namespacedOutput = undefined; + } + } + + // Generate and write validations + const validations = validate(solcOutputCore, decodeSrc, solcVersion, solcInputCore, namespacedOutput); + await writeValidations(context as HardhatRuntimeEnvironment, validations); + + return output; + }, + + /** + * Hook for cleanup - only handles AST injection for Foundry plugin compatibility. + * Validation logic has been moved to invokeSolc hook. + */ + async onCleanUpArtifacts(context, artifactPaths, next) { + await next(context, artifactPaths); + + const path = await import('path'); + + // Get the artifacts directory + const artifactsDir = context.config.paths.artifacts; + const buildInfoDir = path.join(artifactsDir, 'build-info'); + + try { + // Inject AST into artifact files for Hardhat 3 compatibility with Foundry plugin + // In Hardhat 3, AST is stored in build-info output files, not in artifacts + // The Foundry upgrades plugin expects AST in artifact files, so we inject it here + console.log('[OpenZeppelin Upgrades] Starting AST injection into artifacts...'); + await injectAstIntoArtifacts(artifactsDir, buildInfoDir); + } catch (error: unknown) { + if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') { + throw error; + } + } + }, + }; +}; + +/** + * Injects AST from build-info output files into artifact files for Hardhat 3 compatibility. + * This allows the Foundry upgrades plugin to find AST in artifact files as expected. + */ +export async function injectAstIntoArtifacts(artifactsDir: string, buildInfoDir: string): Promise { + const path = await import('path'); + const fs = await import('fs/promises'); + + // Recursively find all artifact JSON files + async function findArtifactFiles(dir: string): Promise { + const files: string[] = []; + try { + const entries = await fs.readdir(dir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await findArtifactFiles(fullPath))); + } else if (entry.isFile() && entry.name.endsWith('.json')) { + files.push(fullPath); + } + } + } catch (error: unknown) { + // Ignore errors (e.g., directory doesn't exist) + if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') { + throw error; + } + } + return files; + } + + const artifactFiles = await findArtifactFiles(path.join(artifactsDir, 'contracts')); + let processedCount = 0; + let skippedCount = 0; + let errorCount = 0; + + for (const artifactPath of artifactFiles) { + try { + const artifactContent = await fs.readFile(artifactPath, 'utf-8'); + const artifact = JSON.parse(artifactContent); + + // Only process Hardhat 3 artifacts that have buildInfoId and inputSourceName + // and don't already have AST + // Skip artifacts without buildInfoId (they may not be fully processed yet) + if (artifact._format === 'hh3-artifact-1' && artifact.buildInfoId && artifact.inputSourceName && !artifact.ast) { + // Read the corresponding build-info output file + const buildInfoOutputPath = path.join(buildInfoDir, `${artifact.buildInfoId}.output.json`); + + try { + const buildInfoOutputContent = await fs.readFile(buildInfoOutputPath, 'utf-8'); + const buildInfoOutput = JSON.parse(buildInfoOutputContent); + + // Extract AST from output.sources[inputSourceName].ast + const inputSourceName = artifact.inputSourceName; + const contractName = artifact.contractName; + + if (buildInfoOutput.output?.sources?.[inputSourceName]?.ast) { + const ast = buildInfoOutput.output.sources[inputSourceName].ast; + + // Inject AST into artifact + artifact.ast = ast; + + // Also inject metadata if it's missing (needed for Foundry plugin) + // Metadata is stored in output.contracts[inputSourceName][contractName].metadata + // Metadata is a JSON string, but we need to parse it to an object for the Foundry plugin + if (!artifact.metadata && buildInfoOutput.output?.contracts?.[inputSourceName]?.[contractName]?.metadata) { + const metadataString = buildInfoOutput.output.contracts[inputSourceName][contractName].metadata; + try { + // Parse the metadata JSON string to an object + artifact.metadata = JSON.parse(metadataString); + } catch { + // If parsing fails, store as string (fallback) + artifact.metadata = metadataString; + } + } + + // Write the updated artifact back + await fs.writeFile(artifactPath, JSON.stringify(artifact, null, 2), 'utf-8'); + processedCount += 1; + } else { + // AST not found in build-info - log for debugging + console.warn( + `Warning: AST not found in build-info for artifact ${artifactPath}\n` + + ` buildInfoId: ${artifact.buildInfoId}\n` + + ` inputSourceName: ${inputSourceName}\n` + + ` build-info output exists: ${!!buildInfoOutput.output}\n` + + ` sources exists: ${!!buildInfoOutput.output?.sources}\n` + + ` source key exists: ${!!buildInfoOutput.output?.sources?.[inputSourceName]}`, + ); + skippedCount += 1; + } + } catch (err: unknown) { + // If build-info output file doesn't exist or AST is missing, skip this artifact + const isEnoent = typeof err === 'object' && err !== null && 'code' in err && err.code === 'ENOENT'; + if (isEnoent) { + console.warn( + `Warning: Build-info output file not found for artifact ${artifactPath}\n` + + ` Expected: ${buildInfoOutputPath}\n` + + ` buildInfoId: ${artifact.buildInfoId}`, + ); + } else { + // Log other errors but don't fail the whole process + const message = err instanceof Error ? err.message : String(err); + console.warn(`Warning: Could not inject AST into artifact ${artifactPath}: ${message}`); + errorCount += 1; + } + skippedCount += 1; + } + } + } catch (err: unknown) { + // If artifact file is invalid JSON or can't be read, skip it + const isEnoent = typeof err === 'object' && err !== null && 'code' in err && err.code === 'ENOENT'; + if (!isEnoent) { + const message = err instanceof Error ? err.message : String(err); + console.warn(`Warning: Could not process artifact ${artifactPath}: ${message}`); + errorCount += 1; + } + } + } + + // Log summary if any artifacts were processed or had issues + if (processedCount > 0 || skippedCount > 0 || errorCount > 0) { + console.log( + `[OpenZeppelin Upgrades] AST injection: ${processedCount} processed, ${skippedCount} skipped, ${errorCount} errors`, + ); + } +} diff --git a/packages/plugin-hardhat/src/index.ts b/packages/plugin-hardhat/src/index.ts index 21bb4241f..461f55056 100644 --- a/packages/plugin-hardhat/src/index.ts +++ b/packages/plugin-hardhat/src/index.ts @@ -1,299 +1,33 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ +import './type-extensions.js'; +import type { HardhatPlugin } from 'hardhat/types/plugins'; -import '@nomicfoundation/hardhat-ethers'; -import './type-extensions'; -import { subtask, extendEnvironment, extendConfig } from 'hardhat/config'; -import { TASK_COMPILE_SOLIDITY, TASK_COMPILE_SOLIDITY_COMPILE } from 'hardhat/builtin-tasks/task-names'; -import { lazyObject } from 'hardhat/plugins'; -import { HardhatConfig, HardhatRuntimeEnvironment } from 'hardhat/types'; -import { assertUnreachable, type silenceWarnings, type SolcInput, type SolcOutput } from '@openzeppelin/upgrades-core'; -import type { DeployFunction } from './deploy-proxy'; -import type { PrepareUpgradeFunction } from './prepare-upgrade'; -import type { UpgradeFunction } from './upgrade-proxy'; -import type { DeployBeaconFunction } from './deploy-beacon'; -import type { DeployBeaconProxyFunction } from './deploy-beacon-proxy'; -import type { UpgradeBeaconFunction } from './upgrade-beacon'; -import type { ForceImportFunction } from './force-import'; -import type { ChangeAdminFunction, TransferProxyAdminOwnershipFunction } from './admin'; -import type { ValidateImplementationFunction } from './validate-implementation'; -import type { ValidateUpgradeFunction } from './validate-upgrade'; -import type { DeployImplementationFunction } from './deploy-implementation'; -import type { DeployContractFunction } from './deploy-contract'; -import type { ProposeUpgradeWithApprovalFunction } from './defender/propose-upgrade-with-approval'; -import type { - GetDeployApprovalProcessFunction, - GetUpgradeApprovalProcessFunction, -} from './defender/get-approval-process'; +const plugin: HardhatPlugin = { + id: '@openzeppelin/hardhat-upgrades', -export interface HardhatUpgrades { - deployProxy: DeployFunction; - upgradeProxy: UpgradeFunction; - validateImplementation: ValidateImplementationFunction; - validateUpgrade: ValidateUpgradeFunction; - deployImplementation: DeployImplementationFunction; - prepareUpgrade: PrepareUpgradeFunction; - deployBeacon: DeployBeaconFunction; - deployBeaconProxy: DeployBeaconProxyFunction; - upgradeBeacon: UpgradeBeaconFunction; - forceImport: ForceImportFunction; - silenceWarnings: typeof silenceWarnings; - admin: { - changeProxyAdmin: ChangeAdminFunction; - transferProxyAdminOwnership: TransferProxyAdminOwnershipFunction; - }; - erc1967: { - getAdminAddress: (proxyAdress: string) => Promise; - getImplementationAddress: (proxyAdress: string) => Promise; - getBeaconAddress: (proxyAdress: string) => Promise; - }; - beacon: { - getImplementationAddress: (beaconAddress: string) => Promise; - }; -} + hookHandlers: { + config: () => import('./hooks/config.js'), + solidity: () => import('./hooks/solidity.js'), + }, -export interface DefenderHardhatUpgrades extends HardhatUpgrades { - deployContract: DeployContractFunction; - proposeUpgradeWithApproval: ProposeUpgradeWithApprovalFunction; - getDeployApprovalProcess: GetDeployApprovalProcessFunction; - getUpgradeApprovalProcess: GetUpgradeApprovalProcessFunction; - /** - * @deprecated Use `getUpgradeApprovalProcess` instead. - */ - getDefaultApprovalProcess: GetUpgradeApprovalProcessFunction; -} + dependencies: () => [import('@nomicfoundation/hardhat-ethers').then(m => ({ default: m.default }))], -interface RunCompilerArgs { - input: SolcInput; - solcVersion: string; - quiet: boolean; -} - -subtask(TASK_COMPILE_SOLIDITY, async (args: { force: boolean }, hre, runSuper) => { - const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import( - './utils/validations.js' - ); - - try { - await readValidations(hre); - } catch (e) { - if (e instanceof ValidationsCacheOutdated || e instanceof ValidationsCacheNotFound) { - args = { ...args, force: true }; - } else { - throw e; - } - } - - return runSuper(args); -}); - -subtask(TASK_COMPILE_SOLIDITY_COMPILE, async (args: RunCompilerArgs, hre, runSuper) => { - const { isNamespaceSupported, validate, solcInputOutputDecoder, makeNamespacedInput, trySanitizeNatSpec } = - await import('@openzeppelin/upgrades-core'); - const { writeValidations } = await import('./utils/validations.js'); - - // TODO: patch input - const { output, solcBuild } = await runSuper(); - - const { isFullSolcOutput } = await import('./utils/is-full-solc-output.js'); - if (isFullSolcOutput(output)) { - const decodeSrc = solcInputOutputDecoder(args.input, output); - - let namespacedOutput = undefined; - if (isNamespaceSupported(args.solcVersion)) { - let namespacedInput = makeNamespacedInput(args.input, output, args.solcVersion); - namespacedInput = await trySanitizeNatSpec(namespacedInput, args.solcVersion); - namespacedOutput = (await runSuper({ ...args, quiet: true, input: namespacedInput })).output; - - const namespacedCompileErrors = getNamespacedCompileErrors(namespacedOutput); - if (namespacedCompileErrors.length > 0) { - // If there are compile errors in the namespaced output, show error or warning if needed, then only use the original output - - const msg = `Failed to compile modified contracts for namespaced storage layout validations:\n\n${namespacedCompileErrors.join('\n')}`; - const preamble = [ - 'Please report this at https://zpl.in/upgrades/report. If possible, include the source code for the contracts mentioned in the errors above.', - 'This step allows for advanced storage modifications such as tight varible packing when performing upgrades with namespaced storage layouts.', - ]; - - switch (hre.config.namespacedCompileErrors) { - case undefined: - case 'error': { - const { UpgradesError } = await import('@openzeppelin/upgrades-core'); - const details = [ - ...preamble, - 'If you are not using namespaced storage, or if you do not anticipate making advanced modifications to namespaces during upgrades,', - "you can set namespacedCompileErrors: 'warn' or namespacedCompileErrors: 'ignore' in your hardhat config to convert this to a warning or to ignore this.", - ]; - throw new UpgradesError(msg, () => details.join('\n')); - } - case 'warn': { - const { logWarning } = await import('@openzeppelin/upgrades-core'); - const details = [ - ...preamble, - 'If you are not using namespaced storage, or if you do not anticipate making advanced modifications to namespaces during upgrades,', - "you can set namespacedCompileErrors: 'ignore' in your hardhat config to ignore this.", - ]; - logWarning(msg, details); - break; - } - case 'ignore': - break; - default: - assertUnreachable(hre.config.namespacedCompileErrors); - } - - namespacedOutput = undefined; - } - } - - const validations = validate(output, decodeSrc, args.solcVersion, args.input, namespacedOutput); - await writeValidations(hre, validations); - } - - return { output, solcBuild }; -}); - -function getNamespacedCompileErrors(namespacedOutput: SolcOutput) { - const errors = []; - if (namespacedOutput.errors !== undefined) { - for (const error of namespacedOutput.errors) { - if (error.severity === 'error') { - errors.push(error.formattedMessage); - } - } - } - return errors; -} - -extendEnvironment(hre => { - hre.upgrades = lazyObject((): HardhatUpgrades => { - return makeUpgradesFunctions(hre); - }); - - warnOnHardhatDefender(); - - hre.defender = lazyObject((): DefenderHardhatUpgrades => { - return makeDefenderFunctions(hre); - }); -}); - -function warnOnHardhatDefender() { - if (tryRequire('@openzeppelin/hardhat-defender', true)) { - const { logWarning } = require('@openzeppelin/upgrades-core'); - logWarning('The @openzeppelin/hardhat-defender package is deprecated.', [ - 'Uninstall the @openzeppelin/hardhat-defender package.', - 'OpenZeppelin Defender integration is included as part of the Hardhat Upgrades plugin.', - ]); - } -} - -extendConfig((config: HardhatConfig) => { - // Accumulate references to all the compiler settings, including overrides - const settings = []; - for (const compiler of config.solidity.compilers) { - compiler.settings ??= {}; - settings.push(compiler.settings); - } - for (const compilerOverride of Object.values(config.solidity.overrides)) { - compilerOverride.settings ??= {}; - settings.push(compilerOverride.settings); - } - - // Enable storage layout in all of them - for (const setting of settings) { - setting.outputSelection ??= {}; - setting.outputSelection['*'] ??= {}; - setting.outputSelection['*']['*'] ??= []; - - if (!setting.outputSelection['*']['*'].includes('storageLayout')) { - setting.outputSelection['*']['*'].push('storageLayout'); - } - } -}); - -if (tryRequire('@nomicfoundation/hardhat-verify')) { - subtask('verify:etherscan').setAction(async (args, hre, runSuper) => { - const { verify } = await import('./verify-proxy.js'); - return await verify(args, hre, runSuper); - }); -} - -function makeFunctions(hre: HardhatRuntimeEnvironment, defender: boolean) { - const { - silenceWarnings, - getAdminAddress, - getImplementationAddress, - getBeaconAddress, - getImplementationAddressFromBeacon, - } = require('@openzeppelin/upgrades-core'); - const { makeDeployProxy } = require('./deploy-proxy'); - const { makeUpgradeProxy } = require('./upgrade-proxy'); - const { makeValidateImplementation } = require('./validate-implementation'); - const { makeValidateUpgrade } = require('./validate-upgrade'); - const { makeDeployImplementation } = require('./deploy-implementation'); - const { makePrepareUpgrade } = require('./prepare-upgrade'); - const { makeDeployBeacon } = require('./deploy-beacon'); - const { makeDeployBeaconProxy } = require('./deploy-beacon-proxy'); - const { makeUpgradeBeacon } = require('./upgrade-beacon'); - const { makeForceImport } = require('./force-import'); - const { makeChangeProxyAdmin, makeTransferProxyAdminOwnership } = require('./admin'); - - return { - silenceWarnings, - deployProxy: makeDeployProxy(hre, defender), - upgradeProxy: makeUpgradeProxy(hre, defender), // block on defender - validateImplementation: makeValidateImplementation(hre), - validateUpgrade: makeValidateUpgrade(hre), - deployImplementation: makeDeployImplementation(hre, defender), - prepareUpgrade: makePrepareUpgrade(hre, defender), - deployBeacon: makeDeployBeacon(hre, defender), // block on defender - deployBeaconProxy: makeDeployBeaconProxy(hre, defender), - upgradeBeacon: makeUpgradeBeacon(hre, defender), // block on defender - forceImport: makeForceImport(hre), - admin: { - changeProxyAdmin: makeChangeProxyAdmin(hre, defender), // block on defender - transferProxyAdminOwnership: makeTransferProxyAdminOwnership(hre, defender), // block on defender + conditionalDependencies: [ + { + condition: () => [import('@nomicfoundation/hardhat-verify').then(m => ({ default: m.default }))], + plugin: () => import('./verify-plugin.js'), }, - erc1967: { - getAdminAddress: (proxyAddress: string) => getAdminAddress(hre.network.provider, proxyAddress), - getImplementationAddress: (proxyAddress: string) => getImplementationAddress(hre.network.provider, proxyAddress), - getBeaconAddress: (proxyAddress: string) => getBeaconAddress(hre.network.provider, proxyAddress), - }, - beacon: { - getImplementationAddress: (beaconAddress: string) => - getImplementationAddressFromBeacon(hre.network.provider, beaconAddress), - }, - }; -} - -function makeUpgradesFunctions(hre: HardhatRuntimeEnvironment): HardhatUpgrades { - return makeFunctions(hre, false); -} + ], +}; -function makeDefenderFunctions(hre: HardhatRuntimeEnvironment): DefenderHardhatUpgrades { - const { makeDeployContract } = require('./deploy-contract'); - const { makeProposeUpgradeWithApproval } = require('./defender/propose-upgrade-with-approval'); - const { makeGetDeployApprovalProcess, makeGetUpgradeApprovalProcess } = require('./defender/get-approval-process'); +export default plugin; - const getUpgradeApprovalProcess = makeGetUpgradeApprovalProcess(hre); +// Public API - Factory functions +export { upgrades, defender } from './utils/factory.js'; - return { - ...makeFunctions(hre, true), - deployContract: makeDeployContract(hre, true), - proposeUpgradeWithApproval: makeProposeUpgradeWithApproval(hre, true), - getDeployApprovalProcess: makeGetDeployApprovalProcess(hre), - getUpgradeApprovalProcess: getUpgradeApprovalProcess, - getDefaultApprovalProcess: getUpgradeApprovalProcess, // deprecated, is an alias for getUpgradeApprovalProcess - }; -} +// Types +export type { HardhatUpgrades, DefenderHardhatUpgrades } from './types.js'; -function tryRequire(id: string, resolveOnly?: boolean) { - try { - resolveOnly ? require.resolve(id) : require(id); - return true; - } catch (e: any) { - // do nothing - } - return false; -} +// Utilities +export type { UpgradeOptions } from './utils/options.js'; -export { UpgradeOptions } from './utils/options'; +export { proxyFilesToBuild } from './utils/npmFilesToBuild.js'; diff --git a/packages/plugin-hardhat/src/prepare-upgrade.ts b/packages/plugin-hardhat/src/prepare-upgrade.ts index b1ef1fd97..8195fc8dc 100644 --- a/packages/plugin-hardhat/src/prepare-upgrade.ts +++ b/packages/plugin-hardhat/src/prepare-upgrade.ts @@ -1,4 +1,6 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; + import type { ContractFactory } from 'ethers'; import { @@ -7,7 +9,7 @@ import { deployProxyImpl, deployBeaconImpl, PrepareUpgradeOptions, -} from './utils'; +} from './utils/index.js'; import { getBeaconAddress, isBeaconProxy, @@ -15,9 +17,9 @@ import { isBeacon, PrepareUpgradeRequiresKindError, } from '@openzeppelin/upgrades-core'; -import { DeployImplementationResponse } from './deploy-implementation'; -import { enableDefender } from './defender/utils'; -import { deployUpgradeableImpl, DeployedImpl } from './utils/deploy-impl'; +import { DeployImplementationResponse } from './deploy-implementation.js'; +import { enableDefender } from './defender/utils.js'; +import { deployUpgradeableImpl, DeployedImpl } from './utils/deploy-impl.js'; export type PrepareUpgradeFunction = ( referenceAddressOrContract: ContractAddressOrInstance, @@ -25,11 +27,15 @@ export type PrepareUpgradeFunction = ( opts?: PrepareUpgradeOptions, ) => Promise; -export function makePrepareUpgrade(hre: HardhatRuntimeEnvironment, defenderModule: boolean): PrepareUpgradeFunction { +export function makePrepareUpgrade( + hre: HardhatRuntimeEnvironment, + defenderModule: boolean, + connection: NetworkConnection, +): PrepareUpgradeFunction { return async function prepareUpgrade(referenceAddressOrContract, ImplFactory, opts: PrepareUpgradeOptions = {}) { opts = enableDefender(hre, defenderModule, opts); - const deployedImpl = await deployImplForUpgrade(hre, referenceAddressOrContract, ImplFactory, opts); + const deployedImpl = await deployImplForUpgrade(hre, referenceAddressOrContract, ImplFactory, opts, connection); if (opts.getTxResponse && deployedImpl.txResponse) { return deployedImpl.txResponse; @@ -44,22 +50,25 @@ export async function deployImplForUpgrade( referenceAddressOrContract: ContractAddressOrInstance, ImplFactory: ContractFactory, opts: PrepareUpgradeOptions = {}, + connection: NetworkConnection, ): Promise { const referenceAddress = await getContractAddress(referenceAddressOrContract); - const { provider } = hre.network; + const { ethers } = connection; + const provider = ethers.provider; + let deployedImpl; if (await isTransparentOrUUPSProxy(provider, referenceAddress)) { - deployedImpl = await deployProxyImpl(hre, ImplFactory, opts, referenceAddress); + deployedImpl = await deployProxyImpl(hre, ImplFactory, opts, referenceAddress, connection); } else if (await isBeaconProxy(provider, referenceAddress)) { const beaconAddress = await getBeaconAddress(provider, referenceAddress); - deployedImpl = await deployBeaconImpl(hre, ImplFactory, opts, beaconAddress); + deployedImpl = await deployBeaconImpl(hre, ImplFactory, opts, beaconAddress, connection); } else if (await isBeacon(provider, referenceAddress)) { - deployedImpl = await deployBeaconImpl(hre, ImplFactory, opts, referenceAddress); + deployedImpl = await deployBeaconImpl(hre, ImplFactory, opts, referenceAddress, connection); } else { if (opts.kind === undefined) { throw new PrepareUpgradeRequiresKindError(); } - deployedImpl = await deployUpgradeableImpl(hre, ImplFactory, opts, referenceAddress); + deployedImpl = await deployUpgradeableImpl(hre, ImplFactory, opts, referenceAddress, connection); } return deployedImpl; } diff --git a/packages/plugin-hardhat/src/test-utils/mock-deploy.ts b/packages/plugin-hardhat/src/test-utils/mock-deploy.ts new file mode 100644 index 000000000..af742bf0e --- /dev/null +++ b/packages/plugin-hardhat/src/test-utils/mock-deploy.ts @@ -0,0 +1,35 @@ +import type { ContractFactory } from 'ethers'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; + +import type { EthersOrDefenderDeployment } from '../utils/deploy.js'; +import type { DefenderDeployOptions, EthersDeployOptions, UpgradeOptions } from '../utils/options.js'; + +export async function mockDeploy( + _hre: HardhatRuntimeEnvironment, + opts: UpgradeOptions & EthersDeployOptions & DefenderDeployOptions, + factory: ContractFactory, + ...args: unknown[] +): Promise { + const deployArgs = [...args]; + + if (opts.txOverrides !== undefined) { + deployArgs.push(opts.txOverrides); + } + + const contractInstance = await factory.deploy(...deployArgs); + const deployTransaction = contractInstance.deploymentTransaction(); + + if (deployTransaction === null) { + throw new Error('Broken invariant: deploymentTransaction is null'); + } + + const address = await contractInstance.getAddress(); + const txHash = deployTransaction.hash; + + return { + address, + txHash, + deployTransaction, + remoteDeploymentId: 'abc', + }; +} diff --git a/packages/plugin-hardhat/src/type-extensions.ts b/packages/plugin-hardhat/src/type-extensions.ts index 76b72b4a9..e3e8c6081 100644 --- a/packages/plugin-hardhat/src/type-extensions.ts +++ b/packages/plugin-hardhat/src/type-extensions.ts @@ -1,18 +1,10 @@ -import 'hardhat/types/runtime'; import 'hardhat/types/config'; - -import type { HardhatUpgrades, DefenderHardhatUpgrades } from '.'; -import { ContractFactory } from 'ethers'; +import 'hardhat/types/hre'; +import type { HardhatUpgrades, DefenderHardhatUpgrades } from './types.js'; +import type { ContractFactory } from 'ethers'; export type ContractTypeOfFactory = ReturnType & ReturnType; -declare module 'hardhat/types/runtime' { - export interface HardhatRuntimeEnvironment { - upgrades: HardhatUpgrades; - defender: DefenderHardhatUpgrades; - } -} - export interface HardhatDefenderConfig { apiKey: string; apiSecret: string; @@ -33,3 +25,16 @@ declare module 'hardhat/types/config' { namespacedCompileErrors?: NamespacedCompileErrorsRule; } } + +declare module 'hardhat/types/hre' { + export interface HardhatRuntimeEnvironment { + upgrades: HardhatUpgrades; + defender: DefenderHardhatUpgrades; + + // Internal caching properties (not part of public API) + _upgrades?: HardhatUpgrades; + _defender?: DefenderHardhatUpgrades; + } +} + +export {}; diff --git a/packages/plugin-hardhat/src/types.ts b/packages/plugin-hardhat/src/types.ts new file mode 100644 index 000000000..d7a20c1d4 --- /dev/null +++ b/packages/plugin-hardhat/src/types.ts @@ -0,0 +1,52 @@ +import type { silenceWarnings } from '@openzeppelin/upgrades-core'; +import type { DeployFunction } from './deploy-proxy.js'; +import type { PrepareUpgradeFunction } from './prepare-upgrade.js'; +import type { UpgradeFunction } from './upgrade-proxy.js'; +import type { DeployBeaconFunction } from './deploy-beacon.js'; +import type { DeployBeaconProxyFunction } from './deploy-beacon-proxy.js'; +import type { UpgradeBeaconFunction } from './upgrade-beacon.js'; +import type { ForceImportFunction } from './force-import.js'; +import type { ChangeAdminFunction, TransferProxyAdminOwnershipFunction } from './admin.js'; +import type { ValidateImplementationFunction } from './validate-implementation.js'; +import type { ValidateUpgradeFunction } from './validate-upgrade.js'; +import type { DeployImplementationFunction } from './deploy-implementation.js'; +import type { DeployContractFunction } from './deploy-contract.js'; +import type { ProposeUpgradeWithApprovalFunction } from './defender/propose-upgrade-with-approval.js'; +import type { + GetDeployApprovalProcessFunction, + GetUpgradeApprovalProcessFunction, +} from './defender/get-approval-process.js'; + +export interface HardhatUpgrades { + deployProxy: DeployFunction; + upgradeProxy: UpgradeFunction; + validateImplementation: ValidateImplementationFunction; + validateUpgrade: ValidateUpgradeFunction; + deployImplementation: DeployImplementationFunction; + prepareUpgrade: PrepareUpgradeFunction; + deployBeacon: DeployBeaconFunction; + deployBeaconProxy: DeployBeaconProxyFunction; + upgradeBeacon: UpgradeBeaconFunction; + forceImport: ForceImportFunction; + silenceWarnings: typeof silenceWarnings; + admin: { + changeProxyAdmin: ChangeAdminFunction; + transferProxyAdminOwnership: TransferProxyAdminOwnershipFunction; + }; + erc1967: { + getAdminAddress: (proxyAddress: string) => Promise; + getImplementationAddress: (proxyAddress: string) => Promise; + getBeaconAddress: (proxyAddress: string) => Promise; + }; + beacon: { + getImplementationAddress: (beaconAddress: string) => Promise; + }; +} + +export interface DefenderHardhatUpgrades extends HardhatUpgrades { + deployContract: DeployContractFunction; + proposeUpgradeWithApproval: ProposeUpgradeWithApprovalFunction; + getDeployApprovalProcess: GetDeployApprovalProcessFunction; + getUpgradeApprovalProcess: GetUpgradeApprovalProcessFunction; + getDefaultApprovalProcess: GetUpgradeApprovalProcessFunction; +} diff --git a/packages/plugin-hardhat/src/upgrade-beacon.ts b/packages/plugin-hardhat/src/upgrade-beacon.ts index 980d80e73..c3001d59c 100644 --- a/packages/plugin-hardhat/src/upgrade-beacon.ts +++ b/packages/plugin-hardhat/src/upgrade-beacon.ts @@ -1,4 +1,6 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; + import type { ContractFactory, Contract } from 'ethers'; import { @@ -9,8 +11,8 @@ import { UpgradeBeaconOptions, attach, getSigner, -} from './utils'; -import { disableDefender } from './defender/utils'; +} from './utils/index.js'; +import { disableDefender } from './defender/utils.js'; export type UpgradeBeaconFunction = ( beacon: ContractAddressOrInstance, @@ -18,14 +20,18 @@ export type UpgradeBeaconFunction = ( opts?: UpgradeBeaconOptions, ) => Promise; -export function makeUpgradeBeacon(hre: HardhatRuntimeEnvironment, defenderModule: boolean): UpgradeBeaconFunction { +export function makeUpgradeBeacon( + hre: HardhatRuntimeEnvironment, + defenderModule: boolean, + connection: NetworkConnection, +): UpgradeBeaconFunction { return async function upgradeBeacon(beacon, ImplFactory, opts: UpgradeBeaconOptions = {}) { disableDefender(hre, defenderModule, opts, upgradeBeacon.name); const beaconAddress = await getContractAddress(beacon); - const { impl: nextImpl } = await deployBeaconImpl(hre, ImplFactory, opts, beaconAddress); + const { impl: nextImpl } = await deployBeaconImpl(hre, ImplFactory, opts, beaconAddress, connection); - const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(hre, getSigner(ImplFactory.runner)); + const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(connection, getSigner(ImplFactory.runner)); const beaconContract = attach(UpgradeableBeaconFactory, beaconAddress); const overrides = opts.txOverrides ? [opts.txOverrides] : []; diff --git a/packages/plugin-hardhat/src/upgrade-proxy.ts b/packages/plugin-hardhat/src/upgrade-proxy.ts index 74e421e44..00bcbb032 100644 --- a/packages/plugin-hardhat/src/upgrade-proxy.ts +++ b/packages/plugin-hardhat/src/upgrade-proxy.ts @@ -1,6 +1,8 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; + import type { ethers, ContractFactory, Signer } from 'ethers'; -import debug from './utils/debug'; +import debug from './utils/debug.js'; import { getAdminAddress, getCode, getUpgradeInterfaceVersion, isEmptySlot } from '@openzeppelin/upgrades-core'; import { @@ -9,16 +11,16 @@ import { getContractAddress, ContractAddressOrInstance, getSigner, -} from './utils'; -import { disableDefender } from './defender/utils'; -import { attach } from './utils/ethers'; +} from './utils/index.js'; +import { disableDefender } from './defender/utils.js'; +import { attach } from './utils/ethers.js'; import { attachITransparentUpgradeableProxyV4, attachITransparentUpgradeableProxyV5, attachProxyAdminV4, attachProxyAdminV5, -} from './utils/attach-abi'; -import { ContractTypeOfFactory } from './type-extensions'; +} from './utils/attach-abi.js'; +import { ContractTypeOfFactory } from './type-extensions.js'; export type UpgradeFunction = ( proxy: ContractAddressOrInstance, @@ -29,6 +31,7 @@ export type UpgradeFunction = ( export function makeUpgradeProxy( hre: HardhatRuntimeEnvironment, defenderModule: boolean, + connection: NetworkConnection, log = debug, ): UpgradeFunction { return async function upgradeProxy( @@ -40,7 +43,7 @@ export function makeUpgradeProxy( const proxyAddress = await getContractAddress(proxy); - const { impl: nextImpl } = await deployProxyImpl(hre, ImplFactory, opts, proxyAddress); + const { impl: nextImpl } = await deployProxyImpl(hre, ImplFactory, opts, proxyAddress, connection); // upgrade kind is inferred above const upgradeTo = await getUpgrader(proxyAddress, opts, getSigner(ImplFactory.runner)); const call = encodeCall(ImplFactory, opts.call); @@ -55,7 +58,8 @@ export function makeUpgradeProxy( type Upgrader = (nextImpl: string, call?: string) => Promise; async function getUpgrader(proxyAddress: string, opts: UpgradeProxyOptions, signer?: Signer): Promise { - const { provider } = hre.network; + const { ethers } = connection; + const provider = ethers.provider; const adminAddress = await getAdminAddress(provider, proxyAddress); const adminBytecode = await getCode(provider, adminAddress); @@ -67,7 +71,7 @@ export function makeUpgradeProxy( const upgradeInterfaceVersion = await getUpgradeInterfaceVersion(provider, proxyAddress, log); switch (upgradeInterfaceVersion) { case '5.0.0': { - const proxy = await attachITransparentUpgradeableProxyV5(hre, proxyAddress, signer); + const proxy = await attachITransparentUpgradeableProxyV5(connection, proxyAddress, signer); return (nextImpl, call) => proxy.upgradeToAndCall(nextImpl, call ?? '0x', ...overrides); } default: { @@ -78,7 +82,7 @@ export function makeUpgradeProxy( `Unknown UPGRADE_INTERFACE_VERSION ${upgradeInterfaceVersion} for proxy at ${proxyAddress}. Expected 5.0.0`, ); } - const proxy = await attachITransparentUpgradeableProxyV4(hre, proxyAddress, signer); + const proxy = await attachITransparentUpgradeableProxyV4(connection, proxyAddress, signer); return (nextImpl, call) => call ? proxy.upgradeToAndCall(nextImpl, call, ...overrides) : proxy.upgradeTo(nextImpl, ...overrides); } @@ -88,7 +92,7 @@ export function makeUpgradeProxy( const upgradeInterfaceVersion = await getUpgradeInterfaceVersion(provider, adminAddress, log); switch (upgradeInterfaceVersion) { case '5.0.0': { - const admin = await attachProxyAdminV5(hre, adminAddress, signer); + const admin = await attachProxyAdminV5(connection, adminAddress, signer); return (nextImpl, call) => admin.upgradeAndCall(proxyAddress, nextImpl, call ?? '0x', ...overrides); } default: { @@ -99,7 +103,7 @@ export function makeUpgradeProxy( `Unknown UPGRADE_INTERFACE_VERSION ${upgradeInterfaceVersion} for proxy admin at ${adminAddress}. Expected 5.0.0`, ); } - const admin = await attachProxyAdminV4(hre, adminAddress, signer); + const admin = await attachProxyAdminV4(connection, adminAddress, signer); return (nextImpl, call) => call ? admin.upgradeAndCall(proxyAddress, nextImpl, call, ...overrides) diff --git a/packages/plugin-hardhat/src/utils/artifacts.ts b/packages/plugin-hardhat/src/utils/artifacts.ts new file mode 100644 index 000000000..66d734918 --- /dev/null +++ b/packages/plugin-hardhat/src/utils/artifacts.ts @@ -0,0 +1,130 @@ +import type { BuildInfo, ArtifactManager } from 'hardhat/types/artifacts'; +import type { SolidityBuildInfoOutput, CompilerInput } from 'hardhat/types/solidity'; +import { readJsonFile } from '@nomicfoundation/hardhat-utils/fs'; + +/** + * Combined build info with output, compatible with legacy format expectations. + * This matches the structure expected by code that was written for Hardhat 2. + * The output field contains at minimum the contracts property, but may have other + * properties depending on the compiler output format. + */ +export interface CombinedBuildInfo { + _format: string; + id: string; + solcVersion: string; + solcLongVersion: string; + input: CompilerInput; + output: { + contracts: { + [inputSourceName: string]: { + [contractName: string]: any; + }; + }; + [key: string]: any; + }; +} + +/** + * Gets the BuildInfo for a given contract name or fully qualified name. + * + * This utility uses the public API of Hardhat's ArtifactManager to retrieve + * the full BuildInfo object, which includes compilation details, compiler version, + * and all contract artifacts from a compilation unit. + * + * @param artifactManager - The ArtifactManager instance from hre.artifacts + * @param contractNameOrFullyQualifiedName - Contract name or fully qualified name (e.g., "MyContract" or "contracts/MyContract.sol:MyContract") + * @returns The BuildInfo object or undefined if not found + * + * @example + * ```typescript + * const buildInfo = await getBuildInfo(hre.artifacts, 'MyContract'); + * if (buildInfo) { + * console.log('Solidity version:', buildInfo.solcVersion); + * console.log('Input:', buildInfo.input); + * } + * ``` + */ +export async function getBuildInfo( + artifactManager: ArtifactManager, + contractNameOrFullyQualifiedName: string, +): Promise { + // Get the build info ID from the artifact + const buildInfoId = await artifactManager.getBuildInfoId(contractNameOrFullyQualifiedName); + + if (buildInfoId === undefined) { + return undefined; + } + + // Get the build info file path + const buildInfoPath = await artifactManager.getBuildInfoPath(buildInfoId); + + if (buildInfoPath === undefined) { + return undefined; + } + + // Read and return the build info + return readJsonFile(buildInfoPath); +} + +/** + * Gets the combined BuildInfo with output for a given contract name or fully qualified name. + * + * In Hardhat 3, build info and output are stored separately. This utility combines them + * into a single object for backwards compatibility with code expecting the Hardhat 2 format. + * + * @param artifactManager - The ArtifactManager instance from hre.artifacts + * @param contractNameOrFullyQualifiedName - Contract name or fully qualified name (e.g., "MyContract" or "contracts/MyContract.sol:MyContract") + * @returns The combined BuildInfo with output, or undefined if not found + * + * @example + * ```typescript + * const buildInfo = await getCombinedBuildInfo(hre.artifacts, 'MyContract'); + * if (buildInfo) { + * console.log('Solidity version:', buildInfo.solcVersion); + * console.log('Contracts:', Object.keys(buildInfo.output.contracts)); + * } + * ``` + */ +export async function getCombinedBuildInfo( + artifactManager: ArtifactManager, + contractNameOrFullyQualifiedName: string, +): Promise { + // Get the build info ID from the artifact + const buildInfoId = await artifactManager.getBuildInfoId(contractNameOrFullyQualifiedName); + + if (buildInfoId === undefined) { + return undefined; + } + + // Get the build info file path + const buildInfoPath = await artifactManager.getBuildInfoPath(buildInfoId); + + if (buildInfoPath === undefined) { + return undefined; + } + + // Get the build info output path + const buildInfoOutputPath = await artifactManager.getBuildInfoOutputPath(buildInfoId); + + if (buildInfoOutputPath === undefined) { + return undefined; + } + + // Read both files + const buildInfo: BuildInfo = await readJsonFile(buildInfoPath); + const buildInfoOutput: SolidityBuildInfoOutput = await readJsonFile(buildInfoOutputPath); + + if (!buildInfoOutput.output.contracts) { + return undefined; + } + + // Combine them into the legacy format + return { + _format: buildInfo._format, + id: buildInfo.id, + solcVersion: buildInfo.solcVersion, + solcLongVersion: buildInfo.solcLongVersion, + input: buildInfo.input, + output: buildInfoOutput.output as CombinedBuildInfo['output'], + }; +} diff --git a/packages/plugin-hardhat/src/utils/attach-abi.ts b/packages/plugin-hardhat/src/utils/attach-abi.ts index d4a686530..27cff7348 100644 --- a/packages/plugin-hardhat/src/utils/attach-abi.ts +++ b/packages/plugin-hardhat/src/utils/attach-abi.ts @@ -1,41 +1,46 @@ import { Contract, Signer } from 'ethers'; +import type { NetworkConnection } from 'hardhat/types/network'; +import { createRequire } from 'node:module'; -import ITransparentUpgradeableProxyV5 from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/ITransparentUpgradeableProxy.json'; -import ITransparentUpgradeableProxyV4 from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/ITransparentUpgradeableProxy.json'; +const require = createRequire(import.meta.url); -import ProxyAdminV5 from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'; -import ProxyAdminV4 from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'; - -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +const ITransparentUpgradeableProxyV5 = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/ITransparentUpgradeableProxy.json'); +const ITransparentUpgradeableProxyV4 = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/ITransparentUpgradeableProxy.json'); +const ProxyAdminV5 = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); +const ProxyAdminV4 = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); export async function attachITransparentUpgradeableProxyV5( - hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, address: string, signer?: Signer, ): Promise { - return hre.ethers.getContractAt(ITransparentUpgradeableProxyV5.abi, address, signer); + const { ethers } = connection; + return ethers.getContractAt(ITransparentUpgradeableProxyV5.abi, address, signer); } export async function attachITransparentUpgradeableProxyV4( - hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, address: string, signer?: Signer, ): Promise { - return hre.ethers.getContractAt(ITransparentUpgradeableProxyV4.abi, address, signer); + const { ethers } = connection; + return ethers.getContractAt(ITransparentUpgradeableProxyV4.abi, address, signer); } export async function attachProxyAdminV5( - hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, address: string, signer?: Signer, ): Promise { - return hre.ethers.getContractAt(ProxyAdminV5.abi, address, signer); + const { ethers } = connection; + return ethers.getContractAt(ProxyAdminV5.abi, address, signer); } export async function attachProxyAdminV4( - hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, address: string, signer?: Signer, ): Promise { - return hre.ethers.getContractAt(ProxyAdminV4.abi, address, signer); + const { ethers } = connection; + return ethers.getContractAt(ProxyAdminV4.abi, address, signer); } diff --git a/packages/plugin-hardhat/src/utils/contract-instance.ts b/packages/plugin-hardhat/src/utils/contract-instance.ts index cafb4cc9e..62ed90e18 100644 --- a/packages/plugin-hardhat/src/utils/contract-instance.ts +++ b/packages/plugin-hardhat/src/utils/contract-instance.ts @@ -1,12 +1,12 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import type { ContractFactory } from 'ethers'; import assert from 'assert'; -import { DeployTransaction, DefenderDeploy } from '.'; -import { waitForDeployment } from '../defender/utils'; +import { DeployTransaction, DefenderDeploy } from './index.js'; +import { waitForDeployment } from '../defender/utils.js'; import { Deployment, RemoteDeploymentId, DeployOpts } from '@openzeppelin/upgrades-core'; -import { attach } from './ethers'; -import { ContractTypeOfFactory } from '../type-extensions'; +import { attach } from './ethers.js'; +import { ContractTypeOfFactory } from '../type-extensions.js'; /** * Gets a contract instance from a deployment, where the deployment may be remote. @@ -42,8 +42,10 @@ export function getContractInstance( deployment.remoteDeploymentId, ); + const { ethers } = await hre.network.connect(); + if (updatedTxHash !== undefined && updatedTxHash !== deployment.txHash) { - const updatedTx = await hre.ethers.provider.getTransaction(updatedTxHash); + const updatedTx = await ethers.provider.getTransaction(updatedTxHash); // @ts-ignore Won't be readonly because instance was created through attach. instance.deploymentTransaction = () => updatedTx; } diff --git a/packages/plugin-hardhat/src/utils/deploy-impl.ts b/packages/plugin-hardhat/src/utils/deploy-impl.ts index 94e9d971b..5fa74a383 100644 --- a/packages/plugin-hardhat/src/utils/deploy-impl.ts +++ b/packages/plugin-hardhat/src/utils/deploy-impl.ts @@ -10,12 +10,14 @@ import { Version, } from '@openzeppelin/upgrades-core'; import type { ContractFactory, ethers } from 'ethers'; -import type { EthereumProvider, HardhatRuntimeEnvironment } from 'hardhat/types'; -import { deploy } from './deploy'; -import { GetTxResponse, DefenderDeployOptions, StandaloneOptions, UpgradeOptions, withDefaults } from './options'; -import { getRemoteDeployment } from '../defender/utils'; -import { validateBeaconImpl, validateProxyImpl, validateImpl } from './validate-impl'; -import { readValidations } from './validations'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; +import type { EthereumProvider } from 'hardhat/types/providers'; +import { deploy } from './deploy.js'; +import { GetTxResponse, DefenderDeployOptions, StandaloneOptions, UpgradeOptions, withDefaults } from './options.js'; +import { getRemoteDeployment } from '../defender/utils.js'; +import { validateBeaconImpl, validateProxyImpl, validateImpl } from './validate-impl.js'; +import { readValidations } from './validations.js'; export interface DeployedImpl { impl: string; @@ -40,9 +42,18 @@ export async function getDeployData( hre: HardhatRuntimeEnvironment, ImplFactory: ContractFactory, opts: UpgradeOptions, + connection: NetworkConnection, ): Promise { - const { provider } = hre.network; + const { ethers } = connection; + + // the type HardhatEthersProvider; has method send(method: string, params?: any[]): Promise; + // EthereumProvider have a bunch of send methods just like the one above, like this: + // send(method: 'anvil_metadata', params: []): Promise + // so we can make the cast safely + const provider = ethers.provider as unknown as EthereumProvider; + const validations = await readValidations(hre); + const unlinkedBytecode = getUnlinkedBytecode(validations, ImplFactory.bytecode); const encodedArgs = ImplFactory.interface.encodeDeploy(opts.constructorArgs); const version = getVersion(unlinkedBytecode, ImplFactory.bytecode, encodedArgs); @@ -56,10 +67,15 @@ export async function deployUpgradeableImpl( ImplFactory: ContractFactory, opts: StandaloneOptions, currentImplAddress?: string, + connection?: NetworkConnection, ): Promise { - const deployData = await getDeployData(hre, ImplFactory, opts); + // If connection not provided, create one (for backwards compatibility during migration) + if (!connection) { + connection = await hre.network.connect(); + } + const deployData = await getDeployData(hre, ImplFactory, opts, connection); await validateImpl(deployData, opts, currentImplAddress); - return await deployImpl(hre, deployData, ImplFactory, opts); + return await deployImpl(hre, deployData, ImplFactory, opts, connection); } export async function deployProxyImpl( @@ -67,14 +83,19 @@ export async function deployProxyImpl( ImplFactory: ContractFactory, opts: UpgradeOptions, proxyAddress?: string, + connection?: NetworkConnection, ): Promise { - const deployData = await getDeployData(hre, ImplFactory, opts); + // If connection not provided, create one (for backwards compatibility during migration) + if (!connection) { + connection = await hre.network.connect(); + } + const deployData = await getDeployData(hre, ImplFactory, opts, connection); await validateProxyImpl(deployData, opts, proxyAddress); if (opts.kind === undefined) { throw new Error('Broken invariant: Proxy kind is undefined'); } return { - ...(await deployImpl(hre, deployData, ImplFactory, opts)), + ...(await deployImpl(hre, deployData, ImplFactory, opts, connection)), kind: opts.kind, }; } @@ -84,10 +105,15 @@ export async function deployBeaconImpl( ImplFactory: ContractFactory, opts: UpgradeOptions, beaconAddress?: string, + connection?: NetworkConnection, ): Promise { - const deployData = await getDeployData(hre, ImplFactory, opts); + // If connection not provided, create one (for backwards compatibility during migration) + if (!connection) { + connection = await hre.network.connect(); + } + const deployData = await getDeployData(hre, ImplFactory, opts, connection); await validateBeaconImpl(deployData, opts, beaconAddress); - return await deployImpl(hre, deployData, ImplFactory, opts); + return await deployImpl(hre, deployData, ImplFactory, opts, connection); } async function deployImpl( @@ -95,6 +121,7 @@ async function deployImpl( deployData: DeployData, ImplFactory: ContractFactory, opts: UpgradeOptions & GetTxResponse & DefenderDeployOptions, + connection: NetworkConnection, ): Promise { const layout = deployData.layout; @@ -129,15 +156,18 @@ async function deployImpl( }, opts, merge, - remoteDeploymentId => getRemoteDeployment(hre, remoteDeploymentId), + remoteDeploymentId => getRemoteDeployment(hre, remoteDeploymentId, connection), ); + const { ethers } = connection; + const provider = ethers.provider; + let txResponse; if (opts.getTxResponse) { if ('deployTransaction' in deployment) { txResponse = deployment.deployTransaction ?? undefined; } else if (deployment.txHash !== undefined) { - txResponse = (await hre.ethers.provider.getTransaction(deployment.txHash)) ?? undefined; + txResponse = (await provider.getTransaction(deployment.txHash)) ?? undefined; } } diff --git a/packages/plugin-hardhat/src/utils/deploy.ts b/packages/plugin-hardhat/src/utils/deploy.ts index 954331726..253069607 100644 --- a/packages/plugin-hardhat/src/utils/deploy.ts +++ b/packages/plugin-hardhat/src/utils/deploy.ts @@ -1,8 +1,9 @@ import type { Deployment, RemoteDeploymentId } from '@openzeppelin/upgrades-core'; import type { ethers, ContractFactory, ContractMethodArgs } from 'ethers'; -import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { defenderDeploy } from '../defender/deploy'; -import { EthersDeployOptions, DefenderDeployOptions, UpgradeOptions } from './options'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; + +import { defenderDeploy } from '../defender/deploy.js'; +import { EthersDeployOptions, DefenderDeployOptions, UpgradeOptions } from './options.js'; export interface DeployTransaction { deployTransaction?: ethers.TransactionResponse; @@ -20,6 +21,7 @@ export async function deploy( ...args: unknown[] ): Promise { if (opts?.useDefenderDeploy) { + // For defenderDeploy, we'll handle connection internally for now return await defenderDeploy(hre, factory, opts, ...args); } else { if (opts.txOverrides !== undefined) { diff --git a/packages/plugin-hardhat/src/utils/etherscan-api.ts b/packages/plugin-hardhat/src/utils/etherscan-api.ts index ac1573058..1542c30a9 100644 --- a/packages/plugin-hardhat/src/utils/etherscan-api.ts +++ b/packages/plugin-hardhat/src/utils/etherscan-api.ts @@ -1,88 +1,59 @@ +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { + Etherscan, + EtherscanCustomApiCallOptions, + EtherscanVerifyArgs, + EtherscanResponseBody, +} from '@nomicfoundation/hardhat-verify/types'; import { UpgradesError } from '@openzeppelin/upgrades-core'; -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import debug from './debug.js'; -import { request } from 'undici'; +export type { Etherscan, EtherscanCustomApiCallOptions, EtherscanVerifyArgs, EtherscanResponseBody }; -import debug from './debug'; -import { Etherscan } from '@nomicfoundation/hardhat-verify/etherscan'; +export const RESPONSE_OK = '1'; /** - * Call the configured Etherscan API with the given parameters. - * - * @param etherscan Etherscan instance - * @param params The API parameters to call with - * @returns The Etherscan API response + * Gets the Etherscan instance from the network connection (hardhat-verify v3.0.10+). + * Requires @nomicfoundation/hardhat-verify to be loaded and the network to expose verification.etherscan. */ -export async function callEtherscanApi(etherscan: Etherscan, params: any): Promise { - const parameters = { ...params, apikey: etherscan.apiKey, chainid: etherscan.chainId }; - const response = await request(etherscan.apiUrl, { - method: 'POST', - query: parameters, - }); - - if (!(response.statusCode >= 200 && response.statusCode <= 299)) { - const responseBodyText = await response.body.text(); +export async function getEtherscanFromConnection(hre: HardhatRuntimeEnvironment): Promise { + const connection = await hre.network.connect(); + const verification = (connection as unknown as { verification?: { etherscan?: Etherscan } }).verification; + if (!verification?.etherscan) { throw new UpgradesError( - `Etherscan API call failed with status ${response.statusCode}, response: ${responseBodyText}`, + 'Etherscan verification is not available for this network.', + () => + 'Ensure @nomicfoundation/hardhat-verify v3.0.10+ is installed and that the network supports Etherscan (e.g. etherscan config in hardhat.config).', ); } - - const responseBodyJson = await response.body.json(); - debug('Etherscan response', JSON.stringify(responseBodyJson)); - - return responseBodyJson as EtherscanResponseBody; + return verification.etherscan; } /** - * Gets an Etherscan instance based on Hardhat config. - * Throws an error if Etherscan API key is not present in config. + * Call the Etherscan API via hardhat-verify's customApiCall (adds apikey and chainid as needed). */ -export async function getEtherscanInstance(hre: HardhatRuntimeEnvironment): Promise { - const etherscanConfig: EtherscanConfig | undefined = (hre.config as any).etherscan; // This should never be undefined, but check just in case - const chainConfig = await Etherscan.getCurrentChainConfig( - hre.network.name, - hre.network.provider, - etherscanConfig?.customChains ?? [], - ); - - return Etherscan.fromChainConfig(etherscanConfig?.apiKey, chainConfig); -} - -/** - * Etherscan configuration for hardhat-verify. - */ -interface EtherscanConfig { - apiKey: string | Record; - customChains: any[]; +export async function callEtherscanApi( + etherscan: Etherscan, + params: Record, + options?: EtherscanCustomApiCallOptions, +): Promise { + try { + const response = await etherscan.customApiCall(params, options); + debug('Etherscan response', JSON.stringify(response)); + return response; + } catch (e: unknown) { + const message = e instanceof Error ? e.message : String(e); + throw new UpgradesError(`Etherscan API call failed: ${message}`); + } } /** - * The response body from an Etherscan API call. + * Submit contract for verification and poll for status using the Etherscan instance. */ -interface EtherscanResponseBody { - status: string; - message: string; - result: unknown; -} - -export const RESPONSE_OK = '1'; - export async function verifyAndGetStatus( - params: { - contractAddress: string; - sourceCode: string; - contractName: string; - compilerVersion: string; - constructorArguments: string; - }, + params: EtherscanVerifyArgs, etherscan: Etherscan, -) { - const response = await etherscan.verify( - params.contractAddress, - params.sourceCode, - params.contractName, - params.compilerVersion, - params.constructorArguments, - ); - return etherscan.getVerificationStatus(response.message); +): Promise<{ success: boolean; message: string }> { + const guid = await etherscan.verify(params); + return etherscan.pollVerificationStatus(guid, params.contractAddress, params.contractName); } diff --git a/packages/plugin-hardhat/src/utils/factories.ts b/packages/plugin-hardhat/src/utils/factories.ts index ba1a07522..cfb179a41 100644 --- a/packages/plugin-hardhat/src/utils/factories.ts +++ b/packages/plugin-hardhat/src/utils/factories.ts @@ -1,30 +1,36 @@ import { ContractFactory, Signer } from 'ethers'; +import type { NetworkConnection } from 'hardhat/types/network'; +import { createRequire } from 'node:module'; -import ERC1967Proxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'; -import BeaconProxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'; -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'; +const require = createRequire(import.meta.url); -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +const ERC1967Proxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'); +const BeaconProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'); +const UpgradeableBeacon = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json'); +const TransparentUpgradeableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); -export async function getProxyFactory(hre: HardhatRuntimeEnvironment, signer?: Signer): Promise { - return hre.ethers.getContractFactory(ERC1967Proxy.abi, ERC1967Proxy.bytecode, signer); +export async function getProxyFactory(connection: NetworkConnection, signer?: Signer): Promise { + const { ethers } = connection; + return ethers.getContractFactory(ERC1967Proxy.abi, ERC1967Proxy.bytecode, signer); } export async function getTransparentUpgradeableProxyFactory( - hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, signer?: Signer, ): Promise { - return hre.ethers.getContractFactory(TransparentUpgradeableProxy.abi, TransparentUpgradeableProxy.bytecode, signer); + const { ethers } = connection; + return ethers.getContractFactory(TransparentUpgradeableProxy.abi, TransparentUpgradeableProxy.bytecode, signer); } -export async function getBeaconProxyFactory(hre: HardhatRuntimeEnvironment, signer?: Signer): Promise { - return hre.ethers.getContractFactory(BeaconProxy.abi, BeaconProxy.bytecode, signer); +export async function getBeaconProxyFactory(connection: NetworkConnection, signer?: Signer): Promise { + const { ethers } = connection; + return ethers.getContractFactory(BeaconProxy.abi, BeaconProxy.bytecode, signer); } export async function getUpgradeableBeaconFactory( - hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, signer?: Signer, ): Promise { - return hre.ethers.getContractFactory(UpgradeableBeacon.abi, UpgradeableBeacon.bytecode, signer); + const { ethers } = connection; + return ethers.getContractFactory(UpgradeableBeacon.abi, UpgradeableBeacon.bytecode, signer); } diff --git a/packages/plugin-hardhat/src/utils/factory.ts b/packages/plugin-hardhat/src/utils/factory.ts new file mode 100644 index 000000000..67391dafb --- /dev/null +++ b/packages/plugin-hardhat/src/utils/factory.ts @@ -0,0 +1,180 @@ +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; +import type { HardhatUpgrades, DefenderHardhatUpgrades } from '../types.js'; +import { + silenceWarnings, + getAdminAddress, + getImplementationAddress, + getBeaconAddress, + getImplementationAddressFromBeacon, +} from '@openzeppelin/upgrades-core'; + +/** + * Factory function to create the upgrades API for a given HRE. + * + * @example + * ```typescript + * import { upgrades } from '@openzeppelin/hardhat-upgrades'; + * + * task('deploy', async (args, hre) => { + * const connection = await hre.network.connect(); + * const api = await upgrades(hre, connection); + * await api.deployProxy(MyContract, []); + * }); + * ``` + * + * @param hre - Hardhat Runtime Environment + * @param connection - Optional network connection object from await hre.network.connect() + * @returns API object with all upgrade functions + */ +export async function upgrades( + hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, +): Promise { + await warnOnHardhatDefender(); + if (!connection) { + connection = await hre.network.connect(); + } + return await createUpgradesAPI(hre, false, connection); +} + +/** + * Factory function to create the Defender-enabled upgrades API. + * + * @example + * ```typescript + * import { defender } from '@openzeppelin/hardhat-upgrades'; + * + * task('deploy', async (args, hre) => { + * const connection = await hre.network.connect(); + * const api = await defender(hre, connection); + * await api.deployContract(...); + * }); + * ``` + * + * @param hre - Hardhat Runtime Environment + * @param connection - Optional network connection object from await hre.network.connect() + * @returns API object with all upgrade and Defender functions + */ +export async function defender( + hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, +): Promise { + await warnOnHardhatDefender(); + return await createDefenderAPI(hre, connection); +} + +async function createUpgradesAPI( + hre: HardhatRuntimeEnvironment, + isDefender: boolean, + connection: NetworkConnection, +): Promise { + // Dynamic imports for ES modules + const [ + { makeDeployProxy }, + { makeUpgradeProxy }, + { makeValidateImplementation }, + { makeValidateUpgrade }, + { makeDeployImplementation }, + { makePrepareUpgrade }, + { makeDeployBeacon }, + { makeDeployBeaconProxy }, + { makeUpgradeBeacon }, + { makeForceImport }, + { makeChangeProxyAdmin, makeTransferProxyAdminOwnership }, + ] = await Promise.all([ + import('../deploy-proxy.js'), + import('../upgrade-proxy.js'), + import('../validate-implementation.js'), + import('../validate-upgrade.js'), + import('../deploy-implementation.js'), + import('../prepare-upgrade.js'), + import('../deploy-beacon.js'), + import('../deploy-beacon-proxy.js'), + import('../upgrade-beacon.js'), + import('../force-import.js'), + import('../admin.js'), + ]); + + // Extract ethers from connection for use in erc1967 and beacon helpers + const { ethers } = connection; + + return { + silenceWarnings, + deployProxy: makeDeployProxy(hre, isDefender, connection), + upgradeProxy: makeUpgradeProxy(hre, isDefender, connection), + validateImplementation: makeValidateImplementation(hre, connection), + validateUpgrade: makeValidateUpgrade(hre, connection), + deployImplementation: makeDeployImplementation(hre, isDefender, connection), + prepareUpgrade: makePrepareUpgrade(hre, isDefender, connection), + deployBeacon: makeDeployBeacon(hre, isDefender, connection), + deployBeaconProxy: makeDeployBeaconProxy(hre, isDefender, connection), + upgradeBeacon: makeUpgradeBeacon(hre, isDefender, connection), + forceImport: makeForceImport(hre, connection), + admin: { + changeProxyAdmin: makeChangeProxyAdmin(hre, isDefender, connection), + transferProxyAdminOwnership: makeTransferProxyAdminOwnership(hre, isDefender, connection), + }, + erc1967: { + getAdminAddress: async (proxyAddress: string) => { + return getAdminAddress(ethers.provider, proxyAddress); + }, + getImplementationAddress: async (proxyAddress: string) => { + return getImplementationAddress(ethers.provider, proxyAddress); + }, + getBeaconAddress: async (proxyAddress: string) => { + return getBeaconAddress(ethers.provider, proxyAddress); + }, + }, + beacon: { + getImplementationAddress: async (beaconAddress: string) => { + return getImplementationAddressFromBeacon(ethers.provider, beaconAddress); + }, + }, + }; +} + +async function createDefenderAPI( + hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, +): Promise { + // Get base upgrades API with defender flag + const upgradesAPI = await createUpgradesAPI(hre, true, connection); + + // Dynamic imports for Defender-specific functions + const [ + { makeDeployContract }, + { makeProposeUpgradeWithApproval }, + { makeGetDeployApprovalProcess, makeGetUpgradeApprovalProcess }, + ] = await Promise.all([ + import('../deploy-contract.js'), + import('../defender/propose-upgrade-with-approval.js'), + import('../defender/get-approval-process.js'), + ]); + + const getUpgradeApprovalProcess = makeGetUpgradeApprovalProcess(hre, connection); + + return { + ...upgradesAPI, + deployContract: makeDeployContract(hre, true, connection), + proposeUpgradeWithApproval: makeProposeUpgradeWithApproval(hre, true, connection), + getDeployApprovalProcess: makeGetDeployApprovalProcess(hre, connection), + getUpgradeApprovalProcess: getUpgradeApprovalProcess, + getDefaultApprovalProcess: getUpgradeApprovalProcess, // deprecated alias + }; +} + +async function warnOnHardhatDefender(): Promise { + try { + // Try to import the deprecated package to check if it's installed + // @ts-expect-error - Package may not be installed, which is the expected case + await import('@openzeppelin/hardhat-defender'); + const { logWarning } = await import('@openzeppelin/upgrades-core'); + logWarning('The @openzeppelin/hardhat-defender package is deprecated.', [ + 'Uninstall the @openzeppelin/hardhat-defender package.', + 'OpenZeppelin Defender integration is included as part of the Hardhat Upgrades plugin.', + ]); + } catch (e: any) { + // Package not installed, no warning needed + } +} diff --git a/packages/plugin-hardhat/src/utils/index.ts b/packages/plugin-hardhat/src/utils/index.ts index ff38ae3d1..bcfa0b265 100644 --- a/packages/plugin-hardhat/src/utils/index.ts +++ b/packages/plugin-hardhat/src/utils/index.ts @@ -1,16 +1,18 @@ -export * from './deploy'; -export { deployProxyImpl, deployBeaconImpl } from './deploy-impl'; -export { simulateDeployImpl } from './simulate-deploy'; -export * from './factories'; -export * from './is-full-solc-output'; -export * from './validations'; -export * from './contract-types'; -export * from './options'; -export * from './initializer-data'; -export { attach, getSigner } from './ethers'; +export * from './deploy.js'; +export { deployProxyImpl, deployBeaconImpl } from './deploy-impl.js'; +export { simulateDeployImpl } from './simulate-deploy.js'; +export * from './factories.js'; +export * from './is-full-solc-output.js'; +export * from './validations.js'; +export * from './contract-types.js'; +export * from './options.js'; +export type { UpgradeOptions } from './options.js'; // ✓ Correct +export * from './initializer-data.js'; +export { attach, getSigner } from './ethers.js'; export { attachITransparentUpgradeableProxyV4, attachITransparentUpgradeableProxyV5, attachProxyAdminV4, attachProxyAdminV5, -} from './attach-abi'; +} from './attach-abi.js'; +export * from './artifacts.js'; diff --git a/packages/plugin-hardhat/src/utils/initial-owner.ts b/packages/plugin-hardhat/src/utils/initial-owner.ts index 3b55de72b..dcebdc397 100644 --- a/packages/plugin-hardhat/src/utils/initial-owner.ts +++ b/packages/plugin-hardhat/src/utils/initial-owner.ts @@ -1,5 +1,5 @@ import { Signer } from 'ethers'; -import { InitialOwner } from './options'; +import { InitialOwner } from './options.js'; import { UpgradesError } from '@openzeppelin/upgrades-core'; export async function getInitialOwner(opts: InitialOwner, signer?: Signer) { diff --git a/packages/plugin-hardhat/src/utils/npmFilesToBuild.ts b/packages/plugin-hardhat/src/utils/npmFilesToBuild.ts new file mode 100644 index 000000000..4d1f234dd --- /dev/null +++ b/packages/plugin-hardhat/src/utils/npmFilesToBuild.ts @@ -0,0 +1,20 @@ +/** + * Returns an array of OpenZeppelin contract file paths that are used in tests. + * + * These contracts are essential proxy and beacon implementations used for testing + * upgrade patterns. If you want to test with these contracts in your Solidity code, + * you must add them to the `npmFilesToBuild` configuration in your `hardhat.config.ts`. + * + * @see {@link https://hardhat.org/docs/cookbook/npm-artifacts} for more information on npm artifacts configuration + * + * @returns {string[]} An array of file paths to OpenZeppelin proxy and beacon contracts + */ +export function proxyFilesToBuild(): string[] { + return [ + '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol', + '@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol', + '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol', + '@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol', + '@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol', + ]; +} diff --git a/packages/plugin-hardhat/src/utils/options.ts b/packages/plugin-hardhat/src/utils/options.ts index 3ee7954a1..9fb639636 100644 --- a/packages/plugin-hardhat/src/utils/options.ts +++ b/packages/plugin-hardhat/src/utils/options.ts @@ -7,7 +7,7 @@ import { withValidationDefaults, } from '@openzeppelin/upgrades-core'; import { ContractFactory, Overrides } from 'ethers'; -import { EthersOrDefenderDeployment } from './deploy'; +import { EthersOrDefenderDeployment } from './deploy.js'; /** * Options for customizing the factory or deploy functions diff --git a/packages/plugin-hardhat/src/utils/simulate-deploy.ts b/packages/plugin-hardhat/src/utils/simulate-deploy.ts index b8e144c60..5927a57cb 100644 --- a/packages/plugin-hardhat/src/utils/simulate-deploy.ts +++ b/packages/plugin-hardhat/src/utils/simulate-deploy.ts @@ -1,8 +1,9 @@ import { fetchOrDeploy } from '@openzeppelin/upgrades-core'; import type { ContractFactory } from 'ethers'; -import type { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { getDeployData } from './deploy-impl'; -import { UpgradeOptions } from './options'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; +import { getDeployData } from './deploy-impl.js'; +import { UpgradeOptions } from './options.js'; // To import an already deployed contract we want to reuse fetchOrDeploy for its ability to validate // a deployment and record it in the network file. We are able to do this by "simulating" a deployment: @@ -14,8 +15,9 @@ export async function simulateDeployImpl( ImplFactory: ContractFactory, opts: UpgradeOptions, implAddress: string, + connection: NetworkConnection, ) { - const { deployData, simulateDeploy } = await getSimulatedData(hre, ImplFactory, opts, implAddress); + const { deployData, simulateDeploy } = await getSimulatedData(hre, ImplFactory, opts, implAddress, connection); await fetchOrDeploy(deployData.version, deployData.provider, simulateDeploy, opts, true); } @@ -27,8 +29,9 @@ async function getSimulatedData( ImplFactory: ContractFactory, opts: UpgradeOptions, implAddress: string, + connection: NetworkConnection, ) { - const deployData = await getDeployData(hre, ImplFactory, opts); + const deployData = await getDeployData(hre, ImplFactory, opts, connection); const simulateDeploy = async () => { return { abi: ImplFactory.interface.format(true), diff --git a/packages/plugin-hardhat/src/utils/validate-impl.ts b/packages/plugin-hardhat/src/utils/validate-impl.ts index b033346d2..34d221bc4 100644 --- a/packages/plugin-hardhat/src/utils/validate-impl.ts +++ b/packages/plugin-hardhat/src/utils/validate-impl.ts @@ -3,13 +3,13 @@ import { assertStorageUpgradeSafe, assertUpgradeSafe, getImplementationAddress, - getImplementationAddressFromBeacon, + getImplementationAddressFromBeacon, // substitute this ( not working ) getStorageLayoutForAddress, Manifest, processProxyKind, ValidationOptions, } from '@openzeppelin/upgrades-core'; -import { DeployData } from './deploy-impl'; +import { DeployData } from './deploy-impl.js'; /** * Processes the proxy kind and returns the implementation address if proxyAddress is provided. diff --git a/packages/plugin-hardhat/src/utils/validations.ts b/packages/plugin-hardhat/src/utils/validations.ts index 8367d8a85..410e6feb8 100644 --- a/packages/plugin-hardhat/src/utils/validations.ts +++ b/packages/plugin-hardhat/src/utils/validations.ts @@ -2,7 +2,7 @@ import { promises as fs } from 'fs'; import path from 'path'; import lockfile from 'proper-lockfile'; -import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import { ValidationDataCurrent, ValidationRunData, diff --git a/packages/plugin-hardhat/src/validate-implementation.ts b/packages/plugin-hardhat/src/validate-implementation.ts index 8cb294686..06f0e082a 100644 --- a/packages/plugin-hardhat/src/validate-implementation.ts +++ b/packages/plugin-hardhat/src/validate-implementation.ts @@ -1,18 +1,23 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; + import type { ContractFactory } from 'ethers'; -import { validateImpl } from './utils/validate-impl'; -import { getDeployData } from './utils/deploy-impl'; -import { ValidateImplementationOptions } from './utils'; +import { validateImpl } from './utils/validate-impl.js'; +import { getDeployData } from './utils/deploy-impl.js'; +import { ValidateImplementationOptions } from './utils/index.js'; export type ValidateImplementationFunction = ( ImplFactory: ContractFactory, opts?: ValidateImplementationOptions, ) => Promise; -export function makeValidateImplementation(hre: HardhatRuntimeEnvironment): ValidateImplementationFunction { +export function makeValidateImplementation( + hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, +): ValidateImplementationFunction { return async function validateImplementation(ImplFactory, opts: ValidateImplementationOptions = {}) { - const deployData = await getDeployData(hre, ImplFactory, opts); + const deployData = await getDeployData(hre, ImplFactory, opts, connection); await validateImpl(deployData, opts); }; } diff --git a/packages/plugin-hardhat/src/validate-upgrade.ts b/packages/plugin-hardhat/src/validate-upgrade.ts index f407c8787..c5d4997e1 100644 --- a/packages/plugin-hardhat/src/validate-upgrade.ts +++ b/packages/plugin-hardhat/src/validate-upgrade.ts @@ -1,7 +1,9 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { NetworkConnection } from 'hardhat/types/network'; + import { ContractFactory } from 'ethers'; -import { ContractAddressOrInstance, getContractAddress } from './utils'; +import { ContractAddressOrInstance, getContractAddress } from './utils/index.js'; import { getBeaconAddress, isBeaconProxy, @@ -13,8 +15,8 @@ import { inferProxyKind, ValidateUpdateRequiresKindError, } from '@openzeppelin/upgrades-core'; -import { validateBeaconImpl, validateImpl, validateProxyImpl } from './utils/validate-impl'; -import { getDeployData } from './utils/deploy-impl'; +import { validateBeaconImpl, validateImpl, validateProxyImpl } from './utils/validate-impl.js'; +import { getDeployData } from './utils/deploy-impl.js'; export interface ValidateUpgradeFunction { (origImplFactory: ContractFactory, newImplFactory: ContractFactory, opts?: ValidationOptions): Promise; @@ -25,19 +27,22 @@ export interface ValidateUpgradeFunction { ): Promise; } -export function makeValidateUpgrade(hre: HardhatRuntimeEnvironment): ValidateUpgradeFunction { +export function makeValidateUpgrade( + hre: HardhatRuntimeEnvironment, + connection: NetworkConnection, +): ValidateUpgradeFunction { return async function validateUpgrade( referenceAddressOrImplFactory: ContractAddressOrInstance | ContractFactory, newImplFactory: ContractFactory, opts: ValidationOptions = {}, ) { if (referenceAddressOrImplFactory instanceof ContractFactory) { - const origDeployData = await getDeployData(hre, referenceAddressOrImplFactory, opts); + const origDeployData = await getDeployData(hre, referenceAddressOrImplFactory, opts, connection); if (opts.kind === undefined) { opts.kind = inferProxyKind(origDeployData.validations, origDeployData.version); } - const newDeployData = await getDeployData(hre, newImplFactory, opts); + const newDeployData = await getDeployData(hre, newImplFactory, opts, connection); assertUpgradeSafe(newDeployData.validations, newDeployData.version, newDeployData.fullOpts); if (opts.unsafeSkipStorageCheck !== true) { @@ -45,8 +50,9 @@ export function makeValidateUpgrade(hre: HardhatRuntimeEnvironment): ValidateUpg } } else { const referenceAddress = await getContractAddress(referenceAddressOrImplFactory); - const { provider } = hre.network; - const deployData = await getDeployData(hre, newImplFactory, opts); + const { ethers } = connection; + const provider = ethers.provider; + const deployData = await getDeployData(hre, newImplFactory, opts, connection); if (await isTransparentOrUUPSProxy(provider, referenceAddress)) { await validateProxyImpl(deployData, opts, referenceAddress); } else if (await isBeaconProxy(provider, referenceAddress)) { diff --git a/packages/plugin-hardhat/src/verify-plugin.ts b/packages/plugin-hardhat/src/verify-plugin.ts new file mode 100644 index 000000000..b4dfa1574 --- /dev/null +++ b/packages/plugin-hardhat/src/verify-plugin.ts @@ -0,0 +1,16 @@ +import { overrideTask } from 'hardhat/config'; +import type { HardhatPlugin } from 'hardhat/types/plugins'; + +const plugin: HardhatPlugin = { + id: '@openzeppelin/hardhat-upgrades/verify', + + dependencies: () => [import('@nomicfoundation/hardhat-verify').then(m => ({ default: m.default }))], + + tasks: [ + overrideTask('verify') + .setAction(async () => import('./verify-proxy-task-action.js')) + .build(), + ], +}; + +export default plugin; diff --git a/packages/plugin-hardhat/src/verify-proxy-task-action.ts b/packages/plugin-hardhat/src/verify-proxy-task-action.ts new file mode 100644 index 000000000..4f20db379 --- /dev/null +++ b/packages/plugin-hardhat/src/verify-proxy-task-action.ts @@ -0,0 +1,8 @@ +import type { TaskOverrideActionFunction } from 'hardhat/types/tasks'; +import { verify } from './verify-proxy.js'; + +const action: TaskOverrideActionFunction = async (taskArguments, hre, runSuper) => { + return verify(taskArguments as Record, hre, runSuper); +}; + +export default action; diff --git a/packages/plugin-hardhat/src/verify-proxy.ts b/packages/plugin-hardhat/src/verify-proxy.ts index 7e117c2bc..480fa8688 100644 --- a/packages/plugin-hardhat/src/verify-proxy.ts +++ b/packages/plugin-hardhat/src/verify-proxy.ts @@ -13,7 +13,7 @@ import { } from '@openzeppelin/upgrades-core'; import artifactsBuildInfo from '@openzeppelin/upgrades-core/artifacts/build-info-v5.json'; -import { HardhatRuntimeEnvironment, RunSuperFunction } from 'hardhat/types'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import ERC1967Proxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'; import BeaconProxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'; @@ -23,10 +23,15 @@ import ProxyAdmin from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/cont import { keccak256 } from 'ethereumjs-util'; -import debug from './utils/debug'; -import { callEtherscanApi, getEtherscanInstance, RESPONSE_OK } from './utils/etherscan-api'; -import { verifyAndGetStatus } from './utils/etherscan-api'; -import { Etherscan } from '@nomicfoundation/hardhat-verify/etherscan'; +import debug from './utils/debug.js'; +import { + callEtherscanApi, + getEtherscanFromConnection, + RESPONSE_OK, + verifyAndGetStatus, + type Etherscan, + type EtherscanVerifyArgs, +} from './utils/etherscan-api.js'; /** * Hardhat artifact for a precompiled contract @@ -34,9 +39,9 @@ import { Etherscan } from '@nomicfoundation/hardhat-verify/etherscan'; interface ContractArtifact { contractName: string; sourceName: string; - abi: any; - bytecode: any; - deployedBytecode: any; + abi: unknown; + bytecode: string; + deployedBytecode: string; } /** @@ -60,44 +65,35 @@ interface EtherscanEventResponse { transactionHash: string; } -/** - * The proxy-related contracts and their corresponding events that may have been deployed the current version of this plugin. - */ const verifiableContracts = { - erc1967proxy: { artifact: ERC1967Proxy, event: 'Upgraded(address)' }, - beaconProxy: { artifact: BeaconProxy, event: 'BeaconUpgraded(address)' }, - upgradeableBeacon: { artifact: UpgradeableBeacon, event: 'OwnershipTransferred(address,address)' }, - transparentUpgradeableProxy: { artifact: TransparentUpgradeableProxy, event: 'AdminChanged(address,address)' }, - proxyAdmin: { artifact: ProxyAdmin, event: 'OwnershipTransferred(address,address)' }, + erc1967proxy: { artifact: ERC1967Proxy as ContractArtifact, event: 'Upgraded(address)' }, + beaconProxy: { artifact: BeaconProxy as ContractArtifact, event: 'BeaconUpgraded(address)' }, + upgradeableBeacon: { + artifact: UpgradeableBeacon as ContractArtifact, + event: 'OwnershipTransferred(address,address)', + }, + transparentUpgradeableProxy: { + artifact: TransparentUpgradeableProxy as ContractArtifact, + event: 'AdminChanged(address,address)', + }, + proxyAdmin: { artifact: ProxyAdmin as ContractArtifact, event: 'OwnershipTransferred(address,address)' }, }; /** - * Overrides hardhat-verify's verify:etherscan subtask to fully verify a proxy or beacon. - * + * Overrides hardhat-verify's verify flow to fully verify a proxy or beacon. * Verifies the contract at an address. If the address is an ERC-1967 compatible proxy, verifies the proxy and associated proxy contracts, * as well as the implementation. Otherwise, calls hardhat-verify's verify function directly. * - * @param args Args to the hardhat-verify verify function - * @param hre - * @param runSuper The parent function which is expected to be hardhat-verify's verify function - * @returns + * Requires @nomicfoundation/hardhat-verify v3.0.10+ and uses verification.etherscan from hre.network.connect(). */ -export async function verify(args: any, hre: HardhatRuntimeEnvironment, runSuper: RunSuperFunction) { - if (!runSuper.isDefined) { - throw new UpgradesError( - 'The hardhat-verify plugin must be imported before the hardhat-upgrades plugin.', - () => - 'Import the plugins in the following order in hardhat.config.js:\n' + - ' require("@nomicfoundation/hardhat-verify");\n' + - ' require("@openzeppelin/hardhat-upgrades");\n' + - 'Or if you are using TypeScript, import the plugins in the following order in hardhat.config.ts:\n' + - ' import "@nomicfoundation/hardhat-verify";\n' + - ' import "@openzeppelin/hardhat-upgrades";\n', - ); - } - - const provider = hre.network.provider; - const proxyAddress = args.address; +export async function verify( + args: Record, + hre: HardhatRuntimeEnvironment, + runSuper: (taskArguments: Record) => Promise, +): Promise { + const connection = await hre.network.connect(); + const { provider } = connection; + const proxyAddress = args.address as string; const errorReport: ErrorReport = { errors: [], severity: 'error', @@ -105,17 +101,20 @@ export async function verify(args: any, hre: HardhatRuntimeEnvironment, runSuper let proxy = true; + async function hardhatVerify(address: string): Promise { + return await runSuper({ ...args, address }); + } + if (await isTransparentOrUUPSProxy(provider, proxyAddress)) { await fullVerifyTransparentOrUUPS(hre, proxyAddress, hardhatVerify, errorReport); } else if (await isBeaconProxy(provider, proxyAddress)) { await fullVerifyBeaconProxy(hre, proxyAddress, hardhatVerify, errorReport); } else if (await isBeacon(provider, proxyAddress)) { proxy = false; - const etherscan = await getEtherscanInstance(hre); + const etherscan = await getEtherscanFromConnection(hre); await fullVerifyBeacon(hre, proxyAddress, hardhatVerify, etherscan, errorReport); } else { - // Doesn't look like a proxy, so just verify directly - return hardhatVerify(proxyAddress); + return await hardhatVerify(proxyAddress); } if (errorReport.errors.length > 0) { @@ -124,24 +123,15 @@ export async function verify(args: any, hre: HardhatRuntimeEnvironment, runSuper console.info(`\n${proxy ? 'Proxy' : 'Contract'} fully verified.`); } - async function hardhatVerify(address: string) { - return await runSuper({ ...args, address }); - } + return undefined; } -/** - * Throws or warns with a formatted summary of all of the verification errors that have been recorded. - * - * @param errorReport Accumulated verification errors - * @throws UpgradesError if errorReport.severity is 'error' - */ -function displayErrorReport(errorReport: ErrorReport) { +function displayErrorReport(errorReport: ErrorReport): void { let summary = `\nVerification completed with the following ${ errorReport.severity === 'error' ? 'errors' : 'warnings' }.`; for (let i = 0; i < errorReport.errors.length; i++) { - const error = errorReport.errors[i]; - summary += `\n\n${errorReport.severity === 'error' ? 'Error' : 'Warning'} ${i + 1}: ${error}`; + summary += `\n\n${errorReport.severity === 'error' ? 'Error' : 'Warning'} ${i + 1}: ${errorReport.errors[i]}`; } if (errorReport.severity === 'error') { throw new UpgradesError(summary); @@ -150,44 +140,33 @@ function displayErrorReport(errorReport: ErrorReport) { } } -/** - * Log an error about the given contract's verification attempt, and save it so it can be summarized at the end. - * - * @param address The address that failed to verify - * @param contractType The type or name of the contract - * @param details The error details - * @param errorReport Accumulated verification errors - */ -function recordVerificationError(address: string, contractType: string, details: string, errorReport: ErrorReport) { +function recordVerificationError( + address: string, + contractType: string, + details: string, + errorReport: ErrorReport, +): void { const message = `Failed to verify ${contractType} contract at ${address}: ${details}`; recordError(message, errorReport); } -function recordError(message: string, errorReport: ErrorReport) { +function recordError(message: string, errorReport: ErrorReport): void { console.error(message); errorReport.errors.push(message); } -/** - * Indicates that the expected event topic was not found in the contract's logs according to the Etherscan API, or an expected function was not found. - */ class EventOrFunctionNotFound extends UpgradesError {} class EventsNotFound extends EventOrFunctionNotFound { constructor(address: string, events: string[]) { super( - `Could not find an event with any of the following topics in the logs for address ${address}: ${events.join( - ', ', - )}`, + `Could not find an event with any of the following topics in the logs for address ${address}: ${events.join(', ')}`, () => 'If the proxy was recently deployed, the transaction may not be available on Etherscan yet. Try running the verify task again after waiting a few blocks.', ); } } -/** - * Indicates that the contract's bytecode does not match with the plugin's artifact. - */ class BytecodeNotMatchArtifact extends Error { contractName: string; constructor(message: string, contractName: string) { @@ -196,98 +175,31 @@ class BytecodeNotMatchArtifact extends Error { } } -/** - * Fully verifies all contracts related to the given transparent or UUPS proxy address: implementation, admin (if any), and proxy. - * Also links the proxy to the implementation ABI on Etherscan. - * - * This function will determine whether the address is a transparent or UUPS proxy based on whether its creation bytecode matches with - * TransparentUpgradeableProxy or ERC1967Proxy. - * - * Note: this function does not use the admin slot to determine whether the proxy is transparent or UUPS, but will always verify - * the admin address as long as the admin storage slot has an address. - * - * @param hre - * @param proxyAddress The transparent or UUPS proxy address - * @param hardhatVerify A function that invokes the hardhat-verify plugin's verify command - * @param errorReport Accumulated verification errors - */ async function fullVerifyTransparentOrUUPS( hre: HardhatRuntimeEnvironment, - proxyAddress: any, - hardhatVerify: (address: string) => Promise, + proxyAddress: string, + hardhatVerify: (address: string) => Promise, errorReport: ErrorReport, -) { - const provider = hre.network.provider; - const implAddress = await getImplementationAddress(provider, proxyAddress); +): Promise { + const connection = await hre.network.connect(); + const implAddress = await getImplementationAddress(connection.provider, proxyAddress); await verifyImplementation(hardhatVerify, implAddress, errorReport); - const etherscan = await getEtherscanInstance(hre); + const etherscan = await getEtherscanFromConnection(hre); await verifyTransparentOrUUPS(); await linkProxyWithImplementationAbi(etherscan, proxyAddress, implAddress, errorReport); - // Either UUPS or Transparent proxy could have admin slot set, although typically this should only be for Transparent await verifyAdmin(); - async function verifyAdmin() { - const adminAddress = await getAdminAddress(provider, proxyAddress); + async function verifyAdmin(): Promise { + const adminAddress = await getAdminAddress(connection.provider, proxyAddress); if (!isEmptySlot(adminAddress)) { console.log(`Verifying proxy admin: ${adminAddress}`); - await verifyAdminOrFallback(hardhatVerify, etherscan, adminAddress, errorReport); + await verifyAdminOrFallback(hre, hardhatVerify, etherscan, adminAddress, errorReport); } } - /** - * Verifies a proxy admin contract by looking up an OwnershipTransferred event that should have been logged during construction - * to get the owner used for its constructor. - * - * This is different from the verifyWithArtifactOrFallback function because the proxy admin in Contracts 5.0 is not deployed directly by the plugin, - * but is deployed by the transparent proxy itself, so we cannot infer the admin's constructor arguments from the originating transaction's input bytecode. - */ - async function verifyAdminOrFallback( - hardhatVerify: (address: string) => Promise, - etherscan: Etherscan, - adminAddress: string, - errorReport: ErrorReport, - ) { - const attemptVerify = async () => { - let encodedOwner: string; - // Get the OwnershipTransferred event when the ProxyAdmin was created, which should have the encoded owner address as its second parameter (third topic). - const response = await getEventResponse(adminAddress, verifiableContracts.proxyAdmin.event, etherscan); - if (response === undefined) { - throw new EventsNotFound(adminAddress, [verifiableContracts.proxyAdmin.event]); - } else if (response.topics.length !== 3) { - throw new EventOrFunctionNotFound( - `Unexpected number of topics in event logs for ${verifiableContracts.proxyAdmin.event} from ${adminAddress}. Expected 3, got ${response.topics.length}: ${response.topics}`, - () => `The contract at ${adminAddress} does not appear to be a known proxy admin contract.`, - ); - } else { - encodedOwner = response.topics[2].replace(/^0x/, ''); - } - - const artifact = verifiableContracts.proxyAdmin.artifact; - const deployedBytecode = await getCode(provider, adminAddress); - if (deployedBytecode !== artifact.deployedBytecode) { - throw new BytecodeNotMatchArtifact( - `Bytecode does not match with the current version of ${artifact.contractName} in the Hardhat Upgrades plugin.`, - artifact.contractName, - ); - } - - await verifyContractWithConstructorArgs(etherscan, adminAddress, artifact, encodedOwner, errorReport); - }; - - await attemptVerifyOrFallback( - attemptVerify, - hardhatVerify, - adminAddress, - errorReport, - // The user provided the proxy address to verify, whereas this function is only verifying the related proxy admin. - // So even if this falls back and succeeds, we want to keep any errors that might have occurred while verifying the proxy itself. - false, - ); - } - - async function verifyTransparentOrUUPS() { + async function verifyTransparentOrUUPS(): Promise { console.log(`Verifying proxy: ${proxyAddress}`); await verifyWithArtifactOrFallback( hre, @@ -301,31 +213,59 @@ async function fullVerifyTransparentOrUUPS( } } -/** - * Fully verifies all contracts related to the given beacon proxy address: implementation, beacon, and beacon proxy. - * Also links the proxy to the implementation ABI on Etherscan. - * - * @param hre - * @param proxyAddress The beacon proxy address - * @param hardhatVerify A function that invokes the hardhat-verify plugin's verify command - * @param errorReport Accumulated verification errors - */ +async function verifyAdminOrFallback( + hre: HardhatRuntimeEnvironment, + hardhatVerify: (address: string) => Promise, + etherscan: Etherscan, + adminAddress: string, + errorReport: ErrorReport, +): Promise { + const attemptVerify = async (): Promise => { + let encodedOwner: string; + const response = await getEventResponse(adminAddress, verifiableContracts.proxyAdmin.event, etherscan); + if (response === undefined) { + throw new EventsNotFound(adminAddress, [verifiableContracts.proxyAdmin.event]); + } else if (response.topics.length !== 3) { + throw new EventOrFunctionNotFound( + `Unexpected number of topics in event logs for ${verifiableContracts.proxyAdmin.event} from ${adminAddress}. Expected 3, got ${response.topics.length}: ${response.topics}`, + () => `The contract at ${adminAddress} does not appear to be a known proxy admin contract.`, + ); + } else { + encodedOwner = response.topics[2].replace(/^0x/, ''); + } + + const artifact = verifiableContracts.proxyAdmin.artifact; + const connection = await hre.network.connect(); + const deployedBytecode = await getCode(connection.provider, adminAddress); + if (deployedBytecode !== artifact.deployedBytecode) { + throw new BytecodeNotMatchArtifact( + `Bytecode does not match with the current version of ${artifact.contractName} in the Hardhat Upgrades plugin.`, + artifact.contractName, + ); + } + + await verifyContractWithConstructorArgs(etherscan, adminAddress, artifact, encodedOwner, errorReport); + }; + + await attemptVerifyOrFallback(attemptVerify, hardhatVerify, adminAddress, errorReport, false); +} + async function fullVerifyBeaconProxy( hre: HardhatRuntimeEnvironment, - proxyAddress: any, - hardhatVerify: (address: string) => Promise, + proxyAddress: string, + hardhatVerify: (address: string) => Promise, errorReport: ErrorReport, -) { - const provider = hre.network.provider; - const beaconAddress = await getBeaconAddress(provider, proxyAddress); - const implAddress = await getImplementationAddressFromBeacon(provider, beaconAddress); - const etherscan = await getEtherscanInstance(hre); +): Promise { + const connection = await hre.network.connect(); + const beaconAddress = await getBeaconAddress(connection.provider, proxyAddress); + const implAddress = await getImplementationAddressFromBeacon(connection.provider, beaconAddress); + const etherscan = await getEtherscanFromConnection(hre); await fullVerifyBeacon(hre, beaconAddress, hardhatVerify, etherscan, errorReport); await verifyBeaconProxy(); await linkProxyWithImplementationAbi(etherscan, proxyAddress, implAddress, errorReport); - async function verifyBeaconProxy() { + async function verifyBeaconProxy(): Promise { console.log(`Verifying beacon proxy: ${proxyAddress}`); await verifyWithArtifactOrFallback( hre, @@ -339,29 +279,19 @@ async function fullVerifyBeaconProxy( } } -/** - * Verifies all contracts resulting from a beacon deployment: implementation, beacon - * - * @param hre - * @param beaconAddress The beacon address - * @param hardhatVerify A function that invokes the hardhat-verify plugin's verify command - * @param etherscan Etherscan instance - * @param errorReport Accumulated verification errors - */ async function fullVerifyBeacon( hre: HardhatRuntimeEnvironment, - beaconAddress: any, - hardhatVerify: (address: string) => Promise, + beaconAddress: string, + hardhatVerify: (address: string) => Promise, etherscan: Etherscan, errorReport: ErrorReport, -) { - const provider = hre.network.provider; - - const implAddress = await getImplementationAddressFromBeacon(provider, beaconAddress); +): Promise { + const connection = await hre.network.connect(); + const implAddress = await getImplementationAddressFromBeacon(connection.provider, beaconAddress); await verifyImplementation(hardhatVerify, implAddress, errorReport); await verifyBeacon(); - async function verifyBeacon() { + async function verifyBeacon(): Promise { console.log(`Verifying beacon or beacon-like contract: ${beaconAddress}`); await verifyWithArtifactOrFallback( hre, @@ -375,42 +305,29 @@ async function fullVerifyBeacon( } } -/** - * Runs hardhat-verify plugin's verify command on the given implementation address. - * - * @param hardhatVerify A function that invokes the hardhat-verify plugin's verify command - * @param implAddress The implementation address - * @param errorReport Accumulated verification errors - */ async function verifyImplementation( - hardhatVerify: (address: string) => Promise, + hardhatVerify: (address: string) => Promise, implAddress: string, errorReport: ErrorReport, -) { +): Promise { try { console.log(`Verifying implementation: ${implAddress}`); await hardhatVerify(implAddress); - } catch (e: any) { - if (e.message.toLowerCase().includes('already verified')) { + } catch (e: unknown) { + const message = e instanceof Error ? e.message : String(e); + if (message.toLowerCase().includes('already verified')) { console.log(`Implementation ${implAddress} already verified.`); } else { - recordVerificationError(implAddress, 'implementation', e.message, errorReport); + recordVerificationError(implAddress, 'implementation', message, errorReport); } } } -/** - * Looks for any of the possible events (in array order) at the specified address using Etherscan API, - * and returns the corresponding VerifiableContractInfo and txHash for the first event found. - * - * @param etherscan Etherscan instance - * @param address The contract address for which to look for events - * @param possibleContractInfo An array of possible contract artifacts to use for verification along - * with the corresponding creation event expected in the logs. - * @returns the VerifiableContractInfo and txHash for the first event found - * @throws {EventOrFunctionNotFound} if none of the events were found in the contract's logs according to Etherscan. - */ -async function searchEvent(etherscan: Etherscan, address: string, possibleContractInfo: VerifiableContractInfo[]) { +async function searchEvent( + etherscan: Etherscan, + address: string, + possibleContractInfo: VerifiableContractInfo[], +): Promise<{ contractInfo: VerifiableContractInfo; txHash: string }> { for (let i = 0; i < possibleContractInfo.length; i++) { const contractInfo = possibleContractInfo[i]; const txHash = await getContractCreationTxHash(address, contractInfo.event, etherscan); @@ -419,61 +336,38 @@ async function searchEvent(etherscan: Etherscan, address: string, possibleContra } } - const events = possibleContractInfo.map(contractInfo => { - return contractInfo.event; - }); + const events = possibleContractInfo.map(ci => ci.event); throw new EventsNotFound(address, events); } -/** - * Verifies a contract using the attemptVerify function. If it fails, falls back to verify directly using the regular hardhat verify task. - * - * If the fallback passes, logs as success. - * If the fallback also fails, records errors for both the original and fallback attempts. - * - * @param attemptVerify A function that attempts to verify the contract. - * Should throw EventOrFunctionNotFound if the contract does not contain an expected event in its logs or function in its bytecode, - * or BytecodeNotMatchArtifact if the contract's bytecode does not match with the plugin's known artifact. - * @param hardhatVerify A function that invokes the hardhat-verify plugin's verify command - * @param address The contract address to verify - * @param errorReport Accumulated verification errors - * @param convertErrorsToWarningsOnFallbackSuccess If fallback verification occurred and succeeded, whether any - * previously accumulated errors should be converted into warnings in the final summary. - */ async function attemptVerifyOrFallback( - attemptVerify: () => Promise, - hardhatVerify: (address: string) => Promise, + attemptVerify: () => Promise, + hardhatVerify: (address: string) => Promise, address: string, errorReport: ErrorReport, convertErrorsToWarningsOnFallbackSuccess: boolean, -) { +): Promise { try { await attemptVerify(); - return true; - } catch (origError: any) { + return; + } catch (origError: unknown) { if (origError instanceof BytecodeNotMatchArtifact || origError instanceof EventOrFunctionNotFound) { - // Try falling back to regular hardhat verify in case the source code is available in the user's project. try { await hardhatVerify(address); - } catch (fallbackError: any) { - if (fallbackError.message.toLowerCase().includes('already verified')) { + } catch (fallbackError: unknown) { + const fallbackMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError); + if (fallbackMessage.toLowerCase().includes('already verified')) { console.log(`Contract at ${address} already verified.`); } else { - // Fallback failed, so record both the original error and the fallback attempt, then return if (origError instanceof BytecodeNotMatchArtifact) { recordVerificationError(address, origError.contractName, origError.message, errorReport); } else { - recordError(origError.message, errorReport); + recordError(origError instanceof Error ? origError.message : String(origError), errorReport); } - - recordError(`Failed to verify directly using hardhat verify: ${fallbackError.message}`, errorReport); + recordError(`Failed to verify directly using hardhat verify: ${fallbackMessage}`, errorReport); return; } } - - // Since the contract was able to be verified directly, we don't want the task to fail so we should convert earlier errors into warnings for other related contracts. - // For example, the user provided constructor arguments for the verify command will apply to all calls of the regular hardhat verify, - // so it is not possible to successfully verify both an impl and a proxy that uses the above fallback at the same time. if (convertErrorsToWarningsOnFallbackSuccess) { errorReport.severity = 'warn'; } @@ -483,35 +377,18 @@ async function attemptVerifyOrFallback( } } -/** - * Verifies a contract by matching with known artifacts. - * - * If a match was not found, falls back to verify directly using the regular hardhat verify task. - * - * If the fallback passes, logs as success. - * If the fallback also fails, records errors for both the original and fallback attempts. - * - * @param hre - * @param etherscan Etherscan instance - * @param address The contract address to verify - * @param possibleContractInfo An array of possible contract artifacts to use for verification along - * with the corresponding creation event expected in the logs. - * @param errorReport Accumulated verification errors - * @param convertErrorsToWarningsOnFallbackSuccess If fallback verification occurred and succeeded, whether any - * previously accumulated errors should be converted into warnings in the final summary. - */ async function verifyWithArtifactOrFallback( hre: HardhatRuntimeEnvironment, - hardhatVerify: (address: string) => Promise, + hardhatVerify: (address: string) => Promise, etherscan: Etherscan, address: string, possibleContractInfo: VerifiableContractInfo[], errorReport: ErrorReport, convertErrorsToWarningsOnFallbackSuccess: boolean, -) { - const attemptVerify = () => +): Promise { + const attemptVerify = (): Promise => attemptVerifyWithCreationEvent(hre, etherscan, address, possibleContractInfo, errorReport); - return await attemptVerifyOrFallback( + await attemptVerifyOrFallback( attemptVerify, hardhatVerify, address, @@ -520,109 +397,68 @@ async function verifyWithArtifactOrFallback( ); } -/** - * Attempts to verify a contract by looking up an event that should have been logged during contract construction, - * finds the txHash for that, and infers the constructor args to use for verification. - * - * Iterates through each element of possibleContractInfo to look for that element's event, until an event is found. - * - * @param hre - * @param etherscan Etherscan instance - * @param address The contract address to verify - * @param possibleContractInfo An array of possible contract artifacts to use for verification along - * with the corresponding creation event expected in the logs. - * @param errorReport Accumulated verification errors - * @throws {EventOrFunctionNotFound} if none of the events were found in the contract's logs according to Etherscan. - * @throws {BytecodeNotMatchArtifact} if the contract's bytecode does not match with the plugin's known artifact. - */ async function attemptVerifyWithCreationEvent( hre: HardhatRuntimeEnvironment, etherscan: Etherscan, address: string, possibleContractInfo: VerifiableContractInfo[], errorReport: ErrorReport, -) { +): Promise { const { contractInfo, txHash } = await searchEvent(etherscan, address, possibleContractInfo); debug(`verifying contract ${contractInfo.artifact.contractName} at ${address}`); - const tx = await getTransactionByHash(hre.network.provider, txHash); + const connection = await hre.network.connect(); + const tx = await getTransactionByHash(connection.provider, txHash); if (tx === null) { - // This should not happen since the txHash came from the logged event itself throw new UpgradesError(`The transaction hash ${txHash} from the contract's logs was not found on the network`); } const constructorArguments = inferConstructorArgs(tx.input, contractInfo.artifact.bytecode); if (constructorArguments === undefined) { - // The creation bytecode for the address does not match with the expected artifact. - // This may be because a different version of the contract was deployed compared to what is in the plugins. throw new BytecodeNotMatchArtifact( `Bytecode does not match with the current version of ${contractInfo.artifact.contractName} in the Hardhat Upgrades plugin.`, contractInfo.artifact.contractName, ); - } else { - await verifyContractWithConstructorArgs( - etherscan, - address, - contractInfo.artifact, - constructorArguments, - errorReport, - ); } + await verifyContractWithConstructorArgs(etherscan, address, contractInfo.artifact, constructorArguments, errorReport); } -/** - * Verifies a contract using the given constructor args. - * - * @param etherscan Etherscan instance - * @param address The address of the contract to verify - * @param artifact The contract artifact to use for verification. - * @param constructorArguments The constructor arguments to use for verification. - */ async function verifyContractWithConstructorArgs( etherscan: Etherscan, address: string, artifact: ContractArtifact, constructorArguments: string, errorReport: ErrorReport, -) { +): Promise { debug(`verifying contract ${address} with constructor args ${constructorArguments}`); - const params = { + const buildInfo = artifactsBuildInfo as { input: EtherscanVerifyArgs['compilerInput']; solcLongVersion: string }; + const params: EtherscanVerifyArgs = { contractAddress: address, - sourceCode: JSON.stringify(artifactsBuildInfo.input), + compilerInput: buildInfo.input, contractName: `${artifact.sourceName}:${artifact.contractName}`, - compilerVersion: `v${artifactsBuildInfo.solcLongVersion}`, - constructorArguments: constructorArguments, + compilerVersion: `v${buildInfo.solcLongVersion}`, + constructorArguments, }; try { const status = await verifyAndGetStatus(params, etherscan); - if (status.isSuccess()) { + if (status.success) { console.log(`Successfully verified contract ${artifact.contractName} at ${address}.`); } else { recordVerificationError(address, artifact.contractName, status.message, errorReport); } - } catch (e: any) { - if (e.message.toLowerCase().includes('already verified')) { + } catch (e: unknown) { + const message = e instanceof Error ? e.message : String(e); + if (message.toLowerCase().includes('already verified')) { console.log(`Contract at ${address} already verified.`); } else { - recordVerificationError(address, artifact.contractName, e.message, errorReport); + recordVerificationError(address, artifact.contractName, message, errorReport); } } } -/** - * Calls the Etherscan API to look for an event that should have been emitted during construction - * of the contract at the given address, and returns the result corresponding to the first event found. - * - * @param address The address for which to get the event response. - * @param topic The event topic string that should have been logged. - * @param etherscan Etherscan instance - * @returns The event response, or undefined if not found or if - * the address is not a contract. - * @throws {UpgradesError} if the Etherscan API returned with not OK status - */ async function getEventResponse( address: string, topic: string, @@ -633,7 +469,7 @@ async function getEventResponse( action: 'getLogs', fromBlock: '0', toBlock: 'latest', - address: address, + address, topic0: '0x' + keccak256(Buffer.from(topic)).toString('hex'), }; @@ -653,39 +489,21 @@ async function getEventResponse( } } -/** - * Gets the txhash that created the contract at the given address, by calling the - * Etherscan API to look for an event that should have been emitted during construction. - * - * @param address The address to get the creation txhash for. - * @param topic The event topic string that should have been logged. - * @param etherscan Etherscan instance - * @returns The txhash corresponding to the logged event, or undefined if not found or if - * the address is not a contract. - * @throws {UpgradesError} if the Etherscan API returned with not OK status - */ -async function getContractCreationTxHash(address: string, topic: string, etherscan: Etherscan): Promise { +async function getContractCreationTxHash( + address: string, + topic: string, + etherscan: Etherscan, +): Promise { const eventResponse = await getEventResponse(address, topic, etherscan); - if (eventResponse === undefined) { - return undefined; - } else { - return eventResponse.transactionHash; - } + return eventResponse?.transactionHash; } -/** - * Calls the Etherscan API to link a proxy with its implementation ABI. - * - * @param etherscan Etherscan instance - * @param proxyAddress The proxy address - * @param implAddress The implementation address - */ async function linkProxyWithImplementationAbi( etherscan: Etherscan, proxyAddress: string, implAddress: string, errorReport: ErrorReport, -) { +): Promise { console.log(`Linking proxy ${proxyAddress} with implementation`); const params = { module: 'contract', @@ -693,10 +511,9 @@ async function linkProxyWithImplementationAbi( address: proxyAddress, expectedimplementation: implAddress, }; - let responseBody = await callEtherscanApi(etherscan, params); + let responseBody = await callEtherscanApi(etherscan, params, { method: 'POST', body: params }); if (responseBody.status === RESPONSE_OK) { - // initial call was OK, but need to send a status request using the returned guid to get the actual verification status const guid = responseBody.result as string; responseBody = await checkProxyVerificationStatus(etherscan, guid); @@ -714,33 +531,27 @@ async function linkProxyWithImplementationAbi( errorReport, ); } +} - async function delay(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); - } +function delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); } -async function checkProxyVerificationStatus(etherscan: Etherscan, guid: string) { - const checkProxyVerificationParams = { +async function checkProxyVerificationStatus( + etherscan: Etherscan, + guid: string, +): Promise<{ status: string; message: string; result: unknown }> { + const params = { module: 'contract', action: 'checkproxyverification', - apikey: etherscan.apiKey, - guid: guid, + guid, }; - return await callEtherscanApi(etherscan, checkProxyVerificationParams); + return (await callEtherscanApi(etherscan, params)) as { status: string; message: string; result: unknown }; } -/** - * Gets the constructor args from the given transaction input and creation code. - * - * @param txInput The transaction input that was used to deploy the contract. - * @param creationCode The contract creation code. - * @returns the encoded constructor args, or undefined if txInput does not start with the creationCode. - */ -function inferConstructorArgs(txInput: string, creationCode: string) { +function inferConstructorArgs(txInput: string, creationCode: string): string | undefined { if (txInput.startsWith(creationCode)) { return txInput.substring(creationCode.length); - } else { - return undefined; } + return undefined; } diff --git a/packages/plugin-hardhat/test/beacon-000-externally-deployed.js b/packages/plugin-hardhat/test/beacon-000-externally-deployed.js index aa6ed9dad..9e51f2db1 100644 --- a/packages/plugin-hardhat/test/beacon-000-externally-deployed.js +++ b/packages/plugin-hardhat/test/beacon-000-externally-deployed.js @@ -1,17 +1,28 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + // Initialize upgrades API (needs full HRE) + upgrades = await upgradesFactory(hre, connection); + + // Now get contract factories t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.Beacon = await ethers.getContractFactory('Beacon'); }); const IS_NOT_REGISTERED = 'is not registered'; -// These tests need to run before the other deploy beacon tests so that the beacon implementation will not already be in the manifest. - test('block upgrade to unregistered beacon', async t => { const { Greeter, GreeterV2, Beacon } = t.context; @@ -19,14 +30,20 @@ test('block upgrade to unregistered beacon', async t => { const greeter = await Greeter.deploy(); await greeter.waitForDeployment(); + console.log('Deployed Greeter at:', await greeter.getAddress()); + + // upgrades.deployBeacon() const beacon = await Beacon.deploy(await greeter.getAddress()); await beacon.waitForDeployment(); + console.log('Deployed Beacon at:', await beacon.getAddress()); + // upgrade beacon to new impl try { await upgrades.upgradeBeacon(await beacon.getAddress(), GreeterV2); t.fail('Expected an error due to unregistered deployment'); } catch (e) { + console.log('Upgrade error message:', e.message); t.true(e.message.includes(IS_NOT_REGISTERED), e.message); } }); @@ -37,7 +54,6 @@ test('add proxy to unregistered beacon using contract factory', async t => { // deploy beacon without upgrades plugin const greeter = await Greeter.deploy(); await greeter.waitForDeployment(); - const beacon = await Beacon.deploy(await greeter.getAddress()); await beacon.waitForDeployment(); diff --git a/packages/plugin-hardhat/test/beacon-001-externally-upgraded.js b/packages/plugin-hardhat/test/beacon-001-externally-upgraded.js index 5f34df093..984f304b0 100644 --- a/packages/plugin-hardhat/test/beacon-001-externally-upgraded.js +++ b/packages/plugin-hardhat/test/beacon-001-externally-upgraded.js @@ -1,10 +1,22 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.Beacon = await ethers.getContractFactory('Beacon'); }); diff --git a/packages/plugin-hardhat/test/beacon-constructor.js b/packages/plugin-hardhat/test/beacon-constructor.js index 0f4caef56..9a3506c53 100644 --- a/packages/plugin-hardhat/test/beacon-constructor.js +++ b/packages/plugin-hardhat/test/beacon-constructor.js @@ -1,9 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.WithConstructor = await ethers.getContractFactory('WithConstructor'); + upgrades = await upgradesFactory(hre, connection); + + t.context.WithConstructor = await ethers.getContractFactory('contracts/WithConstructor.sol:WithConstructor'); }); test('new beacon - do not redeploy with same args', async t => { @@ -11,12 +23,12 @@ test('new beacon - do not redeploy with same args', async t => { const beacon1 = await upgrades.deployBeacon(WithConstructor, { constructorArgs: [17] }); const implementation1 = await upgrades.beacon.getImplementationAddress(await beacon1.getAddress()); - const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor); + const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor, [], { initializer: false }); t.is(await proxy1.value(), 17n); const beacon2 = await upgrades.deployBeacon(WithConstructor, { constructorArgs: [17] }); const implementation2 = await upgrades.beacon.getImplementationAddress(await beacon2.getAddress()); - const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor); + const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor, [], { initializer: false }); t.is(await proxy2.value(), 17n); t.not(await beacon1.getAddress(), await beacon2.getAddress()); @@ -32,12 +44,12 @@ test('new beacon - redeploy with different args', async t => { const beacon1 = await upgrades.deployBeacon(WithConstructor, { constructorArgs: [17] }); const implementation1 = await upgrades.beacon.getImplementationAddress(await beacon1.getAddress()); - const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor); + const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor, [], { initializer: false }); t.is(await proxy1.value(), 17n); const beacon2 = await upgrades.deployBeacon(WithConstructor, { constructorArgs: [42] }); const implementation2 = await upgrades.beacon.getImplementationAddress(await beacon2.getAddress()); - const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor); + const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor, [], { initializer: false }); t.is(await proxy2.value(), 42n); t.not(await beacon1.getAddress(), await beacon2.getAddress()); @@ -53,12 +65,12 @@ test('upgrade - do not redeploy with same args', async t => { const beacon1 = await upgrades.deployBeacon(WithConstructor, { constructorArgs: [17] }); const implementation1 = await upgrades.beacon.getImplementationAddress(await beacon1.getAddress()); - const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor); + const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor, [], { initializer: false }); t.is(await proxy1.value(), 17n); const beacon2 = await upgrades.upgradeBeacon(beacon1, WithConstructor, { constructorArgs: [17] }); const implementation2 = await upgrades.beacon.getImplementationAddress(await beacon2.getAddress()); - const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor); + const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor, [], { initializer: false }); t.is(await proxy2.value(), 17n); t.is(await beacon1.getAddress(), await beacon2.getAddress()); @@ -74,12 +86,12 @@ test('upgrade - redeploy with different args', async t => { const beacon1 = await upgrades.deployBeacon(WithConstructor, { constructorArgs: [17] }); const implementation1 = await upgrades.beacon.getImplementationAddress(await beacon1.getAddress()); - const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor); + const proxy1 = await upgrades.deployBeaconProxy(beacon1, WithConstructor, [], { initializer: false }); t.is(await proxy1.value(), 17n); const beacon2 = await upgrades.upgradeBeacon(beacon1, WithConstructor, { constructorArgs: [42] }); const implementation2 = await upgrades.beacon.getImplementationAddress(await beacon2.getAddress()); - const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor); + const proxy2 = await upgrades.deployBeaconProxy(beacon2, WithConstructor, [], { initializer: false }); t.is(await proxy2.value(), 42n); t.is(await beacon1.getAddress(), await beacon2.getAddress()); diff --git a/packages/plugin-hardhat/test/beacon-custom-beacon-proxy.js b/packages/plugin-hardhat/test/beacon-custom-beacon-proxy.js index 9a77472e5..ddbd69a0f 100644 --- a/packages/plugin-hardhat/test/beacon-custom-beacon-proxy.js +++ b/packages/plugin-hardhat/test/beacon-custom-beacon-proxy.js @@ -1,11 +1,23 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { deploy } from '../dist/utils/deploy.js'; -const { ethers, upgrades } = require('hardhat'); -const { deploy } = require('../dist/utils/deploy'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); t.context.CustomBeaconProxy = await ethers.getContractFactory('CustomBeaconProxy'); const [deployer, anon] = await ethers.getSigners(); diff --git a/packages/plugin-hardhat/test/beacon-deploy-overload.js b/packages/plugin-hardhat/test/beacon-deploy-overload.js index 302a5da36..380883c61 100644 --- a/packages/plugin-hardhat/test/beacon-deploy-overload.js +++ b/packages/plugin-hardhat/test/beacon-deploy-overload.js @@ -1,8 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.DeployOverload = await ethers.getContractFactory('DeployOverload'); }); diff --git a/packages/plugin-hardhat/test/beacon-deploy-validation.js b/packages/plugin-hardhat/test/beacon-deploy-validation.js index 00d6b587b..e75044695 100644 --- a/packages/plugin-hardhat/test/beacon-deploy-validation.js +++ b/packages/plugin-hardhat/test/beacon-deploy-validation.js @@ -1,8 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Invalid = await ethers.getContractFactory('Invalid'); }); diff --git a/packages/plugin-hardhat/test/beacon-happy-path-with-addresses.js b/packages/plugin-hardhat/test/beacon-happy-path-with-addresses.js index 00541da04..a67db7d9b 100644 --- a/packages/plugin-hardhat/test/beacon-happy-path-with-addresses.js +++ b/packages/plugin-hardhat/test/beacon-happy-path-with-addresses.js @@ -1,10 +1,22 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); }); diff --git a/packages/plugin-hardhat/test/beacon-happy-path-with-enums.js b/packages/plugin-hardhat/test/beacon-happy-path-with-enums.js index 9f63bf2d6..53854b70e 100644 --- a/packages/plugin-hardhat/test/beacon-happy-path-with-enums.js +++ b/packages/plugin-hardhat/test/beacon-happy-path-with-enums.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; -upgrades.silenceWarnings(); +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + upgrades.silenceWarnings(); + t.context.Action = await ethers.getContractFactory('Action'); t.context.ActionV2 = await ethers.getContractFactory('ActionV2'); t.context.ActionV2Bad = await ethers.getContractFactory('ActionV2Bad'); diff --git a/packages/plugin-hardhat/test/beacon-happy-path-with-library.js b/packages/plugin-hardhat/test/beacon-happy-path-with-library.js index 884432828..066e18ce4 100644 --- a/packages/plugin-hardhat/test/beacon-happy-path-with-library.js +++ b/packages/plugin-hardhat/test/beacon-happy-path-with-library.js @@ -1,8 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Adder = await ethers.getContractFactory('Adder'); t.context.AdderV2 = await ethers.getContractFactory('AdderV2'); }); diff --git a/packages/plugin-hardhat/test/beacon-happy-path-with-structs.js b/packages/plugin-hardhat/test/beacon-happy-path-with-structs.js index 1a906df20..abaa7759e 100644 --- a/packages/plugin-hardhat/test/beacon-happy-path-with-structs.js +++ b/packages/plugin-hardhat/test/beacon-happy-path-with-structs.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; -upgrades.silenceWarnings(); +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + upgrades.silenceWarnings(); + t.context.Portfolio = await ethers.getContractFactory('Portfolio'); t.context.PortfolioV2 = await ethers.getContractFactory('PortfolioV2'); t.context.PortfolioV2Bad = await ethers.getContractFactory('PortfolioV2Bad'); diff --git a/packages/plugin-hardhat/test/beacon-happy-path.js b/packages/plugin-hardhat/test/beacon-happy-path.js index c3491dc58..1413fc035 100644 --- a/packages/plugin-hardhat/test/beacon-happy-path.js +++ b/packages/plugin-hardhat/test/beacon-happy-path.js @@ -1,10 +1,22 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); }); diff --git a/packages/plugin-hardhat/test/beacon-initial-owner.js b/packages/plugin-hardhat/test/beacon-initial-owner.js index 7b6b8195e..1c7f0c24a 100644 --- a/packages/plugin-hardhat/test/beacon-initial-owner.js +++ b/packages/plugin-hardhat/test/beacon-initial-owner.js @@ -1,8 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('Greeter'); }); diff --git a/packages/plugin-hardhat/test/beacon-initializers.js b/packages/plugin-hardhat/test/beacon-initializers.js index d846da26f..bb30111d2 100644 --- a/packages/plugin-hardhat/test/beacon-initializers.js +++ b/packages/plugin-hardhat/test/beacon-initializers.js @@ -1,8 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.InitializerOverloaded = await ethers.getContractFactory('InitializerOverloaded'); t.context.InitializerMissing = await ethers.getContractFactory('InitializerMissing'); }); diff --git a/packages/plugin-hardhat/test/beacon-linked-libraries.js b/packages/plugin-hardhat/test/beacon-linked-libraries.js index 8891503c2..53da5ed16 100644 --- a/packages/plugin-hardhat/test/beacon-linked-libraries.js +++ b/packages/plugin-hardhat/test/beacon-linked-libraries.js @@ -1,10 +1,23 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades, artifacts } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +const artifacts = hre.artifacts; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; -upgrades.silenceWarnings(); +test.after.always(async () => { + await connection.close(); +}); + +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); test('without flag', async t => { // Deploying library diff --git a/packages/plugin-hardhat/test/beacon-upgrade-block-proxy.js b/packages/plugin-hardhat/test/beacon-upgrade-block-proxy.js index 0ddb3b52f..bcd82fc1e 100644 --- a/packages/plugin-hardhat/test/beacon-upgrade-block-proxy.js +++ b/packages/plugin-hardhat/test/beacon-upgrade-block-proxy.js @@ -1,12 +1,23 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); - t.context.GreeterV2Proxiable = await ethers.getContractFactory('GreeterV2Proxiable'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); + t.context.GreeterV2Proxiable = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); t.context.GreeterFallback = await ethers.getContractFactory('GreeterFallback'); }); @@ -72,8 +83,11 @@ test('block transparent proxy upgrade via upgradeBeacon', async t => { test('block uups proxy upgrade via upgradeBeacon', async t => { const { GreeterProxiable, GreeterV2Proxiable } = t.context; + const signer = await ethers.provider.getSigner(); - const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hello, Hardhat!'], { kind: 'uups' }); + const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hello, Hardhat!'], { + kind: 'uups', + }); try { await upgrades.upgradeBeacon(greeter, GreeterV2Proxiable); diff --git a/packages/plugin-hardhat/test/beacon-upgrade-storage.js b/packages/plugin-hardhat/test/beacon-upgrade-storage.js index dfec7e4a4..d56d0b5c2 100644 --- a/packages/plugin-hardhat/test/beacon-upgrade-storage.js +++ b/packages/plugin-hardhat/test/beacon-upgrade-storage.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.GreeterStorageConflict = await ethers.getContractFactory('GreeterStorageConflict'); }); diff --git a/packages/plugin-hardhat/test/beacon-upgrade-validation.js b/packages/plugin-hardhat/test/beacon-upgrade-validation.js index 23155666e..8cb19e35d 100644 --- a/packages/plugin-hardhat/test/beacon-upgrade-validation.js +++ b/packages/plugin-hardhat/test/beacon-upgrade-validation.js @@ -1,8 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.Invalid = await ethers.getContractFactory('Invalid'); }); @@ -11,10 +23,7 @@ test('invalid upgrade', async t => { const { Greeter, Invalid } = t.context; const beacon = await upgrades.deployBeacon(Greeter); - const greeter = await upgrades.deployBeaconProxy(beacon, Greeter, ['Hola mundo!']); - await t.throwsAsync( - () => upgrades.upgradeProxy(greeter, Invalid), - undefined, - 'Contract `Invalid` is not upgrade safe', - ); + await upgrades.deployBeaconProxy(beacon, Greeter, ['Hola mundo!']); + const error = await t.throwsAsync(() => upgrades.upgradeBeacon(beacon, Invalid)); + t.true(error.message.includes('is not upgrade safe'), error.message); }); diff --git a/packages/plugin-hardhat/test/beacon-with-call.js b/packages/plugin-hardhat/test/beacon-with-call.js index 6a01945a7..99845baad 100644 --- a/packages/plugin-hardhat/test/beacon-with-call.js +++ b/packages/plugin-hardhat/test/beacon-with-call.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); }); test('call with args', async t => { diff --git a/packages/plugin-hardhat/test/constructor.js b/packages/plugin-hardhat/test/constructor.js index dbb2ad35a..6b4220c8f 100644 --- a/packages/plugin-hardhat/test/constructor.js +++ b/packages/plugin-hardhat/test/constructor.js @@ -1,19 +1,30 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.WithConstructor = await ethers.getContractFactory('WithConstructor'); + upgrades = await upgradesFactory(hre, connection); + t.context.WithConstructor = await ethers.getContractFactory('contracts/WithConstructor.sol:WithConstructor'); }); test('new proxy - do not redeploy with same args', async t => { const { WithConstructor } = t.context; - const proxy1 = await upgrades.deployProxy(WithConstructor, { constructorArgs: [17] }); + const proxy1 = await upgrades.deployProxy(WithConstructor, [], { constructorArgs: [17], initializer: false }); const implementation1 = await upgrades.erc1967.getImplementationAddress(await proxy1.getAddress()); t.is(await proxy1.value(), 17n); - const proxy2 = await upgrades.deployProxy(WithConstructor, { constructorArgs: [17] }); + const proxy2 = await upgrades.deployProxy(WithConstructor, [], { constructorArgs: [17], initializer: false }); const implementation2 = await upgrades.erc1967.getImplementationAddress(await proxy2.getAddress()); t.is(await proxy2.value(), 17n); @@ -23,11 +34,11 @@ test('new proxy - do not redeploy with same args', async t => { test('new proxy - redeploy with different args', async t => { const { WithConstructor } = t.context; - const proxy1 = await upgrades.deployProxy(WithConstructor, { constructorArgs: [17] }); + const proxy1 = await upgrades.deployProxy(WithConstructor, [], { constructorArgs: [17], initializer: false }); const implementation1 = await upgrades.erc1967.getImplementationAddress(await proxy1.getAddress()); t.is(await proxy1.value(), 17n); - const proxy2 = await upgrades.deployProxy(WithConstructor, { constructorArgs: [42] }); + const proxy2 = await upgrades.deployProxy(WithConstructor, [], { constructorArgs: [42], initializer: false }); const implementation2 = await upgrades.erc1967.getImplementationAddress(await proxy2.getAddress()); t.is(await proxy2.value(), 42n); @@ -37,11 +48,11 @@ test('new proxy - redeploy with different args', async t => { test('upgrade - do not redeploy with same args', async t => { const { WithConstructor } = t.context; - const proxy1 = await upgrades.deployProxy(WithConstructor, { constructorArgs: [17] }); + const proxy1 = await upgrades.deployProxy(WithConstructor, [], { constructorArgs: [17], initializer: false }); const implementation1 = await upgrades.erc1967.getImplementationAddress(await proxy1.getAddress()); t.is(await proxy1.value(), 17n); - const proxy2 = await upgrades.upgradeProxy(proxy1, WithConstructor, { constructorArgs: [17] }); + const proxy2 = await upgrades.upgradeProxy(proxy1, WithConstructor, { constructorArgs: [17], initializer: false }); const implementation2 = await upgrades.erc1967.getImplementationAddress(await proxy2.getAddress()); t.is(await proxy2.value(), 17n); @@ -51,11 +62,11 @@ test('upgrade - do not redeploy with same args', async t => { test('upgrade - redeploy with different args', async t => { const { WithConstructor } = t.context; - const proxy1 = await upgrades.deployProxy(WithConstructor, { constructorArgs: [17] }); + const proxy1 = await upgrades.deployProxy(WithConstructor, [], { constructorArgs: [17], initializer: false }); const implementation1 = await upgrades.erc1967.getImplementationAddress(await proxy1.getAddress()); t.is(await proxy1.value(), 17n); - const proxy2 = await upgrades.upgradeProxy(proxy1, WithConstructor, { constructorArgs: [42] }); + const proxy2 = await upgrades.upgradeProxy(proxy1, WithConstructor, { constructorArgs: [42], initializer: false }); const implementation2 = await upgrades.erc1967.getImplementationAddress(await proxy2.getAddress()); t.is(await proxy2.value(), 42n); diff --git a/packages/plugin-hardhat/test/defender-contract-instance.js b/packages/plugin-hardhat/test/defender-contract-instance.js index 83932eb83..dee99b22b 100644 --- a/packages/plugin-hardhat/test/defender-contract-instance.js +++ b/packages/plugin-hardhat/test/defender-contract-instance.js @@ -1,20 +1,25 @@ -const test = require('ava'); -const proxyquire = require('proxyquire').noCallThru(); -const sinon = require('sinon'); +import test from 'ava'; +import hre from 'hardhat'; +import esmock from 'esmock'; +import sinon from 'sinon'; -const hre = require('hardhat'); -const { ethers } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; const DEPLOYMENT_ID = 'abc'; test.beforeEach(async t => { - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); }); test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('get contract instance - tx hash not updated', async t => { const { GreeterProxiable } = t.context; @@ -30,12 +35,11 @@ test('get contract instance - tx hash not updated', async t => { const waitStub = sinon.stub(); - const getContractInstance = proxyquire('../dist/utils/contract-instance', { - '../defender/utils': { + const { getContractInstance } = await esmock('../dist/utils/contract-instance.js', { + '../dist/defender/utils.js': { waitForDeployment: waitStub, - '@global': true, }, - }).getContractInstance; + }); const stubbedInstance = await getContractInstance(hre, GreeterProxiable, { useDefenderDeploy: true }, deployment); await stubbedInstance.waitForDeployment(); @@ -65,12 +69,25 @@ test('get contract instance - tx hash updated', async t => { const waitStub = sinon.stub().returns(second.deploymentTransaction().hash); - const getContractInstance = proxyquire('../dist/utils/contract-instance', { - '../defender/utils': { + const { getContractInstance } = await esmock('../dist/utils/contract-instance.js', { + '../dist/defender/utils.js': { waitForDeployment: waitStub, - '@global': true, }, - }).getContractInstance; + }); + + // Stub hre.network.connect to return a provider with mocked getTransaction + const originalConnect = hre.network.connect.bind(hre.network); + const connectStub = sinon.stub(hre.network, 'connect').callsFake(async () => { + const connection = await originalConnect(); + const originalGetTransaction = connection.ethers.provider.getTransaction.bind(connection.ethers.provider); + sinon.stub(connection.ethers.provider, 'getTransaction').callsFake(async hash => { + if (hash === second.deploymentTransaction().hash) { + return second.deploymentTransaction(); + } + return originalGetTransaction(hash); + }); + return connection; + }); const stubbedInstance = await getContractInstance(hre, GreeterProxiable, { useDefenderDeploy: true }, deployment); @@ -87,4 +104,6 @@ test('get contract instance - tx hash updated', async t => { t.not(stubbedInstance.deploymentTransaction().hash, undefined); t.not(stubbedInstance.deploymentTransaction().hash, first.deploymentTransaction().hash); t.is(stubbedInstance.deploymentTransaction().hash, second.deploymentTransaction().hash); + + connectStub.restore(); }); diff --git a/packages/plugin-hardhat/test/defender-deploy-contract.js b/packages/plugin-hardhat/test/defender-deploy-contract.js index 4c3bc093b..edd1a4e14 100644 --- a/packages/plugin-hardhat/test/defender-deploy-contract.js +++ b/packages/plugin-hardhat/test/defender-deploy-contract.js @@ -1,24 +1,25 @@ -const test = require('ava'); -const proxyquire = require('proxyquire').noCallThru(); -const sinon = require('sinon'); +import test from 'ava'; +import hre from 'hardhat'; +import esmock from 'esmock'; +import sinon from 'sinon'; -const hre = require('hardhat'); -const { ethers } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; const ADDR = '0x1'; const TX_HASH = '0x0000000000000000000000000000000000000000000000000000000000000002'; test.before(async t => { t.context.NonUpgradeable = await ethers.getContractFactory('NonUpgradeable'); - t.context.WithConstructor = await ethers.getContractFactory('WithConstructor'); + t.context.WithConstructor = await ethers.getContractFactory('contracts/WithConstructor.sol:WithConstructor'); t.context.IsInitializable = await ethers.getContractFactory('IsInitializable'); t.context.IsInitializableUpgradeable = await ethers.getContractFactory('IsInitializableUpgradeable'); t.context.IsUUPS = await ethers.getContractFactory('IsUUPS'); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); - t.context.deployContract = proxyquire('../dist/deploy-contract', { - './defender/deploy': { - defenderDeploy: async () => { + const { makeDeployContract } = await esmock('../dist/deploy-contract.js', { + '../dist/utils/index.js': { + deploy: async () => { return { address: ADDR, txHash: TX_HASH, @@ -26,15 +27,19 @@ test.before(async t => { remoteDeploymentId: 'abc', }; }, - '@global': true, }, - }).makeDeployContract(hre, true); + }); + t.context.deployContract = makeDeployContract(hre, true, connection); }); test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('deploy contract', async t => { const { deployContract, NonUpgradeable } = t.context; const inst = await deployContract(NonUpgradeable); @@ -102,9 +107,9 @@ test('await deployed contract', async t => { const precreated = await NonUpgradeable.deploy(); - const deployContract = proxyquire('../dist/deploy-contract', { - './defender/deploy': { - defenderDeploy: async () => { + const { makeDeployContract } = await esmock('../dist/deploy-contract.js', { + '../dist/utils/index.js': { + deploy: async () => { return { address: await precreated.getAddress(), txHash: precreated.deploymentTransaction().hash, @@ -112,9 +117,17 @@ test('await deployed contract', async t => { remoteDeploymentId: 'abc', }; }, - '@global': true, }, - }).makeDeployContract(hre, true); + '../dist/utils/contract-instance.js': { + getContractInstance: (hre, contract, opts, deployment) => { + const instance = contract.attach(deployment.address); + // @ts-ignore + instance.deploymentTransaction = () => deployment.deployTransaction ?? null; + return instance; + }, + }, + }); + const deployContract = makeDeployContract(hre, true, connection); const inst = await deployContract(NonUpgradeable); t.is(await inst.getAddress(), await precreated.getAddress()); @@ -129,9 +142,9 @@ test('deployed calls wait for deployment', async t => { // just predeploy a contract so that it exists on the network const deployed = await NonUpgradeable.deploy(); - const deployContract = proxyquire('../dist/deploy-contract', { - './defender/deploy': { - defenderDeploy: async () => { + const { makeDeployContract } = await esmock('../dist/deploy-contract.js', { + '../dist/utils/index.js': { + deploy: async () => { return { address: await deployed.getAddress(), txHash: TX_HASH, @@ -139,19 +152,22 @@ test('deployed calls wait for deployment', async t => { remoteDeploymentId: 'abc', }; }, - '@global': true, }, - './defender/utils': { - waitForDeployment: stub, - enableDefender: (hre, defenderModule, opts) => { - return { - ...opts, - useDefenderDeploy: true, + '../dist/utils/contract-instance.js': { + getContractInstance: (hre, contract, opts, deployment) => { + const instance = contract.attach(deployment.address); + // @ts-ignore + instance.deploymentTransaction = () => deployment.deployTransaction ?? null; + const origWait = instance.waitForDeployment.bind(instance); + instance.waitForDeployment = async () => { + await stub(); + return await origWait(); }; + return instance; }, - '@global': true, }, - }).makeDeployContract(hre, true); + }); + const deployContract = makeDeployContract(hre, true, connection); const inst = await deployContract(NonUpgradeable); t.is(await inst.getAddress(), await deployed.getAddress()); diff --git a/packages/plugin-hardhat/test/defender-deploy-implementation.js b/packages/plugin-hardhat/test/defender-deploy-implementation.js index 0b585c37c..942ec3767 100644 --- a/packages/plugin-hardhat/test/defender-deploy-implementation.js +++ b/packages/plugin-hardhat/test/defender-deploy-implementation.js @@ -1,27 +1,37 @@ -const test = require('ava'); -const proxyquire = require('proxyquire').noCallThru(); +import test from 'ava'; +import hre from 'hardhat'; +import esmock from 'esmock'; +import manifest from '@openzeppelin/upgrades-core/dist/manifest.js'; +import { mockDeploy } from '../dist/test-utils/mock-deploy.js'; -const hre = require('hardhat'); -const { ethers } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; -const manifest = require('@openzeppelin/upgrades-core/dist/manifest'); +test.after.always(async () => { + await connection.close(); +}); -test.before(async t => { - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); +test.beforeEach(async t => { + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); t.context.Invalid = await ethers.getContractFactory('Invalid'); - t.context.deployImplementation = proxyquire('../dist/deploy-implementation', { - './utils/deploy': { - deploy: async (hre, opts, factory, ...args) => { - opts.useDefenderDeploy = false; - return { - // just do regular deploy but add a deployment id - ...(await require('../dist/utils/deploy').deploy(hre, opts, factory, ...args)), - remoteDeploymentId: 'abc', - }; + + // Mock at the deploy-implementation level AND at the deploy-impl level + const module = await esmock( + '../dist/deploy-implementation.js', + { + '../dist/utils/deploy.js': { + deploy: mockDeploy, }, - '@global': true, }, - }).makeDeployImplementation(hre, true); + { + // This is the third parameter - global mocks that apply to all imports + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, + }, + ); + + t.context.deployImplementation = module.makeDeployImplementation(hre, true, connection); }); test('deploy implementation', async t => { diff --git a/packages/plugin-hardhat/test/defender-deploy-proxy.js b/packages/plugin-hardhat/test/defender-deploy-proxy.js index d8677eef9..9fbfee1b3 100644 --- a/packages/plugin-hardhat/test/defender-deploy-proxy.js +++ b/packages/plugin-hardhat/test/defender-deploy-proxy.js @@ -1,45 +1,77 @@ -const test = require('ava'); -const proxyquire = require('proxyquire').noCallThru(); -const sinon = require('sinon'); - -const hre = require('hardhat'); -const { ethers } = hre; - -const manifest = require('@openzeppelin/upgrades-core/dist/manifest'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +// defender as defenderFactory +import esmock from 'esmock'; +import sinon from 'sinon'; +import manifest from '@openzeppelin/upgrades-core/dist/manifest.js'; +import { mockDeploy as baseMockDeploy } from '../dist/test-utils/mock-deploy.js'; + +const connection = await hre.network.connect(); +const { ethers } = connection; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; +/** @type {import('@openzeppelin/hardhat-upgrades').DefenderHardhatUpgrades} */ +// let defender; const IMPL_ID = 'abc'; const PROXY_TX_HASH = '0x2'; const PROXY_ID = 'def'; +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); + // defender = await defenderFactory(hre, connection); +}); + test.beforeEach(async t => { const stub = sinon.stub(); stub.onCall(0).returns(IMPL_ID); stub.onCall(1).returns(PROXY_ID); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); - t.context.deployProxy = proxyquire('../dist/deploy-proxy', { - './utils/deploy': { - deploy: async (hre, opts, factory, ...args) => { - opts.useDefenderDeploy = false; - return { - // just do regular deploy but add a deployment id - ...(await require('../dist/utils/deploy').deploy(hre, opts, factory, ...args)), - remoteDeploymentId: stub(), - }; + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.Invalid = await ethers.getContractFactory('Invalid'); + + // Create a wrapper around the shared mock deploy function to use the stub + const mockDeploy = async (hre, opts, factory, ...args) => { + const result = await baseMockDeploy(hre, opts, factory, ...args); + return { + ...result, + remoteDeploymentId: stub(), + }; + }; + + const module = await esmock( + '../dist/deploy-proxy.js', + { + '../dist/utils/deploy.js': { + deploy: mockDeploy, }, - '@global': true, }, - }).makeDeployProxy(hre, true); + { + // Global mocks + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, + }, + ); + + t.context.deployProxy = module.makeDeployProxy(hre, true, connection); }); test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('deploy proxy', async t => { const { deployProxy, GreeterProxiable } = t.context; + const signer = await ethers.provider.getSigner(); const inst = await deployProxy(GreeterProxiable, ['Hello World']); t.not(await inst.getAddress(), undefined); @@ -51,7 +83,7 @@ test('deploy proxy', async t => { t.is(proxy.remoteDeploymentId, PROXY_ID); const impl = await m.getDeploymentFromAddress( - await hre.upgrades.erc1967.getImplementationAddress(await inst.getAddress()), + await upgrades.erc1967.getImplementationAddress(await inst.getAddress()), ); t.is(impl.remoteDeploymentId, IMPL_ID); }); @@ -69,7 +101,8 @@ test('deployed calls wait for deployment', async t => { // predeploy a proxy normally for two reasons: // 1. so we have a real address // 2. so it predeploys the implementation since we are assuming the impl is being deployed by Defender - const realProxy = await hre.upgrades.deployProxy(GreeterProxiable, ['Hello World']); + const signer = await ethers.provider.getSigner(); + const realProxy = await upgrades.deployProxy(GreeterProxiable, ['Hello World']); // stub proxy deployment const deployStub = sinon.stub(); @@ -83,23 +116,42 @@ test('deployed calls wait for deployment', async t => { // stub the waitForDeployment function const waitStub = sinon.stub(); - const deployProxy = proxyquire('../dist/deploy-proxy', { - './defender/deploy': { - defenderDeploy: deployStub, - '@global': true, + const module = await esmock( + '../dist/deploy-proxy.js', + { + '../dist/defender/deploy.js': { + defenderDeploy: deployStub, + }, + '../dist/defender/utils.js': { + waitForDeployment: waitStub, + enableDefender: (hre, defenderModule, opts) => { + return { + ...opts, + useDefenderDeploy: true, + }; + }, + }, }, - './defender/utils': { - waitForDeployment: waitStub, - enableDefender: (hre, defenderModule, opts) => { - return { - ...opts, - useDefenderDeploy: true, - }; + { + // Global mocks + '../dist/defender/deploy.js': { + defenderDeploy: deployStub, + }, + '../dist/defender/utils.js': { + waitForDeployment: waitStub, + enableDefender: (hre, defenderModule, opts) => { + return { + ...opts, + useDefenderDeploy: true, + }; + }, }, - '@global': true, }, - }).makeDeployProxy(hre, true); + ); + + const deployProxy = module.makeDeployProxy(hre, true, connection); + const signer2 = await ethers.provider.getSigner(); const inst = await deployProxy(GreeterProxiable, ['Hello World']); await inst.waitForDeployment(); diff --git a/packages/plugin-hardhat/test/defender-deploy.js b/packages/plugin-hardhat/test/defender-deploy.js index 7d2c9580b..5a3a5d5d8 100644 --- a/packages/plugin-hardhat/test/defender-deploy.js +++ b/packages/plugin-hardhat/test/defender-deploy.js @@ -1,18 +1,26 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); - -const hre = require('hardhat'); -const { ethers } = hre; - -const { +import test from 'ava'; +import hre from 'hardhat'; +import { defender as defenderFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import esmock from 'esmock'; +import { getProxyFactory, getBeaconProxyFactory, getTransparentUpgradeableProxyFactory, -} = require('../dist/utils/factories'); +} from '../dist/utils/factories.js'; +import { getCombinedBuildInfo } from '../dist/utils/artifacts.js'; +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); + const artifactsBuildInfo = require('@openzeppelin/upgrades-core/artifacts/build-info-v5.json'); +import { AbiCoder } from 'ethers'; +import * as defenderUtils from '../dist/defender/utils.js'; -const { AbiCoder } = require('ethers'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +const defender = await defenderFactory(hre, connection); const TX_HASH = '0x1'; const DEPLOYMENT_ID = 'abc'; @@ -43,32 +51,51 @@ test.beforeEach(async t => { }; t.context.spy = sinon.spy(t.context.fakeDefenderClient, 'deployContract'); - t.context.deploy = proxyquire('../dist/defender/deploy', { - './utils': { - ...require('../dist/defender/utils'), + t.context.deploy = await esmock('../dist/defender/deploy.js', { + '../dist/defender/utils.js': { + ...defenderUtils, getNetwork: () => t.context.fakeChainId, }, - './client': { + '../dist/defender/client.js': { getDeployClient: () => t.context.fakeDefenderClient, }, - '../utils/etherscan-api': { - getEtherscanAPIConfig: () => { - return { key: ETHERSCAN_API_KEY }; + }); + + // Mock verification.etherscan for Hardhat 3 (hardhat-verify v3.0.10+) + const mockEtherscan = { + customApiCall: sinon.stub().resolves({ status: '1', message: 'OK', result: null }), + verify: sinon.stub().resolves({ message: 'guid' }), + getVerificationStatus: sinon.stub().resolves({ success: true, message: 'OK' }), + }; + + // Create a mock connection with mocked provider + const fakeConnection = { + ...connection, + verification: { + etherscan: mockEtherscan, + }, + ethers: { + ...connection.ethers, + provider: { + ...connection.ethers.provider, + getTransaction: async () => TX_RESPONSE, }, + getAddress: address => address, }, - }); + }; t.context.fakeHre = { - artifacts: hre.artifacts, + artifacts: hre.artifacts, // Use real artifacts object which has getBuildInfo config: hre.config, ethers: { provider: { - getTransaction: () => 'mocked response', + getTransaction: async () => TX_RESPONSE, }, getAddress: address => address, }, network: { provider: { send: async () => t.context.fakeChainId }, + connect: async () => fakeConnection, // Hardhat 3: network.connect() returns connection with mocked provider }, }; }); @@ -77,6 +104,10 @@ test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + function assertResult(t, result) { t.deepEqual(result, { address: ADDRESS, @@ -95,7 +126,7 @@ test('calls defender deploy', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, {}); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -125,7 +156,7 @@ test('calls defender deploy with relayerId', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, { relayerId: RELAYER_ID }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -155,7 +186,7 @@ test('calls defender deploy with salt', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, { salt: SALT }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -185,7 +216,7 @@ test('calls defender deploy with createFactoryAddress', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, { createFactoryAddress: CREATE_FACTORY }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -215,7 +246,7 @@ test('calls defender deploy with license', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, {}); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -247,7 +278,7 @@ test('calls defender deploy - licenseType', async t => { licenseType: 'My License Type', // not a valid type, but this just sets the option }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -279,7 +310,7 @@ test('calls defender deploy - verifySourceCode false', async t => { verifySourceCode: false, }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -311,7 +342,7 @@ test('calls defender deploy - skipLicenseType', async t => { skipLicenseType: true, }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -371,7 +402,7 @@ test('calls defender deploy - error - unrecognized license', async t => { const contractName = 'UnrecognizedLicense'; - const factory = await ethers.getContractFactory(contractName); + const factory = await ethers.getContractFactory('contracts/UnrecognizedLicense.sol:UnrecognizedLicense'); const error = await t.throwsAsync(() => deploy.defenderDeploy(fakeHre, factory, {})); t.true( error?.message.includes( @@ -391,10 +422,10 @@ test('calls defender deploy - no contract license', async t => { const contractPath = 'contracts/NoLicense.sol'; const contractName = 'NoLicense'; - const factory = await ethers.getContractFactory(contractName); + const factory = await ethers.getContractFactory('contracts/NoLicense.sol:NoLicense'); const result = await deploy.defenderDeploy(fakeHre, factory, {}); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -421,10 +452,10 @@ test('calls defender deploy - unlicensed', async t => { const contractPath = 'contracts/Unlicensed.sol'; const contractName = 'Unlicensed'; - const factory = await ethers.getContractFactory(contractName); + const factory = await ethers.getContractFactory('contracts/Unlicensed.sol:Unlicensed'); const result = await deploy.defenderDeploy(fakeHre, factory, {}); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -448,13 +479,13 @@ test('calls defender deploy - unlicensed', async t => { test('calls defender deploy with constructor args', async t => { const { spy, deploy, fakeHre, fakeChainId } = t.context; - const contractPath = 'contracts/Constructor.sol'; + const contractPath = 'contracts/WithConstructor.sol'; const contractName = 'WithConstructor'; - const factory = await ethers.getContractFactory(contractName); + const factory = await ethers.getContractFactory('contracts/WithConstructor.sol:WithConstructor'); const result = await deploy.defenderDeploy(fakeHre, factory, {}, 10); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -478,13 +509,13 @@ test('calls defender deploy with constructor args', async t => { test('calls defender deploy with constructor args with array', async t => { const { spy, deploy, fakeHre, fakeChainId } = t.context; - const contractPath = 'contracts/Constructor.sol'; + const contractPath = 'contracts/WithConstructor.sol'; const contractName = 'WithConstructorArray'; - const factory = await ethers.getContractFactory(contractName); + const factory = await ethers.getContractFactory('contracts/WithConstructor.sol:WithConstructorArray'); const result = await deploy.defenderDeploy(fakeHre, factory, {}, [1, 2, 3]); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -514,7 +545,7 @@ test('calls defender deploy with verify false', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, { verifySourceCode: false }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -540,7 +571,7 @@ test('calls defender deploy with ERC1967Proxy', async t => { const contractPath = '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol'; const contractName = 'ERC1967Proxy'; - const factory = await getProxyFactory(hre); + const factory = await getProxyFactory(connection); const result = await deploy.defenderDeploy(fakeHre, factory, {}, LOGIC_ADDRESS, DATA); assertResult(t, result); @@ -568,7 +599,7 @@ test('calls defender deploy with ERC1967Proxy - ignores constructorArgs', async const contractPath = '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol'; const contractName = 'ERC1967Proxy'; - const factory = await getProxyFactory(hre); + const factory = await getProxyFactory(connection); const result = await deploy.defenderDeploy(fakeHre, factory, { constructorArgs: ['foo'] }, LOGIC_ADDRESS, DATA); assertResult(t, result); @@ -596,7 +627,7 @@ test('calls defender deploy with ERC1967Proxy - ignores empty constructorArgs', const contractPath = '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol'; const contractName = 'ERC1967Proxy'; - const factory = await getProxyFactory(hre); + const factory = await getProxyFactory(connection); const result = await deploy.defenderDeploy(fakeHre, factory, { constructorArgs: [] }, LOGIC_ADDRESS, DATA); assertResult(t, result); @@ -624,7 +655,7 @@ test('calls defender deploy with BeaconProxy', async t => { const contractPath = '@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol'; const contractName = 'BeaconProxy'; - const factory = await getBeaconProxyFactory(hre); + const factory = await getBeaconProxyFactory(connection); const result = await deploy.defenderDeploy(fakeHre, factory, {}, LOGIC_ADDRESS, DATA); assertResult(t, result); @@ -652,7 +683,7 @@ test('calls defender deploy with TransparentUpgradeableProxy', async t => { const contractPath = '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol'; const contractName = 'TransparentUpgradeableProxy'; - const factory = await getTransparentUpgradeableProxyFactory(hre); + const factory = await getTransparentUpgradeableProxyFactory(connection); const result = await deploy.defenderDeploy(fakeHre, factory, {}, LOGIC_ADDRESS, INITIAL_OWNER_ADDRESS, DATA); assertResult(t, result); @@ -687,7 +718,7 @@ test('calls defender deploy with txOverrides.gasLimit', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, { txOverrides: { gasLimit: 1 } }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -722,7 +753,7 @@ test('calls defender deploy with txOverrides.gasPrice', async t => { const factory = await ethers.getContractFactory(contractName); const result = await deploy.defenderDeploy(fakeHre, factory, { txOverrides: { gasPrice: 1 } }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -759,7 +790,7 @@ test('calls defender deploy with txOverrides.maxFeePerGas and txOverrides.maxPri txOverrides: { maxFeePerGas: 100, maxPriorityFeePerGas: '0xa' }, }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -798,25 +829,34 @@ test('calls defender deploy with external library', async t => { }); const result = await deploy.defenderDeploy(fakeHre, factory, {}); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); - sinon.assert.calledWithExactly(spy, { - contractName: contractName, - contractPath: contractPath, - network: fakeChainId, - artifactPayload: JSON.stringify(buildInfo), - licenseType: undefined, - constructorBytecode: '0x', - verifySourceCode: true, - relayerId: undefined, - salt: undefined, - createFactoryAddress: undefined, - txOverrides: undefined, - libraries: { - 'contracts/ExternalLibraries.sol:SafeMath': EXTERNAL_LIBRARY_ADDRESS, - }, - metadata: undefined, - origin: 'Hardhat', - }); + // Get the actual call arguments + t.is(spy.callCount, 1); + const actualCall = spy.getCall(0); + const actualArgs = actualCall.args[0]; + + // Verify the call arguments + t.is(actualArgs.contractName, contractName); + t.is(actualArgs.contractPath, contractPath); + t.is(actualArgs.network, fakeChainId); + t.is(actualArgs.licenseType, undefined); + t.is(actualArgs.constructorBytecode, '0x'); + t.is(actualArgs.verifySourceCode, true); + t.is(actualArgs.relayerId, undefined); + t.is(actualArgs.salt, undefined); + t.is(actualArgs.createFactoryAddress, undefined); + t.is(actualArgs.txOverrides, undefined); + // Libraries may have project/ prefix in Hardhat 3 + t.truthy(actualArgs.libraries); + const libraryKey = Object.keys(actualArgs.libraries).find(k => k.includes('SafeMath') && !k.includes('SafeMathV2')); + t.truthy(libraryKey); + t.is(actualArgs.libraries[libraryKey], EXTERNAL_LIBRARY_ADDRESS); + t.is(actualArgs.metadata, undefined); + t.is(actualArgs.origin, 'Hardhat'); + + // Verify buildInfo structure (may have project/ prefix in sources) + const actualBuildInfo = JSON.parse(actualArgs.artifactPayload); + t.truthy(actualBuildInfo); + t.is(actualBuildInfo.solcVersion, '0.5.17'); assertResult(t, result); }); @@ -835,26 +875,38 @@ test('calls defender deploy with multiple external libraries', async t => { }); const result = await deploy.defenderDeploy(fakeHre, factory, {}); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); - sinon.assert.calledWithExactly(spy, { - contractName: contractName, - contractPath: contractPath, - network: fakeChainId, - artifactPayload: JSON.stringify(buildInfo), - licenseType: undefined, - constructorBytecode: '0x', - verifySourceCode: true, - relayerId: undefined, - salt: undefined, - createFactoryAddress: undefined, - txOverrides: undefined, - libraries: { - 'contracts/ExternalLibraries.sol:SafeMath': EXTERNAL_LIBRARY_ADDRESS, - 'contracts/ExternalLibraries.sol:SafeMathV2': EXTERNAL_LIBRARY_2_ADDRESS, - }, - metadata: undefined, - origin: 'Hardhat', - }); + // Get the actual call arguments + t.is(spy.callCount, 1); + const actualCall = spy.getCall(0); + const actualArgs = actualCall.args[0]; + + // Verify the call arguments + t.is(actualArgs.contractName, contractName); + t.is(actualArgs.contractPath, contractPath); + t.is(actualArgs.network, fakeChainId); + t.is(actualArgs.licenseType, undefined); + t.is(actualArgs.constructorBytecode, '0x'); + t.is(actualArgs.verifySourceCode, true); + t.is(actualArgs.relayerId, undefined); + t.is(actualArgs.salt, undefined); + t.is(actualArgs.createFactoryAddress, undefined); + t.is(actualArgs.txOverrides, undefined); + // Libraries may have project/ prefix in Hardhat 3 + t.truthy(actualArgs.libraries); + const libraryKey1 = Object.keys(actualArgs.libraries).find(k => k.includes('SafeMath') && !k.includes('SafeMathV2')); + const libraryKey2 = Object.keys(actualArgs.libraries).find(k => k.includes('SafeMathV2')); + t.truthy(libraryKey1); + t.truthy(libraryKey2); + t.is(actualArgs.libraries[libraryKey1], EXTERNAL_LIBRARY_ADDRESS); + t.is(actualArgs.libraries[libraryKey2], EXTERNAL_LIBRARY_2_ADDRESS); + t.is(Object.keys(actualArgs.libraries).length, 2); + t.is(actualArgs.metadata, undefined); + t.is(actualArgs.origin, 'Hardhat'); + + // Verify buildInfo structure (may have project/ prefix in sources) + const actualBuildInfo = JSON.parse(actualArgs.artifactPayload); + t.truthy(actualBuildInfo); + t.is(actualBuildInfo.solcVersion, '0.5.17'); assertResult(t, result); }); @@ -874,7 +926,7 @@ test('calls defender deploy with metadata', async t => { }, }); - const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + const buildInfo = await getCombinedBuildInfo(hre.artifacts, `${contractPath}:${contractName}`); sinon.assert.calledWithExactly(spy, { contractName: contractName, contractPath: contractPath, @@ -950,19 +1002,14 @@ async function testGetDeployedContractPolling(t, getDeployedContractStub, expect }; const deployContractSpy = sinon.spy(defenderClientWaits, 'deployContract'); - const deployPending = proxyquire('../dist/defender/deploy', { - './utils': { - ...require('../dist/defender/utils'), + const deployPending = await esmock('../dist/defender/deploy.js', { + '../dist/defender/utils.js': { + ...defenderUtils, getNetwork: () => fakeChainId, }, - './client': { + '../dist/defender/client.js': { getDeployClient: () => defenderClientWaits, }, - '../utils/etherscan-api': { - getEtherscanAPIConfig: () => { - return { key: ETHERSCAN_API_KEY }; - }, - }, }); const factory = await ethers.getContractFactory(contractName); diff --git a/packages/plugin-hardhat/test/defender-get-approval-process.js b/packages/plugin-hardhat/test/defender-get-approval-process.js index e09a74441..6ebf2f196 100644 --- a/packages/plugin-hardhat/test/defender-get-approval-process.js +++ b/packages/plugin-hardhat/test/defender-get-approval-process.js @@ -1,9 +1,11 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); +import test from 'ava'; +import hre from 'hardhat'; +import { defender as defenderFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import esmock from 'esmock'; -const { defender } = require('hardhat'); -const hre = require('hardhat'); +const connection = await hre.network.connect(); +const defender = await defenderFactory(hre, connection); const APPROVAL_PROCESS_ID = 'abc-def'; const MULTISIG_ADDRESS = '0x123'; @@ -17,12 +19,13 @@ test.beforeEach(async t => { getUpgradeApprovalProcess: sinon.stub(), }; - const mockedGetApprovalProcess = proxyquire('../dist/defender/get-approval-process', { - './utils': { - ...require('../dist/defender/utils'), + const { getNetwork: _getNetwork, ...otherDefenderUtils } = await import('../dist/defender/utils.js'); + const mockedGetApprovalProcess = await esmock('../dist/defender/get-approval-process.js', { + '../dist/defender/utils.js': { + ...otherDefenderUtils, getNetwork: () => t.context.fakeChainId, }, - './client': { + '../dist/defender/client.js': { getDeployClient: () => t.context.fakeDefenderClient, }, }); @@ -36,6 +39,10 @@ test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('getDeployApprovalProcess', async t => { const { fakeChainId, fakeDefenderClient, getDeployApprovalProcess } = t.context; diff --git a/packages/plugin-hardhat/test/defender-hardhat-setup.js b/packages/plugin-hardhat/test/defender-hardhat-setup.js index cce2a7181..bb95a5456 100644 --- a/packages/plugin-hardhat/test/defender-hardhat-setup.js +++ b/packages/plugin-hardhat/test/defender-hardhat-setup.js @@ -1,6 +1,13 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { defender as defenderFactory } from '@openzeppelin/hardhat-upgrades'; -const { defender } = require('hardhat'); +const connection = await hre.network.connect(); +const defender = await defenderFactory(hre, connection); + +test.after.always(async () => { + await connection.close(); +}); test('creates defender object in hardhat runtime', async t => { t.is(typeof defender.proposeUpgradeWithApproval, 'function'); diff --git a/packages/plugin-hardhat/test/defender-utils.js b/packages/plugin-hardhat/test/defender-utils.js index 3c26d350d..8c75636d6 100644 --- a/packages/plugin-hardhat/test/defender-utils.js +++ b/packages/plugin-hardhat/test/defender-utils.js @@ -1,8 +1,9 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); -const { disableDefender, enableDefender } = require('../dist/defender/utils'); -const { getDeployClient } = require('../dist/defender/client'); +import test from 'ava'; + +import sinon from 'sinon'; +import esmock from 'esmock'; +import { disableDefender, enableDefender } from '../dist/defender/utils.js'; +import { getDeployClient } from '../dist/defender/client.js'; test.beforeEach(async t => { t.context.fakeChainId = '0x01'; @@ -29,13 +30,18 @@ test('getNetwork finds network', async t => { }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); - const network = await utils.getNetwork(t.context.fakeHre); + const fakeConnection = { + networkConfig: { chainId: 1 }, + ethers: { provider: {} }, + }; + + const network = await utils.getNetwork(t.context.fakeHre, fakeConnection); t.is(network, 'mainnet'); }); @@ -51,13 +57,18 @@ test('getNetwork cannot find network', async t => { }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); - await t.throwsAsync(() => utils.getNetwork(t.context.fakeHre), { + const fakeConnection = { + networkConfig: { chainId: 0x123456 }, + ethers: { provider: {} }, + }; + + await t.throwsAsync(() => utils.getNetwork(t.context.fakeHre, fakeConnection), { message: /The current network with chainId \d+ is not supported/, }); }); @@ -83,13 +94,18 @@ test('getNetworks finds forked network', async t => { }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); - const network = await utils.getNetwork(t.context.fakeHre); + const fakeConnection = { + networkConfig: { chainId: 0x123456 }, + ethers: { provider: {} }, + }; + + const network = await utils.getNetwork(t.context.fakeHre, fakeConnection); t.is(network, 'my-forked-network'); }); @@ -110,13 +126,18 @@ test('getNetwork finds private network', async t => { }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); - const network = await utils.getNetwork(t.context.fakeHre); + const fakeConnection = { + networkConfig: { chainId: 0x123456 }, + ethers: { provider: {} }, + }; + + const network = await utils.getNetwork(t.context.fakeHre, fakeConnection); t.is(network, 'my-private-network'); }); @@ -141,13 +162,18 @@ test('getNetworks finds multiple networks', async t => { }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); - await t.throwsAsync(() => utils.getNetwork(t.context.fakeHre), { + const fakeConnection = { + networkConfig: { chainId: 0x123456 }, + ethers: { provider: {} }, + }; + + await t.throwsAsync(() => utils.getNetwork(t.context.fakeHre, fakeConnection), { message: /Detected multiple networks with the same chainId \d+ on OpenZeppelin Defender: first-forked-network, second-forked-network/, }); @@ -170,8 +196,8 @@ test('getNetworks finds one network, does not match specified network', async t }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); @@ -184,7 +210,12 @@ test('getNetworks finds one network, does not match specified network', async t }, }; - await t.throwsAsync(() => utils.getNetwork(hreWithDefenderNetwork), { + const fakeConnection = { + networkConfig: { chainId: 0x123456 }, + ethers: { provider: {} }, + }; + + await t.throwsAsync(() => utils.getNetwork(hreWithDefenderNetwork, fakeConnection), { message: /Detected network my-forked-network does not match specified network: specified-network/, }); }); @@ -210,8 +241,8 @@ test('getNetworks finds multiple network, does not match specified network', asy }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); @@ -224,7 +255,12 @@ test('getNetworks finds multiple network, does not match specified network', asy }, }; - await t.throwsAsync(() => utils.getNetwork(hreWithDefenderNetwork), { + const fakeConnection = { + networkConfig: { chainId: 0x123456 }, + ethers: { provider: {} }, + }; + + await t.throwsAsync(() => utils.getNetwork(hreWithDefenderNetwork, fakeConnection), { message: /Specified network specified-network does not match any of the detected networks for chainId 1193046: my-forked-network, my-forked-network-2/, }); @@ -251,8 +287,8 @@ test('getNetworks finds multiple networks, includes specified network', async t }, }; - const utils = proxyquire('../dist/defender/utils', { - './client': { + const utils = await esmock('../dist/defender/utils.js', { + '../dist/defender/client.js': { getNetworkClient: () => fakeNetworkClient, }, }); @@ -265,7 +301,12 @@ test('getNetworks finds multiple networks, includes specified network', async t }, }; - const network = await utils.getNetwork(hreWithDefenderNetwork); + const fakeConnection = { + networkConfig: { chainId: 0x123456 }, + ethers: { provider: {} }, + }; + + const network = await utils.getNetwork(hreWithDefenderNetwork, fakeConnection); t.is(network, 'specified-network'); }); diff --git a/packages/plugin-hardhat/test/implementation-functions.js b/packages/plugin-hardhat/test/implementation-functions.js index 21bcd9e5f..8c0fc8814 100644 --- a/packages/plugin-hardhat/test/implementation-functions.js +++ b/packages/plugin-hardhat/test/implementation-functions.js @@ -1,12 +1,23 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2Proxiable = await ethers.getContractFactory('GreeterV2Proxiable'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2Proxiable = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); t.context.Invalid = await ethers.getContractFactory('Invalid'); t.context.GreeterStorageConflict = await ethers.getContractFactory('GreeterStorageConflict'); t.context.GreeterStorageConflictProxiable = await ethers.getContractFactory('GreeterStorageConflictProxiable'); @@ -155,14 +166,20 @@ test('validate upgrade transparent - incompatible storage - forced', async t => test('validate upgrade uups - happy path', async t => { const { GreeterProxiable, GreeterV2Proxiable } = t.context; - const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!'], { kind: 'uups' }); + const signer = await ethers.provider.getSigner(); + const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!'], { + kind: 'uups', + }); await upgrades.validateUpgrade(greeter, GreeterV2Proxiable); }); test('validate upgrade uups - incompatible storage', async t => { const { GreeterProxiable, GreeterStorageConflictProxiable } = t.context; - const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!'], { kind: 'uups' }); + const signer = await ethers.provider.getSigner(); + const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!'], { + kind: 'uups', + }); await t.throwsAsync(() => upgrades.validateUpgrade(greeter, GreeterStorageConflictProxiable), { message: /(New storage layout is incompatible)/, }); @@ -171,13 +188,17 @@ test('validate upgrade uups - incompatible storage', async t => { test('validate upgrade uups - incompatible storage - forced', async t => { const { GreeterProxiable, GreeterStorageConflictProxiable } = t.context; - const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!'], { kind: 'uups' }); + const signer = await ethers.provider.getSigner(); + const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!'], { + kind: 'uups', + }); await upgrades.validateUpgrade(greeter, GreeterStorageConflictProxiable, { unsafeSkipStorageCheck: true }); }); test('validate upgrade uups - wrong kind', async t => { const { GreeterProxiable, GreeterV2 } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!']); await t.throwsAsync(() => upgrades.validateUpgrade(greeter, GreeterV2), { message: /(Requested an upgrade of kind transparent but proxy is uups)/, @@ -187,6 +208,7 @@ test('validate upgrade uups - wrong kind', async t => { test('validate upgrade uups - no upgrade function', async t => { const { GreeterProxiable, GreeterV2 } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(GreeterProxiable, ['Hola mundo!']); await t.throwsAsync(() => upgrades.validateUpgrade(greeter, GreeterV2, { kind: 'uups' }), { message: getUpgradeUnsafeRegex('GreeterV2'), diff --git a/packages/plugin-hardhat/test/import-v4.js b/packages/plugin-hardhat/test/import-v4.js index 2d1c41a46..70c3a7e25 100644 --- a/packages/plugin-hardhat/test/import-v4.js +++ b/packages/plugin-hardhat/test/import-v4.js @@ -1,18 +1,30 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); - const ERC1967Proxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'); - const BeaconProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'); const UpgradableBeacon = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable40'); t.context.GreeterV2Proxiable = await ethers.getContractFactory('GreeterV2Proxiable40'); diff --git a/packages/plugin-hardhat/test/import-v5.js b/packages/plugin-hardhat/test/import-v5.js index dc36750e6..edb8915a2 100644 --- a/packages/plugin-hardhat/test/import-v5.js +++ b/packages/plugin-hardhat/test/import-v5.js @@ -1,20 +1,31 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); +const require = createRequire(import.meta.url); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); - const ERC1967Proxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'); - const BeaconProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'); const UpgradableBeacon = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2Proxiable = await ethers.getContractFactory('GreeterV2Proxiable'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2Proxiable = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); t.context.GreeterV3Proxiable = await ethers.getContractFactory('GreeterV3Proxiable'); t.context.CustomProxy = await ethers.getContractFactory('CustomProxy'); t.context.CustomProxyWithAdmin = await ethers.getContractFactory('CustomProxyWithAdmin'); diff --git a/packages/plugin-hardhat/test/import-with-deploy-v4.js b/packages/plugin-hardhat/test/import-with-deploy-v4.js index 31177f0de..2d5ef9d15 100644 --- a/packages/plugin-hardhat/test/import-with-deploy-v4.js +++ b/packages/plugin-hardhat/test/import-with-deploy-v4.js @@ -1,15 +1,28 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); - const ERC1967Proxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable40'); t.context.GreeterV2Proxiable = await ethers.getContractFactory('GreeterV2Proxiable40'); diff --git a/packages/plugin-hardhat/test/import-with-deploy-v5.js b/packages/plugin-hardhat/test/import-with-deploy-v5.js index 2158e7fd0..494706cb6 100644 --- a/packages/plugin-hardhat/test/import-with-deploy-v5.js +++ b/packages/plugin-hardhat/test/import-with-deploy-v5.js @@ -1,17 +1,30 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); - const ERC1967Proxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2Proxiable = await ethers.getContractFactory('GreeterV2Proxiable'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2Proxiable = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); t.context.TransparentUpgradableProxy = await ethers.getContractFactory( @@ -75,7 +88,7 @@ test('deploy then import with same impl', async t => { // upgrade imported proxy to different impl await upgrades.upgradeProxy(greeter2, GreeterV2Proxiable); const implAddrUpgraded2 = await upgrades.erc1967.getImplementationAddress(await greeter2.getAddress()); - t.not(implAddrUpgraded2 !== implAddrUpgraded, implAddrUpgraded2); + t.not(implAddrUpgraded2, implAddrUpgraded); }); test('import previous deployment', async t => { diff --git a/packages/plugin-hardhat/test/infer-proxy-kind.js b/packages/plugin-hardhat/test/infer-proxy-kind.js index 002288216..decf52040 100644 --- a/packages/plugin-hardhat/test/infer-proxy-kind.js +++ b/packages/plugin-hardhat/test/infer-proxy-kind.js @@ -1,15 +1,27 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); }); test('infer proxy kind', async t => { const { Greeter, GreeterProxiable } = t.context; + const signer = await ethers.provider.getSigner(); const uups = await upgrades.deployProxy(GreeterProxiable, ['Hello, Hardhat!']); t.is(await upgrades.erc1967.getAdminAddress(await uups.getAddress()), ethers.ZeroAddress); diff --git a/packages/plugin-hardhat/test/namespaced.js b/packages/plugin-hardhat/test/namespaced.js index 9164efcb2..61658de20 100644 --- a/packages/plugin-hardhat/test/namespaced.js +++ b/packages/plugin-hardhat/test/namespaced.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Example = await ethers.getContractFactory('Example'); t.context.ExampleV2_Ok = await ethers.getContractFactory('ExampleV2_Ok'); t.context.ExampleV2_Bad = await ethers.getContractFactory('ExampleV2_Bad'); diff --git a/packages/plugin-hardhat/test/namespaced.js.md b/packages/plugin-hardhat/test/namespaced.js.md index 44199f0ac..b0e6f6641 100644 --- a/packages/plugin-hardhat/test/namespaced.js.md +++ b/packages/plugin-hardhat/test/namespaced.js.md @@ -19,7 +19,7 @@ Generated by [AVA](https://avajs.dev). `New storage layout is incompatible␊ ␊ - contracts/Namespaced.sol:109: Upgraded \`s\` to an incompatible type␊ + project/contracts/Namespaced.sol:109: Upgraded \`s\` to an incompatible type␊ - Bad upgrade from struct RecursiveStruct.MyStruct to struct RecursiveStructV2_Bad.MyStruct␊ - In struct RecursiveStructV2_Bad.MyStruct␊ - Added \`c\`` @@ -30,7 +30,7 @@ Generated by [AVA](https://avajs.dev). `New storage layout is incompatible␊ ␊ - contracts/Namespaced.sol:162: Upgraded \`s\` to an incompatible type␊ + project/contracts/Namespaced.sol:162: Upgraded \`s\` to an incompatible type␊ - Bad upgrade from struct TripleStruct.Outer to struct TripleStructV2_Bad.Outer␊ - In struct TripleStructV2_Bad.Outer␊ - Upgraded \`i\` to an incompatible type␊ @@ -44,13 +44,13 @@ Generated by [AVA](https://avajs.dev). `New storage layout is incompatible␊ ␊ - contracts/Namespaced.sol:225: Inserted \`a2\`␊ + project/contracts/Namespaced.sol:225: Inserted \`a2\`␊ > New variables should be placed after all existing inherited variables␊ ␊ - contracts/Namespaced.sol:211: Inserted \`a2\`␊ + project/contracts/Namespaced.sol:211: Inserted \`a2\`␊ > New variables should be placed after all existing inherited variables␊ ␊ - contracts/Namespaced.sol:219: Inserted \`a2\`␊ + project/contracts/Namespaced.sol:219: Inserted \`a2\`␊ > New variables should be placed after all existing inherited variables` ## delete namespace - bad diff --git a/packages/plugin-hardhat/test/namespaced.js.snap b/packages/plugin-hardhat/test/namespaced.js.snap index edaec09305a71a60568e0cdbb30a934682c46427..c4e8c86104a922da9a2091514a4ee912b4f22755 100644 GIT binary patch literal 617 zcmV-v0+#(jRzVElFQS%L;W+o zd3j5hVjqhL00000000BcRm*M@K@hY-2;m#I9Q%M2F1F*90A&vmL~yWB5{Z}KfN*=J z?Hw{ZvzqDM*hfx$21-7FGk?Hup?&!g*|8M`$ewmJHPzKsJ3Z&4p|;}Gf4?ABtZA~7 zp;i)^z+g;1K|#=lAxh;9CO)$4znx2oj2}<$Htsj_r+KgOu=)9z&cL&EI400IwJC%0 zpp39_f~zxP9ZNw(dekH+OVGTPJ?7PQxP1*5QhrPZ15Z)(ahca))4}dM8 zS)u`+Ce#9G!y!sYOE8R_jlr`^1;d+K{M+KYb2KeqaJd`OYHufJTC&ajMbH&Z7Uf$p^uc5c;m6%S9%3PZN)xUhEI07`rGP1G;cAtyXvyH>5d|O*l zuB_tRU2}UJ#5T*VnM-23;8UFmw4rinb{pNIEAZ&rP2^`gLSIX}1wH)%F6VmFe+U2o DB}OIT literal 621 zcmV-z0+RhfRzVe;eklQwt#&nM?m73IojVtUzEH`jIsZYHTS4Oo zISS6u5=czRWS9`Np^uCkiJ2KGYrbAE55SM7w;OjG;c4D!+;4t9p))X6X&e#=oG533 z8{jfgX@-{f1+`kQ#+YV;Vt7e{EHUVfdo6$iI3mixjtFwp96c5&CkcE2PC7%ZmiKU} zJWYwyhUcrKern#E*Lthj+(vAoq}7;MvwKo=v5rw9?(Xf!@ID)AWT6Gq1FHb#!gjEe zjC^Ztc!3OD35J1Ispr)$vGArA{kHh-98b!ZP;5EmL-fTLn*xjKhjMFhfInx2=4R&yUyU3rzMKNn2>eoa%KM1R55cZKlE(bvwBp0jg)v9hOtTrzSEEC6R3`8tgy zcq^TUQf|^&KsM{FVN0XlO`WhsuG6TESI_+M89IS_diKo9#J?>hetx~gqv&z$+cBhz zNq|u~3PR1963_1hgFZnf!cM?J2o;3@8gpa09D2SHX&#D9_pPJtcK;9U{y(Fw63$sM zRX0!W-wQGRYFue_&sch-lc&*MH>MKPNs-G-bG`a!&$u1=e@-gv>naI5&l2aE)3~B< z``f5oSyppr#jS9VsytXTljIN6%%?gNZ%5_O^p?3nSKINGn+Rukgr=5u1A6)crx5!f HR|o(Aa8Dv8 diff --git a/packages/plugin-hardhat/test/prepare-upgrade-txresponse.js b/packages/plugin-hardhat/test/prepare-upgrade-txresponse.js index 3fee59118..818ea77f7 100644 --- a/packages/plugin-hardhat/test/prepare-upgrade-txresponse.js +++ b/packages/plugin-hardhat/test/prepare-upgrade-txresponse.js @@ -1,14 +1,26 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3Proxiable'); + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV3 = await ethers.getContractFactory('contracts/GreeterV3.sol:GreeterV3Proxiable'); }); test('prepare upgrade with txresponse', async t => { const { Greeter, GreeterV3 } = t.context; + const signer = await ethers.provider.getSigner(); // deploy a proxy const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); @@ -28,6 +40,7 @@ test('prepare upgrade with txresponse', async t => { test('prepare upgrade twice with txresponse', async t => { const { Greeter, GreeterV3 } = t.context; + const signer = await ethers.provider.getSigner(); // deploy a proxy const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); diff --git a/packages/plugin-hardhat/test/propose-upgrade-with-approval-beacon.js b/packages/plugin-hardhat/test/propose-upgrade-with-approval-beacon.js index 166acdf74..7dced74fb 100644 --- a/packages/plugin-hardhat/test/propose-upgrade-with-approval-beacon.js +++ b/packages/plugin-hardhat/test/propose-upgrade-with-approval-beacon.js @@ -1,13 +1,26 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; +// import { defender as defenderFactory } from '@openzeppelin/hardhat-upgrades'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import esmock from 'esmock'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; +// /** @type {import('@openzeppelin/hardhat-upgrades').DefenderHardhatUpgrades} */ +// let defender; const proposalId = 'mocked proposal id'; const proposalUrl = 'https://example.com'; +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); + // defender = await defenderFactory(hre, connection); +}); + test.beforeEach(async t => { t.context.fakeChainId = 'goerli'; @@ -23,15 +36,18 @@ test.beforeEach(async t => { t.context.spy = sinon.spy(t.context.fakeDefenderClient, 'upgradeContract'); - t.context.proposeUpgradeWithApproval = proxyquire('../dist/defender/propose-upgrade-with-approval', { - './utils': { - ...require('../dist/defender/utils'), + const { getNetwork: _getNetwork, ...otherDefenderUtils } = await import('../dist/defender/utils.js'); + const module = await esmock('../dist/defender/propose-upgrade-with-approval.js', { + '../dist/defender/utils.js': { + ...otherDefenderUtils, getNetwork: () => t.context.fakeChainId, }, - './client': { + '../dist/defender/client.js': { getDeployClient: () => t.context.fakeDefenderClient, }, - }).makeProposeUpgradeWithApproval(hre); + }); + + t.context.proposeUpgradeWithApproval = module.makeProposeUpgradeWithApproval(hre, true, connection); t.context.Greeter = await ethers.getContractFactory('GreeterDefender'); t.context.GreeterV2 = await ethers.getContractFactory('GreeterDefenderV2'); @@ -43,6 +59,10 @@ test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + // TODO change the below tests when defender.proposeUpgradeWithApproval() supports beacons and beacon proxies. test('block proposing an upgrade on beacon proxy', async t => { diff --git a/packages/plugin-hardhat/test/propose-upgrade-with-approval-transparent.js b/packages/plugin-hardhat/test/propose-upgrade-with-approval-transparent.js index f92e6a8ab..c4ddc2550 100644 --- a/packages/plugin-hardhat/test/propose-upgrade-with-approval-transparent.js +++ b/packages/plugin-hardhat/test/propose-upgrade-with-approval-transparent.js @@ -1,15 +1,24 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import esmock from 'esmock'; +import { mockDeploy } from '../dist/test-utils/mock-deploy.js'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const proposalId = 'mocked proposal id'; const proposalUrl = 'https://example.com'; - const approvalProcessId = '123'; +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); + test.beforeEach(async t => { t.context.fakeChainId = 'goerli'; @@ -25,15 +34,35 @@ test.beforeEach(async t => { t.context.spy = sinon.spy(t.context.fakeDefenderClient, 'upgradeContract'); - t.context.proposeUpgradeWithApproval = proxyquire('../dist/defender/propose-upgrade-with-approval', { - './utils': { - ...require('../dist/defender/utils'), - getNetwork: () => t.context.fakeChainId, + const { getNetwork: _getNetwork, ...otherDefenderUtils } = await import('../dist/defender/utils.js'); + const module = await esmock( + '../dist/defender/propose-upgrade-with-approval.js', + { + '../dist/defender/utils.js': { + ...otherDefenderUtils, + getNetwork: () => t.context.fakeChainId, + }, + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - './client': { - getDeployClient: () => t.context.fakeDefenderClient, + { + // Global mocks + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - }).makeProposeUpgradeWithApproval(hre); + ); + + t.context.proposeUpgradeWithApproval = module.makeProposeUpgradeWithApproval(hre, true, connection); t.context.Greeter = await ethers.getContractFactory('GreeterDefender'); t.context.GreeterV2 = await ethers.getContractFactory('GreeterDefenderV2'); @@ -45,6 +74,10 @@ test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('proposes an upgrade', async t => { const { proposeUpgradeWithApproval, spy, proxyAdmin, greeter, GreeterV2 } = t.context; diff --git a/packages/plugin-hardhat/test/propose-upgrade-with-approval-unsafeAllow.js b/packages/plugin-hardhat/test/propose-upgrade-with-approval-unsafeAllow.js index c42259aaf..42aef84f4 100644 --- a/packages/plugin-hardhat/test/propose-upgrade-with-approval-unsafeAllow.js +++ b/packages/plugin-hardhat/test/propose-upgrade-with-approval-unsafeAllow.js @@ -1,13 +1,23 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import esmock from 'esmock'; +import { mockDeploy } from '../dist/test-utils/mock-deploy.js'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const proposalId = 'mocked proposal id'; const proposalUrl = 'https://example.com'; +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); + test.beforeEach(async t => { t.context.fakeChainId = 'goerli'; @@ -23,15 +33,35 @@ test.beforeEach(async t => { t.context.spy = sinon.spy(t.context.fakeDefenderClient, 'upgradeContract'); - t.context.proposeUpgradeWithApproval = proxyquire('../dist/defender/propose-upgrade-with-approval', { - './utils': { - ...require('../dist/defender/utils'), - getNetwork: () => t.context.fakeChainId, + const { getNetwork: _getNetwork, ...otherDefenderUtils } = await import('../dist/defender/utils.js'); + const module = await esmock( + '../dist/defender/propose-upgrade-with-approval.js', + { + '../dist/defender/utils.js': { + ...otherDefenderUtils, + getNetwork: () => t.context.fakeChainId, + }, + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - './client': { - getDeployClient: () => t.context.fakeDefenderClient, + { + // Global mocks + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - }).makeProposeUpgradeWithApproval(hre); + ); + + t.context.proposeUpgradeWithApproval = module.makeProposeUpgradeWithApproval(hre, true, connection); t.context.Greeter = await ethers.getContractFactory('GreeterDefender'); t.context.GreeterV2 = await ethers.getContractFactory('GreeterDefenderV2Bad'); @@ -43,6 +73,10 @@ test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('proposes an upgrade', async t => { const { proposeUpgradeWithApproval, spy, proxyAdmin, greeter, GreeterV2 } = t.context; diff --git a/packages/plugin-hardhat/test/propose-upgrade-with-approval-use-deployed.js b/packages/plugin-hardhat/test/propose-upgrade-with-approval-use-deployed.js index 4a376a111..0be0f0906 100644 --- a/packages/plugin-hardhat/test/propose-upgrade-with-approval-use-deployed.js +++ b/packages/plugin-hardhat/test/propose-upgrade-with-approval-use-deployed.js @@ -1,13 +1,23 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import esmock from 'esmock'; +import { mockDeploy } from '../dist/test-utils/mock-deploy.js'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const proposalId = 'mocked proposal id'; const proposalUrl = 'https://example.com'; +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); + test.beforeEach(async t => { t.context.fakeChainId = 'goerli'; @@ -23,15 +33,35 @@ test.beforeEach(async t => { t.context.spy = sinon.spy(t.context.fakeDefenderClient, 'upgradeContract'); - t.context.proposeUpgradeWithApproval = proxyquire('../dist/defender/propose-upgrade-with-approval', { - './utils': { - ...require('../dist/defender/utils'), - getNetwork: () => t.context.fakeChainId, + const { getNetwork: _getNetwork, ...otherDefenderUtils } = await import('../dist/defender/utils.js'); + const module = await esmock( + '../dist/defender/propose-upgrade-with-approval.js', + { + '../dist/defender/utils.js': { + ...otherDefenderUtils, + getNetwork: () => t.context.fakeChainId, + }, + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - './client': { - getDeployClient: () => t.context.fakeDefenderClient, + { + // Global mocks + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - }).makeProposeUpgradeWithApproval(hre); + ); + + t.context.proposeUpgradeWithApproval = module.makeProposeUpgradeWithApproval(hre, true, connection); t.context.Greeter = await ethers.getContractFactory('GreeterDefenderProxiable'); t.context.GreeterV2 = await ethers.getContractFactory('GreeterDefenderV2Proxiable'); @@ -42,6 +72,10 @@ test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('proposes an upgrade using deployed implementation - implementation not deployed', async t => { const { proposeUpgradeWithApproval, greeter, GreeterV2 } = t.context; diff --git a/packages/plugin-hardhat/test/propose-upgrade-with-approval-uups.js b/packages/plugin-hardhat/test/propose-upgrade-with-approval-uups.js index 7e43992bd..f90fee231 100644 --- a/packages/plugin-hardhat/test/propose-upgrade-with-approval-uups.js +++ b/packages/plugin-hardhat/test/propose-upgrade-with-approval-uups.js @@ -1,15 +1,25 @@ -const test = require('ava'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire').noCallThru(); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import esmock from 'esmock'; +import { mockDeploy } from '../dist/test-utils/mock-deploy.js'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const proposalId = 'mocked proposal id'; const proposalUrl = 'https://example.com'; const approvalProcessId = '123'; +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); + test.beforeEach(async t => { t.context.fakeChainId = 'goerli'; @@ -25,27 +35,51 @@ test.beforeEach(async t => { t.context.spy = sinon.spy(t.context.fakeDefenderClient, 'upgradeContract'); - t.context.proposeUpgradeWithApproval = proxyquire('../dist/defender/propose-upgrade-with-approval', { - './utils': { - ...require('../dist/defender/utils'), - getNetwork: () => t.context.fakeChainId, + const { getNetwork: _getNetwork, ...otherDefenderUtils } = await import('../dist/defender/utils.js'); + const module = await esmock( + '../dist/defender/propose-upgrade-with-approval.js', + { + '../dist/defender/utils.js': { + ...otherDefenderUtils, + getNetwork: () => t.context.fakeChainId, + }, + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - './client': { - getDeployClient: () => t.context.fakeDefenderClient, + { + // Global mocks + '../dist/defender/client.js': { + getDeployClient: () => t.context.fakeDefenderClient, + getNetworkClient: () => t.context.fakeDefenderClient, + }, + '../dist/utils/deploy.js': { + deploy: mockDeploy, + }, }, - }).makeProposeUpgradeWithApproval(hre); + ); + + t.context.proposeUpgradeWithApproval = module.makeProposeUpgradeWithApproval(hre, true, connection); t.context.Greeter = await ethers.getContractFactory('GreeterDefenderProxiable'); t.context.GreeterV2 = await ethers.getContractFactory('GreeterDefenderV2Proxiable'); t.context.greeter = await upgrades.deployProxy(t.context.Greeter, { kind: 'uups' }); t.context.GreeterTransparent = await ethers.getContractFactory('Greeter'); - t.context.GreeterTransparentV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterTransparentV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); }); test.afterEach.always(() => { sinon.restore(); }); +test.after.always(async () => { + await connection.close(); +}); + test('proposes an upgrade and get tx response', async t => { const { proposeUpgradeWithApproval, greeter, GreeterV2 } = t.context; @@ -60,7 +94,7 @@ test('proposes an upgrade and get tx response', async t => { // even though impl was already deployed in first proposal, it should still provide a tx response for the same tx hash t.is(proposal2.txResponse.hash, proposal.txResponse.hash); - const txReceipt2 = await proposal.txResponse.wait(); + const txReceipt2 = await proposal2.txResponse.wait(); t.is(txReceipt2.contractAddress, txReceipt.contractAddress); }); diff --git a/packages/plugin-hardhat/test/redeploy-implementation-always.js b/packages/plugin-hardhat/test/redeploy-implementation-always.js index 014e9272b..07253a180 100644 --- a/packages/plugin-hardhat/test/redeploy-implementation-always.js +++ b/packages/plugin-hardhat/test/redeploy-implementation-always.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); }); test('deployImplementation - both useDeployedImplementation and redeployImplementation options enabled', async t => { diff --git a/packages/plugin-hardhat/test/redeploy-implementation-never.js b/packages/plugin-hardhat/test/redeploy-implementation-never.js index 37a132546..b23330eb0 100644 --- a/packages/plugin-hardhat/test/redeploy-implementation-never.js +++ b/packages/plugin-hardhat/test/redeploy-implementation-never.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); }); diff --git a/packages/plugin-hardhat/test/redeploy-implementation-onchange.js b/packages/plugin-hardhat/test/redeploy-implementation-onchange.js index 8e62f7bbc..7a81e27b6 100644 --- a/packages/plugin-hardhat/test/redeploy-implementation-onchange.js +++ b/packages/plugin-hardhat/test/redeploy-implementation-onchange.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); }); test('onchange', async t => { diff --git a/packages/plugin-hardhat/test/solidity-overrides-storage.js b/packages/plugin-hardhat/test/solidity-overrides-storage.js index 336eafd74..622b19a03 100644 --- a/packages/plugin-hardhat/test/solidity-overrides-storage.js +++ b/packages/plugin-hardhat/test/solidity-overrides-storage.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.GapV1 = await ethers.getContractFactory('GapV1'); t.context.GapV2 = await ethers.getContractFactory('GapV2'); t.context.GapV2_Bad = await ethers.getContractFactory('GapV2_Bad'); diff --git a/packages/plugin-hardhat/test/solidity/Upgrades.t.sol b/packages/plugin-hardhat/test/solidity/Upgrades.t.sol new file mode 100644 index 000000000..4a83415df --- /dev/null +++ b/packages/plugin-hardhat/test/solidity/Upgrades.t.sol @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { IBeacon } from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import { Upgrades } from "@openzeppelin/foundry-upgrades/src/Upgrades.sol"; +import { Options } from "@openzeppelin/foundry-upgrades/src/Options.sol"; + +import { StringFinder } from "@openzeppelin/foundry-upgrades/src/internal/StringFinder.sol"; + +import { GreeterInitializable } from "../../contracts/foundry/GreeterInitializable.sol"; +import { GreeterProxiable } from "../../contracts/foundry/GreeterProxiable.sol"; +import { GreeterV2 } from "../../contracts/foundry/GreeterV2.sol"; +import { GreeterV2Proxiable } from "../../contracts/foundry/GreeterV2Proxiable.sol"; +import { WithConstructor, NoInitializer } from "../../contracts/foundry/WithConstructor.sol"; +import { HasOwner } from "../../contracts/foundry/HasOwner.sol"; +import "../../contracts/foundry/Validations.sol"; + +/** + * @dev Tests for the Upgrades library. + */ +contract UpgradesTest is Test { + using StringFinder for string; + + function testUUPS() public { + address proxy = Upgrades.deployUUPSProxy( + "foundry/GreeterProxiable.sol:GreeterProxiable", + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")) + ); + GreeterInitializable instance = GreeterInitializable(proxy); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeProxy( + proxy, + "foundry/GreeterV2Proxiable.sol:GreeterV2Proxiable", + abi.encodeCall(GreeterV2Proxiable.resetGreeting, ()), + msg.sender + ); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testUUPS_upgradeWithoutData() public { + address proxy = Upgrades.deployUUPSProxy( + "foundry/GreeterProxiable.sol:GreeterProxiable", + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")) + ); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + + Upgrades.upgradeProxy(proxy, "foundry/GreeterV2Proxiable.sol:GreeterV2Proxiable", "", msg.sender); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertFalse(implAddressV2 == implAddressV1); + } + + function testTransparent() public { + address proxy = Upgrades.deployTransparentProxy( + "foundry/GreeterInitializable.sol:GreeterInitializable", + msg.sender, + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")) + ); + GreeterInitializable instance = GreeterInitializable(proxy); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + address adminAddress = Upgrades.getAdminAddress(proxy); + + assertFalse(adminAddress == address(0)); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeProxy(proxy, "foundry/GreeterV2.sol:GreeterV2", abi.encodeCall(GreeterV2.resetGreeting, ()), msg.sender); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertEq(Upgrades.getAdminAddress(proxy), adminAddress); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testTransparent_upgradeWithoutData() public { + address proxy = Upgrades.deployTransparentProxy( + "foundry/GreeterInitializable.sol:GreeterInitializable", + msg.sender, + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")) + ); + address implAddressV1 = Upgrades.getImplementationAddress(proxy); + address adminAddress = Upgrades.getAdminAddress(proxy); + + assertFalse(adminAddress == address(0)); + + Upgrades.upgradeProxy(proxy, "foundry/GreeterV2.sol:GreeterV2", "", msg.sender); + address implAddressV2 = Upgrades.getImplementationAddress(proxy); + + assertEq(Upgrades.getAdminAddress(proxy), adminAddress); + + assertFalse(implAddressV2 == implAddressV1); + } + + function testBeacon() public { + address beacon = Upgrades.deployBeacon("foundry/GreeterInitializable.sol:GreeterInitializable", msg.sender); + address implAddressV1 = IBeacon(beacon).implementation(); + + address proxy = Upgrades.deployBeaconProxy(beacon, abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello"))); + GreeterInitializable instance = GreeterInitializable(proxy); + + assertEq(Upgrades.getBeaconAddress(proxy), beacon); + + assertEq(instance.greeting(), "hello"); + + Upgrades.upgradeBeacon(beacon, "foundry/GreeterV2.sol:GreeterV2", msg.sender); + address implAddressV2 = IBeacon(beacon).implementation(); + + vm.prank(msg.sender); + GreeterV2(address(instance)).setGreeting("modified"); + + assertEq(instance.greeting(), "modified"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testUpgradeProxyWithoutCaller() public { + address proxy = Upgrades.deployUUPSProxy( + "foundry/GreeterProxiable.sol:GreeterProxiable", + abi.encodeCall(GreeterProxiable.initialize, (msg.sender, "hello")) + ); + + vm.startPrank(msg.sender); + Upgrades.upgradeProxy(proxy, "foundry/GreeterV2Proxiable.sol:GreeterV2Proxiable", abi.encodeCall(GreeterV2Proxiable.resetGreeting, ())); + vm.stopPrank(); + } + + function testUpgradeBeaconWithoutCaller() public { + address beacon = Upgrades.deployBeacon("foundry/GreeterInitializable.sol:GreeterInitializable", msg.sender); + + vm.startPrank(msg.sender); + Upgrades.upgradeBeacon(beacon, "foundry/GreeterV2.sol:GreeterV2"); + vm.stopPrank(); + } + + function testValidateImplementation() public { + Options memory opts; + Invoker i = new Invoker(); + try i.validateImplementation("Validations.sol:Unsafe", opts) { + fail(); + } catch { + // TODO: check error message + } + } + + function testValidateLayout() public { + Options memory opts; + opts.referenceContract = "Validations.sol:LayoutV1"; + Invoker i = new Invoker(); + try i.validateUpgrade("Validations.sol:LayoutV2_Bad", opts) { + fail(); + } catch { + // TODO: check error message + } + } + + function testValidateLayoutUpgradesFrom() public { + Options memory opts; + Invoker i = new Invoker(); + try i.validateUpgrade("Validations.sol:LayoutV2_UpgradesFrom_Bad", opts) { + fail(); + } catch { + // TODO: check error message + } + } + + function testValidateNamespaced() public { + Options memory opts; + opts.referenceContract = "Validations.sol:NamespacedV1"; + Invoker i = new Invoker(); + try i.validateUpgrade("Validations.sol:NamespacedV2_Bad", opts) { + fail(); + } catch { + // TODO: check error message + } + } + + function testValidateNamespacedUpgradesFrom() public { + Options memory opts; + Invoker i = new Invoker(); + try i.validateUpgrade("Validations.sol:NamespacedV2_UpgradesFrom_Bad", opts) { + fail(); + } catch { + // TODO: check error message + } + } + + function testValidateNamespacedOk() public { + Options memory opts; + opts.referenceContract = "Validations.sol:NamespacedV1"; + Upgrades.validateUpgrade("Validations.sol:NamespacedV2_Ok", opts); + } + + function testValidateNamespacedUpgradesFromOk() public { + Options memory opts; + Upgrades.validateUpgrade("Validations.sol:NamespacedV2_UpgradesFrom_Ok", opts); + } + + function testValidateNamespacedNoReference() public { + Options memory opts; + Invoker i = new Invoker(); + // validate upgrade without reference contract - an error is expected from upgrades-core CLI + try i.validateUpgrade("Validations.sol:NamespacedV2_Ok", opts) { + fail(); + } catch { + // TODO: check error message + } + } + + function testUnsafeSkipAllChecks() public { + Options memory opts; + opts.unsafeSkipAllChecks = true; + Upgrades.validateImplementation("Validations.sol:Unsafe", opts); + } + + function testUnsafeSkipStorageCheck() public { + Options memory opts; + opts.unsafeSkipStorageCheck = true; + Upgrades.validateUpgrade("Validations.sol:NamespacedV2_UpgradesFrom_Bad", opts); + } + + function testUnsafeAllow() public { + Options memory opts; + opts.unsafeAllow = "delegatecall,selfdestruct"; + Upgrades.validateImplementation("Validations.sol:Unsafe", opts); + } + + function testUnsafeAllowRenames() public { + Options memory opts; + opts.unsafeAllowRenames = true; + Upgrades.validateImplementation("Validations.sol:LayoutV2_Renamed", opts); + } + + function testSkipStorageCheckNoReference() public { + Options memory opts; + opts.unsafeSkipStorageCheck = true; + Upgrades.validateUpgrade("Validations.sol:NamespacedV2_Ok", opts); + } + + function testWithConstructor() public { + console.log("testWithConstructor Test 1: Checking hardhat.config.js existence..."); + bool configJsExists = vm.exists("hardhat.config.js"); + console.log("testWithConstructor hardhat.config.js exists:", configJsExists); + + console.log("testWithConstructor Checking hardhat.config.ts existence..."); + bool configTsExists = vm.exists("hardhat.config.ts"); + console.log("testWithConstructor hardhat.config.ts exists:", configTsExists); + + Options memory opts; + opts.constructorData = abi.encode(123); + console.log("testWithConstructor before Upgrades.deployTransparentProxy"); + + address proxy = Upgrades.deployTransparentProxy( + "foundry/WithConstructor.sol:WithConstructor", + msg.sender, + abi.encodeCall(WithConstructor.initialize, (456)), + opts + ); + console.log("testWithConstructor after Upgrades.deployTransparentProxy"); + assertEq(WithConstructor(proxy).a(), 123); + assertEq(WithConstructor(proxy).b(), 456); + } + + function testNoInitializer() public { + Options memory opts; + opts.constructorData = abi.encode(123); + address proxy = Upgrades.deployTransparentProxy("foundry/WithConstructor.sol:NoInitializer", msg.sender, "", opts); + assertEq(WithConstructor(proxy).a(), 123); + } + + function testProxyAdminCheck() public { + ProxyAdmin admin = new ProxyAdmin(msg.sender); + + Invoker i = new Invoker(); + try + i.deployTransparentProxy( + "foundry/GreeterInitializable.sol:GreeterInitializable", + address(admin), // NOT SAFE + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")) + ) + { + fail(); + } catch Error(string memory reason) { + assertTrue(reason.contains("`initialOwner` must not be a ProxyAdmin contract.")); + assertTrue(reason.contains(vm.toString(address(admin)))); + } + } + + function testProxyAdminCheck_emptyOpts() public { + HasOwner hasOwner = new HasOwner(msg.sender); + Options memory opts; + + Invoker i = new Invoker(); + try + i.deployTransparentProxy( + "foundry/GreeterInitializable.sol:GreeterInitializable", + address(hasOwner), // false positive + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")), + opts + ) + { + fail(); + } catch Error(string memory reason) { + assertTrue(reason.contains("`initialOwner` must not be a ProxyAdmin contract.")); + assertTrue(reason.contains(vm.toString(address(hasOwner)))); + } + } + + function testProxyAdminCheck_skip() public { + HasOwner hasOwner = new HasOwner(msg.sender); + Options memory opts; + opts.unsafeSkipProxyAdminCheck = true; + + Upgrades.deployTransparentProxy( + "foundry/GreeterInitializable.sol:GreeterInitializable", + address(hasOwner), + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")), + opts + ); + } + + function testProxyAdminCheck_skipAll() public { + HasOwner hasOwner = new HasOwner(msg.sender); + Options memory opts; + opts.unsafeSkipAllChecks = true; + + Upgrades.deployTransparentProxy( + "foundry/GreeterInitializable.sol:GreeterInitializable", + address(hasOwner), + abi.encodeCall(GreeterInitializable.initialize, (msg.sender, "hello")), + opts + ); + } + + function testWarningAndError() public { + Options memory opts; + opts.unsafeAllow = "state-variable-immutable"; + + Invoker i = new Invoker(); + try i.validateImplementation("Validations.sol:HasWarningAndError", opts) { + fail(); + } catch Error(string memory reason) { + assertTrue(vm.contains(reason, "Use of delegatecall is not allowed")); + } + } +} + +contract Invoker { + function deployTransparentProxy( + string memory contractName, + address admin, + bytes memory data + ) public returns (address) { + return Upgrades.deployTransparentProxy(contractName, admin, data); + } + + function deployTransparentProxy( + string memory contractName, + address admin, + bytes memory data, + Options memory opts + ) public returns (address) { + return Upgrades.deployTransparentProxy(contractName, admin, data, opts); + } + + function validateImplementation(string memory contractName, Options memory opts) public { + Upgrades.validateImplementation(contractName, opts); + } + + function validateUpgrade(string memory contractName, Options memory opts) public { + Upgrades.validateUpgrade(contractName, opts); + } +} \ No newline at end of file diff --git a/packages/plugin-hardhat/test/timeout.js b/packages/plugin-hardhat/test/timeout.js index 67be62e05..1fb47dcbd 100644 --- a/packages/plugin-hardhat/test/timeout.js +++ b/packages/plugin-hardhat/test/timeout.js @@ -1,34 +1,50 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades, network } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); - t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2Proxiable = await ethers.getContractFactory('GreeterV2Proxiable'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); + t.context.GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2Proxiable = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); }); test.beforeEach(async t => { - // reset network before each test to avoid finding a previously deployed impl - await network.provider.request({ - method: 'hardhat_reset', - params: [], - }); + // Use ethers.provider from connection + const provider = ethers.provider; + + // In Hardhat 3, we need to reset using the network manager + // or simply create a new snapshot and restore it + const snapshotId = await provider.send('evm_snapshot', []); + t.context.snapshotId = snapshotId; // enable interval mining for timeout tests - t.context.automine = await network.provider.send('hardhat_getAutomine'); - await network.provider.send('evm_setAutomine', [false]); - await network.provider.send('evm_setIntervalMining', [500]); + t.context.automine = await provider.send('hardhat_getAutomine', []); + await provider.send('evm_setAutomine', [false]); + await provider.send('evm_setIntervalMining', [500]); + + // Store provider in context for use in tests + t.context.provider = provider; }); test.afterEach(async t => { - // reset network state after each test, otherwise ava tests may hang due to interval mining - await network.provider.send('evm_setAutomine', [t.context.automine]); - await network.provider.request({ - method: 'hardhat_reset', - params: [], - }); + const provider = t.context.provider; + // reset network state after each test + await provider.send('evm_setAutomine', [t.context.automine]); + + // Revert to snapshot instead of hardhat_reset + await provider.send('evm_revert', [t.context.snapshotId]); +}); + +test.after.always(async () => { + await connection.close(); }); const TIMED_OUT_IMPL = 'Timed out waiting for implementation contract deployment'; @@ -43,8 +59,9 @@ test('timeout too low - beacon', async t => { }); test('timeout too low - proxy impl', async t => { + const provider = t.context.provider; // manual mining - await network.provider.send('evm_setIntervalMining', [0]); + await provider.send('evm_setIntervalMining', [0]); const error = await t.throwsAsync(() => upgrades.deployProxy(t.context.Greeter, ['Hello, Hardhat!'], { @@ -56,7 +73,7 @@ test('timeout too low - proxy impl', async t => { t.true(error.message.includes(TIMED_OUT_IMPL) && error.message.includes(USE_OPTIONS), error.message); // mine the impl deployment - await network.provider.send('evm_mine'); + await provider.send('evm_mine', []); // run again to continue with proxy deployment await upgrades.deployProxy(t.context.Greeter, ['Hello, Hardhat!'], { @@ -98,13 +115,14 @@ test('single option', async t => { }); test('upgrade beacon', async t => { + const provider = t.context.provider; // automine to immediately deploy a new beacon to use in below tests - await network.provider.send('evm_setAutomine', [true]); + await provider.send('evm_setAutomine', [true]); const beacon = await upgrades.deployBeacon(t.context.Greeter, { timeout: 0, pollingInterval: 0, }); - await network.provider.send('evm_setAutomine', [false]); + await provider.send('evm_setAutomine', [false]); // upgrade: timeout too low const error = await t.throwsAsync(() => @@ -117,14 +135,16 @@ test('upgrade beacon', async t => { }); test('upgrade proxy', async t => { + const provider = t.context.provider; // automine to immediately deploy a new proxy to use in below tests - await network.provider.send('evm_setAutomine', [true]); + await provider.send('evm_setAutomine', [true]); + const signer = await ethers.provider.getSigner(); const proxy = await upgrades.deployProxy(t.context.GreeterProxiable, ['Hello, Hardhat!'], { kind: 'uups', timeout: 0, pollingInterval: 0, }); - await network.provider.send('evm_setAutomine', [false]); + await provider.send('evm_setAutomine', [false]); // upgrade: timeout too low const error = await t.throwsAsync(() => diff --git a/packages/plugin-hardhat/test/transparent-admin-initial-owner.js b/packages/plugin-hardhat/test/transparent-admin-initial-owner.js index 4c849c656..abe5df53e 100644 --- a/packages/plugin-hardhat/test/transparent-admin-initial-owner.js +++ b/packages/plugin-hardhat/test/transparent-admin-initial-owner.js @@ -1,15 +1,28 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); -const hre = require('hardhat'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + const OWNABLE_ABI = ['function owner() view returns (address)']; +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.HasOwner = await ethers.getContractFactory('HasOwner'); + t.context.HasOwner = await ethers.getContractFactory('contracts/HasOwner.sol:HasOwner'); t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); }); @@ -18,7 +31,7 @@ test('initial owner using default signer', async t => { const proxy = await upgrades.deployProxy(Greeter, ['hello']); const adminAddress = await upgrades.erc1967.getAdminAddress(await proxy.getAddress()); - const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); + const admin = await ethers.getContractAt(OWNABLE_ABI, adminAddress); const defaultSigner = await ethers.provider.getSigner(0); @@ -32,7 +45,7 @@ test('initial owner using custom signer', async t => { const proxy = await upgrades.deployProxy(Greeter, ['hello']); const adminAddress = await upgrades.erc1967.getAdminAddress(await proxy.getAddress()); - const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); + const admin = await ethers.getContractAt(OWNABLE_ABI, adminAddress); t.is(await admin.owner(), customSigner.address); }); @@ -44,7 +57,7 @@ test('initial owner using initialOwner option', async t => { const proxy = await upgrades.deployProxy(Greeter, ['hello'], { initialOwner: initialOwner.address }); const adminAddress = await upgrades.erc1967.getAdminAddress(await proxy.getAddress()); - const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); + const admin = await ethers.getContractAt(OWNABLE_ABI, adminAddress); t.is(await admin.owner(), initialOwner.address); }); @@ -62,7 +75,7 @@ test('initial owner - no signer in ContractFactory', async t => { const proxy = await upgrades.deployProxy(Greeter, ['hello'], { initialOwner: initialOwner.address }); const adminAddress = await upgrades.erc1967.getAdminAddress(await proxy.getAddress()); - const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); + const admin = await ethers.getContractAt(OWNABLE_ABI, adminAddress); t.is(await admin.owner(), initialOwner.address); }); diff --git a/packages/plugin-hardhat/test/transparent-admin-unknown-upgrade-interface.js b/packages/plugin-hardhat/test/transparent-admin-unknown-upgrade-interface.js index a295b3785..3b7c13ee8 100644 --- a/packages/plugin-hardhat/test/transparent-admin-unknown-upgrade-interface.js +++ b/packages/plugin-hardhat/test/transparent-admin-unknown-upgrade-interface.js @@ -1,12 +1,25 @@ -const test = require('ava'); -const sinon = require('sinon'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import sinon from 'sinon'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); -const hre = require('hardhat'); +const require = createRequire(import.meta.url); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.GreeterTransparent40Fallback = await ethers.getContractFactory('GreeterTransparent40Fallback'); t.context.GreeterTransparent40FallbackV2 = await ethers.getContractFactory('GreeterTransparent40FallbackV2'); t.context.UnsafeAdminFallback = await ethers.getContractFactory('UnsafeAdminFallback'); @@ -52,7 +65,8 @@ test('admin with unknown upgrades interface version due to fallback returning no await upgrades.forceImport(await proxy.getAddress(), GreeterTransparent40Fallback); const debugStub = sinon.stub(); - const upgradeProxy = require('../dist/upgrade-proxy').makeUpgradeProxy(hre, false, debugStub); + const { makeUpgradeProxy } = await import('../dist/upgrade-proxy.js'); + const upgradeProxy = makeUpgradeProxy(hre, false, connection, debugStub); const greeter2 = await upgradeProxy(proxy, GreeterTransparent40FallbackV2); await greeter2.resetGreeting(); @@ -90,7 +104,8 @@ test('admin with unknown upgrades interface version due to fallback returning st await upgrades.forceImport(await proxy.getAddress(), GreeterTransparent40FallbackString); const debugStub = sinon.stub(); - const upgradeProxy = require('../dist/upgrade-proxy').makeUpgradeProxy(hre, false, debugStub); + const { makeUpgradeProxy } = await import('../dist/upgrade-proxy.js'); + const upgradeProxy = makeUpgradeProxy(hre, false, connection, debugStub); const greeter2 = await upgradeProxy(proxy, GreeterTransparent40FallbackStringV2); await greeter2.resetGreeting(); diff --git a/packages/plugin-hardhat/test/transparent-deploy-overload.js b/packages/plugin-hardhat/test/transparent-deploy-overload.js index 57299ebbc..1b528bfe9 100644 --- a/packages/plugin-hardhat/test/transparent-deploy-overload.js +++ b/packages/plugin-hardhat/test/transparent-deploy-overload.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.DeployOverload = await ethers.getContractFactory('DeployOverload'); }); diff --git a/packages/plugin-hardhat/test/transparent-deploy-validation.js b/packages/plugin-hardhat/test/transparent-deploy-validation.js index 3451c3df4..3e53cb170 100644 --- a/packages/plugin-hardhat/test/transparent-deploy-validation.js +++ b/packages/plugin-hardhat/test/transparent-deploy-validation.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Invalid = await ethers.getContractFactory('Invalid'); }); diff --git a/packages/plugin-hardhat/test/transparent-happy-path-with-call.js b/packages/plugin-hardhat/test/transparent-happy-path-with-call.js index bd6aa6977..9a52fd978 100644 --- a/packages/plugin-hardhat/test/transparent-happy-path-with-call.js +++ b/packages/plugin-hardhat/test/transparent-happy-path-with-call.js @@ -1,10 +1,22 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); }); test('happy path - call with args', async t => { diff --git a/packages/plugin-hardhat/test/transparent-happy-path-with-enums.js b/packages/plugin-hardhat/test/transparent-happy-path-with-enums.js index e139b7966..a17133dc1 100644 --- a/packages/plugin-hardhat/test/transparent-happy-path-with-enums.js +++ b/packages/plugin-hardhat/test/transparent-happy-path-with-enums.js @@ -1,10 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -upgrades.silenceWarnings(); +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + upgrades.silenceWarnings(); t.context.Action = await ethers.getContractFactory('Action'); t.context.ActionV2 = await ethers.getContractFactory('ActionV2'); t.context.ActionV2Bad = await ethers.getContractFactory('ActionV2Bad'); diff --git a/packages/plugin-hardhat/test/transparent-happy-path-with-library.js b/packages/plugin-hardhat/test/transparent-happy-path-with-library.js index 5a6051ff6..35304efbb 100644 --- a/packages/plugin-hardhat/test/transparent-happy-path-with-library.js +++ b/packages/plugin-hardhat/test/transparent-happy-path-with-library.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Adder = await ethers.getContractFactory('Adder'); t.context.AdderV2 = await ethers.getContractFactory('AdderV2'); }); diff --git a/packages/plugin-hardhat/test/transparent-happy-path-with-structs.js b/packages/plugin-hardhat/test/transparent-happy-path-with-structs.js index b0c6d4191..fce70bfd1 100644 --- a/packages/plugin-hardhat/test/transparent-happy-path-with-structs.js +++ b/packages/plugin-hardhat/test/transparent-happy-path-with-structs.js @@ -1,10 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -upgrades.silenceWarnings(); +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + upgrades.silenceWarnings(); t.context.Portfolio = await ethers.getContractFactory('Portfolio'); t.context.PortfolioV2 = await ethers.getContractFactory('PortfolioV2'); t.context.PortfolioV2Bad = await ethers.getContractFactory('PortfolioV2Bad'); diff --git a/packages/plugin-hardhat/test/transparent-happy-path.js b/packages/plugin-hardhat/test/transparent-happy-path.js index c24457a55..f2207c04f 100644 --- a/packages/plugin-hardhat/test/transparent-happy-path.js +++ b/packages/plugin-hardhat/test/transparent-happy-path.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); }); diff --git a/packages/plugin-hardhat/test/transparent-initializers.js b/packages/plugin-hardhat/test/transparent-initializers.js index c65228fec..3ce1cd849 100644 --- a/packages/plugin-hardhat/test/transparent-initializers.js +++ b/packages/plugin-hardhat/test/transparent-initializers.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.InitializerOverloaded = await ethers.getContractFactory('InitializerOverloaded'); t.context.InitializerMissing = await ethers.getContractFactory('InitializerMissing'); }); diff --git a/packages/plugin-hardhat/test/transparent-linked-libraries.js b/packages/plugin-hardhat/test/transparent-linked-libraries.js index acd654d6a..25de75daa 100644 --- a/packages/plugin-hardhat/test/transparent-linked-libraries.js +++ b/packages/plugin-hardhat/test/transparent-linked-libraries.js @@ -1,10 +1,23 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades, artifacts } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +const artifacts = hre.artifacts; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; -upgrades.silenceWarnings(); +test.after.always(async () => { + await connection.close(); +}); + +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); test('without flag', async t => { // Deploying library diff --git a/packages/plugin-hardhat/test/transparent-multi-compiler.js b/packages/plugin-hardhat/test/transparent-multi-compiler.js index acb6385eb..f84f5203d 100644 --- a/packages/plugin-hardhat/test/transparent-multi-compiler.js +++ b/packages/plugin-hardhat/test/transparent-multi-compiler.js @@ -1,11 +1,21 @@ -const { getVersion, getContractNameAndRunValidation } = require('@openzeppelin/upgrades-core'); -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { readValidations } = require('../dist/utils/validations'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { getVersion, getContractNameAndRunValidation } from '@openzeppelin/upgrades-core'; +import { readValidations } from '../dist/utils/validations.js'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let _upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - const { ethers } = hre; + _upgrades = await upgradesFactory(hre, connection); t.context.validations = await readValidations(hre); t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.GreeterMulti = await ethers.getContractFactory('GreeterMultiPragma'); diff --git a/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-happy-path.js b/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-happy-path.js index 185e6f9fa..316727fb4 100644 --- a/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-happy-path.js +++ b/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-happy-path.js @@ -1,12 +1,22 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const TEST_ADDRESS = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; const OWNABLE_ABI = ['function owner() view returns (address)']; +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); }); @@ -18,7 +28,7 @@ test('transferProxyAdminOwnership', async t => { await upgrades.admin.transferProxyAdminOwnership(await greeter.getAddress(), TEST_ADDRESS); const adminAddress = await upgrades.erc1967.getAdminAddress(await greeter.getAddress()); - const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); + const admin = await ethers.getContractAt(OWNABLE_ABI, adminAddress); // ← mudou aqui de hre.ethers para ethers const newOwner = await admin.owner(); t.is(newOwner, TEST_ADDRESS); diff --git a/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-signer.js b/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-signer.js index b50015801..2c20fa6ea 100644 --- a/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-signer.js +++ b/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-signer.js @@ -1,11 +1,24 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const TEST_ADDRESS = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; const OWNABLE_ABI = ['function owner() view returns (address)']; +test.after.always(async () => { + await connection.close(); +}); + +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); + test('transferProxyAdminOwnership - signer', async t => { // we need to deploy a proxy so we have a Proxy Admin const signer = await ethers.provider.getSigner(1); @@ -15,7 +28,7 @@ test('transferProxyAdminOwnership - signer', async t => { await upgrades.admin.transferProxyAdminOwnership(await greeter.getAddress(), TEST_ADDRESS, signer); const adminAddress = await upgrades.erc1967.getAdminAddress(await greeter.getAddress()); - const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); + const admin = await ethers.getContractAt(OWNABLE_ABI, adminAddress); // ← mudou de hre.ethers para ethers const newOwner = await admin.owner(); t.is(newOwner, TEST_ADDRESS); diff --git a/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-wrong-signer.js b/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-wrong-signer.js index ae5c86a9e..b51b7c854 100644 --- a/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-wrong-signer.js +++ b/packages/plugin-hardhat/test/transparent-transfer-admin-ownership-wrong-signer.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; + +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; -const hre = require('hardhat'); -const { ethers, upgrades } = hre; const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); }); diff --git a/packages/plugin-hardhat/test/transparent-upgrade-storage.js b/packages/plugin-hardhat/test/transparent-upgrade-storage.js index 0321cd80c..9f0531478 100644 --- a/packages/plugin-hardhat/test/transparent-upgrade-storage.js +++ b/packages/plugin-hardhat/test/transparent-upgrade-storage.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.GreeterStorageConflict = await ethers.getContractFactory('GreeterStorageConflict'); }); diff --git a/packages/plugin-hardhat/test/transparent-upgrade-validation.js b/packages/plugin-hardhat/test/transparent-upgrade-validation.js index a87c0fd36..5087977c4 100644 --- a/packages/plugin-hardhat/test/transparent-upgrade-validation.js +++ b/packages/plugin-hardhat/test/transparent-upgrade-validation.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.Invalid = await ethers.getContractFactory('Invalid'); }); diff --git a/packages/plugin-hardhat/test/transparent-v4-change-admin-different-address.js b/packages/plugin-hardhat/test/transparent-v4-change-admin-different-address.js index e1ae3beee..3f394d07a 100644 --- a/packages/plugin-hardhat/test/transparent-v4-change-admin-different-address.js +++ b/packages/plugin-hardhat/test/transparent-v4-change-admin-different-address.js @@ -1,14 +1,27 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); -const hre = require('hardhat'); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); -const { ethers, upgrades } = hre; + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); t.context.TransparentUpgradableProxy = await ethers.getContractFactory( diff --git a/packages/plugin-hardhat/test/transparent-v4-change-admin-happy-path.js b/packages/plugin-hardhat/test/transparent-v4-change-admin-happy-path.js index 77aadee94..86c28859e 100644 --- a/packages/plugin-hardhat/test/transparent-v4-change-admin-happy-path.js +++ b/packages/plugin-hardhat/test/transparent-v4-change-admin-happy-path.js @@ -1,13 +1,27 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); t.context.TransparentUpgradableProxy = await ethers.getContractFactory( diff --git a/packages/plugin-hardhat/test/transparent-v4-transfer-admin-ownership-multiple.js b/packages/plugin-hardhat/test/transparent-v4-transfer-admin-ownership-multiple.js index b5cddda85..84d752fe6 100644 --- a/packages/plugin-hardhat/test/transparent-v4-transfer-admin-ownership-multiple.js +++ b/packages/plugin-hardhat/test/transparent-v4-transfer-admin-ownership-multiple.js @@ -1,13 +1,27 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); t.context.TransparentUpgradableProxy = await ethers.getContractFactory( @@ -40,7 +54,7 @@ test('transferProxyAdminOwnership v4 multiple proxies', async t => { await upgrades.forceImport(await proxy2.getAddress(), Greeter); // Deploy an unrelated UUPS proxy - const GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); + const GreeterProxiable = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); await upgrades.deployProxy(GreeterProxiable, ['Hello, Hardhat!'], { kind: 'uups' }); await upgrades.admin.transferProxyAdminOwnership(await greeter.getAddress(), testAddress); diff --git a/packages/plugin-hardhat/test/transparent-v5-with-v4-manifest-admin.js b/packages/plugin-hardhat/test/transparent-v5-with-v4-manifest-admin.js index 1f7a7f73d..bdeaacc52 100644 --- a/packages/plugin-hardhat/test/transparent-v5-with-v4-manifest-admin.js +++ b/packages/plugin-hardhat/test/transparent-v5-with-v4-manifest-admin.js @@ -1,14 +1,26 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { fetchOrDeployAdmin } from '@openzeppelin/upgrades-core'; +import { deploy } from '../dist/utils/deploy.js'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); -const hre = require('hardhat'); - -const { fetchOrDeployAdmin } = require('@openzeppelin/upgrades-core'); -const { deploy } = require('../dist/utils'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); }); diff --git a/packages/plugin-hardhat/test/tx-overrides.js b/packages/plugin-hardhat/test/tx-overrides.js index a985ebcd2..d560e72cd 100644 --- a/packages/plugin-hardhat/test/tx-overrides.js +++ b/packages/plugin-hardhat/test/tx-overrides.js @@ -1,13 +1,27 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { createRequire } from 'node:module'; -const { ethers, upgrades } = require('hardhat'); +const require = createRequire(import.meta.url); const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); + +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); t.context.TransparentUpgradableProxy = await ethers.getContractFactory( TransparentUpgradableProxy.abi, diff --git a/packages/plugin-hardhat/test/use-deployed-implementation.js b/packages/plugin-hardhat/test/use-deployed-implementation.js index cedaf28c5..4c7f7f9c5 100644 --- a/packages/plugin-hardhat/test/use-deployed-implementation.js +++ b/packages/plugin-hardhat/test/use-deployed-implementation.js @@ -1,10 +1,21 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('Greeter'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); }); diff --git a/packages/plugin-hardhat/test/uups-custom-proxy.js b/packages/plugin-hardhat/test/uups-custom-proxy.js index fd1dd1441..98f4a660c 100644 --- a/packages/plugin-hardhat/test/uups-custom-proxy.js +++ b/packages/plugin-hardhat/test/uups-custom-proxy.js @@ -1,13 +1,25 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { deploy } from '../dist/utils/deploy.js'; +// AccessManager is from @openzeppelin/contracts, we'll get it via artifacts -const { ethers, upgrades } = require('hardhat'); -const { deploy } = require('../dist/utils/deploy'); +const connection = await hre.network.connect(); +const { ethers } = connection; + +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable'); + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3Proxiable'); t.context.AccessManagedProxy = await ethers.getContractFactory('AccessManagedProxy'); + // AccessManager is from @openzeppelin/contracts, imported via imports.sol const AccessManager = await ethers.getContractFactory('AccessManager'); const [admin, anon] = await ethers.getSigners(); t.context.admin = admin; diff --git a/packages/plugin-hardhat/test/uups-deploy-overload.js b/packages/plugin-hardhat/test/uups-deploy-overload.js index 58525f1b9..fc162b5bc 100644 --- a/packages/plugin-hardhat/test/uups-deploy-overload.js +++ b/packages/plugin-hardhat/test/uups-deploy-overload.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.DeployOverload = await ethers.getContractFactory('DeployOverloadProxiable'); }); diff --git a/packages/plugin-hardhat/test/uups-deploy-validation.js b/packages/plugin-hardhat/test/uups-deploy-validation.js index 43740056b..c84df4b3f 100644 --- a/packages/plugin-hardhat/test/uups-deploy-validation.js +++ b/packages/plugin-hardhat/test/uups-deploy-validation.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Invalid = await ethers.getContractFactory('InvalidProxiable'); }); diff --git a/packages/plugin-hardhat/test/uups-happy-path-v4.js b/packages/plugin-hardhat/test/uups-happy-path-v4.js index 0d1d73de2..befd8ce7b 100644 --- a/packages/plugin-hardhat/test/uups-happy-path-v4.js +++ b/packages/plugin-hardhat/test/uups-happy-path-v4.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('GreeterProxiable40'); t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable40'); t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3Proxiable40'); diff --git a/packages/plugin-hardhat/test/uups-happy-path-with-call-v4.js b/packages/plugin-hardhat/test/uups-happy-path-with-call-v4.js index 191e47213..2a72f3645 100644 --- a/packages/plugin-hardhat/test/uups-happy-path-with-call-v4.js +++ b/packages/plugin-hardhat/test/uups-happy-path-with-call-v4.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Greeter = await ethers.getContractFactory('GreeterProxiable40'); t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable40'); }); diff --git a/packages/plugin-hardhat/test/uups-happy-path-with-call.js b/packages/plugin-hardhat/test/uups-happy-path-with-call.js index deed6865e..31c3fa6dd 100644 --- a/packages/plugin-hardhat/test/uups-happy-path-with-call.js +++ b/packages/plugin-hardhat/test/uups-happy-path-with-call.js @@ -1,14 +1,26 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable'); + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); }); test('happy path - call with args', async t => { const { Greeter, GreeterV2 } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); @@ -23,6 +35,7 @@ test('happy path - call with args', async t => { test('happy path - call without args', async t => { const { Greeter, GreeterV2 } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); diff --git a/packages/plugin-hardhat/test/uups-happy-path-with-enums.js b/packages/plugin-hardhat/test/uups-happy-path-with-enums.js index e253f6998..edd1de633 100644 --- a/packages/plugin-hardhat/test/uups-happy-path-with-enums.js +++ b/packages/plugin-hardhat/test/uups-happy-path-with-enums.js @@ -1,10 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -upgrades.silenceWarnings(); +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + upgrades.silenceWarnings(); t.context.Action = await ethers.getContractFactory('ActionProxiable'); t.context.ActionV2 = await ethers.getContractFactory('ActionV2Proxiable'); t.context.ActionV2Bad = await ethers.getContractFactory('ActionV2BadProxiable'); diff --git a/packages/plugin-hardhat/test/uups-happy-path-with-library.js b/packages/plugin-hardhat/test/uups-happy-path-with-library.js index 6062a3aba..188f3cb12 100644 --- a/packages/plugin-hardhat/test/uups-happy-path-with-library.js +++ b/packages/plugin-hardhat/test/uups-happy-path-with-library.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.Adder = await ethers.getContractFactory('AdderProxiable'); t.context.AdderV2 = await ethers.getContractFactory('AdderV2Proxiable'); }); diff --git a/packages/plugin-hardhat/test/uups-happy-path-with-structs.js b/packages/plugin-hardhat/test/uups-happy-path-with-structs.js index 6ad031876..db07a7553 100644 --- a/packages/plugin-hardhat/test/uups-happy-path-with-structs.js +++ b/packages/plugin-hardhat/test/uups-happy-path-with-structs.js @@ -1,10 +1,20 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; -upgrades.silenceWarnings(); +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); + upgrades.silenceWarnings(); t.context.Portfolio = await ethers.getContractFactory('PortfolioProxiable'); t.context.PortfolioV2 = await ethers.getContractFactory('PortfolioV2Proxiable'); t.context.PortfolioV2Bad = await ethers.getContractFactory('PortfolioV2BadProxiable'); diff --git a/packages/plugin-hardhat/test/uups-happy-path.js b/packages/plugin-hardhat/test/uups-happy-path.js index a89575be8..39cce1dcb 100644 --- a/packages/plugin-hardhat/test/uups-happy-path.js +++ b/packages/plugin-hardhat/test/uups-happy-path.js @@ -1,15 +1,27 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable'); - t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3Proxiable'); + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterV2 = await ethers.getContractFactory('contracts/GreeterV2.sol:GreeterV2Proxiable'); + t.context.GreeterV3 = await ethers.getContractFactory('contracts/GreeterV3.sol:GreeterV3Proxiable'); }); test('happy path', async t => { const { Greeter, GreeterV2, GreeterV3 } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); diff --git a/packages/plugin-hardhat/test/uups-initial-owner.js b/packages/plugin-hardhat/test/uups-initial-owner.js index 85ef0692c..0b3d80c36 100644 --- a/packages/plugin-hardhat/test/uups-initial-owner.js +++ b/packages/plugin-hardhat/test/uups-initial-owner.js @@ -1,17 +1,32 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); }); test('uups with initialOwner option', async t => { const { Greeter } = t.context; const initialOwner = await ethers.provider.getSigner(1); + const signer = await ethers.provider.getSigner(); - await t.throwsAsync(upgrades.deployProxy(Greeter, ['hello'], { initialOwner: initialOwner.address }), { - message: /The `initialOwner` option is not supported for this kind of proxy \('uups'\)/, - }); + await t.throwsAsync( + upgrades.deployProxy(Greeter, ['hello'], { initialOwner: initialOwner.address }), + { + message: /The `initialOwner` option is not supported for this kind of proxy \('uups'\)/, + }, + ); }); diff --git a/packages/plugin-hardhat/test/uups-initializers.js b/packages/plugin-hardhat/test/uups-initializers.js index 91e48699f..0483a0628 100644 --- a/packages/plugin-hardhat/test/uups-initializers.js +++ b/packages/plugin-hardhat/test/uups-initializers.js @@ -1,8 +1,19 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.InitializerOverloaded = await ethers.getContractFactory('InitializerOverloadedProxiable'); t.context.InitializerMissing = await ethers.getContractFactory('InitializerMissingProxiable'); }); diff --git a/packages/plugin-hardhat/test/uups-linked-libraries.js b/packages/plugin-hardhat/test/uups-linked-libraries.js index 1ebc4b279..c8e4410c4 100644 --- a/packages/plugin-hardhat/test/uups-linked-libraries.js +++ b/packages/plugin-hardhat/test/uups-linked-libraries.js @@ -1,10 +1,23 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades, artifacts } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +const artifacts = hre.artifacts; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; -upgrades.silenceWarnings(); +test.after.always(async () => { + await connection.close(); +}); + +test.before(async () => { + upgrades = await upgradesFactory(hre, connection); +}); test('without flag', async t => { // Deploying library diff --git a/packages/plugin-hardhat/test/uups-multi-compiler.js b/packages/plugin-hardhat/test/uups-multi-compiler.js index 5148abf99..706d97da4 100644 --- a/packages/plugin-hardhat/test/uups-multi-compiler.js +++ b/packages/plugin-hardhat/test/uups-multi-compiler.js @@ -1,13 +1,23 @@ -const { getVersion, getContractNameAndRunValidation } = require('@openzeppelin/upgrades-core'); -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const hre = require('hardhat'); -const { readValidations } = require('../dist/utils/validations'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import { getVersion, getContractNameAndRunValidation } from '@openzeppelin/upgrades-core'; +import { readValidations } from '../dist/utils/validations.js'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let _upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - const { ethers } = hre; + _upgrades = await upgradesFactory(hre, connection); t.context.validations = await readValidations(hre); - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); t.context.GreeterMulti = await ethers.getContractFactory('GreeterMultiPragmaProxiable'); }); diff --git a/packages/plugin-hardhat/test/uups-unknown-upgrade-interface.js b/packages/plugin-hardhat/test/uups-unknown-upgrade-interface.js index 06b449a9b..b9adc0164 100644 --- a/packages/plugin-hardhat/test/uups-unknown-upgrade-interface.js +++ b/packages/plugin-hardhat/test/uups-unknown-upgrade-interface.js @@ -1,10 +1,31 @@ -const test = require('ava'); -const sinon = require('sinon'); - -const { ethers, upgrades } = require('hardhat'); -const hre = require('hardhat'); +import test from 'ava'; +import hre from 'hardhat'; + +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +// TODO: Debug logging verification removed during Hardhat 3 migration +// In Hardhat 2, makeUpgradeProxy accepted a debugStub as the third parameter: +// const upgradeProxy = makeUpgradeProxy(hre, false, debugStub); +// In Hardhat 3, the signature changed to: +// makeUpgradeProxy(hre, isDefender, connection) +// The debug logger is no longer injectable, so we can't verify the specific debug messages: +// - "Unexpected type for UPGRADE_INTERFACE_VERSION at address ... Expected a string" +// - "Unknown UPGRADE_INTERFACE_VERSION Hello, Hardhat! for proxy at ... Expected 5.0.0" +// These messages are still logged internally by upgrades-core, but we now test functional +// behavior (that upgrades succeed despite unknown interface versions) rather than +// implementation details (debug message content). + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { + upgrades = await upgradesFactory(hre, connection); t.context.GreeterProxiable40Fallback = await ethers.getContractFactory('GreeterProxiable40Fallback'); t.context.GreeterProxiable40FallbackV2 = await ethers.getContractFactory('GreeterProxiable40FallbackV2'); @@ -18,19 +39,12 @@ test('unknown upgrades interface version due to fallback returning non-string', const greeter = await upgrades.deployProxy(GreeterProxiable40Fallback, ['Hello, Hardhat!'], { kind: 'uups' }); t.is(await greeter.greet(), 'Hello, Hardhat!'); - const debugStub = sinon.stub(); - const upgradeProxy = require('../dist/upgrade-proxy').makeUpgradeProxy(hre, false, debugStub); - - const greeter2 = await upgradeProxy(greeter, GreeterProxiable40FallbackV2); + // The upgrade should succeed even though the proxy has an unknown upgrade interface version + // The system handles this gracefully and logs a debug message internally + const greeter2 = await upgrades.upgradeProxy(greeter, GreeterProxiable40FallbackV2); await greeter2.resetGreeting(); t.is(await greeter2.greet(), 'Hello World'); - - t.true( - debugStub.calledWith( - `Unexpected type for UPGRADE_INTERFACE_VERSION at address ${await greeter.getAddress()}. Expected a string`, - ), - ); }); test('unknown upgrades interface version due to fallback returning string', async t => { @@ -39,16 +53,9 @@ test('unknown upgrades interface version due to fallback returning string', asyn const greeter = await upgrades.deployProxy(GreeterProxiable40FallbackString, ['Hello, Hardhat!'], { kind: 'uups' }); t.is(await greeter.greet(), 'Hello, Hardhat!'); - const debugStub = sinon.stub(); - const upgradeProxy = require('../dist/upgrade-proxy').makeUpgradeProxy(hre, false, debugStub); - - const greeter2 = await upgradeProxy(greeter, GreeterProxiable40FallbackStringV2); + // The upgrade should succeed even though the proxy has an unknown upgrade interface version + // The system handles this gracefully and logs a debug message internally + const greeter2 = await upgrades.upgradeProxy(greeter, GreeterProxiable40FallbackStringV2); await greeter2.resetGreeting(); t.is(await greeter2.greet(), 'Hello World'); - - t.true( - debugStub.calledWith( - `Unknown UPGRADE_INTERFACE_VERSION Hello, Hardhat! for proxy at ${await greeter.getAddress()}. Expected 5.0.0`, - ), - ); }); diff --git a/packages/plugin-hardhat/test/uups-upgrade-storage.js b/packages/plugin-hardhat/test/uups-upgrade-storage.js index b1cb9e9b7..a3e1f85fa 100644 --- a/packages/plugin-hardhat/test/uups-upgrade-storage.js +++ b/packages/plugin-hardhat/test/uups-upgrade-storage.js @@ -1,14 +1,28 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); - t.context.GreeterStorageConflict = await ethers.getContractFactory('GreeterStorageConflictProxiable'); + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); + t.context.GreeterStorageConflict = await ethers.getContractFactory( + 'contracts/InvalidGreeter.sol:GreeterStorageConflictProxiable', + ); }); test('incompatible storage', async t => { const { Greeter, GreeterStorageConflict } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'uups' }); await t.throwsAsync( () => upgrades.upgradeProxy(greeter, GreeterStorageConflict), @@ -19,6 +33,7 @@ test('incompatible storage', async t => { test('incompatible storage - forced', async t => { const { Greeter, GreeterStorageConflict } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'uups' }); await upgrades.upgradeProxy(greeter, GreeterStorageConflict, { unsafeSkipStorageCheck: true }); }); diff --git a/packages/plugin-hardhat/test/uups-upgrade-validation.js b/packages/plugin-hardhat/test/uups-upgrade-validation.js index cf33a015f..14e00883c 100644 --- a/packages/plugin-hardhat/test/uups-upgrade-validation.js +++ b/packages/plugin-hardhat/test/uups-upgrade-validation.js @@ -1,15 +1,27 @@ -const test = require('ava'); +import test from 'ava'; +import hre from 'hardhat'; -const { ethers, upgrades } = require('hardhat'); +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); test.before(async t => { - t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); + upgrades = await upgradesFactory(hre, connection); + t.context.Greeter = await ethers.getContractFactory('contracts/Greeter.sol:GreeterProxiable'); t.context.Invalid = await ethers.getContractFactory('InvalidProxiable'); }); test('invalid upgrade', async t => { const { Greeter, Invalid } = t.context; + const signer = await ethers.provider.getSigner(); const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'uups' }); await t.throwsAsync( () => upgrades.upgradeProxy(greeter, Invalid), diff --git a/packages/plugin-hardhat/tsconfig.json b/packages/plugin-hardhat/tsconfig.json index c42543bee..4df6ba8ab 100644 --- a/packages/plugin-hardhat/tsconfig.json +++ b/packages/plugin-hardhat/tsconfig.json @@ -1,16 +1,25 @@ { - "extends": "../../tsconfig.base.json", "compilerOptions": { - "composite": true, + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, "outDir": "dist", "rootDir": "src", - "resolveJsonModule": true, + "composite": true, "skipLibCheck": true, + "strict": true, + "sourceMap": true }, "include": [ "src/**/*" ], "references": [ - { "path": "../core" } + { + "path": "../core" + } ] -} +} \ No newline at end of file diff --git a/submodules/openzeppelin-foundry-upgrades b/submodules/openzeppelin-foundry-upgrades index cfd861bc1..997228d69 160000 --- a/submodules/openzeppelin-foundry-upgrades +++ b/submodules/openzeppelin-foundry-upgrades @@ -1 +1 @@ -Subproject commit cfd861bc18ef4737e82eae6ec75304e27af699ef +Subproject commit 997228d69bbfc000984e0f50f9a47bf1235035ef diff --git a/yarn.lock b/yarn.lock index 0a7957714..3ef59f1fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -893,6 +893,136 @@ human-id "^4.1.1" prettier "^2.7.1" +"@esbuild/aix-ppc64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz#2ae33300598132cc4cf580dbbb28d30fed3c5c49" + integrity sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg== + +"@esbuild/android-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz#927708b3db5d739d6cb7709136924cc81bec9b03" + integrity sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ== + +"@esbuild/android-arm@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.11.tgz#571f94e7f4068957ec4c2cfb907deae3d01b55ae" + integrity sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg== + +"@esbuild/android-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.11.tgz#8a3bf5cae6c560c7ececa3150b2bde76e0fb81e6" + integrity sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g== + +"@esbuild/darwin-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz#0a678c4ac4bf8717e67481e1a797e6c152f93c84" + integrity sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w== + +"@esbuild/darwin-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz#70f5e925a30c8309f1294d407a5e5e002e0315fe" + integrity sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ== + +"@esbuild/freebsd-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz#4ec1db687c5b2b78b44148025da9632397553e8a" + integrity sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA== + +"@esbuild/freebsd-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz#4c81abd1b142f1e9acfef8c5153d438ca53f44bb" + integrity sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw== + +"@esbuild/linux-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz#69517a111acfc2b93aa0fb5eaeb834c0202ccda5" + integrity sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA== + +"@esbuild/linux-arm@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz#58dac26eae2dba0fac5405052b9002dac088d38f" + integrity sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw== + +"@esbuild/linux-ia32@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz#b89d4efe9bdad46ba944f0f3b8ddd40834268c2b" + integrity sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw== + +"@esbuild/linux-loong64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz#11f603cb60ad14392c3f5c94d64b3cc8b630fbeb" + integrity sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw== + +"@esbuild/linux-mips64el@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz#b7d447ff0676b8ab247d69dac40a5cf08e5eeaf5" + integrity sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ== + +"@esbuild/linux-ppc64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz#b3a28ed7cc252a61b07ff7c8fd8a984ffd3a2f74" + integrity sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw== + +"@esbuild/linux-riscv64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz#ce75b08f7d871a75edcf4d2125f50b21dc9dc273" + integrity sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww== + +"@esbuild/linux-s390x@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz#cd08f6c73b6b6ff9ccdaabbd3ff6ad3dca99c263" + integrity sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw== + +"@esbuild/linux-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz#3c3718af31a95d8946ebd3c32bb1e699bdf74910" + integrity sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ== + +"@esbuild/netbsd-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz#b4c767082401e3a4e8595fe53c47cd7f097c8077" + integrity sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg== + +"@esbuild/netbsd-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz#f2a930458ed2941d1f11ebc34b9c7d61f7a4d034" + integrity sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A== + +"@esbuild/openbsd-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz#b4ae93c75aec48bc1e8a0154957a05f0641f2dad" + integrity sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg== + +"@esbuild/openbsd-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz#b42863959c8dcf9b01581522e40012d2c70045e2" + integrity sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw== + +"@esbuild/openharmony-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz#b2e717141c8fdf6bddd4010f0912e6b39e1640f1" + integrity sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ== + +"@esbuild/sunos-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz#9fbea1febe8778927804828883ec0f6dd80eb244" + integrity sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA== + +"@esbuild/win32-arm64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz#501539cedb24468336073383989a7323005a8935" + integrity sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q== + +"@esbuild/win32-ia32@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz#8ac7229aa82cef8f16ffb58f1176a973a7a15343" + integrity sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA== + +"@esbuild/win32-x64@0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz#5ecda6f3fe138b7e456f4e429edde33c823f392f" + integrity sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -938,182 +1068,182 @@ "@ethereumjs/rlp" "^5.0.2" ethereum-cryptography "^2.2.1" -"@ethersproject/abi@^5.1.2": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" - integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/abstract-provider@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" - integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/networks" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/web" "^5.7.0" - -"@ethersproject/abstract-signer@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" - integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - -"@ethersproject/address@^5.0.2", "@ethersproject/address@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" - integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - -"@ethersproject/base64@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" - integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== - dependencies: - "@ethersproject/bytes" "^5.7.0" - -"@ethersproject/bignumber@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" - integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" +"@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.8.0.tgz#e79bb51940ac35fe6f3262d7fe2cdb25ad5f07d9" + integrity sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/hash" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + +"@ethersproject/abstract-provider@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz#7581f9be601afa1d02b95d26b9d9840926a35b0c" + integrity sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/networks" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/web" "^5.8.0" + +"@ethersproject/abstract-signer@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz#8d7417e95e4094c1797a9762e6789c7356db0754" + integrity sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA== + dependencies: + "@ethersproject/abstract-provider" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + +"@ethersproject/address@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.8.0.tgz#3007a2c352eee566ad745dca1dbbebdb50a6a983" + integrity sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + +"@ethersproject/base64@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.8.0.tgz#61c669c648f6e6aad002c228465d52ac93ee83eb" + integrity sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ== + dependencies: + "@ethersproject/bytes" "^5.8.0" + +"@ethersproject/bignumber@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.8.0.tgz#c381d178f9eeb370923d389284efa19f69efa5d7" + integrity sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" bn.js "^5.2.1" -"@ethersproject/bytes@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" - integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/constants@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" - integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - -"@ethersproject/hash@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" - integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/keccak256@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" - integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== - dependencies: - "@ethersproject/bytes" "^5.7.0" +"@ethersproject/bytes@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.8.0.tgz#9074820e1cac7507a34372cadeb035461463be34" + integrity sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A== + dependencies: + "@ethersproject/logger" "^5.8.0" + +"@ethersproject/constants@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.8.0.tgz#12f31c2f4317b113a4c19de94e50933648c90704" + integrity sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + +"@ethersproject/hash@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.8.0.tgz#b8893d4629b7f8462a90102572f8cd65a0192b4c" + integrity sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA== + dependencies: + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + +"@ethersproject/keccak256@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.8.0.tgz#d2123a379567faf2d75d2aaea074ffd4df349e6a" + integrity sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng== + dependencies: + "@ethersproject/bytes" "^5.8.0" js-sha3 "0.8.0" -"@ethersproject/logger@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" - integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== +"@ethersproject/logger@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.8.0.tgz#f0232968a4f87d29623a0481690a2732662713d6" + integrity sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA== -"@ethersproject/networks@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" - integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== +"@ethersproject/networks@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.8.0.tgz#8b4517a3139380cba9fb00b63ffad0a979671fde" + integrity sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg== dependencies: - "@ethersproject/logger" "^5.7.0" + "@ethersproject/logger" "^5.8.0" -"@ethersproject/properties@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" - integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== +"@ethersproject/properties@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.8.0.tgz#405a8affb6311a49a91dabd96aeeae24f477020e" + integrity sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw== dependencies: - "@ethersproject/logger" "^5.7.0" + "@ethersproject/logger" "^5.8.0" -"@ethersproject/rlp@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" - integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== +"@ethersproject/rlp@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.8.0.tgz#5a0d49f61bc53e051532a5179472779141451de5" + integrity sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q== dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" -"@ethersproject/signing-key@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" - integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== +"@ethersproject/signing-key@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.8.0.tgz#9797e02c717b68239c6349394ea85febf8893119" + integrity sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w== dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" bn.js "^5.2.1" - elliptic "6.5.4" + elliptic "6.6.1" hash.js "1.1.7" -"@ethersproject/strings@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" - integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/transactions@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" - integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - -"@ethersproject/web@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" - integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== - dependencies: - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" +"@ethersproject/strings@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.8.0.tgz#ad79fafbf0bd272d9765603215ac74fd7953908f" + integrity sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + +"@ethersproject/transactions@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.8.0.tgz#1e518822403abc99def5a043d1c6f6fe0007e46b" + integrity sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + "@ethersproject/signing-key" "^5.8.0" + +"@ethersproject/web@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.8.0.tgz#3e54badc0013b7a801463a7008a87988efce8a37" + integrity sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw== + dependencies: + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" "@fastify/busboy@^2.0.0": version "2.1.1" @@ -1156,6 +1286,13 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1235,31 +1372,18 @@ globby "^11.0.0" read-yaml-file "^1.1.0" -"@mapbox/node-pre-gyp@^1.0.5": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" - integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== +"@mapbox/node-pre-gyp@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz#16d1d9049c0218820da81a12ae084e7fe67790d1" + integrity sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg== dependencies: + consola "^3.2.3" detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" + https-proxy-agent "^7.0.5" node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@metamask/eth-sig-util@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" - integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== - dependencies: - ethereumjs-abi "^0.6.8" - ethereumjs-util "^6.2.1" - ethjs-util "^0.1.6" - tweetnacl "^1.0.3" - tweetnacl-util "^0.15.1" + nopt "^8.0.0" + semver "^7.5.3" + tar "^7.4.0" "@noble/curves@1.2.0": version "1.2.0" @@ -1302,11 +1426,16 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== -"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": +"@noble/secp256k1@1.7.1": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== +"@noble/secp256k1@~1.7.0": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.2.tgz#c2c3343e2dce80e15a914d7442147507f8a98e7f" + integrity sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1328,162 +1457,169 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomicfoundation/edr-darwin-arm64@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.11.0.tgz#fa791451c5ce2acf6634143bca9fe8f1b5c66603" - integrity sha512-aYTVdcSs27XG7ayTzvZ4Yn9z/ABSaUwicrtrYK2NR8IH0ik4N4bWzo/qH8rax6rewVLbHUkGyGYnsy5ZN4iiMw== - -"@nomicfoundation/edr-darwin-arm64@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz#72f7a826c9f0f2c91308edca562de3b9484ac079" - integrity sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A== - -"@nomicfoundation/edr-darwin-x64@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.11.0.tgz#b1aaf0bfb331f6d136a92cbe31f184e2209e7a4f" - integrity sha512-RxX7UYgvJrfcyT/uHUn44Nsy1XaoW+Q1khKMdHKxeW7BrgIi+Lz+siz3bX5vhSoAnKilDPhIVLrnC8zxQhjR2A== - -"@nomicfoundation/edr-darwin-x64@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz#6d0fedb219d664631c6feddc596ab8c3bbc36fa8" - integrity sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg== - -"@nomicfoundation/edr-linux-arm64-gnu@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.11.0.tgz#fef6763c5d42bb68b4fc95df45c4745a0e31df93" - integrity sha512-J0j+rs0s11FuSipt/ymqrFmpJ7c0FSz1/+FohCIlUXDxFv//+1R/8lkGPjEYFmy8DPpk/iO8mcpqHTGckREbqA== - -"@nomicfoundation/edr-linux-arm64-gnu@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz#60e4d52d963141bc2bb4a02639dc590a7fbdda2f" - integrity sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA== - -"@nomicfoundation/edr-linux-arm64-musl@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.11.0.tgz#ef89d5d2aefc1f8d4f7c699c59b8897a645d33eb" - integrity sha512-4r32zkGMN7WT/CMEuW0VjbuEdIeCskHNDMW4SSgQSJOE/N9L1KSLJCSsAbPD3aYE+e4WRDTyOwmuLjeUTcLZKQ== - -"@nomicfoundation/edr-linux-arm64-musl@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz#6676a09eab57c435a16ffc144658c896acca9baa" - integrity sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg== - -"@nomicfoundation/edr-linux-x64-gnu@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.11.0.tgz#97432126110aa805b761d4743ab158698cae6d66" - integrity sha512-SmdncQHLYtVNWLIMyGaY6LpAfamzTDe3fxjkirmJv3CWR5tcEyC6LMui/GsIVnJzXeNJBXAzwl8hTUAxHTM6kQ== - -"@nomicfoundation/edr-linux-x64-gnu@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz#f558d9697ce961410e7a7468f9ab8c8a601b9df6" - integrity sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A== - -"@nomicfoundation/edr-linux-x64-musl@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.11.0.tgz#7605fddbada22dfdd14b15f4ac562014d9c82332" - integrity sha512-w6hUqpn/trwiH6SRuRGysj37LsQVCX5XDCA3Xi81sbOaLhbHrNvK9TXWyZmcuzbdTKQQW6VNywcSxDdOiChcJg== - -"@nomicfoundation/edr-linux-x64-musl@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz#c9c9cbb2997499f75c1d022be724b0551d44569f" - integrity sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA== - -"@nomicfoundation/edr-win32-x64-msvc@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.11.0.tgz#6766175f3ec47bfbda0429ca00fed4ae5632a3c4" - integrity sha512-BLmULjRKoH9BsX+c4Na2ypV7NGeJ+M6Zpqj/faPOwleVscDdSr/IhriyPaXCe8dyfwbge7lWsbekiADtPSnB2Q== - -"@nomicfoundation/edr-win32-x64-msvc@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz#f16db88bf4fe09a996af0a25096e09deecb72bfa" - integrity sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w== - -"@nomicfoundation/edr@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.11.0.tgz#d8b0ba4dfd7d93b9c54762e72eb9cd4e8244ce46" - integrity sha512-36WERf8ldvyHR6UAbcYsa+vpbW7tCrJGBwF4gXSsb8+STj1n66Hz85Y/O7B9+8AauX3PhglvV5dKl91tk43mWw== - dependencies: - "@nomicfoundation/edr-darwin-arm64" "0.11.0" - "@nomicfoundation/edr-darwin-x64" "0.11.0" - "@nomicfoundation/edr-linux-arm64-gnu" "0.11.0" - "@nomicfoundation/edr-linux-arm64-musl" "0.11.0" - "@nomicfoundation/edr-linux-x64-gnu" "0.11.0" - "@nomicfoundation/edr-linux-x64-musl" "0.11.0" - "@nomicfoundation/edr-win32-x64-msvc" "0.11.0" - -"@nomicfoundation/edr@^0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.5.2.tgz#e8c7b3d3dd4a312432ab3930dec60f76dc5c4926" - integrity sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw== - dependencies: - "@nomicfoundation/edr-darwin-arm64" "0.5.2" - "@nomicfoundation/edr-darwin-x64" "0.5.2" - "@nomicfoundation/edr-linux-arm64-gnu" "0.5.2" - "@nomicfoundation/edr-linux-arm64-musl" "0.5.2" - "@nomicfoundation/edr-linux-x64-gnu" "0.5.2" - "@nomicfoundation/edr-linux-x64-musl" "0.5.2" - "@nomicfoundation/edr-win32-x64-msvc" "0.5.2" - -"@nomicfoundation/ethereumjs-common@4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz#9901f513af2d4802da87c66d6f255b510bef5acb" - integrity sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg== - dependencies: - "@nomicfoundation/ethereumjs-util" "9.0.4" - -"@nomicfoundation/ethereumjs-rlp@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz#66c95256fc3c909f6fb18f6a586475fc9762fa30" - integrity sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw== - -"@nomicfoundation/ethereumjs-tx@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz#b0ceb58c98cc34367d40a30d255d6315b2f456da" - integrity sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw== - dependencies: - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-util@9.0.4": - version "9.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz#84c5274e82018b154244c877b76bc049a4ed7b38" - integrity sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q== +"@nomicfoundation/edr-darwin-arm64@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.11.3.tgz#d8e2609fc24cf20e75c3782e39cd5a95f7488075" + integrity sha512-w0tksbdtSxz9nuzHKsfx4c2mwaD0+l5qKL2R290QdnN9gi9AV62p9DHkOgfBdyg6/a6ZlnQqnISi7C9avk/6VA== + +"@nomicfoundation/edr-darwin-arm64@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.24.tgz#78ce3e9eba3b2b4ea56c19cce3443de7c92d9460" + integrity sha512-lYcD9IM52G0hk/3Sso2Rpdpyfafy3aHH0GsSy/FVog9UrEkmmU14AmccE18/zTL+UyV0yzYMDOmh6y83SD/lbg== + +"@nomicfoundation/edr-darwin-x64@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.11.3.tgz#7a9e94cee330269a33c7f1dce267560c7e12dbd3" + integrity sha512-QR4jAFrPbOcrO7O2z2ESg+eUeIZPe2bPIlQYgiJ04ltbSGW27FblOzdd5+S3RoOD/dsZGKAvvy6dadBEl0NgoA== + +"@nomicfoundation/edr-darwin-x64@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.24.tgz#899ca3ebbd47c861c86ad97aed8853a5804a667b" + integrity sha512-cHDJZlPDpDXJXxQDVM0TGzEuNvV3wW94gipEdjNxZHeC9T2/NU/5GUoQajMJgvCZ6PWDlRMwIBRtM1jC/ny5DA== + +"@nomicfoundation/edr-linux-arm64-gnu@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.11.3.tgz#cd5ec90c7263045c3dfd0b109c73206e488edc27" + integrity sha512-Ktjv89RZZiUmOFPspuSBVJ61mBZQ2+HuLmV67InNlh9TSUec/iDjGIwAn59dx0bF/LOSrM7qg5od3KKac4LJDQ== + +"@nomicfoundation/edr-linux-arm64-gnu@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.24.tgz#622cd337fe2fc7711d4296317ca4606a99b49444" + integrity sha512-G/iln4W79CR9f68+crBZM1kBdmmK3IbQCD4b5u+iqby+H5BOLSPQmjeW9UREK5WSecnv7Oxr/ZTHHRq/w9pUPA== + +"@nomicfoundation/edr-linux-arm64-musl@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.11.3.tgz#ed23df2d9844470f5661716da27d99a72a69e99e" + integrity sha512-B3sLJx1rL2E9pfdD4mApiwOZSrX0a/KQSBWdlq1uAhFKqkl00yZaY4LejgZndsJAa4iKGQJlGnw4HCGeVt0+jA== + +"@nomicfoundation/edr-linux-arm64-musl@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.24.tgz#4d2b6a6a061d293e2291dadf015d843967f41824" + integrity sha512-wt6UuOutufL3UTSyMiwPOyfRly3uQEFHASXqLsNjgp4qBrm0s+kkyaYpAe8h53lGzZmXIDOAbO0P/fwxnLCnWw== + +"@nomicfoundation/edr-linux-x64-gnu@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.11.3.tgz#87a62496c2c4b808bc4a9ae96cca1642a21c2b51" + integrity sha512-D/4cFKDXH6UYyKPu6J3Y8TzW11UzeQI0+wS9QcJzjlrrfKj0ENW7g9VihD1O2FvXkdkTjcCZYb6ai8MMTCsaVw== + +"@nomicfoundation/edr-linux-x64-gnu@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.24.tgz#6c86c8f59514885887a706dd1e0994355ff5a466" + integrity sha512-mHgkUSynINTnnIvZuZymJ4dMqjemGjdrzQ87rP5/SQQGRQVV82uDomSEglp9btSmbBWfPj4r4tWsV+a3844W0w== + +"@nomicfoundation/edr-linux-x64-musl@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.11.3.tgz#8cfe408c73bcb9ed5e263910c313866d442f4b48" + integrity sha512-ergXuIb4nIvmf+TqyiDX5tsE49311DrBky6+jNLgsGDTBaN1GS3OFwFS8I6Ri/GGn6xOaT8sKu3q7/m+WdlFzg== + +"@nomicfoundation/edr-linux-x64-musl@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.24.tgz#68b737e5fcde210028750172388932926e9068e8" + integrity sha512-E0XNSlPc8Hx5Nhowe5VIvAqVeT+1VUWSRqG0cZtYcpUgJZxTp8p03ojPtbyfjL4T+78GfnpmzkkLhB6S2jZ1FQ== + +"@nomicfoundation/edr-win32-x64-msvc@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.11.3.tgz#fb208b94553c7eb22246d73a1ac4de5bfdb97d01" + integrity sha512-snvEf+WB3OV0wj2A7kQ+ZQqBquMcrozSLXcdnMdEl7Tmn+KDCbmFKBt3Tk0X3qOU4RKQpLPnTxdM07TJNVtung== + +"@nomicfoundation/edr-win32-x64-msvc@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.24.tgz#7320d2e7883702d4f607a0a955457d2a837e644a" + integrity sha512-PbtY2zWc4k8HK4gVnVbPohJnfrICboo6J91vxTlhnPKCWGvfGbsqLfDUAp91ExHHY+80qRfQnwaLbhJiIqLFGw== + +"@nomicfoundation/edr@0.12.0-next.24": + version "0.12.0-next.24" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.12.0-next.24.tgz#736c6566dee65ff3de8864c0601ecc529ba3c351" + integrity sha512-/NwB9yX7uBs/FIJKHBZo2hVhP7g3v6LbE21JvTLvshgb+XscyaRRUmzB//ankxLGJ1TehtXAf/Qh/a19vgpiig== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.12.0-next.24" + "@nomicfoundation/edr-darwin-x64" "0.12.0-next.24" + "@nomicfoundation/edr-linux-arm64-gnu" "0.12.0-next.24" + "@nomicfoundation/edr-linux-arm64-musl" "0.12.0-next.24" + "@nomicfoundation/edr-linux-x64-gnu" "0.12.0-next.24" + "@nomicfoundation/edr-linux-x64-musl" "0.12.0-next.24" + "@nomicfoundation/edr-win32-x64-msvc" "0.12.0-next.24" + +"@nomicfoundation/edr@^0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.11.3.tgz#e8b30b868788e45d7a2ee2359a021ef7dcb96952" + integrity sha512-kqILRkAd455Sd6v8mfP3C1/0tCOynJWY+Ir+k/9Boocu2kObCrsFgG+ZWB7fSBVdd9cPVSNrnhWS+V+PEo637g== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.11.3" + "@nomicfoundation/edr-darwin-x64" "0.11.3" + "@nomicfoundation/edr-linux-arm64-gnu" "0.11.3" + "@nomicfoundation/edr-linux-arm64-musl" "0.11.3" + "@nomicfoundation/edr-linux-x64-gnu" "0.11.3" + "@nomicfoundation/edr-linux-x64-musl" "0.11.3" + "@nomicfoundation/edr-win32-x64-msvc" "0.11.3" + +"@nomicfoundation/hardhat-errors@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.7.tgz#9c5d45c38adf09f3db481dbc1b8138dbdc67eb04" + integrity sha512-l4RrzTfJ/WO0B9Te6i9161+pRbCSFXILanmHmgRfS4Bb4pDOjs+0eQf2I2cQrnqcJ6O/bxLvChPTQuad97dmQw== dependencies: - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - ethereum-cryptography "0.1.3" + "@nomicfoundation/hardhat-utils" "^4.0.0" "@nomicfoundation/hardhat-ethers@^3.0.5": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.7.tgz#be90f79e96a22e6e982bca20ab0af05ed1f09c26" - integrity sha512-pxLWpDiqC208shoz/lMbVFbxcVxE+qIs8qDrwdcubWH99UO1p6uwXakMa36ICRfB/IEToSLDJGSsKhwY84feCQ== + version "3.1.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.1.0.tgz#d33595e85cdb53e802391de64ee35910c078dbb0" + integrity sha512-jx6fw3Ms7QBwFGT2MU6ICG292z0P81u6g54JjSV105+FbTZOF4FJqPksLfDybxkkOeq28eDxbqq7vpxRYyIlxA== dependencies: debug "^4.1.1" lodash.isequal "^4.5.0" -"@nomicfoundation/hardhat-ethers@^3.0.6": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.9.tgz#a1fa5b123db39e4ee4ae86bee0e458e2b733ce02" - integrity sha512-xBJdRUiCwKpr0OYrOzPwAyNGtsVzoBx32HFPJVv6S+sFA9TmBIBDaqNlFPmBH58ZjgNnGhEr/4oBZvGr4q4TjQ== +"@nomicfoundation/hardhat-ethers@^4.0.0": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-4.0.5.tgz#b5a75e70477da8a19a36a281abaf3e0d8c5d62ce" + integrity sha512-DFTpsg6MBV/JNv8Rd0M/C70Wqk3giYSAYCrBu3PeerX1eNd4wuz5kUlsX4yVoNOPpM6Lva9z+30JRDEuFEt0bQ== dependencies: - debug "^4.1.1" - lodash.isequal "^4.5.0" + "@nomicfoundation/hardhat-errors" "^3.0.7" + "@nomicfoundation/hardhat-utils" "^4.0.0" + debug "^4.3.2" + ethereum-cryptography "^2.2.1" + ethers "^6.14.0" -"@nomicfoundation/hardhat-verify@^2.0.14": - version "2.0.14" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.14.tgz#ba80918fac840f1165825f2a422a694486f82f6f" - integrity sha512-z3iVF1WYZHzcdMMUuureFpSAfcnlfJbJx3faOnGrOYg6PRTki1Ut9JAuRccnFzMHf1AmTEoSUpWcyvBCoxL5Rg== +"@nomicfoundation/hardhat-utils@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.0.0.tgz#3e2fc740f6a38160ee2330c8780f748695b6f92c" + integrity sha512-Deu4od7flcM89K+SEAxmOyn7FFWGiEILrGjoxYl/Gus0tctgpLNaK3M4LIjrJ25ci8LBjGVe3i28XZA4+QGQHQ== dependencies: - "@ethersproject/abi" "^5.1.2" - "@ethersproject/address" "^5.0.2" - cbor "^8.1.0" - debug "^4.1.1" - lodash.clonedeep "^4.5.0" - picocolors "^1.1.0" - semver "^6.3.0" - table "^6.8.0" - undici "^5.14.0" + "@streamparser/json-node" "^0.0.22" + debug "^4.3.2" + env-paths "^2.2.0" + ethereum-cryptography "^2.2.1" + fast-equals "^5.4.0" + json-stream-stringify "^3.1.6" + rfdc "^1.3.1" + undici "^6.16.1" + +"@nomicfoundation/hardhat-vendored@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.1.tgz#414a4dfbd03cda6e9a21d1001ba543268a2db0bc" + integrity sha512-jBOAqmEAMJ8zdfiQmTLV+c0IaSyySqkDSJ9spTy8Ts/m/mO8w364TClyfn+p4ZpxBjyX4LMa3NfC402hoDtwCg== + +"@nomicfoundation/hardhat-verify@^3.0.10": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-3.0.11.tgz#384f3cd1001622b09d71e65ce5fb18df38a3ca67" + integrity sha512-AcEK5azLRnTyzjvkPKMN4RSCwXHtBRipIdJmN7/GTBfducik/3plG7m8TxNumWcERfCkh9+viLWXddpReocxZw== + dependencies: + "@ethersproject/abi" "^5.8.0" + "@nomicfoundation/hardhat-errors" "^3.0.7" + "@nomicfoundation/hardhat-utils" "^4.0.0" + "@nomicfoundation/hardhat-zod-utils" "^3.0.2" + cbor2 "^1.9.0" + chalk "^5.3.0" + debug "^4.3.2" + semver "^7.6.3" + zod "^3.23.8" + +"@nomicfoundation/hardhat-zod-utils@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.2.tgz#8f468a18ac7830c837771ed0e3a215e86354ac82" + integrity sha512-EtMIhi7jtpeQYd+pRQBNlxthi8OPVr/t32yn+VHHp6nwS5wgXLh6/KpvFZfJj5mBAUbOtogB7YQ4n5fpOeuggA== + dependencies: + "@nomicfoundation/hardhat-errors" "^3.0.7" + "@nomicfoundation/hardhat-utils" "^4.0.0" "@nomicfoundation/slang@^0.18.3": version "0.18.3" @@ -1527,7 +1663,7 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz#e6363d13b8709ca66f330562337dbc01ce8bbbd9" integrity sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA== -"@nomicfoundation/solidity-analyzer@^0.1.0": +"@nomicfoundation/solidity-analyzer@^0.1.0", "@nomicfoundation/solidity-analyzer@^0.1.1": version "0.1.2" resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz#8bcea7d300157bf3a770a851d9f5c5e2db34ac55" integrity sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA== @@ -1592,6 +1728,11 @@ lodash.startcase "^4.4.0" minimist "^1.2.0" +"@openzeppelin/foundry-upgrades@0.4.1-alpha.0": + version "0.4.1-alpha.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/foundry-upgrades/-/foundry-upgrades-0.4.1-alpha.0.tgz#62379eb7826dfba25214d895acc9432ed230fcd2" + integrity sha512-AhZiwM3pFq6OAs7yGj/rWKYMECqXBXffVlDRTs+oCeFPjdfE/MqjzyQSHCWFiYnJlOWDiP5GKz/8zcSbU4FjEQ== + "@openzeppelin/upgrades-core-legacy@npm:@openzeppelin/upgrades-core@1.31.3": version "1.31.3" resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.31.3.tgz#ff268a498fcd23106c33149e01dd3e7dfcae79fe" @@ -1616,20 +1757,16 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@rollup/pluginutils@^4.0.0": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" - integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== +"@rollup/pluginutils@^5.1.3": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.3.0.tgz#57ba1b0cbda8e7a3c597a4853c807b156e21a7b4" + integrity sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q== dependencies: - estree-walker "^2.0.1" - picomatch "^2.2.2" - -"@scure/base@~1.1.0": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.7.tgz#fe973311a5c6267846aa131bc72e96c5d40d2b30" - integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^4.0.2" -"@scure/base@~1.1.6": +"@scure/base@~1.1.0", "@scure/base@~1.1.6": version "1.1.9" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== @@ -1684,6 +1821,11 @@ "@sentry/utils" "5.30.0" tslib "^1.9.3" +"@sentry/core@^9.4.0": + version "9.46.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-9.46.0.tgz#be4824d8b028b3184f9a05be9b232c9440cb35ac" + integrity sha512-it7JMFqxVproAgEtbLgCVBYtQ9fIb+Bu0JD+cEplTN/Ukpe6GaolyYib5geZqslVxhp2sQgT+58aGvfd/k0N8Q== + "@sentry/hub@5.30.0": version "5.30.0" resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" @@ -1761,12 +1903,11 @@ "@sinonjs/commons" "^3.0.1" "@sinonjs/samsam@^8.0.1": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.2.tgz#e4386bf668ff36c95949e55a38dc5f5892fc2689" - integrity sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw== + version "8.0.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.3.tgz#eb6ffaef421e1e27783cc9b52567de20cb28072d" + integrity sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ== dependencies: "@sinonjs/commons" "^3.0.1" - lodash.get "^4.4.2" type-detect "^4.1.0" "@smithy/abort-controller@^4.0.1": @@ -2223,12 +2364,17 @@ "@smithy/types" "^4.1.0" tslib "^2.6.2" -"@types/bn.js@^4.11.3": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== +"@streamparser/json-node@^0.0.22": + version "0.0.22" + resolved "https://registry.yarnpkg.com/@streamparser/json-node/-/json-node-0.0.22.tgz#cee66bb2f175e41abf83574dc7d6fe9c997d94d5" + integrity sha512-sJT2ptNRwqB1lIsQrQlCoWk5rF4tif9wDh+7yluAGijJamAhrHGYpFB/Zg3hJeceoZypi74ftXk8DHzwYpbZSg== dependencies: - "@types/node" "*" + "@streamparser/json" "^0.0.22" + +"@streamparser/json@^0.0.22": + version "0.0.22" + resolved "https://registry.yarnpkg.com/@streamparser/json/-/json-0.0.22.tgz#8ddcbcc8c3ca77aeadf80af47f54a64c8739a037" + integrity sha512-b6gTSBjJ8G8SuO3Gbbj+zXbVx8NSs1EbpbMKpzGLWMdkR+98McH9bEjSz3+0mPJf68c5nxa3CrJHp5EQNXM6zQ== "@types/bn.js@^5.1.0": version "5.1.5" @@ -2244,6 +2390,11 @@ dependencies: "@types/ms" "*" +"@types/estree@^1.0.0": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" @@ -2264,11 +2415,6 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/lru-cache@^5.1.0": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" - integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== - "@types/minimist@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" @@ -2280,9 +2426,9 @@ integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w== "@types/ms@*": - version "0.7.34" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" - integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" + integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== "@types/node@*": version "22.5.0" @@ -2304,11 +2450,11 @@ integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== "@types/node@^20.0.0": - version "20.17.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.43.tgz#66e6923e8e5a56317866c7813dc2b555c158332a" - integrity sha512-DnDEcDUnVAUYSa7U03QvrXbj1MZj00xoyi/a3lRGkR/c7BFUnqv+OY9EUphMqXUKdZJEOmuzu2mm+LmCisnPow== + version "20.19.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.21.tgz#6e5378e04993c40395473b13baf94a09875157b8" + integrity sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA== dependencies: - undici-types "~6.19.2" + undici-types "~6.21.0" "@types/normalize-package-data@^2.4.0": version "2.4.4" @@ -2342,9 +2488,9 @@ "@types/node" "*" "@types/sinon@^17.0.0": - version "17.0.3" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.3.tgz#9aa7e62f0a323b9ead177ed23a36ea757141a5fa" - integrity sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw== + version "17.0.4" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.4.tgz#fd9a3e8e07eea1a3f4a6f82a972c899e5778f369" + integrity sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew== dependencies: "@types/sinonjs__fake-timers" "*" @@ -2451,30 +2597,30 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@vercel/nft@^0.26.2": - version "0.26.5" - resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.26.5.tgz#f21e40576b76446851b6cbff79f39a72dab4d6b2" - integrity sha512-NHxohEqad6Ra/r4lGknO52uc/GrWILXAMs1BB4401GTqww0fw1bAqzpG1XHuDO+dprg4GvsD9ZLLSsdo78p9hQ== +"@vercel/nft@^0.29.4": + version "0.29.4" + resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.29.4.tgz#e56b07d193776bcf67b31ac4da065ceb8e8d362e" + integrity sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA== dependencies: - "@mapbox/node-pre-gyp" "^1.0.5" - "@rollup/pluginutils" "^4.0.0" + "@mapbox/node-pre-gyp" "^2.0.0" + "@rollup/pluginutils" "^5.1.3" acorn "^8.6.0" - acorn-import-attributes "^1.9.2" + acorn-import-attributes "^1.9.5" async-sema "^3.1.1" bindings "^1.4.0" estree-walker "2.0.2" - glob "^7.1.3" + glob "^10.4.5" graceful-fs "^4.2.9" - micromatch "^4.0.2" node-gyp-build "^4.2.2" + picomatch "^4.0.2" resolve-from "^5.0.0" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-3.0.1.tgz#8ac8b3b5024d31464fe2a5feeea9f4536bf44025" + integrity sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg== -acorn-import-attributes@^1.9.2: +acorn-import-attributes@^1.9.5: version "1.9.5" resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== @@ -2484,14 +2630,19 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.3.2: - version "8.3.3" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" - integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== +acorn-walk@^8.3.4: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" -acorn@^8.11.0, acorn@^8.11.3, acorn@^8.6.0, acorn@^8.9.0: +acorn@^8.11.0, acorn@^8.15.0, acorn@^8.6.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +acorn@^8.9.0: version "8.12.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -2513,6 +2664,11 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -2531,16 +2687,6 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - amazon-cognito-identity-js@^6.3.6: version "6.3.12" resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.12.tgz#af73df033094ad4c679c19cf6122b90058021619" @@ -2582,9 +2728,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" @@ -2601,9 +2747,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: color-convert "^2.0.1" ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== anymatch@~3.1.2: version "3.1.3" @@ -2620,24 +2766,11 @@ append-transform@^2.0.0: dependencies: default-require-extensions "^3.0.0" -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2650,14 +2783,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-buffer-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" - integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== - dependencies: - call-bind "^1.0.5" - is-array-buffer "^3.0.4" - array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -2668,32 +2793,6 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.findlast@^1.2.2: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" - integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" - -arraybuffer.prototype.slice@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" - integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== - dependencies: - array-buffer-byte-length "^1.0.1" - call-bind "^1.0.5" - define-properties "^1.2.1" - es-abstract "^1.22.3" - es-errors "^1.2.1" - get-intrinsic "^1.2.3" - is-array-buffer "^3.0.4" - is-shared-array-buffer "^1.0.2" - arrgv@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/arrgv/-/arrgv-1.0.2.tgz#025ed55a6a433cad9b604f8112fc4292715a6ec0" @@ -2704,11 +2803,6 @@ arrify@^3.0.0: resolved "https://registry.yarnpkg.com/arrify/-/arrify-3.0.0.tgz#ccdefb8eaf2a1d2ab0da1ca2ce53118759fd46bc" integrity sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async-retry@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" @@ -2727,58 +2821,51 @@ asynckit@^0.4.0: integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== ava@^6.0.0: - version "6.1.3" - resolved "https://registry.yarnpkg.com/ava/-/ava-6.1.3.tgz#aed54a4528653c7a62b6d68d0a53608b22a5b1dc" - integrity sha512-tkKbpF1pIiC+q09wNU9OfyTDYZa8yuWvU2up3+lFJ3lr1RmnYh2GBpPwzYUEB0wvTPIUysGjcZLNZr7STDviRA== + version "6.4.1" + resolved "https://registry.yarnpkg.com/ava/-/ava-6.4.1.tgz#89ce905d73bcd6c1d55bbba2598df3dc74e957dd" + integrity sha512-vxmPbi1gZx9zhAjHBgw81w/iEDKcrokeRk/fqDTyA2DQygZ0o+dUGRHFOtX8RA5N0heGJTTsIk7+xYxitDb61Q== dependencies: - "@vercel/nft" "^0.26.2" - acorn "^8.11.3" - acorn-walk "^8.3.2" + "@vercel/nft" "^0.29.4" + acorn "^8.15.0" + acorn-walk "^8.3.4" ansi-styles "^6.2.1" arrgv "^1.0.2" arrify "^3.0.0" - callsites "^4.1.0" - cbor "^9.0.1" - chalk "^5.3.0" + callsites "^4.2.0" + cbor "^10.0.9" + chalk "^5.4.1" chunkd "^2.0.1" - ci-info "^4.0.0" + ci-info "^4.3.0" ci-parallel-vars "^1.0.1" cli-truncate "^4.0.0" code-excerpt "^4.0.0" common-path-prefix "^3.0.0" concordance "^5.0.4" currently-unhandled "^0.4.1" - debug "^4.3.4" - emittery "^1.0.1" - figures "^6.0.1" - globby "^14.0.0" + debug "^4.4.1" + emittery "^1.2.0" + figures "^6.1.0" + globby "^14.1.0" ignore-by-default "^2.1.0" indent-string "^5.0.0" is-plain-object "^5.0.0" is-promise "^4.0.0" matcher "^5.0.0" - memoize "^10.0.0" + memoize "^10.1.0" ms "^2.1.3" - p-map "^7.0.1" + p-map "^7.0.3" package-config "^5.0.0" - picomatch "^3.0.1" + picomatch "^4.0.2" plur "^5.1.0" - pretty-ms "^9.0.0" + pretty-ms "^9.2.0" resolve-cwd "^3.0.0" stack-utils "^2.0.6" strip-ansi "^7.1.0" supertap "^3.0.1" temp-dir "^3.0.0" - write-file-atomic "^5.0.1" + write-file-atomic "^6.0.0" yargs "^17.7.2" -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - axios@^1.7.4: version "1.8.4" resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.4.tgz#78990bb4bc63d2cae072952d374835950a82f447" @@ -2844,16 +2931,21 @@ blueimp-md5@^2.10.0: resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== -bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9: +bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: +bn.js@^5.1.2, bn.js@^5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +bn.js@^5.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566" + integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== + bowser@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -2982,23 +3074,12 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -callsites@^4.1.0: +callsites@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-4.2.0.tgz#98761d5be3ce092e4b9c92f7fb8c8eb9b83cadc8" integrity sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ== @@ -3018,6 +3099,11 @@ caniuse-lite@^1.0.30001646: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz#52de59529e8b02b1aedcaaf5c05d9e23c0c28138" integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg== +cbor2@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/cbor2/-/cbor2-1.12.0.tgz#b1cae821ee8d673cff70b078c3f08041bc47bdde" + integrity sha512-3Cco8XQhi27DogSp9Ri6LYNZLi/TBY/JVnDe+mj06NkBjW/ZYOtekaEU4wZ4xcRMNrFkDv8KNtOAqHyDfz3lYg== + cbor@^10.0.0: version "10.0.3" resolved "https://registry.yarnpkg.com/cbor/-/cbor-10.0.3.tgz#202d79cd696f408700af51b0c9771577048a860e" @@ -3025,14 +3111,14 @@ cbor@^10.0.0: dependencies: nofilter "^3.0.2" -cbor@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" - integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== +cbor@^10.0.9: + version "10.0.11" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-10.0.11.tgz#f60e7cc2be6c943fecec159874ae651e75661745" + integrity sha512-vIwORDd/WyB8Nc23o2zNN5RrtFGlR6Fca61TtjkUXueI3Jf2DOZDl1zsshvBntZ3wZHBM9ztjnkXSmzQDaq3WA== dependencies: - nofilter "^3.1.0" + nofilter "^3.0.2" -cbor@^9.0.0, cbor@^9.0.1: +cbor@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/cbor/-/cbor-9.0.2.tgz#536b4f2d544411e70ec2b19a2453f10f83cd9fdb" integrity sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ== @@ -3064,17 +3150,17 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" - integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== +chalk@^5.3.0, chalk@^5.4.1: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^3.4.0, chokidar@^3.5.3: +chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -3089,17 +3175,17 @@ chokidar@^3.4.0, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chokidar@^4.0.0: +chokidar@^4.0.0, chokidar@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== dependencies: readdirp "^4.0.1" -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== chunkd@^2.0.1: version "2.0.1" @@ -3121,6 +3207,11 @@ ci-info@^4.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== +ci-info@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" + integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== + ci-parallel-vars@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz#e87ff0625ccf9d286985b29b4ada8485ca9ffbc2" @@ -3226,11 +3317,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -3282,10 +3368,10 @@ concordance@^5.0.4: semver "^7.3.2" well-known-symbols "^2.0.0" -console-control-strings@^1.0.0, console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== +consola@^3.2.3: + version "3.4.2" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== convert-source-map@^1.7.0: version "1.9.0" @@ -3357,7 +3443,7 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cross-spawn@^7.0.5: +cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -3373,33 +3459,6 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -data-view-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" - integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -data-view-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" - integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -data-view-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" - integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-data-view "^1.0.1" - dataloader@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" @@ -3412,7 +3471,14 @@ date-time@^3.1.0: dependencies: time-zone "^1.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: +debug@4, debug@^4.3.5, debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== @@ -3441,34 +3507,11 @@ default-require-extensions@^3.0.0: dependencies: strip-bom "^4.0.0" -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.2.0, define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -3480,9 +3523,9 @@ detect-indent@^6.0.0: integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-libc@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" - integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== diff@^5.2.0: version "5.2.0" @@ -3509,9 +3552,9 @@ doctrine@^3.0.0: esutils "^2.0.2" dotenv@^16.0.0: - version "16.4.5" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" - integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== dotenv@^8.1.0: version "8.6.0" @@ -3528,10 +3571,10 @@ electron-to-chromium@^1.5.4: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6" integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q== -elliptic@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== +elliptic@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -3541,7 +3584,7 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -elliptic@^6.5.2, elliptic@^6.5.7: +elliptic@^6.5.7: version "6.5.7" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== @@ -3554,15 +3597,15 @@ elliptic@^6.5.2, elliptic@^6.5.7: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emittery@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-1.0.3.tgz#c9d2a9c689870f15251bb13b31c67715c26d69ac" - integrity sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA== +emittery@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-1.2.0.tgz#466edb32bada8d9b35f779c3ee8c514ac2df8dc0" + integrity sha512-KxdRyyFcS85pH3dnU8Y5yFUm2YJdaHwcBZWrfG8o89ZY9a13/f9itbN+YG3ELbBo9Pg5zvIozstmuV8bX13q6g== emoji-regex@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" - integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + version "10.6.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== emoji-regex@^7.0.1: version "7.0.3" @@ -3606,108 +3649,49 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: - version "1.23.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" - integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== - dependencies: - array-buffer-byte-length "^1.0.1" - arraybuffer.prototype.slice "^1.0.3" - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - data-view-buffer "^1.0.1" - data-view-byte-length "^1.0.1" - data-view-byte-offset "^1.0.0" - es-define-property "^1.0.0" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-set-tostringtag "^2.0.3" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.6" - get-intrinsic "^1.2.4" - get-symbol-description "^1.0.2" - globalthis "^1.0.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - has-proto "^1.0.3" - has-symbols "^1.0.3" - hasown "^2.0.2" - internal-slot "^1.0.7" - is-array-buffer "^3.0.4" - is-callable "^1.2.7" - is-data-view "^1.0.1" - is-negative-zero "^2.0.3" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.3" - is-string "^1.0.7" - is-typed-array "^1.1.13" - is-weakref "^1.0.2" - object-inspect "^1.13.1" - object-keys "^1.1.1" - object.assign "^4.1.5" - regexp.prototype.flags "^1.5.2" - safe-array-concat "^1.1.2" - safe-regex-test "^1.0.3" - string.prototype.trim "^1.2.9" - string.prototype.trimend "^1.0.8" - string.prototype.trimstart "^1.0.8" - typed-array-buffer "^1.0.2" - typed-array-byte-length "^1.0.1" - typed-array-byte-offset "^1.0.2" - typed-array-length "^1.0.6" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.15" - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.2.1, es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-object-atoms@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" - integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== - dependencies: - es-errors "^1.3.0" - -es-set-tostringtag@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" - integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== - dependencies: - get-intrinsic "^1.2.4" - has-tostringtag "^1.0.2" - hasown "^2.0.1" - -es-shim-unscopables@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" - integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== - dependencies: - hasown "^2.0.0" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - es6-error@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -escalade@^3.1.1, escalade@^3.1.2: +esbuild@~0.25.0: + version "0.25.11" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.11.tgz#0f31b82f335652580f75ef6897bba81962d9ae3d" + integrity sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.11" + "@esbuild/android-arm" "0.25.11" + "@esbuild/android-arm64" "0.25.11" + "@esbuild/android-x64" "0.25.11" + "@esbuild/darwin-arm64" "0.25.11" + "@esbuild/darwin-x64" "0.25.11" + "@esbuild/freebsd-arm64" "0.25.11" + "@esbuild/freebsd-x64" "0.25.11" + "@esbuild/linux-arm" "0.25.11" + "@esbuild/linux-arm64" "0.25.11" + "@esbuild/linux-ia32" "0.25.11" + "@esbuild/linux-loong64" "0.25.11" + "@esbuild/linux-mips64el" "0.25.11" + "@esbuild/linux-ppc64" "0.25.11" + "@esbuild/linux-riscv64" "0.25.11" + "@esbuild/linux-s390x" "0.25.11" + "@esbuild/linux-x64" "0.25.11" + "@esbuild/netbsd-arm64" "0.25.11" + "@esbuild/netbsd-x64" "0.25.11" + "@esbuild/openbsd-arm64" "0.25.11" + "@esbuild/openbsd-x64" "0.25.11" + "@esbuild/openharmony-arm64" "0.25.11" + "@esbuild/sunos-x64" "0.25.11" + "@esbuild/win32-arm64" "0.25.11" + "@esbuild/win32-ia32" "0.25.11" + "@esbuild/win32-x64" "0.25.11" + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escalade@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== @@ -3824,6 +3808,11 @@ eslint@^8.0.0: strip-ansi "^6.0.1" text-table "^0.2.0" +esmock@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esmock/-/esmock-2.7.3.tgz#25d8fd57b9608f9430185c501e7dab91fb1247bc" + integrity sha512-/M/YZOjgyLaVoY6K83pwCsGE1AJQnj4S4GyXLYgi/Y79KL8EeW6WU7Rmjc89UO7jv6ec8+j34rKeWOfiLeEu0A== + espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -3857,7 +3846,7 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-walker@2.0.2, estree-walker@^2.0.1: +estree-walker@2.0.2, estree-walker@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== @@ -3867,7 +3856,7 @@ esutils@^2.0.2, esutils@^2.0.3: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3: +ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -3908,27 +3897,6 @@ ethereum-cryptography@^2.2.1: "@scure/bip32" "1.4.0" "@scure/bip39" "1.3.0" -ethereumjs-abi@^0.6.8: - version "0.6.8" - resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" - integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== - dependencies: - bn.js "^4.11.8" - ethereumjs-util "^6.0.0" - -ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" - integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== - dependencies: - "@types/bn.js" "^4.11.3" - bn.js "^4.11.0" - create-hash "^1.1.2" - elliptic "^6.5.2" - ethereum-cryptography "^0.1.3" - ethjs-util "0.1.6" - rlp "^2.2.3" - ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" @@ -3940,10 +3908,10 @@ ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.5: ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@^6.8.1: - version "6.14.3" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.14.3.tgz#7c4443c165ee59b2964e691600fd4586004b2000" - integrity sha512-qq7ft/oCJohoTcsNPFaXSQUm457MA5iWqkf1Mb11ujONdg7jBI6sAOrHaTi3j0CBqIGFSCeR/RMc+qwRRub7IA== +ethers@^6.14.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.15.0.tgz#2980f2a3baf0509749b7e21f8692fa8a8349c0e3" + integrity sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ== dependencies: "@adraffy/ens-normalize" "1.10.1" "@noble/curves" "1.2.0" @@ -3953,14 +3921,6 @@ ethers@^6.8.1: tslib "2.7.0" ws "8.17.1" -ethjs-util@0.1.6, ethjs-util@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" - integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== - dependencies: - is-hex-prefixed "1.0.0" - strip-hex-prefix "1.0.0" - evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -4026,7 +3986,12 @@ fast-diff@^1.1.2, fast-diff@^1.2.0: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-equals@^5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.4.0.tgz#b60073b8764f27029598447f05773c7534ba7f1e" + integrity sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw== + +fast-glob@^3.2.9: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -4037,6 +4002,17 @@ fast-glob@^3.2.9, fast-glob@^3.3.2: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -4047,11 +4023,6 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-uri@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134" - integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== - fast-xml-parser@4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" @@ -4066,17 +4037,17 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fdir@^6.4.4: - version "6.4.6" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.6.tgz#2b268c0232697063111bbf3f64810a2a741ba281" - integrity sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w== +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== fgbg@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/fgbg/-/fgbg-0.1.5.tgz#b14b73b8ef3d27e295594f92b6c2c3a8f282d434" integrity sha512-4aNmnxxTBXR2kJHYRyR/KAX7Q27CgQNNRbvuH1IUOUO3gG4UTgU9yOUdhSkjSGGgct1GlKW4xW0tLPaf4zutsw== -figures@^6.0.1: +figures@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a" integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg== @@ -4095,14 +4066,6 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -fill-keys@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" - integrity sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA== - dependencies: - is-object "~1.0.1" - merge-descriptors "~1.0.0" - fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -4120,16 +4083,9 @@ find-cache-dir@^3.2.0: pkg-dir "^4.1.0" find-up-simple@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/find-up-simple/-/find-up-simple-1.0.0.tgz#21d035fde9fdbd56c8f4d2f63f32fd93a1cfc368" - integrity sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw== - -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== - dependencies: - locate-path "^2.0.0" + version "1.0.1" + resolved "https://registry.yarnpkg.com/find-up-simple/-/find-up-simple-1.0.1.tgz#18fb90ad49e45252c4d7fca56baade04fa3fca1e" + integrity sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ== find-up@^3.0.0: version "3.0.0" @@ -4173,18 +4129,16 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -follow-redirects@^1.12.1, follow-redirects@^1.15.6: +follow-redirects@^1.12.1: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +follow-redirects@^1.15.6: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - foreground-child@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" @@ -4194,13 +4148,17 @@ foreground-child@^2.0.0: signal-exit "^3.0.2" foreground-child@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" - integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== dependencies: - cross-spawn "^7.0.0" + cross-spawn "^7.0.6" signal-exit "^4.0.1" +"forge-std@github:foundry-rs/forge-std#v1.9.7": + version "1.9.7" + resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/77041d2ce690e692d6e03cc812b57d1ddaa4d505" + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -4243,19 +4201,12 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: +fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -4265,36 +4216,6 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" - integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - functions-have-names "^1.2.3" - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -4306,20 +4227,9 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" - integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== - -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" + version "1.4.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz#9bc4caa131702b4b61729cb7e42735bc550c9ee6" + integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== get-package-type@^0.1.0: version "0.1.0" @@ -4338,14 +4248,12 @@ get-stream@^8.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== -get-symbol-description@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" - integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== +get-tsconfig@^4.7.5: + version "4.12.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.12.0.tgz#cfb3a4446a2abd324a205469e8bda4e7e44cbd35" + integrity sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw== dependencies: - call-bind "^1.0.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" + resolve-pkg-maps "^1.0.0" glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" @@ -4361,19 +4269,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^10.3.7: +glob@^10.3.7, glob@^10.4.5: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -4420,14 +4316,6 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globalthis@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" - integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== - dependencies: - define-properties "^1.2.1" - gopd "^1.0.1" - globby@^11.0.0, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -4440,24 +4328,17 @@ globby@^11.0.0, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^14.0.0: - version "14.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.2.tgz#06554a54ccfe9264e5a9ff8eded46aa1e306482f" - integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== +globby@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.1.0.tgz#138b78e77cf5a8d794e327b15dce80bf1fb0a73e" + integrity sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA== dependencies: "@sindresorhus/merge-streams" "^2.1.0" - fast-glob "^3.3.2" - ignore "^5.2.4" - path-type "^5.0.0" + fast-glob "^3.3.3" + ignore "^7.0.3" + path-type "^6.0.0" slash "^5.1.0" - unicorn-magic "^0.1.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" + unicorn-magic "^0.3.0" graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" @@ -4470,82 +4351,31 @@ graphemer@^1.4.0: integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== hardhat@^2.19.1: - version "2.22.9" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.9.tgz#d8f2720561dc60f5cc0ee80c82f9b1907fd61c88" - integrity sha512-sWiuI/yRdFUPfndIvL+2H18Vs2Gav0XacCFYY5msT5dHOWkhLxESJySIk9j83mXL31aXL8+UMA9OgViFLexklg== + version "2.26.3" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.26.3.tgz#87f3f4b6d1001970299d5bff135d57e8adae7a07" + integrity sha512-gBfjbxCCEaRgMCRgTpjo1CEoJwqNPhyGMMVHYZJxoQ3LLftp2erSVf8ZF6hTQC0r2wst4NcqNmLWqMnHg1quTw== dependencies: + "@ethereumjs/util" "^9.1.0" "@ethersproject/abi" "^5.1.2" - "@metamask/eth-sig-util" "^4.0.0" - "@nomicfoundation/edr" "^0.5.2" - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-tx" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" + "@nomicfoundation/edr" "^0.11.3" "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" - "@types/bn.js" "^5.1.0" - "@types/lru-cache" "^5.1.0" adm-zip "^0.4.16" aggregate-error "^3.0.0" ansi-escapes "^4.3.0" boxen "^5.1.2" - chalk "^2.4.2" - chokidar "^3.4.0" + chokidar "^4.0.0" ci-info "^2.0.0" debug "^4.1.1" enquirer "^2.3.0" env-paths "^2.2.0" ethereum-cryptography "^1.0.3" - ethereumjs-abi "^0.6.8" - find-up "^2.1.0" + find-up "^5.0.0" fp-ts "1.19.3" fs-extra "^7.0.1" - glob "7.2.0" immutable "^4.0.0-rc.12" io-ts "1.10.4" - keccak "^3.0.2" - lodash "^4.17.11" - mnemonist "^0.38.0" - mocha "^10.0.0" - p-map "^4.0.0" - raw-body "^2.4.1" - resolve "1.17.0" - semver "^6.3.0" - solc "0.8.26" - source-map-support "^0.5.13" - stacktrace-parser "^0.1.10" - tsort "0.0.1" - undici "^5.14.0" - uuid "^8.3.2" - ws "^7.4.6" - -hardhat@^2.24.1: - version "2.24.2" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.24.2.tgz#fe44c0d264557880a4e9e9a03fd03b5b68e95a5a" - integrity sha512-oYt+tcN2379Z3kqIhvVw6IFgWqTm/ixcrTvyAuQdE2RbD+kknwF7hDfUeggy0akrw6xdgCtXvnw9DFrxAB70hA== - dependencies: - "@ethereumjs/util" "^9.1.0" - "@ethersproject/abi" "^5.1.2" - "@nomicfoundation/edr" "^0.11.0" - "@nomicfoundation/solidity-analyzer" "^0.1.0" - "@sentry/node" "^5.18.1" - "@types/bn.js" "^5.1.0" - "@types/lru-cache" "^5.1.0" - adm-zip "^0.4.16" - aggregate-error "^3.0.0" - ansi-escapes "^4.3.0" - boxen "^5.1.2" - chokidar "^4.0.0" - ci-info "^2.0.0" - debug "^4.1.1" - enquirer "^2.3.0" - env-paths "^2.2.0" - ethereum-cryptography "^1.0.3" - find-up "^5.0.0" - fp-ts "1.19.3" - fs-extra "^7.0.1" - immutable "^4.0.0-rc.12" - io-ts "1.10.4" - json-stream-stringify "^3.1.4" + json-stream-stringify "^3.1.4" keccak "^3.0.2" lodash "^4.17.11" micro-eth-signer "^0.14.0" @@ -4565,10 +4395,31 @@ hardhat@^2.24.1: uuid "^8.3.2" ws "^7.4.6" -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +hardhat@^3.1.5: + version "3.1.10" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-3.1.10.tgz#184b400f40ce0c584c07f8a0dc789ae638597cdd" + integrity sha512-+J3LmO5j3r8bYRIiImaTT6WtT0EKcR0nfFxWq/bokAKZq7GKYf6ErKSrOuH+gFIqo+CfnrkxcgbPY20P5vuuSQ== + dependencies: + "@nomicfoundation/edr" "0.12.0-next.24" + "@nomicfoundation/hardhat-errors" "^3.0.7" + "@nomicfoundation/hardhat-utils" "^4.0.0" + "@nomicfoundation/hardhat-vendored" "^3.0.1" + "@nomicfoundation/hardhat-zod-utils" "^3.0.2" + "@nomicfoundation/solidity-analyzer" "^0.1.1" + "@sentry/core" "^9.4.0" + adm-zip "^0.4.16" + chalk "^5.3.0" + chokidar "^4.0.3" + debug "^4.3.2" + enquirer "^2.3.0" + ethereum-cryptography "^2.2.1" + micro-eth-signer "^0.14.0" + p-map "^7.0.2" + resolve.exports "^2.0.3" + semver "^7.6.3" + tsx "^4.19.3" + ws "^8.18.0" + zod "^3.23.8" has-flag@^3.0.0: version "3.0.0" @@ -4580,35 +4431,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1, has-proto@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - hash-base@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" @@ -4634,7 +4456,7 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" -hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: +hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== @@ -4684,6 +4506,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.5: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + human-id@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/human-id/-/human-id-4.1.1.tgz#2801fbd61b9a5c1c9170f332802db6408a39a4b0" @@ -4711,11 +4541,16 @@ ignore-by-default@^2.1.0: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-2.1.0.tgz#c0e0de1a99b6065bdc93315a6f728867981464db" integrity sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw== -ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: +ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== +ignore@^7.0.3: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + immutable@^4.0.0-rc.12: version "4.3.7" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381" @@ -4757,15 +4592,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -internal-slot@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" - integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== - dependencies: - es-errors "^1.3.0" - hasown "^2.0.0" - side-channel "^1.0.4" - io-ts@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" @@ -4778,26 +4604,11 @@ irregular-plurals@^3.3.0: resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.5.0.tgz#0835e6639aa8425bdc8b0d33d0dc4e89d9c01d2b" integrity sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ== -is-array-buffer@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" - integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -4805,14 +4616,6 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-builtin-module@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" @@ -4820,11 +4623,6 @@ is-builtin-module@^3.2.1: dependencies: builtin-modules "^3.3.0" -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - is-core-module@^2.13.0: version "2.15.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" @@ -4832,20 +4630,6 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.2" -is-data-view@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" - integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== - dependencies: - is-typed-array "^1.1.13" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -4873,33 +4657,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-hex-prefixed@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" - integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== - -is-negative-zero@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" - integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-object@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" - integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== - is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -4925,21 +4687,6 @@ is-promise@^4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" - integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== - dependencies: - call-bind "^1.0.7" - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4955,13 +4702,6 @@ is-stream@^3.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - is-subdir@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/is-subdir/-/is-subdir-1.2.0.tgz#b791cd28fab5202e91a08280d51d9d7254fd20d4" @@ -4969,20 +4709,6 @@ is-subdir@^1.1.1: dependencies: better-path-resolve "1.0.0" -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typed-array@^1.1.13: - version "1.1.13" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" - integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== - dependencies: - which-typed-array "^1.1.14" - is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -4994,16 +4720,9 @@ is-unicode-supported@^0.1.0: integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-unicode-supported@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz#fdf32df9ae98ff6ab2cedc155a5a6e895701c451" - integrity sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== is-windows@^1.0.0, is-windows@^1.0.2: version "1.0.2" @@ -5015,11 +4734,6 @@ isarray@^1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5177,17 +4891,12 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stream-stringify@^3.1.4: +json-stream-stringify@^3.1.4, json-stream-stringify@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz#ebe32193876fb99d4ec9f612389a8d8e2b5d54d4" integrity sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog== @@ -5238,14 +4947,6 @@ load-json-file@^7.0.1: resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-7.0.1.tgz#a3c9fde6beffb6bedb5acf104fad6bb1604e1b00" integrity sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ== -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -5268,21 +4969,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== - lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== - lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" @@ -5298,11 +4989,6 @@ lodash.startcase@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== - lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -5333,7 +5019,7 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== -make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -5370,23 +5056,18 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -memoize@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/memoize/-/memoize-10.0.0.tgz#43fa66b2022363c7c50cf5dfab732a808a3d7147" - integrity sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA== +memoize@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/memoize/-/memoize-10.1.0.tgz#32a9d09da985a1ab518dfe9fd52d14d1d130446f" + integrity sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg== dependencies: - mimic-function "^5.0.0" + mimic-function "^5.0.1" memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== -merge-descriptors@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" - integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -5413,7 +5094,7 @@ micro-packed@~0.7.2: dependencies: "@scure/base" "~1.2.5" -micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8: +micromatch@^4.0.4, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -5438,7 +5119,7 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== -mimic-function@^5.0.0: +mimic-function@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== @@ -5484,35 +5165,17 @@ minimist@^1.2.0, minimist@^1.2.7: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== +minizlib@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" + integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + minipass "^7.1.2" mnemonist@^0.38.0: version "0.38.5" @@ -5522,9 +5185,9 @@ mnemonist@^0.38.0: obliterator "^2.0.0" mocha@^10.0.0: - version "10.7.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" - integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== + version "10.8.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.8.2.tgz#8d8342d016ed411b12a429eb731b825f961afb96" + integrity sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg== dependencies: ansi-colors "^4.1.3" browser-stdout "^1.3.1" @@ -5547,11 +5210,6 @@ mocha@^10.0.0: yargs-parser "^20.2.9" yargs-unparser "^2.0.0" -module-not-found-error@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" - integrity sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g== - mri@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -5594,11 +5252,16 @@ node-fetch@^2.5.0, node-fetch@^2.6.1, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.2.0, node-gyp-build@^4.2.2: +node-gyp-build@^4.2.0: version "4.8.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== +node-gyp-build@^4.2.2: + version "4.8.4" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" + integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== + node-preload@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" @@ -5616,12 +5279,12 @@ nofilter@^3.0.2, nofilter@^3.1.0: resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== +nopt@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-8.1.0.tgz#b11d38caf0f8643ce885818518064127f602eae3" + integrity sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A== dependencies: - abbrev "1" + abbrev "^3.0.0" normalize-package-data@^2.5.0: version "2.5.0" @@ -5652,16 +5315,6 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - nyc@^17.0.0: version "17.0.0" resolved "https://registry.yarnpkg.com/nyc/-/nyc-17.0.0.tgz#d8943407584242a448a70290b15bb72207fac9fd" @@ -5695,35 +5348,10 @@ nyc@^17.0.0: test-exclude "^6.0.0" yargs "^15.0.2" -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" - integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== - dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" - object-keys "^1.1.1" - obliterator@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" - integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + version "2.0.5" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.5.tgz#031e0145354b0c18840336ae51d41e7d6d2c76aa" + integrity sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw== once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" @@ -5773,13 +5401,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -5794,13 +5415,6 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== - dependencies: - p-limit "^1.1.0" - p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -5841,15 +5455,10 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -p-map@^7.0.1: - version "7.0.2" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-7.0.2.tgz#7c5119fada4755660f70199a66aa3fe2f85a1fe8" - integrity sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q== - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== +p-map@^7.0.2, p-map@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-7.0.3.tgz#7ac210a2d36f81ec28b736134810f7ba4418cdb6" + integrity sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA== p-try@^2.0.0: version "2.2.0" @@ -5875,9 +5484,9 @@ package-hash@^4.0.0: release-zalgo "^1.0.0" package-json-from-dist@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" - integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== package-manager-detector@^0.2.0: version "0.2.11" @@ -5956,10 +5565,10 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path-type@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" - integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== +path-type@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-6.0.0.tgz#2f1bb6791a91ce99194caede5d6c5920ed81eb51" + integrity sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ== pbkdf2@^3.0.17: version "3.1.2" @@ -5982,20 +5591,15 @@ picocolors@^1.1.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -picomatch@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-3.0.1.tgz#817033161def55ec9638567a2f3bbc876b3e7516" - integrity sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag== - -picomatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" - integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pify@^4.0.1: version "4.0.1" @@ -6021,11 +5625,6 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -6048,10 +5647,10 @@ prettier@^3.0.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== -pretty-ms@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-9.1.0.tgz#0ad44de6086454f48a168e5abb3c26f8db1b3253" - integrity sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw== +pretty-ms@^9.2.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-9.3.0.tgz#dd2524fcb3c326b4931b2272dfd1e1a8ed9a9f5a" + integrity sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ== dependencies: parse-ms "^4.0.0" @@ -6081,15 +5680,6 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -proxyquire@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" - integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg== - dependencies: - fill-keys "^1.0.2" - module-not-found-error "^1.0.1" - resolve "^1.11.1" - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -6185,16 +5775,6 @@ regexp-tree@^0.1.27: resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== -regexp.prototype.flags@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== - dependencies: - call-bind "^1.0.6" - define-properties "^1.2.1" - es-errors "^1.3.0" - set-function-name "^2.0.1" - regjsparser@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.10.0.tgz#b1ed26051736b436f22fdec1c8f72635f9f44892" @@ -6214,11 +5794,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -6241,6 +5816,16 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve.exports@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== + resolve@1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" @@ -6248,7 +5833,7 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -resolve@^1.10.0, resolve@^1.11.1: +resolve@^1.10.0: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -6272,6 +5857,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -6294,7 +5884,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rlp@^2.2.3, rlp@^2.2.4: +rlp@^2.2.4: version "2.2.7" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== @@ -6308,30 +5898,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-array-concat@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" - integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== - dependencies: - call-bind "^1.0.7" - get-intrinsic "^1.2.4" - has-symbols "^1.0.3" - isarray "^2.0.5" - safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-regex-test@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" - integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-regex "^1.1.4" - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -6361,7 +5932,12 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: +semver@^7.3.2, semver@^7.6.3: + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== + +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -6385,28 +5961,6 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -set-function-name@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -6449,16 +6003,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" - signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -6490,15 +6034,6 @@ slash@^5.1.0: resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - slice-ansi@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" @@ -6521,11 +6056,9 @@ solc@0.8.26: tmp "0.0.33" solidity-ast@^0.4.51: - version "0.4.56" - resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.56.tgz#94fe296f12e8de1a3bed319bc06db8d05a113d7a" - integrity sha512-HgmsA/Gfklm/M8GFbCX/J1qkVH0spXHgALCNZ8fA8x5X+MFdn/8CP2gr5OVyXjXw6RZTPC/Sxl2RUDQOXyNMeA== - dependencies: - array.prototype.findlast "^1.2.2" + version "0.4.61" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.61.tgz#b51720ece553a2c7d84551ee5a7a2306080d23d0" + integrity sha512-OYBJYcYyG7gLV0VuXl9CUrvgJXjV/v0XnR4+1YomVe3q+QyENQXJJxAEASUz4vN6lMAl+C8RSRSr5MBAz09f6w== solidity-ast@^0.4.60: version "0.4.60" @@ -6611,9 +6144,9 @@ stack-utils@^2.0.6: escape-string-regexp "^2.0.0" stacktrace-parser@^0.1.10: - version "0.1.10" - resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" - integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + version "0.1.11" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz#c7c08f9b29ef566b9a6f7b255d7db572f66fabc4" + integrity sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg== dependencies: type-fest "^0.7.1" @@ -6631,15 +6164,6 @@ statuses@2.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -6649,6 +6173,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -6667,34 +6200,6 @@ string-width@^7.0.0: get-east-asian-width "^1.0.0" strip-ansi "^7.1.0" -string.prototype.trim@^1.2.9: - version "1.2.9" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" - integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.0" - es-object-atoms "^1.0.0" - -string.prototype.trimend@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" - integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -string.prototype.trimstart@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" - integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -6724,9 +6229,9 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: ansi-regex "^5.0.1" strip-ansi@^7.0.1, strip-ansi@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" @@ -6750,13 +6255,6 @@ strip-final-newline@^3.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== -strip-hex-prefix@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" - integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== - dependencies: - is-hex-prefixed "1.0.0" - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -6818,28 +6316,16 @@ synckit@^0.9.1: "@pkgr/core" "^0.1.0" tslib "^2.6.2" -table@^6.8.0: - version "6.8.2" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" - integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== +tar@^7.4.0: + version "7.5.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.1.tgz#750a8bd63b7c44c1848e7bf982260a083cf747c9" + integrity sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g== dependencies: - ajv "^8.0.1" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - -tar@^6.1.11: - version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" temp-dir@^3.0.0: version "3.0.0" @@ -6881,12 +6367,12 @@ time-zone@^1.0.0: integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== tinyglobby@^0.2.6: - version "0.2.14" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d" - integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ== + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: - fdir "^6.4.4" - picomatch "^4.0.2" + fdir "^6.5.0" + picomatch "^4.0.3" tmp@0.0.33, tmp@^0.0.33: version "0.0.33" @@ -6942,15 +6428,15 @@ tsort@0.0.1: resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw== -tweetnacl-util@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" - integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== - -tweetnacl@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== +tsx@^4.19.3: + version "4.20.6" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.20.6.tgz#8fb803fd9c1f70e8ccc93b5d7c5e03c3979ccb2e" + integrity sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg== + dependencies: + esbuild "~0.25.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -6999,50 +6485,6 @@ type-fest@^0.8.0, type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -typed-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" - integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - is-typed-array "^1.1.13" - -typed-array-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" - integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== - dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - -typed-array-byte-offset@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" - integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - -typed-array-length@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" - integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== - dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - possible-typed-array-names "^1.0.0" - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -7055,21 +6497,16 @@ typescript@^5.0.0: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - undici-types@~6.19.2: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + undici@^5.14.0: version "5.29.0" resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3" @@ -7082,15 +6519,20 @@ undici@^6.11.1: resolved "https://registry.yarnpkg.com/undici/-/undici-6.21.2.tgz#49c5884e8f9039c65a89ee9018ef3c8e2f1f4928" integrity sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g== +undici@^6.16.1: + version "6.22.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-6.22.0.tgz#281adbc157af41da8e75393c9d75a1b788811bc3" + integrity sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw== + unfetch@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== -unicorn-magic@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" - integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== +unicorn-magic@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104" + integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== universalify@^0.1.0: version "0.1.2" @@ -7158,33 +6600,11 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - which-module@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.14, which-typed-array@^1.1.15: - version "1.1.15" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" - integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.2" - which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -7199,13 +6619,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -7283,10 +6696,10 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" - integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== +write-file-atomic@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-6.0.0.tgz#e9c89c8191b3ef0606bc79fb92681aa1aa16fa93" + integrity sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ== dependencies: imurmurhash "^0.1.4" signal-exit "^4.0.1" @@ -7301,6 +6714,11 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== +ws@^8.18.0: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + wsrun@^5.2.4: version "5.2.4" resolved "https://registry.yarnpkg.com/wsrun/-/wsrun-5.2.4.tgz#6eb6c3ccd3327721a8df073a5e3578fb0dea494e" @@ -7331,10 +6749,10 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== yargs-parser@^13.1.2: version "13.1.2" @@ -7435,3 +6853,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@^3.23.8: + version "3.25.76" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== From e0d0aedfb102fb2f0591d06ec02b1a01fa61d4ac Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 16 Mar 2026 13:36:20 -0400 Subject: [PATCH 04/21] Add manifest stability tests for HH2 to HH3 migration (#1222) --- .../test/manifest-hh2-migration.js | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 packages/plugin-hardhat/test/manifest-hh2-migration.js diff --git a/packages/plugin-hardhat/test/manifest-hh2-migration.js b/packages/plugin-hardhat/test/manifest-hh2-migration.js new file mode 100644 index 000000000..d5e4c8345 --- /dev/null +++ b/packages/plugin-hardhat/test/manifest-hh2-migration.js @@ -0,0 +1,132 @@ +import test from 'ava'; +import hre from 'hardhat'; + +const connection = await hre.network.connect(); +const { ethers } = connection; +import { upgrades as upgradesFactory } from '@openzeppelin/hardhat-upgrades'; +import manifest from '@openzeppelin/upgrades-core/dist/manifest.js'; + +/** @type {import('@openzeppelin/hardhat-upgrades').HardhatUpgrades} */ +let upgrades; + +test.after.always(async () => { + await connection.close(); +}); + +test.before(async t => { + upgrades = await upgradesFactory(hre, connection); +}); + +async function patchManifest(provider, fn) { + const m = await manifest.Manifest.forNetwork(provider); + await m.lockedRun(async () => { + const data = await m.read(); + fn(data); + await m.write(data); + }); +} + +async function readManifest(provider) { + const m = await manifest.Manifest.forNetwork(provider); + return m.read(); +} + +function rewriteLayoutSrcs(layout, fn) { + const rewrite = ({ src, ...rest }) => ({ ...rest, ...fn(src) }); + return { + ...layout, + storage: layout.storage.map(rewrite), + namespaces: layout.namespaces + ? Object.fromEntries( + Object.entries(layout.namespaces).map(([key, items]) => [key, items.map(rewrite)]), + ) + : undefined, + }; +} + +function toLegacySrcs(layout) { + return rewriteLayoutSrcs(layout, src => ({ src: `legacy/${src}` })); +} + +function omitSrcs(layout) { + return rewriteLayoutSrcs(layout, () => ({})); +} + +function collectSrcs(layout) { + return [ + ...layout.storage.map(item => item.src), + ...Object.values(layout.namespaces ?? {}).flatMap(items => items.map(item => item.src)), + ]; +} + +test.serial('upgrade refreshes stale V1 layout srcs', async t => { + const V1 = await ethers.getContractFactory('MultipleNamespacesAndRegularVariables'); + const V2 = await ethers.getContractFactory('MultipleNamespacesAndRegularVariablesV2_Ok'); + + const proxy = await upgrades.deployProxy(V1, { kind: 'transparent' }); + + const data = await readManifest(ethers.provider); + const v1Key = Object.keys(data.impls)[0]; + const v1 = data.impls[v1Key]; + + const srcs = collectSrcs(v1.layout); + t.true(srcs.length > 0); + t.truthy(v1.layout.namespaces); + t.true(Object.keys(v1.layout.namespaces).length > 0); + t.true(srcs.every(s => !s.startsWith('legacy/'))); + + await patchManifest(ethers.provider, d => { + d.impls[v1Key].layout = toLegacySrcs(v1.layout); + }); + + const patched = await readManifest(ethers.provider); + t.true(collectSrcs(patched.impls[v1Key].layout).every(s => s.startsWith('legacy/'))); + + const upgraded = await upgrades.upgradeProxy(proxy, V2); + await upgraded.waitForDeployment(); + + const after = await readManifest(ethers.provider); + const v1After = after.impls[v1Key]; + t.truthy(v1After); + t.is(v1After.address, v1.address); + t.is(v1After.txHash, v1.txHash); + t.true(collectSrcs(v1After.layout).every(s => !s.startsWith('legacy/'))); + t.deepEqual(omitSrcs(v1After.layout), omitSrcs(v1.layout)); + + const v2Key = Object.keys(after.impls).find(k => k !== v1Key); + t.truthy(v2Key); + t.true(collectSrcs(after.impls[v2Key].layout).every(s => !s.startsWith('legacy/'))); +}); + +test.serial('upgrade preserves V1 entry with no version hash match', async t => { + const V1 = await ethers.getContractFactory('Example'); + const V2 = await ethers.getContractFactory('ExampleV2_Ok'); + + const proxy = await upgrades.deployProxy(V1, { kind: 'transparent' }); + const proxyAddress = await proxy.getAddress(); + + const fakeHash = 'deadbeef'.repeat(8); + await patchManifest(ethers.provider, d => { + const realKey = Object.keys(d.impls).find(k => d.impls[k].address !== undefined); + const entry = d.impls[realKey]; + delete d.impls[realKey]; + entry.layout = toLegacySrcs(entry.layout); + d.impls[fakeHash] = entry; + }); + + const patched = await readManifest(ethers.provider); + const patchedEntry = patched.impls[fakeHash]; + + const upgraded = await upgrades.upgradeProxy(proxyAddress, V2); + await upgraded.waitForDeployment(); + + const after = await readManifest(ethers.provider); + const v1After = after.impls[fakeHash]; + t.truthy(v1After); + t.is(v1After.address, patchedEntry.address); + t.is(v1After.txHash, patchedEntry.txHash); + t.truthy(v1After.layout.namespaces); + t.true(Object.keys(v1After.layout.namespaces).length > 0); + t.true(collectSrcs(v1After.layout).every(s => s.startsWith('legacy/'))); + t.deepEqual(v1After.layout, patchedEntry.layout); +}); \ No newline at end of file From 2237d5515388751c992e323449317fb4a7275dfa Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Sat, 28 Mar 2026 13:08:45 -0400 Subject: [PATCH 05/21] Improve error handling for Hardhat 3 .output.json files (#1232) --- packages/core/CHANGELOG.md | 4 + .../src/cli/validate/build-info-file.test.ts | 89 ++++++++++++++++++- .../core/src/cli/validate/build-info-file.ts | 7 +- 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 40fa84500..5472dc0f7 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Improve error handling for Hardhat 3 .output.json files. + ## 1.45.0-alpha.1 (2026-03-02) - Improve support for validating build-info files in CLI for Hardhat 3, Hardhat 2, and Foundry. ([#1194](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1194)) diff --git a/packages/core/src/cli/validate/build-info-file.test.ts b/packages/core/src/cli/validate/build-info-file.test.ts index d30246234..497221758 100644 --- a/packages/core/src/cli/validate/build-info-file.test.ts +++ b/packages/core/src/cli/validate/build-info-file.test.ts @@ -313,6 +313,55 @@ test.serial('get build info files - default foundry', async t => { assertBuildInfoFiles(t, buildInfoFiles); }); +test.serial('get build info files - Foundry format success path', async t => { + const dir = 'foundry-format-success'; + + await fs.mkdir(dir, { recursive: true }); + await fs.writeFile( + `${dir}/build-info.json`, + JSON.stringify({ + _format: 'ethers-rs-sol-build-info-1', + ...BUILD_INFO, + }), + ); + + const buildInfoFiles = await getBuildInfoFiles(dir); + + t.is(buildInfoFiles.length, 1); + t.is(buildInfoFiles[0]?.dirShortName, dir); + t.is(buildInfoFiles[0]?.solcVersion, BUILD_INFO.solcVersion); + t.deepEqual(buildInfoFiles[0]?.input, BUILD_INFO.input); + t.deepEqual(buildInfoFiles[0]?.output, BUILD_INFO.output); +}); + +test.serial('get build info files - Hardhat 3 split format success path', async t => { + const dir = 'hh3-format-success'; + + await fs.mkdir(dir, { recursive: true }); + await fs.writeFile( + `${dir}/solc-0_8_9-abc123.json`, + JSON.stringify({ + _format: 'hh3-sol-build-info-1', + input: BUILD_INFO.input, + solcVersion: BUILD_INFO.solcVersion, + }), + ); + await fs.writeFile( + `${dir}/solc-0_8_9-abc123.output.json`, + JSON.stringify({ + output: BUILD_INFO.output, + }), + ); + + const buildInfoFiles = await getBuildInfoFiles(dir); + + t.is(buildInfoFiles.length, 1); + t.is(buildInfoFiles[0]?.dirShortName, dir); + t.is(buildInfoFiles[0]?.solcVersion, BUILD_INFO.solcVersion); + t.deepEqual(buildInfoFiles[0]?.input, BUILD_INFO.input); + t.deepEqual(buildInfoFiles[0]?.output, BUILD_INFO.output); +}); + test.serial('get build info files - both hardhat and foundry dirs exist', async t => { await fs.mkdir('artifacts/build-info', { recursive: true }); await fs.writeFile('artifacts/build-info/build-info.json', JSON.stringify(BUILD_INFO)); @@ -363,6 +412,7 @@ test.serial('generic invalid build info file', async t => { await fs.writeFile('invalid-build-info/invalid.json', JSON.stringify({ output: {} })); const error = await t.throwsAsync(getBuildInfoFiles('invalid-build-info')); t.true(error?.message.includes('must include Solidity compiler input, output, and solcVersion')); + t.true(error?.message.includes('Got format: unknown')); }); test.serial('Foundry format (ethers-rs) with missing output suggests forge clean && forge build', async t => { @@ -383,6 +433,21 @@ test.serial('Foundry format (ethers-rs) with missing output suggests forge clean t.true(error?.message.includes('forge clean && forge build')); }); +test.serial('Hardhat 3 format with missing input and solcVersion suggests hardhat compile', async t => { + await fs.mkdir('hh3-format-missing-input', { recursive: true }); + + await fs.writeFile( + 'hh3-format-missing-input/solc-0_8_0-abc123.json', + JSON.stringify({ + _format: 'hh3-sol-build-info-1', + }), + ); + const error = await t.throwsAsync(getBuildInfoFiles('hh3-format-missing-input')); + t.true(error?.message.includes('Hardhat 3 format')); + t.true(error?.message.includes('must contain input and solcVersion')); + t.true(error?.message.includes('hardhat compile')); +}); + test.serial('Hardhat 3 format with missing .output.json suggests hardhat compile', async t => { await fs.mkdir('hh3-format-missing-output', { recursive: true }); @@ -396,7 +461,29 @@ test.serial('Hardhat 3 format with missing .output.json suggests hardhat compile }), ); const error = await t.throwsAsync(getBuildInfoFiles('hh3-format-missing-output')); - t.true(error?.message.includes('could not be read') || error?.message.includes('missing Solidity compiler output')); + t.true(error?.message.includes('could not be read')); + t.false(error?.message.includes('missing Solidity compiler output')); + t.true(error?.message.includes('Hardhat 3')); + t.true(error?.message.includes('hardhat compile')); +}); + +test.serial('Hardhat 3 format with empty .output.json reports missing compiler output', async t => { + await fs.mkdir('hh3-format-empty-output', { recursive: true }); + + await fs.writeFile( + 'hh3-format-empty-output/solc-0_8_0-abc123.json', + JSON.stringify({ + _format: 'hh3-sol-build-info-1', + input: BUILD_INFO.input, + solcVersion: '0.8.9', + }), + ); + await fs.writeFile('hh3-format-empty-output/solc-0_8_0-abc123.output.json', JSON.stringify({})); + + const error = await t.throwsAsync(getBuildInfoFiles('hh3-format-empty-output')); + t.true(error?.message.includes('missing Solidity compiler output')); + t.false(error?.message.includes('could not be read')); + t.true(error?.message.includes('Hardhat 3')); t.true(error?.message.includes('hardhat compile')); }); diff --git a/packages/core/src/cli/validate/build-info-file.ts b/packages/core/src/cli/validate/build-info-file.ts index faa50f617..ef8369ecf 100644 --- a/packages/core/src/cli/validate/build-info-file.ts +++ b/packages/core/src/cli/validate/build-info-file.ts @@ -222,7 +222,12 @@ async function loadBuildInfo(buildInfoFilePath: string): Promise<{ ); } - if (outputData === undefined) { + if ( + outputData === undefined || + outputData === null || + typeof outputData !== 'object' || + (!('sources' in outputData) && !('contracts' in outputData) && !('errors' in outputData)) + ) { throw new ValidateCommandError( `Build info file ${buildInfoFilePath} does not contain output, and output file ${outputFilePath} is missing Solidity compiler output.`, () => HH3_BUILD_INFO_HELP, From 892e5718edb41892d0971ae68162ef0dcb5a1959 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 31 Mar 2026 08:21:28 -0400 Subject: [PATCH 06/21] Add unittest for Hardhat 3 user source map in build info (#1233) --- .../src/cli/validate/build-info-file.test.ts | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/packages/core/src/cli/validate/build-info-file.test.ts b/packages/core/src/cli/validate/build-info-file.test.ts index 497221758..712b7bee1 100644 --- a/packages/core/src/cli/validate/build-info-file.test.ts +++ b/packages/core/src/cli/validate/build-info-file.test.ts @@ -581,6 +581,74 @@ test.serial('no output selection', async t => { t.true(error?.message.includes('is not from a full compilation')); }); +test.serial('Hardhat 3 format with userSourceNameMap remaps input and output sources', async t => { + const dir = 'hh3-source-name-map'; + + await fs.mkdir(dir, { recursive: true }); + await fs.writeFile( + `${dir}/solc-0_8_9-abc123.json`, + JSON.stringify({ + _format: 'hh3-sol-build-info-1', + solcVersion: '0.8.9', + input: { + language: 'Solidity', + sources: { + 'project/contracts/MyContract.sol': { + content: 'contract MyContract {}', + }, + 'lib/Helper.sol': { + content: 'contract Helper {}', + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['storageLayout'], + }, + }, + }, + }, + // Only maps project sources, not library sources + userSourceNameMap: { + 'contracts/MyContract.sol': 'project/contracts/MyContract.sol', + }, + }), + ); + await fs.writeFile( + `${dir}/solc-0_8_9-abc123.output.json`, + JSON.stringify({ + sources: { + 'project/contracts/MyContract.sol': { ast: {}, id: 0 }, + 'lib/Helper.sol': { ast: {}, id: 1 }, + }, + contracts: { + 'project/contracts/MyContract.sol': { MyContract: {} }, + }, + }), + ); + + const buildInfoFiles = await getBuildInfoFiles(dir); + + t.is(buildInfoFiles.length, 1); + const file = buildInfoFiles[0]; + + const user = 'contracts/MyContract.sol'; + const canonical = 'project/contracts/MyContract.sol'; + const lib = 'lib/Helper.sol'; + + // Mapped: canonical paths replaced with user paths + t.truthy(file.input.sources[user]); + t.falsy(file.input.sources[canonical]); + t.truthy(file.output.sources[user]); + t.falsy(file.output.sources[canonical]); + t.truthy(file.output.contracts?.[user]); + t.falsy(file.output.contracts?.[canonical]); + + // Unmapped: library deps preserved as-is + t.truthy(file.input.sources[lib]); + t.truthy(file.output.sources[lib]); +}); + function assertBuildInfoFiles(t: ExecutionContext, buildInfoFiles: BuildInfoFile[]) { t.is(buildInfoFiles.length, 2); From 3c09fbf7a9874b7d37f4f7cab426d2c7793014dc Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 14:10:54 -0400 Subject: [PATCH 07/21] Make network connection parameter required and handle outdated validations cache --- packages/plugin-hardhat/src/hooks/solidity.ts | 12 ++++++++-- .../plugin-hardhat/src/utils/deploy-impl.ts | 24 +++++-------------- packages/plugin-hardhat/src/utils/factory.ts | 7 ++---- .../plugin-hardhat/src/utils/validations.ts | 7 +++--- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/packages/plugin-hardhat/src/hooks/solidity.ts b/packages/plugin-hardhat/src/hooks/solidity.ts index 90b7df062..4c6526cbe 100644 --- a/packages/plugin-hardhat/src/hooks/solidity.ts +++ b/packages/plugin-hardhat/src/hooks/solidity.ts @@ -90,8 +90,14 @@ export default async (): Promise> => { nextSolcConfig: SolcConfig, ) => Promise, ): Promise { - const { validate, solcInputOutputDecoder, isNamespaceSupported, makeNamespacedInput, trySanitizeNatSpec } = - await import('@openzeppelin/upgrades-core'); + const { + validate, + solcInputOutputDecoder, + isNamespaceSupported, + makeNamespacedInput, + trySanitizeNatSpec, + assertUnreachable, + } = await import('@openzeppelin/upgrades-core'); const { writeValidations } = await import('../utils/validations.js'); const { isFullSolcOutput } = await import('../utils/is-full-solc-output.js'); @@ -158,6 +164,8 @@ export default async (): Promise> => { } case 'ignore': break; + default: + assertUnreachable(namespacedErrorsSetting); } namespacedOutput = undefined; } else { diff --git a/packages/plugin-hardhat/src/utils/deploy-impl.ts b/packages/plugin-hardhat/src/utils/deploy-impl.ts index 5fa74a383..22ce21716 100644 --- a/packages/plugin-hardhat/src/utils/deploy-impl.ts +++ b/packages/plugin-hardhat/src/utils/deploy-impl.ts @@ -66,13 +66,9 @@ export async function deployUpgradeableImpl( hre: HardhatRuntimeEnvironment, ImplFactory: ContractFactory, opts: StandaloneOptions, - currentImplAddress?: string, - connection?: NetworkConnection, + currentImplAddress: string | undefined, + connection: NetworkConnection, ): Promise { - // If connection not provided, create one (for backwards compatibility during migration) - if (!connection) { - connection = await hre.network.connect(); - } const deployData = await getDeployData(hre, ImplFactory, opts, connection); await validateImpl(deployData, opts, currentImplAddress); return await deployImpl(hre, deployData, ImplFactory, opts, connection); @@ -82,13 +78,9 @@ export async function deployProxyImpl( hre: HardhatRuntimeEnvironment, ImplFactory: ContractFactory, opts: UpgradeOptions, - proxyAddress?: string, - connection?: NetworkConnection, + proxyAddress: string | undefined, + connection: NetworkConnection, ): Promise { - // If connection not provided, create one (for backwards compatibility during migration) - if (!connection) { - connection = await hre.network.connect(); - } const deployData = await getDeployData(hre, ImplFactory, opts, connection); await validateProxyImpl(deployData, opts, proxyAddress); if (opts.kind === undefined) { @@ -104,13 +96,9 @@ export async function deployBeaconImpl( hre: HardhatRuntimeEnvironment, ImplFactory: ContractFactory, opts: UpgradeOptions, - beaconAddress?: string, - connection?: NetworkConnection, + beaconAddress: string | undefined, + connection: NetworkConnection, ): Promise { - // If connection not provided, create one (for backwards compatibility during migration) - if (!connection) { - connection = await hre.network.connect(); - } const deployData = await getDeployData(hre, ImplFactory, opts, connection); await validateBeaconImpl(deployData, opts, beaconAddress); return await deployImpl(hre, deployData, ImplFactory, opts, connection); diff --git a/packages/plugin-hardhat/src/utils/factory.ts b/packages/plugin-hardhat/src/utils/factory.ts index 67391dafb..df6a7039c 100644 --- a/packages/plugin-hardhat/src/utils/factory.ts +++ b/packages/plugin-hardhat/src/utils/factory.ts @@ -24,7 +24,7 @@ import { * ``` * * @param hre - Hardhat Runtime Environment - * @param connection - Optional network connection object from await hre.network.connect() + * @param connection - Network connection from `await hre.network.connect()`. Share one connection across operations; do not create a new one per call. * @returns API object with all upgrade functions */ export async function upgrades( @@ -32,9 +32,6 @@ export async function upgrades( connection: NetworkConnection, ): Promise { await warnOnHardhatDefender(); - if (!connection) { - connection = await hre.network.connect(); - } return await createUpgradesAPI(hre, false, connection); } @@ -53,7 +50,7 @@ export async function upgrades( * ``` * * @param hre - Hardhat Runtime Environment - * @param connection - Optional network connection object from await hre.network.connect() + * @param connection - Network connection from `await hre.network.connect()`. Share one connection across operations; do not create a new one per call. * @returns API object with all upgrade and Defender functions */ export async function defender( diff --git a/packages/plugin-hardhat/src/utils/validations.ts b/packages/plugin-hardhat/src/utils/validations.ts index 410e6feb8..3e52b2ee9 100644 --- a/packages/plugin-hardhat/src/utils/validations.ts +++ b/packages/plugin-hardhat/src/utils/validations.ts @@ -21,9 +21,10 @@ export async function writeValidations(hre: HardhatRuntimeEnvironment, newRunDat try { releaseLock = await lock(cachePath); const storedData = await readValidations(hre, false).catch(e => { - // If there is no previous data to append to, we ignore the error and write - // the file from scratch. - if (e instanceof ValidationsCacheNotFound) { + // No prior data to append to: either the cache file is absent, or it was + // outdated and readValidations has already removed it. In both cases, + // write from scratch. + if (e instanceof ValidationsCacheNotFound || e instanceof ValidationsCacheOutdated) { return undefined; } else { throw e; From 6d0ba22615082d6de7547794b293ea0ec49c3e76 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 14:59:19 -0400 Subject: [PATCH 08/21] Route solidity hook log output through debug and logWarning channels --- packages/plugin-hardhat/src/hooks/solidity.ts | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/packages/plugin-hardhat/src/hooks/solidity.ts b/packages/plugin-hardhat/src/hooks/solidity.ts index 4c6526cbe..71243487d 100644 --- a/packages/plugin-hardhat/src/hooks/solidity.ts +++ b/packages/plugin-hardhat/src/hooks/solidity.ts @@ -3,6 +3,8 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import type { SolcConfig } from 'hardhat/types/config'; import type { CompilerInput, CompilerOutput, Compiler } from 'hardhat/types/solidity'; import type { SolcInput, SolcOutput } from '@openzeppelin/upgrades-core'; +import { logWarning } from '@openzeppelin/upgrades-core'; +import debug from '../utils/debug.js'; // Helper to extract compile errors from output function getNamespacedCompileErrors(output: CompilerOutput | undefined): string[] { @@ -61,9 +63,7 @@ export default async (): Promise> => { } else if (typeof e === 'object' && e !== null && 'code' in e && e.code === 'ELOCKED') { // Lock file is being held by another process - warn once and continue if (!lockWarningShown) { - console.warn( - '\nWarning: Validations cache is locked by another process. Continuing without cache validation.', - ); + logWarning('Validations cache is locked by another process.', ['Continuing without cache validation.']); lockWarningShown = true; } } else { @@ -154,7 +154,6 @@ export default async (): Promise> => { throw new UpgradesError(msg, () => details.join('\n')); } case 'warn': { - const { logWarning } = await import('@openzeppelin/upgrades-core'); const details = [ ...preamble, "you can set namespacedCompileErrors: 'ignore' in your hardhat config to ignore this.", @@ -206,7 +205,7 @@ export default async (): Promise> => { // Inject AST into artifact files for Hardhat 3 compatibility with Foundry plugin // In Hardhat 3, AST is stored in build-info output files, not in artifacts // The Foundry upgrades plugin expects AST in artifact files, so we inject it here - console.log('[OpenZeppelin Upgrades] Starting AST injection into artifacts...'); + debug('Starting AST injection into artifacts...'); await injectAstIntoArtifacts(artifactsDir, buildInfoDir); } catch (error: unknown) { if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') { @@ -297,29 +296,27 @@ export async function injectAstIntoArtifacts(artifactsDir: string, buildInfoDir: processedCount += 1; } else { // AST not found in build-info - log for debugging - console.warn( - `Warning: AST not found in build-info for artifact ${artifactPath}\n` + - ` buildInfoId: ${artifact.buildInfoId}\n` + - ` inputSourceName: ${inputSourceName}\n` + - ` build-info output exists: ${!!buildInfoOutput.output}\n` + - ` sources exists: ${!!buildInfoOutput.output?.sources}\n` + - ` source key exists: ${!!buildInfoOutput.output?.sources?.[inputSourceName]}`, - ); + logWarning(`AST not found in build-info for artifact ${artifactPath}`, [ + `buildInfoId: ${artifact.buildInfoId}`, + `inputSourceName: ${inputSourceName}`, + `build-info output exists: ${!!buildInfoOutput.output}`, + `sources exists: ${!!buildInfoOutput.output?.sources}`, + `source key exists: ${!!buildInfoOutput.output?.sources?.[inputSourceName]}`, + ]); skippedCount += 1; } } catch (err: unknown) { // If build-info output file doesn't exist or AST is missing, skip this artifact const isEnoent = typeof err === 'object' && err !== null && 'code' in err && err.code === 'ENOENT'; if (isEnoent) { - console.warn( - `Warning: Build-info output file not found for artifact ${artifactPath}\n` + - ` Expected: ${buildInfoOutputPath}\n` + - ` buildInfoId: ${artifact.buildInfoId}`, - ); + logWarning(`Build-info output file not found for artifact ${artifactPath}`, [ + `Expected: ${buildInfoOutputPath}`, + `buildInfoId: ${artifact.buildInfoId}`, + ]); } else { // Log other errors but don't fail the whole process const message = err instanceof Error ? err.message : String(err); - console.warn(`Warning: Could not inject AST into artifact ${artifactPath}: ${message}`); + logWarning(`Could not inject AST into artifact ${artifactPath}: ${message}`); errorCount += 1; } skippedCount += 1; @@ -330,7 +327,7 @@ export async function injectAstIntoArtifacts(artifactsDir: string, buildInfoDir: const isEnoent = typeof err === 'object' && err !== null && 'code' in err && err.code === 'ENOENT'; if (!isEnoent) { const message = err instanceof Error ? err.message : String(err); - console.warn(`Warning: Could not process artifact ${artifactPath}: ${message}`); + logWarning(`Could not process artifact ${artifactPath}: ${message}`); errorCount += 1; } } @@ -338,8 +335,6 @@ export async function injectAstIntoArtifacts(artifactsDir: string, buildInfoDir: // Log summary if any artifacts were processed or had issues if (processedCount > 0 || skippedCount > 0 || errorCount > 0) { - console.log( - `[OpenZeppelin Upgrades] AST injection: ${processedCount} processed, ${skippedCount} skipped, ${errorCount} errors`, - ); + debug(`AST injection: ${processedCount} processed, ${skippedCount} skipped, ${errorCount} errors`); } } From a819cf7ea9f2ab8189ecaf46d11c3d158fe9b2f9 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 15:18:38 -0400 Subject: [PATCH 09/21] Document AST injection consumer --- packages/plugin-hardhat/src/hooks/solidity.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/plugin-hardhat/src/hooks/solidity.ts b/packages/plugin-hardhat/src/hooks/solidity.ts index 71243487d..c38741781 100644 --- a/packages/plugin-hardhat/src/hooks/solidity.ts +++ b/packages/plugin-hardhat/src/hooks/solidity.ts @@ -217,8 +217,11 @@ export default async (): Promise> => { }; /** - * Injects AST from build-info output files into artifact files for Hardhat 3 compatibility. - * This allows the Foundry upgrades plugin to find AST in artifact files as expected. + * Injects AST and metadata from HH3's split `.output.json` build-info files into artifact JSON. + * + * Required by the `@openzeppelin/foundry-upgrades` npm package, which reads this data from + * artifacts via FFI during `hardhat test solidity`. HH3's artifact schema does not include AST + * by default, so this hook bridges the gap. Removing it breaks `examples/BoxSolidityTests`. */ export async function injectAstIntoArtifacts(artifactsDir: string, buildInfoDir: string): Promise { const path = await import('path'); From 42b9e9d919fa9bc2ac5b9c2b4462ad5bb266950f Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 18:41:12 -0400 Subject: [PATCH 10/21] Prepare docs for release --- README.md | 5 ----- packages/plugin-hardhat/MIGRATION.md | 2 -- packages/plugin-hardhat/README.md | 21 ++------------------- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 16831f4c1..462a419fa 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,6 @@ [![Checks](https://github.com/OpenZeppelin/openzeppelin-upgrades/actions/workflows/checks.yml/badge.svg)](https://github.com/OpenZeppelin/openzeppelin-upgrades/actions/workflows/checks.yml) [![License](https://img.shields.io/github/license/OpenZeppelin/openzeppelin-upgrades)](https://github.com/OpenZeppelin/openzeppelin-upgrades/blob/master/LICENSE) -> [!NOTE] -> This branch contains support for Hardhat 3. This version is still in testing. Do not use it to deploy or upgrade production deployments. For details and migration instructions, see: -> - [Hardhat Upgrades for Hardhat 3](./packages/plugin-hardhat/README.md) -> - [Hardhat 2 to 3 migration guide](./packages/plugin-hardhat/MIGRATION.md) - **Integrate upgrades into your existing workflow.** Plugins for [Hardhat](https://hardhat.org/) and [Foundry](https://book.getfoundry.sh/) to deploy and manage upgradeable contracts on Ethereum. - Deploy upgradeable contracts. diff --git a/packages/plugin-hardhat/MIGRATION.md b/packages/plugin-hardhat/MIGRATION.md index aba8fbc61..2c222b9ee 100644 --- a/packages/plugin-hardhat/MIGRATION.md +++ b/packages/plugin-hardhat/MIGRATION.md @@ -1,8 +1,6 @@ # Migration Guide: Hardhat 2 to Hardhat 3 > **Prerequisite:** Migrate your Hardhat project to Hardhat 3 first. See the [official Hardhat 3 migration guide](https://hardhat.org/docs/migrate-from-hardhat2). -> -> **Note** ⚠️ This version is still in testing. Do not use it to deploy or upgrade production deployments. ## Breaking Changes diff --git a/packages/plugin-hardhat/README.md b/packages/plugin-hardhat/README.md index 994be226d..806489ebe 100644 --- a/packages/plugin-hardhat/README.md +++ b/packages/plugin-hardhat/README.md @@ -6,13 +6,11 @@ **Hardhat plugin for deploying and managing upgradeable contracts.** 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 [Migration Guide](./MIGRATION.md) for breaking changes and how to update your code, or see the [example projects](./examples/) for complete transparent and UUPS proxy examples using Hardhat 3. -> -> **Note** ⚠️ This version is still in testing. Do not use it to deploy or upgrade production deployments. ## Installation ``` -npm install --save-dev @openzeppelin/hardhat-upgrades@next +npm install --save-dev @openzeppelin/hardhat-upgrades npm install --save-dev @nomicfoundation/hardhat-ethers ethers # peer dependencies ``` @@ -216,7 +214,7 @@ This is optional and only needed if you want Solidity-based tests. Install: ```bash -npm install --save-dev @openzeppelin/foundry-upgrades@next +npm install --save-dev @openzeppelin/foundry-upgrades ``` Configure your Hardhat config for Solidity tests: @@ -253,21 +251,6 @@ npx hardhat compile --force npx hardhat test solidity ``` -## TypeScript Support - -Full TypeScript support is included. Import the factory functions with type safety: - -```typescript -import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'; -import type { HardhatUpgrades, DefenderHardhatUpgrades } from '@openzeppelin/hardhat-upgrades'; - -task('deploy', async (args, hre) => { - const connection = await hre.network.connect(); - const upgradesApi: HardhatUpgrades = await upgrades(hre, connection); - // ... -}); -``` - ## Learn more * Refer to the [API documentation](https://docs.openzeppelin.com/upgrades-plugins/api-hardhat-upgrades). * Also see the [main documentation](https://docs.openzeppelin.com/upgrades-plugins) for more info. From 12987478c8d0b46c405c4637316f34a0394c23f1 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 20:38:18 -0400 Subject: [PATCH 11/21] Update changesets, changelogs, readme --- .changeset/migrate-to-hardhat-3.md | 30 ++++++++++++++++++++ .changeset/support-hardhat-3-build-info.md | 8 ++++++ packages/core/CHANGELOG.md | 16 ----------- packages/plugin-hardhat/CHANGELOG.md | 32 ---------------------- packages/plugin-hardhat/README.md | 4 +-- 5 files changed, 40 insertions(+), 50 deletions(-) create mode 100644 .changeset/migrate-to-hardhat-3.md create mode 100644 .changeset/support-hardhat-3-build-info.md diff --git a/.changeset/migrate-to-hardhat-3.md b/.changeset/migrate-to-hardhat-3.md new file mode 100644 index 000000000..7e315a007 --- /dev/null +++ b/.changeset/migrate-to-hardhat-3.md @@ -0,0 +1,30 @@ +--- +'@openzeppelin/hardhat-upgrades': major +--- + +Migrate to Hardhat 3 with ESM module structure and plugin hooks architecture. + +### Breaking Changes + +- **Requires Hardhat 3**: minimum `hardhat@^3.0.0` required. +- **ESM-only**: package converted to ESM; CommonJS is no longer supported. +- **API Changes**: + - No automatic `hre.upgrades` — call the `upgrades(hre, connection)` factory explicitly. + - Factory functions (`upgrades`, `defender`) are async and require a network connection. + - Network connection must be explicitly created: `const connection = await hre.network.connect()`. Share one connection across operations. + - `ethers` now comes from the connection (`const { ethers } = connection`), not `hre.ethers`. +- **Import Changes**: import factory functions instead of a side-effect import. + - Before: `import '@openzeppelin/hardhat-upgrades'` + - After: `import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'` + +### Usage and Migration + +See the [README](./README.md) for Hardhat 3 usage, the [examples](./examples/README.md) directory for sample projects, and the [Migration Guide](./MIGRATION.md) for Hardhat 2 to 3 migration steps. + +### Changes + +- Migrated from `extendEnvironment` to Hardhat 3's `HardhatPlugin` with `hookHandlers`. +- Converted package to ESM. +- Etherscan verification requires `@nomicfoundation/hardhat-verify@^3.0.10` (optional peer dependency). +- Support [Solidity tests in Hardhat 3](./README.md#solidity-tests) with `@openzeppelin/foundry-upgrades`. +- Added example projects for Hardhat 3 (Transparent, UUPS, and Solidity-test scaffolds under `packages/plugin-hardhat/examples/`). diff --git a/.changeset/support-hardhat-3-build-info.md b/.changeset/support-hardhat-3-build-info.md new file mode 100644 index 000000000..dfbb1a4e1 --- /dev/null +++ b/.changeset/support-hardhat-3-build-info.md @@ -0,0 +1,8 @@ +--- +'@openzeppelin/upgrades-core': minor +--- + +Support Hardhat 3 build-info file format for CLI validation. +- Handle the split `.json` / `.output.json` file format used by Hardhat 3. +- Improve support for validating build-info files in the CLI across Hardhat 3, Hardhat 2, and Foundry. +- Improve error handling for Hardhat 3 `.output.json` files. diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 5472dc0f7..0a782f065 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,21 +1,5 @@ # Changelog -## Unreleased - -- Improve error handling for Hardhat 3 .output.json files. - -## 1.45.0-alpha.1 (2026-03-02) - -- Improve support for validating build-info files in CLI for Hardhat 3, Hardhat 2, and Foundry. ([#1194](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1194)) - -> **Note** ⚠️ This version is still in testing and should not be used in production. - -## 1.45.0-alpha.0 (2026-01-20) - -- Support Hardhat 3 build-info file format for CLI validation ([#1203](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1203)) - -> **Note** ⚠️ This version is still in testing and should not be used in production. - ## 1.44.2 (2025-11-03) - Add Celo Sepolia network to manifest file names. ([#1189](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1189)) diff --git a/packages/plugin-hardhat/CHANGELOG.md b/packages/plugin-hardhat/CHANGELOG.md index 2b2062182..4a8fa208d 100644 --- a/packages/plugin-hardhat/CHANGELOG.md +++ b/packages/plugin-hardhat/CHANGELOG.md @@ -1,37 +1,5 @@ # Changelog -## 4.0.0-alpha.0 (2026-03-02) - -> **Note** ⚠️ This version is still in testing. Do not use it to deploy or upgrade production deployments. - -- Migrate to Hardhat 3 with ESM module structure and plugin hooks architecture. ([#1194](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1194)) - - ### Breaking Changes - - - **Requires Hardhat 3**: Minimum `hardhat@^3.0.0` required - - **ESM-only**: Package converted to ESM, CommonJS no longer supported - - **API Changes**: - - No automatic `hre.upgrades` - must call factory function explicitly - - Factory functions are async: `await upgrades(hre, connection)` - - Network connection must be explicitly created: `await hre.network.connect()` - - `ethers` now comes from connection, not `hre.ethers` - - **Import Changes**: Import factory functions instead of side-effect import - - Before: `import '@openzeppelin/hardhat-upgrades'` - - After: `import { upgrades, defender } from '@openzeppelin/hardhat-upgrades'` - - ### Usage and Migration - - See the [README](./README.md) for Hardhat 3 usage, the [examples](./examples/README.md) directory for sample projects, and the [Migration Guide](./MIGRATION.md) for Hardhat 2 to 3 migration steps. - - ### Changes - - - Migrated from `extendEnvironment` to Hardhat 3's `HardhatPlugin` with `hookHandlers` - - Converted package to ESM - - Etherscan verification requires `@nomicfoundation/hardhat-verify@^3.0.10` (optional peer dependency). - - Support [Solidity tests in Hardhat 3](./README.md#solidity-tests-hardhat-3) with `@openzeppelin/foundry-upgrades`. - - Added example projects to work with Hardhat 3 - - ## 3.9.1 (2025-06-30) - Support contract verification via etherscan V2 API. ([#1166](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1166)) diff --git a/packages/plugin-hardhat/README.md b/packages/plugin-hardhat/README.md index 806489ebe..38381f104 100644 --- a/packages/plugin-hardhat/README.md +++ b/packages/plugin-hardhat/README.md @@ -205,9 +205,9 @@ describe("Box", function() { }); ``` -### Solidity tests (Hardhat 3) +### Solidity tests -You can also write Solidity tests in Hardhat 3 and perform proxy deployments, upgrades, and upgrade safety validations directly from Solidity via `@openzeppelin/foundry-upgrades`. +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. This is optional and only needed if you want Solidity-based tests. From 7b82f32314c8ef0dc885f134e12a4be65e5d605a Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 20:58:13 -0400 Subject: [PATCH 12/21] Add soldity proxy note --- packages/plugin-hardhat/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/plugin-hardhat/README.md b/packages/plugin-hardhat/README.md index 38381f104..f5c803b56 100644 --- a/packages/plugin-hardhat/README.md +++ b/packages/plugin-hardhat/README.md @@ -211,6 +211,8 @@ Hardhat 3 supports writing tests in Solidity. You can use Solidity to test deplo This is optional and 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: ```bash From 6e3fbecd7965a607930b4b46e153b4d0f84899c6 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 22:41:47 -0400 Subject: [PATCH 13/21] Auto-force recompile when the validations cache is outdated --- .../plugin-hardhat/src/compile-task-action.ts | 39 ++++++++++++++++++ packages/plugin-hardhat/src/hooks/solidity.ts | 9 ++-- packages/plugin-hardhat/src/index.ts | 14 +++++++ .../plugin-hardhat/src/utils/validations.ts | 4 ++ .../test/force-recompile-on-outdated-cache.js | 41 +++++++++++++++++++ 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 packages/plugin-hardhat/src/compile-task-action.ts create mode 100644 packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js diff --git a/packages/plugin-hardhat/src/compile-task-action.ts b/packages/plugin-hardhat/src/compile-task-action.ts new file mode 100644 index 000000000..9dc1e8204 --- /dev/null +++ b/packages/plugin-hardhat/src/compile-task-action.ts @@ -0,0 +1,39 @@ +import type { TaskOverrideActionFunction } from 'hardhat/types/tasks'; + +/** + * Overrides the built-in `compile` / `build` task so that an outdated or missing + * validations cache triggers a full recompile automatically. + * + * This is the Hardhat 3 equivalent of the Hardhat 2 subtask override that + * forced `args.force = true` when the cache was detected as stale. Without + * this, a user who upgrades past a validations-schema bump would have to + * manually re-run `hardhat compile --force` to regenerate the cache. + * + * Fails-closed either way (the cache-check branches inside readValidations + * always delete the stale file), so this is a UX / parity fix, not a safety + * fix — see HH3-TODO-FORCE-RECOMPILATION.md for the full analysis. + */ +const action: TaskOverrideActionFunction = async (taskArguments, hre, runSuper) => { + const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound, isLockError } = await import( + './utils/validations.js' + ); + + let force = taskArguments.force === true; + if (!force) { + try { + await readValidations(hre); + } catch (e: unknown) { + if (e instanceof ValidationsCacheOutdated || e instanceof ValidationsCacheNotFound) { + force = true; + } else if (isLockError(e)) { + // Another process holds the lock; let that process handle regeneration. + } else { + throw e; + } + } + } + + return runSuper({ ...taskArguments, force }); +}; + +export default action; diff --git a/packages/plugin-hardhat/src/hooks/solidity.ts b/packages/plugin-hardhat/src/hooks/solidity.ts index c38741781..a9429a313 100644 --- a/packages/plugin-hardhat/src/hooks/solidity.ts +++ b/packages/plugin-hardhat/src/hooks/solidity.ts @@ -47,7 +47,7 @@ function toCompilerInput(input: SolcInput): CompilerInput { export default async (): Promise> => { return { async preprocessSolcInputBeforeBuilding(context, solcInput, next) { - const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import( + const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound, isLockError } = await import( '../utils/validations.js' ); @@ -56,11 +56,12 @@ export default async (): Promise> => { // Cache exists and is valid, continue normally } catch (e: unknown) { if (e instanceof ValidationsCacheOutdated) { - // Cache exists but is outdated - // TODO: when hardhat supports forcing recompilation, we should do it here + // Cache exists but is outdated. The compile-task override (see + // compile-task-action.ts) already detected this and forced a full + // recompile, so invokeSolc will regenerate the cache. } else if (e instanceof ValidationsCacheNotFound) { // Cache doesn't exist - that's fine, just proceed with compilation - } else if (typeof e === 'object' && e !== null && 'code' in e && e.code === 'ELOCKED') { + } else if (isLockError(e)) { // Lock file is being held by another process - warn once and continue if (!lockWarningShown) { logWarning('Validations cache is locked by another process.', ['Continuing without cache validation.']); diff --git a/packages/plugin-hardhat/src/index.ts b/packages/plugin-hardhat/src/index.ts index 461f55056..127d80818 100644 --- a/packages/plugin-hardhat/src/index.ts +++ b/packages/plugin-hardhat/src/index.ts @@ -1,4 +1,5 @@ import './type-extensions.js'; +import { overrideTask } from 'hardhat/config'; import type { HardhatPlugin } from 'hardhat/types/plugins'; const plugin: HardhatPlugin = { @@ -17,6 +18,19 @@ const plugin: HardhatPlugin = { plugin: () => import('./verify-plugin.js'), }, ], + + tasks: [ + // Force a full recompile when the validations cache is outdated or missing, + // so that the cache is regenerated transparently after a schema bump. + // Hardhat 3 registers `compile` and `build` as separate task IDs pointing at + // the same action, so we override both. + overrideTask('compile') + .setAction(async () => import('./compile-task-action.js')) + .build(), + overrideTask('build') + .setAction(async () => import('./compile-task-action.js')) + .build(), + ], }; export default plugin; diff --git a/packages/plugin-hardhat/src/utils/validations.ts b/packages/plugin-hardhat/src/utils/validations.ts index 3e52b2ee9..892e46c83 100644 --- a/packages/plugin-hardhat/src/utils/validations.ts +++ b/packages/plugin-hardhat/src/utils/validations.ts @@ -64,6 +64,10 @@ export async function readValidations( } } +export function isLockError(e: unknown): boolean { + return typeof e === 'object' && e !== null && 'code' in e && (e as NodeJS.ErrnoException).code === 'ELOCKED'; +} + export class ValidationsCacheNotFound extends Error { constructor() { super('Validations cache not found. Recompile with `hardhat compile --force`'); diff --git a/packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js b/packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js new file mode 100644 index 000000000..022ecccd6 --- /dev/null +++ b/packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js @@ -0,0 +1,41 @@ +import test from 'ava'; +import hre from 'hardhat'; +import { promises as fs } from 'fs'; +import path from 'path'; +import { isCurrentValidationData } from '@openzeppelin/upgrades-core'; + +const connection = await hre.network.connect(); +test.after.always(async () => connection.close()); + +const CACHE_PATH = path.join(hre.config.paths.cache, 'validations.json'); + +async function readCache() { + return JSON.parse(await fs.readFile(CACHE_PATH, 'utf8')); +} + +async function writeCache(cache) { + await fs.writeFile(CACHE_PATH, JSON.stringify(cache, null, 2)); +} + +test.serial('hardhat compile regenerates the validations cache when it is outdated', async t => { + t.timeout(60000); // Forcing a full recompile of all contracts takes several seconds. + + const originalRaw = await fs.readFile(CACHE_PATH, 'utf8'); + const currentCache = JSON.parse(originalRaw); + t.true(isCurrentValidationData(currentCache)); + t.true(currentCache.log.length > 0); + + try { + const outdatedCache = { ...currentCache, version: '3.0' }; + t.false(isCurrentValidationData(outdatedCache), 'precondition: modified cache must be detectable as outdated'); + await writeCache(outdatedCache); + + await hre.tasks.getTask('compile').run({}); + + const cacheAfterCompile = await readCache(); + t.true(isCurrentValidationData(cacheAfterCompile), 'compile should regenerate the cache to current schema'); + t.true(cacheAfterCompile.log.length > 0, 'regenerated cache must retain log entries'); + } finally { + await fs.writeFile(CACHE_PATH, originalRaw); + } +}); From 963d9de6ba0ed8356581215b6b4b182de10072ce Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 23:06:06 -0400 Subject: [PATCH 14/21] use semver range for deps --- packages/plugin-hardhat/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-hardhat/package.json b/packages/plugin-hardhat/package.json index 1365c07f8..81666606a 100644 --- a/packages/plugin-hardhat/package.json +++ b/packages/plugin-hardhat/package.json @@ -34,7 +34,7 @@ "@nomicfoundation/hardhat-verify": "^3.0.10", "@openzeppelin/contracts": "5.3.0", "@openzeppelin/contracts-upgradeable": "5.3.0", - "@openzeppelin/foundry-upgrades": "0.4.1-alpha.0", + "@openzeppelin/foundry-upgrades": "^0.4.1-alpha.0", "@types/mocha": "^7.0.2", "ava": "^6.0.0", "dotenv": "^16.0.0", @@ -61,7 +61,7 @@ "@openzeppelin/defender-sdk-base-client": "^2.1.0", "@openzeppelin/defender-sdk-deploy-client": "^2.1.0", "@openzeppelin/defender-sdk-network-client": "^2.1.0", - "@openzeppelin/upgrades-core": "1.45.0-alpha.1", + "@openzeppelin/upgrades-core": "^1.45.0-alpha.1", "chalk": "^4.1.0", "debug": "^4.1.1", "ethereumjs-util": "^7.1.5", From ee68c6e48c177007deb8df8c9af1558ac2225854 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 22 Apr 2026 23:07:48 -0400 Subject: [PATCH 15/21] Update lockfile --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 3ef59f1fa..b790c05e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1728,7 +1728,7 @@ lodash.startcase "^4.4.0" minimist "^1.2.0" -"@openzeppelin/foundry-upgrades@0.4.1-alpha.0": +"@openzeppelin/foundry-upgrades@^0.4.1-alpha.0": version "0.4.1-alpha.0" resolved "https://registry.yarnpkg.com/@openzeppelin/foundry-upgrades/-/foundry-upgrades-0.4.1-alpha.0.tgz#62379eb7826dfba25214d895acc9432ed230fcd2" integrity sha512-AhZiwM3pFq6OAs7yGj/rWKYMECqXBXffVlDRTs+oCeFPjdfE/MqjzyQSHCWFiYnJlOWDiP5GKz/8zcSbU4FjEQ== From 0867cf13ecc7a1726196d19fc78fb27ef2396c72 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 23 Apr 2026 16:02:28 -0400 Subject: [PATCH 16/21] Fix AST injection for non-default layouts and handle missing split build-info files --- .../plugin-hardhat/src/compile-task-action.ts | 7 ++-- packages/plugin-hardhat/src/hooks/solidity.ts | 33 ++++--------------- .../plugin-hardhat/src/utils/artifacts.ts | 17 ++++++++-- packages/plugin-hardhat/src/utils/errors.ts | 3 ++ .../plugin-hardhat/src/utils/validations.ts | 4 --- 5 files changed, 26 insertions(+), 38 deletions(-) create mode 100644 packages/plugin-hardhat/src/utils/errors.ts diff --git a/packages/plugin-hardhat/src/compile-task-action.ts b/packages/plugin-hardhat/src/compile-task-action.ts index 9dc1e8204..f0c7d3629 100644 --- a/packages/plugin-hardhat/src/compile-task-action.ts +++ b/packages/plugin-hardhat/src/compile-task-action.ts @@ -14,9 +14,8 @@ import type { TaskOverrideActionFunction } from 'hardhat/types/tasks'; * fix — see HH3-TODO-FORCE-RECOMPILATION.md for the full analysis. */ const action: TaskOverrideActionFunction = async (taskArguments, hre, runSuper) => { - const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound, isLockError } = await import( - './utils/validations.js' - ); + const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import('./utils/validations.js'); + const { isErrorCode } = await import('./utils/errors.js'); let force = taskArguments.force === true; if (!force) { @@ -25,7 +24,7 @@ const action: TaskOverrideActionFunction = async (taskArguments, hre, runSuper) } catch (e: unknown) { if (e instanceof ValidationsCacheOutdated || e instanceof ValidationsCacheNotFound) { force = true; - } else if (isLockError(e)) { + } else if (isErrorCode(e, 'ELOCKED')) { // Another process holds the lock; let that process handle regeneration. } else { throw e; diff --git a/packages/plugin-hardhat/src/hooks/solidity.ts b/packages/plugin-hardhat/src/hooks/solidity.ts index a9429a313..3d0781744 100644 --- a/packages/plugin-hardhat/src/hooks/solidity.ts +++ b/packages/plugin-hardhat/src/hooks/solidity.ts @@ -47,9 +47,10 @@ function toCompilerInput(input: SolcInput): CompilerInput { export default async (): Promise> => { return { async preprocessSolcInputBeforeBuilding(context, solcInput, next) { - const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound, isLockError } = await import( + const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import( '../utils/validations.js' ); + const { isErrorCode } = await import('../utils/errors.js'); try { await readValidations(context as HardhatRuntimeEnvironment); @@ -61,7 +62,7 @@ export default async (): Promise> => { // recompile, so invokeSolc will regenerate the cache. } else if (e instanceof ValidationsCacheNotFound) { // Cache doesn't exist - that's fine, just proceed with compilation - } else if (isLockError(e)) { + } else if (isErrorCode(e, 'ELOCKED')) { // Lock file is being held by another process - warn once and continue if (!lockWarningShown) { logWarning('Validations cache is locked by another process.', ['Continuing without cache validation.']); @@ -207,7 +208,7 @@ export default async (): Promise> => { // In Hardhat 3, AST is stored in build-info output files, not in artifacts // The Foundry upgrades plugin expects AST in artifact files, so we inject it here debug('Starting AST injection into artifacts...'); - await injectAstIntoArtifacts(artifactsDir, buildInfoDir); + await injectAstIntoArtifacts(artifactPaths, buildInfoDir); } catch (error: unknown) { if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') { throw error; @@ -224,33 +225,11 @@ export default async (): Promise> => { * artifacts via FFI during `hardhat test solidity`. HH3's artifact schema does not include AST * by default, so this hook bridges the gap. Removing it breaks `examples/BoxSolidityTests`. */ -export async function injectAstIntoArtifacts(artifactsDir: string, buildInfoDir: string): Promise { +export async function injectAstIntoArtifacts(artifactPaths: string[], buildInfoDir: string): Promise { const path = await import('path'); const fs = await import('fs/promises'); - // Recursively find all artifact JSON files - async function findArtifactFiles(dir: string): Promise { - const files: string[] = []; - try { - const entries = await fs.readdir(dir, { withFileTypes: true }); - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - if (entry.isDirectory()) { - files.push(...(await findArtifactFiles(fullPath))); - } else if (entry.isFile() && entry.name.endsWith('.json')) { - files.push(fullPath); - } - } - } catch (error: unknown) { - // Ignore errors (e.g., directory doesn't exist) - if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') { - throw error; - } - } - return files; - } - - const artifactFiles = await findArtifactFiles(path.join(artifactsDir, 'contracts')); + const artifactFiles = artifactPaths.filter(p => p.endsWith('.json')); let processedCount = 0; let skippedCount = 0; let errorCount = 0; diff --git a/packages/plugin-hardhat/src/utils/artifacts.ts b/packages/plugin-hardhat/src/utils/artifacts.ts index 66d734918..58f15826b 100644 --- a/packages/plugin-hardhat/src/utils/artifacts.ts +++ b/packages/plugin-hardhat/src/utils/artifacts.ts @@ -1,6 +1,7 @@ import type { BuildInfo, ArtifactManager } from 'hardhat/types/artifacts'; import type { SolidityBuildInfoOutput, CompilerInput } from 'hardhat/types/solidity'; import { readJsonFile } from '@nomicfoundation/hardhat-utils/fs'; +import { isErrorCode } from './errors.js'; /** * Combined build info with output, compatible with legacy format expectations. @@ -110,9 +111,19 @@ export async function getCombinedBuildInfo( return undefined; } - // Read both files - const buildInfo: BuildInfo = await readJsonFile(buildInfoPath); - const buildInfoOutput: SolidityBuildInfoOutput = await readJsonFile(buildInfoOutputPath); + // Read both files. A stale cache may have left the pair incomplete; treat a + // missing file as "no build info" so callers can fall back rather than throw. + let buildInfo: BuildInfo; + let buildInfoOutput: SolidityBuildInfoOutput; + try { + buildInfo = await readJsonFile(buildInfoPath); + buildInfoOutput = await readJsonFile(buildInfoOutputPath); + } catch (e: unknown) { + if (isErrorCode(e, 'ENOENT')) { + return undefined; + } + throw e; + } if (!buildInfoOutput.output.contracts) { return undefined; diff --git a/packages/plugin-hardhat/src/utils/errors.ts b/packages/plugin-hardhat/src/utils/errors.ts new file mode 100644 index 000000000..eaf576ccd --- /dev/null +++ b/packages/plugin-hardhat/src/utils/errors.ts @@ -0,0 +1,3 @@ +export function isErrorCode(e: unknown, code: string): boolean { + return typeof e === 'object' && e !== null && 'code' in e && (e as NodeJS.ErrnoException).code === code; +} diff --git a/packages/plugin-hardhat/src/utils/validations.ts b/packages/plugin-hardhat/src/utils/validations.ts index 892e46c83..3e52b2ee9 100644 --- a/packages/plugin-hardhat/src/utils/validations.ts +++ b/packages/plugin-hardhat/src/utils/validations.ts @@ -64,10 +64,6 @@ export async function readValidations( } } -export function isLockError(e: unknown): boolean { - return typeof e === 'object' && e !== null && 'code' in e && (e as NodeJS.ErrnoException).code === 'ELOCKED'; -} - export class ValidationsCacheNotFound extends Error { constructor() { super('Validations cache not found. Recompile with `hardhat compile --force`'); From 66680bf587b766c842e006784d180f459cf23b80 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 23 Apr 2026 16:03:04 -0400 Subject: [PATCH 17/21] fix lint --- packages/plugin-hardhat/src/compile-task-action.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/plugin-hardhat/src/compile-task-action.ts b/packages/plugin-hardhat/src/compile-task-action.ts index f0c7d3629..52c1a60ea 100644 --- a/packages/plugin-hardhat/src/compile-task-action.ts +++ b/packages/plugin-hardhat/src/compile-task-action.ts @@ -14,7 +14,9 @@ import type { TaskOverrideActionFunction } from 'hardhat/types/tasks'; * fix — see HH3-TODO-FORCE-RECOMPILATION.md for the full analysis. */ const action: TaskOverrideActionFunction = async (taskArguments, hre, runSuper) => { - const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import('./utils/validations.js'); + const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import( + './utils/validations.js' + ); const { isErrorCode } = await import('./utils/errors.js'); let force = taskArguments.force === true; From f71027c8a03e083673f37b3e0ab9b580637ef2b2 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 23 Apr 2026 22:55:48 -0400 Subject: [PATCH 18/21] Cleanup comments and format --- packages/plugin-hardhat/src/compile-task-action.ts | 8 +------- packages/plugin-hardhat/test/manifest-hh2-migration.js | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/plugin-hardhat/src/compile-task-action.ts b/packages/plugin-hardhat/src/compile-task-action.ts index 52c1a60ea..61e436a22 100644 --- a/packages/plugin-hardhat/src/compile-task-action.ts +++ b/packages/plugin-hardhat/src/compile-task-action.ts @@ -4,14 +4,8 @@ import type { TaskOverrideActionFunction } from 'hardhat/types/tasks'; * Overrides the built-in `compile` / `build` task so that an outdated or missing * validations cache triggers a full recompile automatically. * - * This is the Hardhat 3 equivalent of the Hardhat 2 subtask override that - * forced `args.force = true` when the cache was detected as stale. Without - * this, a user who upgrades past a validations-schema bump would have to + * Without this, a user who upgrades past a validations-schema bump would have to * manually re-run `hardhat compile --force` to regenerate the cache. - * - * Fails-closed either way (the cache-check branches inside readValidations - * always delete the stale file), so this is a UX / parity fix, not a safety - * fix — see HH3-TODO-FORCE-RECOMPILATION.md for the full analysis. */ const action: TaskOverrideActionFunction = async (taskArguments, hre, runSuper) => { const { readValidations, ValidationsCacheOutdated, ValidationsCacheNotFound } = await import( diff --git a/packages/plugin-hardhat/test/manifest-hh2-migration.js b/packages/plugin-hardhat/test/manifest-hh2-migration.js index d5e4c8345..a43ab9923 100644 --- a/packages/plugin-hardhat/test/manifest-hh2-migration.js +++ b/packages/plugin-hardhat/test/manifest-hh2-migration.js @@ -129,4 +129,4 @@ test.serial('upgrade preserves V1 entry with no version hash match', async t => t.true(Object.keys(v1After.layout.namespaces).length > 0); t.true(collectSrcs(v1After.layout).every(s => s.startsWith('legacy/'))); t.deepEqual(v1After.layout, patchedEntry.layout); -}); \ No newline at end of file +}); From c2dae435ccbf34f5a1b4d9758d8f4d6f5a4cac70 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 23 Apr 2026 23:01:28 -0400 Subject: [PATCH 19/21] Test overrides of compile and build --- .../test/force-recompile-on-outdated-cache.js | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js b/packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js index 022ecccd6..4fe7ce9b2 100644 --- a/packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js +++ b/packages/plugin-hardhat/test/force-recompile-on-outdated-cache.js @@ -17,25 +17,29 @@ async function writeCache(cache) { await fs.writeFile(CACHE_PATH, JSON.stringify(cache, null, 2)); } -test.serial('hardhat compile regenerates the validations cache when it is outdated', async t => { - t.timeout(60000); // Forcing a full recompile of all contracts takes several seconds. - - const originalRaw = await fs.readFile(CACHE_PATH, 'utf8'); - const currentCache = JSON.parse(originalRaw); - t.true(isCurrentValidationData(currentCache)); - t.true(currentCache.log.length > 0); - - try { - const outdatedCache = { ...currentCache, version: '3.0' }; - t.false(isCurrentValidationData(outdatedCache), 'precondition: modified cache must be detectable as outdated'); - await writeCache(outdatedCache); - - await hre.tasks.getTask('compile').run({}); - - const cacheAfterCompile = await readCache(); - t.true(isCurrentValidationData(cacheAfterCompile), 'compile should regenerate the cache to current schema'); - t.true(cacheAfterCompile.log.length > 0, 'regenerated cache must retain log entries'); - } finally { - await fs.writeFile(CACHE_PATH, originalRaw); - } -}); +// Both task IDs must be covered: `hardhat compile` is the user-facing command, while +// `hardhat test`, `test solidity`, and `run` invoke the `build` task internally. +for (const taskId of ['compile', 'build']) { + test.serial(`hardhat ${taskId} regenerates the validations cache when it is outdated`, async t => { + t.timeout(60000); // Forcing a full recompile of all contracts takes several seconds. + + const originalRaw = await fs.readFile(CACHE_PATH, 'utf8'); + const currentCache = JSON.parse(originalRaw); + t.true(isCurrentValidationData(currentCache)); + t.true(currentCache.log.length > 0); + + try { + const outdatedCache = { ...currentCache, version: '3.0' }; + t.false(isCurrentValidationData(outdatedCache), 'precondition: modified cache must be detectable as outdated'); + await writeCache(outdatedCache); + + await hre.tasks.getTask(taskId).run({}); + + const cacheAfterCompile = await readCache(); + t.true(isCurrentValidationData(cacheAfterCompile), `${taskId} should regenerate the cache to current schema`); + t.true(cacheAfterCompile.log.length > 0, 'regenerated cache must retain log entries'); + } finally { + await fs.writeFile(CACHE_PATH, originalRaw); + } + }); +} From ca718c8c1444d3c220d984405262d0b638fae34f Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 24 Apr 2026 11:26:53 -0400 Subject: [PATCH 20/21] Use tryRequire for hardhat-defender existence check and bind manifest tests to deployed impl --- packages/plugin-hardhat/src/utils/factory.ts | 16 ++++++-------- .../plugin-hardhat/src/utils/try-require.ts | 18 ++++++++++++++++ .../test/manifest-hh2-migration.js | 21 +++++++++++++++---- 3 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 packages/plugin-hardhat/src/utils/try-require.ts diff --git a/packages/plugin-hardhat/src/utils/factory.ts b/packages/plugin-hardhat/src/utils/factory.ts index df6a7039c..6b79a6439 100644 --- a/packages/plugin-hardhat/src/utils/factory.ts +++ b/packages/plugin-hardhat/src/utils/factory.ts @@ -7,7 +7,9 @@ import { getImplementationAddress, getBeaconAddress, getImplementationAddressFromBeacon, + logWarning, } from '@openzeppelin/upgrades-core'; +import { tryRequire } from './try-require.js'; /** * Factory function to create the upgrades API for a given HRE. @@ -31,7 +33,7 @@ export async function upgrades( hre: HardhatRuntimeEnvironment, connection: NetworkConnection, ): Promise { - await warnOnHardhatDefender(); + warnOnHardhatDefender(); return await createUpgradesAPI(hre, false, connection); } @@ -57,7 +59,7 @@ export async function defender( hre: HardhatRuntimeEnvironment, connection: NetworkConnection, ): Promise { - await warnOnHardhatDefender(); + warnOnHardhatDefender(); return await createDefenderAPI(hre, connection); } @@ -161,17 +163,11 @@ async function createDefenderAPI( }; } -async function warnOnHardhatDefender(): Promise { - try { - // Try to import the deprecated package to check if it's installed - // @ts-expect-error - Package may not be installed, which is the expected case - await import('@openzeppelin/hardhat-defender'); - const { logWarning } = await import('@openzeppelin/upgrades-core'); +function warnOnHardhatDefender(): void { + if (tryRequire('@openzeppelin/hardhat-defender', true)) { logWarning('The @openzeppelin/hardhat-defender package is deprecated.', [ 'Uninstall the @openzeppelin/hardhat-defender package.', 'OpenZeppelin Defender integration is included as part of the Hardhat Upgrades plugin.', ]); - } catch (e: any) { - // Package not installed, no warning needed } } diff --git a/packages/plugin-hardhat/src/utils/try-require.ts b/packages/plugin-hardhat/src/utils/try-require.ts new file mode 100644 index 000000000..7af2660d3 --- /dev/null +++ b/packages/plugin-hardhat/src/utils/try-require.ts @@ -0,0 +1,18 @@ +import { createRequire } from 'node:module'; + +const nodeRequire = createRequire(import.meta.url); + +/** + * Returns true if `id` can be resolved (and optionally loaded) from this package. + * With `resolveOnly`, only the module path is resolved — the module body is not executed. + * Mirrors the HH2 plugin's helper of the same name. + */ +export function tryRequire(id: string, resolveOnly?: boolean): boolean { + try { + resolveOnly ? nodeRequire.resolve(id) : nodeRequire(id); + return true; + } catch { + // do nothing + } + return false; +} diff --git a/packages/plugin-hardhat/test/manifest-hh2-migration.js b/packages/plugin-hardhat/test/manifest-hh2-migration.js index a43ab9923..443765dca 100644 --- a/packages/plugin-hardhat/test/manifest-hh2-migration.js +++ b/packages/plugin-hardhat/test/manifest-hh2-migration.js @@ -59,14 +59,22 @@ function collectSrcs(layout) { ]; } +function findManifestKeyByAddress(impls, address) { + const entry = Object.entries(impls).find(([, v]) => v.address === address); + return entry?.[0]; +} + test.serial('upgrade refreshes stale V1 layout srcs', async t => { const V1 = await ethers.getContractFactory('MultipleNamespacesAndRegularVariables'); const V2 = await ethers.getContractFactory('MultipleNamespacesAndRegularVariablesV2_Ok'); const proxy = await upgrades.deployProxy(V1, { kind: 'transparent' }); + const proxyAddress = await proxy.getAddress(); + const v1ImplAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress); const data = await readManifest(ethers.provider); - const v1Key = Object.keys(data.impls)[0]; + const v1Key = findManifestKeyByAddress(data.impls, v1ImplAddress); + t.truthy(v1Key, `expected manifest entry for impl ${v1ImplAddress}`); const v1 = data.impls[v1Key]; const srcs = collectSrcs(v1.layout); @@ -84,6 +92,7 @@ test.serial('upgrade refreshes stale V1 layout srcs', async t => { const upgraded = await upgrades.upgradeProxy(proxy, V2); await upgraded.waitForDeployment(); + const v2ImplAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress); const after = await readManifest(ethers.provider); const v1After = after.impls[v1Key]; @@ -93,8 +102,8 @@ test.serial('upgrade refreshes stale V1 layout srcs', async t => { t.true(collectSrcs(v1After.layout).every(s => !s.startsWith('legacy/'))); t.deepEqual(omitSrcs(v1After.layout), omitSrcs(v1.layout)); - const v2Key = Object.keys(after.impls).find(k => k !== v1Key); - t.truthy(v2Key); + const v2Key = findManifestKeyByAddress(after.impls, v2ImplAddress); + t.truthy(v2Key, `expected manifest entry for impl ${v2ImplAddress}`); t.true(collectSrcs(after.impls[v2Key].layout).every(s => !s.startsWith('legacy/'))); }); @@ -104,10 +113,14 @@ test.serial('upgrade preserves V1 entry with no version hash match', async t => const proxy = await upgrades.deployProxy(V1, { kind: 'transparent' }); const proxyAddress = await proxy.getAddress(); + const v1ImplAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress); const fakeHash = 'deadbeef'.repeat(8); await patchManifest(ethers.provider, d => { - const realKey = Object.keys(d.impls).find(k => d.impls[k].address !== undefined); + const realKey = findManifestKeyByAddress(d.impls, v1ImplAddress); + if (!realKey) { + throw new Error(`Missing manifest entry for impl ${v1ImplAddress}`); + } const entry = d.impls[realKey]; delete d.impls[realKey]; entry.layout = toLegacySrcs(entry.layout); From e4ac934ffa36a185a23611d0872b1d3f2d6e9a3f Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 24 Apr 2026 13:46:03 -0400 Subject: [PATCH 21/21] Propagate unexpected errors from the namespaced compile --- packages/plugin-hardhat/src/hooks/solidity.ts | 99 +++++++++---------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/packages/plugin-hardhat/src/hooks/solidity.ts b/packages/plugin-hardhat/src/hooks/solidity.ts index 3d0781744..ebeb4aaed 100644 --- a/packages/plugin-hardhat/src/hooks/solidity.ts +++ b/packages/plugin-hardhat/src/hooks/solidity.ts @@ -119,67 +119,60 @@ export default async (): Promise> => { const decodeSrc = solcInputOutputDecoder(solcInputCore, solcOutputCore); let namespacedOutput: SolcOutput | undefined = undefined; - // Handle namespaced storage layouts + // Handle namespaced storage layouts. Solidity compile errors in the namespaced + // output are surfaced through the `namespacedCompileErrors` setting below; any + // other exception indicates an unexpected bug and is allowed to propagate rather + // than silently dropping namespaced layout validation. if (isNamespaceSupported(solcVersion)) { - try { - let namespacedInput = makeNamespacedInput(solcInputCore, solcOutputCore, solcVersion); - namespacedInput = await trySanitizeNatSpec(namespacedInput, solcVersion); + let namespacedInput = makeNamespacedInput(solcInputCore, solcOutputCore, solcVersion); + namespacedInput = await trySanitizeNatSpec(namespacedInput, solcVersion); - // Run the namespaced compilation by calling next() with modified input - const namespacedResult = await next(context, compiler, toCompilerInput(namespacedInput), solcConfig); + // Run the namespaced compilation by calling next() with modified input + const namespacedResult = await next(context, compiler, toCompilerInput(namespacedInput), solcConfig); - const namespacedCompileErrors = getNamespacedCompileErrors(namespacedResult); + const namespacedCompileErrors = getNamespacedCompileErrors(namespacedResult); - if (namespacedCompileErrors.length > 0) { - const msg = `Failed to compile modified contracts for namespaced storage layout validations:\n\n${namespacedCompileErrors.join('\n')}`; - const preamble = [ - 'Please report this at https://zpl.in/upgrades/report.', - 'If possible, include the source code for the contracts mentioned in the errors above.', - 'This step allows for advanced storage modifications such as tight variable packing when performing upgrades with namespaced storage layouts.', - ]; + if (namespacedCompileErrors.length > 0) { + const msg = `Failed to compile modified contracts for namespaced storage layout validations:\n\n${namespacedCompileErrors.join('\n')}`; + const preamble = [ + 'Please report this at https://zpl.in/upgrades/report.', + 'If possible, include the source code for the contracts mentioned in the errors above.', + 'This step allows for advanced storage modifications such as tight variable packing when performing upgrades with namespaced storage layouts.', + ]; - const namespacedErrorsSetting = ( - context.config as HardhatRuntimeEnvironment['config'] & { - namespacedCompileErrors?: 'error' | 'warn' | 'ignore'; - } - ).namespacedCompileErrors; - - switch (namespacedErrorsSetting) { - case undefined: - case 'error': { - const { UpgradesError } = await import('@openzeppelin/upgrades-core'); - const details = [ - ...preamble, - 'If you are not using namespaced storage, or if you do not anticipate making advanced modifications to namespaces during upgrades,', - "you can set namespacedCompileErrors: 'warn' or namespacedCompileErrors: 'ignore' in your hardhat config to convert this to a warning or to ignore this.", - ]; - throw new UpgradesError(msg, () => details.join('\n')); - } - case 'warn': { - const details = [ - ...preamble, - "you can set namespacedCompileErrors: 'ignore' in your hardhat config to ignore this.", - ]; - logWarning(msg, details); - break; - } - case 'ignore': - break; - default: - assertUnreachable(namespacedErrorsSetting); + const namespacedErrorsSetting = ( + context.config as HardhatRuntimeEnvironment['config'] & { + namespacedCompileErrors?: 'error' | 'warn' | 'ignore'; } - namespacedOutput = undefined; - } else { - namespacedOutput = toSolcOutput(namespacedResult); - } - } catch (err: unknown) { - // If it's an UpgradesError, rethrow it - const { UpgradesError } = await import('@openzeppelin/upgrades-core'); - if (err instanceof UpgradesError) { - throw err; + ).namespacedCompileErrors; + + switch (namespacedErrorsSetting) { + case undefined: + case 'error': { + const { UpgradesError } = await import('@openzeppelin/upgrades-core'); + const details = [ + ...preamble, + 'If you are not using namespaced storage, or if you do not anticipate making advanced modifications to namespaces during upgrades,', + "you can set namespacedCompileErrors: 'warn' or namespacedCompileErrors: 'ignore' in your hardhat config to convert this to a warning or to ignore this.", + ]; + throw new UpgradesError(msg, () => details.join('\n')); + } + case 'warn': { + const details = [ + ...preamble, + "you can set namespacedCompileErrors: 'ignore' in your hardhat config to ignore this.", + ]; + logWarning(msg, details); + break; + } + case 'ignore': + break; + default: + assertUnreachable(namespacedErrorsSetting); } - // Otherwise, silently continue without namespaced output namespacedOutput = undefined; + } else { + namespacedOutput = toSolcOutput(namespacedResult); } }