From df6b7ff0590703ad1d80d8a2a511064829b91eb2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 14 Jan 2026 10:03:04 +0100 Subject: [PATCH 01/91] wip --- hardhat.config.js | 124 - hardhat.config.ts | 114 + hardhat/env-artifacts.js | 29 - hardhat/hardhat-exposed/core/format-lines.ts | 26 + hardhat/hardhat-exposed/core/index.ts | 705 +++ hardhat/hardhat-exposed/core/types.ts | 14 + .../hardhat-exposed/hook-handlers/config.ts | 22 + .../hardhat-exposed/hook-handlers/solidity.ts | 149 + hardhat/hardhat-exposed/plugin.ts | 23 + hardhat/hardhat-exposed/tasks/build.ts | 26 + hardhat/hardhat-exposed/tasks/clean.ts | 21 + hardhat/hardhat-exposed/type-extensions.ts | 13 + hardhat/hook-handlers/hre.ts | 33 + hardhat/ignore-unreachable-warnings.js | 45 - hardhat/remappings.js | 18 - hardhat/skip-foundry-tests.js | 6 - lib/forge-std | 2 +- package-lock.json | 4967 +++++------------ package.json | 26 +- remappings.txt | 4 + test/access/Ownable.test.js | 9 +- test/account/utils/draft-ERC7579Utils.t.sol | 2 +- test/metatx/ERC2771Forwarder.t.sol | 4 +- test/proxy/Clones.t.sol | 3 +- 24 files changed, 2589 insertions(+), 3796 deletions(-) delete mode 100644 hardhat.config.js create mode 100644 hardhat.config.ts delete mode 100644 hardhat/env-artifacts.js create mode 100644 hardhat/hardhat-exposed/core/format-lines.ts create mode 100644 hardhat/hardhat-exposed/core/index.ts create mode 100644 hardhat/hardhat-exposed/core/types.ts create mode 100644 hardhat/hardhat-exposed/hook-handlers/config.ts create mode 100644 hardhat/hardhat-exposed/hook-handlers/solidity.ts create mode 100644 hardhat/hardhat-exposed/plugin.ts create mode 100644 hardhat/hardhat-exposed/tasks/build.ts create mode 100644 hardhat/hardhat-exposed/tasks/clean.ts create mode 100644 hardhat/hardhat-exposed/type-extensions.ts create mode 100644 hardhat/hook-handlers/hre.ts delete mode 100644 hardhat/ignore-unreachable-warnings.js delete mode 100644 hardhat/remappings.js delete mode 100644 hardhat/skip-foundry-tests.js diff --git a/hardhat.config.js b/hardhat.config.js deleted file mode 100644 index c32c7977a99..00000000000 --- a/hardhat.config.js +++ /dev/null @@ -1,124 +0,0 @@ -/// ENVVAR -// - COMPILER: compiler version (default: 0.8.31) -// - SRC: contracts folder to compile (default: contracts) -// - RUNS: number of optimization runs (default: 200) -// - IR: enable IR compilation (default: false) -// - COVERAGE: enable coverage report (default: false) -// - GAS: enable gas report (default: false) -// - COINMARKETCAP: coinmarketcap api key for USD value in gas report -// - CI: output gas report to file instead of stdout - -const fs = require('fs'); -const path = require('path'); - -const { argv } = require('yargs/yargs')() - .env('') - .options({ - // Compilation settings - compiler: { - alias: 'compileVersion', - type: 'string', - default: '0.8.31', - }, - src: { - alias: 'source', - type: 'string', - default: 'contracts', - }, - runs: { - alias: 'optimizationRuns', - type: 'number', - default: 200, - }, - ir: { - alias: 'enableIR', - type: 'boolean', - default: false, - }, - evm: { - alias: 'evmVersion', - type: 'string', - default: 'osaka', - }, - // Extra modules - coverage: { - type: 'boolean', - default: false, - }, - gas: { - alias: 'enableGasReport', - type: 'boolean', - default: false, - }, - coinmarketcap: { - alias: 'coinmarketcapApiKey', - type: 'string', - }, - }); - -require('@nomicfoundation/hardhat-chai-matchers'); -require('@nomicfoundation/hardhat-ethers'); -require('hardhat-exposed'); -require('hardhat-gas-reporter'); -require('hardhat-ignore-warnings'); -require('hardhat-predeploy'); -require('solidity-coverage'); -require('solidity-docgen'); - -for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) { - require(path.join(__dirname, 'hardhat', f)); -} - -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: { - version: argv.compiler, - settings: { - optimizer: { - enabled: true, - runs: argv.runs, - }, - evmVersion: argv.evm, - viaIR: argv.ir, - outputSelection: { '*': { '*': ['storageLayout'] } }, - }, - }, - warnings: { - 'contracts-exposed/**/*': { - 'code-size': 'off', - 'initcode-size': 'off', - }, - '*': { - 'unused-param': !argv.coverage, // coverage causes unused-param warnings - 'transient-storage': false, - default: 'error', - }, - }, - networks: { - hardhat: { - hardfork: argv.evm, - // Exposed contracts often exceed the maximum contract size. For normal contract, - // we rely on the `code-size` compiler warning, that will cause a compilation error. - allowUnlimitedContractSize: true, - initialBaseFeePerGas: argv.coverage ? 0 : undefined, - }, - }, - exposed: { - imports: true, - initializers: true, - exclude: ['vendor/**/*', '**/*WithInit.sol'], - }, - gasReporter: { - enabled: argv.gas, - showMethodSig: true, - includeBytecodeInJSON: true, - currency: 'USD', - coinmarketcap: argv.coinmarketcap, - }, - paths: { - sources: argv.src, - }, - docgen: require('./docs/config'), -}; diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 00000000000..505266269b7 --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,114 @@ +import { defineConfig, overrideTask } from 'hardhat/config'; + +// Plugins +import hardhatEthers from '@nomicfoundation/hardhat-ethers'; +import hardhatEthersChaiMatchers from '@nomicfoundation/hardhat-ethers-chai-matchers'; +import hardhatIgnoreWarnings from 'hardhat-ignore-warnings'; +import hardhatMocha from '@nomicfoundation/hardhat-mocha'; +import hardhatNetworkHelpers from '@nomicfoundation/hardhat-network-helpers'; +import hardhatPredeploy from 'hardhat-predeploy'; +import hardhatExposed from './hardhat/hardhat-exposed/plugin.js'; + +// Parameters +import yargs from 'yargs/yargs'; +const argv = await yargs() + .env('') + .options({ + // Compilation settings + compiler: { + type: 'string', + default: '0.8.31', + }, + src: { + type: 'string', + default: 'contracts', + }, + runs: { + type: 'number', + default: 200, + }, + ir: { + type: 'boolean', + default: false, + }, + evm: { + type: 'string', + default: 'osaka', + }, + }) + .parse(); + +// Configuration +export default defineConfig({ + plugins: [ + // Imported plugins + hardhatEthers, + hardhatEthersChaiMatchers, + hardhatIgnoreWarnings, + hardhatMocha, + hardhatNetworkHelpers, + hardhatPredeploy, + // Local plugins + // hardhatExposed, + // Additional hooks + { + id: '@openzeppelin/contracts', + hookHandlers: { + hre: () => import('./hardhat/hook-handlers/hre.js'), + }, + }, + ], + paths: { + sources: argv.src, + }, + solidity: { + version: argv.compiler, + settings: { + optimizer: { + enabled: true, + runs: argv.runs, + }, + evmVersion: argv.evm, + viaIR: argv.ir, + outputSelection: { '*': { '*': ['storageLayout'] } }, + }, + }, + networks: { + default: { + type: 'edr-simulated', + hardfork: argv.evm, + // Exposed contracts often exceed the maximum contract size. For normal contract, + // we rely on the `code-size` compiler warning, that will cause a compilation error. + // allowUnlimitedContractSize: true, + // initialBaseFeePerGas: argv.coverage ? 0 : undefined, + }, + }, + test: { + solidity: { + fuzz: { + // runs: 5000, + // maxTestRejects: 150000, + }, + fsPermissions: { + readDirectory: ['node_modules/hardhat-predeploy/bin'], + }, + }, + }, + warnings: { + 'contracts-exposed/**/*': { + 'code-size': 'off', + 'initcode-size': 'off', + }, + 'test/**/*': 'off', + '*': { + 'transient-storage': 'off', + default: 'error', + }, + }, + exposed: { + imports: true, + initializers: true, + include: ['contracts/**/*.sol'], + exclude: ['contracts/vendor/**/*', '**/*WithInit.sol'], + }, +}); diff --git a/hardhat/env-artifacts.js b/hardhat/env-artifacts.js deleted file mode 100644 index e97ae6468e3..00000000000 --- a/hardhat/env-artifacts.js +++ /dev/null @@ -1,29 +0,0 @@ -const { HardhatError } = require('hardhat/internal/core/errors'); - -function isExpectedError(e, suffix) { - // HH700: Artifact not found - from https://hardhat.org/hardhat-runner/docs/errors#HH700 - return HardhatError.isHardhatError(e) && e.number === 700 && suffix !== ''; -} - -// Modifies the artifact require functions so that instead of X it loads the XUpgradeable contract. -// This allows us to run the same test suite on both the original and the transpiled and renamed Upgradeable contracts. -extendEnvironment(hre => { - const suffixes = ['UpgradeableWithInit', 'Upgradeable', '']; - - // Ethers - const originalReadArtifact = hre.artifacts.readArtifact; - hre.artifacts.readArtifact = async function (name) { - for (const suffix of suffixes) { - try { - return await originalReadArtifact.call(this, name + suffix); - } catch (e) { - if (isExpectedError(e, suffix)) { - continue; - } else { - throw e; - } - } - } - throw new Error('Unreachable'); - }; -}); diff --git a/hardhat/hardhat-exposed/core/format-lines.ts b/hardhat/hardhat-exposed/core/format-lines.ts new file mode 100644 index 00000000000..52af7ab522b --- /dev/null +++ b/hardhat/hardhat-exposed/core/format-lines.ts @@ -0,0 +1,26 @@ +export type Lines = string | typeof whitespace | Lines[]; + +const whitespace = Symbol('whitespace'); + +export function formatLines(...lines: Lines[]): string { + return [...indentEach(0, lines)].join('\n') + '\n'; +} + +function* indentEach(indent: number, lines: Lines[]): Generator { + for (const line of lines) { + if (line === whitespace) { + yield ''; + } else if (Array.isArray(line)) { + yield* indentEach(indent + 1, line); + } else { + yield ' '.repeat(indent) + line; + } + } +} + +export function spaceBetween(...lines: Lines[][]): Lines[] { + return lines + .filter(l => l.length > 0) + .flatMap(l => [whitespace, ...l]) + .slice(1); +} diff --git a/hardhat/hardhat-exposed/core/index.ts b/hardhat/hardhat-exposed/core/index.ts new file mode 100644 index 00000000000..fdbe60bcbc5 --- /dev/null +++ b/hardhat/hardhat-exposed/core/index.ts @@ -0,0 +1,705 @@ +import assert from 'assert'; +import path from 'path'; + +import type { HardhatConfig } from 'hardhat/types/config'; +import type { CompilerOutput } from 'hardhat/types/solidity'; +import { ensureDir, remove, writeUtf8File } from '@nomicfoundation/hardhat-utils/fs'; + +import type {} from '../type-extensions'; + +// AST manipulation +import type { + Visibility, + SourceUnit, + ContractDefinition, + FunctionDefinition, + VariableDeclaration, + StorageLocation, + TypeName, + UserDefinedTypeName, +} from 'solidity-ast'; +import type { ASTDereferencer } from 'solidity-ast/utils.js'; +import { findAll, astDereferencer } from 'solidity-ast/utils.js'; + +// Exposed code generation +import { formatLines, Lines, spaceBetween } from './format-lines'; + +type ContractFilter = (node: ContractDefinition) => boolean; +type ResolvedFile = { fsPath: string; content: string }; + +const exposedVersionPragma = '>=0.6.0'; +const defaultPrefix = '$'; + +// Tasks & Hooks +export async function cleanExposed(config: HardhatConfig) { + await remove(getExposedPath(config)); +} + +export async function writeExposed(exposed: Map) { + for (const [fsPath, content] of exposed.entries()) { + await ensureDir(path.dirname(fsPath)); + await writeUtf8File(fsPath, content); + } +} + +export function getExposed( + output: CompilerOutput, + include: (sourceName: string) => boolean, + config: HardhatConfig, +): Map { + const res = new Map(); + const deref = astDereferencer(output); + + for (const { ast } of Object.values(output.sources)) { + if (!include(ast.absolutePath)) { + continue; + } + + const exposedFile = getExposedFile(config, ast, deref); + if (exposedFile !== undefined) { + res.set(exposedFile.fsPath, exposedFile.content); + } + } + + return res; +} + +// Helpers +function getExposedPath(config: HardhatConfig) { + return path.join(config.paths.root, config.exposed.outDir); +} + +function getExposedFile( + config: HardhatConfig, + ast: SourceUnit, + deref: ASTDereferencer, + filter?: ContractFilter, +): ResolvedFile | undefined { + const exposedRootPath = getExposedPath(config); + const fsPath = path.join( + exposedRootPath, + ...(ast.absolutePath.startsWith('project/') + ? [path.relative('project', ast.absolutePath)] + : ['$_', ast.absolutePath]), + ); + const dirname = path.dirname(fsPath); + + const relativizePath = (p: string) => + (p.startsWith('project/') ? path.relative(dirname, path.relative('project', p)) : p).replace(/\\/g, '/'); + + const content = getExposedContent( + ast, + relativizePath, + deref, + config.exposed?.initializers, + config.exposed?.prefix, + filter, + ); + + return content === undefined ? undefined : { fsPath, content }; +} + +function getExposedContent( + ast: SourceUnit, + relativizePath: (p: string) => string, + deref: ASTDereferencer, + initializers = false, + prefix = defaultPrefix, + filter?: ContractFilter, +): string | undefined { + if (prefix === '' || /^\d|[^0-9a-z_$]/i.test(prefix)) { + throw new Error(`Prefix '${prefix}' is not valid`); + } + + const contractPrefix = prefix.replace(/^./, c => c.toUpperCase()); + const imports = Array.from(getNeededImports(ast, deref), u => relativizePath(u.absolutePath)); + const contracts = [...findAll('ContractDefinition', ast)].filter( + c => filter?.(c) !== false && c.contractKind !== 'interface', + ); + + return contracts.length === 0 + ? undefined + : formatLines( + ...spaceBetween( + ['// SPDX-License-Identifier: UNLICENSED'], + [`pragma solidity ${exposedVersionPragma};`], + imports.map(i => `import "${i}";`), + + ...contracts.map(c => { + const isLibrary = c.contractKind === 'library'; + const contractHeader = [`contract ${contractPrefix}${c.name}`]; + if (!areFunctionsFullyImplemented(c, deref)) { + contractHeader.unshift('abstract'); + } + if (!isLibrary) { + contractHeader.push(`is ${c.name}`); + } + contractHeader.push('{'); + + const subset: Visibility[] = isLibrary ? ['internal', 'public', 'external'] : ['internal']; + + const hasReceiveFunction = getFunctions(c, deref, ['external']).some(fn => fn.kind === 'receive'); + const externalizableVariables = getVariables(c, deref, subset).filter( + v => v.typeName?.nodeType !== 'UserDefinedTypeName' || isTypeExternalizable(v.typeName, deref), + ); + const externalizableFunctions = getFunctions(c, deref, subset).filter(f => isExternalizable(f, deref)); + const returnedEventFunctions = externalizableFunctions.filter(fn => isNonViewWithReturns(fn)); + + const clashingFunctions: Record = {}; + for (const fn of externalizableFunctions) { + const id = getFunctionId(fn, c, deref); + clashingFunctions[id] ??= 0; + clashingFunctions[id] += 1; + } + + const clashingEvents: Record = {}; + for (const fn of returnedEventFunctions) { + clashingEvents[fn.name] ??= 0; + clashingEvents[fn.name] += 1; + } + + return [ + contractHeader.join(' '), + [`bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";\n`], + spaceBetween( + // slots for storage function parameters + ...getAllStorageArguments(externalizableFunctions, c, deref).map(a => [ + `mapping(uint256 => ${a.storageType}) internal ${prefix}${a.storageVar};`, + ]), + // events for internal returns + ...returnedEventFunctions.map(fn => { + const evName = + clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false); + const params = getFunctionReturnParameters(fn, c, deref, null); + return [`event return${prefix}${evName}(${params.map(printArgument).join(', ')});`]; + }), + // constructor + makeConstructor(c, deref, initializers), + // accessor to internal variables + ...externalizableVariables.map(v => [ + [ + 'function', + `${prefix}${v.name}(${getVarGetterArgs(v, c, deref).map(printArgument).join(', ')})`, + 'external', + v.mutability === 'mutable' || (v.mutability === 'immutable' && !v.value) ? 'view' : 'pure', + 'returns', + `(${getVarGetterReturnType(v, c, deref)})`, + '{', + ].join(' '), + [ + `return ${isLibrary ? c.name + '.' : ''}${v.name}${getVarGetterArgs(v, c, deref) + .map(a => `[${a.name}]`) + .join('')};`, + ], + '}', + ]), + // external functions + ...externalizableFunctions.map(fn => { + const fnName = + clashingFunctions[getFunctionId(fn, c, deref)] === 1 + ? fn.name + : getFunctionNameQualified(fn, c, deref, true); + const fnArgs = getFunctionArguments(fn, c, deref); + const fnRets = getFunctionReturnParameters(fn, c, deref); + const evName = + isNonViewWithReturns(fn) && + (clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false)); + + // function header + const header = ['function', `${prefix}${fnName}(${fnArgs.map(printArgument)})`, 'external']; + + if (fn.stateMutability === 'nonpayable') { + header.push('payable'); + } else if (fn.stateMutability === 'pure' && fnArgs.some(a => a.storageVar)) { + header.push('view'); + } else { + header.push(fn.stateMutability); + } + + if (fn.returnParameters.parameters.length > 0) { + header.push(`returns (${fnRets.map(printArgument).join(', ')})`); + } + + header.push('{'); + + // function body + const body = [ + (fnRets.length === 0 ? '' : `(${fnRets.map(p => p.name).join(', ')}) = `) + + `${isLibrary ? c.name : 'super'}.${fn.name}(${fnArgs.map(a => (a.storageVar ? `${prefix}${a.storageVar}[${a.name}]` : a.name))});`, + ]; + + if (evName) { + body.push(`emit return${prefix}${evName}(${fnRets.map(p => p.name).join(', ')});`); + } + + // return function + return [header.join(' '), body, `}`]; + }), + // receive function + !hasReceiveFunction ? ['receive() external payable {}'] : [], + ), + `}`, + ]; + }), + ), + ); +} + +// Note this is not the same as contract.fullyImplemented, because this does +// not consider missing constructor calls. We don't use contract.abstract +// because even if a user declares a contract abstract, we want to make it +// concrete if it is possible. +function areFunctionsFullyImplemented(contract: ContractDefinition, deref: ASTDereferencer): boolean { + const parents = contract.linearizedBaseContracts.map(deref('ContractDefinition')); + const abstractFunctionIds = new Set( + parents.flatMap(p => [...findAll('FunctionDefinition', p)].filter(f => !f.implemented).map(f => f.id)), + ); + for (const p of parents) { + for (const f of findAll(['FunctionDefinition', 'VariableDeclaration'], p)) { + for (const b of f.baseFunctions ?? []) { + abstractFunctionIds.delete(b); + } + } + } + return abstractFunctionIds.size === 0; +} + +function getFunctionId(fn: FunctionDefinition, context: ContractDefinition, deref: ASTDereferencer): string { + const abiTypes = getFunctionArguments(fn, context, deref).map(a => a.abiType); + return fn.name + abiTypes.join(','); +} + +function getFunctionNameQualified( + fn: FunctionDefinition, + context: ContractDefinition, + deref: ASTDereferencer, + onlyConflicting: boolean, +): string { + let args = getFunctionArguments(fn, context, deref); + if (onlyConflicting) { + args = args.filter(a => a.type !== a.abiType || a.storageType !== undefined); + } + return ( + fn.name + + args + .map(arg => arg.storageType ?? arg.type) + .map(type => type.replace(/ .*/, '').replace(/[^0-9a-zA-Z$_]+/g, '_')) // sanitize + .join('_') + .replace(/^./, '_$&') + ); +} + +function makeConstructor(contract: ContractDefinition, deref: ASTDereferencer, initializers: boolean): Lines[] { + const parents = contract.linearizedBaseContracts.map(deref('ContractDefinition')).reverse(); + + const constructors = new Map( + parents + .map(p => getConstructor(p, initializers)) + .filter(notNull) + .map(c => [c.scope, c]), + ); + + const initializedParents = new Set(); + + for (const p of parents) { + for (const c of p.baseContracts) { + if (c.arguments?.length) { + initializedParents.add(c.baseName.referencedDeclaration); + } + } + + const ctor = constructors.get(p.id); + + if (ctor) { + if (ctor.kind === 'constructor') { + for (const m of ctor.modifiers) { + if (m.modifierName.referencedDeclaration != undefined) { + initializedParents.add(m.modifierName.referencedDeclaration); + } + } + } + + if (initializers) { + for (const fnCall of findAll('FunctionCall', ctor)) { + if (fnCall.expression.nodeType === 'Identifier' && isInitializerName(fnCall.expression.name, 'any')) { + const fnDef = deref('FunctionDefinition', fnCall.expression.referencedDeclaration!); + if (fnDef.scope !== p.id) { + initializedParents.add(fnDef.scope); + } + } + } + } + } + } + + const uninitializedParents = parents.filter( + c => c.contractKind === 'contract' && constructors.has(c.id) && !initializedParents.has(c.id), + ); + + const missingArguments = new Map(); // name -> type + const parentArguments = new Map(); + + for (const c of uninitializedParents) { + const args = []; + for (const a of constructors.get(c.id)?.parameters.parameters ?? []) { + const name = missingArguments.has(a.name) ? `${c.name}_${a.name}` : a.name; + const type = getVarType(a, c, deref, 'memory'); + missingArguments.set(name, type); + args.push(name); + } + parentArguments.set(c.name, args); + } + + const parentConstructorCalls = []; + const parentInitializerCalls = []; + + for (const p of uninitializedParents) { + const ctor = constructors.get(p.id); + if (ctor) { + const params = mustGet(parentArguments, p.name).join(', '); + if (ctor.kind === 'constructor') { + if (ctor.parameters.parameters.length) { + parentConstructorCalls.push(`${p.name}(${params})`); + } + } else { + parentInitializerCalls.push(`${ctor.name}(${params})`); + } + } + } + + return [ + [ + `constructor(${[...missingArguments].map(([name, type]) => `${type} ${name}`).join(', ')})`, + ...parentConstructorCalls, + ...(parentInitializerCalls.length ? ['initializer'] : []), + 'payable', + '{', + ].join(' '), + parentInitializerCalls.map(e => `${e};`), + '}', + ]; +} + +function getConstructor(contract: ContractDefinition, initializers: boolean): FunctionDefinition | undefined { + let ctor; + let init; + + for (const fnDef of findAll('FunctionDefinition', contract)) { + if (fnDef.kind === 'constructor') { + ctor = fnDef; + if (!initializers) break; + } + if (initializers && isInitializerName(fnDef.name)) { + init = fnDef; + if (ctor) break; + } + } + return init || ctor; +} + +function isInitializerName(fnName: string, kind?: 'unchained' | 'any'): boolean { + const m = fnName.match(/^__[a-zA-Z0-9$_]+_init(_unchained)?$/); + const isUnchained = m?.[1] === '_unchained'; + const wantsUnchained = kind === 'unchained'; + return m !== null && (kind === 'any' || isUnchained === wantsUnchained); +} + +function notNull(value: T): value is NonNullable { + return value != undefined; +} + +function isExternalizable(fnDef: FunctionDefinition, deref: ASTDereferencer): boolean { + return ( + fnDef.kind !== 'constructor' && + fnDef.visibility !== 'private' && + fnDef.implemented && + !fnDef.parameters.parameters.some(p => p.typeName?.nodeType === 'FunctionTypeName') && + fnDef.returnParameters.parameters.every(p => isTypeExternalizable(p.typeName, deref)) + ); +} + +function isTypeExternalizable(typeName: TypeName | null | undefined, deref: ASTDereferencer): boolean { + if (typeName == undefined) { + return true; + } else if (typeName.nodeType === 'UserDefinedTypeName') { + const typeDef = derefUserDefinedTypeName(deref, typeName); + if (typeDef.nodeType !== 'StructDefinition') { + return true; + } else { + return typeDef.members.every(m => isTypeExternalizable(m.typeName, deref)); + } + } else { + return typeName.nodeType !== 'Mapping' && typeName.nodeType !== 'FunctionTypeName'; + } +} + +function isNonViewWithReturns(fnDef: FunctionDefinition): boolean { + return ['payable', 'nonpayable'].includes(fnDef.stateMutability) && fnDef.returnParameters.parameters.length > 0; +} + +interface Argument { + type: string; + name: string; + abiType: string; + storageVar?: string; + storageType?: string; +} + +const printArgument = (arg: Argument) => `${arg.type} ${arg.name}`; + +function getFunctionArguments( + fnDef: FunctionDefinition, + context: ContractDefinition, + deref: ASTDereferencer, +): Argument[] { + return fnDef.parameters.parameters.map((p, i) => { + const name = p.name || `arg${i}`; + if (p.storageLocation === 'storage') { + const storageType = getVarType(p, context, deref, null); + const storageVar = 'v_' + storageType.replace(/[^0-9a-zA-Z$_]+/g, '_'); + // The argument is an index to an array in storage. + const type = 'uint256'; + return { name, type, abiType: type, storageVar, storageType }; + } else { + const type = getVarType(p, context, deref, 'calldata'); + const abiType = getVarAbiType(p, context, deref, 'calldata'); + return { name, type, abiType }; + } + }); +} + +function getStorageArguments( + fn: FunctionDefinition, + context: ContractDefinition, + deref: ASTDereferencer, +): Required[] { + return getFunctionArguments(fn, context, deref).filter( + (a): a is Required => !!(a.storageVar && a.storageType), + ); +} + +function getAllStorageArguments( + fns: FunctionDefinition[], + context: ContractDefinition, + deref: ASTDereferencer, +): Required[] { + return [...new Map(fns.flatMap(fn => getStorageArguments(fn, context, deref)).map(a => [a.storageVar, a])).values()]; +} + +function getFunctionReturnParameters( + fnDef: FunctionDefinition, + context: ContractDefinition, + deref: ASTDereferencer, + location: StorageLocation | null = 'memory', +): Argument[] { + return fnDef.returnParameters.parameters.map((p, i) => { + const name = p.name || `ret${i}`; + const type = getVarType(p, context, deref, location); + const abiType = getVarAbiType(p, context, deref, location); + return { name, type, abiType }; + }); +} + +function getVarType( + varDecl: VariableDeclaration, + context: ContractDefinition, + deref: ASTDereferencer, + location: StorageLocation | null = varDecl.storageLocation, +): string { + if (!varDecl.typeName) { + throw new Error('Missing type information'); + } + return getType(varDecl.typeName, context, deref, location); +} + +function getType( + typeName: TypeName, + context: ContractDefinition, + deref: ASTDereferencer, + location: StorageLocation | null, +): string { + const { typeString, typeIdentifier } = typeName.typeDescriptions; + if (typeof typeString !== 'string' || typeof typeIdentifier !== 'string') { + throw new Error('Missing type information'); + } + + let type = + typeString.replace(/^(struct|enum|contract) /, '') + + (typeIdentifier.endsWith('_ptr') && location ? ` ${location}` : ''); + + const typeScopeMatch = type.match(/^([a-zA-Z0-9_$]+)\./); + if (context.contractKind !== 'library' && typeScopeMatch) { + const [, typeScope] = typeScopeMatch; + + const isScopeImplicit = context.linearizedBaseContracts.some( + c => deref('ContractDefinition', c).name === typeScope, + ); + + if (isScopeImplicit) { + type = type.replace(`${typeScope}.`, ''); + } + } + + return type; +} + +function getVarAbiType( + varDecl: VariableDeclaration, + context: ContractDefinition, + deref: ASTDereferencer, + location: StorageLocation | null = varDecl.storageLocation, +): string { + if (!varDecl.typeName) { + throw new Error('Missing type information'); + } + return getAbiType(varDecl.typeName, context, deref, location); +} + +function getAbiType( + typeName: TypeName, + context: ContractDefinition, + deref: ASTDereferencer, + location: StorageLocation | null, +): string { + switch (typeName.nodeType) { + case 'ElementaryTypeName': + case 'ArrayTypeName': + const { typeString } = typeName.typeDescriptions; + assert(typeString != undefined); + return typeString; + + case 'UserDefinedTypeName': + const typeDef = derefUserDefinedTypeName(deref, typeName); + switch (typeDef.nodeType) { + case 'UserDefinedValueTypeDefinition': + const { typeString } = typeDef.underlyingType.typeDescriptions; + assert(typeString != undefined); + return typeString; + + case 'EnumDefinition': + assert(typeDef.members.length < 256); + return 'uint8'; + + case 'ContractDefinition': + return 'address'; + + case 'StructDefinition': + if (location === 'storage') { + throw new Error('Unexpected error'); // is treated separately in getFunctionArguments + } else { + return '(' + typeDef.members.map(v => getVarAbiType(v, context, deref, location)).join(',') + ')'; + } + } + + default: + throw new Error('Unknown ABI type'); + } +} + +function getVariables( + contract: ContractDefinition, + deref: ASTDereferencer, + subset?: Visibility[], +): VariableDeclaration[] { + const parents = contract.linearizedBaseContracts.map(deref('ContractDefinition')); + + const res = []; + + for (const parent of parents) { + for (const v of findAll('VariableDeclaration', parent)) { + if (v.stateVariable && (!subset || subset.includes(v.visibility))) { + res.push(v); + } + } + } + + return res; +} + +function getVarGetterArgs(v: VariableDeclaration, context: ContractDefinition, deref: ASTDereferencer): Argument[] { + if (!v.typeName) { + throw new Error('missing typenName'); + } + const types = []; + for (let t = v.typeName; t.nodeType === 'Mapping'; t = t.valueType) { + types.push({ + name: `arg${types.length}`, + type: getType(t.keyType, context, deref, 'memory'), + abiType: getAbiType(t.keyType, context, deref, 'memory'), + }); + } + return types; +} + +function getVarGetterReturnType(v: VariableDeclaration, context: ContractDefinition, deref: ASTDereferencer): string { + if (!v.typeName) { + throw new Error('missing typenName'); + } + let t = v.typeName; + while (t.nodeType === 'Mapping') { + t = t.valueType; + } + return getType(t, context, deref, 'memory'); +} + +function getFunctions( + contract: ContractDefinition, + deref: ASTDereferencer, + subset?: Visibility[], +): FunctionDefinition[] { + const parents = contract.linearizedBaseContracts.map(deref('ContractDefinition')); + + const overriden = new Set(); + const res = []; + + for (const parent of parents) { + for (const fn of findAll('FunctionDefinition', parent)) { + if (!overriden.has(fn.id) && (!subset || subset.includes(fn.visibility))) { + res.push(fn); + } + for (const b of fn.baseFunctions ?? []) { + overriden.add(b); + } + } + } + + return res; +} + +function* getNeededImports(ast: SourceUnit, deref: ASTDereferencer): Iterable { + const needed = new Set( + [ast].concat( + [...findAll('ContractDefinition', ast)].flatMap(c => + c.linearizedBaseContracts.map(p => { + const { sourceUnit } = deref.withSourceUnit('ContractDefinition', p); + return sourceUnit; + }), + ), + ), + ); + + for (const n of needed) { + yield n; + + for (const imp of findAll('ImportDirective', n)) { + if (imp.symbolAliases.length > 0) { + needed.add(deref('SourceUnit', imp.sourceUnit)); + } + } + } +} + +function mustGet(map: Map, key: K): V { + const value = map.get(key); + if (value === undefined) { + throw new Error('Key not found'); + } + return value; +} + +function derefUserDefinedTypeName(deref: ASTDereferencer, typeName: UserDefinedTypeName) { + return deref( + ['StructDefinition', 'EnumDefinition', 'ContractDefinition', 'UserDefinedValueTypeDefinition'], + typeName.referencedDeclaration, + ); +} diff --git a/hardhat/hardhat-exposed/core/types.ts b/hardhat/hardhat-exposed/core/types.ts new file mode 100644 index 00000000000..d5732bc8b25 --- /dev/null +++ b/hardhat/hardhat-exposed/core/types.ts @@ -0,0 +1,14 @@ +export interface ExposedUserConfig { + prefix?: string; + exclude?: string[]; + include?: string[]; + outDir?: string; + initializers?: boolean; + imports?: boolean; +} + +export interface ExposedConfig extends ExposedUserConfig { + exclude: string[]; + include: string[]; + outDir: string; +} diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts new file mode 100644 index 00000000000..e5d813d8ad2 --- /dev/null +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -0,0 +1,22 @@ +import type { HardhatConfig, HardhatUserConfig, ConfigurationVariableResolver } from 'hardhat/types/config'; +import type { ConfigHooks } from 'hardhat/types/hooks'; + +export default async (): Promise> => ({ + resolveUserConfig: ( + userConfig: HardhatUserConfig, + resolveConfigurationVariable: ConfigurationVariableResolver, + next: ( + nextUserConfig: HardhatUserConfig, + nextResolveConfigurationVariable: ConfigurationVariableResolver, + ) => Promise, + ): Promise => + next(userConfig, resolveConfigurationVariable).then((resolvedConfig: HardhatConfig) => { + resolvedConfig.exposed = { + ...userConfig.exposed, + exclude: userConfig.exposed?.exclude ?? [], + include: userConfig.exposed?.include ?? ['**/*'], + outDir: userConfig.exposed?.outDir ?? 'contracts-exposed', + }; + return resolvedConfig; + }), +}); diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts new file mode 100644 index 00000000000..3678d0278fb --- /dev/null +++ b/hardhat/hardhat-exposed/hook-handlers/solidity.ts @@ -0,0 +1,149 @@ +import path from 'path'; +import micromatch from 'micromatch'; + +import type { SolcConfig } from 'hardhat/types/config'; +import type { SolidityHooks, HookContext } from 'hardhat/types/hooks'; +import type { + RunCompilationJobResult, + CompilerInput, + CompilerOutput, + CompilerOutputError, +} from 'hardhat/types/solidity'; + +import type { Artifact } from 'hardhat/types/artifacts'; +import { remove, writeJsonFile, writeUtf8File } from '@nomicfoundation/hardhat-utils/fs'; +import { getPrefixedHexString } from '@nomicfoundation/hardhat-utils/hex'; + +import type {} from '../type-extensions'; + +import { getExposed, writeExposed } from '../core'; + +export default async (): Promise> => ({ + async invokeSolc( + context: HookContext, + compiler: RunCompilationJobResult['compiler'], + solcInput: CompilerInput, + solcConfig: SolcConfig, + next: ( + nextContext: HookContext, + nextCompiler: RunCompilationJobResult['compiler'], + nextSolcInput: CompilerInput, + nextSolcConfig: SolcConfig, + ) => Promise, + ): Promise { + // Precompilation + const output: CompilerOutput = await next(context, compiler, solcInput, { + ...solcConfig, + settings: { + ...solcConfig.settings, + optimizer: { enabled: false }, + outputSelection: { '*': { '': ['ast'] } }, + }, + }); + + // If precompilation succeeded, generate exposed files + if (!output.errors?.some((e: CompilerOutputError) => e.severity === 'error')) { + // Determine which files to include + const include = (sourceName: string): boolean => + sourceName.startsWith('project/') && + context.config.exposed.include.some((p: string) => + micromatch.isMatch(path.relative('project/', sourceName), p), + ) && + !context.config.exposed.exclude.some((p: string) => + micromatch.isMatch(path.relative('project/', sourceName), p), + ); + + // Generate exposed files + const exposed = getExposed(output, include, context.config); + + // Write exposed files to disk and add them to the compilation input + await writeExposed(exposed); + for (const [fsPath, content] of exposed) { + const relativePath = path.join('project', path.relative(context.config.paths.root, fsPath)); + solcInput.sources[relativePath] = { content }; + } + + // Full compilation + const fullOutput = await next(context, compiler, solcInput, solcConfig); + + // ############################################################################################################# + // # Write artifacts for exposed files - Begin # + // ############################################################################################################# + // NOTE: this should ideally be handled by hardhat. It currently does NOT work, because the artifacts produced are erased automatically for an unknown reason. + const artifactsDirectory = await context.solidity.getArtifactsDirectory('contracts'); + for (const fsPath of exposed.keys()) { + const relativePath = path.relative(context.config.paths.root, fsPath); + const projectPath = path.join('project', relativePath); + const fileFolder = path.join(artifactsDirectory, relativePath); + + await remove(fileFolder); + + const artifacts: Artifact[] = []; + for (const [contractName, contract] of Object.entries(fullOutput.contracts?.[projectPath] ?? {})) { + const evmBytecode = contract.evm?.bytecode; + const bytecode: string = evmBytecode?.object !== undefined ? getPrefixedHexString(evmBytecode.object) : ''; + const evmDeployedBytecode = contract.evm?.deployedBytecode; + const deployedBytecode: string = + evmDeployedBytecode?.object !== undefined ? getPrefixedHexString(evmDeployedBytecode.object) : ''; + const linkReferences = evmBytecode?.linkReferences ?? {}; + const deployedLinkReferences = evmDeployedBytecode?.linkReferences ?? {}; + const immutableReferences = evmDeployedBytecode?.immutableReferences ?? {}; + + const artifact: Artifact = { + _format: 'hh3-artifact-1', + contractName, + sourceName: relativePath, + abi: contract.abi, + bytecode, + deployedBytecode, + linkReferences, + deployedLinkReferences, + immutableReferences, + inputSourceName: projectPath, + buildInfoId: '', + }; + artifacts.push(artifact); + + await writeJsonFile(path.join(fileFolder, `${contractName}.json`), artifact); + } + + if (artifacts.length === 0) continue; + + const artifactsDeclarationFile = `\ +// This file was autogenerated by Hardhat, do not edit it. +// prettier-ignore +// tslint:disable +// eslint-disable +// biome-ignore format: see above + +${artifacts + .map( + artifact => `export interface ${artifact.contractName}$Type { + ${Object.entries(artifact) + .map(([name, value]) => `readonly ${name}: ${JSON.stringify(value)};`) + .join('\n ')} +};`, + ) + .join('\n\n')} + +import "hardhat/types/artifacts"; +declare module "hardhat/types/artifacts" { + interface ArtifactMap { + ${artifacts.map(artifact => `["${artifact.contractName}"]: ${artifact.contractName}$Type`).join('\n ')}; + ${artifacts.map(artifact => `["${artifact.sourceName}:${artifact.contractName}"]: ${artifact.contractName}$Type`).join('\n ')}; + } +}`; + + await writeUtf8File(path.join(fileFolder, 'artifacts.d.ts'), artifactsDeclarationFile); + } + // ############################################################################################################# + // # Write artifacts for exposed files - End # + // ############################################################################################################# + + return fullOutput; + } else { + // Full compilation + return await next(context, compiler, solcInput, solcConfig); + } + }, +}); diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts new file mode 100644 index 00000000000..4cc8fa5232a --- /dev/null +++ b/hardhat/hardhat-exposed/plugin.ts @@ -0,0 +1,23 @@ +import { overrideTask } from 'hardhat/config'; +import type { HardhatPlugin } from 'hardhat/types/plugins'; + +import type {} from './type-extensions'; + +const hardhatExposedPlugin: HardhatPlugin = { + id: 'hardhat-exposed', + hookHandlers: { + config: () => import('./hook-handlers/config.js'), + solidity: () => import('./hook-handlers/solidity.js'), + }, + tasks: [ + overrideTask('clean') + .setAction(() => import('./tasks/clean.js')) + .build(), + overrideTask('build') + .setAction(() => import('./tasks/build.js')) + .build(), + ], + npmPackage: 'hardhat-exposed', +}; + +export default hardhatExposedPlugin; diff --git a/hardhat/hardhat-exposed/tasks/build.ts b/hardhat/hardhat-exposed/tasks/build.ts new file mode 100644 index 00000000000..35c059bc9fd --- /dev/null +++ b/hardhat/hardhat-exposed/tasks/build.ts @@ -0,0 +1,26 @@ +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type {} from '../type-extensions'; + +import { cleanExposed } from '../core'; + +// See: hardhat/src/internal/builtin-plugins/solidity/tasks/build.ts +type BuildActionArguments = any; +// interface BuildActionArguments { +// force: boolean; +// files: string[]; +// quiet: boolean; +// defaultBuildProfile: string | undefined; +// noTests: boolean; +// noContracts: boolean; +// } + +export default async function ( + taskArguments: BuildActionArguments, + hre: HardhatRuntimeEnvironment, + superCall: (taskArguments: BuildActionArguments) => Promise, +) { + if (taskArguments.force) { + await cleanExposed(hre.config); + } + return superCall(taskArguments); +} diff --git a/hardhat/hardhat-exposed/tasks/clean.ts b/hardhat/hardhat-exposed/tasks/clean.ts new file mode 100644 index 00000000000..046373736f3 --- /dev/null +++ b/hardhat/hardhat-exposed/tasks/clean.ts @@ -0,0 +1,21 @@ +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type {} from '../type-extensions'; + +import { cleanExposed } from '../core'; + +// See: hardhat/src/internal/builtin-plugins/clean/task-action.ts +type TaskArguments = any; +// interface TaskArguments { +// global: boolean; +// } + +export default async function ( + taskArguments: TaskArguments, + hre: HardhatRuntimeEnvironment, + superCall: (taskArguments: TaskArguments) => Promise, +) { + if (!taskArguments.global) { + await cleanExposed(hre.config); + } + return superCall(taskArguments); +} diff --git a/hardhat/hardhat-exposed/type-extensions.ts b/hardhat/hardhat-exposed/type-extensions.ts new file mode 100644 index 00000000000..f614cd3c7e6 --- /dev/null +++ b/hardhat/hardhat-exposed/type-extensions.ts @@ -0,0 +1,13 @@ +import 'hardhat/types/config'; + +import { ExposedUserConfig, ExposedConfig } from './core/types'; + +declare module 'hardhat/types/config' { + export interface HardhatUserConfig { + exposed?: ExposedUserConfig; + } + + export interface HardhatConfig { + exposed: ExposedConfig; + } +} diff --git a/hardhat/hook-handlers/hre.ts b/hardhat/hook-handlers/hre.ts new file mode 100644 index 00000000000..ad91e1e16dd --- /dev/null +++ b/hardhat/hook-handlers/hre.ts @@ -0,0 +1,33 @@ +import { HardhatError } from '@nomicfoundation/hardhat-errors'; +import type { HardhatRuntimeEnvironmentHooks, HookContext } from 'hardhat/types/hooks'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { ArtifactManager } from 'hardhat/types/artifacts'; + +const suffixes = ['UpgradeableWithInit', 'Upgradeable', '']; + +function isExpectedError(e: unknown, suffix: string): boolean { + return HardhatError.isHardhatError(e) && e.number === 1000 && suffix !== ''; +} + +const overrideReadArtifact = + (runSuper: ArtifactManager['readArtifact']) => + async (contractNameOrFullyQualifiedName: ContractNameT) => { + for (const suffix of suffixes) { + try { + return await runSuper((contractNameOrFullyQualifiedName + suffix) as ContractNameT); + } catch (e) { + if (isExpectedError(e, suffix)) { + continue; + } else { + throw e; + } + } + } + throw new Error('Unreachable'); + }; + +export default async (): Promise> => ({ + created: async (context: HookContext, hre: HardhatRuntimeEnvironment): Promise => { + hre.artifacts.readArtifact = overrideReadArtifact(hre.artifacts.readArtifact.bind(hre.artifacts)); + }, +}); diff --git a/hardhat/ignore-unreachable-warnings.js b/hardhat/ignore-unreachable-warnings.js deleted file mode 100644 index eeacf0a1a5e..00000000000 --- a/hardhat/ignore-unreachable-warnings.js +++ /dev/null @@ -1,45 +0,0 @@ -// Warnings about unreachable code are emitted with a source location that corresponds to the unreachable code. -// We have some testing contracts that purposely cause unreachable code, but said code is in the library contracts, and -// with hardhat-ignore-warnings we are not able to selectively ignore them without potentially ignoring relevant -// warnings that we don't want to miss. -// Thus, we need to handle these warnings separately. We force Hardhat to compile them in a separate compilation job and -// then ignore the warnings about unreachable code coming from that compilation job. - -const { task } = require('hardhat/config'); -const { - TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE, - TASK_COMPILE_SOLIDITY_COMPILE, -} = require('hardhat/builtin-tasks/task-names'); - -const marker = Symbol('unreachable'); -const markedCache = new WeakMap(); - -task(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE, async (params, _, runSuper) => { - const job = await runSuper(params); - // If the file is in the unreachable directory, we make a copy of the config and mark it, which will cause it to get - // compiled separately (along with the other marked files). - if (params.file.sourceName.startsWith('contracts/mocks/') && /\bunreachable\b/.test(params.file.sourceName)) { - const originalConfig = job.solidityConfig; - let markedConfig = markedCache.get(originalConfig); - if (markedConfig === undefined) { - markedConfig = { ...originalConfig, [marker]: true }; - markedCache.set(originalConfig, markedConfig); - } - job.solidityConfig = markedConfig; - } - return job; -}); - -const W_UNREACHABLE_CODE = '5740'; - -task(TASK_COMPILE_SOLIDITY_COMPILE, async (params, _, runSuper) => { - const marked = params.compilationJob.solidityConfig[marker]; - const result = await runSuper(params); - if (marked) { - result.output = { - ...result.output, - errors: result.output.errors?.filter(e => e.severity !== 'warning' || e.errorCode !== W_UNREACHABLE_CODE), - }; - } - return result; -}); diff --git a/hardhat/remappings.js b/hardhat/remappings.js deleted file mode 100644 index cd9984d440b..00000000000 --- a/hardhat/remappings.js +++ /dev/null @@ -1,18 +0,0 @@ -const fs = require('fs'); -const { task } = require('hardhat/config'); -const { TASK_COMPILE_GET_REMAPPINGS } = require('hardhat/builtin-tasks/task-names'); - -task(TASK_COMPILE_GET_REMAPPINGS).setAction((taskArgs, env, runSuper) => - runSuper().then(remappings => - Object.assign( - remappings, - Object.fromEntries( - fs - .readFileSync('remappings.txt', 'utf-8') - .split('\n') - .filter(Boolean) - .map(line => line.trim().split('=')), - ), - ), - ), -); diff --git a/hardhat/skip-foundry-tests.js b/hardhat/skip-foundry-tests.js deleted file mode 100644 index 965ba37c39e..00000000000 --- a/hardhat/skip-foundry-tests.js +++ /dev/null @@ -1,6 +0,0 @@ -const { subtask } = require('hardhat/config'); -const { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } = require('hardhat/builtin-tasks/task-names'); - -subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(async (_, __, runSuper) => - (await runSuper()).filter(path => !path.endsWith('.t.sol')), -); diff --git a/lib/forge-std b/lib/forge-std index 3b20d60d14b..1801b0541f4 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 +Subproject commit 1801b0541f4fda118a10798fd3486bb7051c5dd6 diff --git a/package-lock.json b/package-lock.json index 7b675b6666b..23fe13467ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,25 +15,26 @@ "@changesets/read": "^0.6.0", "@eslint/compat": "^1.2.1", "@ethereumjs/mpt": "^10.1.0", - "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", - "@nomicfoundation/hardhat-ethers": "^3.0.9", - "@nomicfoundation/hardhat-network-helpers": "^1.0.13", + "@nomicfoundation/hardhat-ethers": "^4.0.4", + "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.2", + "@nomicfoundation/hardhat-mocha": "^3.0.9", + "@nomicfoundation/hardhat-network-helpers": "^3.0.3", "@openzeppelin/docs-utils": "^0.1.6", "@openzeppelin/merkle-tree": "^1.0.7", "@openzeppelin/upgrade-safe-transpiler": "^0.4.1", "@openzeppelin/upgrades-core": "^1.20.6", - "chai": "^4.2.0", + "@types/node": "^25.0.9", + "@types/yargs": "^17.0.35", + "chai": "^5.2.2", "eslint": "^9.0.0", "eslint-config-prettier": "^10.0.0", "ethers": "^6.16.0", "glob": "^13.0.0", "globals": "^16.0.0", "graphlib": "^2.1.8", - "hardhat": "^2.28.0", - "hardhat-exposed": "^0.3.15", - "hardhat-gas-reporter": "^2.1.0", - "hardhat-ignore-warnings": "^0.2.11", - "hardhat-predeploy": "^0.4.1", + "hardhat": "^3.1.5", + "hardhat-ignore-warnings": "^0.3.0-3", + "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", "interoperable-addresses": "^0.1.3", "lint-staged": "^16.0.0", @@ -46,9 +47,7 @@ "semver": "^7.3.5", "solhint": "^6.0.1", "solhint-plugin-openzeppelin": "file:scripts/solhint-custom", - "solidity-ast": "^0.4.50", - "solidity-coverage": "^0.8.14", - "solidity-docgen": "^0.6.0-beta.29", + "solidity-ast": "^0.4.61", "undici": "^7.4.0", "yargs": "^18.0.0" } @@ -416,15 +415,446 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.1.90" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -779,119 +1209,26 @@ "rlp": "bin/rlp" }, "engines": { - "node": ">=14" - } - }, - "node_modules/@ethereumjs/tx": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", - "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@ethereumjs/common": "^3.2.0", - "@ethereumjs/rlp": "^4.0.1", - "@ethereumjs/util": "^8.1.0", - "ethereum-cryptography": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@ethereumjs/tx/node_modules/@noble/curves": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", - "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/tx/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/tx/node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/tx/node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/tx/node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/tx/node_modules/ethereum-cryptography": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", - "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "1.4.2", - "@noble/hashes": "1.4.0", - "@scure/bip32": "1.4.0", - "@scure/bip39": "1.3.0" - } - }, - "node_modules/@ethereumjs/util": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", - "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", + "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", "dev": true, "license": "MPL-2.0", "dependencies": { + "@ethereumjs/common": "^3.2.0", "@ethereumjs/rlp": "^4.0.1", - "ethereum-cryptography": "^2.0.0", - "micro-ftch": "^0.3.1" + "@ethereumjs/util": "^8.1.0", + "ethereum-cryptography": "^2.0.0" }, "engines": { "node": ">=14" } }, - "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "node_modules/@ethereumjs/tx/node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", @@ -904,7 +1241,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "node_modules/@ethereumjs/tx/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -917,7 +1254,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethereumjs/util/node_modules/@scure/base": { + "node_modules/@ethereumjs/tx/node_modules/@scure/base": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", @@ -927,7 +1264,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "node_modules/@ethereumjs/tx/node_modules/@scure/bip32": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", @@ -942,7 +1279,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "node_modules/@ethereumjs/tx/node_modules/@scure/bip39": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", @@ -956,7 +1293,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "node_modules/@ethereumjs/tx/node_modules/ethereum-cryptography": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", @@ -969,446 +1306,97 @@ "@scure/bip39": "1.3.0" } }, - "node_modules/@ethersproject/abi": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", - "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/@ethersproject/abstract-provider": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", - "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/@ethersproject/abstract-signer": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", - "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/@ethersproject/address": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", - "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/@ethersproject/base64": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", - "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0" - } - }, - "node_modules/@ethersproject/bignumber": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", - "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "bn.js": "^5.2.1" - } - }, - "node_modules/@ethersproject/bytes": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", - "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/constants": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", - "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0" - } - }, - "node_modules/@ethersproject/hash": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", - "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "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" - } - }, - "node_modules/@ethersproject/keccak256": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", - "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "js-sha3": "0.8.0" - } - }, - "node_modules/@ethersproject/logger": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", - "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT" - }, - "node_modules/@ethersproject/networks": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", - "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/properties": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", - "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/rlp": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", - "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==", + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", + "license": "MPL-2.0", "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0" + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" } }, - "node_modules/@ethersproject/signing-key": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", - "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], "license": "MIT", "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "bn.js": "^5.2.1", - "elliptic": "6.6.1", - "hash.js": "1.1.7" + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethersproject/strings": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", - "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0" + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethersproject/transactions": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", - "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", + "node_modules/@ethereumjs/util/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], "license": "MIT", - "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" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethersproject/units": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.8.0.tgz", - "integrity": "sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==", + "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], "license": "MIT", "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0" + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethersproject/web": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", - "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", + "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], "license": "MIT", "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" + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=14" + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } }, "node_modules/@frangio/servbot": { @@ -1637,6 +1625,13 @@ "fs-extra": "^8.1.0" } }, + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@manypkg/find-root/node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -1763,204 +1758,441 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.22.tgz", + "integrity": "sha512-JigYWf2stjpDxSndBsxRoobQHK8kz4SAVaHtTIKQLIHbsBwymE8i120Ejne6Jk+Ndc5CsNINXB8/bK6vLPe9jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.22", + "@nomicfoundation/edr-darwin-x64": "0.12.0-next.22", + "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.22", + "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.22", + "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.22", + "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.22", + "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.22" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.22.tgz", + "integrity": "sha512-TpEBSKyMZJEPvYwBPYclC2b+qobKjn1YhVa7aJ1R7RMPy5dJ/PqsrUK5UuUFFybBqoIorru5NTcsyCMWP5T/Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.22.tgz", + "integrity": "sha512-aK/+m8xUkR4u+czTVGU06nSFVH43AY6XCBoR2YjO8SglAAjCSTWK3WAfVb6FcsriMmKv4PrvoyHLMbMP+fXcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.22.tgz", + "integrity": "sha512-W5vXMleG14hVzRYGPEwlHLJ6iiQE8Qh63Uj538nAz4YUI6wWSgUOZE7K2Gt1EdujZGnrt7kfDslgJ96n4nKQZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.22.tgz", + "integrity": "sha512-VDp7EB3iY8MH/fFVcgEzLDGYmtS6j2honNc0RNUCFECKPrdsngGrTG8p+YFxyVjq2m5GEsdyKo4e+BKhaUNPdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.22.tgz", + "integrity": "sha512-XL6oA3ymRSQYyvg6hF1KIax6V/9vlWr5gJ8GPHVVODk1a/YfuEEY1osN5Zmo6aztUkSGKwSuac/3Ax7rfDDiSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.22.tgz", + "integrity": "sha512-hmkRIXxWa9P0PwfXOAO6WUw11GyV5gpxcMunqWBTkwZ4QW/hi/CkXmlLo6VHd6ceCwpUNLhCGndBtrOPrNRi4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.12.0-next.22", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.22.tgz", + "integrity": "sha512-X7f+7KUMm00trsXAHCHJa+x1fc3QAbk2sBctyOgpET+GLrfCXbxqrccKi7op8f0zTweAVGg1Hsc8SjjC7kwFLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/hardhat-errors": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.6.tgz", + "integrity": "sha512-3x+OVdZv7Rgy3z6os9pB6kiHLxs6q0PCXHRu+WLZflr44PG9zW+7V9o+ehrUqmmivlHcIFr3Qh4M2wZVuoCYww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/hardhat-utils": "^3.0.1" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-4.0.4.tgz", + "integrity": "sha512-UTw3iM7AMZ1kZlzgJbtAEfWWDYjcnT0EZkRUZd1wIVtMOXIE4nc6Ya4veodAt/KpBhG+6W06g50W+Z/0wTm62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/hardhat-errors": "^3.0.2", + "@nomicfoundation/hardhat-utils": "^3.0.5", + "debug": "^4.3.2", + "ethereum-cryptography": "^2.2.1", + "ethers": "^6.14.0" + }, + "peerDependencies": { + "hardhat": "^3.0.7" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers-chai-matchers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers-chai-matchers/-/hardhat-ethers-chai-matchers-3.0.2.tgz", + "integrity": "sha512-nkg+z+fq5PXcRxS/zadyosAA+oPp3sdWrKpuOcASDf0RjqsN2LsNymML0VNNkZF8TF+hYa36fbV+QOas2Fm2BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/hardhat-errors": "^3.0.5", + "@nomicfoundation/hardhat-utils": "^3.0.5", + "@types/chai-as-promised": "^8.0.1", + "chai-as-promised": "^8.0.0", + "deep-eql": "^5.0.1" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^4.0.0", + "chai": "^5.1.2", + "ethers": "^6.14.0", + "hardhat": "^3.0.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers/node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "dev": true, "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, "funding": { "url": "https://paulmillr.com/funding/" } }, - "node_modules/@noble/secp256k1": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", - "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "node_modules/@nomicfoundation/hardhat-ethers/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@nomicfoundation/hardhat-mocha": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-mocha/-/hardhat-mocha-3.0.9.tgz", + "integrity": "sha512-9hsl1TcRMudN/gUPsRjx0iGLEkl8IU9BBQ5wT5bf8N4RTSHbVwqVL+mADzpt+Dmd5nkdItynhrAJnXjwTvy5DQ==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@nomicfoundation/hardhat-errors": "^3.0.3", + "@nomicfoundation/hardhat-utils": "^3.0.5", + "@nomicfoundation/hardhat-zod-utils": "^3.0.0", + "chalk": "^5.3.0", + "tsx": "^4.19.3", + "zod": "^3.23.8" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "hardhat": "^3.0.12", + "mocha": "^11.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@nomicfoundation/hardhat-mocha/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 8" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@nomicfoundation/hardhat-network-helpers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-3.0.3.tgz", + "integrity": "sha512-FqXD8CPFNdluEhELqNV/Q0grOQtlwRWr28LW+/NTas3rrDAXpNOIPCCq3RIXJIqsdbNPQsG2FpnfKj9myqIsKQ==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@nomicfoundation/hardhat-errors": "^3.0.5", + "@nomicfoundation/hardhat-utils": "^3.0.5" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "hardhat": "^3.0.0" } }, - "node_modules/@nomicfoundation/edr": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.17.tgz", - "integrity": "sha512-Y8Kwqd5JpBmI/Kst6NJ/bZ81FeJea9J6WEwoSRTZnEvwfqW9dk9PI8zJs2UJpOACL1fXEPvN+doETbxT9EhwXA==", + "node_modules/@nomicfoundation/hardhat-utils": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-3.0.6.tgz", + "integrity": "sha512-AD/LPNdjXNFRrZcaAAewgJpdnHpPppZxo5p+x6wGMm5Hz4B3+oLf/LUzVn8qb4DDy9RE2c24l2F8vmL/w6ZuXg==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.17", - "@nomicfoundation/edr-darwin-x64": "0.12.0-next.17", - "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.17", - "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.17", - "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.17", - "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.17", - "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.17" - }, - "engines": { - "node": ">= 20" + "@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" } }, - "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.17.tgz", - "integrity": "sha512-gI9/9ysLeAid0+VSTBeutxOJ0/Rrh00niGkGL9+4lR577igDY+v55XGN0oBMST49ILS0f12J6ZY90LG8sxPXmQ==", + "node_modules/@nomicfoundation/hardhat-utils/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 20" + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.17.tgz", - "integrity": "sha512-zSZtwf584RkIyb8awELDt7ctskogH0p4pmqOC4vhykc8ODOv2XLuG1IgeE4WgYhWGZOufbCtgLfpJQrWqN6mmw==", + "node_modules/@nomicfoundation/hardhat-utils/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 20" + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.17.tgz", - "integrity": "sha512-WjdfgV6B7gT5Q0NXtSIWyeK8gzaJX5HK6/jclYVHarWuEtS1LFgePYgMjK8rmm7IRTkM9RsE/PCuQEP1nrSsuA==", + "node_modules/@nomicfoundation/hardhat-utils/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 20" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.17.tgz", - "integrity": "sha512-26rObKhhCDb9JkZbToyr7JVZo4tSVAFvzoJSJVmvpOl0LOHrfFsgVQu2n/8cNkwMAqulPubKL2E0jdnmEoZjWA==", + "node_modules/@nomicfoundation/hardhat-utils/node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 20" + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.17.tgz", - "integrity": "sha512-dPkHScIf/CU6h6k3k4HNUnQyQcVSLKanviHCAcs5HkviiJPxvVtOMMvtNBxoIvKZRxGFxf2eutcqQW4ZV1wRQQ==", + "node_modules/@nomicfoundation/hardhat-utils/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 20" + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.17.tgz", - "integrity": "sha512-5Ixe/bpyWZxC3AjIb8EomAOK44ajemBVx/lZRHZiWSBlwQpbSWriYAtKjKcReQQPwuYVjnFpAD2AtuCvseIjHw==", + "node_modules/@nomicfoundation/hardhat-utils/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 20" + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } }, - "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.12.0-next.17", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.17.tgz", - "integrity": "sha512-29YlvdgofSdXG1mUzIuH4kMXu1lmVc1hvYWUGWEH59L+LaakdhfJ/Wu5izeclKkrTh729Amtk/Hk1m29kFOO8A==", + "node_modules/@nomicfoundation/hardhat-utils/node_modules/undici": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", "dev": true, "license": "MIT", "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/hardhat-chai-matchers": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.8.tgz", - "integrity": "sha512-Z5PiCXH4xhNLASROlSUOADfhfpfhYO6D7Hn9xp8PddmHey0jq704cr6kfU8TRrQ4PUZbpfsZadPj+pCfZdjPIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai-as-promised": "^7.1.3", - "chai-as-promised": "^7.1.1", - "deep-eql": "^4.0.1", - "ordinal": "^1.0.3" - }, - "peerDependencies": { - "@nomicfoundation/hardhat-ethers": "^3.0.0", - "chai": "^4.2.0", - "ethers": "^6.1.0", - "hardhat": "^2.9.4" + "node": ">=18.17" } }, - "node_modules/@nomicfoundation/hardhat-ethers": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.9.tgz", - "integrity": "sha512-xBJdRUiCwKpr0OYrOzPwAyNGtsVzoBx32HFPJVv6S+sFA9TmBIBDaqNlFPmBH58ZjgNnGhEr/4oBZvGr4q4TjQ==", + "node_modules/@nomicfoundation/hardhat-vendored": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.0.tgz", + "integrity": "sha512-bzIOdG4iAuYSs9JSnaVOtH7qUKJ6W5+OtOiL8MlyFuLKYN2hjIisGO4pY5zR4N7xi/3RjfcnjVNz8tU0DPg2Cw==", "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "lodash.isequal": "^4.5.0" - }, - "peerDependencies": { - "ethers": "^6.14.0", - "hardhat": "^2.0.0" - } + "license": "MIT" }, - "node_modules/@nomicfoundation/hardhat-network-helpers": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.13.tgz", - "integrity": "sha512-ptg0+SH8jnfoYHlR3dKWTNTB43HZSxkuy3OeDk+AufEKQvQ7Ru9LQEbJtLuDTQ4HGRBkhl4oJ9RABsEIbn7Taw==", + "node_modules/@nomicfoundation/hardhat-zod-utils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.1.tgz", + "integrity": "sha512-I6/pyYiS9p2lLkzQuedr1ScMocH+ew8l233xTi+LP92gjEiviJDxselpkzgU01MUM0t6BPpfP8yMO958LDEJVg==", "dev": true, "license": "MIT", "dependencies": { - "ethereumjs-util": "^7.1.4" + "@nomicfoundation/hardhat-errors": "^3.0.0", + "@nomicfoundation/hardhat-utils": "^3.0.2" }, "peerDependencies": { - "hardhat": "^2.9.5" + "zod": "^3.23.8" } }, "node_modules/@nomicfoundation/slang": { @@ -2210,217 +2442,76 @@ "node": ">=12.22.0" } }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true, - "license": "ISC" - }, - "node_modules/@pnpm/npm-conf": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", - "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@scure/base": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", - "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", - "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.9.0", - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", - "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@sentry/core": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", - "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/hub": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", - "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/minimal": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", - "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/node": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", - "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/core": "5.30.0", - "@sentry/hub": "5.30.0", - "@sentry/tracing": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/node/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true, - "license": "0BSD" + "license": "ISC" }, - "node_modules/@sentry/tracing": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", - "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", "dev": true, "license": "MIT", "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/@sentry/tracing/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", "dev": true, - "license": "0BSD" + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "node_modules/@sentry/types": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", - "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=6" + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@sentry/utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", - "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@sentry/utils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "node_modules/@sentry/core": { + "version": "9.47.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.47.1.tgz", + "integrity": "sha512-KX62+qIt4xgy8eHKHiikfhz2p5fOciXd0Cl+dNzhgPFq8klq4MGMNaf148GB3M/vBqP4nw/eFvRMAayFCgdRQw==", "dev": true, - "license": "0BSD" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/@sindresorhus/is": { "version": "5.6.0", @@ -2435,13 +2526,23 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@solidity-parser/parser": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.19.0.tgz", - "integrity": "sha512-RV16k/qIxW/wWc+mLzV3ARyKUaMUTBy9tOLMzFhtNSKYeTAanQ3a5MudJKf/8arIFnA2L27SNjarQKmFg0w/jA==", + "node_modules/@streamparser/json": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.22.tgz", + "integrity": "sha512-b6gTSBjJ8G8SuO3Gbbj+zXbVx8NSs1EbpbMKpzGLWMdkR+98McH9bEjSz3+0mPJf68c5nxa3CrJHp5EQNXM6zQ==", "dev": true, "license": "MIT" }, + "node_modules/@streamparser/json-node": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@streamparser/json-node/-/json-node-0.0.22.tgz", + "integrity": "sha512-sJT2ptNRwqB1lIsQrQlCoWk5rF4tif9wDh+7yluAGijJamAhrHGYpFB/Zg3hJeceoZypi74ftXk8DHzwYpbZSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@streamparser/json": "^0.0.22" + } + }, "node_modules/@szmarczak/http-timer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", @@ -2466,19 +2567,20 @@ } }, "node_modules/@types/chai": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.1.tgz", - "integrity": "sha512-iu1JLYmGmITRzUgNiLMZD3WCoFzpYtueuyAgHTXqgwSRAMIlFTnZqG6/xenkpUGRJEzSfklUTI4GNSzks/dc0w==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { - "@types/deep-eql": "*" + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" } }, "node_modules/@types/chai-as-promised": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", - "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-8.0.2.tgz", + "integrity": "sha512-meQ1wDr1K5KRCSvG2lX7n7/5wf70BeptTKst0axGvnN6zqaVpRqegoIbugiAPSqOW9K9aL8gDVrm7a2LXOtn2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2509,17 +2611,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -2534,13 +2625,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -2549,9 +2633,19 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "version": "25.0.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.9.tgz", + "integrity": "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/node/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, @@ -2575,41 +2669,30 @@ "@types/node": "*" } }, - "node_modules/abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/abitype": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", - "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/wevm" - }, - "peerDependencies": { - "typescript": ">=5.0.4", - "zod": "^3 >=3.22.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "zod": { - "optional": true - } + "dependencies": { + "@types/yargs-parser": "*" } }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2644,39 +2727,13 @@ "dev": true, "license": "MIT" }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2698,27 +2755,6 @@ "ajv": ">=5.0.0" } }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", - "dev": true, - "license": "BSD-3-Clause OR MIT", - "optional": true, - "engines": { - "node": ">=0.4.2" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -2729,22 +2765,6 @@ "node": ">=6" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2816,13 +2836,13 @@ } }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/ast-parents": { @@ -2842,20 +2862,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2872,18 +2878,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", - "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2988,59 +2982,6 @@ "dev": true, "license": "MIT" }, - "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3071,13 +3012,6 @@ "dev": true, "license": "MIT" }, - "node_modules/brotli-wasm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brotli-wasm/-/brotli-wasm-2.0.1.tgz", - "integrity": "sha512-+3USgYsC7bzb5yU0/p2HnnynZl0ak0E6uoIm4UW4Aby/8s8HFCq6NCfrrf1E9c3O8OCSzq3oYO1tUVqIi61Nww==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -3122,13 +3056,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -3136,16 +3063,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -3275,35 +3192,34 @@ } }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=18" } }, "node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-8.0.2.tgz", + "integrity": "sha512-1GadL+sEJVLzDjcawPM4kjfnL+p/9vrxiEUonowKOAzvVg0PixJUdtuDzdkDeQhK3zfOE76GqGkZIQ7/Adcrqw==", "dev": true, - "license": "WTFPL", + "license": "MIT", "dependencies": { - "check-error": "^1.0.2" + "check-error": "^2.1.1" }, "peerDependencies": { - "chai": ">= 2.1.2 < 6" + "chai": ">= 2.1.2 < 7" } }, "node_modules/chalk": { @@ -3327,27 +3243,14 @@ "dev": true, "license": "MIT" }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", "dev": true, "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, "engines": { - "node": "*" + "node": ">= 16" } }, "node_modules/chokidar": { @@ -3405,29 +3308,6 @@ "node": ">= 0.10" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -3444,22 +3324,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -3658,26 +3522,6 @@ "dev": true, "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", - "dev": true, - "license": "MIT" - }, "node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", @@ -3713,16 +3557,6 @@ "proto-list": "~1.2.1" } }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", @@ -3827,16 +3661,6 @@ "node": ">= 8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, "node_modules/dataloader": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", @@ -3844,12 +3668,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/death": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", - "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", - "dev": true - }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -3911,14 +3729,11 @@ } }, "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, "engines": { "node": ">=6" } @@ -3968,26 +3783,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -3999,27 +3794,15 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, - "node_modules/difflib": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", - "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", - "dev": true, - "dependencies": { - "heap": ">= 0.2.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4175,20 +3958,46 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/escalade": { @@ -4214,112 +4023,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.12.0" - }, - "optionalDependencies": { - "source-map": "~0.2.0" - } - }, - "node_modules/escodegen/node_modules/esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { "version": "9.25.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4652,16 +4362,6 @@ "node": ">=0.10.0" } }, - "node_modules/ethereum-bloom-filters": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz", - "integrity": "sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.4.0" - } - }, "node_modules/ethereum-cryptography": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-3.2.0.tgz", @@ -4737,6 +4437,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", @@ -4786,28 +4487,6 @@ "undici-types": "~6.19.2" } }, - "node_modules/ethjs-unit": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "4.11.6", - "number-to-bn": "1.7.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/ethjs-unit/node_modules/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", - "dev": true, - "license": "MIT" - }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -4862,6 +4541,16 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -4991,27 +4680,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -5045,23 +4713,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/form-data-encoder": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", @@ -5072,13 +4723,6 @@ "node": ">= 14.17" } }, - "node_modules/fp-ts": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", - "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", - "dev": true, - "license": "MIT" - }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -5149,16 +4793,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -5198,96 +4832,17 @@ "node": ">= 0.4" } }, - "node_modules/ghost-testrpc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", - "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "chalk": "^2.4.2", - "node-emoji": "^1.10.0" - }, - "bin": { - "testrpc-sc": "index.js" - } - }, - "node_modules/ghost-testrpc/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ghost-testrpc/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ghost-testrpc/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/ghost-testrpc/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/ghost-testrpc/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ghost-testrpc/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ghost-testrpc/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "resolve-pkg-maps": "^1.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, "node_modules/glob": { @@ -5333,49 +4888,8 @@ "engines": { "node": "20 || >=22" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/globals": { @@ -5476,419 +4990,83 @@ "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hardhat": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.28.0.tgz", - "integrity": "sha512-A3yBISI18EcnY2IR7Ny2xZF33Q3qH01yrWapeWbyGOiJm/386SasWjbHRHYgUlZ3YWJETIMh7wYfMUaXrofTDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ethereumjs/util": "^9.1.0", - "@ethersproject/abi": "^5.1.2", - "@nomicfoundation/edr": "0.12.0-next.17", - "@nomicfoundation/solidity-analyzer": "^0.1.0", - "@sentry/node": "^5.18.1", - "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", - "keccak": "^3.0.2", - "lodash": "^4.17.11", - "micro-eth-signer": "^0.14.0", - "mnemonist": "^0.38.0", - "mocha": "^10.0.0", - "p-map": "^4.0.0", - "picocolors": "^1.1.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", - "tinyglobby": "^0.2.6", - "tsort": "0.0.1", - "undici": "^5.14.0", - "uuid": "^8.3.2", - "ws": "^7.4.6" - }, - "bin": { - "hardhat": "internal/cli/bootstrap.js" - }, - "peerDependencies": { - "ts-node": "*", - "typescript": "*" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/hardhat-exposed": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/hardhat-exposed/-/hardhat-exposed-0.3.19.tgz", - "integrity": "sha512-vVye5TurJu8dWeo4ma+EfLAOQaJyica4uncd0/BGPO2tmexuDwZUmE1vYx81PlP4Iak3wqkNTEPxWQaE2ZnKnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "micromatch": "^4.0.8", - "solidity-ast": "^0.4.59" - }, - "peerDependencies": { - "hardhat": "^2.3.0" - } - }, - "node_modules/hardhat-gas-reporter": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-2.2.3.tgz", - "integrity": "sha512-/52fDR0WOgPTjImmx4j179SAgxPv/499TD0o0qnMhaRr24i2cqlcmCW92FJi0QAKu7HcnxdBGZWQP/5aPjQqUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/units": "^5.7.0", - "@solidity-parser/parser": "^0.19.0", - "axios": "^1.6.7", - "brotli-wasm": "^2.0.1", - "chalk": "4.1.2", - "cli-table3": "^0.6.3", - "ethereum-cryptography": "^2.1.3", - "glob": "^10.3.10", - "jsonschema": "^1.4.1", - "lodash": "^4.17.21", - "markdown-table": "2.0.0", - "sha1": "^1.1.1", - "viem": "^2.27.0" - }, - "peerDependencies": { - "hardhat": "^2.16.0" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/@noble/curves": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", - "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/ethereum-cryptography": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", - "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "1.4.2", - "@noble/hashes": "1.4.0", - "@scure/bip32": "1.4.0", - "@scure/bip39": "1.3.0" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/hardhat-gas-reporter/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hardhat-gas-reporter/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/hardhat-ignore-warnings": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.2.12.tgz", - "integrity": "sha512-SaxCLKzYBMk3Rd1275TnanUmmxwgU+bu4Ekf2MKcqXxxt6xTGcPTtTaM+USrLgmejZHC4Itg/PaWITlOp4RL3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimatch": "^5.1.0", - "node-interval-tree": "^2.0.1", - "solidity-comments": "^0.0.2" - } - }, - "node_modules/hardhat-ignore-warnings/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hardhat-predeploy": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/hardhat-predeploy/-/hardhat-predeploy-0.4.1.tgz", - "integrity": "sha512-53Eoj5YECKNTkEezGByjKP5rVbYU0s1is8CtEbm0DPbqMgstNrPMQ0dfcDxA5Fu+hlZhc5w0T+RmJ/d/GAbB6g==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@nomicfoundation/hardhat-ethers": "^3.0.8", - "hardhat": "^2.26.0" - } - }, - "node_modules/hardhat/node_modules/@ethereumjs/rlp": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", - "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", - "dev": true, - "license": "MPL-2.0", - "bin": { - "rlp": "bin/rlp.cjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/hardhat/node_modules/@ethereumjs/util": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", - "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@ethereumjs/rlp": "^5.0.2", - "ethereum-cryptography": "^2.2.1" - }, - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" } }, - "node_modules/hardhat/node_modules/@ethereumjs/util/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "node_modules/hardhat": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.5.tgz", + "integrity": "sha512-0Z0KI/m6wJYCMZgDK3QuVqR59lSa3aMu6QHKqnbIYXKu/phQ+YFKJZAY4zkUKX21ZjcrrRg25qLUzZw1bO6g/A==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 16" + "peer": true, + "dependencies": { + "@nomicfoundation/edr": "0.12.0-next.22", + "@nomicfoundation/hardhat-errors": "^3.0.6", + "@nomicfoundation/hardhat-utils": "^3.0.6", + "@nomicfoundation/hardhat-vendored": "^3.0.0", + "@nomicfoundation/hardhat-zod-utils": "^3.0.1", + "@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" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "bin": { + "hardhat": "dist/src/cli.js" } }, - "node_modules/hardhat/node_modules/@ethereumjs/util/node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "node_modules/hardhat-ignore-warnings": { + "version": "0.3.0-3", + "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.3.0-3.tgz", + "integrity": "sha512-tMPCJR3kz0RoglCM+RJHwyB6aGrdl20ahvk+AaY8u8JrLGFybT/DqjjPJadV8Bm6kn/TU3BdWxRDTXpOif+xKA==", "dev": true, "license": "MIT", "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" + "minimatch": "^10.1.1", + "node-interval-tree": "^2.0.1", + "solidity-comments": "^0.0.2" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "hardhat": "^3.1.0" } }, - "node_modules/hardhat/node_modules/@ethereumjs/util/node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "node_modules/hardhat-ignore-warnings/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" }, "funding": { - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/hardhat/node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", - "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "node_modules/hardhat-predeploy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hardhat-predeploy/-/hardhat-predeploy-1.0.0.tgz", + "integrity": "sha512-hckkO0H8Ep5yk7mNqJAdbOt5tmq6/v8SNvJRRgrf8Mcj/5eCRtZ8h+z2adA4XfNbDOD0amXQh8ggc2hUEmJIvg==", "dev": true, "license": "MIT", - "dependencies": { - "@noble/curves": "1.4.2", - "@noble/hashes": "1.4.0", - "@scure/bip32": "1.4.0", - "@scure/bip39": "1.3.0" + "peerDependencies": { + "hardhat": "^3.0.0" } }, "node_modules/hardhat/node_modules/@noble/curves": { @@ -5904,7 +5082,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/hardhat/node_modules/@noble/curves/node_modules/@noble/hashes": { + "node_modules/hardhat/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -5917,19 +5095,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/hardhat/node_modules/@noble/hashes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", - "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, "node_modules/hardhat/node_modules/@scure/base": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", @@ -5941,139 +5106,74 @@ } }, "node_modules/hardhat/node_modules/@scure/bip32": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", - "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.2.0", - "@noble/secp256k1": "~1.7.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/hardhat/node_modules/@scure/bip39": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", - "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.2.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/hardhat/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, "funding": { "url": "https://paulmillr.com/funding/" } }, - "node_modules/hardhat/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/hardhat/node_modules/ethereum-cryptography": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", - "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.2.0", - "@noble/secp256k1": "1.7.1", - "@scure/bip32": "1.1.5", - "@scure/bip39": "1.1.1" - } - }, - "node_modules/hardhat/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/hardhat/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/hardhat/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/hardhat/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/hardhat/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/hardhat/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">= 14.16.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/hardhat/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } }, "node_modules/hardhat/node_modules/readdirp": { @@ -6090,51 +5190,18 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/hardhat/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/hardhat/node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, - "node_modules/hardhat/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/hardhat/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -6145,19 +5212,6 @@ } } }, - "node_modules/hardhat/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -6259,13 +5313,6 @@ "he": "bin/he" } }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", - "dev": true, - "license": "MIT" - }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -6285,23 +5332,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/http2-wrapper": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", @@ -6316,20 +5346,6 @@ "node": ">=10.19.0" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/human-id": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.1.tgz", @@ -6379,13 +5395,6 @@ "node": ">= 4" } }, - "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true, - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -6423,16 +5432,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -6471,26 +5470,6 @@ "typed-regex": "^0.0.8" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/io-ts": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", - "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fp-ts": "^1.0.0" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -6555,20 +5534,9 @@ "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hex-prefixed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", - "dev": true, - "license": "MIT", + }, "engines": { - "node": ">=6.5.0", - "npm": ">=3" + "node": ">=0.10.0" } }, "node_modules/is-number": { @@ -6581,6 +5549,16 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -6667,22 +5645,6 @@ "dev": true, "license": "ISC" }, - "node_modules/isows": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", - "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "peerDependencies": { - "ws": "*" - } - }, "node_modules/jackspeak": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", @@ -6699,13 +5661,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true, - "license": "MIT" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6785,16 +5740,6 @@ "node": ">=0.10.0" } }, - "node_modules/jsonschema": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", - "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/keccak": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", @@ -6821,16 +5766,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/latest-version": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", @@ -7055,14 +5990,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -7273,14 +6200,11 @@ } }, "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } + "license": "MIT" }, "node_modules/lowercase-keys": { "version": "3.0.0", @@ -7295,13 +6219,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", @@ -7312,20 +6229,6 @@ "node": "20 || >=22" } }, - "node_modules/markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "repeat-string": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -7348,15 +6251,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -7442,29 +6336,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -7541,55 +6412,33 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mnemonist": { - "version": "0.38.5", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", - "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "obliterator": "^2.0.0" - } - }, "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { @@ -7597,7 +6446,7 @@ "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/argparse": { @@ -7607,16 +6456,35 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/mocha/node_modules/find-up": { @@ -7637,24 +6505,40 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/mocha/node_modules/js-yaml": { @@ -7686,18 +6570,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/mocha/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } + "license": "ISC" }, "node_modules/mocha/node_modules/p-limit": { "version": "3.1.0", @@ -7731,6 +6609,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -7748,22 +6657,22 @@ } }, "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/mocha/node_modules/yocto-queue": { @@ -7816,13 +6725,6 @@ "dev": true, "license": "MIT" }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -7830,16 +6732,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - } - }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -7896,19 +6788,6 @@ "node": ">=12.19" } }, - "node_modules/nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -7932,35 +6811,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/number-to-bn/node_modules/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", - "dev": true, - "license": "MIT" - }, - "node_modules/obliterator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", - "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", - "dev": true, - "license": "MIT" - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7989,60 +6839,23 @@ "node": ">= 0.8.0" } }, - "node_modules/ordinal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", - "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", - "dev": true, - "license": "MIT" - }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/outdent": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", - "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/ox": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.9.tgz", - "integrity": "sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "dependencies": { - "@adraffy/ens-normalize": "^1.10.1", - "@noble/curves": "^1.6.0", - "@noble/hashes": "^1.5.0", - "@scure/bip32": "^1.5.0", - "@scure/bip39": "^1.4.0", - "abitype": "^1.0.6", - "eventemitter3": "5.0.1" - }, - "peerDependencies": { - "typescript": ">=5.4.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, + "node_modules/outdent": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "dev": true, + "license": "MIT" + }, "node_modules/p-cancelable": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", @@ -8122,16 +6935,13 @@ } }, "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8225,16 +7035,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -8245,13 +7045,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, "node_modules/path-scurry": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -8280,13 +7073,13 @@ } }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">= 14.16" } }, "node_modules/pbkdf2": { @@ -8430,6 +7223,7 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -8508,13 +7302,6 @@ "dev": true, "license": "ISC" }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8586,22 +7373,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -8672,55 +7443,6 @@ "node": ">=8.10.0" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/recursive-readdir/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/recursive-readdir/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/registry-auth-token": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", @@ -8750,16 +7472,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8780,19 +7492,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", @@ -8810,6 +7509,26 @@ "node": ">=8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/responselike": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", @@ -9023,131 +7742,6 @@ "dev": true, "license": "MIT" }, - "node_modules/sc-istanbul": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", - "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "istanbul": "lib/cli.js" - } - }, - "node_modules/sc-istanbul/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/sc-istanbul/node_modules/esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sc-istanbul/node_modules/glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/sc-istanbul/node_modules/has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sc-istanbul/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/sc-istanbul/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", - "dev": true, - "license": "MIT" - }, - "node_modules/sc-istanbul/node_modules/supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/sc-istanbul/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/scrypt-js": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", @@ -9226,13 +7820,6 @@ "dev": true, "license": "MIT" }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" - }, "node_modules/sha.js": { "version": "2.4.12", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", @@ -9254,20 +7841,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sha1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", - "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "charenc": ">= 0.0.1", - "crypt": ">= 0.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -9278,88 +7851,24 @@ "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shelljs/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/shelljs/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "shebang-regex": "^3.0.0" }, "engines": { - "node": "*" + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/signal-exit": { @@ -9415,48 +7924,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/solc": { - "version": "0.8.26", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", - "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "command-exists": "^1.2.8", - "commander": "^8.1.0", - "follow-redirects": "^1.12.1", - "js-sha3": "0.8.0", - "memorystream": "^0.3.1", - "semver": "^5.5.0", - "tmp": "0.0.33" - }, - "bin": { - "solcjs": "solc.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/solc/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/solc/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/solhint": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/solhint/-/solhint-6.0.1.tgz", @@ -9625,9 +8092,9 @@ } }, "node_modules/solidity-ast": { - "version": "0.4.60", - "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.60.tgz", - "integrity": "sha512-UwhasmQ37ji1ul8cIp0XlrQ/+SVQhy09gGqJH4jnwdo2TgI6YIByzi0PI5QvIGcIdFOs1pbSmJW1pnWB7AVh2w==", + "version": "0.4.61", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.61.tgz", + "integrity": "sha512-OYBJYcYyG7gLV0VuXl9CUrvgJXjV/v0XnR4+1YomVe3q+QyENQXJJxAEASUz4vN6lMAl+C8RSRSr5MBAz09f6w==", "dev": true, "license": "MIT" }, @@ -9823,247 +8290,6 @@ "node": ">= 10" } }, - "node_modules/solidity-coverage": { - "version": "0.8.15", - "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.15.tgz", - "integrity": "sha512-qH7290NKww4/t/qFvnSEePEzON0k025IGVlwc8wo8Q6p+h1Tt6fV2M0k3yfsps3TomZYTROsfPXjx7MSnwD5uA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@ethersproject/abi": "^5.0.9", - "@solidity-parser/parser": "^0.19.0", - "chalk": "^2.4.2", - "death": "^1.1.0", - "difflib": "^0.2.4", - "fs-extra": "^8.1.0", - "ghost-testrpc": "^0.0.2", - "global-modules": "^2.0.0", - "globby": "^10.0.1", - "jsonschema": "^1.2.4", - "lodash": "^4.17.21", - "mocha": "^10.2.0", - "node-emoji": "^1.10.0", - "pify": "^4.0.1", - "recursive-readdir": "^2.2.2", - "sc-istanbul": "^0.4.5", - "semver": "^7.3.4", - "shelljs": "^0.8.3", - "web3-utils": "^1.3.6" - }, - "bin": { - "solidity-coverage": "plugins/bin.js" - }, - "peerDependencies": { - "hardhat": "^2.11.0" - } - }, - "node_modules/solidity-coverage/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/solidity-coverage/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/solidity-coverage/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/solidity-coverage/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/solidity-coverage/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/solidity-coverage/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/solidity-coverage/node_modules/globby": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", - "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/solidity-coverage/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-coverage/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/solidity-coverage/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/solidity-docgen": { - "version": "0.6.0-beta.36", - "resolved": "https://registry.npmjs.org/solidity-docgen/-/solidity-docgen-0.6.0-beta.36.tgz", - "integrity": "sha512-f/I5G2iJgU1h0XrrjRD0hHMr7C10u276vYvm//rw1TzFcYQ4xTOyAoi9oNAHRU0JU4mY9eTuxdVc2zahdMuhaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "handlebars": "^4.7.7", - "solidity-ast": "^0.4.38" - }, - "peerDependencies": { - "hardhat": "^2.8.0" - } - }, - "node_modules/source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", - "dev": true, - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/spawndamnit": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", @@ -10082,39 +8308,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/stacktrace-parser": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -10223,20 +8416,6 @@ "node": ">=4" } }, - "node_modules/strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-hex-prefixed": "1.0.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -10301,78 +8480,33 @@ "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10414,16 +8548,6 @@ "node": ">=8.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -10438,12 +8562,25 @@ "dev": true, "license": "0BSD" }, - "node_modules/tsort": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", - "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } }, "node_modules/type-check": { "version": "0.4.0", @@ -10458,29 +8595,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -10503,20 +8617,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/undici": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.8.0.tgz", @@ -10544,16 +8644,6 @@ "node": ">= 4.0.0" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10564,13 +8654,6 @@ "punycode": "^2.1.0" } }, - "node_modules/utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "dev": true, - "license": "MIT" - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -10592,215 +8675,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/viem": { - "version": "2.28.3", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.28.3.tgz", - "integrity": "sha512-kGYmSHNmzXqg7uZlaV6OEL1p68Z45BYTRPsUM0jYLmOn2zWy6DRA+YxntKnt6jBiCPjiWbVrbFm5QS6TWnfAXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "dependencies": { - "@noble/curves": "1.8.2", - "@noble/hashes": "1.7.2", - "@scure/bip32": "1.6.2", - "@scure/bip39": "1.5.4", - "abitype": "1.0.8", - "isows": "1.0.6", - "ox": "0.6.9", - "ws": "8.18.1" - }, - "peerDependencies": { - "typescript": ">=5.0.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/viem/node_modules/@noble/curves": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", - "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.7.2" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@noble/hashes": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", - "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@scure/bip32": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", - "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.8.1", - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@scure/bip39": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", - "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.4" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/web3-utils": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", - "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", - "dev": true, - "license": "LGPL-3.0", - "dependencies": { - "@ethereumjs/util": "^8.1.0", - "bn.js": "^5.2.1", - "ethereum-bloom-filters": "^1.0.6", - "ethereum-cryptography": "^2.1.2", - "ethjs-unit": "0.1.6", - "number-to-bn": "1.7.0", - "randombytes": "^2.1.0", - "utf8": "3.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-utils/node_modules/@noble/curves": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", - "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/web3-utils/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/web3-utils/node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/web3-utils/node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/web3-utils/node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/web3-utils/node_modules/ethereum-cryptography": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", - "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "1.4.2", - "@noble/hashes": "1.4.0", - "@scure/bip32": "1.4.0", - "@scure/bip39": "1.3.0" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -10857,19 +8731,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -10880,17 +8741,10 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "license": "Apache-2.0" }, @@ -11002,13 +8856,13 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -11104,6 +8958,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "scripts/solhint-custom": { "name": "solhint-plugin-openzeppelin", "version": "0.0.0", diff --git a/package.json b/package.json index d31a4d28051..28dc4e3ff7e 100644 --- a/package.json +++ b/package.json @@ -58,25 +58,26 @@ "@changesets/read": "^0.6.0", "@eslint/compat": "^1.2.1", "@ethereumjs/mpt": "^10.1.0", - "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", - "@nomicfoundation/hardhat-ethers": "^3.0.9", - "@nomicfoundation/hardhat-network-helpers": "^1.0.13", + "@nomicfoundation/hardhat-ethers": "^4.0.4", + "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.2", + "@nomicfoundation/hardhat-mocha": "^3.0.9", + "@nomicfoundation/hardhat-network-helpers": "^3.0.3", "@openzeppelin/docs-utils": "^0.1.6", "@openzeppelin/merkle-tree": "^1.0.7", "@openzeppelin/upgrade-safe-transpiler": "^0.4.1", "@openzeppelin/upgrades-core": "^1.20.6", - "chai": "^4.2.0", + "@types/node": "^25.0.9", + "@types/yargs": "^17.0.35", + "chai": "^5.2.2", "eslint": "^9.0.0", "eslint-config-prettier": "^10.0.0", "ethers": "^6.16.0", "glob": "^13.0.0", "globals": "^16.0.0", "graphlib": "^2.1.8", - "hardhat": "^2.28.0", - "hardhat-exposed": "^0.3.15", - "hardhat-gas-reporter": "^2.1.0", - "hardhat-ignore-warnings": "^0.2.11", - "hardhat-predeploy": "^0.4.1", + "hardhat": "^3.1.5", + "hardhat-ignore-warnings": "^0.3.0-3", + "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", "interoperable-addresses": "^0.1.3", "lint-staged": "^16.0.0", @@ -89,9 +90,7 @@ "semver": "^7.3.5", "solhint": "^6.0.1", "solhint-plugin-openzeppelin": "file:scripts/solhint-custom", - "solidity-ast": "^0.4.50", - "solidity-coverage": "^0.8.14", - "solidity-docgen": "^0.6.0-beta.29", + "solidity-ast": "^0.4.61", "undici": "^7.4.0", "yargs": "^18.0.0" }, @@ -104,5 +103,6 @@ "prettier --log-level warn --ignore-path .gitignore --check", "solhint --config solhint.config.js --noPoster" ] - } + }, + "type": "module" } diff --git a/remappings.txt b/remappings.txt index 304d1386a58..a6be3bfd689 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1 +1,5 @@ @openzeppelin/contracts/=contracts/ + +forge-std=lib/forge-std/src +halmos-cheatcodes=lib/halmos-cheatcodes/src +erc4626-tests=lib/erc4626-tests diff --git a/test/access/Ownable.test.js b/test/access/Ownable.test.js index 2d9b561a1dc..1fa3f0e27b3 100644 --- a/test/access/Ownable.test.js +++ b/test/access/Ownable.test.js @@ -1,6 +1,7 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { ethers, networkHelpers } = await network.connect(); async function fixture() { const [owner, other] = await ethers.getSigners(); @@ -10,7 +11,7 @@ async function fixture() { describe('Ownable', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, await networkHelpers.loadFixture(fixture)); }); it('emits ownership transfer events during construction', async function () { diff --git a/test/account/utils/draft-ERC7579Utils.t.sol b/test/account/utils/draft-ERC7579Utils.t.sol index bd7b9ad826e..b4a4e62c39a 100644 --- a/test/account/utils/draft-ERC7579Utils.t.sol +++ b/test/account/utils/draft-ERC7579Utils.t.sol @@ -394,7 +394,7 @@ contract ERC7579UtilsTest is Test { return useroperation.hash(address(ERC4337Utils.ENTRYPOINT_V07)); } - function _collectAndPrintLogs(bool includeTotalValue) internal { + function _collectAndPrintLogs(bool includeTotalValue) internal view { Vm.Log[] memory logs = vm.getRecordedLogs(); for (uint256 i = 0; i < logs.length; i++) { if (logs[i].emitter == _account) { diff --git a/test/metatx/ERC2771Forwarder.t.sol b/test/metatx/ERC2771Forwarder.t.sol index 224367377ff..dcaccd08af1 100644 --- a/test/metatx/ERC2771Forwarder.t.sol +++ b/test/metatx/ERC2771Forwarder.t.sol @@ -99,7 +99,7 @@ contract ERC2771ForwarderTest is Test { function _tamperRequestData( ERC2771Forwarder.ForwardRequestData memory request, TamperType tamper - ) private returns (ERC2771Forwarder.ForwardRequestData memory) { + ) private view returns (ERC2771Forwarder.ForwardRequestData memory) { if (tamper == TamperType.FROM) request.from = vm.randomAddress(); else if (tamper == TamperType.TO) request.to = vm.randomAddress(); else if (tamper == TamperType.VALUE) request.value = vm.randomUint(); @@ -200,7 +200,7 @@ contract ERC2771ForwarderTest is Test { assertEq(refundReceiver.balance, refundExpected); } - function testVerifyTamperedValues(uint8 _tamper) public { + function testVerifyTamperedValues(uint8 _tamper) public view { TamperType tamper = _asTamper(_tamper); // create request, sign, tamper diff --git a/test/proxy/Clones.t.sol b/test/proxy/Clones.t.sol index 5da6d56d5a6..6bb678c65c4 100644 --- a/test/proxy/Clones.t.sol +++ b/test/proxy/Clones.t.sol @@ -28,8 +28,7 @@ contract ClonesTest is Test { address predicted = Clones.predictDeterministicAddressWithImmutableArgs(implementation, args, salt); bytes32 spillage; - /// @solidity memory-safe-assembly - assembly { + assembly ("memory-safe") { spillage := and(predicted, 0xffffffffffffffffffffffff0000000000000000000000000000000000000000) } assertEq(spillage, bytes32(0)); From feec8696ffcc3eff682c7d8c2c494072e709c914 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 22 Jan 2026 08:38:41 +0100 Subject: [PATCH 02/91] new hook --- hardhat.config.ts | 2 +- .../hardhat-exposed/hook-handlers/solidity.ts | 124 ++++++------------ package-lock.json | 6 +- package.json | 2 +- 4 files changed, 43 insertions(+), 91 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 505266269b7..4111681effb 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -49,7 +49,7 @@ export default defineConfig({ hardhatNetworkHelpers, hardhatPredeploy, // Local plugins - // hardhatExposed, + hardhatExposed, // Additional hooks { id: '@openzeppelin/contracts', diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts index 3678d0278fb..51ee0240412 100644 --- a/hardhat/hardhat-exposed/hook-handlers/solidity.ts +++ b/hardhat/hardhat-exposed/hook-handlers/solidity.ts @@ -4,19 +4,19 @@ import micromatch from 'micromatch'; import type { SolcConfig } from 'hardhat/types/config'; import type { SolidityHooks, HookContext } from 'hardhat/types/hooks'; import type { - RunCompilationJobResult, + BuildOptions, + CompilationJob, CompilerInput, CompilerOutput, CompilerOutputError, + EmitArtifactsResult, + ResolvedFile, + RunCompilationJobResult, } from 'hardhat/types/solidity'; -import type { Artifact } from 'hardhat/types/artifacts'; -import { remove, writeJsonFile, writeUtf8File } from '@nomicfoundation/hardhat-utils/fs'; -import { getPrefixedHexString } from '@nomicfoundation/hardhat-utils/hex'; - import type {} from '../type-extensions'; -import { getExposed, writeExposed } from '../core'; +import { getExposed, getExposedPath, writeExposed } from '../core'; export default async (): Promise> => ({ async invokeSolc( @@ -62,88 +62,40 @@ export default async (): Promise> => ({ const relativePath = path.join('project', path.relative(context.config.paths.root, fsPath)); solcInput.sources[relativePath] = { content }; } + } - // Full compilation - const fullOutput = await next(context, compiler, solcInput, solcConfig); - - // ############################################################################################################# - // # Write artifacts for exposed files - Begin # - // ############################################################################################################# - // NOTE: this should ideally be handled by hardhat. It currently does NOT work, because the artifacts produced are erased automatically for an unknown reason. - const artifactsDirectory = await context.solidity.getArtifactsDirectory('contracts'); - for (const fsPath of exposed.keys()) { - const relativePath = path.relative(context.config.paths.root, fsPath); - const projectPath = path.join('project', relativePath); - const fileFolder = path.join(artifactsDirectory, relativePath); - - await remove(fileFolder); - - const artifacts: Artifact[] = []; - for (const [contractName, contract] of Object.entries(fullOutput.contracts?.[projectPath] ?? {})) { - const evmBytecode = contract.evm?.bytecode; - const bytecode: string = evmBytecode?.object !== undefined ? getPrefixedHexString(evmBytecode.object) : ''; - const evmDeployedBytecode = contract.evm?.deployedBytecode; - const deployedBytecode: string = - evmDeployedBytecode?.object !== undefined ? getPrefixedHexString(evmDeployedBytecode.object) : ''; - const linkReferences = evmBytecode?.linkReferences ?? {}; - const deployedLinkReferences = evmDeployedBytecode?.linkReferences ?? {}; - const immutableReferences = evmDeployedBytecode?.immutableReferences ?? {}; - - const artifact: Artifact = { - _format: 'hh3-artifact-1', - contractName, - sourceName: relativePath, - abi: contract.abi, - bytecode, - deployedBytecode, - linkReferences, - deployedLinkReferences, - immutableReferences, - inputSourceName: projectPath, - buildInfoId: '', - }; - artifacts.push(artifact); - - await writeJsonFile(path.join(fileFolder, `${contractName}.json`), artifact); - } - - if (artifacts.length === 0) continue; - - const artifactsDeclarationFile = `\ -// This file was autogenerated by Hardhat, do not edit it. -// prettier-ignore -// tslint:disable -// eslint-disable -// biome-ignore format: see above - -${artifacts - .map( - artifact => `export interface ${artifact.contractName}$Type { - ${Object.entries(artifact) - .map(([name, value]) => `readonly ${name}: ${JSON.stringify(value)};`) - .join('\n ')} -};`, - ) - .join('\n\n')} - -import "hardhat/types/artifacts"; -declare module "hardhat/types/artifacts" { - interface ArtifactMap { - ${artifacts.map(artifact => `["${artifact.contractName}"]: ${artifact.contractName}$Type`).join('\n ')}; - ${artifacts.map(artifact => `["${artifact.sourceName}:${artifact.contractName}"]: ${artifact.contractName}$Type`).join('\n ')}; - } -}`; + // Full compilation + return await next(context, compiler, solcInput, solcConfig); + }, - await writeUtf8File(path.join(fileFolder, 'artifacts.d.ts'), artifactsDeclarationFile); + emitArtifacts( + context: HookContext, + compilationJob: CompilationJob, + compilerOutput: CompilerOutput, + options: BuildOptions, + next: ( + nextContext: HookContext, + nextCompilationJob: CompilationJob, + nextCompilerOutput: CompilerOutput, + nextOptions: BuildOptions, + ) => Promise, + ): Promise { + + for (const [root, value] of compilationJob.dependencyGraph.getRoots().entries()) { + const exposedPath = path.join(context.config.exposed.outDir, root); + const fsPath = path.join(context.config.paths.root, exposedPath); + const inputSourceName = path.join('project', exposedPath); + + if (compilerOutput.contracts?.[inputSourceName]) { + (compilationJob.dependencyGraph as any).addRootFile(exposedPath, { + ...value, + inputSourceName, + fsPath, + content: { text: '', importPaths: '', versionPragmas: [ '>=0.6.0' ] }, + }); } - // ############################################################################################################# - // # Write artifacts for exposed files - End # - // ############################################################################################################# - - return fullOutput; - } else { - // Full compilation - return await next(context, compiler, solcInput, solcConfig); } - }, + + return next(context, compilationJob, compilerOutput, options); + } }); diff --git a/package-lock.json b/package-lock.json index 23fe13467ce..2f7f2d182e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "glob": "^13.0.0", "globals": "^16.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.5", + "hardhat": "file:../../Repos/hardhat/v-next/hardhat/hardhat-3.1.5.tgz", "hardhat-ignore-warnings": "^0.3.0-3", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", @@ -4997,8 +4997,8 @@ }, "node_modules/hardhat": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.5.tgz", - "integrity": "sha512-0Z0KI/m6wJYCMZgDK3QuVqR59lSa3aMu6QHKqnbIYXKu/phQ+YFKJZAY4zkUKX21ZjcrrRg25qLUzZw1bO6g/A==", + "resolved": "file:../../Repos/hardhat/v-next/hardhat/hardhat-3.1.5.tgz", + "integrity": "sha512-QpZB9kCWc2lrwDzRAL5Rz4kpFua8qAUtCds4vsZqTXqlTUKg/bESaeujM0C2OgvDegejNYLWTYVzZpwNiIyYnw==", "dev": true, "license": "MIT", "peer": true, diff --git a/package.json b/package.json index 28dc4e3ff7e..850abf9d160 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "glob": "^13.0.0", "globals": "^16.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.5", + "hardhat": "file:../../Repos/hardhat/v-next/hardhat/hardhat-3.1.5.tgz", "hardhat-ignore-warnings": "^0.3.0-3", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", From 7eaa9d68afab5ae993de2b61edfe878ba362bc02 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 23 Jan 2026 18:41:24 +0100 Subject: [PATCH 03/91] It works! implemented exposed by rewritting context.solidity.build --- hardhat/hardhat-exposed/core/index.ts | 2 +- hardhat/hardhat-exposed/hook-handlers/hre.ts | 82 ++++++++++++++ .../hardhat-exposed/hook-handlers/solidity.ts | 101 ------------------ hardhat/hardhat-exposed/plugin.ts | 2 +- package-lock.json | 6 +- package.json | 2 +- 6 files changed, 88 insertions(+), 107 deletions(-) create mode 100644 hardhat/hardhat-exposed/hook-handlers/hre.ts delete mode 100644 hardhat/hardhat-exposed/hook-handlers/solidity.ts diff --git a/hardhat/hardhat-exposed/core/index.ts b/hardhat/hardhat-exposed/core/index.ts index fdbe60bcbc5..77efbc5f534 100644 --- a/hardhat/hardhat-exposed/core/index.ts +++ b/hardhat/hardhat-exposed/core/index.ts @@ -65,7 +65,7 @@ export function getExposed( } // Helpers -function getExposedPath(config: HardhatConfig) { +export function getExposedPath(config: HardhatConfig) { return path.join(config.paths.root, config.exposed.outDir); } diff --git a/hardhat/hardhat-exposed/hook-handlers/hre.ts b/hardhat/hardhat-exposed/hook-handlers/hre.ts new file mode 100644 index 00000000000..ae0b9f3d161 --- /dev/null +++ b/hardhat/hardhat-exposed/hook-handlers/hre.ts @@ -0,0 +1,82 @@ +import path from 'path'; +import micromatch from 'micromatch'; + +import type { HardhatRuntimeEnvironmentHooks, HookContext } from 'hardhat/types/hooks'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { BuildOptions, SolidityBuildSystem } from 'hardhat/types/solidity'; +import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; + +import { getExposed, getExposedPath, writeExposed } from '../core'; + +const overrideBuild = + (context: HookContext, runSuper: SolidityBuildSystem['build']) => + async (rootFilePaths: string[], options?: BuildOptions) => { + switch (options?.scope) { + case 'contracts': + const compilationJobsResult = await context.solidity.getCompilationJobs(rootFilePaths, options); + + if ('reason' in compilationJobsResult) break; + + // Determine which files to include + const include = (sourceName: string): boolean => + sourceName.startsWith('project/') && + compilationJobsResult.compilationJobsPerFile.has(path.relative('project/', sourceName)) && + context.config.exposed.include.some((p: string) => + micromatch.isMatch(path.relative('project/', sourceName), p), + ) && + !context.config.exposed.exclude.some((p: string) => + micromatch.isMatch(path.relative('project/', sourceName), p), + ); + + const spinner = createSpinner({ + text: `Generation of exposed contracts...`, + enabled: true, + }); + spinner.start(); + + // Expose selected contracts from each compilation job + const processed: Set = new Set(); + for (const job of compilationJobsResult.compilationJobsPerFile.values()) { + const key = await job.getBuildId(); + + // Avoid processing the same job multiple times + if (processed.has(key)) continue; + processed.add(key); + + // Override job settings + const oldEnabled = job.solcConfig.settings.optimizer.enabled; + const oldOutputSelection = job.solcConfig.settings.outputSelection; + job.solcConfig.settings.optimizer.enabled = false; + job.solcConfig.settings.outputSelection = { '*': { '': ['ast'] } }; + + // Run precompilation + const { output } = await context.solidity.runCompilationJob(job, options); + + // Restore job settings + job.solcConfig.settings.optimizer.enabled = oldEnabled; + job.solcConfig.settings.outputSelection = oldOutputSelection; + + // Generate and write exposed artifacts + const exposed = await getExposed(output, include, context.config); + await writeExposed(exposed); + + // Add exposed files to rootFilePaths for actual compilation + rootFilePaths.push(...exposed.keys()); + } + + spinner.stop(); + break; + + case 'tests': + break; + } + + return await runSuper(rootFilePaths, options); + }; + +export default async (): Promise> => ({ + created: async (context: HookContext, hre: HardhatRuntimeEnvironment): Promise => { + hre.config.paths.sources.solidity.push(getExposedPath(hre.config)); + hre.solidity.build = overrideBuild(context, hre.solidity.build.bind(hre.solidity)); + }, +}); diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts deleted file mode 100644 index 51ee0240412..00000000000 --- a/hardhat/hardhat-exposed/hook-handlers/solidity.ts +++ /dev/null @@ -1,101 +0,0 @@ -import path from 'path'; -import micromatch from 'micromatch'; - -import type { SolcConfig } from 'hardhat/types/config'; -import type { SolidityHooks, HookContext } from 'hardhat/types/hooks'; -import type { - BuildOptions, - CompilationJob, - CompilerInput, - CompilerOutput, - CompilerOutputError, - EmitArtifactsResult, - ResolvedFile, - RunCompilationJobResult, -} from 'hardhat/types/solidity'; - -import type {} from '../type-extensions'; - -import { getExposed, getExposedPath, writeExposed } from '../core'; - -export default async (): Promise> => ({ - async invokeSolc( - context: HookContext, - compiler: RunCompilationJobResult['compiler'], - solcInput: CompilerInput, - solcConfig: SolcConfig, - next: ( - nextContext: HookContext, - nextCompiler: RunCompilationJobResult['compiler'], - nextSolcInput: CompilerInput, - nextSolcConfig: SolcConfig, - ) => Promise, - ): Promise { - // Precompilation - const output: CompilerOutput = await next(context, compiler, solcInput, { - ...solcConfig, - settings: { - ...solcConfig.settings, - optimizer: { enabled: false }, - outputSelection: { '*': { '': ['ast'] } }, - }, - }); - - // If precompilation succeeded, generate exposed files - if (!output.errors?.some((e: CompilerOutputError) => e.severity === 'error')) { - // Determine which files to include - const include = (sourceName: string): boolean => - sourceName.startsWith('project/') && - context.config.exposed.include.some((p: string) => - micromatch.isMatch(path.relative('project/', sourceName), p), - ) && - !context.config.exposed.exclude.some((p: string) => - micromatch.isMatch(path.relative('project/', sourceName), p), - ); - - // Generate exposed files - const exposed = getExposed(output, include, context.config); - - // Write exposed files to disk and add them to the compilation input - await writeExposed(exposed); - for (const [fsPath, content] of exposed) { - const relativePath = path.join('project', path.relative(context.config.paths.root, fsPath)); - solcInput.sources[relativePath] = { content }; - } - } - - // Full compilation - return await next(context, compiler, solcInput, solcConfig); - }, - - emitArtifacts( - context: HookContext, - compilationJob: CompilationJob, - compilerOutput: CompilerOutput, - options: BuildOptions, - next: ( - nextContext: HookContext, - nextCompilationJob: CompilationJob, - nextCompilerOutput: CompilerOutput, - nextOptions: BuildOptions, - ) => Promise, - ): Promise { - - for (const [root, value] of compilationJob.dependencyGraph.getRoots().entries()) { - const exposedPath = path.join(context.config.exposed.outDir, root); - const fsPath = path.join(context.config.paths.root, exposedPath); - const inputSourceName = path.join('project', exposedPath); - - if (compilerOutput.contracts?.[inputSourceName]) { - (compilationJob.dependencyGraph as any).addRootFile(exposedPath, { - ...value, - inputSourceName, - fsPath, - content: { text: '', importPaths: '', versionPragmas: [ '>=0.6.0' ] }, - }); - } - } - - return next(context, compilationJob, compilerOutput, options); - } -}); diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index 4cc8fa5232a..f4956fd8543 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -7,7 +7,7 @@ const hardhatExposedPlugin: HardhatPlugin = { id: 'hardhat-exposed', hookHandlers: { config: () => import('./hook-handlers/config.js'), - solidity: () => import('./hook-handlers/solidity.js'), + hre: () => import('./hook-handlers/hre.js'), }, tasks: [ overrideTask('clean') diff --git a/package-lock.json b/package-lock.json index 2f7f2d182e3..23fe13467ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "glob": "^13.0.0", "globals": "^16.0.0", "graphlib": "^2.1.8", - "hardhat": "file:../../Repos/hardhat/v-next/hardhat/hardhat-3.1.5.tgz", + "hardhat": "^3.1.5", "hardhat-ignore-warnings": "^0.3.0-3", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", @@ -4997,8 +4997,8 @@ }, "node_modules/hardhat": { "version": "3.1.5", - "resolved": "file:../../Repos/hardhat/v-next/hardhat/hardhat-3.1.5.tgz", - "integrity": "sha512-QpZB9kCWc2lrwDzRAL5Rz4kpFua8qAUtCds4vsZqTXqlTUKg/bESaeujM0C2OgvDegejNYLWTYVzZpwNiIyYnw==", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.5.tgz", + "integrity": "sha512-0Z0KI/m6wJYCMZgDK3QuVqR59lSa3aMu6QHKqnbIYXKu/phQ+YFKJZAY4zkUKX21ZjcrrRg25qLUzZw1bO6g/A==", "dev": true, "license": "MIT", "peer": true, diff --git a/package.json b/package.json index 850abf9d160..28dc4e3ff7e 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "glob": "^13.0.0", "globals": "^16.0.0", "graphlib": "^2.1.8", - "hardhat": "file:../../Repos/hardhat/v-next/hardhat/hardhat-3.1.5.tgz", + "hardhat": "^3.1.5", "hardhat-ignore-warnings": "^0.3.0-3", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", From a115cfe69b3ac1b4f6e21c57eae07f7c99a4b4a0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 23 Jan 2026 22:01:45 +0100 Subject: [PATCH 04/91] move the solidity path push to the config hook --- hardhat/hardhat-exposed/core/index.ts | 2 +- .../hardhat-exposed/hook-handlers/config.ts | 4 ++++ hardhat/hardhat-exposed/hook-handlers/hre.ts | 19 +++++++------------ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/hardhat/hardhat-exposed/core/index.ts b/hardhat/hardhat-exposed/core/index.ts index 77efbc5f534..fdbe60bcbc5 100644 --- a/hardhat/hardhat-exposed/core/index.ts +++ b/hardhat/hardhat-exposed/core/index.ts @@ -65,7 +65,7 @@ export function getExposed( } // Helpers -export function getExposedPath(config: HardhatConfig) { +function getExposedPath(config: HardhatConfig) { return path.join(config.paths.root, config.exposed.outDir); } diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts index e5d813d8ad2..3050f928421 100644 --- a/hardhat/hardhat-exposed/hook-handlers/config.ts +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -1,3 +1,4 @@ +import path from 'path'; import type { HardhatConfig, HardhatUserConfig, ConfigurationVariableResolver } from 'hardhat/types/config'; import type { ConfigHooks } from 'hardhat/types/hooks'; @@ -17,6 +18,9 @@ export default async (): Promise> => ({ include: userConfig.exposed?.include ?? ['**/*'], outDir: userConfig.exposed?.outDir ?? 'contracts-exposed', }; + // Add exposed contracts path to Solidity sources, and exclude them from further processing + resolvedConfig.exposed.exclude.push(path.join(resolvedConfig.exposed.outDir, "**", "*")); + resolvedConfig.paths.sources.solidity.push(path.join(resolvedConfig.paths.root, resolvedConfig.exposed.outDir)); return resolvedConfig; }), }); diff --git a/hardhat/hardhat-exposed/hook-handlers/hre.ts b/hardhat/hardhat-exposed/hook-handlers/hre.ts index ae0b9f3d161..be729924ef2 100644 --- a/hardhat/hardhat-exposed/hook-handlers/hre.ts +++ b/hardhat/hardhat-exposed/hook-handlers/hre.ts @@ -1,4 +1,3 @@ -import path from 'path'; import micromatch from 'micromatch'; import type { HardhatRuntimeEnvironmentHooks, HookContext } from 'hardhat/types/hooks'; @@ -6,7 +5,7 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import type { BuildOptions, SolidityBuildSystem } from 'hardhat/types/solidity'; import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; -import { getExposed, getExposedPath, writeExposed } from '../core'; +import { getExposed, writeExposed } from '../core'; const overrideBuild = (context: HookContext, runSuper: SolidityBuildSystem['build']) => @@ -18,15 +17,12 @@ const overrideBuild = if ('reason' in compilationJobsResult) break; // Determine which files to include - const include = (sourceName: string): boolean => - sourceName.startsWith('project/') && - compilationJobsResult.compilationJobsPerFile.has(path.relative('project/', sourceName)) && - context.config.exposed.include.some((p: string) => - micromatch.isMatch(path.relative('project/', sourceName), p), - ) && - !context.config.exposed.exclude.some((p: string) => - micromatch.isMatch(path.relative('project/', sourceName), p), - ); + const include = (sourceName: string): boolean => { + const file = sourceName.replace(/^project\//g, ''); + return compilationJobsResult.compilationJobsPerFile.has(file) && + context.config.exposed.include.some((p: string) => micromatch.isMatch(file, p)) && + !context.config.exposed.exclude.some((p: string) => micromatch.isMatch(file, p)); + } const spinner = createSpinner({ text: `Generation of exposed contracts...`, @@ -76,7 +72,6 @@ const overrideBuild = export default async (): Promise> => ({ created: async (context: HookContext, hre: HardhatRuntimeEnvironment): Promise => { - hre.config.paths.sources.solidity.push(getExposedPath(hre.config)); hre.solidity.build = overrideBuild(context, hre.solidity.build.bind(hre.solidity)); }, }); From 8c1f55c4040b5f145a621080ea3c8c62433967b3 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 27 Jan 2026 09:50:28 +0100 Subject: [PATCH 05/91] test migration --- hardhat.config.ts | 12 +- hardhat/hardhat-exposed/core/index.ts | 12 +- .../hardhat-exposed/hook-handlers/config.ts | 2 +- hardhat/hardhat-exposed/hook-handlers/hre.ts | 11 +- hardhat/hardhat-exposed/plugin.ts | 2 +- .../hook-handlers/hre.ts | 0 .../hook-handlers/network.ts | 34 +++++ .../hardhat-oz-contracts-helpers/plugin.ts | 15 ++ .../type-extensions.ts | 37 +++++ scripts/generate/templates/Arrays.opts.js | 4 +- .../generate/templates/Checkpoints.opts.js | 9 +- scripts/generate/templates/Enumerable.opts.js | 20 +-- scripts/generate/templates/Packing.opts.js | 4 +- scripts/helpers.js | 9 +- test/access/AccessControl.behavior.js | 109 +++++++------- test/access/AccessControl.test.js | 13 +- test/access/Ownable.test.js | 7 +- test/access/Ownable2Step.test.js | 10 +- .../AccessControlDefaultAdminRules.test.js | 25 ++-- .../AccessControlEnumerable.test.js | 16 ++- test/access/manager/AccessManaged.test.js | 24 ++-- test/access/manager/AccessManager.behavior.js | 25 ++-- .../access/manager/AccessManager.predicate.js | 83 +++++------ test/access/manager/AccessManager.test.js | 73 +++++----- test/access/manager/AuthorityUtils.test.js | 11 +- test/account/Account.behavior.js | 28 ++-- test/account/Account.test.js | 31 ++-- test/account/AccountECDSA.test.js | 29 ++-- test/account/AccountEIP7702.test.js | 30 ++-- test/account/AccountERC7913.test.js | 31 ++-- test/account/AccountMultiSigner.test.js | 35 ++--- .../AccountMultiSignerWeighted.test.js | 35 ++--- test/account/AccountP256.test.js | 31 ++-- test/account/AccountRSA.test.js | 31 ++-- test/account/AccountWebAuthn.test.js | 31 ++-- .../AccountEIP7702WithModulesMock.test.js | 34 ++--- .../extensions/AccountERC7579.behavior.js | 89 +++++++----- .../account/extensions/AccountERC7579.test.js | 29 ++-- .../extensions/AccountERC7579Hooked.test.js | 29 ++-- test/account/extensions/ERC7821.behavior.js | 37 +++-- test/account/utils/EIP7702Utils.test.js | 22 +-- test/account/utils/draft-ERC4337Utils.test.js | 46 +++--- test/account/utils/draft-ERC7579Utils.test.js | 31 ++-- test/crosschain/BridgeERC20.behavior.js | 62 ++++---- test/crosschain/BridgeERC20.test.js | 32 +++-- test/crosschain/ERC7786Recipient.test.js | 56 +++++--- test/finance/VestingWallet.behavior.js | 28 ++-- test/finance/VestingWallet.test.js | 21 +-- test/finance/VestingWalletCliff.test.js | 21 +-- test/governance/Governor.test.js | 31 ++-- test/governance/TimelockController.test.js | 23 +-- .../GovernorCountingFractional.test.js | 22 +-- .../GovernorCountingOverridable.test.js | 22 +-- .../extensions/GovernorERC721.test.js | 18 ++- .../extensions/GovernorNoncesKeyed.test.js | 24 ++-- .../GovernorPreventLateQuorum.test.js | 22 +-- .../GovernorProposalGuardian.test.js | 27 ++-- .../GovernorSequentialProposalId.test.js | 28 ++-- .../extensions/GovernorStorage.test.js | 24 ++-- .../extensions/GovernorSuperQuorum.test.js | 22 +-- .../extensions/GovernorTimelockAccess.test.js | 42 +++--- .../GovernorTimelockCompound.test.js | 26 ++-- .../GovernorTimelockControl.test.js | 29 ++-- .../GovernorVotesQuorumFraction.test.js | 22 +-- .../GovernorVotesSuperQuorumFraction.test.js | 22 +-- .../extensions/GovernorWithParams.test.js | 22 +-- test/governance/utils/ERC6372.behavior.js | 11 +- test/governance/utils/Votes.behavior.js | 66 ++++----- test/governance/utils/Votes.test.js | 23 +-- test/governance/utils/VotesExtended.test.js | 23 +-- test/helpers/access-manager.js | 39 ++--- test/helpers/account.js | 22 ++- test/helpers/chains.js | 28 ++-- test/helpers/constants.js | 12 +- test/helpers/deploy.js | 14 -- test/helpers/eip712-types.js | 133 ++++++++++-------- test/helpers/eip712.js | 26 ++-- test/helpers/enums.js | 37 +++-- test/helpers/erc4337.js | 54 ++++--- test/helpers/erc7579.js | 44 ++---- test/helpers/erc7739.js | 20 +-- test/helpers/governance.js | 51 +++---- test/helpers/iterate.js | 68 ++++----- test/helpers/math.js | 15 +- test/helpers/methods.js | 11 +- test/helpers/precompiles.js | 22 ++- test/helpers/random.js | 36 ++--- test/helpers/signers.js | 27 ++-- test/helpers/storage.js | 76 +++++----- test/helpers/strings.js | 8 +- test/helpers/time.js | 56 ++++---- test/helpers/trie.js | 8 +- test/helpers/txpool.js | 12 +- test/metatx/ERC2771Context.test.js | 23 +-- test/metatx/ERC2771Forwarder.test.js | 23 +-- test/proxy/Clones.behaviour.js | 31 ++-- test/proxy/Clones.test.js | 19 +-- test/proxy/ERC1967/ERC1967Proxy.test.js | 12 +- test/proxy/ERC1967/ERC1967Utils.test.js | 30 ++-- test/proxy/Proxy.behaviour.js | 30 ++-- test/proxy/beacon/BeaconProxy.test.js | 16 ++- test/proxy/beacon/UpgradeableBeacon.test.js | 10 +- test/proxy/transparent/ProxyAdmin.test.js | 20 ++- .../TransparentUpgradeableProxy.behaviour.js | 123 +++++++++------- .../TransparentUpgradeableProxy.test.js | 16 ++- test/proxy/utils/Initializable.test.js | 8 +- test/proxy/utils/UUPSUpgradeable.test.js | 21 +-- test/sanity.test.js | 10 +- test/token/ERC1155/ERC1155.behavior.js | 41 +++--- test/token/ERC1155/ERC1155.test.js | 20 +-- .../extensions/ERC1155Burnable.test.js | 10 +- .../extensions/ERC1155Pausable.test.js | 10 +- .../ERC1155/extensions/ERC1155Supply.test.js | 10 +- .../extensions/ERC1155URIStorage.test.js | 10 +- .../token/ERC1155/utils/ERC1155Holder.test.js | 13 +- test/token/ERC1155/utils/ERC1155Utils.test.js | 26 ++-- test/token/ERC20/ERC20.behavior.js | 32 +++-- test/token/ERC20/ERC20.test.js | 30 ++-- test/token/ERC20/extensions/ERC1363.test.js | 20 +-- .../ERC20/extensions/ERC20Burnable.test.js | 14 +- .../ERC20/extensions/ERC20Capped.test.js | 10 +- .../ERC20/extensions/ERC20Crosschain.test.js | 33 ++--- .../ERC20/extensions/ERC20FlashMint.test.js | 22 ++- .../ERC20/extensions/ERC20Pausable.test.js | 44 ++++-- .../ERC20/extensions/ERC20Permit.test.js | 15 +- .../token/ERC20/extensions/ERC20Votes.test.js | 36 +++-- .../ERC20/extensions/ERC20Wrapper.test.js | 34 +++-- test/token/ERC20/extensions/ERC4626.test.js | 69 +++++---- .../extensions/draft-ERC20Bridgeable.test.js | 16 ++- .../draft-ERC20TemporaryApproval.test.js | 16 ++- test/token/ERC20/utils/SafeERC20.test.js | 20 +-- test/token/ERC6909/ERC6909.behavior.js | 13 +- test/token/ERC6909/ERC6909.test.js | 13 +- .../extensions/ERC6909ContentURI.test.js | 15 +- .../extensions/ERC6909Metadata.test.js | 14 +- .../extensions/ERC6909TokenSupply.test.js | 15 +- test/token/ERC721/ERC721.behavior.js | 64 +++++---- test/token/ERC721/ERC721.test.js | 12 +- test/token/ERC721/ERC721Enumerable.test.js | 16 ++- .../ERC721/extensions/ERC721Burnable.test.js | 10 +- .../extensions/ERC721Consecutive.test.js | 13 +- .../ERC721/extensions/ERC721Pausable.test.js | 10 +- .../ERC721/extensions/ERC721Royalty.test.js | 13 +- .../extensions/ERC721URIStorage.test.js | 13 +- .../ERC721/extensions/ERC721Votes.test.js | 17 ++- .../ERC721/extensions/ERC721Wrapper.test.js | 14 +- test/token/ERC721/utils/ERC721Holder.test.js | 6 +- test/token/ERC721/utils/ERC721Utils.test.js | 29 ++-- test/token/common/ERC2981.behavior.js | 13 +- test/utils/Address.test.js | 47 ++++--- test/utils/Arrays.test.js | 23 +-- test/utils/Base58.test.js | 13 +- test/utils/Base64.test.js | 13 +- test/utils/Blockhash.test.js | 22 +-- test/utils/Bytes.test.js | 17 ++- test/utils/CAIP.test.js | 12 +- test/utils/Calldata.test.js | 13 +- test/utils/Context.behavior.js | 17 +-- test/utils/Context.test.js | 12 +- test/utils/Create2.test.js | 15 +- test/utils/LowLevelCall.test.js | 18 ++- test/utils/Memory.test.js | 18 +-- test/utils/Multicall.test.js | 10 +- test/utils/Nonces.behavior.js | 13 +- test/utils/Nonces.test.js | 13 +- test/utils/NoncesKeyed.test.js | 13 +- test/utils/Packing.test.js | 16 ++- test/utils/Panic.test.js | 12 +- test/utils/Pausable.test.js | 10 +- test/utils/RLP.test.js | 17 ++- test/utils/ReentrancyGuard.test.js | 10 +- test/utils/RelayedCall.test.js | 32 +++-- test/utils/ShortStrings.test.js | 10 +- test/utils/SlotDerivation.test.js | 14 +- test/utils/StorageSlot.test.js | 12 +- test/utils/Strings.test.js | 15 +- test/utils/TransientSlot.test.js | 12 +- test/utils/cryptography/ECDSA.test.js | 12 +- test/utils/cryptography/EIP712.test.js | 17 ++- test/utils/cryptography/ERC1271.behavior.js | 18 +-- test/utils/cryptography/ERC7739.test.js | 8 +- test/utils/cryptography/ERC7739Utils.test.js | 15 +- test/utils/cryptography/MerkleProof.test.js | 10 +- .../cryptography/MessageHashUtils.test.js | 16 ++- test/utils/cryptography/P256.test.js | 27 ++-- test/utils/cryptography/RSA.helper.js | 9 +- test/utils/cryptography/RSA.test.js | 22 +-- .../cryptography/SignatureChecker.test.js | 15 +- test/utils/cryptography/TrieProof.test.js | 19 +-- test/utils/draft-InteroperableAddress.test.js | 23 +-- test/utils/introspection/ERC165.test.js | 13 +- .../utils/introspection/ERC165Checker.test.js | 10 +- .../SupportsInterface.behavior.js | 21 ++- test/utils/math/Math.test.js | 21 +-- test/utils/math/SafeCast.test.js | 14 +- test/utils/math/SignedMath.test.js | 14 +- test/utils/structs/BitMap.test.js | 13 +- test/utils/structs/Checkpoints.test.js | 13 +- test/utils/structs/CircularBuffer.test.js | 15 +- test/utils/structs/DoubleEndedQueue.test.js | 12 +- test/utils/structs/EnumerableMap.behavior.js | 13 +- test/utils/structs/EnumerableMap.test.js | 16 ++- test/utils/structs/EnumerableSet.behavior.js | 10 +- test/utils/structs/EnumerableSet.test.js | 16 ++- test/utils/structs/Heap.test.js | 15 +- test/utils/structs/MerkleTree.test.js | 19 +-- test/utils/types/Time.test.js | 18 +-- 207 files changed, 2709 insertions(+), 2231 deletions(-) rename hardhat/{ => hardhat-oz-contracts-helpers}/hook-handlers/hre.ts (100%) create mode 100644 hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts create mode 100644 hardhat/hardhat-oz-contracts-helpers/plugin.ts create mode 100644 hardhat/hardhat-oz-contracts-helpers/type-extensions.ts delete mode 100644 test/helpers/deploy.js diff --git a/hardhat.config.ts b/hardhat.config.ts index 4111681effb..eef5d86fbe7 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -8,6 +8,7 @@ import hardhatMocha from '@nomicfoundation/hardhat-mocha'; import hardhatNetworkHelpers from '@nomicfoundation/hardhat-network-helpers'; import hardhatPredeploy from 'hardhat-predeploy'; import hardhatExposed from './hardhat/hardhat-exposed/plugin.js'; +import hardhatOzContractsHelpers from './hardhat/hardhat-oz-contracts-helpers/plugin.js'; // Parameters import yargs from 'yargs/yargs'; @@ -50,13 +51,7 @@ export default defineConfig({ hardhatPredeploy, // Local plugins hardhatExposed, - // Additional hooks - { - id: '@openzeppelin/contracts', - hookHandlers: { - hre: () => import('./hardhat/hook-handlers/hre.js'), - }, - }, + hardhatOzContractsHelpers, ], paths: { sources: argv.src, @@ -95,11 +90,12 @@ export default defineConfig({ }, }, warnings: { + 'npm/**/*': 'off', + 'test/**/*': 'off', 'contracts-exposed/**/*': { 'code-size': 'off', 'initcode-size': 'off', }, - 'test/**/*': 'off', '*': { 'transient-storage': 'off', default: 'error', diff --git a/hardhat/hardhat-exposed/core/index.ts b/hardhat/hardhat-exposed/core/index.ts index fdbe60bcbc5..366171715b8 100644 --- a/hardhat/hardhat-exposed/core/index.ts +++ b/hardhat/hardhat-exposed/core/index.ts @@ -85,7 +85,12 @@ function getExposedFile( const dirname = path.dirname(fsPath); const relativizePath = (p: string) => - (p.startsWith('project/') ? path.relative(dirname, path.relative('project', p)) : p).replace(/\\/g, '/'); + (p.startsWith('project/') + ? path.relative(dirname, path.relative('project', p)) + : p.startsWith('npm/') + ? path.relative('npm', p).replace(/@[a-zA-Z0-9\.-]+/, '') + : p + ).replace(/\\/g, '/'); const content = getExposedContent( ast, @@ -670,10 +675,7 @@ function* getNeededImports(ast: SourceUnit, deref: ASTDereferencer): Iterable( [ast].concat( [...findAll('ContractDefinition', ast)].flatMap(c => - c.linearizedBaseContracts.map(p => { - const { sourceUnit } = deref.withSourceUnit('ContractDefinition', p); - return sourceUnit; - }), + c.linearizedBaseContracts.map(p => deref.withSourceUnit('ContractDefinition', p).sourceUnit), ), ), ); diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts index 3050f928421..473ba596b38 100644 --- a/hardhat/hardhat-exposed/hook-handlers/config.ts +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -19,7 +19,7 @@ export default async (): Promise> => ({ outDir: userConfig.exposed?.outDir ?? 'contracts-exposed', }; // Add exposed contracts path to Solidity sources, and exclude them from further processing - resolvedConfig.exposed.exclude.push(path.join(resolvedConfig.exposed.outDir, "**", "*")); + resolvedConfig.exposed.exclude.push(path.join(resolvedConfig.exposed.outDir, '**', '*')); resolvedConfig.paths.sources.solidity.push(path.join(resolvedConfig.paths.root, resolvedConfig.exposed.outDir)); return resolvedConfig; }), diff --git a/hardhat/hardhat-exposed/hook-handlers/hre.ts b/hardhat/hardhat-exposed/hook-handlers/hre.ts index be729924ef2..a21c138e2b6 100644 --- a/hardhat/hardhat-exposed/hook-handlers/hre.ts +++ b/hardhat/hardhat-exposed/hook-handlers/hre.ts @@ -19,10 +19,12 @@ const overrideBuild = // Determine which files to include const include = (sourceName: string): boolean => { const file = sourceName.replace(/^project\//g, ''); - return compilationJobsResult.compilationJobsPerFile.has(file) && + return ( + compilationJobsResult.compilationJobsPerFile.has(file) && context.config.exposed.include.some((p: string) => micromatch.isMatch(file, p)) && - !context.config.exposed.exclude.some((p: string) => micromatch.isMatch(file, p)); - } + !context.config.exposed.exclude.some((p: string) => micromatch.isMatch(file, p)) + ); + }; const spinner = createSpinner({ text: `Generation of exposed contracts...`, @@ -60,6 +62,9 @@ const overrideBuild = rootFilePaths.push(...exposed.keys()); } + // Remove duplicates + rootFilePaths = rootFilePaths.filter((value, index, array) => array.indexOf(value) === index); + spinner.stop(); break; diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index f4956fd8543..a91a98eda80 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -1,7 +1,7 @@ import { overrideTask } from 'hardhat/config'; import type { HardhatPlugin } from 'hardhat/types/plugins'; -import type {} from './type-extensions'; +import type {} from './type-extensions.js'; const hardhatExposedPlugin: HardhatPlugin = { id: 'hardhat-exposed', diff --git a/hardhat/hook-handlers/hre.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts similarity index 100% rename from hardhat/hook-handlers/hre.ts rename to hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts new file mode 100644 index 00000000000..c6fb0b2c0de --- /dev/null +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts @@ -0,0 +1,34 @@ +import type { HookContext, NetworkHooks } from 'hardhat/types/hooks'; +import type { ChainType, NetworkConnection } from 'hardhat/types/network'; + +import { impersonate } from '../../../test/helpers/account.js'; +import { getLocalChain } from '../../../test/helpers/chains.js'; +import { getSlot, getAddressInSlot, setSlot } from '../../../test/helpers/storage.js'; +import { clock, clockFromReceipt, increaseBy, increaseTo, duration } from '../../../test/helpers/time.js'; + +export default async (): Promise> => ({ + newConnection: async ( + context: HookContext, + next: (nextContext: HookContext) => Promise>, + ): Promise> => + next(context).then(async connection => + Object.assign(connection, { + helpers: { + chain: await getLocalChain(connection.provider), + impersonate: impersonate(connection), + storage: { + getSlot: getSlot(connection), + getAddressInSlot: getAddressInSlot(connection), + setSlot: setSlot(connection), + }, + time: { + clock: clock(connection), + clockFromReceipt: clockFromReceipt(connection), + increaseBy: increaseBy(connection), + increaseTo: increaseTo(connection), + duration: duration(connection), + }, + }, + }), + ), +}); diff --git a/hardhat/hardhat-oz-contracts-helpers/plugin.ts b/hardhat/hardhat-oz-contracts-helpers/plugin.ts new file mode 100644 index 00000000000..3b213593b60 --- /dev/null +++ b/hardhat/hardhat-oz-contracts-helpers/plugin.ts @@ -0,0 +1,15 @@ +import type { HardhatPlugin } from 'hardhat/types/plugins'; + +import type {} from './type-extensions.js'; + +const hardhatOzContractsHelpers: HardhatPlugin = { + id: 'hardhat-oz-contracts-helpers', + hookHandlers: { + hre: () => import('./hook-handlers/hre.js'), + network: () => import('./hook-handlers/network.js'), + }, + dependencies: () => [import('@nomicfoundation/hardhat-ethers')], + npmPackage: 'hardhat-oz-contracts-helpers', +}; + +export default hardhatOzContractsHelpers; diff --git a/hardhat/hardhat-oz-contracts-helpers/type-extensions.ts b/hardhat/hardhat-oz-contracts-helpers/type-extensions.ts new file mode 100644 index 00000000000..6dddb5e3294 --- /dev/null +++ b/hardhat/hardhat-oz-contracts-helpers/type-extensions.ts @@ -0,0 +1,37 @@ +import 'hardhat/types/network'; + +import type { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/types'; +import type { Contract, TransactionReceipt } from 'ethers'; +import type { InteroperableAddress } from 'interoperable-addresses'; + +type AddressLike = HardhatEthersSigner | Contract | string; + +export interface Chain { + namespace: string; + reference: string; + caip2: string; + erc7930: InteroperableAddress; + toCaip10: (other: AddressLike) => string; + toErc7930: (other: AddressLike) => InteroperableAddress; +} + +export type ClockType = Map<'blocknumber' | 'timestamp', T>; + +declare module 'hardhat/types/network' { + interface NetworkConnection< + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- the ChainTypeT must be declared in the interface but in this scenario it's not used + ChainTypeT extends ChainType | string = DefaultChainType, + > { + helpers: { + chain: Chain; + impersonate: (connection: ChainTypeT) => (account: AddressLike, balance?: bigint) => Promise; + time: { + clock: ClockType<() => Promise>; + clockFromReceipt: ClockType<(receipt: TransactionReceipt) => Promise>; + increaseBy: ClockType<(delay: bigint, mine?: boolean) => Promise>; + increaseTo: ClockType<(to: bigint, mine?: boolean) => Promise>; + duration: ClockType bigint>>; + }; + }; + } +} diff --git a/scripts/generate/templates/Arrays.opts.js b/scripts/generate/templates/Arrays.opts.js index 80efc80b00b..7c76968d30b 100644 --- a/scripts/generate/templates/Arrays.opts.js +++ b/scripts/generate/templates/Arrays.opts.js @@ -1,9 +1,7 @@ -const TYPES = [ +export const TYPES = [ { name: 'address', isValueType: true }, { name: 'bytes32', isValueType: true }, { name: 'uint256', isValueType: true }, { name: 'bytes', isValueType: false }, { name: 'string', isValueType: false }, ]; - -module.exports = { TYPES }; diff --git a/scripts/generate/templates/Checkpoints.opts.js b/scripts/generate/templates/Checkpoints.opts.js index c0920290af5..578cf98e1e3 100644 --- a/scripts/generate/templates/Checkpoints.opts.js +++ b/scripts/generate/templates/Checkpoints.opts.js @@ -1,6 +1,4 @@ // OPTIONS -const VALUE_SIZES = [256, 224, 208, 160]; - const defaultOpts = size => ({ historyTypeName: `Trace${size}`, checkpointTypeName: `Checkpoint${size}`, @@ -12,7 +10,6 @@ const defaultOpts = size => ({ valueFieldName: '_value', }); -module.exports = { - VALUE_SIZES, - OPTS: VALUE_SIZES.map(size => defaultOpts(size)), -}; +export const VALUE_SIZES = [256, 224, 208, 160]; + +export const OPTS = VALUE_SIZES.map(size => defaultOpts(size)); diff --git a/scripts/generate/templates/Enumerable.opts.js b/scripts/generate/templates/Enumerable.opts.js index 3551b9cda7d..61daf7a1ceb 100644 --- a/scripts/generate/templates/Enumerable.opts.js +++ b/scripts/generate/templates/Enumerable.opts.js @@ -1,6 +1,6 @@ -const { capitalize, mapValues } = require('../../helpers'); +import { capitalize, mapValues } from '../../helpers'; -const typeDescr = ({ type, size = 0, memory = false }) => { +export const typeDescr = ({ type, size = 0, memory = false }) => { memory |= size > 0; const name = [type == 'uint256' ? 'Uint' : capitalize(type), size].filter(Boolean).join('x'); @@ -10,19 +10,19 @@ const typeDescr = ({ type, size = 0, memory = false }) => { return { name, type: typeFull, typeLoc, base, size, memory }; }; -const toSetTypeDescr = value => ({ +export const toSetTypeDescr = value => ({ name: value.name + 'Set', value, }); -const toMapTypeDescr = ({ key, value }) => ({ +export const toMapTypeDescr = ({ key, value }) => ({ name: `${key.name}To${value.name}Map`, keySet: toSetTypeDescr(key), key, value, }); -const SET_TYPES = [ +export const SET_TYPES = [ { type: 'bytes32' }, { type: 'bytes4' }, { type: 'address' }, @@ -33,7 +33,7 @@ const SET_TYPES = [ .map(typeDescr) .map(toSetTypeDescr); -const MAP_TYPES = [] +export const MAP_TYPES = [] .concat( // value type maps ['uint256', 'address', 'bytes32'] @@ -46,11 +46,3 @@ const MAP_TYPES = [] ) .map(entry => mapValues(entry, typeDescr)) .map(toMapTypeDescr); - -module.exports = { - SET_TYPES, - MAP_TYPES, - typeDescr, - toSetTypeDescr, - toMapTypeDescr, -}; diff --git a/scripts/generate/templates/Packing.opts.js b/scripts/generate/templates/Packing.opts.js index 893ad6297cf..b52d8b65bb9 100644 --- a/scripts/generate/templates/Packing.opts.js +++ b/scripts/generate/templates/Packing.opts.js @@ -1,3 +1 @@ -module.exports = { - SIZES: [1, 2, 4, 6, 8, 10, 12, 16, 20, 22, 24, 28, 32], -}; +export const SIZES = [1, 2, 4, 6, 8, 10, 12, 16, 20, 22, 24, 28, 32]; diff --git a/scripts/helpers.js b/scripts/helpers.js index d28c0866d1c..15f635eff45 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -1,7 +1,2 @@ -const iterate = require('../test/helpers/iterate'); -const strings = require('../test/helpers/strings'); - -module.exports = { - ...iterate, - ...strings, -}; +export * from '../test/helpers/iterate'; +export * from '../test/helpers/strings'; diff --git a/test/access/AccessControl.behavior.js b/test/access/AccessControl.behavior.js index 52f744759fc..82553942c64 100644 --- a/test/access/AccessControl.behavior.js +++ b/test/access/AccessControl.behavior.js @@ -1,15 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { shouldSupportInterfaces } from '../utils/introspection/SupportsInterface.behavior'; -const time = require('../helpers/time'); +export const DEFAULT_ADMIN_ROLE = ethers.ZeroHash; -const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); - -const DEFAULT_ADMIN_ROLE = ethers.ZeroHash; const ROLE = ethers.id('ROLE'); const OTHER_ROLE = ethers.id('OTHER_ROLE'); -function shouldBehaveLikeAccessControl() { +export function shouldBehaveLikeAccessControl() { beforeEach(async function () { [this.authorized, this.other, this.otherAdmin] = this.accounts; }); @@ -224,7 +222,7 @@ function shouldBehaveLikeAccessControl() { }); } -function shouldBehaveLikeAccessControlEnumerable() { +export function shouldBehaveLikeAccessControlEnumerable() { beforeEach(async function () { [this.authorized, this.other, this.otherAdmin, this.otherAuthorized] = this.accounts; }); @@ -261,13 +259,13 @@ function shouldBehaveLikeAccessControlEnumerable() { }); } -function shouldBehaveLikeAccessControlDefaultAdminRules() { - shouldSupportInterfaces(['AccessControlDefaultAdminRules']); - +export function shouldBehaveLikeAccessControlDefaultAdminRules() { beforeEach(async function () { [this.newDefaultAdmin, this.other] = this.accounts; }); + shouldSupportInterfaces(['AccessControlDefaultAdminRules']); + for (const getter of ['owner', 'defaultAdmin']) { describe(`${getter}()`, function () { it('has a default set to the initial default admin', async function () { @@ -281,7 +279,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { await this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin); // Wait for acceptance - await time.increaseBy.timestamp(this.delay + 1n, false); + await this.helpers.time.increaseBy.timestamp(this.delay + 1n, false); await this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer(); const value = await this.mock[getter](); @@ -310,7 +308,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it(`returns pending admin and schedule ${tag} it passes if not accepted`, async function () { // Wait until schedule + fromSchedule const { schedule: firstSchedule } = await this.mock.pendingDefaultAdmin(); - await time.increaseTo.timestamp(firstSchedule + fromSchedule); + await this.helpers.time.increaseTo.timestamp(firstSchedule + fromSchedule); const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin(); expect(newAdmin).to.equal(this.newDefaultAdmin); @@ -321,7 +319,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it('returns 0 after schedule passes and the transfer was accepted', async function () { // Wait after schedule const { schedule: firstSchedule } = await this.mock.pendingDefaultAdmin(); - await time.increaseTo.timestamp(firstSchedule + 1n, false); + await this.helpers.time.increaseTo.timestamp(firstSchedule + 1n, false); // Accepts await this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer(); @@ -353,7 +351,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it(`returns ${delayTag} delay ${tag} delay schedule passes`, async function () { // Wait until schedule + fromSchedule const { schedule } = await this.mock.pendingDefaultAdminDelay(); - await time.increaseTo.timestamp(schedule + fromSchedule); + await this.helpers.time.increaseTo.timestamp(schedule + fromSchedule); const currentDelay = await this.mock.defaultAdminDelay(); expect(currentDelay).to.equal(expectNew ? newDelay : this.delay); @@ -384,7 +382,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it(`returns ${delayTag} delay ${tag} delay schedule passes`, async function () { // Wait until schedule + fromSchedule const { schedule: firstSchedule } = await this.mock.pendingDefaultAdminDelay(); - await time.increaseTo.timestamp(firstSchedule + fromSchedule); + await this.helpers.time.increaseTo.timestamp(firstSchedule + fromSchedule); const { newDelay, schedule } = await this.mock.pendingDefaultAdminDelay(); expect(newDelay).to.equal(expectedDelay); @@ -396,7 +394,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('defaultAdminDelayIncreaseWait()', function () { it('should return 5 days (default)', async function () { - expect(await this.mock.defaultAdminDelayIncreaseWait()).to.equal(time.duration.days(5)); + expect(await this.mock.defaultAdminDelayIncreaseWait()).to.equal(this.helpers.time.duration.days(5)); }); }); @@ -435,10 +433,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('when there is no pending delay nor pending admin transfer', function () { it('should set pending default admin and schedule', async function () { - const nextBlockTimestamp = (await time.clock.timestamp()) + 1n; + const nextBlockTimestamp = (await this.helpers.time.clock.timestamp()) + 1n; const acceptSchedule = nextBlockTimestamp + this.delay; - await time.increaseTo.timestamp(nextBlockTimestamp, false); // set timestamp but don't mine the block yet + await this.helpers.time.increaseTo.timestamp(nextBlockTimestamp, false); // set timestamp but don't mine the block yet await expect(this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin)) .to.emit(this.mock, 'DefaultAdminTransferScheduled') .withArgs(this.newDefaultAdmin, acceptSchedule); @@ -452,7 +450,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('when there is a pending admin transfer', function () { beforeEach('sets a pending default admin transfer', async function () { await this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin); - this.acceptSchedule = (await time.clock.timestamp()) + this.delay; + this.acceptSchedule = (await this.helpers.time.clock.timestamp()) + this.delay; }); for (const [fromSchedule, tag] of [ @@ -462,14 +460,14 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { ]) { it(`should be able to begin a transfer again ${tag} acceptSchedule passes`, async function () { // Wait until schedule + fromSchedule - await time.increaseTo.timestamp(this.acceptSchedule + fromSchedule, false); + await this.helpers.time.increaseTo.timestamp(this.acceptSchedule + fromSchedule, false); // defaultAdmin changes its mind and begins again to another address await expect(this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.other)).to.emit( this.mock, 'DefaultAdminTransferCanceled', // Cancellation is always emitted since it was never accepted ); - const newSchedule = (await time.clock.timestamp()) + this.delay; + const newSchedule = (await this.helpers.time.clock.timestamp()) + this.delay; const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin(); expect(newAdmin).to.equal(this.other); expect(schedule).to.equal(newSchedule); @@ -478,7 +476,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it('should not emit a cancellation event if the new default admin accepted', async function () { // Wait until the acceptSchedule has passed - await time.increaseTo.timestamp(this.acceptSchedule + 1n, false); + await this.helpers.time.increaseTo.timestamp(this.acceptSchedule + 1n, false); // Accept and restart await this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer(); @@ -490,7 +488,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { }); describe('when there is a pending delay', function () { - const newDelay = time.duration.hours(3); + const newDelay = 3n * 3600n; beforeEach('schedule a delay change', async function () { await this.mock.connect(this.defaultAdmin).changeDefaultAdminDelay(newDelay); @@ -507,7 +505,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { } delay and apply it to next default admin transfer schedule ${schedulePassed} effectSchedule passed`, async function () { // Wait until the expected fromSchedule time const nextBlockTimestamp = this.effectSchedule + fromSchedule; - await time.increaseTo.timestamp(nextBlockTimestamp, false); + await this.helpers.time.increaseTo.timestamp(nextBlockTimestamp, false); // Start the new default admin transfer and get its schedule const expectedDelay = expectNewDelay ? newDelay : this.delay; @@ -528,11 +526,11 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('accepts transfer admin', function () { beforeEach(async function () { await this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin); - this.acceptSchedule = (await time.clock.timestamp()) + this.delay; + this.acceptSchedule = (await this.helpers.time.clock.timestamp()) + this.delay; }); it('should revert if caller is not pending default admin', async function () { - await time.increaseTo.timestamp(this.acceptSchedule + 1n, false); + await this.helpers.time.increaseTo.timestamp(this.acceptSchedule + 1n, false); await expect(this.mock.connect(this.other).acceptDefaultAdminTransfer()) .to.be.revertedWithCustomError(this.mock, 'AccessControlInvalidDefaultAdmin') .withArgs(this.other); @@ -540,7 +538,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('when caller is pending default admin and delay has passed', function () { beforeEach(async function () { - await time.increaseTo.timestamp(this.acceptSchedule + 1n, false); + await this.helpers.time.increaseTo.timestamp(this.acceptSchedule + 1n, false); }); it('accepts a transfer and changes default admin', async function () { @@ -569,7 +567,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { [0n, 'equal'], ]) { it(`should revert if block.timestamp is ${tag} to schedule`, async function () { - await time.increaseTo.timestamp(this.acceptSchedule + fromSchedule, false); + await this.helpers.time.increaseTo.timestamp(this.acceptSchedule + fromSchedule, false); await expect(this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer()) .to.be.revertedWithCustomError(this.mock, 'AccessControlEnforcedDefaultAdminDelay') .withArgs(this.acceptSchedule); @@ -588,7 +586,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('when there is a pending default admin transfer', function () { beforeEach(async function () { await this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(this.newDefaultAdmin); - this.acceptSchedule = (await time.clock.timestamp()) + this.delay; + this.acceptSchedule = (await this.helpers.time.clock.timestamp()) + this.delay; }); for (const [fromSchedule, tag] of [ @@ -598,7 +596,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { ]) { it(`resets pending default admin and schedule ${tag} transfer schedule passes`, async function () { // Advance until passed delay - await time.increaseTo.timestamp(this.acceptSchedule + fromSchedule, false); + await this.helpers.time.increaseTo.timestamp(this.acceptSchedule + fromSchedule, false); await expect(this.mock.connect(this.defaultAdmin).cancelDefaultAdminTransfer()).to.emit( this.mock, @@ -615,7 +613,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { await this.mock.connect(this.defaultAdmin).cancelDefaultAdminTransfer(); // Advance until passed delay - await time.increaseTo.timestamp(this.acceptSchedule + 1n, false); + await this.helpers.time.increaseTo.timestamp(this.acceptSchedule + 1n, false); // Previous pending default admin should not be able to accept after cancellation. await expect(this.mock.connect(this.newDefaultAdmin).acceptDefaultAdminTransfer()) @@ -641,18 +639,18 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('renounces admin', function () { beforeEach(async function () { await this.mock.connect(this.defaultAdmin).beginDefaultAdminTransfer(ethers.ZeroAddress); - this.expectedSchedule = (await time.clock.timestamp()) + this.delay; + this.expectedSchedule = (await this.helpers.time.clock.timestamp()) + this.delay; }); it('reverts if caller is not default admin', async function () { - await time.increaseBy.timestamp(this.delay + 1n, false); + await this.helpers.time.increaseBy.timestamp(this.delay + 1n, false); await expect( this.mock.connect(this.defaultAdmin).renounceRole(DEFAULT_ADMIN_ROLE, this.other), ).to.be.revertedWithCustomError(this.mock, 'AccessControlBadConfirmation'); }); it("renouncing the admin role when not an admin doesn't affect the schedule", async function () { - await time.increaseBy.timestamp(this.delay + 1n, false); + await this.helpers.time.increaseBy.timestamp(this.delay + 1n, false); await this.mock.connect(this.other).renounceRole(DEFAULT_ADMIN_ROLE, this.other); const { newAdmin, schedule } = await this.mock.pendingDefaultAdmin(); @@ -661,7 +659,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { }); it('keeps defaultAdmin consistent with hasRole if another non-defaultAdmin user renounces the DEFAULT_ADMIN_ROLE', async function () { - await time.increaseBy.timestamp(this.delay + 1n, false); + await this.helpers.time.increaseBy.timestamp(this.delay + 1n, false); // This passes because it's a noop await this.mock.connect(this.other).renounceRole(DEFAULT_ADMIN_ROLE, this.other); @@ -671,7 +669,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { }); it('renounces role', async function () { - await time.increaseBy.timestamp(this.delay + 1n, false); + await this.helpers.time.increaseBy.timestamp(this.delay + 1n, false); await expect(this.mock.connect(this.defaultAdmin).renounceRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)) .to.emit(this.mock, 'RoleRevoked') .withArgs(DEFAULT_ADMIN_ROLE, this.defaultAdmin, this.defaultAdmin); @@ -686,7 +684,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { }); it('allows to recover access using the internal _grantRole', async function () { - await time.increaseBy.timestamp(this.delay + 1n, false); + await this.helpers.time.increaseBy.timestamp(this.delay + 1n, false); await this.mock.connect(this.defaultAdmin).renounceRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin); await expect(this.mock.connect(this.defaultAdmin).$_grantRole(DEFAULT_ADMIN_ROLE, this.other)) @@ -700,7 +698,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { [0n, 'equal'], ]) { it(`reverts if block.timestamp is ${tag} to schedule`, async function () { - await time.increaseBy.timestamp(this.delay + fromSchedule, false); + await this.helpers.time.increaseBy.timestamp(this.delay + fromSchedule, false); await expect(this.mock.connect(this.defaultAdmin).renounceRole(DEFAULT_ADMIN_ROLE, this.defaultAdmin)) .to.be.revertedWithCustomError(this.mock, 'AccessControlEnforcedDefaultAdminDelay') .withArgs(this.expectedSchedule); @@ -711,15 +709,15 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('changes delay', function () { it('reverts if called by non default admin accounts', async function () { - await expect(this.mock.connect(this.other).changeDefaultAdminDelay(time.duration.hours(4))) + await expect(this.mock.connect(this.other).changeDefaultAdminDelay(this.helpers.time.duration.hours(4))) .to.be.revertedWithCustomError(this.mock, 'AccessControlUnauthorizedAccount') .withArgs(this.other, DEFAULT_ADMIN_ROLE); }); for (const [delayDifference, delayChangeType] of [ - [time.duration.hours(-1), 'decreased'], - [time.duration.hours(1), 'increased'], - [time.duration.days(5), 'increased to more than 5 days'], + [-3600n, 'decreased'], + [3600n, 'increased'], + [5n * 86400n, 'increased to more than 5 days'], ]) { describe(`when the delay is ${delayChangeType}`, function () { beforeEach(function () { @@ -732,10 +730,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { const minWait = capWait < this.newDefaultAdminDelay ? capWait : this.newDefaultAdminDelay; const changeDelay = this.newDefaultAdminDelay <= this.delay ? this.delay - this.newDefaultAdminDelay : minWait; - const nextBlockTimestamp = (await time.clock.timestamp()) + 1n; + const nextBlockTimestamp = (await this.helpers.time.clock.timestamp()) + 1n; const effectSchedule = nextBlockTimestamp + changeDelay; - await time.increaseTo.timestamp(nextBlockTimestamp, false); + await this.helpers.time.increaseTo.timestamp(nextBlockTimestamp, false); // Begins the change await expect(this.mock.connect(this.defaultAdmin).changeDefaultAdminDelay(this.newDefaultAdminDelay)) @@ -764,10 +762,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { // Wait until schedule + fromSchedule const { schedule: firstSchedule } = await this.mock.pendingDefaultAdminDelay(); const nextBlockTimestamp = firstSchedule + fromSchedule; - await time.increaseTo.timestamp(nextBlockTimestamp, false); + await this.helpers.time.increaseTo.timestamp(nextBlockTimestamp, false); // Calculate expected values - const anotherNewDefaultAdminDelay = this.newDefaultAdminDelay + time.duration.hours(2); + const anotherNewDefaultAdminDelay = this.newDefaultAdminDelay + this.helpers.time.duration.hours(2); const capWait = await this.mock.defaultAdminDelayIncreaseWait(); const minWait = capWait < anotherNewDefaultAdminDelay ? capWait : anotherNewDefaultAdminDelay; const effectSchedule = nextBlockTimestamp + minWait; @@ -787,10 +785,10 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it(`should ${emit} a cancellation event ${tag} the delay schedule passes`, async function () { // Wait until schedule + fromSchedule const { schedule: firstSchedule } = await this.mock.pendingDefaultAdminDelay(); - await time.increaseTo.timestamp(firstSchedule + fromSchedule, false); + await this.helpers.time.increaseTo.timestamp(firstSchedule + fromSchedule, false); // Default admin changes its mind and begins another delay change - const anotherNewDefaultAdminDelay = this.newDefaultAdminDelay + time.duration.hours(2); + const anotherNewDefaultAdminDelay = this.newDefaultAdminDelay + this.helpers.time.duration.hours(2); const expected = expect( this.mock.connect(this.defaultAdmin).changeDefaultAdminDelay(anotherNewDefaultAdminDelay), @@ -816,7 +814,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { describe('when there is a pending delay', function () { beforeEach('set pending delay', async function () { - await this.mock.connect(this.defaultAdmin).changeDefaultAdminDelay(time.duration.hours(12)); + await this.mock.connect(this.defaultAdmin).changeDefaultAdminDelay(this.helpers.time.duration.hours(12)); }); for (const [fromSchedule, tag] of [ @@ -829,7 +827,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it(`resets pending delay and schedule ${tag} delay change schedule passes`, async function () { // Wait until schedule + fromSchedule const { schedule: firstSchedule } = await this.mock.pendingDefaultAdminDelay(); - await time.increaseTo.timestamp(firstSchedule + fromSchedule, false); + await this.helpers.time.increaseTo.timestamp(firstSchedule + fromSchedule, false); await this.mock.connect(this.defaultAdmin).rollbackDefaultAdminDelay(); @@ -842,7 +840,7 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { it(`should ${emit} a cancellation event ${tag} the delay schedule passes`, async function () { // Wait until schedule + fromSchedule const { schedule: firstSchedule } = await this.mock.pendingDefaultAdminDelay(); - await time.increaseTo.timestamp(firstSchedule + fromSchedule, false); + await this.helpers.time.increaseTo.timestamp(firstSchedule + fromSchedule, false); const expected = expect(this.mock.connect(this.defaultAdmin).rollbackDefaultAdminDelay()); if (passed) { @@ -865,10 +863,3 @@ function shouldBehaveLikeAccessControlDefaultAdminRules() { }); }); } - -module.exports = { - DEFAULT_ADMIN_ROLE, - shouldBehaveLikeAccessControl, - shouldBehaveLikeAccessControlEnumerable, - shouldBehaveLikeAccessControlDefaultAdminRules, -}; diff --git a/test/access/AccessControl.test.js b/test/access/AccessControl.test.js index 5c70cdc6d7f..c71ac59489f 100644 --- a/test/access/AccessControl.test.js +++ b/test/access/AccessControl.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { DEFAULT_ADMIN_ROLE, shouldBehaveLikeAccessControl } from './AccessControl.behavior'; -const { DEFAULT_ADMIN_ROLE, shouldBehaveLikeAccessControl } = require('./AccessControl.behavior'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const [defaultAdmin, ...accounts] = await ethers.getSigners(); @@ -12,7 +16,8 @@ async function fixture() { describe('AccessControl', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + // write connection to this for use in fixtures + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeAccessControl(); diff --git a/test/access/Ownable.test.js b/test/access/Ownable.test.js index 1fa3f0e27b3..c3e11393281 100644 --- a/test/access/Ownable.test.js +++ b/test/access/Ownable.test.js @@ -1,7 +1,10 @@ import { network } from 'hardhat'; import { expect } from 'chai'; -const { ethers, networkHelpers } = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [owner, other] = await ethers.getSigners(); @@ -11,7 +14,7 @@ async function fixture() { describe('Ownable', function () { beforeEach(async function () { - Object.assign(this, await networkHelpers.loadFixture(fixture)); + Object.assign(this, await loadFixture(fixture)); }); it('emits ownership transfer events during construction', async function () { diff --git a/test/access/Ownable2Step.test.js b/test/access/Ownable2Step.test.js index 5620a249132..aed907bec28 100644 --- a/test/access/Ownable2Step.test.js +++ b/test/access/Ownable2Step.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [owner, accountA, accountB] = await ethers.getSigners(); diff --git a/test/access/extensions/AccessControlDefaultAdminRules.test.js b/test/access/extensions/AccessControlDefaultAdminRules.test.js index 48036fd9bc2..5ce1a458531 100644 --- a/test/access/extensions/AccessControlDefaultAdminRules.test.js +++ b/test/access/extensions/AccessControlDefaultAdminRules.test.js @@ -1,16 +1,19 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const time = require('../../helpers/time'); - -const { +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeAccessControl, shouldBehaveLikeAccessControlDefaultAdminRules, -} = require('../AccessControl.behavior'); +} from '../AccessControl.behavior'; + +const connection = await network.connect(); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const delay = time.duration.hours(10); + const delay = helpers.time.duration.hours(10); const [defaultAdmin, ...accounts] = await ethers.getSigners(); const mock = await ethers.deployContract('$AccessControlDefaultAdminRules', [delay, defaultAdmin]); return { mock, defaultAdmin, delay, accounts }; @@ -18,7 +21,7 @@ async function fixture() { describe('AccessControlDefaultAdminRules', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('initial admin not zero', async function () { @@ -28,5 +31,5 @@ describe('AccessControlDefaultAdminRules', function () { }); shouldBehaveLikeAccessControl(); - shouldBehaveLikeAccessControlDefaultAdminRules(); + shouldBehaveLikeAccessControlDefaultAdminRules({ helpers }); }); diff --git a/test/access/extensions/AccessControlEnumerable.test.js b/test/access/extensions/AccessControlEnumerable.test.js index ea1a8c46f2c..5cda4dba1a1 100644 --- a/test/access/extensions/AccessControlEnumerable.test.js +++ b/test/access/extensions/AccessControlEnumerable.test.js @@ -1,11 +1,15 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { +import { network } from 'hardhat'; +import { DEFAULT_ADMIN_ROLE, shouldBehaveLikeAccessControl, shouldBehaveLikeAccessControlEnumerable, -} = require('../AccessControl.behavior'); +} from '../AccessControl.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const [defaultAdmin, ...accounts] = await ethers.getSigners(); @@ -16,7 +20,7 @@ async function fixture() { describe('AccessControlEnumerable', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeAccessControl(); diff --git a/test/access/manager/AccessManaged.test.js b/test/access/manager/AccessManaged.test.js index d666b5e6dcc..68098c565bf 100644 --- a/test/access/manager/AccessManaged.test.js +++ b/test/access/manager/AccessManaged.test.js @@ -1,9 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; -const { impersonate } = require('../../helpers/account'); -const time = require('../../helpers/time'); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [admin, roleMember, other] = await ethers.getSigners(); @@ -14,7 +16,7 @@ async function fixture() { const anotherAuthority = await ethers.deployContract('$AccessManager', [admin]); const authorityObserveIsConsuming = await ethers.deployContract('$AuthorityObserveIsConsuming'); - await impersonate(authority.target); + await helpers.impersonate(authority.target); const authorityAsSigner = await ethers.getSigner(authority.target); return { @@ -60,7 +62,7 @@ describe('AccessManaged', function () { it('panics in short calldata', async function () { // We avoid adding the `restricted` modifier to the fallback function because other tests may depend on it // being accessible without restrictions. We check for the internal `_checkCanCall` instead. - await expect(this.managed.$_checkCanCall(this.roleMember, '0x1234')).to.be.reverted; + await expect(this.managed.$_checkCanCall(this.roleMember, '0x1234')).to.be.revert(ethers); }); describe('when role is granted with execution delay', function () { @@ -81,18 +83,18 @@ describe('AccessManaged', function () { it('succeeds if the operation is scheduled', async function () { // Arguments - const delay = time.duration.hours(12); + const delay = helpers.time.duration.hours(12); const fn = this.managed.interface.getFunction(this.selector); const calldata = this.managed.interface.encodeFunctionData(fn, []); // Schedule - const scheduledAt = (await time.clock.timestamp()) + 1n; + const scheduledAt = (await helpers.time.clock.timestamp()) + 1n; const when = scheduledAt + delay; - await time.increaseTo.timestamp(scheduledAt, false); + await helpers.time.increaseTo.timestamp(scheduledAt, false); await this.authority.connect(this.roleMember).schedule(this.managed, calldata, when); // Set execution date - await time.increaseTo.timestamp(when, false); + await helpers.time.increaseTo.timestamp(when, false); // Shouldn't revert await this.managed.connect(this.roleMember)[this.selector](); diff --git a/test/access/manager/AccessManager.behavior.js b/test/access/manager/AccessManager.behavior.js index 830700e3762..034a8f9a2e1 100644 --- a/test/access/manager/AccessManager.behavior.js +++ b/test/access/manager/AccessManager.behavior.js @@ -1,6 +1,5 @@ -const { expect } = require('chai'); - -const { +import { expect } from 'chai'; +import { LIKE_COMMON_IS_EXECUTING, LIKE_COMMON_GET_ACCESS, LIKE_COMMON_SCHEDULABLE, @@ -9,14 +8,14 @@ const { testAsDelayedOperation, testAsCanCall, testAsHasRole, -} = require('./AccessManager.predicate'); +} from './AccessManager.predicate'; // ============ ADMIN OPERATION ============ /** * @requires this.{manager,roles,calldata,role} */ -function shouldBehaveLikeDelayedAdminOperation() { +export function shouldBehaveLikeDelayedAdminOperation() { const getAccessPath = LIKE_COMMON_GET_ACCESS; testAsDelayedOperation.mineDelay = true; getAccessPath.requiredRoleIsGranted.roleGrantingIsDelayed.callerHasAnExecutionDelay.afterGrantDelay = @@ -55,7 +54,7 @@ function shouldBehaveLikeDelayedAdminOperation() { /** * @requires this.{manager,roles,calldata,role} */ -function shouldBehaveLikeNotDelayedAdminOperation() { +export function shouldBehaveLikeNotDelayedAdminOperation() { const getAccessPath = LIKE_COMMON_GET_ACCESS; function testScheduleOperation(mineDelay) { @@ -99,7 +98,7 @@ function shouldBehaveLikeNotDelayedAdminOperation() { /** * @requires this.{manager,roles,calldata,role} */ -function shouldBehaveLikeRoleAdminOperation(roleAdmin) { +export function shouldBehaveLikeRoleAdminOperation(roleAdmin) { const getAccessPath = LIKE_COMMON_GET_ACCESS; function afterGrantDelay() { @@ -139,7 +138,7 @@ function shouldBehaveLikeRoleAdminOperation(roleAdmin) { /** * @requires this.{manager,roles,calldata,role} */ -function shouldBehaveLikeAManagedRestrictedOperation() { +export function shouldBehaveLikeAManagedRestrictedOperation() { function revertUnauthorized() { it('reverts as AccessManagedUnauthorized', async function () { await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) @@ -196,7 +195,7 @@ function shouldBehaveLikeAManagedRestrictedOperation() { /** * @requires this.{target,manager,roles,calldata,role} */ -function shouldBehaveLikeASelfRestrictedOperation() { +export function shouldBehaveLikeASelfRestrictedOperation() { function revertUnauthorized() { it('reverts as AccessManagerUnauthorizedAccount', async function () { await expect(this.caller.sendTransaction({ to: this.target, data: this.calldata })) @@ -247,11 +246,3 @@ function shouldBehaveLikeASelfRestrictedOperation() { }, }); } - -module.exports = { - shouldBehaveLikeDelayedAdminOperation, - shouldBehaveLikeNotDelayedAdminOperation, - shouldBehaveLikeRoleAdminOperation, - shouldBehaveLikeAManagedRestrictedOperation, - shouldBehaveLikeASelfRestrictedOperation, -}; diff --git a/test/access/manager/AccessManager.predicate.js b/test/access/manager/AccessManager.predicate.js index 8b4c5f4b642..7970462adf3 100644 --- a/test/access/manager/AccessManager.predicate.js +++ b/test/access/manager/AccessManager.predicate.js @@ -1,14 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { setStorageAt } = require('@nomicfoundation/hardhat-network-helpers'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; -const { EXECUTION_ID_STORAGE_SLOT, EXPIRATION, prepareOperation } = require('../../helpers/access-manager'); -const { impersonate } = require('../../helpers/account'); -const time = require('../../helpers/time'); +import { EXECUTION_ID_STORAGE_SLOT, EXPIRATION, prepareOperation } from '../../helpers/access-manager'; // ============ COMMON PREDICATES ============ -const LIKE_COMMON_IS_EXECUTING = { +export const LIKE_COMMON_IS_EXECUTING = { executing() { it('succeeds', async function () { await this.caller.sendTransaction({ to: this.target, data: this.calldata }); @@ -23,7 +20,7 @@ const LIKE_COMMON_IS_EXECUTING = { }, }; -const LIKE_COMMON_GET_ACCESS = { +export const LIKE_COMMON_GET_ACCESS = { requiredRoleIsGranted: { roleGrantingIsDelayed: { callerHasAnExecutionDelay: { @@ -77,7 +74,7 @@ const LIKE_COMMON_GET_ACCESS = { }, }; -const LIKE_COMMON_SCHEDULABLE = { +export const LIKE_COMMON_SCHEDULABLE = { scheduled: { before() { it('reverts as AccessManagerNotReady', async function () { @@ -117,7 +114,7 @@ const LIKE_COMMON_SCHEDULABLE = { /** * @requires this.{manager,target} */ -function testAsClosable({ closed, open }) { +export function testAsClosable({ closed, open }) { describe('when the manager is closed', function () { beforeEach('close', async function () { await this.manager.$_setTargetClosed(this.target, true); @@ -140,15 +137,15 @@ function testAsClosable({ closed, open }) { /** * @requires this.{delay} */ -function testAsDelay(type, { before, after }) { +export function testAsDelay(type, { before, after }) { beforeEach('define timestamp when delay takes effect', async function () { - const timestamp = await time.clock.timestamp(); + const timestamp = await this.helpers.time.clock.timestamp(); this.delayEffect = timestamp + this.delay; }); describe(`when ${type} delay has not taken effect yet`, function () { beforeEach(`set next block timestamp before ${type} takes effect`, async function () { - await time.increaseTo.timestamp(this.delayEffect - 1n, !!before.mineDelay); + await this.helpers.time.increaseTo.timestamp(this.delayEffect - 1n, !!before.mineDelay); }); before(); @@ -156,7 +153,7 @@ function testAsDelay(type, { before, after }) { describe(`when ${type} delay has taken effect`, function () { beforeEach(`set next block timestamp when ${type} takes effect`, async function () { - await time.increaseTo.timestamp(this.delayEffect, !!after.mineDelay); + await this.helpers.time.increaseTo.timestamp(this.delayEffect, !!after.mineDelay); }); after(); @@ -168,14 +165,13 @@ function testAsDelay(type, { before, after }) { /** * @requires this.{manager,scheduleIn,caller,target,calldata} */ -function testAsSchedulableOperation({ scheduled: { before, after, expired }, notScheduled }) { +export function testAsSchedulableOperation({ scheduled: { before, after, expired }, notScheduled }) { describe('when operation is scheduled', function () { beforeEach('schedule operation', async function () { if (this.caller.target) { - await impersonate(this.caller.target); - this.caller = await ethers.getSigner(this.caller.target); + this.caller = await this.helpers.impersonate(this.caller.target); } - const { operationId, schedule } = await prepareOperation(this.manager, { + const { operationId, schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -187,9 +183,9 @@ function testAsSchedulableOperation({ scheduled: { before, after, expired }, not describe('when operation is not ready for execution', function () { beforeEach('set next block time before operation is ready', async function () { - this.scheduledAt = await time.clock.timestamp(); + this.scheduledAt = await this.helpers.time.clock.timestamp(); const schedule = await this.manager.getSchedule(this.operationId); - await time.increaseTo.timestamp(schedule - 1n, !!before.mineDelay); + await this.helpers.time.increaseTo.timestamp(schedule - 1n, !!before.mineDelay); }); before(); @@ -197,9 +193,9 @@ function testAsSchedulableOperation({ scheduled: { before, after, expired }, not describe('when operation is ready for execution', function () { beforeEach('set next block time when operation is ready for execution', async function () { - this.scheduledAt = await time.clock.timestamp(); + this.scheduledAt = await this.helpers.time.clock.timestamp(); const schedule = await this.manager.getSchedule(this.operationId); - await time.increaseTo.timestamp(schedule, !!after.mineDelay); + await this.helpers.time.increaseTo.timestamp(schedule, !!after.mineDelay); }); after(); @@ -207,9 +203,9 @@ function testAsSchedulableOperation({ scheduled: { before, after, expired }, not describe('when operation has expired', function () { beforeEach('set next block time when operation expired', async function () { - this.scheduledAt = await time.clock.timestamp(); + this.scheduledAt = await this.helpers.time.clock.timestamp(); const schedule = await this.manager.getSchedule(this.operationId); - await time.increaseTo.timestamp(schedule + EXPIRATION, !!expired.mineDelay); + await this.helpers.time.increaseTo.timestamp(schedule + EXPIRATION, !!expired.mineDelay); }); expired(); @@ -231,13 +227,12 @@ function testAsSchedulableOperation({ scheduled: { before, after, expired }, not /** * @requires this.{manager,roles,target,calldata} */ -function testAsRestrictedOperation({ callerIsTheManager: { executing, notExecuting }, callerIsNotTheManager }) { +export function testAsRestrictedOperation({ callerIsTheManager: { executing, notExecuting }, callerIsNotTheManager }) { describe('when the call comes from the manager (msg.sender == manager)', function () { beforeEach('define caller as manager', async function () { this.caller = this.manager; if (this.caller.target) { - await impersonate(this.caller.target); - this.caller = await ethers.getSigner(this.caller.target); + this.caller = await this.helpers.impersonate(this.caller.target); } }); @@ -249,7 +244,7 @@ function testAsRestrictedOperation({ callerIsTheManager: { executing, notExecuti [this.target.target, this.calldata.substring(0, 10)], ), ); - await setStorageAt(this.manager.target, EXECUTION_ID_STORAGE_SLOT, executionId); + await this.networkHelpers.setStorageAt(this.manager.target, EXECUTION_ID_STORAGE_SLOT, executionId); }); executing(); @@ -270,11 +265,11 @@ function testAsRestrictedOperation({ callerIsTheManager: { executing, notExecuti /** * @requires this.{manager,scheduleIn,caller,target,calldata,executionDelay} */ -function testAsDelayedOperation() { +export function testAsDelayedOperation() { describe('with operation delay', function () { describe('when operation delay is greater than execution delay', function () { beforeEach('set operation delay', async function () { - this.operationDelay = this.executionDelay + time.duration.hours(1); + this.operationDelay = this.executionDelay + this.helpers.time.duration.hours(1); await this.manager.$_setTargetAdminDelay(this.target, this.operationDelay); this.scheduleIn = this.operationDelay; // For testAsSchedulableOperation }); @@ -284,7 +279,7 @@ function testAsDelayedOperation() { describe('when operation delay is shorter than execution delay', function () { beforeEach('set operation delay', async function () { - this.operationDelay = this.executionDelay - time.duration.hours(1); + this.operationDelay = this.executionDelay - this.helpers.time.duration.hours(1); await this.manager.$_setTargetAdminDelay(this.target, this.operationDelay); this.scheduleIn = this.executionDelay; // For testAsSchedulableOperation }); @@ -309,7 +304,7 @@ function testAsDelayedOperation() { /** * @requires this.{manager,roles,role,target,calldata} */ -function testAsCanCall({ +export function testAsCanCall({ closed, open: { callerIsTheManager, @@ -335,7 +330,7 @@ function testAsCanCall({ /** * @requires this.{target,calldata,roles,role} */ -function testAsHasRole({ publicRoleIsRequired, specificRoleIsRequired }) { +export function testAsHasRole({ publicRoleIsRequired, specificRoleIsRequired }) { describe('when the function requires the caller to be granted with the PUBLIC_ROLE', function () { beforeEach('set target function role as PUBLIC_ROLE', async function () { this.role = this.roles.PUBLIC; @@ -361,7 +356,7 @@ function testAsHasRole({ publicRoleIsRequired, specificRoleIsRequired }) { /** * @requires this.{manager,role,caller} */ -function testAsGetAccess({ +export function testAsGetAccess({ requiredRoleIsGranted: { roleGrantingIsDelayed: { // Because both grant and execution delay are set within the same $_grantRole call @@ -379,13 +374,13 @@ function testAsGetAccess({ describe('when the required role is granted to the caller', function () { describe('when role granting is delayed', function () { beforeEach('define delay', function () { - this.grantDelay = time.duration.minutes(3); + this.grantDelay = this.helpers.time.duration.minutes(3); this.delay = this.grantDelay; // For testAsDelay }); describe('when caller has an execution delay', function () { beforeEach('set role and delay', async function () { - this.executionDelay = time.duration.hours(10); + this.executionDelay = this.helpers.time.duration.hours(10); this.delay = this.grantDelay; await this.manager.$_grantRole(this.role.id, this.caller, this.grantDelay, this.executionDelay); }); @@ -410,7 +405,7 @@ function testAsGetAccess({ describe('when caller has an execution delay', function () { beforeEach('set role and delay', async function () { - this.executionDelay = time.duration.hours(10); + this.executionDelay = this.helpers.time.duration.hours(10); await this.manager.$_grantRole(this.role.id, this.caller, this.grantDelay, this.executionDelay); }); @@ -440,17 +435,3 @@ function testAsGetAccess({ requiredRoleIsNotGranted(); }); } - -module.exports = { - LIKE_COMMON_IS_EXECUTING, - LIKE_COMMON_GET_ACCESS, - LIKE_COMMON_SCHEDULABLE, - testAsClosable, - testAsDelay, - testAsSchedulableOperation, - testAsRestrictedOperation, - testAsDelayedOperation, - testAsCanCall, - testAsHasRole, - testAsGetAccess, -}; diff --git a/test/access/manager/AccessManager.test.js b/test/access/manager/AccessManager.test.js index 7726831b268..f82e616d7f3 100644 --- a/test/access/manager/AccessManager.test.js +++ b/test/access/manager/AccessManager.test.js @@ -1,13 +1,9 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT48 } from '../../helpers/constants'; +import { selector } from '../../helpers/methods'; -const { impersonate } = require('../../helpers/account'); -const { MAX_UINT48 } = require('../../helpers/constants'); -const { selector } = require('../../helpers/methods'); -const time = require('../../helpers/time'); - -const { +import { buildBaseRoles, formatAccess, EXPIRATION, @@ -16,17 +12,17 @@ const { CONSUMING_SCHEDULE_STORAGE_SLOT, prepareOperation, hashOperation, -} = require('../../helpers/access-manager'); +} from '../../helpers/access-manager'; -const { +import { shouldBehaveLikeDelayedAdminOperation, shouldBehaveLikeNotDelayedAdminOperation, shouldBehaveLikeRoleAdminOperation, shouldBehaveLikeAManagedRestrictedOperation, shouldBehaveLikeASelfRestrictedOperation, -} = require('./AccessManager.behavior'); +} from './AccessManager.behavior'; -const { +import { LIKE_COMMON_SCHEDULABLE, testAsClosable, testAsDelay, @@ -34,7 +30,14 @@ const { testAsCanCall, testAsHasRole, testAsGetAccess, -} = require('./AccessManager.predicate'); +} from './AccessManager.predicate'; + +const connection = await network.connect(); +const { + ethers, + helpers: { impersonate, time }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const [admin, roleAdmin, roleGuardian, member, user, other] = await ethers.getSigners(); @@ -99,7 +102,7 @@ async function fixture() { // defined as constants. describe('AccessManager', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); describe('during construction', function () { @@ -735,7 +738,7 @@ describe('AccessManager', function () { this.calldata = this.target.interface.encodeFunctionData(fnRestricted, []); this.delay = time.duration.days(10); - const { operationId, schedule } = await prepareOperation(this.manager, { + const { operationId, schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1798,7 +1801,7 @@ describe('AccessManager', function () { testAsCanCall({ closed() { it('reverts as AccessManagerUnauthorizedCall', async function () { - const { schedule } = await prepareOperation(this.manager, { + const { schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1816,7 +1819,7 @@ describe('AccessManager', function () { }, notExecuting() { it('reverts as AccessManagerUnauthorizedCall', async function () { - const { schedule } = await prepareOperation(this.manager, { + const { schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1878,7 +1881,7 @@ describe('AccessManager', function () { roleGrantingIsNotDelayed: { callerHasAnExecutionDelay() { it('succeeds', async function () { - const { schedule } = await prepareOperation(this.manager, { + const { schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1900,7 +1903,7 @@ describe('AccessManager', function () { }, requiredRoleIsNotGranted() { it('reverts as AccessManagerUnauthorizedCall', async function () { - const { schedule } = await prepareOperation(this.manager, { + const { schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1918,7 +1921,7 @@ describe('AccessManager', function () { }); it('schedules an operation at the specified execution date if it is larger than caller execution delay', async function () { - const { operationId, scheduledAt, schedule } = await prepareOperation(this.manager, { + const { operationId, scheduledAt, schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1954,7 +1957,7 @@ describe('AccessManager', function () { expect(await this.manager.getNonce(expectedOperationId)).to.equal('0'); // Schedule - const op1 = await prepareOperation(this.manager, { + const op1 = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1973,7 +1976,7 @@ describe('AccessManager', function () { expect(await this.manager.getNonce(expectedOperationId)).to.equal('1'); // Schedule again - const op2 = await prepareOperation(this.manager, { + const op2 = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -1992,7 +1995,7 @@ describe('AccessManager', function () { const executionDelay = time.duration.weeks(1) + this.delay; await this.manager.$_grantRole(this.role.id, this.caller, 0, executionDelay); - const { schedule } = await prepareOperation(this.manager, { + const { schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -2005,7 +2008,7 @@ describe('AccessManager', function () { }); it('reverts if an operation is already schedule', async function () { - const op1 = await prepareOperation(this.manager, { + const op1 = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -2014,7 +2017,7 @@ describe('AccessManager', function () { await op1.schedule(); - const op2 = await prepareOperation(this.manager, { + const op2 = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -2030,28 +2033,28 @@ describe('AccessManager', function () { const calldata = '0x1234'; // 2 bytes // Managed contract - const op1 = await prepareOperation(this.manager, { + const op1 = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: calldata, delay: this.delay, }); - await expect(op1.schedule()).to.be.revertedWithoutReason(); + await expect(op1.schedule()).to.be.revertedWithoutReason(ethers); // Manager contract - const op2 = await prepareOperation(this.manager, { + const op2 = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.manager, calldata: calldata, delay: this.delay, }); - await expect(op2.schedule()).to.be.revertedWithoutReason(); + await expect(op2.schedule()).to.be.revertedWithoutReason(ethers); }); it('reverts scheduling an unknown operation to the manager', async function () { const calldata = '0x12345678'; - const { schedule } = await prepareOperation(this.manager, { + const { schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.manager, calldata, @@ -2176,7 +2179,7 @@ describe('AccessManager', function () { const delay = time.duration.hours(4); await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // Execution delay is needed so the operation is consumed - const { operationId, schedule } = await prepareOperation(this.manager, { + const { operationId, schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -2197,7 +2200,7 @@ describe('AccessManager', function () { // give caller an execution delay await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); - const { operationId, schedule } = await prepareOperation(this.manager, { + const { operationId, schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -2227,7 +2230,7 @@ describe('AccessManager', function () { const delay = time.duration.hours(2); await this.manager.$_grantRole(this.role.id, this.caller, 0, 1); // Execution delay is needed so the operation is consumed - const { operationId, schedule } = await prepareOperation(this.manager, { + const { operationId, schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, @@ -2374,7 +2377,7 @@ describe('AccessManager', function () { }); it('cancels an operation and resets schedule', async function () { - const { operationId, schedule } = await prepareOperation(this.manager, { + const { operationId, schedule } = await prepareOperation.bind(this)(this.manager, { caller: this.caller, target: this.target, calldata: this.calldata, diff --git a/test/access/manager/AuthorityUtils.test.js b/test/access/manager/AuthorityUtils.test.js index 465f617adca..f4cecffb167 100644 --- a/test/access/manager/AuthorityUtils.test.js +++ b/test/access/manager/AuthorityUtils.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT32, MAX_UINT64 } from '../../helpers/constants'; -const { MAX_UINT32, MAX_UINT64 } = require('../../helpers/constants'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [user, other] = await ethers.getSigners(); diff --git a/test/account/Account.behavior.js b/test/account/Account.behavior.js index 5e993e46a2e..2fd57258b25 100644 --- a/test/account/Account.behavior.js +++ b/test/account/Account.behavior.js @@ -1,14 +1,13 @@ -const { ethers, predeploy } = require('hardhat'); -const { expect } = require('chai'); -const { impersonate } = require('../helpers/account'); -const { SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILURE } = require('../helpers/erc4337'); -const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILURE } from '../helpers/erc4337'; +import { shouldSupportInterfaces } from '../utils/introspection/SupportsInterface.behavior'; -function shouldBehaveLikeAccountCore() { +export function shouldBehaveLikeAccountCore() { describe('entryPoint', function () { it('should return the canonical entrypoint', async function () { await this.mock.deploy(); - await expect(this.mock.entryPoint()).to.eventually.equal(predeploy.entrypoint.v09); + await expect(this.mock.entryPoint()).to.eventually.equal(this.ethers.predeploy.entrypoint.v09); }); }); @@ -30,7 +29,9 @@ function shouldBehaveLikeAccountCore() { describe('when the caller is the canonical entrypoint', function () { beforeEach(async function () { - this.mockFromEntrypoint = this.mock.connect(await impersonate(predeploy.entrypoint.v09.target)); + this.mockFromEntrypoint = this.mock.connect( + await this.helpers.impersonate(this.ethers.predeploy.entrypoint.v09.target), + ); }); it('should return SIG_VALIDATION_SUCCESS if the signature is valid', async function () { @@ -59,7 +60,7 @@ function shouldBehaveLikeAccountCore() { await expect( this.mockFromEntrypoint.validateUserOp(operation.packed, operation.hash(), value), - ).to.changeEtherBalances([this.mock, predeploy.entrypoint.v09], [-value, value]); + ).to.changeEtherBalances(this.ethers, [this.mock, this.ethers.predeploy.entrypoint.v09], [-value, value]); }); }); }); @@ -70,6 +71,7 @@ function shouldBehaveLikeAccountCore() { const value = 42n; await expect(this.other.sendTransaction({ to: this.mock, value })).to.changeEtherBalances( + this.ethers, [this.other, this.mock], [-value, value], ); @@ -77,7 +79,7 @@ function shouldBehaveLikeAccountCore() { }); } -function shouldBehaveLikeAccountHolder() { +export function shouldBehaveLikeAccountHolder() { describe('onReceived', function () { beforeEach(async function () { await this.mock.deploy(); @@ -91,7 +93,7 @@ function shouldBehaveLikeAccountHolder() { const data = '0x12345678'; beforeEach(async function () { - this.token = await ethers.deployContract('$ERC1155', ['https://somedomain.com/{id}.json']); + this.token = await this.ethers.deployContract('$ERC1155', ['https://somedomain.com/{id}.json']); await this.token.$_mintBatch(this.other, ids, values, '0x'); }); @@ -128,7 +130,7 @@ function shouldBehaveLikeAccountHolder() { const tokenId = 1n; beforeEach(async function () { - this.token = await ethers.deployContract('$ERC721', ['Some NFT', 'SNFT']); + this.token = await this.ethers.deployContract('$ERC721', ['Some NFT', 'SNFT']); await this.token.$_mint(this.other, tokenId); }); @@ -140,5 +142,3 @@ function shouldBehaveLikeAccountHolder() { }); }); } - -module.exports = { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder }; diff --git a/test/account/Account.test.js b/test/account/Account.test.js index 4bd402e05b5..2dfc1d8ed38 100644 --- a/test/account/Account.test.js +++ b/test/account/Account.test.js @@ -1,14 +1,17 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { PackedUserOperation } = require('../helpers/eip712-types'); -const { NonNativeSigner } = require('../helpers/signers'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { NonNativeSigner } from '../helpers/signers'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { // EOAs and environment @@ -19,11 +22,11 @@ async function fixture() { const signer = new NonNativeSigner({ sign: hash => ({ serialized: hash }) }); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountMock', ['Account', '1']); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { name: 'Account', version: '1', chainId: entrypointDomain.chainId, verifyingContract: mock.address }; @@ -38,7 +41,7 @@ async function fixture() { describe('Account', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeAccountCore(); diff --git a/test/account/AccountECDSA.test.js b/test/account/AccountECDSA.test.js index 472c68433b3..e228dcce199 100644 --- a/test/account/AccountECDSA.test.js +++ b/test/account/AccountECDSA.test.js @@ -1,13 +1,16 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { PackedUserOperation } = require('../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { // EOAs and environment @@ -18,11 +21,11 @@ async function fixture() { const signer = ethers.Wallet.createRandom(); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountECDSAMock', [signer, 'AccountECDSA', '1']); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { @@ -42,7 +45,7 @@ async function fixture() { describe('AccountECDSA', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeAccountCore(); diff --git a/test/account/AccountEIP7702.test.js b/test/account/AccountEIP7702.test.js index d832e68771f..dcb2cc862fb 100644 --- a/test/account/AccountEIP7702.test.js +++ b/test/account/AccountEIP7702.test.js @@ -1,13 +1,16 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { PackedUserOperation } = require('../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { // EOAs and environment @@ -18,12 +21,11 @@ async function fixture() { const signer = ethers.Wallet.createRandom(ethers.provider); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountEIP7702Mock', ['AccountEIP7702Mock', '1'], { eip7702signer: signer }); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); - + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { name: 'AccountEIP7702Mock', @@ -42,7 +44,7 @@ async function fixture() { describe('AccountEIP7702', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeAccountCore(); diff --git a/test/account/AccountERC7913.test.js b/test/account/AccountERC7913.test.js index 2807b6cd6dc..a391b71e0ad 100644 --- a/test/account/AccountERC7913.test.js +++ b/test/account/AccountERC7913.test.js @@ -1,14 +1,17 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, WebAuthnSigningKey } = require('../helpers/signers'); -const { PackedUserOperation } = require('../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, WebAuthnSigningKey } from '../helpers/signers'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; // Prepare signer in advance (RSA are long to initialize) const signerECDSA = ethers.Wallet.createRandom(); @@ -28,9 +31,9 @@ async function fixture() { const verifierWebAuthn = await ethers.deployContract('ERC7913WebAuthnVerifier'); // ERC-4337 env - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); await helper.wait(); - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); const domain = { name: 'AccountERC7913', version: '1', chainId: entrypointDomain.chainId }; // Missing verifyingContract, const makeMock = signer => @@ -61,7 +64,7 @@ async function fixture() { describe('AccountERC7913', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); // Using ECDSA key as verifier diff --git a/test/account/AccountMultiSigner.test.js b/test/account/AccountMultiSigner.test.js index 9f4042a8eb0..6fca664747f 100644 --- a/test/account/AccountMultiSigner.test.js +++ b/test/account/AccountMultiSigner.test.js @@ -1,16 +1,19 @@ -const { ethers, predeploy } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, MultiERC7913SigningKey } = require('../helpers/signers'); -const { MAX_UINT64 } = require('../helpers/constants'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); -const { PackedUserOperation } = require('../helpers/eip712-types'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT64 } from '../helpers/constants'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, MultiERC7913SigningKey } from '../helpers/signers'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; // Prepare signers in advance (RSA are long to initialize) const signerECDSA1 = ethers.Wallet.createRandom(); @@ -31,9 +34,9 @@ async function fixture() { const verifierRSA = await ethers.deployContract('ERC7913RSAVerifier'); // ERC-4337 env - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); await helper.wait(); - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); const domain = { name: 'AccountMultiSigner', version: '1', chainId: entrypointDomain.chainId }; // Missing verifyingContract const makeMock = (signers, threshold) => @@ -69,7 +72,7 @@ async function fixture() { describe('AccountMultiSigner', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); describe('Multi ECDSA signers with threshold=1', function () { diff --git a/test/account/AccountMultiSignerWeighted.test.js b/test/account/AccountMultiSignerWeighted.test.js index c954d421a7a..599b8017949 100644 --- a/test/account/AccountMultiSignerWeighted.test.js +++ b/test/account/AccountMultiSignerWeighted.test.js @@ -1,16 +1,19 @@ -const { ethers, predeploy } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, MultiERC7913SigningKey } = require('../helpers/signers'); -const { PackedUserOperation } = require('../helpers/eip712-types'); -const { MAX_UINT64 } = require('../helpers/constants'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT64 } from '../helpers/constants'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { NonNativeSigner, P256SigningKey, RSASHA256SigningKey, MultiERC7913SigningKey } from '../helpers/signers'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; // Prepare signers in advance (RSA are long to initialize) const signerECDSA1 = ethers.Wallet.createRandom(); @@ -31,9 +34,9 @@ async function fixture() { const verifierRSA = await ethers.deployContract('ERC7913RSAVerifier'); // ERC-4337 env - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); await helper.wait(); - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); const domain = { name: 'AccountMultiSignerWeighted', version: '1', chainId: entrypointDomain.chainId }; // Missing verifyingContract const makeMock = (signers, weights, threshold) => @@ -71,7 +74,7 @@ async function fixture() { describe('AccountMultiSignerWeighted', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); describe('Weighted signers with equal weights (1, 1, 1) and threshold=2', function () { diff --git a/test/account/AccountP256.test.js b/test/account/AccountP256.test.js index cd69abcec95..b23a89b598e 100644 --- a/test/account/AccountP256.test.js +++ b/test/account/AccountP256.test.js @@ -1,14 +1,17 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { NonNativeSigner, P256SigningKey } = require('../helpers/signers'); -const { PackedUserOperation } = require('../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { NonNativeSigner, P256SigningKey } from '../helpers/signers'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { // EOAs and environment @@ -19,7 +22,7 @@ async function fixture() { const signer = new NonNativeSigner(P256SigningKey.random()); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountP256Mock', [ signer.signingKey.publicKey.qx, signer.signingKey.publicKey.qy, @@ -28,7 +31,7 @@ async function fixture() { ]); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { @@ -48,7 +51,7 @@ async function fixture() { describe('AccountP256', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeAccountCore(); diff --git a/test/account/AccountRSA.test.js b/test/account/AccountRSA.test.js index 057691719bc..9e8a30c73f3 100644 --- a/test/account/AccountRSA.test.js +++ b/test/account/AccountRSA.test.js @@ -1,14 +1,17 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { NonNativeSigner, RSASHA256SigningKey } = require('../helpers/signers'); -const { PackedUserOperation } = require('../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { NonNativeSigner, RSASHA256SigningKey } from '../helpers/signers'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { // EOAs and environment @@ -19,7 +22,7 @@ async function fixture() { const signer = new NonNativeSigner(RSASHA256SigningKey.random()); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountRSAMock', [ signer.signingKey.publicKey.e, signer.signingKey.publicKey.n, @@ -28,7 +31,7 @@ async function fixture() { ]); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { @@ -48,7 +51,7 @@ async function fixture() { describe('AccountRSA', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeAccountCore(); diff --git a/test/account/AccountWebAuthn.test.js b/test/account/AccountWebAuthn.test.js index 6a9962049d7..9557b05bf8a 100644 --- a/test/account/AccountWebAuthn.test.js +++ b/test/account/AccountWebAuthn.test.js @@ -1,14 +1,17 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../helpers/eip712'); -const { ERC4337Helper } = require('../helpers/erc4337'); -const { NonNativeSigner, P256SigningKey, WebAuthnSigningKey } = require('../helpers/signers'); -const { PackedUserOperation } = require('../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('./Account.behavior'); -const { shouldBehaveLikeERC1271 } = require('../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('./extensions/ERC7821.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../helpers/eip712'; +import { ERC4337Helper } from '../helpers/erc4337'; +import { PackedUserOperation } from '../helpers/eip712-types'; +import { NonNativeSigner, P256SigningKey, WebAuthnSigningKey } from '../helpers/signers'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Account.behavior'; +import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const webAuthnSigner = new NonNativeSigner(WebAuthnSigningKey.random()); const p256Signer = new NonNativeSigner(P256SigningKey.random()); @@ -19,7 +22,7 @@ async function fixture() { const target = await ethers.deployContract('CallReceiverMock'); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const webAuthnMock = await helper.newAccount('$AccountWebAuthnMock', [ webAuthnSigner.signingKey.publicKey.qx, @@ -36,7 +39,7 @@ async function fixture() { ]); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { @@ -57,7 +60,7 @@ async function fixture() { describe('AccountWebAuthn', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); describe('WebAuthn Assertions', function () { diff --git a/test/account/examples/AccountEIP7702WithModulesMock.test.js b/test/account/examples/AccountEIP7702WithModulesMock.test.js index 86816e55e57..2369040a288 100644 --- a/test/account/examples/AccountEIP7702WithModulesMock.test.js +++ b/test/account/examples/AccountEIP7702WithModulesMock.test.js @@ -1,16 +1,18 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture, setBalance } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../../helpers/eip712'); -const { ERC4337Helper } = require('../../helpers/erc4337'); -const { PackedUserOperation } = require('../../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } = require('../Account.behavior'); -const { shouldBehaveLikeAccountERC7579 } = require('../extensions/AccountERC7579.behavior'); -const { shouldBehaveLikeERC1271 } = require('../../utils/cryptography/ERC1271.behavior'); -const { shouldBehaveLikeERC7821 } = require('../extensions/ERC7821.behavior'); - -const { MODULE_TYPE_VALIDATOR } = require('../../helpers/erc7579'); +import { network } from 'hardhat'; +import { getDomain } from '../../helpers/eip712'; +import { ERC4337Helper } from '../../helpers/erc4337'; +import { MODULE_TYPE_VALIDATOR } from '../../helpers/erc7579'; +import { PackedUserOperation } from '../../helpers/eip712-types'; +import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from '../Account.behavior'; +import { shouldBehaveLikeAccountERC7579 } from '../extensions/AccountERC7579.behavior'; +import { shouldBehaveLikeERC1271 } from '../../utils/cryptography/ERC1271.behavior'; +import { shouldBehaveLikeERC7821 } from '../extensions/ERC7821.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture, setBalance }, +} = connection; async function fixture() { // EOAs and environment @@ -26,13 +28,13 @@ async function fixture() { const validator = await ethers.deployContract('$ERC7579ValidatorMock'); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountEIP7702WithModulesMock', ['AccountEIP7702WithModulesMock', '1'], { eip7702signer: eoa, }); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { @@ -47,7 +49,7 @@ async function fixture() { describe('AccountEIP7702WithModules: EIP-7702 account with ERC-7579 modules supports', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); describe('using EIP-7702 signer', function () { diff --git a/test/account/extensions/AccountERC7579.behavior.js b/test/account/extensions/AccountERC7579.behavior.js index 49953c4c214..6277a7cade5 100644 --- a/test/account/extensions/AccountERC7579.behavior.js +++ b/test/account/extensions/AccountERC7579.behavior.js @@ -1,9 +1,8 @@ -const { ethers, predeploy } = require('hardhat'); -const { expect } = require('chai'); -const { impersonate } = require('../../helpers/account'); -const { selector } = require('../../helpers/methods'); -const { zip } = require('../../helpers/iterate'); -const { +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { selector } from '../../helpers/methods'; +import { zip } from '../../helpers/iterate'; +import { encodeMode, encodeBatch, encodeSingle, @@ -17,28 +16,36 @@ const { CALL_TYPE_DELEGATE, EXEC_TYPE_DEFAULT, EXEC_TYPE_TRY, -} = require('../../helpers/erc7579'); +} from '../../helpers/erc7579'; const CALL_TYPE_INVALID = '0x42'; const EXEC_TYPE_INVALID = '0x17'; const MODULE_TYPE_INVALID = 999n; -const coder = ethers.AbiCoder.defaultAbiCoder(); - -function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { +export function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { describe('AccountERC7579', function () { beforeEach(async function () { await this.mock.deploy(); await this.other.sendTransaction({ to: this.mock.target, value: ethers.parseEther('1') }); this.modules = {}; - this.modules[MODULE_TYPE_VALIDATOR] = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_VALIDATOR]); - this.modules[MODULE_TYPE_EXECUTOR] = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_EXECUTOR]); - this.modules[MODULE_TYPE_FALLBACK] = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_FALLBACK]); - this.modules[MODULE_TYPE_HOOK] = await ethers.deployContract('$ERC7579HookMock'); + this.modules[MODULE_TYPE_VALIDATOR] = await this.ethers.deployContract('$ERC7579ModuleMock', [ + MODULE_TYPE_VALIDATOR, + ]); + this.modules[MODULE_TYPE_EXECUTOR] = await this.ethers.deployContract('$ERC7579ModuleMock', [ + MODULE_TYPE_EXECUTOR, + ]); + this.modules[MODULE_TYPE_FALLBACK] = await this.ethers.deployContract('$ERC7579ModuleMock', [ + MODULE_TYPE_FALLBACK, + ]); + this.modules[MODULE_TYPE_HOOK] = await this.ethers.deployContract('$ERC7579HookMock'); - this.mockFromEntrypoint = this.mock.connect(await impersonate(predeploy.entrypoint.v09.target)); - this.mockFromExecutor = this.mock.connect(await impersonate(this.modules[MODULE_TYPE_EXECUTOR].target)); + this.mockFromEntrypoint = this.mock.connect( + await this.helpers.impersonate(this.ethers.predeploy.entrypoint.v09.target), + ); + this.mockFromExecutor = this.mock.connect( + await this.helpers.impersonate(this.modules[MODULE_TYPE_EXECUTOR].target), + ); }); describe('accountId', function () { @@ -196,7 +203,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { await expect(this.mockFromEntrypoint.installModule(MODULE_TYPE_EXECUTOR, instance, initData)) .to.emit(this.modules[MODULE_TYPE_HOOK], 'PreCheck') - .withArgs(predeploy.entrypoint.v09, 0n, precheckData) + .withArgs(this.ethers.predeploy.entrypoint.v09, 0n, precheckData) .to.emit(this.modules[MODULE_TYPE_HOOK], 'PostCheck') .withArgs(precheckData); }); @@ -264,7 +271,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { it('should revert uninstalling a module of type MODULE_TYPE_FALLBACK if a different module was installed for the provided selector', async function () { const instance = this.modules[MODULE_TYPE_FALLBACK]; - const anotherInstance = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_FALLBACK]); + const anotherInstance = await this.ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_FALLBACK]); const initData = '0x12345678abcdef'; await this.mockFromEntrypoint.$_installModule(MODULE_TYPE_FALLBACK, instance, initData); @@ -274,7 +281,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { }); it('should uninstall a module even if its onUninstall hook reverts', async function () { - const maliciousModule = await ethers.deployContract('$ERC7579ModuleMaliciousMock', [MODULE_TYPE_EXECUTOR]); + const maliciousModule = await this.ethers.deployContract('$ERC7579ModuleMaliciousMock', [MODULE_TYPE_EXECUTOR]); // Install the malicious module await this.mock.$_installModule(MODULE_TYPE_EXECUTOR, maliciousModule, '0x'); @@ -312,7 +319,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { await this.mock.$_installModule(MODULE_TYPE_EXECUTOR, instance, initData); await expect(this.mockFromEntrypoint.uninstallModule(MODULE_TYPE_EXECUTOR, instance, initData)) .to.emit(this.modules[MODULE_TYPE_HOOK], 'PreCheck') - .withArgs(predeploy.entrypoint.v09, 0n, precheckData) + .withArgs(this.ethers.predeploy.entrypoint.v09, 0n, precheckData) .to.emit(this.modules[MODULE_TYPE_HOOK], 'PostCheck') .withArgs(precheckData); }); @@ -362,7 +369,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { const tx = this[mock][execFn](encodeMode({ callType: CALL_TYPE_CALL }), data); await expect(tx).to.emit(this.target, 'MockFunctionCalledWithArgs').withArgs(42, '0x1234'); - await expect(tx).to.changeEtherBalances([this.mock, this.target], [-value, value]); + await expect(tx).to.changeEtherBalances(this.ethers, [this.mock, this.target], [-value, value]); }); it('reverts when target reverts in default ExecType', async function () { @@ -392,7 +399,10 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { CALL_TYPE_CALL, ethers.solidityPacked( ['bytes4', 'bytes'], - [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])], + [ + selector('Error(string)'), + ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['CallReceiverMock: reverting']), + ], ), ); }); @@ -416,6 +426,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { .to.emit(this.target, 'MockFunctionCalledWithArgs') .to.emit(this.anotherTarget, 'MockFunctionCalledWithArgs'); await expect(tx).to.changeEtherBalances( + this.ethers, [this.mock, this.target, this.anotherTarget], [-value1 - value2, value1, value2], ); @@ -458,11 +469,15 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { CALL_TYPE_BATCH, ethers.solidityPacked( ['bytes4', 'bytes'], - [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])], + [ + selector('Error(string)'), + ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['CallReceiverMock: reverting']), + ], ), ); await expect(tx).to.changeEtherBalances( + this.ethers, [this.mock, this.target, this.anotherTarget], [-value1, value1, 0], ); @@ -478,9 +493,11 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { this.target.interface.encodeFunctionData('mockFunctionWritesStorage', [slot, value]), ); - await expect(ethers.provider.getStorage(this.mock.target, slot)).to.eventually.equal(ethers.ZeroHash); + await expect(this.ethers.provider.getStorage(this.mock.target, slot)).to.eventually.equal( + ethers.ZeroHash, + ); await this[mock][execFn](encodeMode({ callType: CALL_TYPE_DELEGATE }), data); - await expect(ethers.provider.getStorage(this.mock.target, slot)).to.eventually.equal(value); + await expect(this.ethers.provider.getStorage(this.mock.target, slot)).to.eventually.equal(value); }); it('reverts when target reverts in default ExecType', async function () { @@ -506,7 +523,10 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { CALL_TYPE_CALL, ethers.solidityPacked( ['bytes4', 'bytes'], - [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])], + [ + selector('Error(string)'), + ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['CallReceiverMock: reverting']), + ], ), ); }); @@ -519,7 +539,8 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { }); it(`should call the hook of the installed module when executing ${execFn}`, async function () { - const caller = execFn === 'execute' ? predeploy.entrypoint.v09 : this.modules[MODULE_TYPE_EXECUTOR]; + const caller = + execFn === 'execute' ? this.ethers.predeploy.entrypoint.v09 : this.modules[MODULE_TYPE_EXECUTOR]; const value = 17; const data = this.target.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234']); @@ -534,7 +555,11 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { .withArgs(caller, value, precheckData) .to.emit(this.modules[MODULE_TYPE_HOOK], 'PostCheck') .withArgs(precheckData); - await expect(tx).to.changeEtherBalances([caller, this.mock, this.target], [-value, 0n, value]); + await expect(tx).to.changeEtherBalances( + this.ethers, + [caller, this.mock, this.target], + [-value, 0n, value], + ); }); }); }); @@ -543,7 +568,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { describe('fallback', function () { beforeEach(async function () { - this.fallbackHandler = await ethers.deployContract('$ERC7579FallbackHandlerMock'); + this.fallbackHandler = await this.ethers.deployContract('$ERC7579FallbackHandlerMock'); }); it('reverts if there is no fallback module installed', async function () { @@ -565,7 +590,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { this.mock.$_installModule( MODULE_TYPE_FALLBACK, this.fallbackHandler, - coder.encode(['bytes4', 'bytes'], [selector, '0x']), + ethers.AbiCoder.defaultAbiCoder().encode(['bytes4', 'bytes'], [selector, '0x']), ), ), ); @@ -615,7 +640,3 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) { }); }); } - -module.exports = { - shouldBehaveLikeAccountERC7579, -}; diff --git a/test/account/extensions/AccountERC7579.test.js b/test/account/extensions/AccountERC7579.test.js index 83f0224fa9e..f563cbc68f1 100644 --- a/test/account/extensions/AccountERC7579.test.js +++ b/test/account/extensions/AccountERC7579.test.js @@ -1,13 +1,16 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../../helpers/eip712'); -const { ERC4337Helper } = require('../../helpers/erc4337'); -const { PackedUserOperation } = require('../../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore } = require('../Account.behavior'); -const { shouldBehaveLikeAccountERC7579 } = require('./AccountERC7579.behavior'); -const { shouldBehaveLikeERC1271 } = require('../../utils/cryptography/ERC1271.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../../helpers/eip712'; +import { ERC4337Helper } from '../../helpers/erc4337'; +import { PackedUserOperation } from '../../helpers/eip712-types'; +import { shouldBehaveLikeAccountCore } from '../Account.behavior'; +import { shouldBehaveLikeAccountERC7579 } from './AccountERC7579.behavior'; +import { shouldBehaveLikeERC1271 } from '../../utils/cryptography/ERC1271.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { // EOAs and environment @@ -22,21 +25,21 @@ async function fixture() { const signer = ethers.Wallet.createRandom(); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountERC7579Mock', [ validator, ethers.solidityPacked(['address'], [signer.address]), ]); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); return { helper, validator, mock, entrypointDomain, signer, target, anotherTarget, other }; } describe('AccountERC7579', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); this.signer.signMessage = message => ethers.Wallet.prototype.signMessage diff --git a/test/account/extensions/AccountERC7579Hooked.test.js b/test/account/extensions/AccountERC7579Hooked.test.js index 7e973844dfd..e0055230755 100644 --- a/test/account/extensions/AccountERC7579Hooked.test.js +++ b/test/account/extensions/AccountERC7579Hooked.test.js @@ -1,13 +1,16 @@ -const { ethers, predeploy } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain } = require('../../helpers/eip712'); -const { ERC4337Helper } = require('../../helpers/erc4337'); -const { PackedUserOperation } = require('../../helpers/eip712-types'); - -const { shouldBehaveLikeAccountCore } = require('../Account.behavior'); -const { shouldBehaveLikeAccountERC7579 } = require('./AccountERC7579.behavior'); -const { shouldBehaveLikeERC1271 } = require('../../utils/cryptography/ERC1271.behavior'); +import { network } from 'hardhat'; +import { getDomain } from '../../helpers/eip712'; +import { ERC4337Helper } from '../../helpers/erc4337'; +import { PackedUserOperation } from '../../helpers/eip712-types'; +import { shouldBehaveLikeAccountCore } from '../Account.behavior'; +import { shouldBehaveLikeAccountERC7579 } from './AccountERC7579.behavior'; +import { shouldBehaveLikeERC1271 } from '../../utils/cryptography/ERC1271.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { // EOAs and environment @@ -22,21 +25,21 @@ async function fixture() { const signer = ethers.Wallet.createRandom(); // ERC-4337 account - const helper = new ERC4337Helper(); + const helper = new ERC4337Helper(connection); const mock = await helper.newAccount('$AccountERC7579HookedMock', [ validator, ethers.solidityPacked(['address'], [signer.address]), ]); // ERC-4337 Entrypoint domain - const entrypointDomain = await getDomain(predeploy.entrypoint.v09); + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); return { helper, validator, mock, entrypointDomain, signer, target, anotherTarget, other }; } describe('AccountERC7579Hooked', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); this.signer.signMessage = message => ethers.Wallet.prototype.signMessage diff --git a/test/account/extensions/ERC7821.behavior.js b/test/account/extensions/ERC7821.behavior.js index bb039d52216..dc909ea1715 100644 --- a/test/account/extensions/ERC7821.behavior.js +++ b/test/account/extensions/ERC7821.behavior.js @@ -1,16 +1,16 @@ -const { ethers, predeploy } = require('hardhat'); -const { expect } = require('chai'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; -const { CALL_TYPE_BATCH, encodeMode, encodeBatch } = require('../../helpers/erc7579'); +import { CALL_TYPE_BATCH, encodeMode, encodeBatch } from '../../helpers/erc7579'; -function shouldBehaveLikeERC7821({ deployable = true } = {}) { +export function shouldBehaveLikeERC7821({ deployable = true } = {}) { describe('supports ERC-7821', function () { beforeEach(async function () { // give eth to the account (before deployment) await this.other.sendTransaction({ to: this.mock.target, value: ethers.parseEther('1') }); // account is not initially deployed - await expect(ethers.provider.getCode(this.mock)).to.eventually.equal('0x'); + await expect(this.mock.runner.provider.getCode(this.mock)).to.eventually.equal('0x'); this.encodeUserOpCalldata = (...calls) => this.mock.interface.encodeFunctionData('execute', [ @@ -50,9 +50,9 @@ function shouldBehaveLikeERC7821({ deployable = true } = {}) { .then(op => this.signUserOp(op)); // Can't call the account to get its nonce before it's deployed - await expect(predeploy.entrypoint.v09.getNonce(this.mock.target, 0)).to.eventually.equal(0); - await expect(predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary)) - .to.emit(predeploy.entrypoint.v09, 'AccountDeployed') + await expect(this.ethers.predeploy.entrypoint.v09.getNonce(this.mock.target, 0)).to.eventually.equal(0); + await expect(this.ethers.predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary)) + .to.emit(this.ethers.predeploy.entrypoint.v09, 'AccountDeployed') .withArgs(operation.hash(), this.mock, this.helper.factory, ethers.ZeroAddress) .to.emit(this.target, 'MockFunctionCalledExtra') .withArgs(this.mock, 17); @@ -72,7 +72,9 @@ function shouldBehaveLikeERC7821({ deployable = true } = {}) { operation.signature = '0x00'; - await expect(predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary)).to.be.reverted; + await expect( + this.ethers.predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary), + ).to.be.revert(this.ethers); }); }); } @@ -94,7 +96,7 @@ function shouldBehaveLikeERC7821({ deployable = true } = {}) { .then(op => this.signUserOp(op)); await expect(this.mock.getNonce()).to.eventually.equal(0); - await expect(predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary)) + await expect(this.ethers.predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary)) .to.emit(this.target, 'MockFunctionCalledExtra') .withArgs(this.mock, 42); await expect(this.mock.getNonce()).to.eventually.equal(1); @@ -106,10 +108,9 @@ function shouldBehaveLikeERC7821({ deployable = true } = {}) { .then(op => this.signUserOp(op)); await expect(this.mock.getNonce()).to.eventually.equal(0); - await expect(predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary)).to.changeEtherBalance( - this.other, - 42, - ); + await expect( + this.ethers.predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary), + ).to.changeEtherBalance(this.ethers, this.other, 42); await expect(this.mock.getNonce()).to.eventually.equal(1); }); @@ -131,15 +132,11 @@ function shouldBehaveLikeERC7821({ deployable = true } = {}) { .then(op => this.signUserOp(op)); await expect(this.mock.getNonce()).to.eventually.equal(0); - const tx = predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary); - await expect(tx).to.changeEtherBalances([this.other, this.target], [value1, value2]); + const tx = this.ethers.predeploy.entrypoint.v09.handleOps([operation.packed], this.beneficiary); + await expect(tx).to.changeEtherBalances(this.ethers, [this.other, this.target], [value1, value2]); await expect(tx).to.emit(this.target, 'MockFunctionCalledExtra').withArgs(this.mock, value2); await expect(this.mock.getNonce()).to.eventually.equal(1); }); }); }); } - -module.exports = { - shouldBehaveLikeERC7821, -}; diff --git a/test/account/utils/EIP7702Utils.test.js b/test/account/utils/EIP7702Utils.test.js index fb045f75325..11c578f0f3e 100644 --- a/test/account/utils/EIP7702Utils.test.js +++ b/test/account/utils/EIP7702Utils.test.js @@ -1,6 +1,10 @@ -const { ethers, config } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network, config } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); // [NOTE] // @@ -8,11 +12,13 @@ const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); // we have to instantiate the eoa AND the relayer manually using ethers 6.14.0 wallets. This can be improved when // @nomicfoundation/hardhat-ethers starts instantiating signers with 7702 support. const relayAuthorization = authorization => - ethers.Wallet.fromPhrase(config.networks.hardhat.accounts.mnemonic, ethers.provider).sendTransaction({ - to: ethers.ZeroAddress, - authorizationList: [authorization], - gasLimit: 46_000n, - }); + config.networks.default.accounts.mnemonic.get().then(mnemonic => + ethers.Wallet.fromPhrase(mnemonic, ethers.provider).sendTransaction({ + to: ethers.ZeroAddress, + authorizationList: [authorization], + gasLimit: 46_000n, + }), + ); const fixture = async () => { const eoa = ethers.Wallet.createRandom(ethers.provider); diff --git a/test/account/utils/draft-ERC4337Utils.test.js b/test/account/utils/draft-ERC4337Utils.test.js index 8d3b6847071..f2b62d1163f 100644 --- a/test/account/utils/draft-ERC4337Utils.test.js +++ b/test/account/utils/draft-ERC4337Utils.test.js @@ -1,21 +1,25 @@ -const { ethers, predeploy } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { MAX_UINT48 } = require('../../helpers/constants'); -const { packValidationData, UserOperation } = require('../../helpers/erc4337'); -const { ValidationRange } = require('../../helpers/enums'); -const { clock, increaseTo } = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT48 } from '../../helpers/constants'; +import { packValidationData, UserOperation } from '../../helpers/erc4337'; +import { ValidationRange } from '../../helpers/enums'; + +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = await network.connect(); + const ADDRESS_ONE = '0x0000000000000000000000000000000000000001'; -const fixture = async () => { +async function fixture() { const [authorizer, sender, factory, paymaster] = await ethers.getSigners(); const utils = await ethers.deployContract('$ERC4337Utils'); const SIG_VALIDATION_SUCCESS = await utils.$SIG_VALIDATION_SUCCESS(); const SIG_VALIDATION_FAILED = await utils.$SIG_VALIDATION_FAILED(); return { utils, authorizer, sender, factory, paymaster, SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED }; -}; +} describe('ERC4337Utils', function () { beforeEach(async function () { @@ -24,15 +28,15 @@ describe('ERC4337Utils', function () { describe('entrypoint', function () { it('v0.7.0', async function () { - await expect(this.utils.$ENTRYPOINT_V07()).to.eventually.equal(predeploy.entrypoint.v07); + await expect(this.utils.$ENTRYPOINT_V07()).to.eventually.equal(ethers.predeploy.entrypoint.v07); }); it('v0.8.0', async function () { - await expect(this.utils.$ENTRYPOINT_V08()).to.eventually.equal(predeploy.entrypoint.v08); + await expect(this.utils.$ENTRYPOINT_V08()).to.eventually.equal(ethers.predeploy.entrypoint.v08); }); it('v0.9.0', async function () { - await expect(this.utils.$ENTRYPOINT_V09()).to.eventually.equal(predeploy.entrypoint.v09); + await expect(this.utils.$ENTRYPOINT_V09()).to.eventually.equal(ethers.predeploy.entrypoint.v09); }); }); @@ -385,11 +389,11 @@ describe('ERC4337Utils', function () { it('returns the validation data with invalid validity range (expired)', async function () { const aggregator = this.authorizer; - const validAfter = await clock[name](); + const validAfter = await time.clock[name](); const validUntil = validAfter + 10n; const validationData = packValidationData(validAfter, validUntil, aggregator, range); - await increaseTo[name](validUntil + 1n); + await time.increaseTo[name](validUntil + 1n); await expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([ aggregator.address, true, @@ -398,7 +402,7 @@ describe('ERC4337Utils', function () { it('returns the validation data with invalid validity range (not yet valid)', async function () { const aggregator = this.authorizer; - const validAfter = (await clock[name]()) + 1n; + const validAfter = (await time.clock[name]()) + 1n; const validUntil = validAfter + 10n; const validationData = packValidationData(validAfter, validUntil, aggregator, range); @@ -410,7 +414,7 @@ describe('ERC4337Utils', function () { it('returns the validation data with invalid validity range (current == validAfter)', async function () { const aggregator = this.authorizer; - const validAfter = await clock[name](); + const validAfter = await time.clock[name](); const validUntil = validAfter + 10n; const validationData = packValidationData(validAfter, validUntil, aggregator, range); @@ -422,11 +426,11 @@ describe('ERC4337Utils', function () { it('returns the validation data with valid validity range (current == validUntil)', async function () { const aggregator = this.authorizer; - const validAfter = await clock[name](); + const validAfter = await time.clock[name](); const validUntil = validAfter + 10n; const validationData = packValidationData(validAfter, validUntil, aggregator, range); - await increaseTo[name](validUntil); + await time.increaseTo[name](validUntil); await expect(this.utils.$getValidationData(validationData)).to.eventually.deep.equal([ aggregator.address, false, @@ -440,8 +444,8 @@ describe('ERC4337Utils', function () { }); }); - describe('hash', function () { - for (const [version, instance] of Object.entries(predeploy.entrypoint)) { + describe('hash', async function () { + for (const [version, instance] of Object.entries(ethers.predeploy.entrypoint)) { it(`returns the operation hash for entrypoint ${version}`, async function () { const userOp = new UserOperation({ sender: this.sender, nonce: 1 }); const expected = await userOp.hash(instance); diff --git a/test/account/utils/draft-ERC7579Utils.test.js b/test/account/utils/draft-ERC7579Utils.test.js index b0ca86c46fa..cf14bbad59a 100644 --- a/test/account/utils/draft-ERC7579Utils.test.js +++ b/test/account/utils/draft-ERC7579Utils.test.js @@ -1,7 +1,6 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { CALL_TYPE_CALL, CALL_TYPE_BATCH, CALL_TYPE_DELEGATE, @@ -11,10 +10,13 @@ const { encodeBatch, encodeDelegate, encodeMode, -} = require('../../helpers/erc7579'); -const { selector } = require('../../helpers/methods'); +} from '../../helpers/erc7579'; +import { selector } from '../../helpers/methods'; -const coder = ethers.AbiCoder.defaultAbiCoder(); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const fixture = async () => { const [sender] = await ethers.getSigners(); @@ -100,7 +102,10 @@ describe('ERC7579Utils', function () { CALL_TYPE_CALL, ethers.solidityPacked( ['bytes4', 'bytes'], - [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])], + [ + selector('Error(string)'), + ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['CallReceiverMock: reverting']), + ], ), ); }); @@ -188,7 +193,10 @@ describe('ERC7579Utils', function () { CALL_TYPE_BATCH, ethers.solidityPacked( ['bytes4', 'bytes'], - [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])], + [ + selector('Error(string)'), + ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['CallReceiverMock: reverting']), + ], ), ); @@ -251,7 +259,10 @@ describe('ERC7579Utils', function () { CALL_TYPE_CALL, ethers.solidityPacked( ['bytes4', 'bytes'], - [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])], + [ + selector('Error(string)'), + ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['CallReceiverMock: reverting']), + ], ), ); }); diff --git a/test/crosschain/BridgeERC20.behavior.js b/test/crosschain/BridgeERC20.behavior.js index 22874867464..44e11995d0c 100644 --- a/test/crosschain/BridgeERC20.behavior.js +++ b/test/crosschain/BridgeERC20.behavior.js @@ -1,27 +1,27 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; const amount = 100n; -function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustodial = false } = {}) { +export function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustodial = false } = {}) { beforeEach(function () { // helper this.encodePayload = (from, to, amount) => ethers.AbiCoder.defaultAbiCoder().encode( ['bytes', 'bytes', 'uint256'], - [this.chain.toErc7930(from), to.target ?? to.address ?? to, amount], + [this.helpers.chain.toErc7930(from), to.target ?? to.address ?? to, amount], ); }); it('bridge setup', async function () { - await expect(this.bridgeA.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeA.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ this.gateway.target, - this.chain.toErc7930(this.bridgeB), + this.helpers.chain.toErc7930(this.bridgeB), ]); - await expect(this.bridgeB.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeB.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ this.gateway.target, - this.chain.toErc7930(this.bridgeA), + this.helpers.chain.toErc7930(this.bridgeA), ]); }); @@ -32,18 +32,18 @@ function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustod await this.tokenA.connect(alice).approve(this.bridgeA, ethers.MaxUint256); // Alice sends tokens from chain A to Bruce on chain B. - await expect(this.bridgeA.connect(alice).crosschainTransfer(this.chain.toErc7930(bruce), amount)) + await expect(this.bridgeA.connect(alice).crosschainTransfer(this.helpers.chain.toErc7930(bruce), amount)) // bridge on chain A takes custody of the funds .to.emit(this.tokenA, 'Transfer') .withArgs(alice, chainAIsCustodial ? this.bridgeA : ethers.ZeroAddress, amount) // crosschain transfer sent .to.emit(this.bridgeA, 'CrosschainERC20TransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), amount) + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), amount) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeB, 'CrosschainERC20TransferReceived') - .withArgs(anyValue, this.chain.toErc7930(alice), bruce, amount) + .withArgs(anyValue, this.helpers.chain.toErc7930(alice), bruce, amount) // crosschain mint event .to.emit(this.tokenB, 'CrosschainMint') .withArgs(bruce, amount, this.bridgeB) @@ -52,7 +52,7 @@ function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustod .withArgs(chainBIsCustodial ? this.bridgeB : ethers.ZeroAddress, bruce, amount); // Bruce sends tokens from chain B to Chris on chain A. - await expect(this.bridgeB.connect(bruce).crosschainTransfer(this.chain.toErc7930(chris), amount)) + await expect(this.bridgeB.connect(bruce).crosschainTransfer(this.helpers.chain.toErc7930(chris), amount)) // tokens are burned on chain B .to.emit(this.tokenB, 'Transfer') .withArgs(bruce, chainBIsCustodial ? this.bridgeB : ethers.ZeroAddress, amount) @@ -61,12 +61,12 @@ function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustod .withArgs(bruce, amount, this.bridgeB) // crosschain transfer sent .to.emit(this.bridgeB, 'CrosschainERC20TransferSent') - .withArgs(anyValue, bruce, this.chain.toErc7930(chris), amount) + .withArgs(anyValue, bruce, this.helpers.chain.toErc7930(chris), amount) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeA, 'CrosschainERC20TransferReceived') - .withArgs(anyValue, this.chain.toErc7930(bruce), chris, amount) + .withArgs(anyValue, this.helpers.chain.toErc7930(bruce), chris, amount) // bridge on chain A releases custody of the funds .to.emit(this.tokenA, 'Transfer') .withArgs(chainAIsCustodial ? this.bridgeA : ethers.ZeroAddress, chris, amount); @@ -85,12 +85,12 @@ function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustod .connect(notGateway) .receiveMessage( ethers.ZeroHash, - this.chain.toErc7930(this.tokenB), + this.helpers.chain.toErc7930(this.tokenB), this.encodePayload(notGateway, notGateway, amount), ), ) .to.be.revertedWithCustomError(this.bridgeA, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(notGateway, this.chain.toErc7930(this.tokenB)); + .withArgs(notGateway, this.helpers.chain.toErc7930(this.tokenB)); }); it('only counterpart can send a crosschain message', async function () { @@ -99,10 +99,10 @@ function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustod await expect( this.gateway .connect(invalid) - .sendMessage(this.chain.toErc7930(this.bridgeA), this.encodePayload(invalid, invalid, amount), []), + .sendMessage(this.helpers.chain.toErc7930(this.bridgeA), this.encodePayload(invalid, invalid, amount), []), ) .to.be.revertedWithCustomError(this.bridgeA, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(this.gateway, this.chain.toErc7930(invalid)); + .withArgs(this.gateway, this.helpers.chain.toErc7930(invalid)); }); it('cannot replay message', async function () { @@ -113,12 +113,12 @@ function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustod // first time works await expect( - this.bridgeA.connect(this.gatewayAsEOA).receiveMessage(id, this.chain.toErc7930(this.bridgeB), payload), + this.bridgeA.connect(this.gatewayAsEOA).receiveMessage(id, this.helpers.chain.toErc7930(this.bridgeB), payload), ).to.emit(this.bridgeA, 'CrosschainERC20TransferReceived'); // second time fails await expect( - this.bridgeA.connect(this.gatewayAsEOA).receiveMessage(id, this.chain.toErc7930(this.bridgeB), payload), + this.bridgeA.connect(this.gatewayAsEOA).receiveMessage(id, this.helpers.chain.toErc7930(this.bridgeB), payload), ) .to.be.revertedWithCustomError(this.bridgeA, 'ERC7786RecipientMessageAlreadyProcessed') .withArgs(this.gateway, id); @@ -127,37 +127,33 @@ function shouldBehaveLikeBridgeERC20({ chainAIsCustodial = false, chainBIsCustod describe('reconfiguration', function () { it('updating a link emits an event', async function () { - const newGateway = await ethers.deployContract('$ERC7786GatewayMock'); - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newGateway = await this.ethers.deployContract('$ERC7786GatewayMock'); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(newGateway, newCounterpart, true)) .to.emit(this.bridgeA, 'LinkRegistered') .withArgs(newGateway, newCounterpart); - await expect(this.bridgeA.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeA.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ newGateway.target, newCounterpart, ]); }); it('cannot override configuration is "allowOverride" is false', async function () { - const newGateway = await ethers.deployContract('$ERC7786GatewayMock'); - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newGateway = await this.ethers.deployContract('$ERC7786GatewayMock'); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(newGateway, newCounterpart, false)) .to.be.revertedWithCustomError(this.bridgeA, 'LinkAlreadyRegistered') - .withArgs(this.chain.erc7930); + .withArgs(this.helpers.chain.erc7930); }); it('reject invalid gateway', async function () { const notAGateway = this.accounts[0]; - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); - await expect(this.bridgeA.$_setLink(notAGateway, newCounterpart, false)).to.be.revertedWithoutReason(); + await expect(this.bridgeA.$_setLink(notAGateway, newCounterpart, false)).to.be.revertedWithoutReason(ethers); }); }); } - -module.exports = { - shouldBehaveLikeBridgeERC20, -}; diff --git a/test/crosschain/BridgeERC20.test.js b/test/crosschain/BridgeERC20.test.js index 1e20d491cfd..c91294139e2 100644 --- a/test/crosschain/BridgeERC20.test.js +++ b/test/crosschain/BridgeERC20.test.js @@ -1,19 +1,20 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeBridgeERC20 } from './BridgeERC20.behavior'; -const { impersonate } = require('../helpers/account'); -const { getLocalChain } = require('../helpers/chains'); - -const { shouldBehaveLikeBridgeERC20 } = require('./BridgeERC20.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const chain = await getLocalChain(); const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await impersonate(gateway); + const gatewayAsEOA = await helpers.impersonate(gateway); // Chain A: legacy ERC20 with bridge const tokenA = await ethers.deployContract('$ERC20', ['Token1', 'T1']); @@ -21,20 +22,23 @@ async function fixture() { // Chain B: ERC7802 with bridge (preconfigured link to bridgeA) const tokenB = await ethers.deployContract('$ERC20BridgeableMock', ['Token2', 'T2', ethers.ZeroAddress]); - const bridgeB = await ethers.deployContract('$BridgeERC7802', [[[gateway, chain.toErc7930(bridgeA)]], tokenB]); + const bridgeB = await ethers.deployContract('$BridgeERC7802', [ + [[gateway, helpers.chain.toErc7930(bridgeA)]], + tokenB, + ]); // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, chain.toErc7930(bridgeB)); + .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); await tokenB.$_setBridge(bridgeB); - return { chain, accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; + return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('CrosschainBridgeERC20', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('token getters', async function () { diff --git a/test/crosschain/ERC7786Recipient.test.js b/test/crosschain/ERC7786Recipient.test.js index c8f651743b0..292443c086e 100644 --- a/test/crosschain/ERC7786Recipient.test.js +++ b/test/crosschain/ERC7786Recipient.test.js @@ -1,10 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { generators } from '../helpers/random'; -const { getLocalChain } = require('../helpers/chains'); -const { impersonate } = require('../helpers/account'); -const { generators } = require('../helpers/random'); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = await network.connect(); const value = 42n; const payload = generators.hexBytes(128); @@ -12,12 +14,11 @@ const attributes = []; async function fixture() { const [sender, notAGateway] = await ethers.getSigners(); - const { toErc7930 } = await getLocalChain(); const gateway = await ethers.deployContract('$ERC7786GatewayMock'); const receiver = await ethers.deployContract('$ERC7786RecipientMock', [gateway]); - return { sender, notAGateway, gateway, receiver, toErc7930 }; + return { sender, notAGateway, gateway, receiver }; } // NOTE: here we are only testing the receiver. Failures of the gateway itself (invalid attributes, ...) are out of scope. @@ -28,36 +29,51 @@ describe('ERC7786Recipient', function () { it('receives gateway relayed messages', async function () { await expect( - this.gateway.connect(this.sender).sendMessage(this.toErc7930(this.receiver), payload, attributes, { value }), + this.gateway + .connect(this.sender) + .sendMessage(helpers.chain.toErc7930(this.receiver), payload, attributes, { value }), ) .to.emit(this.gateway, 'MessageSent') - .withArgs(ethers.ZeroHash, this.toErc7930(this.sender), this.toErc7930(this.receiver), payload, value, attributes) + .withArgs( + ethers.ZeroHash, + helpers.chain.toErc7930(this.sender), + helpers.chain.toErc7930(this.receiver), + payload, + value, + attributes, + ) .to.emit(this.receiver, 'MessageReceived') - .withArgs(this.gateway, ethers.toBeHex(1n, 32n), this.toErc7930(this.sender), payload, value); + .withArgs(this.gateway, ethers.toBeHex(1n, 32n), helpers.chain.toErc7930(this.sender), payload, value); }); it('receive multiple similar messages (with different receiveIds)', async function () { for (let i = 1n; i < 5n; ++i) { await expect( - this.gateway.connect(this.sender).sendMessage(this.toErc7930(this.receiver), payload, attributes, { value }), + this.gateway + .connect(this.sender) + .sendMessage(helpers.chain.toErc7930(this.receiver), payload, attributes, { value }), ) .to.emit(this.receiver, 'MessageReceived') - .withArgs(this.gateway, ethers.toBeHex(i, 32n), this.toErc7930(this.sender), payload, value); + .withArgs(this.gateway, ethers.toBeHex(i, 32n), helpers.chain.toErc7930(this.sender), payload, value); } }); it('multiple use of the same receiveId', async function () { - const gatewayAsEOA = await impersonate(this.gateway.target); + const gatewayAsEOA = await helpers.impersonate(this.gateway.target); const receiveId = ethers.toBeHex(1n, 32n); await expect( - this.receiver.connect(gatewayAsEOA).receiveMessage(receiveId, this.toErc7930(this.sender), payload, { value }), + this.receiver + .connect(gatewayAsEOA) + .receiveMessage(receiveId, helpers.chain.toErc7930(this.sender), payload, { value }), ) .to.emit(this.receiver, 'MessageReceived') - .withArgs(this.gateway, receiveId, this.toErc7930(this.sender), payload, value); + .withArgs(this.gateway, receiveId, helpers.chain.toErc7930(this.sender), payload, value); await expect( - this.receiver.connect(gatewayAsEOA).receiveMessage(receiveId, this.toErc7930(this.sender), payload, { value }), + this.receiver + .connect(gatewayAsEOA) + .receiveMessage(receiveId, helpers.chain.toErc7930(this.sender), payload, { value }), ) .to.be.revertedWithCustomError(this.receiver, 'ERC7786RecipientMessageAlreadyProcessed') .withArgs(this.gateway, receiveId); @@ -65,9 +81,11 @@ describe('ERC7786Recipient', function () { it('unauthorized call', async function () { await expect( - this.receiver.connect(this.notAGateway).receiveMessage(ethers.ZeroHash, this.toErc7930(this.sender), payload), + this.receiver + .connect(this.notAGateway) + .receiveMessage(ethers.ZeroHash, helpers.chain.toErc7930(this.sender), payload), ) .to.be.revertedWithCustomError(this.receiver, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(this.notAGateway, this.toErc7930(this.sender)); + .withArgs(this.notAGateway, helpers.chain.toErc7930(this.sender)); }); }); diff --git a/test/finance/VestingWallet.behavior.js b/test/finance/VestingWallet.behavior.js index b45ffeecfd3..b6b9940646a 100644 --- a/test/finance/VestingWallet.behavior.js +++ b/test/finance/VestingWallet.behavior.js @@ -1,15 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const time = require('../helpers/time'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; -async function envSetup(mock, beneficiary, token) { +export async function envSetup(connection, mock, beneficiary, token) { return { eth: { checkRelease: async (tx, amount) => { - await expect(tx).to.changeEtherBalances([mock, beneficiary], [-amount, amount]); + await expect(tx).to.changeEtherBalances(connection.ethers, [mock, beneficiary], [-amount, amount]); }, setupFailure: async () => { - const beneficiaryMock = await ethers.deployContract('EtherReceiverMock'); + const beneficiaryMock = await connection.ethers.deployContract('EtherReceiverMock'); await beneficiaryMock.setAcceptEther(false); await mock.connect(beneficiary).transferOwnership(beneficiaryMock); return { args: [], error: [mock, 'FailedCall'] }; @@ -20,10 +19,10 @@ async function envSetup(mock, beneficiary, token) { token: { checkRelease: async (tx, amount) => { await expect(tx).to.emit(token, 'Transfer').withArgs(mock, beneficiary, amount); - await expect(tx).to.changeTokenBalances(token, [mock, beneficiary], [-amount, amount]); + await expect(tx).to.changeTokenBalances(connection.ethers, token, [mock, beneficiary], [-amount, amount]); }, setupFailure: async () => { - const pausableToken = await ethers.deployContract('$ERC20Pausable', ['Name', 'Symbol']); + const pausableToken = await connection.ethers.deployContract('$ERC20Pausable', ['Name', 'Symbol']); await pausableToken.$_pause(); return { args: [ethers.Typed.address(pausableToken)], @@ -36,10 +35,10 @@ async function envSetup(mock, beneficiary, token) { }; } -function shouldBehaveLikeVesting() { +export function shouldBehaveLikeVesting() { it('check vesting schedule', async function () { for (const timestamp of this.schedule) { - await time.increaseTo.timestamp(timestamp); + await this.helpers.time.increaseTo.timestamp(timestamp); const vesting = this.vestingFn(timestamp); expect(await this.mock.vestedAmount(...this.args, timestamp)).to.equal(vesting); @@ -59,7 +58,7 @@ function shouldBehaveLikeVesting() { } for (const timestamp of this.schedule) { - await time.increaseTo.timestamp(timestamp, false); + await this.helpers.time.increaseTo.timestamp(timestamp, false); const vested = this.vestingFn(timestamp); const tx = await this.mock.release(...this.args); @@ -74,14 +73,9 @@ function shouldBehaveLikeVesting() { const { args, error } = await this.setupFailure(); for (const timestamp of this.schedule) { - await time.increaseTo.timestamp(timestamp); + await this.helpers.time.increaseTo.timestamp(timestamp); await expect(this.mock.release(...args)).to.be.revertedWithCustomError(...error); } }); } - -module.exports = { - envSetup, - shouldBehaveLikeVesting, -}; diff --git a/test/finance/VestingWallet.test.js b/test/finance/VestingWallet.test.js index b89258d65b8..303215af03e 100644 --- a/test/finance/VestingWallet.test.js +++ b/test/finance/VestingWallet.test.js @@ -1,11 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { min } from '../helpers/math'; +import { envSetup, shouldBehaveLikeVesting } from './VestingWallet.behavior'; -const { min } = require('../helpers/math'); -const time = require('../helpers/time'); - -const { envSetup, shouldBehaveLikeVesting } = require('./VestingWallet.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const amount = ethers.parseEther('100'); @@ -19,7 +22,7 @@ async function fixture() { await token.$_mint(mock, amount); await sender.sendTransaction({ to: mock, value: amount }); - const env = await envSetup(mock, beneficiary, token); + const env = await envSetup(connection, mock, beneficiary, token); const schedule = Array.from({ length: 64 }, (_, i) => (BigInt(i) * duration) / 60n + start); const vestingFn = timestamp => min(amount, (amount * (timestamp - start)) / duration); @@ -29,7 +32,7 @@ async function fixture() { describe('VestingWallet', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('rejects zero address for beneficiary', async function () { diff --git a/test/finance/VestingWalletCliff.test.js b/test/finance/VestingWalletCliff.test.js index 799b24c4b2e..8c7ba32a754 100644 --- a/test/finance/VestingWalletCliff.test.js +++ b/test/finance/VestingWalletCliff.test.js @@ -1,11 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { min } from '../helpers/math'; +import { envSetup, shouldBehaveLikeVesting } from './VestingWallet.behavior'; -const { min } = require('../helpers/math'); -const time = require('../helpers/time'); - -const { envSetup, shouldBehaveLikeVesting } = require('./VestingWallet.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const amount = ethers.parseEther('100'); @@ -21,7 +24,7 @@ async function fixture() { await token.$_mint(mock, amount); await sender.sendTransaction({ to: mock, value: amount }); - const env = await envSetup(mock, beneficiary, token); + const env = await envSetup(connection, mock, beneficiary, token); const schedule = Array.from({ length: 64 }, (_, i) => (BigInt(i) * duration) / 60n + start); const vestingFn = timestamp => min(amount, timestamp < cliff ? 0n : (amount * (timestamp - start)) / duration); @@ -31,7 +34,7 @@ async function fixture() { describe('VestingWalletCliff', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('rejects a larger cliff than vesting duration', async function () { diff --git a/test/governance/Governor.test.js b/test/governance/Governor.test.js index ebb5a3884d1..e4fbdc6d983 100644 --- a/test/governance/Governor.test.js +++ b/test/governance/Governor.test.js @@ -1,14 +1,17 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../helpers/governance'); -const { getDomain, Ballot } = require('../helpers/eip712'); -const { ProposalState, VoteType } = require('../helpers/enums'); -const time = require('../helpers/time'); - -const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); -const { shouldBehaveLikeERC6372 } = require('./utils/ERC6372.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { Ballot, getDomain } from '../helpers/eip712'; +import { ProposalState, VoteType } from '../helpers/enums'; +import { GovernorHelper } from '../helpers/governance'; +import { shouldSupportInterfaces } from '../utils/introspection/SupportsInterface.behavior'; +import { shouldBehaveLikeERC6372 } from './utils/ERC6372.behavior'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -59,7 +62,7 @@ describe('Governor', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token: token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token: token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token: token, to: voter3, value: ethers.parseEther('5') }); @@ -82,7 +85,7 @@ describe('Governor', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // initiate fresh proposal this.proposal = this.helper.setProposal( [ @@ -194,7 +197,7 @@ describe('Governor', function () { await this.helper.connect(this.voter1).vote({ support: VoteType.For }); await this.helper.waitForDeadline(); return this.helper.execute(); - }).to.changeEtherBalances([this.mock, this.userEOA], [-value, value]); + }).to.changeEtherBalances(ethers, [this.mock, this.userEOA], [-value, value]); }); describe('vote with signature', function () { diff --git a/test/governance/TimelockController.test.js b/test/governance/TimelockController.test.js index 11a4c7a00c5..3f13c3d9e33 100644 --- a/test/governance/TimelockController.test.js +++ b/test/governance/TimelockController.test.js @@ -1,13 +1,16 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); - -const { GovernorHelper } = require('../helpers/governance'); -const { OperationState } = require('../helpers/enums'); -const time = require('../helpers/time'); - -const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { GovernorHelper } from '../helpers/governance'; +import { OperationState } from '../helpers/enums'; +import { shouldSupportInterfaces } from '../utils/introspection/SupportsInterface.behavior'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const salt = '0x025e7b0be353a74631ad648c667493c0e1cd31caa4cc2d3520fdc171ea0cc726'; // a random value diff --git a/test/governance/extensions/GovernorCountingFractional.test.js b/test/governance/extensions/GovernorCountingFractional.test.js index a46de210b93..5301c18b36d 100644 --- a/test/governance/extensions/GovernorCountingFractional.test.js +++ b/test/governance/extensions/GovernorCountingFractional.test.js @@ -1,11 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { VoteType } = require('../../helpers/enums'); -const { zip } = require('../../helpers/iterate'); -const { sum } = require('../../helpers/math'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; +import { zip } from '../../helpers/iterate'; +import { sum } from '../../helpers/math'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -40,7 +44,7 @@ describe('GovernorCountingFractional', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); diff --git a/test/governance/extensions/GovernorCountingOverridable.test.js b/test/governance/extensions/GovernorCountingOverridable.test.js index f65465291f3..40e8103164e 100644 --- a/test/governance/extensions/GovernorCountingOverridable.test.js +++ b/test/governance/extensions/GovernorCountingOverridable.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { getDomain, OverrideBallot } = require('../../helpers/eip712'); -const { VoteType } = require('../../helpers/enums'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { VoteType } from '../../helpers/enums'; +import { getDomain, OverrideBallot } from '../../helpers/eip712'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture, mine }, +} = connection; const TOKENS = [ { Token: '$ERC20VotesExtendedMock', mode: 'blocknumber' }, @@ -42,7 +46,7 @@ describe('GovernorCountingOverridable', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -53,7 +57,7 @@ describe('GovernorCountingOverridable', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( diff --git a/test/governance/extensions/GovernorERC721.test.js b/test/governance/extensions/GovernorERC721.test.js index 15910b8fe1f..e8b5c7e5ab0 100644 --- a/test/governance/extensions/GovernorERC721.test.js +++ b/test/governance/extensions/GovernorERC721.test.js @@ -1,9 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; -const { GovernorHelper } = require('../../helpers/governance'); -const { VoteType } = require('../../helpers/enums'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC721Votes', mode: 'blocknumber' }, @@ -42,7 +46,7 @@ describe('GovernorERC721', function () { await owner.sendTransaction({ to: mock, value }); await Promise.all([NFT0, NFT1, NFT2, NFT3, NFT4].map(tokenId => token.$_mint(owner, tokenId))); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, tokenId: NFT0 }); await helper.connect(owner).delegate({ token, to: voter2, tokenId: NFT1 }); await helper.connect(owner).delegate({ token, to: voter2, tokenId: NFT2 }); @@ -64,7 +68,7 @@ describe('GovernorERC721', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // initiate fresh proposal this.proposal = this.helper.setProposal( [ diff --git a/test/governance/extensions/GovernorNoncesKeyed.test.js b/test/governance/extensions/GovernorNoncesKeyed.test.js index 955af4bf79c..ee484cbed7d 100644 --- a/test/governance/extensions/GovernorNoncesKeyed.test.js +++ b/test/governance/extensions/GovernorNoncesKeyed.test.js @@ -1,11 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { getDomain, Ballot, ExtendedBallot } = require('../../helpers/eip712'); -const { VoteType } = require('../../helpers/enums'); -const { shouldBehaveLikeNoncesKeyed } = require('../../utils/Nonces.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { getDomain, Ballot, ExtendedBallot } from '../../helpers/eip712'; +import { VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; +import { shouldBehaveLikeNoncesKeyed } from '../../utils/Nonces.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'OZ-Governor'; const version = '1'; @@ -39,7 +43,7 @@ describe('GovernorNoncesKeyed', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, 'blocknumber'); + const helper = new GovernorHelper(connection, mock, 'blocknumber'); await helper.connect(owner).delegate({ token: token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token: token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token: token, to: voter3, value: ethers.parseEther('5') }); @@ -61,7 +65,7 @@ describe('GovernorNoncesKeyed', function () { }; beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( diff --git a/test/governance/extensions/GovernorPreventLateQuorum.test.js b/test/governance/extensions/GovernorPreventLateQuorum.test.js index 761087aa945..e69f883bb16 100644 --- a/test/governance/extensions/GovernorPreventLateQuorum.test.js +++ b/test/governance/extensions/GovernorPreventLateQuorum.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { ProposalState, VoteType } = require('../../helpers/enums'); -const time = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ProposalState, VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -42,7 +46,7 @@ describe('GovernorPreventLateQuorum', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -53,7 +57,7 @@ describe('GovernorPreventLateQuorum', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // initiate fresh proposal this.proposal = this.helper.setProposal( [ diff --git a/test/governance/extensions/GovernorProposalGuardian.test.js b/test/governance/extensions/GovernorProposalGuardian.test.js index 1741072c398..28494b615bb 100644 --- a/test/governance/extensions/GovernorProposalGuardian.test.js +++ b/test/governance/extensions/GovernorProposalGuardian.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { impersonate } = require('../../helpers/account'); -const { GovernorHelper } = require('../../helpers/governance'); -const { ProposalState } = require('../../helpers/enums'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ProposalState } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + helpers: { impersonate }, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -35,11 +39,10 @@ describe('GovernorProposalGuardian', function () { 10n, // quorumNumeratorValue ]); - await impersonate(mock.target); await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -50,7 +53,7 @@ describe('GovernorProposalGuardian', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( @@ -74,8 +77,8 @@ describe('GovernorProposalGuardian', function () { describe('set proposal guardian', function () { it('from governance', async function () { - const governorSigner = await ethers.getSigner(this.mock.target); - await expect(this.mock.connect(governorSigner).setProposalGuardian(this.guardian)) + const governorAsSigner = await impersonate(this.mock); + await expect(this.mock.connect(governorAsSigner).setProposalGuardian(this.guardian)) .to.emit(this.mock, 'ProposalGuardianSet') .withArgs(ethers.ZeroAddress, this.guardian); await expect(this.mock.proposalGuardian()).to.eventually.equal(this.guardian); diff --git a/test/governance/extensions/GovernorSequentialProposalId.test.js b/test/governance/extensions/GovernorSequentialProposalId.test.js index 7fb2c8b40fd..afaea7d4dfe 100644 --- a/test/governance/extensions/GovernorSequentialProposalId.test.js +++ b/test/governance/extensions/GovernorSequentialProposalId.test.js @@ -1,11 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { VoteType } = require('../../helpers/enums'); -const iterate = require('../../helpers/iterate'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; +import { range } from '../../helpers/iterate'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -52,7 +56,7 @@ describe('GovernorSequentialProposalId', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token: token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token: token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token: token, to: voter3, value: ethers.parseEther('5') }); @@ -75,7 +79,7 @@ describe('GovernorSequentialProposalId', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); this.proposal = this.helper.setProposal( [ @@ -90,7 +94,7 @@ describe('GovernorSequentialProposalId', function () { }); it('sequential proposal ids', async function () { - for (const i of iterate.range(1, 10)) { + for (const i of range(1, 10)) { this.proposal.description = ``; await expect(this.mock.hashProposal(...this.proposal.shortProposal)).to.eventually.equal(this.proposal.hash); @@ -124,7 +128,7 @@ describe('GovernorSequentialProposalId', function () { const offset = 69420; await this.mock.$_initializeLatestProposalId(offset); - for (const i of iterate.range(offset + 1, offset + 10)) { + for (const i of range(offset + 1, offset + 10)) { this.proposal.description = ``; await expect(this.mock.hashProposal(...this.proposal.shortProposal)).to.eventually.equal(this.proposal.hash); diff --git a/test/governance/extensions/GovernorStorage.test.js b/test/governance/extensions/GovernorStorage.test.js index 51118f26e88..9a275f9a584 100644 --- a/test/governance/extensions/GovernorStorage.test.js +++ b/test/governance/extensions/GovernorStorage.test.js @@ -1,11 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); - -const { GovernorHelper, timelockSalt } = require('../../helpers/governance'); -const { VoteType } = require('../../helpers/enums'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { VoteType } from '../../helpers/enums'; +import { GovernorHelper, timelockSalt } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -54,7 +58,7 @@ describe('GovernorStorage', function () { await timelock.grantRole(EXECUTOR_ROLE, ethers.ZeroAddress); await timelock.revokeRole(DEFAULT_ADMIN_ROLE, deployer); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -65,7 +69,7 @@ describe('GovernorStorage', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // initiate fresh proposal this.proposal = this.helper.setProposal( [ diff --git a/test/governance/extensions/GovernorSuperQuorum.test.js b/test/governance/extensions/GovernorSuperQuorum.test.js index 9a8b8455cb7..f67faabeb0c 100644 --- a/test/governance/extensions/GovernorSuperQuorum.test.js +++ b/test/governance/extensions/GovernorSuperQuorum.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { ProposalState, VoteType } = require('../../helpers/enums'); -const time = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ProposalState, VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -56,7 +60,7 @@ describe('GovernorSuperQuorum', function () { await timelock.grantRole(EXECUTOR_ROLE, ethers.ZeroAddress); await timelock.revokeRole(DEFAULT_ADMIN_ROLE, proposer); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(proposer).delegate({ token, to: voter1, value: 40 }); await helper.connect(proposer).delegate({ token, to: voter2, value: 30 }); await helper.connect(proposer).delegate({ token, to: voter3, value: 20 }); @@ -68,7 +72,7 @@ describe('GovernorSuperQuorum', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( diff --git a/test/governance/extensions/GovernorTimelockAccess.test.js b/test/governance/extensions/GovernorTimelockAccess.test.js index 5eea6478abd..78ace9b64ae 100644 --- a/test/governance/extensions/GovernorTimelockAccess.test.js +++ b/test/governance/extensions/GovernorTimelockAccess.test.js @@ -1,14 +1,18 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { hashOperation } = require('../../helpers/access-manager'); -const { max } = require('../../helpers/math'); -const { selector } = require('../../helpers/methods'); -const { ProposalState, VoteType } = require('../../helpers/enums'); -const time = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { hashOperation } from '../../helpers/access-manager'; +import { ProposalState, VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; +import { max } from '../../helpers/math'; +import { selector } from '../../helpers/methods'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; function prepareOperation({ sender, target, value = 0n, data = '0x' }) { return { @@ -55,7 +59,7 @@ describe('GovernorTimelockAccess', function () { await admin.sendTransaction({ to: mock, value }); await token.$_mint(admin, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(admin).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(admin).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(admin).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -66,7 +70,7 @@ describe('GovernorTimelockAccess', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // restricted proposal this.restricted = prepareOperation({ @@ -407,7 +411,7 @@ describe('GovernorTimelockAccess', function () { await this.manager.connect(this.admin).grantRole(roleId, this.mock, delay); // Set proposals - const original = new GovernorHelper(this.mock, mode); + const original = new GovernorHelper(connection, this.mock, mode); await original.setProposal([this.restricted.operation, this.unrestricted.operation], 'descr'); // Go through all the governance process @@ -424,7 +428,7 @@ describe('GovernorTimelockAccess', function () { .cancel(this.mock, this.restricted.operation.target, this.restricted.operation.data); // Reschedule the same operation in a different proposal to avoid "AccessManagerNotScheduled" error - const rescheduled = new GovernorHelper(this.mock, mode); + const rescheduled = new GovernorHelper(connection, this.mock, mode); await rescheduled.setProposal([this.restricted.operation], 'descr'); await rescheduled.propose(); await rescheduled.waitForSnapshot(); @@ -562,7 +566,7 @@ describe('GovernorTimelockAccess', function () { it('cancels restricted with queueing if the same operation is part of a more recent proposal (internal)', async function () { // Set proposals - const original = new GovernorHelper(this.mock, mode); + const original = new GovernorHelper(connection, this.mock, mode); await original.setProposal([this.restricted.operation], 'descr'); // Go through all the governance process @@ -578,7 +582,7 @@ describe('GovernorTimelockAccess', function () { .cancel(this.mock, this.restricted.operation.target, this.restricted.operation.data); // Another proposal is added with the same operation - const rescheduled = new GovernorHelper(this.mock, mode); + const rescheduled = new GovernorHelper(connection, this.mock, mode); await rescheduled.setProposal([this.restricted.operation], 'another descr'); // Queue the new proposal @@ -660,8 +664,8 @@ describe('GovernorTimelockAccess', function () { const operationAId = hashOperation(this.mock.target, operationA.target, operationA.data); const operationBId = hashOperation(this.mock.target, operationB.target, operationB.data); - const proposal1 = new GovernorHelper(this.mock, mode); - const proposal2 = new GovernorHelper(this.mock, mode); + const proposal1 = new GovernorHelper(connection, this.mock, mode); + const proposal2 = new GovernorHelper(connection, this.mock, mode); proposal1.setProposal([operationA, operationB], 'proposal A+B'); proposal2.setProposal([operationA, operationC], 'proposal A+C'); diff --git a/test/governance/extensions/GovernorTimelockCompound.test.js b/test/governance/extensions/GovernorTimelockCompound.test.js index cd82481d500..b3dbb91b895 100644 --- a/test/governance/extensions/GovernorTimelockCompound.test.js +++ b/test/governance/extensions/GovernorTimelockCompound.test.js @@ -1,11 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { ProposalState, VoteType } = require('../../helpers/enums'); -const time = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { ProposalState, VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -46,7 +50,7 @@ describe('GovernorTimelockCompound', function () { await owner.sendTransaction({ to: timelock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -57,7 +61,7 @@ describe('GovernorTimelockCompound', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( @@ -362,7 +366,7 @@ describe('GovernorTimelockCompound', function () { const txExecute = this.helper.execute(); - await expect(txExecute).to.changeTokenBalances(this.token, [this.mock, this.other], [-1n, 1n]); + await expect(txExecute).to.changeTokenBalances(ethers, this.token, [this.mock, this.other], [-1n, 1n]); await expect(txExecute).to.emit(this.token, 'Transfer').withArgs(this.mock, this.other, 1n); }); diff --git a/test/governance/extensions/GovernorTimelockControl.test.js b/test/governance/extensions/GovernorTimelockControl.test.js index 507c7e27832..39e573006f4 100644 --- a/test/governance/extensions/GovernorTimelockControl.test.js +++ b/test/governance/extensions/GovernorTimelockControl.test.js @@ -1,12 +1,16 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); - -const { GovernorHelper, timelockSalt } = require('../../helpers/governance'); -const { OperationState, ProposalState, VoteType } = require('../../helpers/enums'); -const time = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { OperationState, ProposalState, VoteType } from '../../helpers/enums'; +import { GovernorHelper, timelockSalt } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -55,7 +59,7 @@ describe('GovernorTimelockControl', function () { await timelock.grantRole(EXECUTOR_ROLE, ethers.ZeroAddress); await timelock.revokeRole(DEFAULT_ADMIN_ROLE, deployer); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -66,7 +70,7 @@ describe('GovernorTimelockControl', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( @@ -331,7 +335,7 @@ describe('GovernorTimelockControl', function () { const txExecute = await this.helper.execute(); - await expect(txExecute).to.changeTokenBalances(this.token, [this.mock, this.other], [-1n, 1n]); + await expect(txExecute).to.changeTokenBalances(ethers, this.token, [this.mock, this.other], [-1n, 1n]); await expect(txExecute).to.emit(this.token, 'Transfer').withArgs(this.mock, this.other, 1n); }); @@ -359,6 +363,7 @@ describe('GovernorTimelockControl', function () { await this.helper.waitForEta(); await expect(this.helper.execute()).to.changeEtherBalances( + ethers, [this.timelock, this.mock, this.other], [-t2g, t2g - g2o, g2o], ); diff --git a/test/governance/extensions/GovernorVotesQuorumFraction.test.js b/test/governance/extensions/GovernorVotesQuorumFraction.test.js index 99afd393173..0b3500287e4 100644 --- a/test/governance/extensions/GovernorVotesQuorumFraction.test.js +++ b/test/governance/extensions/GovernorVotesQuorumFraction.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { ProposalState, VoteType } = require('../../helpers/enums'); -const time = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ProposalState, VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture, mine }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -35,7 +39,7 @@ describe('GovernorVotesQuorumFraction', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -46,7 +50,7 @@ describe('GovernorVotesQuorumFraction', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( diff --git a/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js b/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js index 10a444848bf..c26808dc9dc 100644 --- a/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js +++ b/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { ProposalState, VoteType } = require('../../helpers/enums'); -const time = require('../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ProposalState, VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -43,7 +47,7 @@ describe('GovernorVotesSuperQuorumFraction', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('30') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('20') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('15') }); @@ -54,7 +58,7 @@ describe('GovernorVotesSuperQuorumFraction', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( diff --git a/test/governance/extensions/GovernorWithParams.test.js b/test/governance/extensions/GovernorWithParams.test.js index db19bc61683..56781d4ec2e 100644 --- a/test/governance/extensions/GovernorWithParams.test.js +++ b/test/governance/extensions/GovernorWithParams.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { GovernorHelper } = require('../../helpers/governance'); -const { VoteType } = require('../../helpers/enums'); -const { getDomain, ExtendedBallot } = require('../../helpers/eip712'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { getDomain, ExtendedBallot } from '../../helpers/eip712'; +import { VoteType } from '../../helpers/enums'; +import { GovernorHelper } from '../../helpers/governance'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -37,7 +41,7 @@ describe('GovernorWithParams', function () { await owner.sendTransaction({ to: mock, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(mock, mode); + const helper = new GovernorHelper(connection, mock, mode); await helper.connect(owner).delegate({ token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token, to: voter3, value: ethers.parseEther('5') }); @@ -48,7 +52,7 @@ describe('GovernorWithParams', function () { describe(`using ${Token}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); // default proposal this.proposal = this.helper.setProposal( diff --git a/test/governance/utils/ERC6372.behavior.js b/test/governance/utils/ERC6372.behavior.js index 32f27b59a7c..ea969aab979 100644 --- a/test/governance/utils/ERC6372.behavior.js +++ b/test/governance/utils/ERC6372.behavior.js @@ -1,7 +1,6 @@ -const { expect } = require('chai'); -const time = require('../../helpers/time'); +import { expect } from 'chai'; -function shouldBehaveLikeERC6372(mode = 'blocknumber') { +export function shouldBehaveLikeERC6372(mode = 'blocknumber') { describe(`ERC-6372 behavior in ${mode} mode`, function () { beforeEach(async function () { this.mock = this.mock ?? this.token ?? this.votes; @@ -9,7 +8,7 @@ function shouldBehaveLikeERC6372(mode = 'blocknumber') { it('should have a correct clock value', async function () { const currentClock = await this.mock.clock(); - const expectedClock = await time.clock[mode](); + const expectedClock = await this.helpers.time.clock[mode](); expect(currentClock).to.equal(expectedClock, `Clock mismatch in ${mode} mode`); }); @@ -22,7 +21,3 @@ function shouldBehaveLikeERC6372(mode = 'blocknumber') { }); }); } - -module.exports = { - shouldBehaveLikeERC6372, -}; diff --git a/test/governance/utils/Votes.behavior.js b/test/governance/utils/Votes.behavior.js index 1e6e01d9d1c..707e4c08b3c 100644 --- a/test/governance/utils/Votes.behavior.js +++ b/test/governance/utils/Votes.behavior.js @@ -1,13 +1,9 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { mine } = require('@nomicfoundation/hardhat-network-helpers'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { Delegation, getDomain } from '../../helpers/eip712'; +import { shouldBehaveLikeERC6372 } from './ERC6372.behavior'; -const { getDomain, Delegation } = require('../../helpers/eip712'); -const time = require('../../helpers/time'); - -const { shouldBehaveLikeERC6372 } = require('./ERC6372.behavior'); - -function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }) { +export function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true }) { beforeEach(async function () { [this.delegator, this.delegatee, this.alice, this.bob, this.other] = this.accounts; this.domain = await getDomain(this.votes); @@ -43,7 +39,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } expect(await this.votes.delegates(this.alice)).to.equal(ethers.ZeroAddress); const tx = await this.votes.connect(this.alice).delegate(this.alice); - const timepoint = await time.clockFromReceipt[mode](tx); + const timepoint = await this.helpers.time.clockFromReceipt[mode](tx); await expect(tx) .to.emit(this.votes, 'DelegateChanged') @@ -54,7 +50,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } expect(await this.votes.delegates(this.alice)).to.equal(this.alice); expect(await this.votes.getVotes(this.alice)).to.equal(weight); expect(await this.votes.getPastVotes(this.alice, timepoint - 1n)).to.equal(0n); - await mine(); + await this.networkHelpers.mine(); expect(await this.votes.getPastVotes(this.alice, timepoint)).to.equal(weight); }); @@ -68,7 +64,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } expect(await this.votes.getVotes(this.bob)).to.equal(0n); const tx = await this.votes.connect(this.alice).delegate(this.bob); - const timepoint = await time.clockFromReceipt[mode](tx); + const timepoint = await this.helpers.time.clockFromReceipt[mode](tx); await expect(tx) .to.emit(this.votes, 'DelegateChanged') @@ -84,7 +80,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } expect(await this.votes.getPastVotes(this.alice, timepoint - 1n)).to.equal(weight); expect(await this.votes.getPastVotes(this.bob, timepoint - 1n)).to.equal(0n); - await mine(); + await this.networkHelpers.mine(); expect(await this.votes.getPastVotes(this.alice, timepoint)).to.equal(0n); expect(await this.votes.getPastVotes(this.bob, timepoint)).to.equal(weight); }); @@ -111,7 +107,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } expect(await this.votes.delegates(this.delegator)).to.equal(ethers.ZeroAddress); const tx = await this.votes.delegateBySig(this.delegatee, nonce, ethers.MaxUint256, v, r, s); - const timepoint = await time.clockFromReceipt[mode](tx); + const timepoint = await this.helpers.time.clockFromReceipt[mode](tx); await expect(tx) .to.emit(this.votes, 'DelegateChanged') @@ -123,7 +119,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } expect(await this.votes.getVotes(this.delegator.address)).to.equal(0n); expect(await this.votes.getVotes(this.delegatee)).to.equal(weight); expect(await this.votes.getPastVotes(this.delegatee, timepoint - 1n)).to.equal(0n); - await mine(); + await this.networkHelpers.mine(); expect(await this.votes.getPastVotes(this.delegatee, timepoint)).to.equal(weight); }); @@ -191,7 +187,7 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } }); it('rejects expired permit', async function () { - const expiry = (await time.clock.timestamp()) - 1n; + const expiry = (await this.helpers.time.clock.timestamp()) - 1n; const { r, s, v } = await this.delegator .signTypedData( this.domain, @@ -233,29 +229,29 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } // t0 = mint #0 const t0 = await this.votes.$_mint(this.alice, tokens[0]); - await mine(); + await this.networkHelpers.mine(); // t1 = mint #1 const t1 = await this.votes.$_mint(this.alice, tokens[1]); - await mine(); + await this.networkHelpers.mine(); // t2 = burn #1 const t2 = await this.votes.$_burn(...(fungible ? [this.alice] : []), tokens[1]); - await mine(); + await this.networkHelpers.mine(); // t3 = mint #2 const t3 = await this.votes.$_mint(this.alice, tokens[2]); - await mine(); + await this.networkHelpers.mine(); // t4 = burn #0 const t4 = await this.votes.$_burn(...(fungible ? [this.alice] : []), tokens[0]); - await mine(); + await this.networkHelpers.mine(); // t5 = burn #2 const t5 = await this.votes.$_burn(...(fungible ? [this.alice] : []), tokens[2]); - await mine(); + await this.networkHelpers.mine(); - t0.timepoint = await time.clockFromReceipt[mode](t0); - t1.timepoint = await time.clockFromReceipt[mode](t1); - t2.timepoint = await time.clockFromReceipt[mode](t2); - t3.timepoint = await time.clockFromReceipt[mode](t3); - t4.timepoint = await time.clockFromReceipt[mode](t4); - t5.timepoint = await time.clockFromReceipt[mode](t5); + t0.timepoint = await this.helpers.time.clockFromReceipt[mode](t0); + t1.timepoint = await this.helpers.time.clockFromReceipt[mode](t1); + t2.timepoint = await this.helpers.time.clockFromReceipt[mode](t2); + t3.timepoint = await this.helpers.time.clockFromReceipt[mode](t3); + t4.timepoint = await this.helpers.time.clockFromReceipt[mode](t4); + t5.timepoint = await this.helpers.time.clockFromReceipt[mode](t5); expect(await this.votes.getPastTotalSupply(t0.timepoint - 1n)).to.equal(0n); expect(await this.votes.getPastTotalSupply(t0.timepoint)).to.equal(weight[0]); @@ -299,8 +295,8 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } it('returns the latest block if >= last checkpoint block', async function () { const delegate = await this.votes.connect(this.alice).delegate(this.bob); - const timepoint = await time.clockFromReceipt[mode](delegate); - await mine(2); + const timepoint = await this.helpers.time.clockFromReceipt[mode](delegate); + await this.networkHelpers.mine(2); const latest = await this.votes.getVotes(this.bob); expect(await this.votes.getPastVotes(this.bob, timepoint)).to.equal(latest); @@ -308,10 +304,10 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } }); it('returns zero if < first checkpoint block', async function () { - await mine(); + await this.networkHelpers.mine(); const delegate = await this.votes.connect(this.alice).delegate(this.bob); - const timepoint = await time.clockFromReceipt[mode](delegate); - await mine(2); + const timepoint = await this.helpers.time.clockFromReceipt[mode](delegate); + await this.networkHelpers.mine(2); expect(await this.votes.getPastVotes(this.bob, timepoint - 1n)).to.equal(0n); }); @@ -319,7 +315,3 @@ function shouldBehaveLikeVotes(tokens, { mode = 'blocknumber', fungible = true } }); }); } - -module.exports = { - shouldBehaveLikeVotes, -}; diff --git a/test/governance/utils/Votes.test.js b/test/governance/utils/Votes.test.js index 7acacfc6661..3b37cf556ed 100644 --- a/test/governance/utils/Votes.test.js +++ b/test/governance/utils/Votes.test.js @@ -1,12 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { sum } = require('../../helpers/math'); -const { zip } = require('../../helpers/iterate'); -const time = require('../../helpers/time'); - -const { shouldBehaveLikeVotes } = require('./Votes.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { zip } from '../../helpers/iterate'; +import { sum } from '../../helpers/math'; +import { shouldBehaveLikeVotes } from './Votes.behavior'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = connection; const MODES = { blocknumber: '$VotesMock', @@ -36,7 +39,7 @@ describe('Votes', function () { describe(`vote with ${mode}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeVotes(AMOUNTS, { mode, fungible: true }); diff --git a/test/governance/utils/VotesExtended.test.js b/test/governance/utils/VotesExtended.test.js index 4a66ef2748c..e0e970177e4 100644 --- a/test/governance/utils/VotesExtended.test.js +++ b/test/governance/utils/VotesExtended.test.js @@ -1,12 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); - -const { sum } = require('../../helpers/math'); -const { zip } = require('../../helpers/iterate'); -const time = require('../../helpers/time'); - -const { shouldBehaveLikeVotes } = require('./Votes.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { zip } from '../../helpers/iterate'; +import { sum } from '../../helpers/math'; +import { shouldBehaveLikeVotes } from './Votes.behavior'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture, mine }, +} = connection; const MODES = { blocknumber: '$VotesExtendedMock', @@ -36,7 +39,7 @@ describe('VotesExtended', function () { describe(`vote with ${mode}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeVotes(AMOUNTS, { mode, fungible: true }); diff --git a/test/helpers/access-manager.js b/test/helpers/access-manager.js index 3b8343059c0..9e4d13f0a1f 100644 --- a/test/helpers/access-manager.js +++ b/test/helpers/access-manager.js @@ -1,10 +1,8 @@ -const { ethers } = require('hardhat'); +import { ethers } from 'ethers'; +import { MAX_UINT64 } from './constants'; +import { upgradeableSlot } from './storage'; -const { MAX_UINT64 } = require('./constants'); -const time = require('./time'); -const { upgradeableSlot } = require('./storage'); - -function buildBaseRoles() { +export function buildBaseRoles() { const roles = { ADMIN: { id: 0n, @@ -41,20 +39,20 @@ function buildBaseRoles() { return roles; } -const formatAccess = access => [access[0], access[1].toString()]; +export const formatAccess = access => [access[0], access[1].toString()]; -const MINSETBACK = time.duration.days(5); -const EXPIRATION = time.duration.weeks(1); +export const MINSETBACK = 432000n; // time.duration.days(5); +export const EXPIRATION = 604800n; // time.duration.weeks(1); -const EXECUTION_ID_STORAGE_SLOT = upgradeableSlot('AccessManager', 3n); -const CONSUMING_SCHEDULE_STORAGE_SLOT = upgradeableSlot('AccessManaged', 0n); +export const EXECUTION_ID_STORAGE_SLOT = await upgradeableSlot('AccessManager', 3n); +export const CONSUMING_SCHEDULE_STORAGE_SLOT = await upgradeableSlot('AccessManaged', 0n); /** * @requires this.{manager, caller, target, calldata} */ -async function prepareOperation(manager, { caller, target, calldata, delay }) { - const scheduledAt = (await time.clock.timestamp()) + 1n; - await time.increaseTo.timestamp(scheduledAt, false); // Fix next block timestamp for predictability +export async function prepareOperation(manager, { caller, target, calldata, delay }) { + const scheduledAt = (await this.helpers.time.clock.timestamp()) + 1n; + await this.helpers.time.increaseTo.timestamp(scheduledAt, false); // Fix next block timestamp for predictability return { schedule: () => manager.connect(caller).schedule(target, calldata, scheduledAt + delay), @@ -65,21 +63,10 @@ async function prepareOperation(manager, { caller, target, calldata, delay }) { const lazyGetAddress = addressable => addressable.address ?? addressable.target ?? addressable; -const hashOperation = (caller, target, data) => +export const hashOperation = (caller, target, data) => ethers.keccak256( ethers.AbiCoder.defaultAbiCoder().encode( ['address', 'address', 'bytes'], [lazyGetAddress(caller), lazyGetAddress(target), data], ), ); - -module.exports = { - buildBaseRoles, - formatAccess, - MINSETBACK, - EXPIRATION, - EXECUTION_ID_STORAGE_SLOT, - CONSUMING_SCHEDULE_STORAGE_SLOT, - prepareOperation, - hashOperation, -}; diff --git a/test/helpers/account.js b/test/helpers/account.js index f3753098586..03a20e5e239 100644 --- a/test/helpers/account.js +++ b/test/helpers/account.js @@ -1,16 +1,14 @@ -const { ethers } = require('hardhat'); -const { impersonateAccount, setBalance } = require('@nomicfoundation/hardhat-network-helpers'); +import { ethers } from 'ethers'; // Hardhat default balance const DEFAULT_BALANCE = 10000n * ethers.WeiPerEther; -const impersonate = (account, balance = DEFAULT_BALANCE) => { - const address = account.target ?? account.address ?? account; - return impersonateAccount(address) - .then(() => setBalance(address, balance)) - .then(() => ethers.getSigner(address)); -}; - -module.exports = { - impersonate, -}; +export const impersonate = + ({ ethers, networkHelpers }) => + (account, balance = DEFAULT_BALANCE) => { + const address = account.target ?? account.address ?? account; + return networkHelpers + .impersonateAccount(address) + .then(() => networkHelpers.setBalance(address, balance)) + .then(() => ethers.getSigner(address)); + }; diff --git a/test/helpers/chains.js b/test/helpers/chains.js index b0cbf35a0d9..2771cd640a4 100644 --- a/test/helpers/chains.js +++ b/test/helpers/chains.js @@ -1,9 +1,8 @@ // The following listing does not pretend to be exhaustive or even accurate. It SHOULD NOT be used in production. -const { ethers } = require('hardhat'); -const { mapValues } = require('./iterate'); - -const { addressCoder } = require('interoperable-addresses'); +import { ethers } from 'ethers'; +import { addressCoder } from 'interoperable-addresses'; +import { mapValues } from './iterate'; // EVM (https://axelarscan.io/resources/chains?type=evm) const ethereum = { @@ -43,14 +42,15 @@ const format = ({ namespace, reference }) => ({ addressCoder.encode({ chainType: namespace, reference, address: other.target ?? other.address ?? other }), }); -module.exports = { - CHAINS: mapValues( - Object.assign( - mapValues(ethereum, reference => ({ namespace: 'eip155', reference })), - mapValues(solana, reference => ({ namespace: 'solana', reference })), - ), - format, +export const CHAINS = mapValues( + Object.assign( + mapValues(ethereum, reference => ({ namespace: 'eip155', reference })), + mapValues(solana, reference => ({ namespace: 'solana', reference })), ), - getLocalChain: () => - ethers.provider.getNetwork().then(({ chainId }) => format({ namespace: 'eip155', reference: chainId })), -}; + format, +); + +export const getLocalChain = provider => + provider + .send('eth_chainId', []) + .then(chainId => format({ namespace: 'eip155', reference: ethers.toBigInt(chainId) })); diff --git a/test/helpers/constants.js b/test/helpers/constants.js index d08c3ec0455..8b648148ffd 100644 --- a/test/helpers/constants.js +++ b/test/helpers/constants.js @@ -1,7 +1,5 @@ -module.exports = { - MAX_UINT16: 2n ** 16n - 1n, - MAX_UINT32: 2n ** 32n - 1n, - MAX_UINT48: 2n ** 48n - 1n, - MAX_UINT64: 2n ** 64n - 1n, - MAX_UINT128: 2n ** 128n - 1n, -}; +export const MAX_UINT16 = 2n ** 16n - 1n; +export const MAX_UINT32 = 2n ** 32n - 1n; +export const MAX_UINT48 = 2n ** 48n - 1n; +export const MAX_UINT64 = 2n ** 64n - 1n; +export const MAX_UINT128 = 2n ** 128n - 1n; diff --git a/test/helpers/deploy.js b/test/helpers/deploy.js deleted file mode 100644 index 0d4b9563bc9..00000000000 --- a/test/helpers/deploy.js +++ /dev/null @@ -1,14 +0,0 @@ -const { artifacts, ethers } = require('hardhat'); -const { setCode } = require('@nomicfoundation/hardhat-network-helpers'); -const { generators } = require('./random'); - -const forceDeployCode = (name, address = generators.address(), runner = ethers.provider) => - artifacts - .readArtifact(name) - .then(({ abi, deployedBytecode }) => - setCode(address, deployedBytecode).then(() => new ethers.Contract(address, abi, runner)), - ); - -module.exports = { - forceDeployCode, -}; diff --git a/test/helpers/eip712-types.js b/test/helpers/eip712-types.js index fb6fe3aebaf..35020785e35 100644 --- a/test/helpers/eip712-types.js +++ b/test/helpers/eip712-types.js @@ -1,61 +1,74 @@ -const { mapValues } = require('./iterate'); +export const formatType = schema => Object.entries(schema).map(([name, type]) => ({ name, type })); -const formatType = schema => Object.entries(schema).map(([name, type]) => ({ name, type })); - -module.exports = mapValues( - { - EIP712Domain: { - name: 'string', - version: 'string', - chainId: 'uint256', - verifyingContract: 'address', - salt: 'bytes32', - }, - Permit: { owner: 'address', spender: 'address', value: 'uint256', nonce: 'uint256', deadline: 'uint256' }, - Ballot: { proposalId: 'uint256', support: 'uint8', voter: 'address', nonce: 'uint256' }, - ExtendedBallot: { - proposalId: 'uint256', - support: 'uint8', - voter: 'address', - nonce: 'uint256', - reason: 'string', - params: 'bytes', - }, - OverrideBallot: { proposalId: 'uint256', support: 'uint8', voter: 'address', nonce: 'uint256', reason: 'string' }, - Delegation: { delegatee: 'address', nonce: 'uint256', expiry: 'uint256' }, - ForwardRequest: { - from: 'address', - to: 'address', - value: 'uint256', - gas: 'uint256', - nonce: 'uint256', - deadline: 'uint48', - data: 'bytes', - }, - PackedUserOperation: { - sender: 'address', - nonce: 'uint256', - initCode: 'bytes', - callData: 'bytes', - accountGasLimits: 'bytes32', - preVerificationGas: 'uint256', - gasFees: 'bytes32', - paymasterAndData: 'bytes', - }, - UserOperationRequest: { - sender: 'address', - nonce: 'uint256', - initCode: 'bytes', - callData: 'bytes', - accountGasLimits: 'bytes32', - preVerificationGas: 'uint256', - gasFees: 'bytes32', - paymasterVerificationGasLimit: 'uint256', - paymasterPostOpGasLimit: 'uint256', - validAfter: 'uint48', - validUntil: 'uint48', - }, - }, - formatType, -); -module.exports.formatType = formatType; +export const EIP712Domain = formatType({ + name: 'string', + version: 'string', + chainId: 'uint256', + verifyingContract: 'address', + salt: 'bytes32', +}); +export const Permit = formatType({ + owner: 'address', + spender: 'address', + value: 'uint256', + nonce: 'uint256', + deadline: 'uint256', +}); +export const Ballot = formatType({ + proposalId: 'uint256', + support: 'uint8', + voter: 'address', + nonce: 'uint256', +}); +export const ExtendedBallot = formatType({ + proposalId: 'uint256', + support: 'uint8', + voter: 'address', + nonce: 'uint256', + reason: 'string', + params: 'bytes', +}); +export const OverrideBallot = formatType({ + proposalId: 'uint256', + support: 'uint8', + voter: 'address', + nonce: 'uint256', + reason: 'string', +}); +export const Delegation = formatType({ + delegatee: 'address', + nonce: 'uint256', + expiry: 'uint256', +}); +export const ForwardRequest = formatType({ + from: 'address', + to: 'address', + value: 'uint256', + gas: 'uint256', + nonce: 'uint256', + deadline: 'uint48', + data: 'bytes', +}); +export const PackedUserOperation = formatType({ + sender: 'address', + nonce: 'uint256', + initCode: 'bytes', + callData: 'bytes', + accountGasLimits: 'bytes32', + preVerificationGas: 'uint256', + gasFees: 'bytes32', + paymasterAndData: 'bytes', +}); +export const UserOperationRequest = formatType({ + sender: 'address', + nonce: 'uint256', + initCode: 'bytes', + callData: 'bytes', + accountGasLimits: 'bytes32', + preVerificationGas: 'uint256', + gasFees: 'bytes32', + paymasterVerificationGasLimit: 'uint256', + paymasterPostOpGasLimit: 'uint256', + validAfter: 'uint48', + validUntil: 'uint48', +}); diff --git a/test/helpers/eip712.js b/test/helpers/eip712.js index 3843ac02690..a2d754686d0 100644 --- a/test/helpers/eip712.js +++ b/test/helpers/eip712.js @@ -1,7 +1,9 @@ -const { ethers } = require('hardhat'); -const types = require('./eip712-types'); +import { ethers } from 'ethers'; +import { EIP712Domain } from './eip712-types'; -async function getDomain(contract) { +export * from './eip712-types'; + +export async function getDomain(contract) { const { fields, name, version, chainId, verifyingContract, salt, extensions } = await contract.eip712Domain(); if (extensions.length > 0) { @@ -16,7 +18,7 @@ async function getDomain(contract) { salt, }; - for (const [i, { name }] of types.EIP712Domain.entries()) { + for (const [i, { name }] of EIP712Domain.entries()) { if (!(fields & (1 << i))) { delete domain[name]; } @@ -25,21 +27,15 @@ async function getDomain(contract) { return domain; } -function domainType(domain) { - return types.EIP712Domain.filter(({ name }) => domain[name] !== undefined); +export function domainType(domain) { + return EIP712Domain.filter(({ name }) => domain[name] !== undefined); } -function hashTypedData(domain, structHash) { +export const domainSeparator = ethers.TypedDataEncoder.hashDomain; + +export function hashTypedData(domain, structHash) { return ethers.solidityPackedKeccak256( ['bytes', 'bytes32', 'bytes32'], ['0x1901', ethers.TypedDataEncoder.hashDomain(domain), structHash], ); } - -module.exports = { - getDomain, - domainType, - domainSeparator: ethers.TypedDataEncoder.hashDomain, - hashTypedData, - ...types, -}; diff --git a/test/helpers/enums.js b/test/helpers/enums.js index 1e2428bfc0d..f790e04a56a 100644 --- a/test/helpers/enums.js +++ b/test/helpers/enums.js @@ -1,15 +1,26 @@ -const { ethers } = require('ethers'); +import { ethers } from 'ethers'; -const Enum = (...options) => Object.fromEntries(options.map((key, i) => [key, BigInt(i)])); -const EnumTyped = (...options) => Object.fromEntries(options.map((key, i) => [key, ethers.Typed.uint8(i)])); +export const Enum = (...options) => Object.fromEntries(options.map((key, i) => [key, BigInt(i)])); +export const EnumTyped = (...options) => Object.fromEntries(options.map((key, i) => [key, ethers.Typed.uint8(i)])); -module.exports = { - Enum, - EnumTyped, - ProposalState: Enum('Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'), - VoteType: Object.assign(Enum('Against', 'For', 'Abstain'), { Parameters: 255n }), - Rounding: EnumTyped('Floor', 'Ceil', 'Trunc', 'Expand'), - OperationState: Enum('Unset', 'Waiting', 'Ready', 'Done'), - RevertType: EnumTyped('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic'), - ValidationRange: Enum('Timestamp', 'Block'), -}; +export const ProposalState = Enum( + 'Pending', + 'Active', + 'Canceled', + 'Defeated', + 'Succeeded', + 'Queued', + 'Expired', + 'Executed', +); +export const VoteType = Object.assign(Enum('Against', 'For', 'Abstain'), { Parameters: 255n }); +export const Rounding = EnumTyped('Floor', 'Ceil', 'Trunc', 'Expand'); +export const OperationState = Enum('Unset', 'Waiting', 'Ready', 'Done'); +export const RevertType = EnumTyped( + 'None', + 'RevertWithoutMessage', + 'RevertWithMessage', + 'RevertWithCustomError', + 'Panic', +); +export const ValidationRange = Enum('Timestamp', 'Block'); diff --git a/test/helpers/erc4337.js b/test/helpers/erc4337.js index ebde76c4c74..984f528a8dd 100644 --- a/test/helpers/erc4337.js +++ b/test/helpers/erc4337.js @@ -1,8 +1,9 @@ -const { ethers, config, predeploy } = require('hardhat'); -const { ValidationRange } = require('./enums'); +import { config } from 'hardhat'; +import { ethers } from 'ethers'; +import { ValidationRange } from './enums'; -const SIG_VALIDATION_SUCCESS = '0x0000000000000000000000000000000000000000'; -const SIG_VALIDATION_FAILURE = '0x0000000000000000000000000000000000000001'; +export const SIG_VALIDATION_SUCCESS = '0x0000000000000000000000000000000000000000'; +export const SIG_VALIDATION_FAILURE = '0x0000000000000000000000000000000000000001'; const PAYMASTER_SIG_MAGIC = '0x22e325a297439656'; const BLOCK_RANGE_FLAG = 0x800000000000n; @@ -16,7 +17,7 @@ function pack(left, right) { return ethers.solidityPacked(['uint128', 'uint128'], [left, right]); } -function packValidationData(validAfter, validUntil, authorizer, range = undefined) { +export function packValidationData(validAfter, validUntil, authorizer, range = undefined) { // if range is not specified, use the value as provided, // otherwise, clean the values (& BLOCK_RANGE_MASK) and set the flag if corresponding to the range. return ethers.solidityPacked( @@ -37,11 +38,11 @@ function packValidationData(validAfter, validUntil, authorizer, range = undefine ); } -function packInitCode(factory, factoryData) { +export function packInitCode(factory, factoryData) { return ethers.solidityPacked(['address', 'bytes'], [getAddress(factory), factoryData]); } -function packPaymasterAndData( +export function packPaymasterAndData( paymaster, paymasterVerificationGasLimit, paymasterPostOpGasLimit, @@ -63,7 +64,7 @@ function packPaymasterAndData( } /// Represent one user operation -class UserOperation { +export class UserOperation { constructor(params) { this.sender = getAddress(params.sender); this.nonce = params.nonce; @@ -116,9 +117,10 @@ const parseInitCode = initCode => ({ }); /// Global ERC-4337 environment helper. -class ERC4337Helper { - constructor() { - this.factoryAsPromise = ethers.deployContract('$Create2'); +export class ERC4337Helper { + constructor(connection) { + this.connection = connection; + this.factoryAsPromise = connection.ethers.deployContract('$Create2'); } async wait() { @@ -128,13 +130,13 @@ class ERC4337Helper { async newAccount(name, extraArgs = [], params = {}) { const env = { - entrypoint: params.entrypoint ?? predeploy.entrypoint.v09, - senderCreator: params.senderCreator ?? predeploy.senderCreator.v09, + entrypoint: params.entrypoint ?? this.connection.ethers.predeploy.entrypoint.v09, + senderCreator: params.senderCreator ?? this.connection.ethers.predeploy.senderCreator.v09, }; const { factory } = await this.wait(); - const accountFactory = await ethers.getContractFactory(name); + const accountFactory = await this.connection.ethers.getContractFactory(name); if (params.eip7702signer) { const delegate = await accountFactory.deploy(...extraArgs); @@ -149,7 +151,7 @@ class ERC4337Helper { ) .then(deployCode => ethers.concat([factory.target, deployCode])); - const instance = await ethers.provider + const instance = await this.connection.ethers.provider .call({ from: env.entrypoint, to: env.senderCreator, @@ -199,11 +201,13 @@ class EIP7702SmartAccount extends SmartAccount { async deploy() { // hardhat signers from @nomicfoundation/hardhat-ethers do not support type 4 txs. // so we rebuild it using "native" ethers - await ethers.Wallet.fromPhrase(config.networks.hardhat.accounts.mnemonic, ethers.provider).sendTransaction({ - to: ethers.ZeroAddress, - authorizationList: [this.authorization], - gasLimit: 46_000n, // 21,000 base + PER_EMPTY_ACCOUNT_COST - }); + await config.networks.default.accounts.mnemonic.get().then(mnemonic => + ethers.Wallet.fromPhrase(mnemonic, this.runner.provider).sendTransaction({ + to: ethers.ZeroAddress, + authorizationList: [this.authorization], + gasLimit: 46_000n, // 21,000 base + PER_EMPTY_ACCOUNT_COST + }), + ); return this; } @@ -232,13 +236,3 @@ class UserOperationWithContext extends UserOperation { return super.hash(this._env.entrypoint); } } - -module.exports = { - SIG_VALIDATION_SUCCESS, - SIG_VALIDATION_FAILURE, - packValidationData, - packInitCode, - packPaymasterAndData, - UserOperation, - ERC4337Helper, -}; diff --git a/test/helpers/erc7579.js b/test/helpers/erc7579.js index 6c3b4759b73..20c54a6fdc0 100644 --- a/test/helpers/erc7579.js +++ b/test/helpers/erc7579.js @@ -1,18 +1,18 @@ -const { ethers } = require('hardhat'); +import { ethers } from 'ethers'; -const MODULE_TYPE_VALIDATOR = 1; -const MODULE_TYPE_EXECUTOR = 2; -const MODULE_TYPE_FALLBACK = 3; -const MODULE_TYPE_HOOK = 4; +export const MODULE_TYPE_VALIDATOR = 1; +export const MODULE_TYPE_EXECUTOR = 2; +export const MODULE_TYPE_FALLBACK = 3; +export const MODULE_TYPE_HOOK = 4; -const EXEC_TYPE_DEFAULT = '0x00'; -const EXEC_TYPE_TRY = '0x01'; +export const EXEC_TYPE_DEFAULT = '0x00'; +export const EXEC_TYPE_TRY = '0x01'; -const CALL_TYPE_CALL = '0x00'; -const CALL_TYPE_BATCH = '0x01'; -const CALL_TYPE_DELEGATE = '0xff'; +export const CALL_TYPE_CALL = '0x00'; +export const CALL_TYPE_BATCH = '0x01'; +export const CALL_TYPE_DELEGATE = '0xff'; -const encodeMode = ({ +export const encodeMode = ({ callType = '0x00', execType = '0x00', selector = '0x00000000', @@ -23,10 +23,10 @@ const encodeMode = ({ [callType, execType, '0x00000000', selector, payload], ); -const encodeSingle = (target, value = 0n, data = '0x') => +export const encodeSingle = (target, value = 0n, data = '0x') => ethers.solidityPacked(['address', 'uint256', 'bytes'], [target.target ?? target.address ?? target, value, data]); -const encodeBatch = (...entries) => +export const encodeBatch = (...entries) => ethers.AbiCoder.defaultAbiCoder().encode( ['(address,uint256,bytes)[]'], [ @@ -38,21 +38,5 @@ const encodeBatch = (...entries) => ], ); -const encodeDelegate = (target, data = '0x') => +export const encodeDelegate = (target, data = '0x') => ethers.solidityPacked(['address', 'bytes'], [target.target ?? target.address ?? target, data]); - -module.exports = { - MODULE_TYPE_VALIDATOR, - MODULE_TYPE_EXECUTOR, - MODULE_TYPE_FALLBACK, - MODULE_TYPE_HOOK, - EXEC_TYPE_DEFAULT, - EXEC_TYPE_TRY, - CALL_TYPE_CALL, - CALL_TYPE_BATCH, - CALL_TYPE_DELEGATE, - encodeMode, - encodeSingle, - encodeBatch, - encodeDelegate, -}; diff --git a/test/helpers/erc7739.js b/test/helpers/erc7739.js index 5a489de71e9..38fa3881188 100644 --- a/test/helpers/erc7739.js +++ b/test/helpers/erc7739.js @@ -1,8 +1,9 @@ -const { ethers } = require('hardhat'); -const { formatType } = require('./eip712'); +import { ethers } from 'ethers'; +import { formatType } from './eip712'; -const PersonalSign = formatType({ prefixed: 'bytes' }); -const TypedDataSign = contentsTypeName => +export const PersonalSign = formatType({ prefixed: 'bytes' }); + +export const TypedDataSign = contentsTypeName => formatType({ contents: contentsTypeName, name: 'string', @@ -12,7 +13,7 @@ const TypedDataSign = contentsTypeName => salt: 'bytes32', }); -class ERC7739Signer extends ethers.AbstractSigner { +export class ERC7739Signer extends ethers.AbstractSigner { #signer; #domain; @@ -67,7 +68,7 @@ class ERC7739Signer extends ethers.AbstractSigner { } } -class ERC4337Utils { +export class ERC4337Utils { static preparePersonalSign(message) { return { prefixed: ethers.concat([ @@ -109,10 +110,3 @@ class ERC4337Utils { }; } } - -module.exports = { - ERC7739Signer, - ERC4337Utils, - PersonalSign, - TypedDataSign, -}; diff --git a/test/helpers/governance.js b/test/helpers/governance.js index d53e6a2b934..be0986e61bc 100644 --- a/test/helpers/governance.js +++ b/test/helpers/governance.js @@ -1,14 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { ProposalState } = require('./enums'); -const { unique } = require('./iterate'); -const time = require('./time'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { ProposalState } from './enums'; +import { unique } from './iterate'; -const timelockSalt = (address, descriptionHash) => +export const timelockSalt = (address, descriptionHash) => ethers.toBeHex((ethers.toBigInt(address) << 96n) ^ ethers.toBigInt(descriptionHash), 32); -class GovernorHelper { - constructor(governor, mode = 'blocknumber') { +export class GovernorHelper { + constructor(connection, governor, mode = 'blocknumber') { + this.connection = connection; this.governor = governor; this.mode = mode; } @@ -70,18 +70,18 @@ class GovernorHelper { } /// Proposal lifecycle - delegate(delegation) { - return Promise.all([ - delegation.token.connect(delegation.to).delegate(delegation.to), - delegation.value === undefined || - delegation.token.connect(this.governor.runner).transfer(delegation.to, delegation.value), - delegation.tokenId === undefined || - delegation.token - .ownerOf(delegation.tokenId) - .then(owner => - delegation.token.connect(this.governor.runner).transferFrom(owner, delegation.to, delegation.tokenId), - ), - ]); + async delegate(delegation) { + await delegation.token.connect(delegation.to).delegate(delegation.to); + if (delegation.value !== undefined) { + await delegation.token.connect(this.governor.runner).transfer(delegation.to, delegation.value); + } + if (delegation.tokenId !== undefined) { + await delegation.token + .ownerOf(delegation.tokenId) + .then(owner => + delegation.token.connect(this.governor.runner).transferFrom(owner, delegation.to, delegation.tokenId), + ); + } } propose() { @@ -151,17 +151,17 @@ class GovernorHelper { /// Clock helpers async waitForSnapshot(offset = 0n) { const timepoint = await this.governor.proposalSnapshot(await this.id); - return time.increaseTo[this.mode](timepoint + offset); + return this.connection.helpers.time.increaseTo[this.mode](timepoint + offset); } async waitForDeadline(offset = 0n) { const timepoint = await this.governor.proposalDeadline(await this.id); - return time.increaseTo[this.mode](timepoint + offset); + return this.connection.helpers.time.increaseTo[this.mode](timepoint + offset); } async waitForEta(offset = 0n) { const timestamp = await this.governor.proposalEta(await this.id); - return time.increaseTo.timestamp(timestamp + offset); + return this.connection.helpers.time.increaseTo.timestamp(timestamp + offset); } /// Other helpers @@ -211,8 +211,3 @@ class GovernorHelper { return ethers.toBeHex(result, 32); } } - -module.exports = { - GovernorHelper, - timelockSalt, -}; diff --git a/test/helpers/iterate.js b/test/helpers/iterate.js index 8c8e9649dea..4fd09008030 100644 --- a/test/helpers/iterate.js +++ b/test/helpers/iterate.js @@ -1,41 +1,41 @@ -module.exports = { - // ================================================= Array helpers ================================================= +// ================================================= Array helpers ================================================= - // Cut an array into an array of sized-length arrays - // Example: chunk([1,2,3,4,5,6,7,8], 3) → [[1,2,3],[4,5,6],[7,8]] - chunk: (array, size = 1) => - Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size)), +// Cut an array into an array of sized-length arrays +// Example: chunk([1,2,3,4,5,6,7,8], 3) → [[1,2,3],[4,5,6],[7,8]] +export const chunk = (array, size = 1) => + Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size)); - // Cartesian cross product of an array of arrays - // Example: product([1,2],[a,b,c],[true]) → [[1,a,true],[1,b,true],[1,c,true],[2,a,true],[2,b,true],[2,c,true]] - product: (...arrays) => arrays.reduce((a, b) => a.flatMap(ai => b.map(bi => [...ai, bi])), [[]]), +// Cartesian cross product of an array of arrays +// Example: product([1,2],[a,b,c],[true]) → [[1,a,true],[1,b,true],[1,c,true],[2,a,true],[2,b,true],[2,c,true]] +export const product = (...arrays) => arrays.reduce((a, b) => a.flatMap(ai => b.map(bi => [...ai, bi])), [[]]); - // Range from start to end in increment - // Example: range(17,42,7) → [17,24,31,38] - range: (start, stop = undefined, step = 1) => { - if (stop == undefined) { - stop = start; - start = 0; - } - return start < stop ? Array.from({ length: (stop - start + step - 1) / step }, (_, i) => start + i * step) : []; - }, +// Range from start to end in increment +// Example: range(17,42,7) → [17,24,31,38] +export const range = (start, stop = undefined, step = 1) => { + if (stop == undefined) { + stop = start; + start = 0; + } + return start < stop ? Array.from({ length: (stop - start + step - 1) / step }, (_, i) => start + i * step) : []; +}; - // Unique elements, with an optional getter function - // Example: unique([1,1,2,3,4,8,1,3,8,13,42]) → [1,2,3,4,8,13,42] - unique: (array, op = x => x) => array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i), +// Unique elements, with an optional getter function +// Example: unique([1,1,2,3,4,8,1,3,8,13,42]) → [1,2,3,4,8,13,42] +export const unique = (array, op = x => x) => + array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i); - // Zip arrays together. If some arrays are smaller, undefined is used as a filler. - // Example: zip([1,2],[a,b,c],[true]) → [[1,a,true],[2,b,undefined],[undefined,c,undefined]] - zip: (...args) => Array.from({ length: Math.max(...args.map(arg => arg.length)) }, (_, i) => args.map(arg => arg[i])), +// Zip arrays together. If some arrays are smaller, undefined is used as a filler. +// Example: zip([1,2],[a,b,c],[true]) → [[1,a,true],[2,b,undefined],[undefined,c,undefined]] +export const zip = (...args) => + Array.from({ length: Math.max(...args.map(arg => arg.length)) }, (_, i) => args.map(arg => arg[i])); - // ================================================ Object helpers ================================================= +// ================================================ Object helpers ================================================= - // Create a new object by mapping the values through a function, keeping the keys. Second function can be used to pre-filter entries - // Example: mapValues({a:1,b:2,c:3}, x => x**2) → {a:1,b:4,c:9} - mapValues: (obj, fn, fn2 = () => true) => - Object.fromEntries( - Object.entries(obj) - .filter(fn2) - .map(([k, v]) => [k, fn(v)]), - ), -}; +// Create a new object by mapping the values through a function, keeping the keys. Second function can be used to pre-filter entries +// Example: mapValues({a:1,b:2,c:3}, x => x**2) → {a:1,b:4,c:9} +export const mapValues = (obj, fn, fn2 = () => true) => + Object.fromEntries( + Object.entries(obj) + .filter(fn2) + .map(([k, v]) => [k, fn(v)]), + ); diff --git a/test/helpers/math.js b/test/helpers/math.js index 133254aecc2..8b621b75d4f 100644 --- a/test/helpers/math.js +++ b/test/helpers/math.js @@ -1,10 +1,10 @@ // Array of number or bigint -const max = (...values) => values.slice(1).reduce((x, y) => (x > y ? x : y), values.at(0)); -const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), values.at(0)); -const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values.at(0)); +export const max = (...values) => values.slice(1).reduce((x, y) => (x > y ? x : y), values.at(0)); +export const min = (...values) => values.slice(1).reduce((x, y) => (x < y ? x : y), values.at(0)); +export const sum = (...values) => values.slice(1).reduce((x, y) => x + y, values.at(0)); // Computes modexp without BigInt overflow for large numbers -function modExp(b, e, m) { +export function modExp(b, e, m) { let result = 1n; // If e is a power of two, modexp can be calculated as: @@ -24,10 +24,3 @@ function modExp(b, e, m) { return result; } - -module.exports = { - min, - max, - sum, - modExp, -}; diff --git a/test/helpers/methods.js b/test/helpers/methods.js index a4918972019..5aebe8a495c 100644 --- a/test/helpers/methods.js +++ b/test/helpers/methods.js @@ -1,14 +1,9 @@ -const { ethers } = require('hardhat'); +import { ethers } from 'ethers'; -const selector = signature => ethers.FunctionFragment.from(signature).selector; +export const selector = signature => ethers.FunctionFragment.from(signature).selector; -const interfaceId = signatures => +export const interfaceId = signatures => ethers.toBeHex( signatures.reduce((acc, signature) => acc ^ ethers.toBigInt(selector(signature)), 0n), 4, ); - -module.exports = { - selector, - interfaceId, -}; diff --git a/test/helpers/precompiles.js b/test/helpers/precompiles.js index fb6b7132a70..9fbc3f708a5 100644 --- a/test/helpers/precompiles.js +++ b/test/helpers/precompiles.js @@ -1,12 +1,10 @@ -module.exports = { - ecRecover: '0x0000000000000000000000000000000000000001', - SHA2_256: '0x0000000000000000000000000000000000000002', - RIPEMD_160: '0x0000000000000000000000000000000000000003', - identity: '0x0000000000000000000000000000000000000004', - modexp: '0x0000000000000000000000000000000000000005', - ecAdd: '0x0000000000000000000000000000000000000006', - ecMul: '0x0000000000000000000000000000000000000007', - ecPairing: '0x0000000000000000000000000000000000000008', - blake2f: '0x0000000000000000000000000000000000000009', - pointEvaluation: '0x000000000000000000000000000000000000000a', -}; +export const ecRecover = '0x0000000000000000000000000000000000000001'; +export const SHA2_256 = '0x0000000000000000000000000000000000000002'; +export const RIPEMD_160 = '0x0000000000000000000000000000000000000003'; +export const identity = '0x0000000000000000000000000000000000000004'; +export const modexp = '0x0000000000000000000000000000000000000005'; +export const ecAdd = '0x0000000000000000000000000000000000000006'; +export const ecMul = '0x0000000000000000000000000000000000000007'; +export const ecPairing = '0x0000000000000000000000000000000000000008'; +export const blake2f = '0x0000000000000000000000000000000000000009'; +export const pointEvaluation = '0x000000000000000000000000000000000000000a'; diff --git a/test/helpers/random.js b/test/helpers/random.js index 15d00c15bbc..ef528accb9e 100644 --- a/test/helpers/random.js +++ b/test/helpers/random.js @@ -1,26 +1,12 @@ -const { ethers } = require('hardhat'); - -const generators = { - address: () => ethers.Wallet.createRandom().address, - bytes32: () => ethers.hexlify(ethers.randomBytes(32)), - bytes4: () => ethers.hexlify(ethers.randomBytes(4)), - uint256: () => ethers.toBigInt(ethers.randomBytes(32)), - int256: () => ethers.toBigInt(ethers.randomBytes(32)) + ethers.MinInt256, - bytes: (length = 32) => ethers.hexlify(ethers.randomBytes(length)), - string: () => ethers.uuidV4(ethers.randomBytes(32)), -}; - -generators.address.zero = ethers.ZeroAddress; -generators.bytes32.zero = ethers.ZeroHash; -generators.bytes4.zero = ethers.zeroPadBytes('0x', 4); -generators.uint256.zero = 0n; -generators.int256.zero = 0n; -generators.bytes.zero = '0x'; -generators.string.zero = ''; - -// alias hexBytes -> bytes -generators.hexBytes = generators.bytes; - -module.exports = { - generators, +import { ethers } from 'ethers'; + +export const generators = { + address: Object.assign(() => ethers.Wallet.createRandom().address, { zero: ethers.ZeroAddress }), + bytes32: Object.assign(() => ethers.hexlify(ethers.randomBytes(32)), { zero: ethers.ZeroHash }), + bytes4: Object.assign(() => ethers.hexlify(ethers.randomBytes(4)), { zero: ethers.zeroPadBytes('0x', 4) }), + uint256: Object.assign(() => ethers.toBigInt(ethers.randomBytes(32)), { zero: 0n }), + int256: Object.assign(() => ethers.toBigInt(ethers.randomBytes(32)) + ethers.MinInt256, { zero: 0n }), + bytes: Object.assign((length = 32) => ethers.hexlify(ethers.randomBytes(length)), { zero: '0x' }), + hexBytes: Object.assign((length = 32) => ethers.hexlify(ethers.randomBytes(length)), { zero: '0x' }), + string: Object.assign(() => ethers.uuidV4(ethers.randomBytes(32)), { zero: '' }), }; diff --git a/test/helpers/signers.js b/test/helpers/signers.js index b71417a3f09..4ebd7b5e7d9 100644 --- a/test/helpers/signers.js +++ b/test/helpers/signers.js @@ -1,9 +1,9 @@ -const { ethers } = require('ethers'); -const { secp256r1 } = require('@noble/curves/p256'); -const { generateKeyPairSync, privateEncrypt } = require('crypto'); +import { ethers } from 'ethers'; +import { secp256r1 } from '@noble/curves/p256'; +import { generateKeyPairSync, privateEncrypt } from 'crypto'; // Lightweight version of BaseWallet -class NonNativeSigner extends ethers.AbstractSigner { +export class NonNativeSigner extends ethers.AbstractSigner { #signingKey; constructor(privateKey, provider) { @@ -60,7 +60,7 @@ class NonNativeSigner extends ethers.AbstractSigner { } } -class P256SigningKey { +export class P256SigningKey { #privateKey; constructor(privateKey) { @@ -96,7 +96,7 @@ class P256SigningKey { } } -class RSASigningKey { +export class RSASigningKey { #privateKey; #publicKey; @@ -132,14 +132,14 @@ class RSASigningKey { } } -class RSASHA256SigningKey extends RSASigningKey { +export class RSASHA256SigningKey extends RSASigningKey { sign(digest /*: BytesLike*/) /*: ethers.Signature*/ { ethers.assertArgument(ethers.dataLength(digest) === 32, 'invalid digest length', 'digest', digest); return super.sign(ethers.sha256(ethers.getBytes(digest))); } } -class WebAuthnSigningKey extends P256SigningKey { +export class WebAuthnSigningKey extends P256SigningKey { sign(digest /*: BytesLike*/) /*: { serialized: string } */ { ethers.assertArgument(ethers.dataLength(digest) === 32, 'invalid digest length', 'digest', digest); @@ -175,7 +175,7 @@ class WebAuthnSigningKey extends P256SigningKey { } } -class MultiERC7913SigningKey { +export class MultiERC7913SigningKey { // this is a sorted array of objects that contain {signer, weight} #signers; @@ -211,12 +211,3 @@ class MultiERC7913SigningKey { }; } } - -module.exports = { - NonNativeSigner, - P256SigningKey, - RSASigningKey, - RSASHA256SigningKey, - WebAuthnSigningKey, - MultiERC7913SigningKey, -}; diff --git a/test/helpers/storage.js b/test/helpers/storage.js index 466cbb10c56..dc601f3067c 100644 --- a/test/helpers/storage.js +++ b/test/helpers/storage.js @@ -1,48 +1,40 @@ -const { ethers } = require('hardhat'); -const { setStorageAt } = require('@nomicfoundation/hardhat-network-helpers'); +import { artifacts } from 'hardhat'; +import { ethers } from 'ethers'; -const ImplementationLabel = 'eip1967.proxy.implementation'; -const AdminLabel = 'eip1967.proxy.admin'; -const BeaconLabel = 'eip1967.proxy.beacon'; +export const ImplementationLabel = 'eip1967.proxy.implementation'; +export const AdminLabel = 'eip1967.proxy.admin'; +export const BeaconLabel = 'eip1967.proxy.beacon'; -const erc1967Slot = label => ethers.toBeHex(ethers.toBigInt(ethers.id(label)) - 1n); -const erc7201Slot = label => ethers.toBeHex(ethers.toBigInt(ethers.keccak256(erc1967Slot(label))) & ~0xffn); -const erc7201format = contractName => `openzeppelin.storage.${contractName}`; +export const erc1967Slot = label => ethers.toBeHex(ethers.toBigInt(ethers.id(label)) - 1n); +export const erc7201Slot = label => ethers.toBeHex(ethers.toBigInt(ethers.keccak256(erc1967Slot(label))) & ~0xffn); +export const erc7201format = contractName => `openzeppelin.storage.${contractName}`; -const getSlot = (address, slot) => - ethers.provider.getStorage(address, ethers.isBytesLike(slot) ? slot : erc1967Slot(slot)); +export const getSlot = + ({ networkHelpers }) => + (address, slot) => + (ethers.isAddressable(address) ? address.getAddress() : Promise.resolve(address)).then(address => + networkHelpers.getStorageAt(address, ethers.isBytesLike(slot) ? slot : erc1967Slot(slot)), + ); -const setSlot = (address, slot, value) => - Promise.all([ - ethers.isAddressable(address) ? address.getAddress() : Promise.resolve(address), - ethers.isAddressable(value) ? value.getAddress() : Promise.resolve(value), - ]).then(([address, value]) => setStorageAt(address, ethers.isBytesLike(slot) ? slot : erc1967Slot(slot), value)); +export const getAddressInSlot = + ({ networkHelpers }) => + (address, slot) => + getSlot({ networkHelpers })(address, slot).then( + slotValue => ethers.AbiCoder.defaultAbiCoder().decode(['address'], slotValue)[0], + ); -const getAddressInSlot = (address, slot) => - getSlot(address, slot).then(slotValue => ethers.AbiCoder.defaultAbiCoder().decode(['address'], slotValue)[0]); +export const setSlot = + ({ networkHelpers }) => + (address, slot, value) => + Promise.all([ + ethers.isAddressable(address) ? address.getAddress() : Promise.resolve(address), + ethers.isAddressable(value) ? value.getAddress() : Promise.resolve(value), + ]).then(([address, value]) => + networkHelpers.setStorageAt(address, ethers.isBytesLike(slot) ? slot : erc1967Slot(slot), value), + ); -const upgradeableSlot = (contractName, offset) => { - try { - // Try to get the artifact paths, will throw if it doesn't exist - artifacts._getArtifactPathSync(`${contractName}Upgradeable`); - return offset + ethers.toBigInt(erc7201Slot(erc7201format(contractName))); - } catch { - return offset; - } -}; - -module.exports = { - ImplementationLabel, - AdminLabel, - BeaconLabel, - ImplementationSlot: erc1967Slot(ImplementationLabel), - AdminSlot: erc1967Slot(AdminLabel), - BeaconSlot: erc1967Slot(BeaconLabel), - erc1967Slot, - erc7201Slot, - erc7201format, - setSlot, - getSlot, - getAddressInSlot, - upgradeableSlot, -}; +export const upgradeableSlot = (contractName, offset) => + artifacts.readArtifact(`${contractName}Upgradeable`).then( + () => offset + ethers.toBigInt(erc7201Slot(erc7201format(contractName))), + () => offset, + ); diff --git a/test/helpers/strings.js b/test/helpers/strings.js index 4f34099d7df..fcda5e85d87 100644 --- a/test/helpers/strings.js +++ b/test/helpers/strings.js @@ -1,5 +1,3 @@ -module.exports = { - // Capitalize the first char of a string - // Example: capitalize('uint256') → 'Uint256' - capitalize: str => str.charAt(0).toUpperCase() + str.slice(1), -}; +// Capitalize the first char of a string +// Example: capitalize('uint256') → 'Uint256' +export const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1); diff --git a/test/helpers/time.js b/test/helpers/time.js index 574170c137c..ddaf37e70b9 100644 --- a/test/helpers/time.js +++ b/test/helpers/time.js @@ -1,33 +1,39 @@ -const { ethers } = require('hardhat'); -const { time, mine, mineUpTo } = require('@nomicfoundation/hardhat-network-helpers'); -const { mapValues } = require('./iterate'); +import { ethers } from 'ethers'; -const clock = { - blocknumber: () => time.latestBlock().then(ethers.toBigInt), - timestamp: () => time.latest().then(ethers.toBigInt), -}; -const clockFromReceipt = { +export const clock = ({ networkHelpers }) => ({ + blocknumber: () => networkHelpers.time.latestBlock().then(ethers.toBigInt), + timestamp: () => networkHelpers.time.latest().then(ethers.toBigInt), +}); + +export const clockFromReceipt = ({ ethers }) => ({ blocknumber: receipt => Promise.resolve(receipt).then(({ blockNumber }) => ethers.toBigInt(blockNumber)), timestamp: receipt => Promise.resolve(receipt) .then(({ blockNumber }) => ethers.provider.getBlock(blockNumber)) .then(({ timestamp }) => ethers.toBigInt(timestamp)), -}; -const increaseBy = { - blockNumber: mine, +}); + +export const increaseBy = ({ networkHelpers }) => ({ + blocknumber: networkHelpers.mine, timestamp: (delay, mine = true) => - time.latest().then(clock => increaseTo.timestamp(clock + ethers.toNumber(delay), mine)), -}; -const increaseTo = { - blocknumber: mineUpTo, - timestamp: (to, mine = true) => (mine ? time.increaseTo(to) : time.setNextBlockTimestamp(to)), -}; -const duration = mapValues(time.duration, fn => n => ethers.toBigInt(fn(ethers.toNumber(n)))); + networkHelpers.time + .latest() + .then(clock => clock + ethers.toNumber(delay)) + .then(to => (mine ? networkHelpers.time.increaseTo(to) : networkHelpers.time.setNextBlockTimestamp(to))), +}); + +export const increaseTo = ({ networkHelpers }) => ({ + blocknumber: networkHelpers.mineUpTo, + timestamp: (to, mine = true) => + mine ? networkHelpers.time.increaseTo(to) : networkHelpers.time.setNextBlockTimestamp(to), +}); -module.exports = { - clock, - clockFromReceipt, - increaseBy, - increaseTo, - duration, -}; +export const duration = ({ networkHelpers }) => ({ + years: n => ethers.toBigInt(networkHelpers.time.duration.years(ethers.toNumber(n))), + weeks: n => ethers.toBigInt(networkHelpers.time.duration.weeks(ethers.toNumber(n))), + days: n => ethers.toBigInt(networkHelpers.time.duration.days(ethers.toNumber(n))), + hours: n => ethers.toBigInt(networkHelpers.time.duration.hours(ethers.toNumber(n))), + minutes: n => ethers.toBigInt(networkHelpers.time.duration.minutes(ethers.toNumber(n))), + seconds: n => ethers.toBigInt(networkHelpers.time.duration.seconds(ethers.toNumber(n))), + millis: n => ethers.toBigInt(networkHelpers.time.duration.millis(ethers.toNumber(n))), +}); diff --git a/test/helpers/trie.js b/test/helpers/trie.js index a6d28e4265b..564caa680e8 100644 --- a/test/helpers/trie.js +++ b/test/helpers/trie.js @@ -1,7 +1,7 @@ -const { ethers } = require('ethers'); -const { MerklePatriciaTrie, createMerkleProof } = require('@ethereumjs/mpt'); +import { ethers } from 'ethers'; +import { MerklePatriciaTrie, createMerkleProof } from '@ethereumjs/mpt'; -class BlockTries { +export class BlockTries { constructor(block) { this.block = block; @@ -75,5 +75,3 @@ class BlockTries { return ethers.getBytes(BlockTries.indexToKey(index)); } } - -module.exports = { BlockTries }; diff --git a/test/helpers/txpool.js b/test/helpers/txpool.js index ad84f45dd77..9d19c317635 100644 --- a/test/helpers/txpool.js +++ b/test/helpers/txpool.js @@ -1,9 +1,7 @@ -const { network } = require('hardhat'); -const { expect } = require('chai'); +import { expect } from 'chai'; +import { unique } from './iterate'; -const { unique } = require('./iterate'); - -async function batchInBlock(txs, provider = network.provider) { +export async function batchInBlock(txs, provider) { try { // disable auto-mining await provider.send('evm_setAutomine', [false]); @@ -22,7 +20,3 @@ async function batchInBlock(txs, provider = network.provider) { await provider.send('evm_setAutomine', [true]); } } - -module.exports = { - batchInBlock, -}; diff --git a/test/metatx/ERC2771Context.test.js b/test/metatx/ERC2771Context.test.js index 377e1d701f2..69332faf106 100644 --- a/test/metatx/ERC2771Context.test.js +++ b/test/metatx/ERC2771Context.test.js @@ -1,12 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { impersonate } = require('../helpers/account'); -const { getDomain, ForwardRequest } = require('../helpers/eip712'); -const { MAX_UINT48 } = require('../helpers/constants'); - -const { shouldBehaveLikeRegularContext } = require('../utils/Context.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT48 } from '../helpers/constants'; +import { ForwardRequest, getDomain } from '../helpers/eip712'; +import { shouldBehaveLikeRegularContext } from '../utils/Context.behavior'; + +const { + ethers, + helpers: { impersonate }, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [sender, other] = await ethers.getSigners(); @@ -14,6 +16,7 @@ async function fixture() { const forwarder = await ethers.deployContract('ERC2771Forwarder', ['ERC2771Forwarder']); const forwarderAsSigner = await impersonate(forwarder.target); const context = await ethers.deployContract('ERC2771ContextMock', [forwarder]); + const contextHelper = await ethers.deployContract('ContextMockCaller', []); const domain = await getDomain(forwarder); const prepareAndSignRequest = async (signer, request) => { @@ -28,7 +31,7 @@ async function fixture() { return request; }; - return { sender, other, forwarder, forwarderAsSigner, context, prepareAndSignRequest }; + return { sender, other, forwarder, forwarderAsSigner, context, contextHelper, prepareAndSignRequest }; } describe('ERC2771Context', function () { diff --git a/test/metatx/ERC2771Forwarder.test.js b/test/metatx/ERC2771Forwarder.test.js index 1a8bf2cd2c8..84e451694c0 100644 --- a/test/metatx/ERC2771Forwarder.test.js +++ b/test/metatx/ERC2771Forwarder.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ForwardRequest, getDomain } from '../helpers/eip712'; +import { sum } from '../helpers/math'; -const { getDomain, ForwardRequest } = require('../helpers/eip712'); -const { sum } = require('../helpers/math'); -const time = require('../helpers/time'); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [sender, refundReceiver, another, ...accounts] = await ethers.getSigners(); @@ -149,7 +152,7 @@ describe('ERC2771Forwarder', function () { }); const gasLimit = 100_000n; - await expect(this.forwarder.execute(request, { gasLimit })).to.be.revertedWithoutReason(); + await expect(this.forwarder.execute(request, { gasLimit })).to.be.revertedWithoutReason(ethers); const { gasUsed } = await ethers.provider .getBlock('latest') @@ -178,7 +181,7 @@ describe('ERC2771Forwarder', function () { // The subcall out of gas should be caught by the contract and then bubbled up consuming // the available gas with an `invalid` opcode. - await expect(this.forwarder.execute(request, { gasLimit })).to.be.revertedWithoutReason(); + await expect(this.forwarder.execute(request, { gasLimit })).to.be.revertedWithoutReason(ethers); const { gasUsed } = await ethers.provider .getBlock('latest') @@ -339,7 +342,7 @@ describe('ERC2771Forwarder', function () { gasLimit, value: requestsValue(this.requests), }), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); const { gasUsed } = await ethers.provider .getBlock('latest') @@ -369,7 +372,7 @@ describe('ERC2771Forwarder', function () { gasLimit, value: requestsValue(this.requests), }), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); const { gasUsed } = await ethers.provider .getBlock('latest') diff --git a/test/proxy/Clones.behaviour.js b/test/proxy/Clones.behaviour.js index dcc62066533..704e30f37a7 100644 --- a/test/proxy/Clones.behaviour.js +++ b/test/proxy/Clones.behaviour.js @@ -1,15 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { expect } from 'chai'; -module.exports = function shouldBehaveLikeClone() { +export function shouldBehaveLikeClone() { const assertProxyInitialization = function ({ value, balance }) { it('initializes the proxy', async function () { - const dummy = await ethers.getContractAt('DummyImplementation', this.proxy); + const dummy = await this.ethers.getContractAt('DummyImplementation', this.proxy); expect(await dummy.value()).to.equal(value); }); it('has expected balance', async function () { - expect(await ethers.provider.getBalance(this.proxy)).to.equal(balance); + expect(await this.ethers.provider.getBalance(this.proxy)).to.equal(balance); }); }; @@ -20,9 +19,13 @@ module.exports = function shouldBehaveLikeClone() { await this.deployer.sendTransaction({ to: this.factory, value }); const instance = await this.createClone({ deployValue: value }); - await expect(instance.deploymentTransaction()).to.changeEtherBalances([this.factory, instance], [-value, value]); + await expect(instance.deploymentTransaction()).to.changeEtherBalances( + this.ethers, + [this.factory, instance], + [-value, value], + ); - expect(await ethers.provider.getBalance(instance)).to.equal(value); + expect(await this.ethers.provider.getBalance(instance)).to.equal(value); }); it('factory does not have enough balance', async function () { @@ -47,7 +50,7 @@ module.exports = function shouldBehaveLikeClone() { assertProxyInitialization({ value: expectedInitializedValue, - balance: 0, + balance: 0n, }); }); @@ -55,7 +58,7 @@ module.exports = function shouldBehaveLikeClone() { const value = 10n ** 6n; it('reverts', async function () { - await expect(this.createClone({ initData: this.initializeData, initValue: value })).to.be.reverted; + await expect(this.createClone({ initData: this.initializeData, initValue: value })).to.be.revert(this.ethers); }); }); }); @@ -74,7 +77,7 @@ module.exports = function shouldBehaveLikeClone() { assertProxyInitialization({ value: expectedInitializedValue, - balance: 0, + balance: 0n, }); }); @@ -110,7 +113,7 @@ module.exports = function shouldBehaveLikeClone() { assertProxyInitialization({ value: expectedInitializedValue, - balance: 0, + balance: 0n, }); }); @@ -118,7 +121,7 @@ module.exports = function shouldBehaveLikeClone() { const value = 10n ** 6n; it('reverts', async function () { - await expect(this.createClone({ initData: this.initializeData, initValue: value })).to.be.reverted; + await expect(this.createClone({ initData: this.initializeData, initValue: value })).to.be.revert(this.ethers); }); }); }); @@ -139,7 +142,7 @@ module.exports = function shouldBehaveLikeClone() { assertProxyInitialization({ value: expectedInitializedValue, - balance: 0, + balance: 0n, }); }); @@ -157,4 +160,4 @@ module.exports = function shouldBehaveLikeClone() { }); }); }); -}; +} diff --git a/test/proxy/Clones.test.js b/test/proxy/Clones.test.js index 93bcfba19b9..5b2ec65b7fe 100644 --- a/test/proxy/Clones.test.js +++ b/test/proxy/Clones.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { generators } from '../helpers/random'; +import { shouldBehaveLikeClone } from './Clones.behaviour'; -const { generators } = require('../helpers/random'); - -const shouldBehaveLikeClone = require('./Clones.behaviour'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const cloneInitCode = (instance, args = undefined) => args @@ -81,7 +84,7 @@ async function fixture() { describe('Clones', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); for (const args of [undefined, '0x', '0x11223344']) { @@ -120,7 +123,7 @@ describe('Clones', function () { : this.factory.$cloneDeterministic(this.implementation, salt); // deploy once - await expect(deployClone()).to.not.be.reverted; + await expect(deployClone()).to.not.be.revert(ethers); // deploy twice await expect(deployClone()).to.be.revertedWithCustomError(this.factory, 'FailedDeployment'); diff --git a/test/proxy/ERC1967/ERC1967Proxy.test.js b/test/proxy/ERC1967/ERC1967Proxy.test.js index 319830e1f10..b62e53db87f 100644 --- a/test/proxy/ERC1967/ERC1967Proxy.test.js +++ b/test/proxy/ERC1967/ERC1967Proxy.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); +import { network } from 'hardhat'; +import { shouldBehaveLikeProxy } from '../Proxy.behaviour'; -const shouldBehaveLikeProxy = require('../Proxy.behaviour'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const fixture = async () => { const [nonContractAddress] = await ethers.getSigners(); @@ -13,7 +17,7 @@ const fixture = async () => { describe('ERC1967Proxy', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); describe('(default) allowUninitialized is false', function () { diff --git a/test/proxy/ERC1967/ERC1967Utils.test.js b/test/proxy/ERC1967/ERC1967Utils.test.js index 089032498e4..9a1f6045b94 100644 --- a/test/proxy/ERC1967/ERC1967Utils.test.js +++ b/test/proxy/ERC1967/ERC1967Utils.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ImplementationLabel, AdminLabel, BeaconLabel } from '../../helpers/storage'; -const { getAddressInSlot, setSlot, ImplementationSlot, AdminSlot, BeaconSlot } = require('../../helpers/storage'); +const { + ethers, + helpers: { storage }, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [, admin, anotherAccount] = await ethers.getSigners(); @@ -21,13 +25,13 @@ describe('ERC1967Utils', function () { describe('IMPLEMENTATION_SLOT', function () { beforeEach('set v1 implementation', async function () { - await setSlot(this.utils, ImplementationSlot, this.v1); + await storage.setSlot(this.utils, ImplementationLabel, this.v1); }); describe('getImplementation', function () { it('returns current implementation and matches implementation slot value', async function () { expect(await this.utils.$getImplementation()).to.equal(this.v1); - expect(await getAddressInSlot(this.utils, ImplementationSlot)).to.equal(this.v1); + expect(await storage.getAddressInSlot(this.utils, ImplementationLabel)).to.equal(this.v1); }); }); @@ -36,7 +40,7 @@ describe('ERC1967Utils', function () { const newImplementation = this.v2; const tx = await this.utils.$upgradeToAndCall(newImplementation, '0x'); - expect(await getAddressInSlot(this.utils, ImplementationSlot)).to.equal(newImplementation); + expect(await storage.getAddressInSlot(this.utils, ImplementationLabel)).to.equal(newImplementation); await expect(tx).to.emit(this.utils, 'Upgraded').withArgs(newImplementation); }); @@ -67,13 +71,13 @@ describe('ERC1967Utils', function () { describe('ADMIN_SLOT', function () { beforeEach('set admin', async function () { - await setSlot(this.utils, AdminSlot, this.admin); + await storage.setSlot(this.utils, AdminLabel, this.admin); }); describe('getAdmin', function () { it('returns current admin and matches admin slot value', async function () { expect(await this.utils.$getAdmin()).to.equal(this.admin); - expect(await getAddressInSlot(this.utils, AdminSlot)).to.equal(this.admin); + expect(await storage.getAddressInSlot(this.utils, AdminLabel)).to.equal(this.admin); }); }); @@ -82,7 +86,7 @@ describe('ERC1967Utils', function () { const newAdmin = this.anotherAccount; const tx = await this.utils.$changeAdmin(newAdmin); - expect(await getAddressInSlot(this.utils, AdminSlot)).to.equal(newAdmin); + expect(await storage.getAddressInSlot(this.utils, AdminLabel)).to.equal(newAdmin); await expect(tx).to.emit(this.utils, 'AdminChanged').withArgs(this.admin, newAdmin); }); @@ -97,13 +101,13 @@ describe('ERC1967Utils', function () { describe('BEACON_SLOT', function () { beforeEach('set beacon', async function () { this.beacon = await ethers.deployContract('UpgradeableBeaconMock', [this.v1]); - await setSlot(this.utils, BeaconSlot, this.beacon); + await storage.setSlot(this.utils, BeaconLabel, this.beacon); }); describe('getBeacon', function () { it('returns current beacon and matches beacon slot value', async function () { expect(await this.utils.$getBeacon()).to.equal(this.beacon); - expect(await getAddressInSlot(this.utils, BeaconSlot)).to.equal(this.beacon); + expect(await storage.getAddressInSlot(this.utils, BeaconLabel)).to.equal(this.beacon); }); }); @@ -112,7 +116,7 @@ describe('ERC1967Utils', function () { const newBeacon = await ethers.deployContract('UpgradeableBeaconMock', [this.v2]); const tx = await this.utils.$upgradeBeaconToAndCall(newBeacon, '0x'); - expect(await getAddressInSlot(this.utils, BeaconSlot)).to.equal(newBeacon); + expect(await storage.getAddressInSlot(this.utils, BeaconLabel)).to.equal(newBeacon); await expect(tx).to.emit(this.utils, 'BeaconUpgraded').withArgs(newBeacon); }); diff --git a/test/proxy/Proxy.behaviour.js b/test/proxy/Proxy.behaviour.js index 53fecffa12a..974b1c66ed2 100644 --- a/test/proxy/Proxy.behaviour.js +++ b/test/proxy/Proxy.behaviour.js @@ -1,12 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { expect } from 'chai'; +import { ImplementationLabel } from '../helpers/storage'; -const { getAddressInSlot, ImplementationSlot } = require('../helpers/storage'); - -module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { +export function shouldBehaveLikeProxy(allowUninitialized = false) { it('cannot be initialized with a non-contract address', async function () { const initializeData = '0x00'; // non empty data to avoid uninitialized error - const contractFactory = await ethers.getContractFactory('ERC1967Proxy'); + const contractFactory = await this.ethers.getContractFactory('ERC1967Proxy'); await expect(this.createProxy(this.nonContractAddress, initializeData)) .to.be.revertedWithCustomError(contractFactory, 'ERC1967InvalidImplementation') @@ -15,7 +13,9 @@ module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { const assertProxyInitialization = function ({ value, balance }) { it('sets the implementation address', async function () { - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.implementation); + expect(await this.helpers.storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal( + this.implementation, + ); }); it('initializes the proxy', async function () { @@ -24,7 +24,7 @@ module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { }); it('has expected balance', async function () { - expect(await ethers.provider.getBalance(this.proxy)).to.equal(balance); + expect(await this.proxy.runner.provider.getBalance(this.proxy)).to.equal(balance); }); }; @@ -44,13 +44,13 @@ module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { const value = 10n ** 5n; it('reverts', async function () { - await expect(this.createProxy(this.implementation, initializeData, { value })).to.be.reverted; + await expect(this.createProxy(this.implementation, initializeData, { value })).to.be.revert(this.ethers); }); }); } else { it('reverts without initialization', async function () { const initializeData = '0x'; // empty data causes uninitialized error - const contractFactory = await ethers.getContractFactory('ERC1967Proxy'); + const contractFactory = await this.ethers.getContractFactory('ERC1967Proxy'); await expect(this.createProxy(this.implementation, initializeData)).to.be.revertedWithCustomError( contractFactory, @@ -83,7 +83,7 @@ module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { const value = 10n ** 5n; it('reverts', async function () { - await expect(this.createProxy(this.implementation, this.initializeData, { value })).to.be.reverted; + await expect(this.createProxy(this.implementation, this.initializeData, { value })).to.be.revert(this.ethers); }); }); }); @@ -138,7 +138,7 @@ module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { assertProxyInitialization({ value: expectedInitializedValue, - balance: 0, + balance: 0n, }); }); @@ -146,7 +146,7 @@ module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { const value = 10e5; it('reverts', async function () { - await expect(this.createProxy(this.implementation, this.initializeData, { value })).to.be.reverted; + await expect(this.createProxy(this.implementation, this.initializeData, { value })).to.be.revert(this.ethers); }); }); }); @@ -191,8 +191,8 @@ module.exports = function shouldBehaveLikeProxy(allowUninitialized = false) { }); it('reverts', async function () { - await expect(this.createProxy(this.implementation, this.initializeData)).to.be.reverted; + await expect(this.createProxy(this.implementation, this.initializeData)).to.be.revert(this.ethers); }); }); }); -}; +} diff --git a/test/proxy/beacon/BeaconProxy.test.js b/test/proxy/beacon/BeaconProxy.test.js index d64023bbce8..e2451e1f249 100644 --- a/test/proxy/beacon/BeaconProxy.test.js +++ b/test/proxy/beacon/BeaconProxy.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { BeaconLabel } from '../../helpers/storage'; -const { getAddressInSlot, BeaconSlot } = require('../../helpers/storage'); +const { + ethers, + helpers: { storage }, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [admin, other] = await ethers.getSigners(); @@ -36,7 +40,7 @@ describe('BeaconProxy', function () { // BadBeaconNoImpl does not provide `implementation()` and has no fallback. // This causes ERC1967Utils._setBeacon to revert. - await expect(this.newBeaconProxy(badBeacon, '0x')).to.be.revertedWithoutReason(); + await expect(this.newBeaconProxy(badBeacon, '0x')).to.be.revertedWithoutReason(ethers); }); it('non-contract implementation', async function () { @@ -50,7 +54,7 @@ describe('BeaconProxy', function () { describe('initialization', function () { async function assertInitialized({ value, balance }) { - const beaconAddress = await getAddressInSlot(this.proxy, BeaconSlot); + const beaconAddress = await storage.getAddressInSlot(this.proxy, BeaconLabel); expect(beaconAddress).to.equal(this.beacon); const dummy = this.v1.attach(this.proxy); diff --git a/test/proxy/beacon/UpgradeableBeacon.test.js b/test/proxy/beacon/UpgradeableBeacon.test.js index 2da7d0a2730..5cbcbc7c428 100644 --- a/test/proxy/beacon/UpgradeableBeacon.test.js +++ b/test/proxy/beacon/UpgradeableBeacon.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [admin, other] = await ethers.getSigners(); diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 5f7aaec915f..2467c97bd7a 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ImplementationLabel } from '../../helpers/storage'; -const { getAddressInSlot, ImplementationSlot } = require('../../helpers/storage'); +const { + ethers, + helpers: { storage }, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [admin, other] = await ethers.getSigners(); @@ -47,7 +51,7 @@ describe('ProxyAdmin', function () { describe('with authorized account', function () { it('upgrades implementation', async function () { await this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, '0x'); - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.v2); + expect(await storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal(this.v2); }); }); }); @@ -66,7 +70,9 @@ describe('ProxyAdmin', function () { describe('with invalid callData', function () { it('fails to upgrade', async function () { const data = '0x12345678'; - await expect(this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, data)).to.be.reverted; + await expect(this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, data)).to.be.revert( + ethers, + ); }); }); @@ -74,7 +80,7 @@ describe('ProxyAdmin', function () { it('upgrades implementation', async function () { const data = this.v2.interface.encodeFunctionData('initializeNonPayableWithValue', [1337n]); await this.proxyAdmin.connect(this.admin).upgradeAndCall(this.proxy, this.v2, data); - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.v2); + expect(await storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal(this.v2); }); }); }); diff --git a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js index 2ad527d1bc6..cfc0789f316 100644 --- a/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js +++ b/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js @@ -1,25 +1,26 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); - -const { impersonate } = require('../../helpers/account'); -const { getAddressInSlot, ImplementationSlot, AdminSlot } = require('../../helpers/storage'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { ImplementationLabel, AdminLabel } from '../../helpers/storage'; // createProxy, initialOwner, accounts -module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { +export function shouldBehaveLikeTransparentUpgradeableProxy() { before(async function () { - const implementationV0 = await ethers.deployContract('DummyImplementation'); - const implementationV1 = await ethers.deployContract('DummyImplementation'); + const implementationV0 = await this.ethers.deployContract('DummyImplementation'); + const implementationV1 = await this.ethers.deployContract('DummyImplementation'); const createProxyWithImpersonatedProxyAdmin = async (logic, initData, opts = undefined) => { const [proxy, tx] = await this.createProxy(logic, initData, opts).then(instance => - Promise.all([ethers.getContractAt('ITransparentUpgradeableProxy', instance), instance.deploymentTransaction()]), + Promise.all([ + this.ethers.getContractAt('ITransparentUpgradeableProxy', instance), + instance.deploymentTransaction(), + ]), ); - const proxyAdmin = await ethers.getContractAt( + const proxyAdmin = await this.ethers.getContractAt( 'ProxyAdmin', ethers.getCreateAddress({ from: proxy.target, nonce: 1n }), ); - const proxyAdminAsSigner = await proxyAdmin.getAddress().then(impersonate); + const proxyAdminAsSigner = await proxyAdmin.getAddress().then(this.helpers.impersonate); return { instance: logic.attach(proxy.target), // attaching proxy directly works well for everything except for event resolution @@ -49,7 +50,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { describe('implementation', function () { it('returns the current implementation address', async function () { - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.implementationV0); + expect(await this.helpers.storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal( + this.implementationV0, + ); }); it('delegates to the implementation', async function () { @@ -63,7 +66,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('sets the proxy admin in storage with the correct initial owner', async function () { - expect(await getAddressInSlot(this.proxy, AdminSlot)).to.equal(this.proxyAdmin); + expect(await this.helpers.storage.getAddressInSlot(this.proxy, AdminLabel)).to.equal(this.proxyAdmin); expect(await this.proxyAdmin.owner()).to.equal(this.owner); }); @@ -71,7 +74,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { it('can overwrite the admin by the implementation', async function () { await this.instance.unsafeOverrideAdmin(this.other); - const ERC1967AdminSlotValue = await getAddressInSlot(this.proxy, AdminSlot); + const ERC1967AdminSlotValue = await this.helpers.storage.getAddressInSlot(this.proxy, AdminLabel); expect(ERC1967AdminSlotValue).to.equal(this.other); expect(ERC1967AdminSlotValue).to.not.equal(this.proxyAdmin); @@ -85,7 +88,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { describe('upgradeToAndCall', function () { describe('without migrations', function () { beforeEach(async function () { - this.behavior = await ethers.deployContract('InitializableMock'); + this.behavior = await this.ethers.deployContract('InitializableMock'); }); describe('when the call does not fail', function () { @@ -105,7 +108,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('upgrades to the requested implementation', async function () { - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behavior); + expect(await this.helpers.storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal( + this.behavior, + ); }); it('emits an event', async function () { @@ -117,21 +122,22 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('sends given value to the proxy', async function () { - expect(await ethers.provider.getBalance(this.proxy)).to.equal(value); + expect(await this.proxy.runner.provider.getBalance(this.proxy)).to.equal(value); }); it('uses the storage of the proxy', async function () { // storage layout should look as follows: // - 0: Initializable storage ++ initializerRan ++ onlyInitializingRan // - 1: x - expect(await ethers.provider.getStorage(this.proxy, 1n)).to.equal(42n); + expect(await this.proxy.runner.provider.getStorage(this.proxy, 1n)).to.equal(42n); }); }); describe('when the sender is not the admin', function () { it('reverts', async function () { - await expect(this.proxy.connect(this.other).upgradeToAndCall(this.behavior, this.initializeData)).to.be - .reverted; + await expect( + this.proxy.connect(this.other).upgradeToAndCall(this.behavior, this.initializeData), + ).to.be.revert(ethers); }); }); }); @@ -142,8 +148,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('reverts', async function () { - await expect(this.proxy.connect(this.proxyAdminAsSigner).upgradeToAndCall(this.behavior, this.initializeData)) - .to.be.reverted; + await expect( + this.proxy.connect(this.proxyAdminAsSigner).upgradeToAndCall(this.behavior, this.initializeData), + ).to.be.revert(ethers); }); }); }); @@ -154,10 +161,10 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { describe('when upgrading to V1', function () { beforeEach(async function () { - this.behaviorV1 = await ethers.deployContract('MigratableMockV1'); + this.behaviorV1 = await this.ethers.deployContract('MigratableMockV1'); const v1MigrationData = this.behaviorV1.interface.encodeFunctionData('initialize', [42n]); - this.balancePreviousV1 = await ethers.provider.getBalance(this.proxy); + this.balancePreviousV1 = await this.proxy.runner.provider.getBalance(this.proxy); this.tx = await this.proxy .connect(this.proxyAdminAsSigner) .upgradeToAndCall(this.behaviorV1, v1MigrationData, { @@ -166,22 +173,24 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('upgrades to the requested version and emits an event', async function () { - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV1); + expect(await this.helpers.storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal( + this.behaviorV1, + ); await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV1); }); it("calls the 'initialize' function and sends given value to the proxy", async function () { expect(await this.behaviorV1.attach(this.proxy).x()).to.equal(42n); - expect(await ethers.provider.getBalance(this.proxy)).to.equal(this.balancePreviousV1 + value); + expect(await this.proxy.runner.provider.getBalance(this.proxy)).to.equal(this.balancePreviousV1 + value); }); describe('when upgrading to V2', function () { beforeEach(async function () { - this.behaviorV2 = await ethers.deployContract('MigratableMockV2'); + this.behaviorV2 = await this.ethers.deployContract('MigratableMockV2'); const v2MigrationData = this.behaviorV2.interface.encodeFunctionData('migrate', [10n, 42n]); - this.balancePreviousV2 = await ethers.provider.getBalance(this.proxy); + this.balancePreviousV2 = await this.proxy.runner.provider.getBalance(this.proxy); this.tx = await this.proxy .connect(this.proxyAdminAsSigner) .upgradeToAndCall(this.behaviorV2, v2MigrationData, { @@ -190,7 +199,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('upgrades to the requested version and emits an event', async function () { - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV2); + expect(await this.helpers.storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal( + this.behaviorV2, + ); await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV2); }); @@ -198,15 +209,15 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { it("calls the 'migrate' function and sends given value to the proxy", async function () { expect(await this.behaviorV2.attach(this.proxy).x()).to.equal(10n); expect(await this.behaviorV2.attach(this.proxy).y()).to.equal(42n); - expect(await ethers.provider.getBalance(this.proxy)).to.equal(this.balancePreviousV2 + value); + expect(await this.proxy.runner.provider.getBalance(this.proxy)).to.equal(this.balancePreviousV2 + value); }); describe('when upgrading to V3', function () { beforeEach(async function () { - this.behaviorV3 = await ethers.deployContract('MigratableMockV3'); + this.behaviorV3 = await this.ethers.deployContract('MigratableMockV3'); const v3MigrationData = this.behaviorV3.interface.encodeFunctionData('migrate()'); - this.balancePreviousV3 = await ethers.provider.getBalance(this.proxy); + this.balancePreviousV3 = await this.proxy.runner.provider.getBalance(this.proxy); this.tx = await this.proxy .connect(this.proxyAdminAsSigner) .upgradeToAndCall(this.behaviorV3, v3MigrationData, { @@ -215,7 +226,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('upgrades to the requested version and emits an event', async function () { - expect(await getAddressInSlot(this.proxy, ImplementationSlot)).to.equal(this.behaviorV3); + expect(await this.helpers.storage.getAddressInSlot(this.proxy, ImplementationLabel)).to.equal( + this.behaviorV3, + ); await expect(this.tx).to.emit(this.proxy, 'Upgraded').withArgs(this.behaviorV3); }); @@ -223,7 +236,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { it("calls the 'migrate' function and sends given value to the proxy", async function () { expect(await this.behaviorV3.attach(this.proxy).x()).to.equal(42n); expect(await this.behaviorV3.attach(this.proxy).y()).to.equal(10n); - expect(await ethers.provider.getBalance(this.proxy)).to.equal(this.balancePreviousV3 + value); + expect(await this.proxy.runner.provider.getBalance(this.proxy)).to.equal( + this.balancePreviousV3 + value, + ); }); }); }); @@ -232,9 +247,11 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { describe('when the sender is not the admin', function () { it('reverts', async function () { - const behaviorV1 = await ethers.deployContract('MigratableMockV1'); + const behaviorV1 = await this.ethers.deployContract('MigratableMockV1'); const v1MigrationData = behaviorV1.interface.encodeFunctionData('initialize', [42n]); - await expect(this.proxy.connect(this.other).upgradeToAndCall(behaviorV1, v1MigrationData)).to.be.reverted; + await expect(this.proxy.connect(this.other).upgradeToAndCall(behaviorV1, v1MigrationData)).to.be.revert( + ethers, + ); }); }); }); @@ -242,8 +259,8 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { describe('transparent proxy', function () { beforeEach('creating proxy', async function () { - this.clashingImplV0 = await ethers.deployContract('ClashingImplementation'); - this.clashingImplV1 = await ethers.deployContract('ClashingImplementation'); + this.clashingImplV0 = await this.ethers.deployContract('ClashingImplementation'); + this.clashingImplV1 = await this.ethers.deployContract('ClashingImplementation'); Object.assign( this, @@ -255,7 +272,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('proxy admin cannot call delegated functions', async function () { - const factory = await ethers.getContractFactory('TransparentUpgradeableProxy'); + const factory = await this.ethers.getContractFactory('TransparentUpgradeableProxy'); await expect(this.instance.connect(this.proxyAdminAsSigner).delegatedFunction()).to.be.revertedWithCustomError( factory, @@ -280,8 +297,8 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { describe('regression', function () { it('should add new function', async function () { - const impl1 = await ethers.deployContract('Implementation1'); - const impl2 = await ethers.deployContract('Implementation2'); + const impl1 = await this.ethers.deployContract('Implementation1'); + const impl2 = await this.ethers.deployContract('Implementation2'); const { instance, proxy, proxyAdminAsSigner } = await this.createProxyWithImpersonatedProxyAdmin( impl1, impl1.interface.encodeFunctionData('initialize'), @@ -290,7 +307,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { await instance.setValue(42n); // `getValue` is not available in impl1 - await expect(impl2.attach(instance).getValue()).to.be.reverted; + await expect(impl2.attach(instance).getValue()).to.be.revert(ethers); // do upgrade await proxy.connect(proxyAdminAsSigner).upgradeToAndCall(impl2, '0x'); @@ -300,8 +317,8 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('should remove function', async function () { - const impl1 = await ethers.deployContract('Implementation1'); - const impl2 = await ethers.deployContract('Implementation2'); + const impl1 = await this.ethers.deployContract('Implementation1'); + const impl2 = await this.ethers.deployContract('Implementation2'); const { instance, proxy, proxyAdminAsSigner } = await this.createProxyWithImpersonatedProxyAdmin( impl2, impl2.interface.encodeFunctionData('initialize'), @@ -316,12 +333,12 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { await proxy.connect(proxyAdminAsSigner).upgradeToAndCall(impl1, '0x'); // `getValue` is not available in impl1 - await expect(impl2.attach(instance).getValue()).to.be.reverted; + await expect(impl2.attach(instance).getValue()).to.be.revert(ethers); }); it('should change function signature', async function () { - const impl1 = await ethers.deployContract('Implementation1'); - const impl3 = await ethers.deployContract('Implementation3'); + const impl1 = await this.ethers.deployContract('Implementation1'); + const impl3 = await this.ethers.deployContract('Implementation3'); const { instance, proxy, proxyAdminAsSigner } = await this.createProxyWithImpersonatedProxyAdmin( impl1, impl1.interface.encodeFunctionData('initialize'), @@ -335,8 +352,8 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('should add fallback function', async function () { - const impl1 = await ethers.deployContract('Implementation1'); - const impl4 = await ethers.deployContract('Implementation4'); + const impl1 = await this.ethers.deployContract('Implementation1'); + const impl4 = await this.ethers.deployContract('Implementation4'); const { instance, proxy, proxyAdminAsSigner } = await this.createProxyWithImpersonatedProxyAdmin( impl1, impl1.interface.encodeFunctionData('initialize'), @@ -350,8 +367,8 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { }); it('should remove fallback function', async function () { - const impl2 = await ethers.deployContract('Implementation2'); - const impl4 = await ethers.deployContract('Implementation4'); + const impl2 = await this.ethers.deployContract('Implementation2'); + const impl4 = await this.ethers.deployContract('Implementation4'); const { instance, proxy, proxyAdminAsSigner } = await this.createProxyWithImpersonatedProxyAdmin( impl4, impl4.interface.encodeFunctionData('initialize'), @@ -359,9 +376,9 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy() { await proxy.connect(proxyAdminAsSigner).upgradeToAndCall(impl2, '0x'); - await expect(this.other.sendTransaction({ to: proxy })).to.be.reverted; + await expect(this.other.sendTransaction({ to: proxy })).to.be.revert(ethers); expect(await impl2.attach(instance).getValue()).to.equal(0n); }); }); -}; +} diff --git a/test/proxy/transparent/TransparentUpgradeableProxy.test.js b/test/proxy/transparent/TransparentUpgradeableProxy.test.js index 61e18014e5b..d9aa31ea733 100644 --- a/test/proxy/transparent/TransparentUpgradeableProxy.test.js +++ b/test/proxy/transparent/TransparentUpgradeableProxy.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { shouldBehaveLikeProxy } from '../Proxy.behaviour'; +import { shouldBehaveLikeTransparentUpgradeableProxy } from './TransparentUpgradeableProxy.behaviour'; -const shouldBehaveLikeProxy = require('../Proxy.behaviour'); -const shouldBehaveLikeTransparentUpgradeableProxy = require('./TransparentUpgradeableProxy.behaviour'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const [owner, other, ...accounts] = await ethers.getSigners(); @@ -17,6 +21,10 @@ async function fixture() { } describe('TransparentUpgradeableProxy', function () { + before(function () { + Object.assign(this, connection); + }); + beforeEach(async function () { Object.assign(this, await loadFixture(fixture)); }); diff --git a/test/proxy/utils/Initializable.test.js b/test/proxy/utils/Initializable.test.js index 6bf213f0d9d..e74aa0efc37 100644 --- a/test/proxy/utils/Initializable.test.js +++ b/test/proxy/utils/Initializable.test.js @@ -1,6 +1,8 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { MAX_UINT64 } = require('../../helpers/constants'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT64 } from '../../helpers/constants'; + +const { ethers } = await network.connect(); describe('Initializable', function () { describe('basic testing without inheritance', function () { diff --git a/test/proxy/utils/UUPSUpgradeable.test.js b/test/proxy/utils/UUPSUpgradeable.test.js index 55f910df67e..38a6d7fa04c 100644 --- a/test/proxy/utils/UUPSUpgradeable.test.js +++ b/test/proxy/utils/UUPSUpgradeable.test.js @@ -1,8 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { ImplementationLabel } from '../../helpers/storage'; -const { getAddressInSlot, ImplementationSlot } = require('../../helpers/storage'); +const connection = await network.connect(); +const { + ethers, + helpers: { storage }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const implInitial = await ethers.deployContract('UUPSUpgradeableMock'); @@ -30,7 +35,7 @@ async function fixture() { describe('UUPSUpgradeable', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('has an interface version', async function () { @@ -42,7 +47,7 @@ describe('UUPSUpgradeable', function () { .to.emit(this.instance, 'Upgraded') .withArgs(this.implUpgradeOk); - expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeOk); + expect(await storage.getAddressInSlot(this.instance, ImplementationLabel)).to.equal(this.implUpgradeOk); }); it('upgrade to upgradeable implementation with call', async function () { @@ -54,7 +59,7 @@ describe('UUPSUpgradeable', function () { .to.emit(this.instance, 'Upgraded') .withArgs(this.implUpgradeOk); - expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeOk); + expect(await storage.getAddressInSlot(this.instance, ImplementationLabel)).to.equal(this.implUpgradeOk); expect(await this.instance.current()).to.equal(1n); }); @@ -98,7 +103,7 @@ describe('UUPSUpgradeable', function () { .to.emit(this.instance, 'Upgraded') .withArgs(this.implUpgradeUnsafe); - expect(await getAddressInSlot(this.instance, ImplementationSlot)).to.equal(this.implUpgradeUnsafe); + expect(await storage.getAddressInSlot(this.instance, ImplementationLabel)).to.equal(this.implUpgradeUnsafe); }); // delegate to a non existing upgradeTo function causes a low level revert diff --git a/test/sanity.test.js b/test/sanity.test.js index ea0175c48c6..51e02e94666 100644 --- a/test/sanity.test.js +++ b/test/sanity.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture, mine }, +} = await network.connect(); async function fixture() { return {}; diff --git a/test/token/ERC1155/ERC1155.behavior.js b/test/token/ERC1155/ERC1155.behavior.js index 22f3ac6c40a..fe71afcb650 100644 --- a/test/token/ERC1155/ERC1155.behavior.js +++ b/test/token/ERC1155/ERC1155.behavior.js @@ -1,11 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { RevertType } from '../../helpers/enums'; +import { shouldSupportInterfaces } from '../../utils/introspection/SupportsInterface.behavior'; -const { RevertType } = require('../../helpers/enums'); -const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); - -function shouldBehaveLikeERC1155() { +export function shouldBehaveLikeERC1155() { const firstTokenId = 1n; const secondTokenId = 2n; const unknownTokenId = 3n; @@ -259,7 +258,7 @@ function shouldBehaveLikeERC1155() { describe('when sending to a valid receiver', function () { beforeEach(async function () { - this.receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + this.receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.None, @@ -317,7 +316,7 @@ function shouldBehaveLikeERC1155() { describe('to a receiver contract returning unexpected value', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ '0x00c0ffee', RECEIVER_BATCH_MAGIC_VALUE, RevertType.None, @@ -336,7 +335,7 @@ function shouldBehaveLikeERC1155() { describe('to a receiver contract that reverts', function () { describe('with a revert string', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.RevertWithMessage, @@ -352,7 +351,7 @@ function shouldBehaveLikeERC1155() { describe('without a revert string', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.RevertWithoutMessage, @@ -370,7 +369,7 @@ function shouldBehaveLikeERC1155() { describe('with a custom error', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.RevertWithCustomError, @@ -388,7 +387,7 @@ function shouldBehaveLikeERC1155() { describe('with a panic', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.Panic, @@ -613,7 +612,7 @@ function shouldBehaveLikeERC1155() { describe('when sending to a valid receiver', function () { beforeEach(async function () { - this.receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + this.receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.None, @@ -719,7 +718,7 @@ function shouldBehaveLikeERC1155() { describe('to a receiver contract returning unexpected value', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_SINGLE_MAGIC_VALUE, RevertType.None, @@ -744,7 +743,7 @@ function shouldBehaveLikeERC1155() { describe('to a receiver contract that reverts', function () { describe('with a revert string', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.RevertWithMessage, @@ -766,7 +765,7 @@ function shouldBehaveLikeERC1155() { describe('without a revert string', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.RevertWithoutMessage, @@ -790,7 +789,7 @@ function shouldBehaveLikeERC1155() { describe('with a custom error', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.RevertWithCustomError, @@ -814,7 +813,7 @@ function shouldBehaveLikeERC1155() { describe('with a panic', function () { it('reverts', async function () { - const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [ + const receiver = await this.ethers.deployContract('$ERC1155ReceiverMock', [ RECEIVER_SINGLE_MAGIC_VALUE, RECEIVER_BATCH_MAGIC_VALUE, RevertType.Panic, @@ -859,7 +858,3 @@ function shouldBehaveLikeERC1155() { shouldSupportInterfaces(['ERC1155', 'ERC1155MetadataURI']); }); } - -module.exports = { - shouldBehaveLikeERC1155, -}; diff --git a/test/token/ERC1155/ERC1155.test.js b/test/token/ERC1155/ERC1155.test.js index 265cfb17555..ec3a995714a 100644 --- a/test/token/ERC1155/ERC1155.test.js +++ b/test/token/ERC1155/ERC1155.test.js @@ -1,10 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { RevertType } = require('../../helpers/enums'); -const { zip } = require('../../helpers/iterate'); -const { shouldBehaveLikeERC1155 } = require('./ERC1155.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { RevertType } from '../../helpers/enums'; +import { zip } from '../../helpers/iterate'; +import { shouldBehaveLikeERC1155 } from './ERC1155.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const initialURI = 'https://token-cdn-domain/{id}.json'; @@ -16,7 +20,7 @@ async function fixture() { describe('ERC1155', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeERC1155(); diff --git a/test/token/ERC1155/extensions/ERC1155Burnable.test.js b/test/token/ERC1155/extensions/ERC1155Burnable.test.js index 01e7ee2edbf..74e57ae4e17 100644 --- a/test/token/ERC1155/extensions/ERC1155Burnable.test.js +++ b/test/token/ERC1155/extensions/ERC1155Burnable.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const ids = [42n, 1137n]; const values = [3000n, 9902n]; diff --git a/test/token/ERC1155/extensions/ERC1155Pausable.test.js b/test/token/ERC1155/extensions/ERC1155Pausable.test.js index 81c4f69bc45..f91ae5fed06 100644 --- a/test/token/ERC1155/extensions/ERC1155Pausable.test.js +++ b/test/token/ERC1155/extensions/ERC1155Pausable.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [holder, operator, receiver, other] = await ethers.getSigners(); diff --git a/test/token/ERC1155/extensions/ERC1155Supply.test.js b/test/token/ERC1155/extensions/ERC1155Supply.test.js index cca36a0dfed..170aa6f2231 100644 --- a/test/token/ERC1155/extensions/ERC1155Supply.test.js +++ b/test/token/ERC1155/extensions/ERC1155Supply.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [holder] = await ethers.getSigners(); diff --git a/test/token/ERC1155/extensions/ERC1155URIStorage.test.js b/test/token/ERC1155/extensions/ERC1155URIStorage.test.js index a0d9b570488..2c1100bf8fd 100644 --- a/test/token/ERC1155/extensions/ERC1155URIStorage.test.js +++ b/test/token/ERC1155/extensions/ERC1155URIStorage.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const erc1155Uri = 'https://token.com/nfts/'; const baseUri = 'https://token.com/'; diff --git a/test/token/ERC1155/utils/ERC1155Holder.test.js b/test/token/ERC1155/utils/ERC1155Holder.test.js index 9bff487adad..ecfc285a50c 100644 --- a/test/token/ERC1155/utils/ERC1155Holder.test.js +++ b/test/token/ERC1155/utils/ERC1155Holder.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const ids = [1n, 2n, 3n]; const values = [1000n, 2000n, 3000n]; diff --git a/test/token/ERC1155/utils/ERC1155Utils.test.js b/test/token/ERC1155/utils/ERC1155Utils.test.js index 5687568d341..66baf0f0fed 100644 --- a/test/token/ERC1155/utils/ERC1155Utils.test.js +++ b/test/token/ERC1155/utils/ERC1155Utils.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { RevertType } = require('../../../helpers/enums'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { RevertType } from '../../../helpers/enums'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const firstTokenId = 1n; const secondTokenId = 2n; @@ -52,7 +56,7 @@ describe('ERC1155Utils', function () { firstTokenValue, '0x', ), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); }); it('succeeds when data is passed', async function () { @@ -66,7 +70,7 @@ describe('ERC1155Utils', function () { firstTokenValue, data, ), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); }); it('succeeds when data is empty', async function () { @@ -79,7 +83,7 @@ describe('ERC1155Utils', function () { firstTokenValue, '0x', ), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); }); it('reverts when receiver returns invalid value', async function () { @@ -180,7 +184,7 @@ describe('ERC1155Utils', function () { [firstTokenValue, secondTokenValue], '0x', ), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); }); it('succeeds when data is passed', async function () { @@ -194,7 +198,7 @@ describe('ERC1155Utils', function () { [firstTokenValue, secondTokenValue], data, ), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); }); it('succeeds when data is empty', async function () { @@ -207,7 +211,7 @@ describe('ERC1155Utils', function () { [firstTokenValue, secondTokenValue], '0x', ), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); }); it('reverts when receiver returns invalid value', async function () { diff --git a/test/token/ERC20/ERC20.behavior.js b/test/token/ERC20/ERC20.behavior.js index 2447602dbbf..fc83a0a133b 100644 --- a/test/token/ERC20/ERC20.behavior.js +++ b/test/token/ERC20/ERC20.behavior.js @@ -1,7 +1,7 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; -function shouldBehaveLikeERC20(initialSupply, opts = {}) { +export function shouldBehaveLikeERC20(initialSupply, opts = {}) { const { forcedApproval } = opts; beforeEach(async function () { @@ -46,7 +46,12 @@ function shouldBehaveLikeERC20(initialSupply, opts = {}) { }); it('transfers the requested value', async function () { - await expect(this.tx).to.changeTokenBalances(this.token, [this.holder, this.other], [-value, value]); + await expect(this.tx).to.changeTokenBalances( + this.ethers, + this.token, + [this.holder, this.other], + [-value, value], + ); }); it('decreases the spender allowance', async function () { @@ -157,7 +162,7 @@ function shouldBehaveLikeERC20(initialSupply, opts = {}) { }); } -function shouldBehaveLikeERC20Transfer(balance) { +export function shouldBehaveLikeERC20Transfer(balance) { describe('when the recipient is not the zero address', function () { it('reverts when the sender does not have enough balance', async function () { const value = balance + 1n; @@ -174,7 +179,12 @@ function shouldBehaveLikeERC20Transfer(balance) { }); it('transfers the requested value', async function () { - await expect(this.tx).to.changeTokenBalances(this.token, [this.holder, this.recipient], [-value, value]); + await expect(this.tx).to.changeTokenBalances( + this.ethers, + this.token, + [this.holder, this.recipient], + [-value, value], + ); }); it('emits a transfer event', async function () { @@ -190,7 +200,7 @@ function shouldBehaveLikeERC20Transfer(balance) { }); it('transfers the requested value', async function () { - await expect(this.tx).to.changeTokenBalances(this.token, [this.holder, this.recipient], [0n, 0n]); + await expect(this.tx).to.changeTokenBalances(this.ethers, this.token, [this.holder, this.recipient], [0n, 0n]); }); it('emits a transfer event', async function () { @@ -206,7 +216,7 @@ function shouldBehaveLikeERC20Transfer(balance) { }); } -function shouldBehaveLikeERC20Approve(supply) { +export function shouldBehaveLikeERC20Approve(supply) { describe('when the spender is not the zero address', function () { describe('when the sender has enough balance', function () { const value = supply; @@ -261,9 +271,3 @@ function shouldBehaveLikeERC20Approve(supply) { .withArgs(ethers.ZeroAddress); }); } - -module.exports = { - shouldBehaveLikeERC20, - shouldBehaveLikeERC20Transfer, - shouldBehaveLikeERC20Approve, -}; diff --git a/test/token/ERC20/ERC20.test.js b/test/token/ERC20/ERC20.test.js index 2d9eefe1c89..dca0ab1f8a1 100644 --- a/test/token/ERC20/ERC20.test.js +++ b/test/token/ERC20/ERC20.test.js @@ -1,13 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { shouldBehaveLikeERC20, shouldBehaveLikeERC20Transfer, shouldBehaveLikeERC20Approve } from './ERC20.behavior'; +const connection = await network.connect(); const { - shouldBehaveLikeERC20, - shouldBehaveLikeERC20Transfer, - shouldBehaveLikeERC20Approve, -} = require('./ERC20.behavior'); + ethers, + networkHelpers: { loadFixture }, +} = connection; const TOKENS = [{ Token: '$ERC20' }, { Token: '$ERC20ApprovalMock', forcedApproval: true }]; @@ -30,7 +30,7 @@ describe('ERC20', function () { }; beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeERC20(initialSupply, { forcedApproval }); @@ -71,7 +71,7 @@ describe('ERC20', function () { }); it('increments recipient balance', async function () { - await expect(this.tx).to.changeTokenBalance(this.token, this.recipient, value); + await expect(this.tx).to.changeTokenBalance(ethers, this.token, this.recipient, value); }); it('emits Transfer event', async function () { @@ -105,7 +105,7 @@ describe('ERC20', function () { }); it('decrements holder balance', async function () { - await expect(this.tx).to.changeTokenBalance(this.token, this.holder, -value); + await expect(this.tx).to.changeTokenBalance(ethers, this.token, this.holder, -value); }); it('emits Transfer event', async function () { @@ -131,7 +131,7 @@ describe('ERC20', function () { await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.holder, value); expect(await this.token.totalSupply()).to.equal(this.totalSupply + value); - await expect(tx).to.changeTokenBalance(this.token, this.holder, value); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.holder, value); }); it('to is the zero address', async function () { @@ -139,7 +139,7 @@ describe('ERC20', function () { await expect(tx).to.emit(this.token, 'Transfer').withArgs(this.holder, ethers.ZeroAddress, value); expect(await this.token.totalSupply()).to.equal(this.totalSupply - value); - await expect(tx).to.changeTokenBalance(this.token, this.holder, -value); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.holder, -value); }); describe('from and to are the same address', function () { @@ -148,7 +148,7 @@ describe('ERC20', function () { await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, ethers.ZeroAddress, value); expect(await this.token.totalSupply()).to.equal(this.totalSupply); - await expect(tx).to.changeTokenBalance(this.token, ethers.ZeroAddress, 0n); + await expect(tx).to.changeTokenBalance(ethers, this.token, ethers.ZeroAddress, 0n); }); describe('non zero address', function () { @@ -160,7 +160,7 @@ describe('ERC20', function () { it('executes with balance', async function () { const tx = await this.token.$_update(this.holder, this.holder, value); - await expect(tx).to.changeTokenBalance(this.token, this.holder, 0n); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.holder, 0n); await expect(tx).to.emit(this.token, 'Transfer').withArgs(this.holder, this.holder, value); }); }); diff --git a/test/token/ERC20/extensions/ERC1363.test.js b/test/token/ERC20/extensions/ERC1363.test.js index 3d1f4e58f8e..b338c3226d4 100644 --- a/test/token/ERC20/extensions/ERC1363.test.js +++ b/test/token/ERC20/extensions/ERC1363.test.js @@ -1,14 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeERC20, shouldBehaveLikeERC20Transfer, shouldBehaveLikeERC20Approve } from '../ERC20.behavior'; +import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; +import { RevertType } from '../../../helpers/enums'; +const connection = await network.connect(); const { - shouldBehaveLikeERC20, - shouldBehaveLikeERC20Transfer, - shouldBehaveLikeERC20Approve, -} = require('../ERC20.behavior.js'); -const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); -const { RevertType } = require('../../../helpers/enums.js'); + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'My Token'; const symbol = 'MTKN'; @@ -42,7 +42,7 @@ async function fixture() { describe('ERC1363', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldSupportInterfaces(['ERC165', 'ERC1363']); diff --git a/test/token/ERC20/extensions/ERC20Burnable.test.js b/test/token/ERC20/extensions/ERC20Burnable.test.js index dc40c791773..258aefaf261 100644 --- a/test/token/ERC20/extensions/ERC20Burnable.test.js +++ b/test/token/ERC20/extensions/ERC20Burnable.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'My Token'; const symbol = 'MTKN'; @@ -40,7 +44,7 @@ describe('ERC20Burnable', function () { }); it('burns the requested value', async function () { - await expect(this.tx).to.changeTokenBalance(this.token, this.owner, -value); + await expect(this.tx).to.changeTokenBalance(ethers, this.token, this.owner, -value); }); it('emits a transfer event', async function () { @@ -88,7 +92,7 @@ describe('ERC20Burnable', function () { }); it('burns the requested value', async function () { - await expect(this.tx).to.changeTokenBalance(this.token, this.owner, -value); + await expect(this.tx).to.changeTokenBalance(ethers, this.token, this.owner, -value); }); it('decrements allowance', async function () { diff --git a/test/token/ERC20/extensions/ERC20Capped.test.js b/test/token/ERC20/extensions/ERC20Capped.test.js index a32ec43a8ea..f8e34a6a33f 100644 --- a/test/token/ERC20/extensions/ERC20Capped.test.js +++ b/test/token/ERC20/extensions/ERC20Capped.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/ERC20Crosschain.test.js b/test/token/ERC20/extensions/ERC20Crosschain.test.js index 99e922b0b88..a7ec5cce036 100644 --- a/test/token/ERC20/extensions/ERC20Crosschain.test.js +++ b/test/token/ERC20/extensions/ERC20Crosschain.test.js @@ -1,15 +1,16 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { impersonate } = require('../../../helpers/account'); -const { getLocalChain } = require('../../../helpers/chains'); - -const { shouldBehaveLikeBridgeERC20 } = require('../../../crosschain/BridgeERC20.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { shouldBehaveLikeBridgeERC20 } from '../../../crosschain/BridgeERC20.behavior'; + +const connection = await network.connect(); +const { + ethers, + helpers: { chain, impersonate }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const chain = await getLocalChain(); const accounts = await ethers.getSigners(); // Mock gateway @@ -30,12 +31,12 @@ async function fixture() { .withArgs(gateway, chain.toErc7930(bridgeB)); await tokenB.$_setBridge(bridgeB); - return { chain, accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; + return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('ERC20Crosschain', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeBridgeERC20(); @@ -49,18 +50,18 @@ describe('ERC20Crosschain', function () { await this.tokenA.connect(alice).approve(chris, ethers.MaxUint256); // Alice sends tokens from chain A to Bruce on chain B. - await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), amount)) + await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, chain.toErc7930(bruce), amount)) // bridge on chain A takes custody of the funds .to.emit(this.tokenA, 'Transfer') .withArgs(alice, ethers.ZeroAddress, amount) // crosschain transfer sent .to.emit(this.tokenA, 'CrosschainERC20TransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), amount) + .withArgs(anyValue, alice, chain.toErc7930(bruce), amount) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeB, 'CrosschainERC20TransferReceived') - .withArgs(anyValue, this.chain.toErc7930(alice), bruce, amount) + .withArgs(anyValue, chain.toErc7930(alice), bruce, amount) // crosschain mint event .to.emit(this.tokenB, 'CrosschainMint') .withArgs(bruce, amount, this.bridgeB) @@ -75,7 +76,7 @@ describe('ERC20Crosschain', function () { await this.tokenA.$_mint(alice, amount); - await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), amount)) + await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, chain.toErc7930(bruce), amount)) .to.be.revertedWithCustomError(this.tokenA, 'ERC20InsufficientAllowance') .withArgs(chris, 0n, amount); }); diff --git a/test/token/ERC20/extensions/ERC20FlashMint.test.js b/test/token/ERC20/extensions/ERC20FlashMint.test.js index 1c751f74cec..f973c2ea060 100644 --- a/test/token/ERC20/extensions/ERC20FlashMint.test.js +++ b/test/token/ERC20/extensions/ERC20FlashMint.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'My Token'; const symbol = 'MTKN'; @@ -63,7 +67,7 @@ describe('ERC20FlashMint', function () { .withArgs(this.token, receiver, loanValue) .to.emit(receiver, 'TotalSupply') .withArgs(this.token, initialSupply + loanValue); - await expect(tx).to.changeTokenBalance(this.token, receiver, 0); + await expect(tx).to.changeTokenBalance(ethers, this.token, receiver, 0); expect(await this.token.totalSupply()).to.equal(initialSupply); expect(await this.token.allowance(receiver, this.token)).to.equal(0n); @@ -110,7 +114,7 @@ describe('ERC20FlashMint', function () { await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(ethers.ZeroAddress, this.receiver, receiverInitialBalance); - await expect(tx).to.changeTokenBalance(this.token, this.receiver, receiverInitialBalance); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.receiver, receiverInitialBalance); await this.token.setFlashFee(flashFee); expect(await this.token.flashFee(this.token, loanValue)).to.equal(flashFee); @@ -127,7 +131,12 @@ describe('ERC20FlashMint', function () { .withArgs(this.token, this.receiver, receiverInitialBalance + loanValue) .to.emit(this.receiver, 'TotalSupply') .withArgs(this.token, initialSupply + receiverInitialBalance + loanValue); - await expect(tx).to.changeTokenBalances(this.token, [this.receiver, ethers.ZeroAddress], [-flashFee, 0]); + await expect(tx).to.changeTokenBalances( + ethers, + this.token, + [this.receiver, ethers.ZeroAddress], + [-flashFee, 0], + ); expect(await this.token.totalSupply()).to.equal(initialSupply + receiverInitialBalance - flashFee); expect(await this.token.allowance(this.receiver, this.token)).to.equal(0n); @@ -151,6 +160,7 @@ describe('ERC20FlashMint', function () { .to.emit(this.receiver, 'TotalSupply') .withArgs(this.token, initialSupply + receiverInitialBalance + loanValue); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.receiver, flashFeeReceiverAddress], [-flashFee, flashFee], diff --git a/test/token/ERC20/extensions/ERC20Pausable.test.js b/test/token/ERC20/extensions/ERC20Pausable.test.js index 1f1157c19e9..f020fc47c27 100644 --- a/test/token/ERC20/extensions/ERC20Pausable.test.js +++ b/test/token/ERC20/extensions/ERC20Pausable.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'My Token'; const symbol = 'MTKN'; @@ -24,6 +28,7 @@ describe('ERC20Pausable', function () { describe('transfer', function () { it('allows to transfer when unpaused', async function () { await expect(this.token.connect(this.holder).transfer(this.recipient, initialSupply)).to.changeTokenBalances( + ethers, this.token, [this.holder, this.recipient], [-initialSupply, initialSupply], @@ -35,6 +40,7 @@ describe('ERC20Pausable', function () { await this.token.$_unpause(); await expect(this.token.connect(this.holder).transfer(this.recipient, initialSupply)).to.changeTokenBalances( + ethers, this.token, [this.holder, this.recipient], [-initialSupply, initialSupply], @@ -60,7 +66,7 @@ describe('ERC20Pausable', function () { it('allows to transfer from when unpaused', async function () { await expect( this.token.connect(this.approved).transferFrom(this.holder, this.recipient, allowance), - ).to.changeTokenBalances(this.token, [this.holder, this.recipient], [-allowance, allowance]); + ).to.changeTokenBalances(ethers, this.token, [this.holder, this.recipient], [-allowance, allowance]); }); it('allows to transfer when paused and then unpaused', async function () { @@ -69,7 +75,7 @@ describe('ERC20Pausable', function () { await expect( this.token.connect(this.approved).transferFrom(this.holder, this.recipient, allowance), - ).to.changeTokenBalances(this.token, [this.holder, this.recipient], [-allowance, allowance]); + ).to.changeTokenBalances(ethers, this.token, [this.holder, this.recipient], [-allowance, allowance]); }); it('reverts when trying to transfer from when paused', async function () { @@ -85,14 +91,24 @@ describe('ERC20Pausable', function () { const value = 42n; it('allows to mint when unpaused', async function () { - await expect(this.token.$_mint(this.recipient, value)).to.changeTokenBalance(this.token, this.recipient, value); + await expect(this.token.$_mint(this.recipient, value)).to.changeTokenBalance( + ethers, + this.token, + this.recipient, + value, + ); }); it('allows to mint when paused and then unpaused', async function () { await this.token.$_pause(); await this.token.$_unpause(); - await expect(this.token.$_mint(this.recipient, value)).to.changeTokenBalance(this.token, this.recipient, value); + await expect(this.token.$_mint(this.recipient, value)).to.changeTokenBalance( + ethers, + this.token, + this.recipient, + value, + ); }); it('reverts when trying to mint when paused', async function () { @@ -109,14 +125,24 @@ describe('ERC20Pausable', function () { const value = 42n; it('allows to burn when unpaused', async function () { - await expect(this.token.$_burn(this.holder, value)).to.changeTokenBalance(this.token, this.holder, -value); + await expect(this.token.$_burn(this.holder, value)).to.changeTokenBalance( + ethers, + this.token, + this.holder, + -value, + ); }); it('allows to burn when paused and then unpaused', async function () { await this.token.$_pause(); await this.token.$_unpause(); - await expect(this.token.$_burn(this.holder, value)).to.changeTokenBalance(this.token, this.holder, -value); + await expect(this.token.$_burn(this.holder, value)).to.changeTokenBalance( + ethers, + this.token, + this.holder, + -value, + ); }); it('reverts when trying to burn when paused', async function () { diff --git a/test/token/ERC20/extensions/ERC20Permit.test.js b/test/token/ERC20/extensions/ERC20Permit.test.js index c3c80d7bbe9..5a099d2f7b0 100644 --- a/test/token/ERC20/extensions/ERC20Permit.test.js +++ b/test/token/ERC20/extensions/ERC20Permit.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain, domainSeparator, Permit } = require('../../../helpers/eip712'); -const time = require('../../../helpers/time'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { Permit, getDomain, domainSeparator } from '../../../helpers/eip712'; + +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/ERC20Votes.test.js b/test/token/ERC20/extensions/ERC20Votes.test.js index 3c595c9191d..819fa15145e 100644 --- a/test/token/ERC20/extensions/ERC20Votes.test.js +++ b/test/token/ERC20/extensions/ERC20Votes.test.js @@ -1,12 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); - -const { getDomain, Delegation } = require('../../../helpers/eip712'); -const { batchInBlock } = require('../../../helpers/txpool'); -const time = require('../../../helpers/time'); - -const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { Delegation, getDomain } from '../../../helpers/eip712'; +import { batchInBlock } from '../../../helpers/txpool'; +import { shouldBehaveLikeVotes } from '../../../governance/utils/Votes.behavior'; + +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture, mine }, +} = connection; const TOKENS = [ { Token: '$ERC20Votes', mode: 'blocknumber' }, @@ -33,7 +36,7 @@ describe('ERC20Votes', function () { describe(`vote with ${mode}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); this.votes = this.token; }); @@ -401,11 +404,14 @@ describe('ERC20Votes', function () { await this.token.connect(this.holder).transfer(this.recipient, 100n); expect(await this.token.numCheckpoints(this.other1)).to.equal(0n); - const [t1, t2, t3] = await batchInBlock([ - () => this.token.connect(this.recipient).delegate(this.other1, { gasLimit: 200000 }), - () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200000 }), - () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200000 }), - ]); + const [t1, t2, t3] = await batchInBlock( + [ + () => this.token.connect(this.recipient).delegate(this.other1, { gasLimit: 200000 }), + () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200000 }), + () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200000 }), + ], + ethers.provider, + ); t1.timepoint = await time.clockFromReceipt[mode](t1); t2.timepoint = await time.clockFromReceipt[mode](t2); t3.timepoint = await time.clockFromReceipt[mode](t3); diff --git a/test/token/ERC20/extensions/ERC20Wrapper.test.js b/test/token/ERC20/extensions/ERC20Wrapper.test.js index 2f630e638c1..9e6df1db164 100644 --- a/test/token/ERC20/extensions/ERC20Wrapper.test.js +++ b/test/token/ERC20/extensions/ERC20Wrapper.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeERC20 } from '../ERC20.behavior'; -const { shouldBehaveLikeERC20 } = require('../ERC20.behavior'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'My Token'; const symbol = 'MTKN'; @@ -24,7 +28,7 @@ async function fixture() { describe('ERC20Wrapper', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); afterEach('Underlying balance', async function () { @@ -64,11 +68,12 @@ describe('ERC20Wrapper', function () { .to.emit(this.token, 'Transfer') .withArgs(ethers.ZeroAddress, this.holder, initialSupply); await expect(tx).to.changeTokenBalances( + ethers, this.underlying, [this.holder, this.token], [-initialSupply, initialSupply], ); - await expect(tx).to.changeTokenBalance(this.token, this.holder, initialSupply); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.holder, initialSupply); }); it('reverts when missing approval', async function () { @@ -95,11 +100,12 @@ describe('ERC20Wrapper', function () { .to.emit(this.token, 'Transfer') .withArgs(ethers.ZeroAddress, this.recipient, initialSupply); await expect(tx).to.changeTokenBalances( + ethers, this.underlying, [this.holder, this.token], [-initialSupply, initialSupply], ); - await expect(tx).to.changeTokenBalances(this.token, [this.holder, this.recipient], [0, initialSupply]); + await expect(tx).to.changeTokenBalances(ethers, this.token, [this.holder, this.recipient], [0, initialSupply]); }); it('reverts minting to the wrapper contract', async function () { @@ -132,8 +138,8 @@ describe('ERC20Wrapper', function () { .withArgs(this.token.target, this.holder, value) .to.emit(this.token, 'Transfer') .withArgs(this.holder, ethers.ZeroAddress, value); - await expect(tx).to.changeTokenBalances(this.underlying, [this.token, this.holder], [-value, value]); - await expect(tx).to.changeTokenBalance(this.token, this.holder, -value); + await expect(tx).to.changeTokenBalances(ethers, this.underlying, [this.token, this.holder], [-value, value]); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.holder, -value); }); it('entire balance', async function () { @@ -144,11 +150,12 @@ describe('ERC20Wrapper', function () { .to.emit(this.token, 'Transfer') .withArgs(this.holder, ethers.ZeroAddress, initialSupply); await expect(tx).to.changeTokenBalances( + ethers, this.underlying, [this.token, this.holder], [-initialSupply, initialSupply], ); - await expect(tx).to.changeTokenBalance(this.token, this.holder, -initialSupply); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.holder, -initialSupply); }); it('to other account', async function () { @@ -159,11 +166,12 @@ describe('ERC20Wrapper', function () { .to.emit(this.token, 'Transfer') .withArgs(this.holder, ethers.ZeroAddress, initialSupply); await expect(tx).to.changeTokenBalances( + ethers, this.underlying, [this.token, this.holder, this.recipient], [-initialSupply, 0, initialSupply], ); - await expect(tx).to.changeTokenBalance(this.token, this.holder, -initialSupply); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.holder, -initialSupply); }); it('reverts withdrawing to the wrapper contract', async function () { @@ -180,7 +188,7 @@ describe('ERC20Wrapper', function () { const tx = await this.token.$_recover(this.recipient); await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.recipient, 0n); - await expect(tx).to.changeTokenBalance(this.token, this.recipient, 0); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.recipient, 0); }); it('something to recover', async function () { @@ -188,7 +196,7 @@ describe('ERC20Wrapper', function () { const tx = await this.token.$_recover(this.recipient); await expect(tx).to.emit(this.token, 'Transfer').withArgs(ethers.ZeroAddress, this.recipient, initialSupply); - await expect(tx).to.changeTokenBalance(this.token, this.recipient, initialSupply); + await expect(tx).to.changeTokenBalance(ethers, this.token, this.recipient, initialSupply); }); }); diff --git a/test/token/ERC20/extensions/ERC4626.test.js b/test/token/ERC20/extensions/ERC4626.test.js index ad8c926917f..a8056161730 100644 --- a/test/token/ERC20/extensions/ERC4626.test.js +++ b/test/token/ERC20/extensions/ERC4626.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { Enum } from '../../../helpers/enums'; -const { Enum } = require('../../../helpers/enums'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'My Token'; const symbol = 'MTKN'; @@ -260,11 +263,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).deposit(parseToken(1n), this.recipient); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.holder, this.vault], [-parseToken(1n), parseToken(1n)], ); - await expect(tx).to.changeTokenBalance(this.vault, this.recipient, parseShare(1n)); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.recipient, parseShare(1n)); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.holder, this.vault, parseToken(1n)) @@ -281,11 +285,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).mint(parseShare(1n), this.recipient); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.holder, this.vault], [-parseToken(1n), parseToken(1n)], ); - await expect(tx).to.changeTokenBalance(this.vault, this.recipient, parseShare(1n)); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.recipient, parseShare(1n)); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.holder, this.vault, parseToken(1n)) @@ -301,8 +306,8 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).withdraw(0n, this.recipient, this.holder); - await expect(tx).to.changeTokenBalances(this.token, [this.vault, this.recipient], [0n, 0n]); - await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); + await expect(tx).to.changeTokenBalances(ethers, this.token, [this.vault, this.recipient], [0n, 0n]); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.holder, 0n); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.vault, this.recipient, 0n) @@ -318,8 +323,8 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).redeem(0n, this.recipient, this.holder); - await expect(tx).to.changeTokenBalances(this.token, [this.vault, this.recipient], [0n, 0n]); - await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); + await expect(tx).to.changeTokenBalances(ethers, this.token, [this.vault, this.recipient], [0n, 0n]); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.holder, 0n); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.vault, this.recipient, 0n) @@ -365,11 +370,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).deposit(depositAssets, this.recipient); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.holder, this.vault], [-depositAssets, depositAssets], ); - await expect(tx).to.changeTokenBalance(this.vault, this.recipient, expectedShares); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.recipient, expectedShares); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.holder, this.vault, depositAssets) @@ -403,11 +409,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).mint(mintShares, this.recipient); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.holder, this.vault], [-expectedAssets, expectedAssets], ); - await expect(tx).to.changeTokenBalance(this.vault, this.recipient, mintShares); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.recipient, mintShares); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.holder, this.vault, expectedAssets) @@ -423,8 +430,8 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).withdraw(0n, this.recipient, this.holder); - await expect(tx).to.changeTokenBalances(this.token, [this.vault, this.recipient], [0n, 0n]); - await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); + await expect(tx).to.changeTokenBalances(ethers, this.token, [this.vault, this.recipient], [0n, 0n]); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.holder, 0n); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.vault, this.recipient, 0n) @@ -440,8 +447,8 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).redeem(0n, this.recipient, this.holder); - await expect(tx).to.changeTokenBalances(this.token, [this.vault, this.recipient], [0n, 0n]); - await expect(tx).to.changeTokenBalance(this.vault, this.holder, 0n); + await expect(tx).to.changeTokenBalances(ethers, this.token, [this.vault, this.recipient], [0n, 0n]); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.holder, 0n); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.vault, this.recipient, 0n) @@ -486,11 +493,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).deposit(depositAssets, this.recipient); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.holder, this.vault], [-depositAssets, depositAssets], ); - await expect(tx).to.changeTokenBalance(this.vault, this.recipient, expectedShares); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.recipient, expectedShares); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.holder, this.vault, depositAssets) @@ -522,11 +530,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).mint(mintShares, this.recipient); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.holder, this.vault], [-expectedAssets, expectedAssets], ); - await expect(tx).to.changeTokenBalance(this.vault, this.recipient, mintShares); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.recipient, mintShares); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.holder, this.vault, expectedAssets) @@ -549,11 +558,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).withdraw(withdrawAssets, this.recipient, this.holder); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.vault, this.recipient], [-withdrawAssets, withdrawAssets], ); - await expect(tx).to.changeTokenBalance(this.vault, this.holder, -expectedShares); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.holder, -expectedShares); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.vault, this.recipient, withdrawAssets) @@ -570,8 +580,9 @@ describe('ERC4626', function () { .to.be.revertedWithCustomError(this.vault, 'ERC20InsufficientAllowance') .withArgs(this.other, 0n, assets); - await expect(this.vault.connect(this.spender).withdraw(parseToken(1n), this.recipient, this.holder)).to.not.be - .reverted; + await expect( + this.vault.connect(this.spender).withdraw(parseToken(1n), this.recipient, this.holder), + ).to.not.be.revert(ethers); }); it('redeem', async function () { @@ -587,11 +598,12 @@ describe('ERC4626', function () { const tx = this.vault.connect(this.holder).redeem(redeemShares, this.recipient, this.holder); await expect(tx).to.changeTokenBalances( + ethers, this.token, [this.vault, this.recipient], [-expectedAssets, expectedAssets], ); - await expect(tx).to.changeTokenBalance(this.vault, this.holder, -redeemShares); + await expect(tx).to.changeTokenBalance(ethers, this.vault, this.holder, -redeemShares); await expect(tx) .to.emit(this.token, 'Transfer') .withArgs(this.vault, this.recipient, expectedAssets) @@ -606,8 +618,9 @@ describe('ERC4626', function () { .to.be.revertedWithCustomError(this.vault, 'ERC20InsufficientAllowance') .withArgs(this.other, 0n, parseShare(100n)); - await expect(this.vault.connect(this.spender).redeem(parseShare(100n), this.recipient, this.holder)).to.not.be - .reverted; + await expect( + this.vault.connect(this.spender).redeem(parseShare(100n), this.recipient, this.holder), + ).to.not.be.revert(ethers); }); }); }); @@ -650,11 +663,12 @@ describe('ERC4626', function () { afterEach(async function () { await expect(this.tx).to.changeTokenBalances( + ethers, this.token, [this.holder, this.vault, this.other], [-valueWithFees, valueWithoutFees, fees], ); - await expect(this.tx).to.changeTokenBalance(this.vault, this.recipient, valueWithoutFees); + await expect(this.tx).to.changeTokenBalance(ethers, this.vault, this.recipient, valueWithoutFees); await expect(this.tx) // get total .to.emit(this.token, 'Transfer') @@ -702,11 +716,12 @@ describe('ERC4626', function () { afterEach(async function () { await expect(this.tx).to.changeTokenBalances( + ethers, this.token, [this.vault, this.recipient, this.other], [-valueWithFees, valueWithoutFees, fees], ); - await expect(this.tx).to.changeTokenBalance(this.vault, this.holder, -valueWithFees); + await expect(this.tx).to.changeTokenBalance(ethers, this.vault, this.holder, -valueWithFees); await expect(this.tx) // withdraw principal .to.emit(this.token, 'Transfer') diff --git a/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js b/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js index 06c3bb29095..7b72fcc1c5c 100644 --- a/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js +++ b/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js @@ -1,9 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeERC20 } from '../ERC20.behavior'; +import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; -const { shouldBehaveLikeERC20 } = require('../ERC20.behavior.js'); -const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'My Token'; const symbol = 'MTKN'; @@ -20,7 +24,7 @@ async function fixture() { describe('ERC20Bridgeable', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); describe('onlyTokenBridgeFn', function () { diff --git a/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js b/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js index a1f6362add6..ebd25a6cedd 100644 --- a/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js +++ b/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js @@ -1,9 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { max, min } = require('../../../helpers/math.js'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { max, min } from '../../../helpers/math'; +import { shouldBehaveLikeERC20 } from '../ERC20.behavior'; -const { shouldBehaveLikeERC20 } = require('../ERC20.behavior.js'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'My Token'; const symbol = 'MTKN'; @@ -26,7 +30,7 @@ async function fixture() { describe('ERC20TemporaryApproval', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeERC20(initialSupply); diff --git a/test/token/ERC20/utils/SafeERC20.test.js b/test/token/ERC20/utils/SafeERC20.test.js index 0ae94630d31..77679ee57f4 100644 --- a/test/token/ERC20/utils/SafeERC20.test.js +++ b/test/token/ERC20/utils/SafeERC20.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'ERC20Mock'; const symbol = 'ERC20Mock'; @@ -80,12 +84,12 @@ describe('SafeERC20', function () { it('reverts on increaseAllowance', async function () { // Call to 'token.allowance' does not return any data, resulting in a decoding error (revert without reason) - await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n)).to.be.revertedWithoutReason(); + await expect(this.mock.$safeIncreaseAllowance(this.token, this.spender, 0n)).to.be.revertedWithoutReason(ethers); }); it('reverts on decreaseAllowance', async function () { // Call to 'token.allowance' does not return any data, resulting in a decoding error (revert without reason) - await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 0n)).to.be.revertedWithoutReason(); + await expect(this.mock.$safeDecreaseAllowance(this.token, this.spender, 0n)).to.be.revertedWithoutReason(ethers); }); it('reverts on forceApprove', async function () { @@ -316,19 +320,19 @@ describe('SafeERC20', function () { it('reverts on transferAndCallRelaxed', async function () { await expect( this.mock.$transferAndCallRelaxed(this.token, this.erc1363Receiver, 0n, data), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); }); it('reverts on transferFromAndCallRelaxed', async function () { await expect( this.mock.$transferFromAndCallRelaxed(this.token, this.mock, this.erc1363Receiver, 0n, data), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); }); it('reverts on approveAndCallRelaxed', async function () { await expect( this.mock.$approveAndCallRelaxed(this.token, this.erc1363Spender, 0n, data), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); }); }); diff --git a/test/token/ERC6909/ERC6909.behavior.js b/test/token/ERC6909/ERC6909.behavior.js index adfe15a32e6..df51d5ad0e5 100644 --- a/test/token/ERC6909/ERC6909.behavior.js +++ b/test/token/ERC6909/ERC6909.behavior.js @@ -1,9 +1,8 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { shouldSupportInterfaces } from '../../utils/introspection/SupportsInterface.behavior'; -const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); - -function shouldBehaveLikeERC6909() { +export function shouldBehaveLikeERC6909() { const firstTokenId = 1n; const secondTokenId = 2n; const randomTokenId = 125523n; @@ -210,7 +209,3 @@ function shouldBehaveLikeERC6909() { shouldSupportInterfaces(['ERC6909']); }); } - -module.exports = { - shouldBehaveLikeERC6909, -}; diff --git a/test/token/ERC6909/ERC6909.test.js b/test/token/ERC6909/ERC6909.test.js index fa41145aa69..e757cc56e34 100644 --- a/test/token/ERC6909/ERC6909.test.js +++ b/test/token/ERC6909/ERC6909.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { shouldBehaveLikeERC6909 } = require('./ERC6909.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeERC6909 } from './ERC6909.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [holder, operator, recipient, other] = await ethers.getSigners(); diff --git a/test/token/ERC6909/extensions/ERC6909ContentURI.test.js b/test/token/ERC6909/extensions/ERC6909ContentURI.test.js index 72021a4d643..d7ea9cf1cf1 100644 --- a/test/token/ERC6909/extensions/ERC6909ContentURI.test.js +++ b/test/token/ERC6909/extensions/ERC6909ContentURI.test.js @@ -1,11 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const token = await ethers.deployContract('$ERC6909ContentURI'); - return { token }; + return { token: await ethers.deployContract('$ERC6909ContentURI') }; } describe('ERC6909ContentURI', function () { diff --git a/test/token/ERC6909/extensions/ERC6909Metadata.test.js b/test/token/ERC6909/extensions/ERC6909Metadata.test.js index 2fa2e153dc6..7dc2f7ec828 100644 --- a/test/token/ERC6909/extensions/ERC6909Metadata.test.js +++ b/test/token/ERC6909/extensions/ERC6909Metadata.test.js @@ -1,12 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; -const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const token = await ethers.deployContract('$ERC6909Metadata'); - return { token }; + return { token: await ethers.deployContract('$ERC6909Metadata') }; } describe('ERC6909Metadata', function () { diff --git a/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js b/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js index 9ccd5e5a6c3..3a16cad13b7 100644 --- a/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js +++ b/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { shouldBehaveLikeERC6909 } = require('../ERC6909.behavior'); -const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeERC6909 } from '../ERC6909.behavior'; +import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [holder, operator, recipient, other] = await ethers.getSigners(); diff --git a/test/token/ERC721/ERC721.behavior.js b/test/token/ERC721/ERC721.behavior.js index 63b4e19bece..f1048802658 100644 --- a/test/token/ERC721/ERC721.behavior.js +++ b/test/token/ERC721/ERC721.behavior.js @@ -1,10 +1,9 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); - -const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); -const { RevertType } = require('../../helpers/enums'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; +import { RevertType } from '../../helpers/enums'; +import { shouldSupportInterfaces } from '../../utils/introspection/SupportsInterface.behavior'; const firstTokenId = 5042n; const secondTokenId = 79217n; @@ -13,7 +12,7 @@ const fourthTokenId = 4n; const RECEIVER_MAGIC_VALUE = '0x150b7a02'; -function shouldBehaveLikeERC721() { +export function shouldBehaveLikeERC721() { beforeEach(async function () { const [owner, newOwner, approved, operator, other] = this.accounts; Object.assign(this, { owner, newOwner, approved, operator, other }); @@ -240,7 +239,7 @@ function shouldBehaveLikeERC721() { describe('to a valid receiver contract', function () { beforeEach(async function () { - this.to = await ethers.deployContract('ERC721ReceiverMock', [RECEIVER_MAGIC_VALUE, RevertType.None]); + this.to = await this.ethers.deployContract('ERC721ReceiverMock', [RECEIVER_MAGIC_VALUE, RevertType.None]); }); shouldTransferTokensByUsers(fragment, opts); @@ -297,7 +296,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract returning unexpected value', function () { it('reverts', async function () { - const invalidReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const invalidReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ '0xdeadbeef', RevertType.None, ]); @@ -310,7 +309,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that reverts with message', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.RevertWithMessage, ]); @@ -323,7 +322,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that reverts without message', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.RevertWithoutMessage, ]); @@ -336,7 +335,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that reverts with custom error', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.RevertWithCustomError, ]); @@ -349,7 +348,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that panics', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.Panic, ]); @@ -362,7 +361,7 @@ function shouldBehaveLikeERC721() { describe('to a contract that does not implement the required function', function () { it('reverts', async function () { - const nonReceiver = await ethers.deployContract('CallReceiverMock'); + const nonReceiver = await this.ethers.deployContract('CallReceiverMock'); await expect(this.token.connect(this.owner)[fnName](this.owner, nonReceiver, tokenId)) .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') @@ -380,7 +379,10 @@ function shouldBehaveLikeERC721() { describe('via safeMint', function () { // regular minting is tested in ERC721Mintable.test.js and others it('calls onERC721Received — with data', async function () { - const receiver = await ethers.deployContract('ERC721ReceiverMock', [RECEIVER_MAGIC_VALUE, RevertType.None]); + const receiver = await this.ethers.deployContract('ERC721ReceiverMock', [ + RECEIVER_MAGIC_VALUE, + RevertType.None, + ]); await expect(await this.token.$_safeMint(receiver, tokenId, ethers.Typed.bytes(data))) .to.emit(receiver, 'Received') @@ -388,7 +390,10 @@ function shouldBehaveLikeERC721() { }); it('calls onERC721Received — without data', async function () { - const receiver = await ethers.deployContract('ERC721ReceiverMock', [RECEIVER_MAGIC_VALUE, RevertType.None]); + const receiver = await this.ethers.deployContract('ERC721ReceiverMock', [ + RECEIVER_MAGIC_VALUE, + RevertType.None, + ]); await expect(await this.token.$_safeMint(receiver, tokenId)) .to.emit(receiver, 'Received') @@ -397,7 +402,10 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract returning unexpected value', function () { it('reverts', async function () { - const invalidReceiver = await ethers.deployContract('ERC721ReceiverMock', ['0xdeadbeef', RevertType.None]); + const invalidReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ + '0xdeadbeef', + RevertType.None, + ]); await expect(this.token.$_safeMint(invalidReceiver, tokenId)) .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') @@ -407,7 +415,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that reverts with message', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.RevertWithMessage, ]); @@ -420,7 +428,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that reverts without message', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.RevertWithoutMessage, ]); @@ -433,7 +441,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that reverts with custom error', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.RevertWithCustomError, ]); @@ -446,7 +454,7 @@ function shouldBehaveLikeERC721() { describe('to a receiver contract that panics', function () { it('reverts', async function () { - const revertingReceiver = await ethers.deployContract('ERC721ReceiverMock', [ + const revertingReceiver = await this.ethers.deployContract('ERC721ReceiverMock', [ RECEIVER_MAGIC_VALUE, RevertType.Panic, ]); @@ -459,7 +467,7 @@ function shouldBehaveLikeERC721() { describe('to a contract that does not implement the required function', function () { it('reverts', async function () { - const nonReceiver = await ethers.deployContract('CallReceiverMock'); + const nonReceiver = await this.ethers.deployContract('CallReceiverMock'); await expect(this.token.$_safeMint(nonReceiver, tokenId)) .to.be.revertedWithCustomError(this.token, 'ERC721InvalidReceiver') @@ -755,7 +763,7 @@ function shouldBehaveLikeERC721() { }); } -function shouldBehaveLikeERC721Enumerable() { +export function shouldBehaveLikeERC721Enumerable() { beforeEach(async function () { const [owner, newOwner, approved, operator, other] = this.accounts; Object.assign(this, { owner, newOwner, approved, operator, other }); @@ -917,7 +925,7 @@ function shouldBehaveLikeERC721Enumerable() { }); } -function shouldBehaveLikeERC721Metadata(name, symbol) { +export function shouldBehaveLikeERC721Metadata(name, symbol) { shouldSupportInterfaces(['ERC721Metadata']); describe('metadata', function () { @@ -946,9 +954,3 @@ function shouldBehaveLikeERC721Metadata(name, symbol) { }); }); } - -module.exports = { - shouldBehaveLikeERC721, - shouldBehaveLikeERC721Enumerable, - shouldBehaveLikeERC721Metadata, -}; diff --git a/test/token/ERC721/ERC721.test.js b/test/token/ERC721/ERC721.test.js index 1454cb057c6..b617241a2bb 100644 --- a/test/token/ERC721/ERC721.test.js +++ b/test/token/ERC721/ERC721.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { shouldBehaveLikeERC721, shouldBehaveLikeERC721Metadata } from './ERC721.behavior'; -const { shouldBehaveLikeERC721, shouldBehaveLikeERC721Metadata } = require('./ERC721.behavior'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'Non Fungible Token'; const symbol = 'NFT'; @@ -15,7 +19,7 @@ async function fixture() { describe('ERC721', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeERC721(); diff --git a/test/token/ERC721/ERC721Enumerable.test.js b/test/token/ERC721/ERC721Enumerable.test.js index a3bdea73f5a..9d6920ae671 100644 --- a/test/token/ERC721/ERC721Enumerable.test.js +++ b/test/token/ERC721/ERC721Enumerable.test.js @@ -1,11 +1,15 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { +import { network } from 'hardhat'; +import { shouldBehaveLikeERC721, shouldBehaveLikeERC721Metadata, shouldBehaveLikeERC721Enumerable, -} = require('./ERC721.behavior'); +} from './ERC721.behavior'; + +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'Non Fungible Token'; const symbol = 'NFT'; @@ -19,7 +23,7 @@ async function fixture() { describe('ERC721', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeERC721(); diff --git a/test/token/ERC721/extensions/ERC721Burnable.test.js b/test/token/ERC721/extensions/ERC721Burnable.test.js index d6f0b80c4f2..75798f3b197 100644 --- a/test/token/ERC721/extensions/ERC721Burnable.test.js +++ b/test/token/ERC721/extensions/ERC721Burnable.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Consecutive.test.js b/test/token/ERC721/extensions/ERC721Consecutive.test.js index 8d8a3037893..338e001f4ac 100644 --- a/test/token/ERC721/extensions/ERC721Consecutive.test.js +++ b/test/token/ERC721/extensions/ERC721Consecutive.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { sum } = require('../../../helpers/math'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { sum } from '../../../helpers/math'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Pausable.test.js b/test/token/ERC721/extensions/ERC721Pausable.test.js index acf731a4560..df77e958fae 100644 --- a/test/token/ERC721/extensions/ERC721Pausable.test.js +++ b/test/token/ERC721/extensions/ERC721Pausable.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Royalty.test.js b/test/token/ERC721/extensions/ERC721Royalty.test.js index e11954ae7a3..2f41d7f3de4 100644 --- a/test/token/ERC721/extensions/ERC721Royalty.test.js +++ b/test/token/ERC721/extensions/ERC721Royalty.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { shouldBehaveLikeERC2981 } = require('../../common/ERC2981.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeERC2981 } from '../../common/ERC2981.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721URIStorage.test.js b/test/token/ERC721/extensions/ERC721URIStorage.test.js index 2048a85de70..cb2bd6e576b 100644 --- a/test/token/ERC721/extensions/ERC721URIStorage.test.js +++ b/test/token/ERC721/extensions/ERC721URIStorage.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Votes.test.js b/test/token/ERC721/extensions/ERC721Votes.test.js index dcae1b8d2db..d146bfe2c9c 100644 --- a/test/token/ERC721/extensions/ERC721Votes.test.js +++ b/test/token/ERC721/extensions/ERC721Votes.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, mine } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeVotes } from '../../../governance/utils/Votes.behavior'; -const time = require('../../../helpers/time'); - -const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture, mine }, +} = connection; const TOKENS = [ { Token: '$ERC721Votes', mode: 'blocknumber' }, @@ -30,7 +33,7 @@ describe('ERC721Votes', function () { describe(`vote with ${mode}`, function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); this.votes = this.token; }); diff --git a/test/token/ERC721/extensions/ERC721Wrapper.test.js b/test/token/ERC721/extensions/ERC721Wrapper.test.js index eeead4c1f10..bf7f72a0f1f 100644 --- a/test/token/ERC721/extensions/ERC721Wrapper.test.js +++ b/test/token/ERC721/extensions/ERC721Wrapper.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeERC721 } from '../ERC721.behavior'; -const { shouldBehaveLikeERC721 } = require('../ERC721.behavior'); +const connection = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = connection; const name = 'Non Fungible Token'; const symbol = 'NFT'; @@ -23,7 +27,7 @@ async function fixture() { describe('ERC721Wrapper', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('has a name', async function () { diff --git a/test/token/ERC721/utils/ERC721Holder.test.js b/test/token/ERC721/utils/ERC721Holder.test.js index 31dd2fd20db..a706bae3c50 100644 --- a/test/token/ERC721/utils/ERC721Holder.test.js +++ b/test/token/ERC721/utils/ERC721Holder.test.js @@ -1,5 +1,7 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { ethers } = await network.connect(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/utils/ERC721Utils.test.js b/test/token/ERC721/utils/ERC721Utils.test.js index 2327d1ac7da..fd72d30c4a8 100644 --- a/test/token/ERC721/utils/ERC721Utils.test.js +++ b/test/token/ERC721/utils/ERC721Utils.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { RevertType } = require('../../../helpers/enums'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { RevertType } from '../../../helpers/enums'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const tokenId = 1n; @@ -36,19 +40,22 @@ describe('ERC721Utils', function () { describe('onERC721Received', function () { it('succeeds when called by an EOA', async function () { - await expect(this.utils.$checkOnERC721Received(this.operator, this.owner, this.receivers.eoa, tokenId, '0x')).to - .not.be.reverted; + await expect( + this.utils.$checkOnERC721Received(this.operator, this.owner, this.receivers.eoa, tokenId, '0x'), + ).to.not.be.revert(ethers); }); it('succeeds when data is passed', async function () { const data = '0x12345678'; - await expect(this.utils.$checkOnERC721Received(this.operator, this.owner, this.receivers.correct, tokenId, data)) - .to.not.be.reverted; + await expect( + this.utils.$checkOnERC721Received(this.operator, this.owner, this.receivers.correct, tokenId, data), + ).to.not.be.revert(ethers); }); it('succeeds when data is empty', async function () { - await expect(this.utils.$checkOnERC721Received(this.operator, this.owner, this.receivers.correct, tokenId, '0x')) - .to.not.be.reverted; + await expect( + this.utils.$checkOnERC721Received(this.operator, this.owner, this.receivers.correct, tokenId, '0x'), + ).to.not.be.revert(ethers); }); it('reverts when receiver returns invalid value', async function () { diff --git a/test/token/common/ERC2981.behavior.js b/test/token/common/ERC2981.behavior.js index ae6abccaedb..317a1a57afe 100644 --- a/test/token/common/ERC2981.behavior.js +++ b/test/token/common/ERC2981.behavior.js @@ -1,9 +1,8 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { shouldSupportInterfaces } from '../../utils/introspection/SupportsInterface.behavior'; -const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); - -function shouldBehaveLikeERC2981() { +export function shouldBehaveLikeERC2981() { const royaltyFraction = 10n; shouldSupportInterfaces(['ERC2981']); @@ -146,7 +145,3 @@ function shouldBehaveLikeERC2981() { }); }); } - -module.exports = { - shouldBehaveLikeERC2981, -}; diff --git a/test/utils/Address.test.js b/test/utils/Address.test.js index 2335c223722..3a1b139956b 100644 --- a/test/utils/Address.test.js +++ b/test/utils/Address.test.js @@ -1,9 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; -const coder = ethers.AbiCoder.defaultAbiCoder(); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const fakeContract = { interface: ethers.Interface.from(['error SomeCustomErrorWithoutArgs()']) }; const returndata = fakeContract.interface.encodeErrorResult('SomeCustomErrorWithoutArgs'); @@ -26,7 +28,7 @@ describe('Address', function () { describe('sendValue', function () { describe('when sender contract has no funds', function () { it('sends 0 wei', async function () { - await expect(this.mock.$sendValue(this.other, 0n)).to.changeEtherBalance(this.recipient, 0n); + await expect(this.mock.$sendValue(this.other, 0n)).to.changeEtherBalance(ethers, this.recipient, 0n); }); it('reverts when sending non-zero amounts', async function () { @@ -45,18 +47,23 @@ describe('Address', function () { describe('with EOA recipient', function () { it('sends 0 wei', async function () { - await expect(this.mock.$sendValue(this.recipient, 0n)).to.changeEtherBalance(this.recipient, 0n); + await expect(this.mock.$sendValue(this.recipient, 0n)).to.changeEtherBalance(ethers, this.recipient, 0n); }); it('sends non-zero amounts', async function () { await expect(this.mock.$sendValue(this.recipient, funds - 1n)).to.changeEtherBalance( + ethers, this.recipient, funds - 1n, ); }); it('sends the whole balance', async function () { - await expect(this.mock.$sendValue(this.recipient, funds)).to.changeEtherBalance(this.recipient, funds); + await expect(this.mock.$sendValue(this.recipient, funds)).to.changeEtherBalance( + ethers, + this.recipient, + funds, + ); expect(await ethers.provider.getBalance(this.mock)).to.equal(0n); }); @@ -70,7 +77,11 @@ describe('Address', function () { describe('with contract recipient', function () { it('sends funds', async function () { await this.targetEther.setAcceptEther(true); - await expect(this.mock.$sendValue(this.targetEther, funds)).to.changeEtherBalance(this.targetEther, funds); + await expect(this.mock.$sendValue(this.targetEther, funds)).to.changeEtherBalance( + ethers, + this.targetEther, + funds, + ); }); it('reverts on recipient revert', async function () { @@ -92,7 +103,7 @@ describe('Address', function () { await expect(this.mock.$functionCall(this.target, call)) .to.emit(this.target, 'MockFunctionCalled') .to.emit(this.mock, 'return$functionCall') - .withArgs(coder.encode(['string'], ['0x1234'])); + .withArgs(ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['0x1234'])); }); it('calls the requested empty return function', async function () { @@ -156,7 +167,7 @@ describe('Address', function () { await expect(this.mock.$functionCallWithValue(this.target, call, 0n)) .to.emit(this.target, 'MockFunctionCalled') .to.emit(this.mock, 'return$functionCallWithValue') - .withArgs(coder.encode(['string'], ['0x1234'])); + .withArgs(ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['0x1234'])); }); }); @@ -177,12 +188,12 @@ describe('Address', function () { const call = this.target.interface.encodeFunctionData('mockFunction'); const tx = await this.mock.$functionCallWithValue(this.target, call, value); - await expect(tx).to.changeEtherBalance(this.target, value); + await expect(tx).to.changeEtherBalance(ethers, this.target, value); await expect(tx) .to.emit(this.target, 'MockFunctionCalled') .to.emit(this.mock, 'return$functionCallWithValue') - .withArgs(coder.encode(['string'], ['0x1234'])); + .withArgs(ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['0x1234'])); }); it('calls the requested function with transaction funds', async function () { @@ -191,11 +202,11 @@ describe('Address', function () { const call = this.target.interface.encodeFunctionData('mockFunction'); const tx = await this.mock.connect(this.other).$functionCallWithValue(this.target, call, value, { value }); - await expect(tx).to.changeEtherBalance(this.target, value); + await expect(tx).to.changeEtherBalance(ethers, this.target, value); await expect(tx) .to.emit(this.target, 'MockFunctionCalled') .to.emit(this.mock, 'return$functionCallWithValue') - .withArgs(coder.encode(['string'], ['0x1234'])); + .withArgs(ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['0x1234'])); }); it('reverts when calling non-payable functions', async function () { @@ -215,7 +226,9 @@ describe('Address', function () { it('calls the requested function', async function () { const call = this.target.interface.encodeFunctionData('mockStaticFunction'); - expect(await this.mock.$functionStaticCall(this.target, call)).to.equal(coder.encode(['string'], ['0x1234'])); + expect(await this.mock.$functionStaticCall(this.target, call)).to.equal( + ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['0x1234']), + ); }); it('reverts on a non-static function', async function () { @@ -253,7 +266,7 @@ describe('Address', function () { await expect(await this.mock.$functionDelegateCall(this.target, call)) .to.emit(this.mock, 'return$functionDelegateCall') - .withArgs(coder.encode(['string'], ['0x1234'])); + .withArgs(ethers.AbiCoder.defaultAbiCoder().encode(['string'], ['0x1234'])); expect(await ethers.provider.getStorage(this.mock, slot)).to.equal(value); }); diff --git a/test/utils/Arrays.test.js b/test/utils/Arrays.test.js index 8a4bcb0162b..71b170ead35 100644 --- a/test/utils/Arrays.test.js +++ b/test/utils/Arrays.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { generators } from '../helpers/random'; +import { capitalize } from '../../scripts/helpers'; +import { TYPES } from '../../scripts/generate/templates/Arrays.opts'; -const { generators } = require('../helpers/random'); -const { capitalize } = require('../../scripts/helpers'); -const { TYPES } = require('../../scripts/generate/templates/Arrays.opts'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); // See https://en.cppreference.com/w/cpp/algorithm/lower_bound const lowerBound = (array, value) => { @@ -22,11 +25,11 @@ const bigintSign = x => (x > 0n ? 1 : x < 0n ? -1 : 0); const comparator = (a, b) => bigintSign(ethers.toBigInt(a) - ethers.toBigInt(b)); const hasDuplicates = array => array.some((v, i) => array.indexOf(v) != i); -describe('Arrays', function () { - const fixture = async () => { - return { mock: await ethers.deployContract('$Arrays') }; - }; +async function fixture() { + return { mock: await ethers.deployContract('$Arrays') }; +} +describe('Arrays', function () { beforeEach(async function () { Object.assign(this, await loadFixture(fixture)); }); diff --git a/test/utils/Base58.test.js b/test/utils/Base58.test.js index 87611ae3059..ba2d3e1d7d2 100644 --- a/test/utils/Base58.test.js +++ b/test/utils/Base58.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$Base58'); - return { mock }; + return { mock: await ethers.deployContract('$Base58') }; } describe('Base58', function () { diff --git a/test/utils/Base64.test.js b/test/utils/Base64.test.js index c4b7a7b5303..b3e916d9237 100644 --- a/test/utils/Base64.test.js +++ b/test/utils/Base64.test.js @@ -1,14 +1,17 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); // Replace "+/" with "-_" in the char table, and remove the padding // see https://datatracker.ietf.org/doc/html/rfc4648#section-5 const base64toBase64Url = str => str.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', ''); async function fixture() { - const mock = await ethers.deployContract('$Base64'); - return { mock }; + return { mock: await ethers.deployContract('$Base64') }; } describe('Base64', function () { diff --git a/test/utils/Blockhash.test.js b/test/utils/Blockhash.test.js index 7ec92262184..1fd1bcee18d 100644 --- a/test/utils/Blockhash.test.js +++ b/test/utils/Blockhash.test.js @@ -1,7 +1,11 @@ -const { ethers, predeploy } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, mineUpTo, setCode } = require('@nomicfoundation/hardhat-network-helpers'); -const { impersonate } = require('../helpers/account'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + helpers: { impersonate, time }, + networkHelpers: { loadFixture, setCode }, +} = await network.connect(); const SYSTEM_ADDRESS = '0xfffffffffffffffffffffffffffffffffffffffe'; const HISTORY_SERVE_WINDOW = 8191; @@ -24,21 +28,21 @@ describe('Blockhash', function () { describe(`${supported ? 'supported' : 'unsupported'} chain`, function () { beforeEach(async function () { if (supported) { - await this.systemSigner.sendTransaction({ to: predeploy.eip2935, data: this.latestBlock.hash }); + await this.systemSigner.sendTransaction({ to: ethers.predeploy.eip2935, data: this.latestBlock.hash }); } else { - await setCode(predeploy.eip2935.target, '0x'); + await setCode(ethers.predeploy.eip2935.target, '0x'); } }); it('recent block', async function () { // fast forward (less than blockhash serve window) - await mineUpTo(this.latestBlock.number + BLOCKHASH_SERVE_WINDOW); + await time.increaseTo.blocknumber(this.latestBlock.number + BLOCKHASH_SERVE_WINDOW); await expect(this.mock.$blockHash(this.latestBlock.number)).to.eventually.equal(this.latestBlock.hash); }); it('old block', async function () { // fast forward (more than blockhash serve window) - await mineUpTo(this.latestBlock.number + BLOCKHASH_SERVE_WINDOW + 1); + await time.increaseTo.blocknumber(this.latestBlock.number + BLOCKHASH_SERVE_WINDOW + 1); await expect(this.mock.$blockHash(this.latestBlock.number)).to.eventually.equal( supported ? this.latestBlock.hash : ethers.ZeroHash, ); @@ -46,7 +50,7 @@ describe('Blockhash', function () { it('very old block', async function () { // fast forward (more than history serve window) - await mineUpTo(this.latestBlock.number + HISTORY_SERVE_WINDOW + 10); + await time.increaseTo.blocknumber(this.latestBlock.number + HISTORY_SERVE_WINDOW + 10); await expect(this.mock.$blockHash(this.latestBlock.number)).to.eventually.equal(ethers.ZeroHash); }); diff --git a/test/utils/Bytes.test.js b/test/utils/Bytes.test.js index 9366c5d269f..7bb3bd28ecc 100644 --- a/test/utils/Bytes.test.js +++ b/test/utils/Bytes.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { MAX_UINT128, MAX_UINT64, MAX_UINT32, MAX_UINT16 } = require('../helpers/constants'); -const { generators } = require('../helpers/random'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT128, MAX_UINT64, MAX_UINT32, MAX_UINT16 } from '../helpers/constants'; +import { generators } from '../helpers/random'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); // Helper functions for fixed bytes types const bytes32 = value => ethers.toBeHex(value, 32); @@ -12,8 +16,7 @@ const bytes4 = value => ethers.toBeHex(value, 4); const bytes2 = value => ethers.toBeHex(value, 2); async function fixture() { - const mock = await ethers.deployContract('$Bytes'); - return { mock }; + return { mock: await ethers.deployContract('$Bytes') }; } const lorem = ethers.toUtf8Bytes( diff --git a/test/utils/CAIP.test.js b/test/utils/CAIP.test.js index bb0bae023c4..7eda8247a94 100644 --- a/test/utils/CAIP.test.js +++ b/test/utils/CAIP.test.js @@ -1,11 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { CHAINS, getLocalChain } from '../helpers/chains'; +import { generators } from '../helpers/random'; -const { CHAINS, getLocalChain } = require('../helpers/chains'); +const { ethers } = await network.connect(); describe('CAIP utilities', function () { before(async function () { - this.local = await getLocalChain(); + this.local = await getLocalChain(ethers.provider); }); describe('CAIP-2', function () { @@ -30,7 +32,7 @@ describe('CAIP utilities', function () { }); describe('CAIP-10', function () { - const { address: account } = ethers.Wallet.createRandom(); + const account = generators.address(); before(async function () { this.mock = await ethers.deployContract('$CAIP10'); diff --git a/test/utils/Calldata.test.js b/test/utils/Calldata.test.js index 7e9d3d47813..04376713827 100644 --- a/test/utils/Calldata.test.js +++ b/test/utils/Calldata.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$Calldata'); - return { mock }; + return { mock: await ethers.deployContract('$Calldata') }; } describe('Calldata utilities', function () { diff --git a/test/utils/Context.behavior.js b/test/utils/Context.behavior.js index adb140fc150..8c271ac3590 100644 --- a/test/utils/Context.behavior.js +++ b/test/utils/Context.behavior.js @@ -1,15 +1,6 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -async function fixture() { - return { contextHelper: await ethers.deployContract('ContextMockCaller', []) }; -} -function shouldBehaveLikeRegularContext() { - beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); - }); +import { expect } from 'chai'; +export async function shouldBehaveLikeRegularContext() { describe('msgSender', function () { it('returns the transaction sender when called from an EOA', async function () { await expect(this.context.connect(this.sender).msgSender()).to.emit(this.context, 'Sender').withArgs(this.sender); @@ -42,7 +33,3 @@ function shouldBehaveLikeRegularContext() { }); }); } - -module.exports = { - shouldBehaveLikeRegularContext, -}; diff --git a/test/utils/Context.test.js b/test/utils/Context.test.js index b766729e2f6..6f5d0f1d074 100644 --- a/test/utils/Context.test.js +++ b/test/utils/Context.test.js @@ -1,12 +1,16 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { shouldBehaveLikeRegularContext } from './Context.behavior'; -const { shouldBehaveLikeRegularContext } = require('./Context.behavior'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [sender] = await ethers.getSigners(); const context = await ethers.deployContract('ContextMock', []); - return { sender, context }; + const contextHelper = await ethers.deployContract('ContextMockCaller', []); + return { sender, context, contextHelper }; } describe('Context', function () { diff --git a/test/utils/Create2.test.js b/test/utils/Create2.test.js index 99c47a0e34a..6ac361cc993 100644 --- a/test/utils/Create2.test.js +++ b/test/utils/Create2.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); - -const { RevertType } = require('../helpers/enums'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { RevertType } from '../helpers/enums'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [deployer, other] = await ethers.getSigners(); diff --git a/test/utils/LowLevelCall.test.js b/test/utils/LowLevelCall.test.js index c0f06a45e1d..1edc756495d 100644 --- a/test/utils/LowLevelCall.test.js +++ b/test/utils/LowLevelCall.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const value = ethers.parseEther('1'); const returnValue1 = ethers.id('hello'); @@ -39,7 +43,7 @@ describe('LowLevelCall', function () { ethers.Typed.uint256(value), this.target.interface.encodeFunctionData('mockFunction'), ); - await expect(tx).to.changeEtherBalances([this.mock, this.target], [-value, value]); + await expect(tx).to.changeEtherBalances(ethers, [this.mock, this.target], [-value, value]); await expect(tx).to.emit(this.mock, 'return$callNoReturn_address_uint256_bytes').withArgs(true); }); @@ -49,7 +53,7 @@ describe('LowLevelCall', function () { ethers.Typed.uint256(value), this.target.interface.encodeFunctionData('mockFunction'), ); - await expect(tx).to.changeEtherBalances([this.mock, this.target], [0n, 0n]); + await expect(tx).to.changeEtherBalances(ethers, [this.mock, this.target], [0n, 0n]); await expect(tx).to.emit(this.mock, 'return$callNoReturn_address_uint256_bytes').withArgs(false); }); @@ -84,7 +88,7 @@ describe('LowLevelCall', function () { ethers.Typed.uint256(value), this.target.interface.encodeFunctionData('mockFunctionWithArgsReturn', [returnValue1, returnValue2]), ); - await expect(tx).to.changeEtherBalances([this.mock, this.target], [-value, value]); + await expect(tx).to.changeEtherBalances(ethers, [this.mock, this.target], [-value, value]); await expect(tx) .to.emit(this.mock, 'return$callReturn64Bytes_address_uint256_bytes') .withArgs(true, returnValue1, returnValue2); @@ -96,7 +100,7 @@ describe('LowLevelCall', function () { ethers.Typed.uint256(value), this.target.interface.encodeFunctionData('mockFunctionWithArgsReturn', [returnValue1, returnValue2]), ); - await expect(tx).to.changeEtherBalances([this.mock, this.target], [0n, 0n]); + await expect(tx).to.changeEtherBalances(ethers, [this.mock, this.target], [0n, 0n]); await expect(tx) .to.emit(this.mock, 'return$callReturn64Bytes_address_uint256_bytes') .withArgs(false, ethers.ZeroHash, ethers.ZeroHash); diff --git a/test/utils/Memory.test.js b/test/utils/Memory.test.js index 0a7a1949bf8..9f74223be6c 100644 --- a/test/utils/Memory.test.js +++ b/test/utils/Memory.test.js @@ -1,16 +1,18 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { generators } from '../helpers/random'; -const { generators } = require('../helpers/random'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const formatSlice = ({ length, ptr = 0xa0 }) => ethers.toBeHex((ethers.toBigInt(length) << 128n) | ethers.toBigInt(ptr), 32); async function fixture() { - const mock = await ethers.deployContract('$Memory'); - return { mock }; + return { mock: await ethers.deployContract('$Memory') }; } describe('Memory', function () { @@ -22,7 +24,7 @@ describe('Memory', function () { describe('free pointer', function () { it('sets free memory pointer', async function () { const ptr = ethers.toBeHex(0xa0, 32); - await expect(this.mock.$setFreeMemoryPointer(ptr)).to.not.be.reverted; + await expect(this.mock.$setFreeMemoryPointer(ptr)).to.not.be.revert(ethers); }); it('gets free memory pointer', async function () { diff --git a/test/utils/Multicall.test.js b/test/utils/Multicall.test.js index 9c84e443a4e..f231a3720b3 100644 --- a/test/utils/Multicall.test.js +++ b/test/utils/Multicall.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [holder, alice, bruce] = await ethers.getSigners(); diff --git a/test/utils/Nonces.behavior.js b/test/utils/Nonces.behavior.js index c62864ea2bc..3237b05b886 100644 --- a/test/utils/Nonces.behavior.js +++ b/test/utils/Nonces.behavior.js @@ -1,7 +1,7 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; -function shouldBehaveLikeNonces() { +export function shouldBehaveLikeNonces() { describe('should behave like Nonces', function () { const sender = ethers.Wallet.createRandom(); const other = ethers.Wallet.createRandom(); @@ -66,7 +66,7 @@ function shouldBehaveLikeNonces() { }); } -function shouldBehaveLikeNoncesKeyed() { +export function shouldBehaveLikeNoncesKeyed() { describe('should support nonces with keys', function () { const sender = ethers.Wallet.createRandom(); @@ -182,8 +182,3 @@ function shouldBehaveLikeNoncesKeyed() { }); }); } - -module.exports = { - shouldBehaveLikeNonces, - shouldBehaveLikeNoncesKeyed, -}; diff --git a/test/utils/Nonces.test.js b/test/utils/Nonces.test.js index 85aa7358a00..81863d14d5e 100644 --- a/test/utils/Nonces.test.js +++ b/test/utils/Nonces.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { shouldBehaveLikeNonces } = require('./Nonces.behavior'); +import { network } from 'hardhat'; +import { shouldBehaveLikeNonces } from './Nonces.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$Nonces'); - return { mock }; + return { mock: await ethers.deployContract('$Nonces') }; } describe('Nonces', function () { diff --git a/test/utils/NoncesKeyed.test.js b/test/utils/NoncesKeyed.test.js index c46948ee402..c73ce0a96c2 100644 --- a/test/utils/NoncesKeyed.test.js +++ b/test/utils/NoncesKeyed.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { shouldBehaveLikeNonces, shouldBehaveLikeNoncesKeyed } = require('./Nonces.behavior'); +import { network } from 'hardhat'; +import { shouldBehaveLikeNonces, shouldBehaveLikeNoncesKeyed } from './Nonces.behavior'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$NoncesKeyed'); - return { mock }; + return { mock: await ethers.deployContract('$NoncesKeyed') }; } describe('NoncesKeyed', function () { diff --git a/test/utils/Packing.test.js b/test/utils/Packing.test.js index dd36f45d774..9a245c8ea1a 100644 --- a/test/utils/Packing.test.js +++ b/test/utils/Packing.test.js @@ -1,13 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { product } from '../helpers/iterate'; +import { SIZES } from '../../scripts/generate/templates/Packing.opts'; -const { forceDeployCode } = require('../helpers/deploy'); -const { product } = require('../helpers/iterate'); -const { SIZES } = require('../../scripts/generate/templates/Packing.opts'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - return { mock: await forceDeployCode('$Packing') }; + return { mock: await ethers.deployContract('$Packing') }; } describe('Packing', function () { diff --git a/test/utils/Panic.test.js b/test/utils/Panic.test.js index 49673c751cc..4d3ca729323 100644 --- a/test/utils/Panic.test.js +++ b/test/utils/Panic.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { return { mock: await ethers.deployContract('$Panic') }; diff --git a/test/utils/Pausable.test.js b/test/utils/Pausable.test.js index 67d74a0d88c..0c64b51b48a 100644 --- a/test/utils/Pausable.test.js +++ b/test/utils/Pausable.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [pauser] = await ethers.getSigners(); diff --git a/test/utils/RLP.test.js b/test/utils/RLP.test.js index f0172eab05d..a418db7ddef 100644 --- a/test/utils/RLP.test.js +++ b/test/utils/RLP.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { MAX_UINT64 } = require('../helpers/constants'); -const { product } = require('../helpers/iterate'); -const { generators } = require('../helpers/random'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { MAX_UINT64 } from '../helpers/constants'; +import { product } from '../helpers/iterate'; +import { generators } from '../helpers/random'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const mock = await ethers.deployContract('$RLP'); diff --git a/test/utils/ReentrancyGuard.test.js b/test/utils/ReentrancyGuard.test.js index 4a157864998..3c6c8c4ed7b 100644 --- a/test/utils/ReentrancyGuard.test.js +++ b/test/utils/ReentrancyGuard.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); for (const variant of ['', 'Transient']) { describe(`Reentrancy${variant}Guard`, function () { diff --git a/test/utils/RelayedCall.test.js b/test/utils/RelayedCall.test.js index 39d16fcb1c1..3cc5ff9cdff 100644 --- a/test/utils/RelayedCall.test.js +++ b/test/utils/RelayedCall.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { impersonate } = require('../helpers/account'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + helpers: { impersonate }, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [admin, receiver, other] = await ethers.getSigners(); @@ -71,7 +75,7 @@ describe('RelayedCall', function () { ethers.Typed.overrides({ value }), ); - await expect(tx).to.changeEtherBalances([this.mock, this.relayer, this.receiver], [0n, 0n, value]); + await expect(tx).to.changeEtherBalances(ethers, [this.mock, this.relayer, this.receiver], [0n, 0n, value]); await expect(tx).to.emit(this.mock, 'return$relayCall_address_uint256_bytes').withArgs(true, '0x'); }); @@ -94,7 +98,7 @@ describe('RelayedCall', function () { // unauthorized caller await expect( this.other.sendTransaction({ to: this.relayer, data: '0x7859821024E633C5dC8a4FcF86fC52e7720Ce525' }), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); }); it('input format', async function () { @@ -107,15 +111,15 @@ describe('RelayedCall', function () { // 20 bytes (address + empty data) - OK await expect( mockAsWallet.sendTransaction({ to: this.relayer, data: '0x7859821024E633C5dC8a4FcF86fC52e7720Ce525' }), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); // 19 bytes (not enough for an address) - REVERT await expect( mockAsWallet.sendTransaction({ to: this.relayer, data: '0x7859821024E633C5dC8a4FcF86fC52e7720Ce5' }), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); // 0 bytes (not enough for an address) - REVERT - await expect(mockAsWallet.sendTransaction({ to: this.relayer, data: '0x' })).to.be.revertedWithoutReason(); + await expect(mockAsWallet.sendTransaction({ to: this.relayer, data: '0x' })).to.be.revertedWithoutReason(ethers); }); }); @@ -166,7 +170,7 @@ describe('RelayedCall', function () { ethers.Typed.overrides({ value }), ); - await expect(tx).to.changeEtherBalances([this.mock, this.relayer, this.receiver], [0n, 0n, value]); + await expect(tx).to.changeEtherBalances(ethers, [this.mock, this.relayer, this.receiver], [0n, 0n, value]); await expect(tx).to.emit(this.mock, 'return$relayCall_address_uint256_bytes_bytes32').withArgs(true, '0x'); }); @@ -190,7 +194,7 @@ describe('RelayedCall', function () { // unauthorized caller await expect( this.other.sendTransaction({ to: this.relayer, data: '0x7859821024E633C5dC8a4FcF86fC52e7720Ce525' }), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); }); it('input format', async function () { @@ -203,15 +207,15 @@ describe('RelayedCall', function () { // 20 bytes (address + empty data) - OK await expect( mockAsWallet.sendTransaction({ to: this.relayer, data: '0x7859821024E633C5dC8a4FcF86fC52e7720Ce525' }), - ).to.not.be.reverted; + ).to.not.be.revert(ethers); // 19 bytes (not enough for an address) - REVERT await expect( mockAsWallet.sendTransaction({ to: this.relayer, data: '0x7859821024E633C5dC8a4FcF86fC52e7720Ce5' }), - ).to.be.revertedWithoutReason(); + ).to.be.revertedWithoutReason(ethers); // 0 bytes (not enough for an address) - REVERT - await expect(mockAsWallet.sendTransaction({ to: this.relayer, data: '0x' })).to.be.revertedWithoutReason(); + await expect(mockAsWallet.sendTransaction({ to: this.relayer, data: '0x' })).to.be.revertedWithoutReason(ethers); }); }); }); diff --git a/test/utils/ShortStrings.test.js b/test/utils/ShortStrings.test.js index cb1a06aa5c7..053185672aa 100644 --- a/test/utils/ShortStrings.test.js +++ b/test/utils/ShortStrings.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const FALLBACK_SENTINEL = ethers.zeroPadValue('0xFF', 32); diff --git a/test/utils/SlotDerivation.test.js b/test/utils/SlotDerivation.test.js index 22582b37572..519d3587bfb 100644 --- a/test/utils/SlotDerivation.test.js +++ b/test/utils/SlotDerivation.test.js @@ -1,8 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { erc7201Slot } = require('../helpers/storage'); -const { generators } = require('../helpers/random'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { erc7201Slot } from '../helpers/storage'; +import { generators } from '../helpers/random'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const [account] = await ethers.getSigners(); diff --git a/test/utils/StorageSlot.test.js b/test/utils/StorageSlot.test.js index ddcf305d1a7..4525286e650 100644 --- a/test/utils/StorageSlot.test.js +++ b/test/utils/StorageSlot.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { generators } = require('../helpers/random'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { generators } from '../helpers/random'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const slot = ethers.id('some.storage.slot'); const otherSlot = ethers.id('some.other.storage.slot'); diff --git a/test/utils/Strings.test.js b/test/utils/Strings.test.js index 751d6534ea9..8f6b5a33f66 100644 --- a/test/utils/Strings.test.js +++ b/test/utils/Strings.test.js @@ -1,11 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$Strings'); - return { mock }; + return { mock: await ethers.deployContract('$Strings') }; } describe('Strings', function () { diff --git a/test/utils/TransientSlot.test.js b/test/utils/TransientSlot.test.js index 7b70be375d4..a192be4fe26 100644 --- a/test/utils/TransientSlot.test.js +++ b/test/utils/TransientSlot.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { generators } = require('../helpers/random'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { generators } from '../helpers/random'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const slot = ethers.id('some.storage.slot'); const otherSlot = ethers.id('some.other.storage.slot'); diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index f6f7bec4d48..01794068eaf 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { secp256k1 } = require('@noble/curves/secp256k1'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { secp256k1 } from '@noble/curves/secp256k1'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const TEST_MESSAGE = ethers.id('OpenZeppelin'); const WRONG_MESSAGE = ethers.id('Nope'); diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js index 2b6e7fa9787..ab28c4e1b24 100644 --- a/test/utils/cryptography/EIP712.test.js +++ b/test/utils/cryptography/EIP712.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { getDomain, domainSeparator, hashTypedData } from '../../helpers/eip712'; +import { formatType } from '../../helpers/eip712-types'; -const { getDomain, domainSeparator, hashTypedData } = require('../../helpers/eip712'); -const { formatType } = require('../../helpers/eip712-types'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const LENGTHS = { short: ['A Name', '1'], @@ -90,7 +93,9 @@ describe('EIP712', function () { const signature = await this.from.signTypedData(this.domain, types, message); - await expect(this.eip712.verify(signature, this.from.address, message.to, message.contents)).to.not.be.reverted; + await expect(this.eip712.verify(signature, this.from.address, message.to, message.contents)).to.not.be.revert( + ethers, + ); }); it('name', async function () { diff --git a/test/utils/cryptography/ERC1271.behavior.js b/test/utils/cryptography/ERC1271.behavior.js index ef3e668028e..e2231de97d0 100644 --- a/test/utils/cryptography/ERC1271.behavior.js +++ b/test/utils/cryptography/ERC1271.behavior.js @@ -1,16 +1,16 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { Permit, formatType, getDomain } = require('../../helpers/eip712'); -const { ERC7739Signer } = require('../../helpers/erc7739'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { Permit, formatType, getDomain } from '../../helpers/eip712'; +import { ERC7739Signer } from '../../helpers/erc7739'; -function shouldBehaveLikeERC1271({ erc7739 = false } = {}) { +export function shouldBehaveLikeERC1271({ erc7739 = false } = {}) { const MAGIC_VALUE = '0x1626ba7e'; describe(`supports ERC-${erc7739 ? 7739 : 1271}`, function () { beforeEach(async function () { // if deploy function is present, check that code is already in place if (this.mock.deploy) { - await ethers.provider.getCode(this.mock.address).then(code => code != '0x' || this.mock.deploy()); + await this.mock.runner.provider.getCode(this.mock.address).then(code => code != '0x' || this.mock.deploy()); } this._signer = erc7739 ? new ERC7739Signer(this.signer, this.domain ?? (await getDomain(this.mock))) @@ -45,7 +45,7 @@ function shouldBehaveLikeERC1271({ erc7739 = false } = {}) { this.appDomain = { name: 'SomeApp', version: '1', - chainId: await ethers.provider.getNetwork().then(({ chainId }) => chainId), + chainId: await this.mock.runner.provider.getNetwork().then(({ chainId }) => chainId), verifyingContract: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512', salt: '0x02cb3d8cb5e8928c9c6de41e935e16a4e28b2d54e7e7ba47e99f16071efab785', }; @@ -105,7 +105,3 @@ function shouldBehaveLikeERC1271({ erc7739 = false } = {}) { }); }); } - -module.exports = { - shouldBehaveLikeERC1271, -}; diff --git a/test/utils/cryptography/ERC7739.test.js b/test/utils/cryptography/ERC7739.test.js index 8dc6a2b42b8..8252008bc65 100644 --- a/test/utils/cryptography/ERC7739.test.js +++ b/test/utils/cryptography/ERC7739.test.js @@ -1,6 +1,8 @@ -const { ethers } = require('hardhat'); -const { shouldBehaveLikeERC1271 } = require('./ERC1271.behavior'); -const { NonNativeSigner, P256SigningKey, RSASHA256SigningKey } = require('../../helpers/signers'); +import { network } from 'hardhat'; +import { shouldBehaveLikeERC1271 } from './ERC1271.behavior'; +import { NonNativeSigner, P256SigningKey, RSASHA256SigningKey } from '../../helpers/signers'; + +const { ethers } = await network.connect(); describe('ERC7739', function () { describe('for an ECDSA signer', function () { diff --git a/test/utils/cryptography/ERC7739Utils.test.js b/test/utils/cryptography/ERC7739Utils.test.js index 93e382df611..43fd71718ac 100644 --- a/test/utils/cryptography/ERC7739Utils.test.js +++ b/test/utils/cryptography/ERC7739Utils.test.js @@ -1,9 +1,12 @@ -const { expect } = require('chai'); -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const { Permit } = require('../../helpers/eip712'); -const { ERC4337Utils, PersonalSign } = require('../../helpers/erc7739'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { Permit } from '../../helpers/eip712'; +import { ERC4337Utils, PersonalSign } from '../../helpers/erc7739'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const details = ERC4337Utils.getContentsDetail({ Permit }); diff --git a/test/utils/cryptography/MerkleProof.test.js b/test/utils/cryptography/MerkleProof.test.js index fd480dd9374..0617801f082 100644 --- a/test/utils/cryptography/MerkleProof.test.js +++ b/test/utils/cryptography/MerkleProof.test.js @@ -1,7 +1,9 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); -const { SimpleMerkleTree } = require('@openzeppelin/merkle-tree'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { SimpleMerkleTree } from '@openzeppelin/merkle-tree'; + +const { ethers } = await network.connect(); // generate bytes32 leaves from a string const toLeaves = (str, separator = '') => str.split(separator).map(e => ethers.keccak256(ethers.toUtf8Bytes(e))); diff --git a/test/utils/cryptography/MessageHashUtils.test.js b/test/utils/cryptography/MessageHashUtils.test.js index 7f18b51c929..2c36f93c514 100644 --- a/test/utils/cryptography/MessageHashUtils.test.js +++ b/test/utils/cryptography/MessageHashUtils.test.js @@ -1,13 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { domainType, domainSeparator, hashTypedData } from '../../helpers/eip712'; +import { generators } from '../../helpers/random'; -const { domainType, domainSeparator, hashTypedData } = require('../../helpers/eip712'); -const { generators } = require('../../helpers/random'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$MessageHashUtils'); - return { mock }; + return { mock: await ethers.deployContract('$MessageHashUtils') }; } describe('MessageHashUtils', function () { diff --git a/test/utils/cryptography/P256.test.js b/test/utils/cryptography/P256.test.js index a75d527be37..40e62a589c1 100644 --- a/test/utils/cryptography/P256.test.js +++ b/test/utils/cryptography/P256.test.js @@ -1,7 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { secp256r1 } = require('@noble/curves/p256'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import fs from 'fs'; +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { secp256r1 } from '@noble/curves/p256'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const N = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n; @@ -29,11 +34,11 @@ const prepareSignature = ( return { privateKey, publicKey, signature, recovery, messageHash }; }; -describe('P256', function () { - async function fixture() { - return { mock: await ethers.deployContract('$P256') }; - } +async function fixture() { + return { mock: await ethers.deployContract('$P256') }; +} +describe('P256', function () { beforeEach(async function () { Object.assign(this, await loadFixture(fixture)); }); @@ -148,7 +153,11 @@ describe('P256', function () { // test cases for https://github.com/C2SP/wycheproof/blob/4672ff74d68766e7785c2cac4c597effccef2c5c/testvectors/ecdsa_secp256r1_sha256_p1363_test.json describe('wycheproof tests', function () { - for (const { key, tests } of require('./ecdsa_secp256r1_sha256_p1363_test.json').testGroups) { + const { testGroups } = JSON.parse( + fs.readFileSync('./test/utils/cryptography/ecdsa_secp256r1_sha256_p1363_test.json', 'utf8'), + ); + + for (const { key, tests } of testGroups) { // parse public key let [x, y] = [key.wx, key.wy].map(v => ethers.stripZerosLeft('0x' + v, 32)); if (x.length > 66 || y.length > 66) continue; diff --git a/test/utils/cryptography/RSA.helper.js b/test/utils/cryptography/RSA.helper.js index 48c8ee43cb2..cf14c1568d0 100644 --- a/test/utils/cryptography/RSA.helper.js +++ b/test/utils/cryptography/RSA.helper.js @@ -1,9 +1,8 @@ -const path = require('path'); -const fs = require('fs'); +import fs from 'fs'; -module.exports = function* parse(file) { +export function* parse(file) { const cache = {}; - const data = fs.readFileSync(path.resolve(__dirname, file), 'utf8'); + const data = fs.readFileSync(file, 'utf8'); for (const line of data.split('\r\n')) { const groups = line.match(/^(?\w+) = (?\w+)(?.*)$/)?.groups; if (groups) { @@ -14,4 +13,4 @@ module.exports = function* parse(file) { } } } -}; +} diff --git a/test/utils/cryptography/RSA.test.js b/test/utils/cryptography/RSA.test.js index 7ddf4d3a238..b3de596abc5 100644 --- a/test/utils/cryptography/RSA.test.js +++ b/test/utils/cryptography/RSA.test.js @@ -1,9 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { bytes, bytes32 } = ethers.Typed; +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { parse } from './RSA.helper'; -const parse = require('./RSA.helper'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { return { mock: await ethers.deployContract('$RSA') }; @@ -17,7 +19,7 @@ describe('RSA', function () { // Load test cases from file SigVer15_186-3.rsp from: // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/dss/186-2rsatestvectors.zip describe('SigVer15_186-3.rsp tests', function () { - for (const test of parse('SigVer15_186-3.rsp')) { + for (const test of parse('./test/utils/cryptography/SigVer15_186-3.rsp')) { const { length } = Buffer.from(test.S, 'hex'); /// For now, RSA only supports digest that are 32bytes long. If we ever extend that, we can use these hashing functions for @noble: @@ -34,8 +36,10 @@ describe('RSA', function () { const exp = ethers.stripZerosLeft('0x' + test.e); // strip zeros to reduce gas cost of the precompile const mod = '0x' + test.n; - expect(await this.mock.$pkcs1Sha256(bytes32(ethers.sha256(data)), sig, exp, mod)).to.equal(result); - expect(await this.mock.$pkcs1Sha256(bytes(data), sig, exp, mod)).to.equal(result); + expect(await this.mock.$pkcs1Sha256(ethers.Typed.bytes32(ethers.sha256(data)), sig, exp, mod)).to.equal( + result, + ); + expect(await this.mock.$pkcs1Sha256(ethers.Typed.bytes(data), sig, exp, mod)).to.equal(result); }); } } @@ -95,7 +99,7 @@ describe('RSA', function () { for (const { descr, data, sig, exp, mod, result } of [openssl, rfc4055, shortN, differentLength, sTooLarge]) { it(descr, async function () { - expect(await this.mock.$pkcs1Sha256(bytes(data), sig, exp, mod)).to.equal(result); + expect(await this.mock.$pkcs1Sha256(ethers.Typed.bytes(data), sig, exp, mod)).to.equal(result); }); } }); diff --git a/test/utils/cryptography/SignatureChecker.test.js b/test/utils/cryptography/SignatureChecker.test.js index 99b4f694adc..90e1e5b44b0 100644 --- a/test/utils/cryptography/SignatureChecker.test.js +++ b/test/utils/cryptography/SignatureChecker.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -const precompile = require('../../helpers/precompiles'); -const { P256SigningKey, NonNativeSigner } = require('../../helpers/signers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import * as precompile from '../../helpers/precompiles'; +import { P256SigningKey, NonNativeSigner } from '../../helpers/signers'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const TEST_MESSAGE = ethers.id('OpenZeppelin'); const TEST_MESSAGE_HASH = ethers.hashMessage(TEST_MESSAGE); diff --git a/test/utils/cryptography/TrieProof.test.js b/test/utils/cryptography/TrieProof.test.js index 98fc3aabb6f..e1b28a5c398 100644 --- a/test/utils/cryptography/TrieProof.test.js +++ b/test/utils/cryptography/TrieProof.test.js @@ -1,12 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { spawn } = require('child_process'); - -const { Enum } = require('../../helpers/enums'); -const { zip } = require('../../helpers/iterate'); -const { generators } = require('../../helpers/random'); -const { BlockTries } = require('../../helpers/trie'); -const { batchInBlock } = require('../../helpers/txpool'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { spawn } from 'child_process'; +import { Enum } from '../../helpers/enums'; +import { zip } from '../../helpers/iterate'; +import { generators } from '../../helpers/random'; +import { BlockTries } from '../../helpers/trie'; +import { batchInBlock } from '../../helpers/txpool'; + +const { ethers } = await network.connect(); const ProofError = Enum( 'NO_ERROR', // No error occurred during proof traversal diff --git a/test/utils/draft-InteroperableAddress.test.js b/test/utils/draft-InteroperableAddress.test.js index f7d5a1d9bf6..6a463f2dc6e 100644 --- a/test/utils/draft-InteroperableAddress.test.js +++ b/test/utils/draft-InteroperableAddress.test.js @@ -1,14 +1,16 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { addressCoder, nameCoder } = require('interoperable-addresses'); -const { CAIP350, chainTypeCoder } = require('interoperable-addresses/dist/CAIP350'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { addressCoder, nameCoder } from 'interoperable-addresses'; +import { CAIP350, chainTypeCoder } from 'interoperable-addresses/dist/CAIP350'; -const { getLocalChain } = require('../helpers/chains'); +const { + ethers, + helpers: { chain }, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$InteroperableAddress'); - return { mock }; + return { mock: await ethers.deployContract('$InteroperableAddress') }; } describe('ERC7390', function () { @@ -17,10 +19,9 @@ describe('ERC7390', function () { }); it('formatEvmV1 address on the local chain', async function () { - const { reference: chainid, toErc7930 } = await getLocalChain(); await expect( - this.mock.$formatEvmV1(ethers.Typed.uint256(chainid), ethers.Typed.address(this.mock)), - ).to.eventually.equal(toErc7930(this.mock)); + this.mock.$formatEvmV1(ethers.Typed.uint256(chain.reference), ethers.Typed.address(this.mock)), + ).to.eventually.equal(chain.toErc7930(this.mock)); }); it('formatV1 fails if both reference and address are empty', async function () { diff --git a/test/utils/introspection/ERC165.test.js b/test/utils/introspection/ERC165.test.js index 8117c695ebd..96842e45331 100644 --- a/test/utils/introspection/ERC165.test.js +++ b/test/utils/introspection/ERC165.test.js @@ -1,12 +1,13 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { shouldSupportInterfaces } from './SupportsInterface.behavior'; -const { shouldSupportInterfaces } = require('./SupportsInterface.behavior'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - return { - mock: await ethers.deployContract('$ERC165'), - }; + return { mock: await ethers.deployContract('$ERC165') }; } describe('ERC165', function () { diff --git a/test/utils/introspection/ERC165Checker.test.js b/test/utils/introspection/ERC165Checker.test.js index 7954b3d7621..a1dcd204465 100644 --- a/test/utils/introspection/ERC165Checker.test.js +++ b/test/utils/introspection/ERC165Checker.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const DUMMY_ID = '0xdeadbeef'; const DUMMY_ID_2 = '0xcafebabe'; diff --git a/test/utils/introspection/SupportsInterface.behavior.js b/test/utils/introspection/SupportsInterface.behavior.js index d40dc8b2e7b..866d0492c29 100644 --- a/test/utils/introspection/SupportsInterface.behavior.js +++ b/test/utils/introspection/SupportsInterface.behavior.js @@ -1,8 +1,7 @@ -const { expect } = require('chai'); -const { interfaceId } = require('../../helpers/methods'); -const { mapValues } = require('../../helpers/iterate'); +import { expect } from 'chai'; +import { interfaceId } from '../../helpers/methods'; +import { mapValues } from '../../helpers/iterate'; -const INVALID_ID = '0xffffffff'; const GOVERNOR_INTERFACE = [ 'name()', 'version()', @@ -31,7 +30,8 @@ const GOVERNOR_INTERFACE = [ 'castVoteBySig(uint256,uint8,address,bytes)', 'castVoteWithReasonAndParamsBySig(uint256,uint8,address,string,bytes,bytes)', ]; -const SIGNATURES = { + +export const SIGNATURES = { ERC165: ['supportsInterface(bytes4)'], ERC721: [ 'balanceOf(address)', @@ -104,9 +104,10 @@ const SIGNATURES = { ERC6909ContentURI: ['contractURI()', 'tokenURI(uint256)'], }; -const INTERFACE_IDS = mapValues(SIGNATURES, interfaceId); +export const INVALID_ID = '0xffffffff'; +export const INTERFACE_IDS = mapValues(SIGNATURES, interfaceId); -function shouldSupportInterfaces(interfaces = [], signatures = SIGNATURES) { +export function shouldSupportInterfaces(interfaces = [], signatures = SIGNATURES) { // case where only signatures are provided if (!Array.isArray(interfaces)) { signatures = interfaces; @@ -161,9 +162,3 @@ function shouldSupportInterfaces(interfaces = [], signatures = SIGNATURES) { }); }); } - -module.exports = { - SIGNATURES, - INTERFACE_IDS, - shouldSupportInterfaces, -}; diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index 507a3e9cec0..99ed847ecdb 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -1,12 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); - -const { Rounding } = require('../../helpers/enums'); -const { min, max, modExp } = require('../../helpers/math'); -const { generators } = require('../../helpers/random'); -const { product, range } = require('../../helpers/iterate'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { Rounding } from '../../helpers/enums'; +import { min, max, modExp } from '../../helpers/math'; +import { generators } from '../../helpers/random'; +import { product, range } from '../../helpers/iterate'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const RoundingDown = [Rounding.Floor, Rounding.Trunc]; const RoundingUp = [Rounding.Ceil, Rounding.Expand]; diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index ab62406ce45..487082daae2 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -1,12 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { range } from '../../helpers/iterate'; -const { range } = require('../../helpers/iterate'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$SafeCast'); - return { mock }; + return { mock: await ethers.deployContract('$SafeCast') }; } describe('SafeCast', function () { diff --git a/test/utils/math/SignedMath.test.js b/test/utils/math/SignedMath.test.js index 877f3b480c4..8e6454baead 100644 --- a/test/utils/math/SignedMath.test.js +++ b/test/utils/math/SignedMath.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { min, max } from '../../helpers/math'; -const { min, max } = require('../../helpers/math'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function testCommutative(fn, lhs, rhs, expected, ...extra) { expect(await fn(lhs, rhs, ...extra)).to.deep.equal(expected); @@ -10,8 +13,7 @@ async function testCommutative(fn, lhs, rhs, expected, ...extra) { } async function fixture() { - const mock = await ethers.deployContract('$SignedMath'); - return { mock }; + return { mock: await ethers.deployContract('$SignedMath') }; } describe('SignedMath', function () { diff --git a/test/utils/structs/BitMap.test.js b/test/utils/structs/BitMap.test.js index 5662ab13f88..830383fd96d 100644 --- a/test/utils/structs/BitMap.test.js +++ b/test/utils/structs/BitMap.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const bitmap = await ethers.deployContract('$BitMaps'); - return { bitmap }; + return { bitmap: await ethers.deployContract('$BitMaps') }; } describe('BitMap', function () { diff --git a/test/utils/structs/Checkpoints.test.js b/test/utils/structs/Checkpoints.test.js index fe055a77805..b83085fc52d 100644 --- a/test/utils/structs/Checkpoints.test.js +++ b/test/utils/structs/Checkpoints.test.js @@ -1,8 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { OPTS } from '../../../scripts/generate/templates/Checkpoints.opts'; -const { OPTS } = require('../../../scripts/generate/templates/Checkpoints.opts'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); describe('Checkpoints', function () { for (const opt of OPTS) { @@ -32,7 +35,7 @@ describe('Checkpoints', function () { describe('without checkpoints', function () { it('at zero reverts', async function () { // Reverts with array out of bound access, which is unspecified - await expect(this.methods.at(0)).to.be.reverted; + await expect(this.methods.at(0)).to.be.revert(ethers); }); it('returns zero as latest value', async function () { diff --git a/test/utils/structs/CircularBuffer.test.js b/test/utils/structs/CircularBuffer.test.js index e79ba6923b4..970c187fff6 100644 --- a/test/utils/structs/CircularBuffer.test.js +++ b/test/utils/structs/CircularBuffer.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); - -const { generators } = require('../../helpers/random'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { generators } from '../../helpers/random'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const LENGTH = 4; diff --git a/test/utils/structs/DoubleEndedQueue.test.js b/test/utils/structs/DoubleEndedQueue.test.js index 6f8235ccdd3..ef16c8afc8f 100644 --- a/test/utils/structs/DoubleEndedQueue.test.js +++ b/test/utils/structs/DoubleEndedQueue.test.js @@ -1,7 +1,11 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { const mock = await ethers.deployContract('$DoubleEndedQueue'); diff --git a/test/utils/structs/EnumerableMap.behavior.js b/test/utils/structs/EnumerableMap.behavior.js index 4074b077973..daf7e0d8ab1 100644 --- a/test/utils/structs/EnumerableMap.behavior.js +++ b/test/utils/structs/EnumerableMap.behavior.js @@ -1,9 +1,8 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { zip } from '../../helpers/iterate'; -const zip = (array1, array2) => array1.map((item, index) => [item, array2[index]]); - -function shouldBehaveLikeMap() { +export function shouldBehaveLikeMap() { async function expectMembersMatch(methods, keys, values) { expect(keys.length).to.equal(values.length); expect(await methods.length()).to.equal(keys.length); @@ -208,7 +207,3 @@ function shouldBehaveLikeMap() { } }); } - -module.exports = { - shouldBehaveLikeMap, -}; diff --git a/test/utils/structs/EnumerableMap.test.js b/test/utils/structs/EnumerableMap.test.js index 567c82dec0c..d511c0be347 100644 --- a/test/utils/structs/EnumerableMap.test.js +++ b/test/utils/structs/EnumerableMap.test.js @@ -1,11 +1,13 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { mapValues } from '../../helpers/iterate'; +import { generators } from '../../helpers/random'; +import { MAP_TYPES, typeDescr, toMapTypeDescr } from '../../../scripts/generate/templates/Enumerable.opts'; +import { shouldBehaveLikeMap } from './EnumerableMap.behavior'; -const { mapValues } = require('../../helpers/iterate'); -const { generators } = require('../../helpers/random'); -const { MAP_TYPES, typeDescr, toMapTypeDescr } = require('../../../scripts/generate/templates/Enumerable.opts'); - -const { shouldBehaveLikeMap } = require('./EnumerableMap.behavior'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); // Add Bytes32ToBytes32Map that must be tested but is not part of the generated types. MAP_TYPES.unshift(toMapTypeDescr({ key: typeDescr({ type: 'bytes32' }), value: typeDescr({ type: 'bytes32' }) })); diff --git a/test/utils/structs/EnumerableSet.behavior.js b/test/utils/structs/EnumerableSet.behavior.js index 286563b2262..4f40a4a953b 100644 --- a/test/utils/structs/EnumerableSet.behavior.js +++ b/test/utils/structs/EnumerableSet.behavior.js @@ -1,7 +1,7 @@ -const { expect } = require('chai'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; -function shouldBehaveLikeSet() { +export function shouldBehaveLikeSet() { async function expectMembersMatch(methods, values) { expect(await methods.length()).to.equal(values.length); for (const value of values) expect(await methods.contains(value)).to.be.true; @@ -169,7 +169,3 @@ function shouldBehaveLikeSet() { } }); } - -module.exports = { - shouldBehaveLikeSet, -}; diff --git a/test/utils/structs/EnumerableSet.test.js b/test/utils/structs/EnumerableSet.test.js index 135bdf5084d..6fcdff4396c 100644 --- a/test/utils/structs/EnumerableSet.test.js +++ b/test/utils/structs/EnumerableSet.test.js @@ -1,11 +1,13 @@ -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { mapValues } from '../../helpers/iterate'; +import { generators } from '../../helpers/random'; +import { SET_TYPES } from '../../../scripts/generate/templates/Enumerable.opts'; +import { shouldBehaveLikeSet } from './EnumerableSet.behavior'; -const { mapValues } = require('../../helpers/iterate'); -const { generators } = require('../../helpers/random'); -const { SET_TYPES } = require('../../../scripts/generate/templates/Enumerable.opts'); - -const { shouldBehaveLikeSet } = require('./EnumerableSet.behavior'); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const getMethods = (mock, fnSigs) => mapValues( diff --git a/test/utils/structs/Heap.test.js b/test/utils/structs/Heap.test.js index 0c6581d68fc..969a488e32d 100644 --- a/test/utils/structs/Heap.test.js +++ b/test/utils/structs/Heap.test.js @@ -1,11 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); async function fixture() { - const mock = await ethers.deployContract('$Heap'); - return { mock }; + return { mock: await ethers.deployContract('$Heap') }; } describe('Heap', function () { diff --git a/test/utils/structs/MerkleTree.test.js b/test/utils/structs/MerkleTree.test.js index f0380ed023d..0f0534125b4 100644 --- a/test/utils/structs/MerkleTree.test.js +++ b/test/utils/structs/MerkleTree.test.js @@ -1,11 +1,14 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); -const { StandardMerkleTree } = require('@openzeppelin/merkle-tree'); - -const { generators } = require('../../helpers/random'); -const { range } = require('../../helpers/iterate'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { StandardMerkleTree } from '@openzeppelin/merkle-tree'; +import { generators } from '../../helpers/random'; +import { range } from '../../helpers/iterate'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const DEPTH = 4; // 16 slots diff --git a/test/utils/types/Time.test.js b/test/utils/types/Time.test.js index 17baacfd110..eeb14d8f7eb 100644 --- a/test/utils/types/Time.test.js +++ b/test/utils/types/Time.test.js @@ -1,10 +1,13 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { product } from '../../helpers/iterate'; +import { max } from '../../helpers/math'; -const { product } = require('../../helpers/iterate'); -const { max } = require('../../helpers/math'); -const time = require('../../helpers/time'); +const { + ethers, + helpers: { time }, + networkHelpers: { loadFixture }, +} = await network.connect(); const MAX_UINT32 = (1n << 32n) - 1n; const MAX_UINT48 = (1n << 48n) - 1n; @@ -37,8 +40,7 @@ const effectSamplesForTimepoint = timepoint => [ ]; async function fixture() { - const mock = await ethers.deployContract('$Time'); - return { mock }; + return { mock: await ethers.deployContract('$Time') }; } describe('Time', function () { From a03c738dfe9cc6382f2577860214d7f21261d757 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 27 Jan 2026 20:42:17 +0100 Subject: [PATCH 06/91] test network env --- hardhat.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index eef5d86fbe7..bfac09f753a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -74,7 +74,7 @@ export default defineConfig({ hardfork: argv.evm, // Exposed contracts often exceed the maximum contract size. For normal contract, // we rely on the `code-size` compiler warning, that will cause a compilation error. - // allowUnlimitedContractSize: true, + allowUnlimitedContractSize: true, // initialBaseFeePerGas: argv.coverage ? 0 : undefined, }, }, From 319dd4152ce009b0ebe2af86c24273e2d62412a0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 27 Jan 2026 22:06:49 +0100 Subject: [PATCH 07/91] update package.json scripts --- hardhat.config.ts | 30 +++++++----------------------- package.json | 6 +++--- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index bfac09f753a..ed58ae3d2d3 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, overrideTask } from 'hardhat/config'; +import { defineConfig } from 'hardhat/config'; // Plugins import hardhatEthers from '@nomicfoundation/hardhat-ethers'; @@ -9,33 +9,18 @@ import hardhatNetworkHelpers from '@nomicfoundation/hardhat-network-helpers'; import hardhatPredeploy from 'hardhat-predeploy'; import hardhatExposed from './hardhat/hardhat-exposed/plugin.js'; import hardhatOzContractsHelpers from './hardhat/hardhat-oz-contracts-helpers/plugin.js'; +import './hardhat/async-test-sanity.js'; // Parameters import yargs from 'yargs/yargs'; const argv = await yargs() .env('') .options({ - // Compilation settings - compiler: { - type: 'string', - default: '0.8.31', - }, - src: { - type: 'string', - default: 'contracts', - }, - runs: { - type: 'number', - default: 200, - }, - ir: { - type: 'boolean', - default: false, - }, - evm: { - type: 'string', - default: 'osaka', - }, + compiler: { type: 'string', default: '0.8.31' }, + src: { type: 'string', default: 'contracts' }, + runs: { type: 'number', default: 200 }, + ir: { type: 'boolean', default: false }, + evm: { type: 'string', default: 'osaka' }, }) .parse(); @@ -75,7 +60,6 @@ export default defineConfig({ // Exposed contracts often exceed the maximum contract size. For normal contract, // we rely on the `code-size` compiler warning, that will cause a compilation error. allowUnlimitedContractSize: true, - // initialBaseFeePerGas: argv.coverage ? 0 : undefined, }, }, test: { diff --git a/package.json b/package.json index 28dc4e3ff7e..a7dbc89fce3 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "scripts": { "compile": "hardhat compile", "compile:harnesses": "env SRC=./fv/harnesses hardhat compile", - "coverage": "scripts/checks/coverage.sh", "docs": "npm run prepare-docs && oz-docs", "docs:watch": "oz-docs watch contracts docs/templates docs/config.js", "prepare": "husky", @@ -26,11 +25,12 @@ "generate": "scripts/generate/run.js", "pragma": "npm run compile && scripts/minimize-pragma.js artifacts/build-info/*", "version": "scripts/release/version.sh", - "test": ". scripts/set-max-old-space-size.sh && hardhat test", + "test": "hardhat test", "test:generation": "scripts/checks/generation.sh", "test:inheritance": "npm run compile && scripts/checks/inheritance-ordering.js artifacts/build-info/*", "test:pragma": "npm run compile && scripts/checks/pragma-validity.js artifacts/build-info/*", - "gas-report": "env ENABLE_GAS_REPORT=true npm run test", + "coverage": "hardhat test --coverage", + "gas-report": "hardhat test --gas-stats", "slither": "npm run clean && slither ." }, "repository": { From 75ed0fa2f609b9a61291472252e6c173067da693 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 28 Jan 2026 14:46:21 +0100 Subject: [PATCH 08/91] plugin update --- hardhat/hardhat-exposed/core/index.ts | 4 --- .../hardhat-exposed/hook-handlers/clean.ts | 7 +++++ hardhat/hardhat-exposed/plugin.ts | 10 +------ hardhat/hardhat-exposed/tasks/build.ts | 26 ------------------- hardhat/hardhat-exposed/tasks/clean.ts | 21 --------------- 5 files changed, 8 insertions(+), 60 deletions(-) create mode 100644 hardhat/hardhat-exposed/hook-handlers/clean.ts delete mode 100644 hardhat/hardhat-exposed/tasks/build.ts delete mode 100644 hardhat/hardhat-exposed/tasks/clean.ts diff --git a/hardhat/hardhat-exposed/core/index.ts b/hardhat/hardhat-exposed/core/index.ts index 366171715b8..e1f9725a1f5 100644 --- a/hardhat/hardhat-exposed/core/index.ts +++ b/hardhat/hardhat-exposed/core/index.ts @@ -31,10 +31,6 @@ const exposedVersionPragma = '>=0.6.0'; const defaultPrefix = '$'; // Tasks & Hooks -export async function cleanExposed(config: HardhatConfig) { - await remove(getExposedPath(config)); -} - export async function writeExposed(exposed: Map) { for (const [fsPath, content] of exposed.entries()) { await ensureDir(path.dirname(fsPath)); diff --git a/hardhat/hardhat-exposed/hook-handlers/clean.ts b/hardhat/hardhat-exposed/hook-handlers/clean.ts new file mode 100644 index 00000000000..f45210f80a1 --- /dev/null +++ b/hardhat/hardhat-exposed/hook-handlers/clean.ts @@ -0,0 +1,7 @@ +import fs from 'node:fs/promises'; + +import type { CleanHooks } from 'hardhat/types/hooks'; + +export default async (): Promise> => ({ + onClean: context => fs.rm(context.config.exposed.outDir, { recursive: true, force: true }), +}); diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index a91a98eda80..9bd37321e0c 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -1,4 +1,3 @@ -import { overrideTask } from 'hardhat/config'; import type { HardhatPlugin } from 'hardhat/types/plugins'; import type {} from './type-extensions.js'; @@ -6,17 +5,10 @@ import type {} from './type-extensions.js'; const hardhatExposedPlugin: HardhatPlugin = { id: 'hardhat-exposed', hookHandlers: { + clean: () => import('./hook-handlers/clean.js'), config: () => import('./hook-handlers/config.js'), hre: () => import('./hook-handlers/hre.js'), }, - tasks: [ - overrideTask('clean') - .setAction(() => import('./tasks/clean.js')) - .build(), - overrideTask('build') - .setAction(() => import('./tasks/build.js')) - .build(), - ], npmPackage: 'hardhat-exposed', }; diff --git a/hardhat/hardhat-exposed/tasks/build.ts b/hardhat/hardhat-exposed/tasks/build.ts deleted file mode 100644 index 35c059bc9fd..00000000000 --- a/hardhat/hardhat-exposed/tasks/build.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; -import type {} from '../type-extensions'; - -import { cleanExposed } from '../core'; - -// See: hardhat/src/internal/builtin-plugins/solidity/tasks/build.ts -type BuildActionArguments = any; -// interface BuildActionArguments { -// force: boolean; -// files: string[]; -// quiet: boolean; -// defaultBuildProfile: string | undefined; -// noTests: boolean; -// noContracts: boolean; -// } - -export default async function ( - taskArguments: BuildActionArguments, - hre: HardhatRuntimeEnvironment, - superCall: (taskArguments: BuildActionArguments) => Promise, -) { - if (taskArguments.force) { - await cleanExposed(hre.config); - } - return superCall(taskArguments); -} diff --git a/hardhat/hardhat-exposed/tasks/clean.ts b/hardhat/hardhat-exposed/tasks/clean.ts deleted file mode 100644 index 046373736f3..00000000000 --- a/hardhat/hardhat-exposed/tasks/clean.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; -import type {} from '../type-extensions'; - -import { cleanExposed } from '../core'; - -// See: hardhat/src/internal/builtin-plugins/clean/task-action.ts -type TaskArguments = any; -// interface TaskArguments { -// global: boolean; -// } - -export default async function ( - taskArguments: TaskArguments, - hre: HardhatRuntimeEnvironment, - superCall: (taskArguments: TaskArguments) => Promise, -) { - if (!taskArguments.global) { - await cleanExposed(hre.config); - } - return superCall(taskArguments); -} From 8c53c5d6c4229d50455ca20b1bd98c1f530ac464 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 28 Jan 2026 15:57:12 +0100 Subject: [PATCH 09/91] fix package logic --- scripts/prepack.sh | 1 - scripts/remove-ignored-artifacts.js | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/prepack.sh b/scripts/prepack.sh index 6af10329f66..182af4e5856 100755 --- a/scripts/prepack.sh +++ b/scripts/prepack.sh @@ -17,7 +17,6 @@ env COMPILE_MODE=production npm run compile mkdirp contracts/build/contracts cp artifacts/contracts/**/*.json contracts/build/contracts -rm contracts/build/contracts/*.dbg.json node scripts/remove-ignored-artifacts.js cp README.md contracts/ diff --git a/scripts/remove-ignored-artifacts.js b/scripts/remove-ignored-artifacts.js index e156032b17c..9dd0acfc2bf 100644 --- a/scripts/remove-ignored-artifacts.js +++ b/scripts/remove-ignored-artifacts.js @@ -2,9 +2,9 @@ // This script removes the build artifacts of ignored contracts. -const fs = require('fs'); -const path = require('path'); -const match = require('micromatch'); +import fs from 'fs'; +import path from 'path'; +import match from 'micromatch'; function readJSON(path) { return JSON.parse(fs.readFileSync(path)); @@ -25,14 +25,14 @@ const ignorePatternsSubtrees = ignorePatterns const artifactsDir = 'contracts/build/contracts'; const buildinfo = 'artifacts/build-info'; -const filenames = fs.readdirSync(buildinfo); +const filenames = fs.readdirSync(buildinfo).filter(filename => match.isMatch(filename, '*.output.json')); let n = 0; for (const filename of filenames) { const solcOutput = readJSON(path.join(buildinfo, filename)).output; for (const sourcePath in solcOutput.contracts) { - const ignore = match.any(sourcePath, ignorePatternsSubtrees); + const ignore = match.any(sourcePath.replace(/^project\//, ''), ignorePatternsSubtrees); if (ignore) { for (const contract in solcOutput.contracts[sourcePath]) { fs.unlinkSync(path.join(artifactsDir, contract + '.json')); From 85bd93bfa3191f6cc7321a1a50dc661416a9f222 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 28 Jan 2026 15:59:37 +0100 Subject: [PATCH 10/91] remove old unused file --- scripts/fetch-common-contracts.js | 50 ------------------------------- 1 file changed, 50 deletions(-) delete mode 100755 scripts/fetch-common-contracts.js diff --git a/scripts/fetch-common-contracts.js b/scripts/fetch-common-contracts.js deleted file mode 100755 index af904243b8f..00000000000 --- a/scripts/fetch-common-contracts.js +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env node - -// This script snapshots the bytecode and ABI for the `hardhat/common-contracts.js` script. -// - Bytecode is fetched directly from the blockchain by querying the provided client endpoint. If no endpoint is -// provided, ethers default provider is used instead. -// - ABI is fetched from etherscan's API using the provided etherscan API key. If no API key is provided, ABI will not -// be fetched and saved. -// -// The produced artifacts are stored in the `output` folder ('test/bin' by default). For each contract, two files are -// produced: -// - `.bytecode` containing the contract bytecode (in binary encoding) -// - `.abi` containing the ABI (in utf-8 encoding) - -const fs = require('fs'); -const path = require('path'); -const { ethers } = require('ethers'); -const { request } = require('undici'); -const { hideBin } = require('yargs/helpers'); -const { argv } = require('yargs/yargs')(hideBin(process.argv)) - .env('') - .options({ - output: { type: 'string', default: 'test/bin/' }, - client: { type: 'string' }, - etherscan: { type: 'string' }, - }); - -// List of contract names and addresses to fetch -const config = { - EntryPoint070: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', - SenderCreator070: '0xEFC2c1444eBCC4Db75e7613d20C6a62fF67A167C', - EntryPoint080: '0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108', - SenderCreator080: '0x449ED7C3e6Fee6a97311d4b55475DF59C44AdD33', -}; - -Promise.all( - Object.entries(config).flatMap(([name, addr]) => - Promise.all([ - argv.etherscan && - request(`https://api.etherscan.io/api?module=contract&action=getabi&address=${addr}&apikey=${argv.etherscan}`) - .then(({ body }) => body.json()) - .then(({ result: abi }) => fs.writeFile(path.join(argv.output, `${name}.abi`), abi, 'utf-8', () => {})), - ethers - .getDefaultProvider(argv.client) - .getCode(addr) - .then(bytecode => - fs.writeFile(path.join(argv.output, `${name}.bytecode`), ethers.getBytes(bytecode), 'binary', () => {}), - ), - ]), - ), -); From 52fc608f3e9afe8f2eae392b305f039cee7913a6 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 28 Jan 2026 16:18:24 +0100 Subject: [PATCH 11/91] migrate generation logic --- scripts/generate/format-lines.js | 4 +--- scripts/generate/helpers/sanitize.js | 8 +++----- scripts/generate/run.js | 18 +++++++++--------- scripts/generate/templates/Arrays.js | 8 ++++---- scripts/generate/templates/Checkpoints.js | 6 +++--- scripts/generate/templates/Checkpoints.t.js | 8 ++++---- scripts/generate/templates/Enumerable.opts.js | 2 +- scripts/generate/templates/EnumerableMap.js | 8 ++++---- scripts/generate/templates/EnumerableSet.js | 8 ++++---- scripts/generate/templates/MerkleProof.js | 6 +++--- scripts/generate/templates/MerkleProof.opts.js | 6 ++---- scripts/generate/templates/Packing.js | 10 +++++----- scripts/generate/templates/Packing.t.js | 8 ++++---- scripts/generate/templates/SafeCast.js | 6 +++--- scripts/generate/templates/Slot.opts.js | 4 ++-- scripts/generate/templates/SlotDerivation.js | 8 ++++---- scripts/generate/templates/SlotDerivation.t.js | 8 ++++---- scripts/generate/templates/StorageSlot.js | 6 +++--- scripts/generate/templates/StorageSlotMock.js | 6 +++--- scripts/generate/templates/TransientSlot.js | 6 +++--- .../generate/templates/TransientSlotMock.js | 6 +++--- scripts/generate/templates/conversion.js | 9 ++------- scripts/helpers.js | 4 ++-- scripts/solc-versions.js | 2 +- 24 files changed, 77 insertions(+), 88 deletions(-) diff --git a/scripts/generate/format-lines.js b/scripts/generate/format-lines.js index fa3d6b120d8..8a5516f7c6c 100644 --- a/scripts/generate/format-lines.js +++ b/scripts/generate/format-lines.js @@ -1,4 +1,4 @@ -function formatLines(...lines) { +export default function formatLines(...lines) { return [...indentEach(0, lines)].join('\n') + '\n'; } @@ -12,5 +12,3 @@ function* indentEach(indent, lines) { } } } - -module.exports = formatLines; diff --git a/scripts/generate/helpers/sanitize.js b/scripts/generate/helpers/sanitize.js index e680ec1bf46..3a337629df0 100644 --- a/scripts/generate/helpers/sanitize.js +++ b/scripts/generate/helpers/sanitize.js @@ -1,5 +1,3 @@ -module.exports = { - address: expr => `and(${expr}, shr(96, not(0)))`, - bool: expr => `iszero(iszero(${expr}))`, - bytes: (expr, size) => `and(${expr}, shl(${256 - 8 * size}, not(0)))`, -}; +export const address = expr => `and(${expr}, shr(96, not(0)))`; +export const bool = expr => `iszero(iszero(${expr}))`; +export const bytes = (expr, size) => `and(${expr}, shl(${256 - 8 * size}, not(0)))`; diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 394bb39525e..0dc4b486096 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -const cp = require('child_process'); -const fs = require('fs'); -const path = require('path'); -const format = require('./format-lines'); +import cp from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import format from './format-lines.js'; function getVersion(path) { try { @@ -13,8 +13,8 @@ function getVersion(path) { } } -function generateFromTemplate(file, template, outputPrefix = '', lint = false) { - const script = path.relative(path.join(__dirname, '../..'), __filename); +async function generateFromTemplate(file, template, outputPrefix = '', lint = false) { + const script = path.relative(path.join(import.meta.dirname, '../..'), import.meta.filename); const input = path.join(path.dirname(script), template); const output = path.join(outputPrefix, file); const version = getVersion(output); @@ -23,7 +23,7 @@ function generateFromTemplate(file, template, outputPrefix = '', lint = false) { ...(version ? [version + ` (${file})`] : []), `// This file was procedurally generated from ${input}.`, '', - require(template).trimEnd(), + (await import(template)).default.trimEnd(), ); fs.writeFileSync(output, content); @@ -48,7 +48,7 @@ for (const [file, template] of Object.entries({ 'mocks/StorageSlotMock.sol': './templates/StorageSlotMock.js', 'mocks/TransientSlotMock.sol': './templates/TransientSlotMock.js', })) { - generateFromTemplate(file, template, './contracts/', needsLinter.includes(file)); + await generateFromTemplate(file, template, './contracts/', needsLinter.includes(file)); } // Tests @@ -57,5 +57,5 @@ for (const [file, template] of Object.entries({ 'utils/Packing.t.sol': './templates/Packing.t.js', 'utils/SlotDerivation.t.sol': './templates/SlotDerivation.t.js', })) { - generateFromTemplate(file, template, './test/', needsLinter.includes(file)); + await generateFromTemplate(file, template, './test/', needsLinter.includes(file)); } diff --git a/scripts/generate/templates/Arrays.js b/scripts/generate/templates/Arrays.js index 6c6f5cff98c..493ee49110d 100644 --- a/scripts/generate/templates/Arrays.js +++ b/scripts/generate/templates/Arrays.js @@ -1,6 +1,6 @@ -const format = require('../format-lines'); -const { capitalize } = require('../../helpers'); -const { TYPES } = require('./Arrays.opts'); +import format from '../format-lines.js'; +import { capitalize } from '../../helpers.js'; +import { TYPES } from './Arrays.opts.js'; const header = `\ pragma solidity ^0.8.24; @@ -476,7 +476,7 @@ function replace( `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library Arrays {', format( diff --git a/scripts/generate/templates/Checkpoints.js b/scripts/generate/templates/Checkpoints.js index 1ead8df6f29..ae34ab1d8cd 100644 --- a/scripts/generate/templates/Checkpoints.js +++ b/scripts/generate/templates/Checkpoints.js @@ -1,5 +1,5 @@ -const format = require('../format-lines'); -const { OPTS } = require('./Checkpoints.opts'); +import format from '../format-lines.js'; +import { OPTS } from './Checkpoints.opts.js'; // TEMPLATE const header = `\ @@ -229,7 +229,7 @@ function _unsafeAccess( `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library Checkpoints {', format( diff --git a/scripts/generate/templates/Checkpoints.t.js b/scripts/generate/templates/Checkpoints.t.js index 23d9466d28f..6e399f1b967 100644 --- a/scripts/generate/templates/Checkpoints.t.js +++ b/scripts/generate/templates/Checkpoints.t.js @@ -1,6 +1,6 @@ -const format = require('../format-lines'); -const { capitalize } = require('../../helpers'); -const { OPTS } = require('./Checkpoints.opts.js'); +import format from '../format-lines.js'; +import { capitalize } from '../../helpers.js'; +import { OPTS } from './Checkpoints.opts.js'; // TEMPLATE const header = `\ @@ -130,7 +130,7 @@ function testLookup(${opts.keyTypeName}[] memory keys, ${opts.valueTypeName}[] m `; // GENERATE -module.exports = format( +export default format( header, ...OPTS.flatMap(opts => [ `contract Checkpoints${opts.historyTypeName}Test is Test {`, diff --git a/scripts/generate/templates/Enumerable.opts.js b/scripts/generate/templates/Enumerable.opts.js index 61daf7a1ceb..fe7c0948feb 100644 --- a/scripts/generate/templates/Enumerable.opts.js +++ b/scripts/generate/templates/Enumerable.opts.js @@ -1,4 +1,4 @@ -import { capitalize, mapValues } from '../../helpers'; +import { capitalize, mapValues } from '../../helpers.js'; export const typeDescr = ({ type, size = 0, memory = false }) => { memory |= size > 0; diff --git a/scripts/generate/templates/EnumerableMap.js b/scripts/generate/templates/EnumerableMap.js index 107e29e8ed7..56c3fef17e9 100644 --- a/scripts/generate/templates/EnumerableMap.js +++ b/scripts/generate/templates/EnumerableMap.js @@ -1,6 +1,6 @@ -const format = require('../format-lines'); -const { fromBytes32, toBytes32 } = require('./conversion'); -const { MAP_TYPES } = require('./Enumerable.opts'); +import format from '../format-lines.js'; +import { fromBytes32, toBytes32 } from './conversion.js'; +import { MAP_TYPES } from './Enumerable.opts.js'; const header = `\ pragma solidity ^0.8.24; @@ -448,7 +448,7 @@ function keys(${name} storage map, uint256 start, uint256 end) internal view ret `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library EnumerableMap {', format( diff --git a/scripts/generate/templates/EnumerableSet.js b/scripts/generate/templates/EnumerableSet.js index 2c5a54227a8..6277b9fc7f8 100644 --- a/scripts/generate/templates/EnumerableSet.js +++ b/scripts/generate/templates/EnumerableSet.js @@ -1,6 +1,6 @@ -const format = require('../format-lines'); -const { fromBytes32, toBytes32 } = require('./conversion'); -const { SET_TYPES } = require('./Enumerable.opts'); +import format from '../format-lines.js'; +import { fromBytes32, toBytes32 } from './conversion.js'; +import { SET_TYPES } from './Enumerable.opts.js'; const header = `\ pragma solidity ^0.8.24; @@ -456,7 +456,7 @@ function values(${name} storage set, uint256 start, uint256 end) internal view r `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library EnumerableSet {', format( diff --git a/scripts/generate/templates/MerkleProof.js b/scripts/generate/templates/MerkleProof.js index f7b8a6a7ac5..13abe0b2cf1 100644 --- a/scripts/generate/templates/MerkleProof.js +++ b/scripts/generate/templates/MerkleProof.js @@ -1,5 +1,5 @@ -const format = require('../format-lines'); -const { OPTS } = require('./MerkleProof.opts'); +import format from '../format-lines.js'; +import { OPTS } from './MerkleProof.opts.js'; const DEFAULT_HASH = 'Hashes.commutativeKeccak256'; @@ -172,7 +172,7 @@ function processMultiProof${suffix}(${formatArgsMultiline( `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library MerkleProof {', format( diff --git a/scripts/generate/templates/MerkleProof.opts.js b/scripts/generate/templates/MerkleProof.opts.js index 911f2392257..8c9d3e6a143 100644 --- a/scripts/generate/templates/MerkleProof.opts.js +++ b/scripts/generate/templates/MerkleProof.opts.js @@ -1,11 +1,9 @@ -const { product } = require('../../helpers'); +import { product } from '../../helpers.js'; -const OPTS = product( +export const OPTS = product( [ { suffix: '', location: 'memory' }, { suffix: 'Calldata', location: 'calldata' }, ], [{ visibility: 'pure' }, { visibility: 'view', hash: 'hasher' }], ).map(objs => Object.assign({}, ...objs)); - -module.exports = { OPTS }; diff --git a/scripts/generate/templates/Packing.js b/scripts/generate/templates/Packing.js index 9f3b7716a6a..e74f8944193 100644 --- a/scripts/generate/templates/Packing.js +++ b/scripts/generate/templates/Packing.js @@ -1,7 +1,7 @@ -const format = require('../format-lines'); -const sanitize = require('../helpers/sanitize'); -const { product } = require('../../helpers'); -const { SIZES } = require('./Packing.opts'); +import format from '../format-lines.js'; +import * as sanitize from '../helpers/sanitize.js'; +import { product } from '../../helpers.js'; +import { SIZES } from './Packing.opts.js'; // TEMPLATE const header = `\ @@ -74,7 +74,7 @@ function replace_${outer}_${inner}(bytes${outer} self, bytes${inner} value, uint `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library Packing {', format( diff --git a/scripts/generate/templates/Packing.t.js b/scripts/generate/templates/Packing.t.js index 3889eea06ee..44ba66b7bbd 100644 --- a/scripts/generate/templates/Packing.t.js +++ b/scripts/generate/templates/Packing.t.js @@ -1,6 +1,6 @@ -const format = require('../format-lines'); -const { product } = require('../../helpers'); -const { SIZES } = require('./Packing.opts'); +import format from '../format-lines.js'; +import { product } from '../../helpers.js'; +import { SIZES } from './Packing.opts.js'; // TEMPLATE const header = `\ @@ -29,7 +29,7 @@ function testSymbolicReplace(bytes${outer} container, bytes${inner} newValue, ui `; // GENERATE -module.exports = format( +export default format( header, 'contract PackingTest is Test {', format( diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 16d1ee8f7f1..e482904f7f4 100644 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -1,5 +1,5 @@ -const format = require('../format-lines'); -const { range } = require('../../helpers'); +import format from '../format-lines.js'; +import { range } from '../../helpers.js'; const LENGTHS = range(8, 256, 8).reverse(); // 248 → 8 (in steps of 8) @@ -126,7 +126,7 @@ function toUint(bool b) internal pure returns (uint256 u) { `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library SafeCast {', format( diff --git a/scripts/generate/templates/Slot.opts.js b/scripts/generate/templates/Slot.opts.js index 3eca2bcf08f..363ecda1ab8 100644 --- a/scripts/generate/templates/Slot.opts.js +++ b/scripts/generate/templates/Slot.opts.js @@ -1,4 +1,4 @@ -const { capitalize } = require('../../helpers'); +import { capitalize } from '../../helpers.js'; const TYPES = [ { type: 'address', isValueType: true }, @@ -12,4 +12,4 @@ const TYPES = [ Object.assign(TYPES, Object.fromEntries(TYPES.map(entry => [entry.type, entry]))); -module.exports = { TYPES }; +export { TYPES }; diff --git a/scripts/generate/templates/SlotDerivation.js b/scripts/generate/templates/SlotDerivation.js index 931c9fc25e8..e7a85949f0b 100644 --- a/scripts/generate/templates/SlotDerivation.js +++ b/scripts/generate/templates/SlotDerivation.js @@ -1,6 +1,6 @@ -const format = require('../format-lines'); -const sanitize = require('../helpers/sanitize'); -const { TYPES } = require('./Slot.opts'); +import format from '../format-lines.js'; +import * as sanitize from '../helpers/sanitize.js'; +import { TYPES } from './Slot.opts.js'; const header = `\ pragma solidity ^0.8.20; @@ -105,7 +105,7 @@ function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns ( `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library SlotDerivation {', format( diff --git a/scripts/generate/templates/SlotDerivation.t.js b/scripts/generate/templates/SlotDerivation.t.js index 8e18ca528bb..c2149e38d3a 100644 --- a/scripts/generate/templates/SlotDerivation.t.js +++ b/scripts/generate/templates/SlotDerivation.t.js @@ -1,6 +1,6 @@ -const format = require('../format-lines'); -const { capitalize } = require('../../helpers'); -const { TYPES } = require('./Slot.opts'); +import format from '../format-lines.js'; +import { capitalize } from '../../helpers.js'; +import { TYPES } from './Slot.opts.js'; const header = `\ pragma solidity ^0.8.20; @@ -101,7 +101,7 @@ function _assertDeriveMapping${name}(${type} memory key) internal view { `; // GENERATE -module.exports = format( +export default format( header, 'contract SlotDerivationTest is Test, SymTest {', format( diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 53287b81fd9..0be00caca96 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -1,5 +1,5 @@ -const format = require('../format-lines'); -const { TYPES } = require('./Slot.opts'); +import format from '../format-lines.js'; +import { TYPES } from './Slot.opts.js'; const header = `\ pragma solidity ^0.8.20; @@ -64,7 +64,7 @@ function get${name}Slot(${type} storage store) internal pure returns (${name}Slo `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library StorageSlot {', format( diff --git a/scripts/generate/templates/StorageSlotMock.js b/scripts/generate/templates/StorageSlotMock.js index c6d326a5e26..a6c8138c357 100644 --- a/scripts/generate/templates/StorageSlotMock.js +++ b/scripts/generate/templates/StorageSlotMock.js @@ -1,5 +1,5 @@ -const format = require('../format-lines'); -const { TYPES } = require('./Slot.opts'); +import format from '../format-lines.js'; +import { TYPES } from './Slot.opts.js'; const header = `\ pragma solidity ^0.8.20; @@ -41,7 +41,7 @@ function get${name}Storage(uint256 key) public view returns (${type} memory) { `; // GENERATE -module.exports = format( +export default format( header, 'contract StorageSlotMock is Multicall {', format( diff --git a/scripts/generate/templates/TransientSlot.js b/scripts/generate/templates/TransientSlot.js index 9ede32f85c5..ac85a7bb559 100644 --- a/scripts/generate/templates/TransientSlot.js +++ b/scripts/generate/templates/TransientSlot.js @@ -1,5 +1,5 @@ -const format = require('../format-lines'); -const { TYPES } = require('./Slot.opts'); +import format from '../format-lines.js'; +import { TYPES } from './Slot.opts.js'; const header = `\ pragma solidity ^0.8.24; @@ -67,7 +67,7 @@ function tstore(${name}Slot slot, ${type} value) internal { `; // GENERATE -module.exports = format( +export default format( header.trimEnd(), 'library TransientSlot {', format( diff --git a/scripts/generate/templates/TransientSlotMock.js b/scripts/generate/templates/TransientSlotMock.js index 4807b0cc1ff..06d8a55d8e9 100644 --- a/scripts/generate/templates/TransientSlotMock.js +++ b/scripts/generate/templates/TransientSlotMock.js @@ -1,5 +1,5 @@ -const format = require('../format-lines'); -const { TYPES } = require('./Slot.opts'); +import format from '../format-lines.js'; +import { TYPES } from './Slot.opts.js'; const header = `\ pragma solidity ^0.8.24; @@ -21,7 +21,7 @@ function tstore(bytes32 slot, ${type} value) public { `; // GENERATE -module.exports = format( +export default format( header, 'contract TransientSlotMock is Multicall {', format( diff --git a/scripts/generate/templates/conversion.js b/scripts/generate/templates/conversion.js index c0f55a982e5..5238e047a73 100644 --- a/scripts/generate/templates/conversion.js +++ b/scripts/generate/templates/conversion.js @@ -1,4 +1,4 @@ -function toBytes32(type, value) { +export function toBytes32(type, value) { switch (type) { case 'bytes32': return value; @@ -13,7 +13,7 @@ function toBytes32(type, value) { } } -function fromBytes32(type, value) { +export function fromBytes32(type, value) { switch (type) { case 'bytes32': return value; @@ -27,8 +27,3 @@ function fromBytes32(type, value) { throw new Error(`Conversion from bytes32 to ${type} not supported`); } } - -module.exports = { - toBytes32, - fromBytes32, -}; diff --git a/scripts/helpers.js b/scripts/helpers.js index 15f635eff45..b687e279dde 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -1,2 +1,2 @@ -export * from '../test/helpers/iterate'; -export * from '../test/helpers/strings'; +export * from '../test/helpers/iterate.js'; +export * from '../test/helpers/strings.js'; diff --git a/scripts/solc-versions.js b/scripts/solc-versions.js index cd27a3621e7..c06888d8486 100644 --- a/scripts/solc-versions.js +++ b/scripts/solc-versions.js @@ -3,7 +3,7 @@ const semver = require('semver'); const { range } = require('./helpers'); module.exports = { - versions: ['0.4.26', '0.5.16', '0.6.12', '0.7.6', '0.8.30'] + versions: ['0.4.26', '0.5.16', '0.6.12', '0.7.6', '0.8.33'] .map(semver.parse) .flatMap(({ major, minor, patch }) => range(patch + 1).map(p => `${major}.${minor}.${p}`)), compile: (source, version) => From d6e4477f3cfca20c0e7b83b188d30f92029c26b2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 28 Jan 2026 16:33:13 +0100 Subject: [PATCH 12/91] migrate inheritance-ordering and pragma check/minimize check --- package.json | 4 ++-- scripts/checks/inheritance-ordering.js | 22 ++++++++++++++-------- scripts/checks/pragma-validity.js | 14 ++++++-------- scripts/get-contracts-metadata.js | 18 +++++++++--------- scripts/minimize-pragma.js | 18 +++++++++--------- scripts/solc-versions.js | 25 ++++++++++++------------- 6 files changed, 52 insertions(+), 49 deletions(-) diff --git a/package.json b/package.json index a7dbc89fce3..1b666a11004 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "version": "scripts/release/version.sh", "test": "hardhat test", "test:generation": "scripts/checks/generation.sh", - "test:inheritance": "npm run compile && scripts/checks/inheritance-ordering.js artifacts/build-info/*", - "test:pragma": "npm run compile && scripts/checks/pragma-validity.js artifacts/build-info/*", + "test:inheritance": "npm run compile && scripts/checks/inheritance-ordering.js artifacts/build-info/*.output.json", + "test:pragma": "npm run compile && scripts/checks/pragma-validity.js artifacts/build-info/*.output.json", "coverage": "hardhat test --coverage", "gas-report": "hardhat test --gas-stats", "slither": "npm run clean && slither ." diff --git a/scripts/checks/inheritance-ordering.js b/scripts/checks/inheritance-ordering.js index e0c8149e428..513a80f74e1 100755 --- a/scripts/checks/inheritance-ordering.js +++ b/scripts/checks/inheritance-ordering.js @@ -1,23 +1,29 @@ #!/usr/bin/env node -const path = require('path'); -const graphlib = require('graphlib'); -const match = require('micromatch'); -const { findAll } = require('solidity-ast/utils'); -const { _: artifacts } = require('yargs/yargs')().argv; +import fs from 'fs'; +import path from 'path'; +import graphlib from 'graphlib'; +import match from 'micromatch'; +import { findAll } from 'solidity-ast/utils.js'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +const { _: artifacts } = yargs(hideBin(process.argv)).argv; // files to skip const skipPatterns = ['contracts-exposed/**', 'contracts/mocks/**']; for (const artifact of artifacts) { - const { output: solcOutput } = require(path.resolve(__dirname, '../..', artifact)); + const { output: solcOutput } = JSON.parse( + fs.readFileSync(path.resolve(import.meta.dirname, '../..', artifact), 'utf-8'), + ); const graph = new graphlib.Graph({ directed: true }); const names = {}; const linearized = []; - for (const source in solcOutput.contracts) { - if (match.any(source, skipPatterns)) continue; + for (const source in solcOutput?.contracts ?? []) { + if (match.any(source.replace(/^project\//, ''), skipPatterns)) continue; for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) { names[contractDef.id] = contractDef.name; linearized.push(contractDef.linearizedBaseContracts); diff --git a/scripts/checks/pragma-validity.js b/scripts/checks/pragma-validity.js index 491f650bce2..73e66cfd596 100755 --- a/scripts/checks/pragma-validity.js +++ b/scripts/checks/pragma-validity.js @@ -1,13 +1,11 @@ #!/usr/bin/env node -const semver = require('semver'); -const pLimit = require('p-limit').default; - -const { hideBin } = require('yargs/helpers'); -const yargs = require('yargs/yargs'); - -const getContractsMetadata = require('../get-contracts-metadata'); -const { compile } = require('../solc-versions'); +import semver from 'semver'; +import pLimit from 'p-limit'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { getContractsMetadata } from '../get-contracts-metadata.js'; +import { compile } from '../solc-versions.js'; const { argv: { pattern, skipPatterns, verbose, concurrency, _: artifacts }, diff --git a/scripts/get-contracts-metadata.js b/scripts/get-contracts-metadata.js index 030ab5a3d94..8f77589f2b7 100644 --- a/scripts/get-contracts-metadata.js +++ b/scripts/get-contracts-metadata.js @@ -1,10 +1,10 @@ -const fs = require('fs'); -const glob = require('glob'); -const match = require('micromatch'); -const path = require('path'); -const { findAll } = require('solidity-ast/utils'); +import fs from 'fs'; +import path from 'path'; +import { glob } from 'glob'; +import match from 'micromatch'; +import { findAll } from 'solidity-ast/utils.js'; -module.exports = function ( +export function getContractsMetadata( pattern = 'contracts/**/*.sol', skipPatterns = ['contracts/mocks/**/*.sol'], artifacts = [], @@ -13,8 +13,8 @@ module.exports = function ( // definitions with minimal IO operations. const metadata = Object.fromEntries( artifacts.flatMap(artifact => { - const { output: solcOutput } = require(path.resolve(__dirname, '..', artifact)); - return Object.keys(solcOutput.contracts) + const { output: solcOutput } = JSON.parse(fs.readFileSync(path.resolve(import.meta.dirname, '..', artifact))); + return Object.keys(solcOutput?.contracts ?? {}) .filter(source => match.all(source, pattern) && !match.any(source, skipPatterns)) .map(source => [ source, @@ -52,4 +52,4 @@ module.exports = function ( }); return metadata; -}; +} diff --git a/scripts/minimize-pragma.js b/scripts/minimize-pragma.js index a4cbf03aa77..69bd47f6014 100755 --- a/scripts/minimize-pragma.js +++ b/scripts/minimize-pragma.js @@ -1,14 +1,14 @@ #!/usr/bin/env node -const fs = require('fs'); -const graphlib = require('graphlib'); -const semver = require('semver'); -const pLimit = require('p-limit').default; -const { hideBin } = require('yargs/helpers'); -const yargs = require('yargs/yargs'); - -const getContractsMetadata = require('./get-contracts-metadata'); -const { versions: allSolcVersions, compile } = require('./solc-versions'); +import fs from 'fs'; +import graphlib from 'graphlib'; +import semver from 'semver'; +import pLimit from 'p-limit'; +import { hideBin } from 'yargs/helpers'; +import yargs from 'yargs/yargs'; + +import { getContractsMetadata } from './get-contracts-metadata.js'; +import { versions as allSolcVersions, compile } from './solc-versions.js'; const { argv: { pattern, skipPatterns, minVersionForContracts, minVersionForInterfaces, concurrency, _: artifacts }, diff --git a/scripts/solc-versions.js b/scripts/solc-versions.js index c06888d8486..17e3771bcb0 100644 --- a/scripts/solc-versions.js +++ b/scripts/solc-versions.js @@ -1,15 +1,14 @@ -const { exec } = require('child_process'); -const semver = require('semver'); -const { range } = require('./helpers'); +import { exec } from 'child_process'; +import semver from 'semver'; +import { range } from './helpers.js'; -module.exports = { - versions: ['0.4.26', '0.5.16', '0.6.12', '0.7.6', '0.8.33'] - .map(semver.parse) - .flatMap(({ major, minor, patch }) => range(patch + 1).map(p => `${major}.${minor}.${p}`)), - compile: (source, version) => - new Promise((resolve, reject) => - exec(`forge build ${source} --use ${version} --out out/solc-${version}`, error => - error ? reject(error) : resolve(), - ), +export const versions = ['0.4.26', '0.5.16', '0.6.12', '0.7.6', '0.8.33'] + .map(semver.parse) + .flatMap(({ major, minor, patch }) => range(patch + 1).map(p => `${major}.${minor}.${p}`)); + +export const compile = (source, version) => + new Promise((resolve, reject) => + exec(`forge build ${source} --use ${version} --out out/solc-${version}`, error => + error ? reject(error) : resolve(), ), -}; + ); From 825a2039cc9c40c2477a473c433a844197d739fa Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 28 Jan 2026 18:17:03 +0100 Subject: [PATCH 13/91] remove supperfluous test that doesn't run in hh3 --- test/account/AccountEIP7702.t.sol | 116 ------------------------------ 1 file changed, 116 deletions(-) delete mode 100644 test/account/AccountEIP7702.t.sol diff --git a/test/account/AccountEIP7702.t.sol b/test/account/AccountEIP7702.t.sol deleted file mode 100644 index 88323fc7fc9..00000000000 --- a/test/account/AccountEIP7702.t.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {AccountEIP7702Mock} from "@openzeppelin/contracts/mocks/account/AccountMock.sol"; -import {CallReceiverMock} from "@openzeppelin/contracts/mocks/CallReceiverMock.sol"; -import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; -import { - ERC7579Utils, - Execution, - Mode, - ModeSelector, - ModePayload -} from "@openzeppelin/contracts/account/utils/draft-ERC7579Utils.sol"; -import {ERC4337Utils, IEntryPointExtra} from "@openzeppelin/contracts/account/utils/draft-ERC4337Utils.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {PackedUserOperation} from "@openzeppelin/contracts/interfaces/draft-IERC4337.sol"; -import {ERC7821} from "@openzeppelin/contracts/account/extensions/draft-ERC7821.sol"; - -contract AccountEIP7702MockConstructor is AccountEIP7702Mock { - constructor() EIP712("MyAccount", "1") {} -} - -contract AccountEIP7702Test is Test { - using ERC7579Utils for *; - using ERC4337Utils for PackedUserOperation; - using Strings for *; - - uint256 private constant MAX_ETH = type(uint128).max; - - // Test accounts - CallReceiverMock private _target; - - // ERC-4337 signer - uint256 private _signerPrivateKey; - AccountEIP7702MockConstructor private _signer; - - function setUp() public { - // Deploy target contract - _target = new CallReceiverMock(); - - // Setup signer - _signerPrivateKey = 0x1234; - _signer = AccountEIP7702MockConstructor(payable(vm.addr(_signerPrivateKey))); - vm.deal(address(_signer), MAX_ETH); - - // Sign and attach delegation - vm.signAndAttachDelegation(address(new AccountEIP7702MockConstructor()), _signerPrivateKey); - - // Setup entrypoint - address entrypoint = address(ERC4337Utils.ENTRYPOINT_V09); - vm.deal(entrypoint, MAX_ETH); - vm.etch( - entrypoint, - vm.readFileBinary( - string.concat( - "node_modules/hardhat-predeploy/bin/", - Strings.toChecksumHexString(entrypoint), - ".bytecode" - ) - ) - ); - } - - function testExecuteBatch(address bundler, uint256 argA, uint256 argB) public { - vm.assume(bundler.code.length == 0); - vm.startPrank(bundler, bundler); - - // Create the mode for batch execution - Mode mode = ERC7579Utils.CALLTYPE_BATCH.encodeMode( - ERC7579Utils.EXECTYPE_DEFAULT, - ModeSelector.wrap(0x00000000), - ModePayload.wrap(0x00000000) - ); - - Execution[] memory execution = new Execution[](2); - execution[0] = Execution({ - target: address(_target), - value: 1 ether, - callData: abi.encodeCall(CallReceiverMock.mockFunctionExtra, ()) - }); - execution[1] = Execution({ - target: address(_target), - value: 0, - callData: abi.encodeCall(CallReceiverMock.mockFunctionWithArgs, (argA, argB)) - }); - - // Pack the batch within a PackedUserOperation - PackedUserOperation[] memory ops = new PackedUserOperation[](1); - ops[0] = PackedUserOperation({ - sender: address(_signer), - nonce: 0, - initCode: bytes(""), - callData: abi.encodeCall(ERC7821.execute, (Mode.unwrap(mode), execution.encodeBatch())), - preVerificationGas: 100000, - accountGasLimits: bytes32(abi.encodePacked(uint128(100000), uint128(100000))), - gasFees: bytes32(abi.encodePacked(uint128(1000000), uint128(1000000))), - paymasterAndData: bytes(""), - signature: bytes("") - }); - (uint8 v, bytes32 r, bytes32 s) = vm.sign( - _signerPrivateKey, - IEntryPointExtra(address(ERC4337Utils.ENTRYPOINT_V09)).getUserOpHash(ops[0]) - ); - ops[0].signature = abi.encodePacked(r, s, v); - - // Expect the events to be emitted - vm.expectEmit(true, true, true, true); - emit CallReceiverMock.MockFunctionCalledExtra(address(_signer), 1 ether); - vm.expectEmit(true, true, true, true); - emit CallReceiverMock.MockFunctionCalledWithArgs(argA, argB); - - // Execute the batch - _signer.entryPoint().handleOps(ops, payable(makeAddr("beneficiary"))); - } -} From 3b840e6fce9030d5e10a9edf79bec43bf8b0096d Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 30 Jan 2026 16:11:58 -0300 Subject: [PATCH 14/91] update patch --- scripts/upgradeable/upgradeable.patch | 37 +++++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/scripts/upgradeable/upgradeable.patch b/scripts/upgradeable/upgradeable.patch index 834d4f1ed61..c1106277dbe 100644 --- a/scripts/upgradeable/upgradeable.patch +++ b/scripts/upgradeable/upgradeable.patch @@ -1,6 +1,6 @@ diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 -index 35ad097ff..000000000 +index 35ad097f..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,21 +0,0 @@ @@ -26,7 +26,7 @@ index 35ad097ff..000000000 - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml -index 4018cef29..d343a53d8 100644 +index 4018cef2..d343a53d 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,8 @@ @@ -40,7 +40,7 @@ index 4018cef29..d343a53d8 100644 about: Ask in the OpenZeppelin Forum diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 -index ff596b0c3..000000000 +index ff596b0c..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,14 +0,0 @@ @@ -59,7 +59,7 @@ index ff596b0c3..000000000 - - diff --git a/README.md b/README.md -index 6a01f5616..b01c6e88f 100644 +index 6a01f561..b01c6e88 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ @@ -116,7 +116,7 @@ index 6a01f5616..b01c6e88f 100644 } ``` diff --git a/contracts/package.json b/contracts/package.json -index 3535a2f56..9a73abc22 100644 +index 3535a2f5..9a73abc2 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,5 +1,5 @@ @@ -146,7 +146,7 @@ index 3535a2f56..9a73abc22 100644 + } } diff --git a/contracts/utils/ReentrancyGuard.sol b/contracts/utils/ReentrancyGuard.sol -index c156fa1cc..895e39342 100644 +index c156fa1c..895e3934 100644 --- a/contracts/utils/ReentrancyGuard.sol +++ b/contracts/utils/ReentrancyGuard.sol @@ -36,6 +36,11 @@ abstract contract ReentrancyGuard { @@ -173,7 +173,7 @@ index c156fa1cc..895e39342 100644 * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol -index 2bc45a4b2..a5aa41d21 100644 +index 2bc45a4b..a5aa41d2 100644 --- a/contracts/utils/cryptography/EIP712.sol +++ b/contracts/utils/cryptography/EIP712.sol @@ -4,7 +4,6 @@ @@ -306,7 +306,7 @@ index 2bc45a4b2..a5aa41d21 100644 } } diff --git a/package.json b/package.json -index 6f2d411dd..956933f33 100644 +index 77d405cf..d6b41c95 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ @@ -319,19 +319,22 @@ index 6f2d411dd..956933f33 100644 "keywords": [ "solidity", diff --git a/remappings.txt b/remappings.txt -index 304d1386a..a1cd63bee 100644 +index a6be3bfd..d20f777f 100644 --- a/remappings.txt +++ b/remappings.txt -@@ -1 +1,2 @@ +@@ -1,4 +1,5 @@ -@openzeppelin/contracts/=contracts/ +@openzeppelin/contracts-upgradeable/=contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ + + forge-std=lib/forge-std/src + halmos-cheatcodes=lib/halmos-cheatcodes/src diff --git a/test/account/AccountEIP7702.test.js b/test/account/AccountEIP7702.test.js -index d832e6877..6a0ab4a53 100644 +index dcb2cc86..df4ff523 100644 --- a/test/account/AccountEIP7702.test.js +++ b/test/account/AccountEIP7702.test.js -@@ -26,8 +26,8 @@ async function fixture() { - +@@ -28,8 +28,8 @@ async function fixture() { + const entrypointDomain = await getDomain(ethers.predeploy.entrypoint.v09); // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { - name: 'AccountEIP7702Mock', @@ -342,10 +345,10 @@ index d832e6877..6a0ab4a53 100644 verifyingContract: mock.address, }; diff --git a/test/account/examples/AccountEIP7702WithModulesMock.test.js b/test/account/examples/AccountEIP7702WithModulesMock.test.js -index 86816e55e..de6adc2c5 100644 +index 2369040a..3fc0a517 100644 --- a/test/account/examples/AccountEIP7702WithModulesMock.test.js +++ b/test/account/examples/AccountEIP7702WithModulesMock.test.js -@@ -36,8 +36,8 @@ async function fixture() { +@@ -38,8 +38,8 @@ async function fixture() { // domain cannot be fetched using getDomain(mock) before the mock is deployed const domain = { @@ -357,10 +360,10 @@ index 86816e55e..de6adc2c5 100644 verifyingContract: mock.address, }; diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js -index 2b6e7fa97..268e0d29d 100644 +index ab28c4e1..92e0f368 100644 --- a/test/utils/cryptography/EIP712.test.js +++ b/test/utils/cryptography/EIP712.test.js -@@ -47,27 +47,6 @@ describe('EIP712', function () { +@@ -50,27 +50,6 @@ describe('EIP712', function () { const rebuildDomain = await getDomain(this.eip712); expect(rebuildDomain).to.be.deep.equal(this.domain); }); From 561a42722d9a5d677b3514fd588ae70c28a7a716 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 30 Jan 2026 16:17:10 -0300 Subject: [PATCH 15/91] update transpile.sh --- scripts/upgradeable/transpile.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/upgradeable/transpile.sh b/scripts/upgradeable/transpile.sh index 5e63fc789f4..100153e1e3f 100644 --- a/scripts/upgradeable/transpile.sh +++ b/scripts/upgradeable/transpile.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -x +shopt -s extglob VERSION="$(jq -r .version contracts/package.json)" DIRNAME="$(dirname -- "${BASH_SOURCE[0]}")" @@ -10,9 +11,9 @@ sed -i'' -e "s//$VERSION/g" "contracts/package.json" git add contracts/package.json npm run clean -npm run compile +npm run compile -- --no-tests -build_info=($(jq -r '.input.sources | keys | if any(test("^contracts/mocks/.*\\bunreachable\\b")) then empty else input_filename end' artifacts/build-info/*)) +build_info=($(jq -r '.input.sources | keys | if any(test("^contracts/mocks/.*\\bunreachable\\b")) then empty else input_filename end' artifacts/build-info/!(*.output).json)) build_info_num=${#build_info[@]} if [ $build_info_num -ne 1 ]; then From 4f8812b7aa4d0b15674ad2055bfaed0dd1c5e02a Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 30 Jan 2026 21:00:26 +0100 Subject: [PATCH 16/91] remove npmPackage name for internal plugins --- hardhat/hardhat-exposed/plugin.ts | 1 - hardhat/hardhat-oz-contracts-helpers/plugin.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index 9bd37321e0c..a5f3bb1345c 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -9,7 +9,6 @@ const hardhatExposedPlugin: HardhatPlugin = { config: () => import('./hook-handlers/config.js'), hre: () => import('./hook-handlers/hre.js'), }, - npmPackage: 'hardhat-exposed', }; export default hardhatExposedPlugin; diff --git a/hardhat/hardhat-oz-contracts-helpers/plugin.ts b/hardhat/hardhat-oz-contracts-helpers/plugin.ts index 3b213593b60..828b8b419dd 100644 --- a/hardhat/hardhat-oz-contracts-helpers/plugin.ts +++ b/hardhat/hardhat-oz-contracts-helpers/plugin.ts @@ -9,7 +9,6 @@ const hardhatOzContractsHelpers: HardhatPlugin = { network: () => import('./hook-handlers/network.js'), }, dependencies: () => [import('@nomicfoundation/hardhat-ethers')], - npmPackage: 'hardhat-oz-contracts-helpers', }; export default hardhatOzContractsHelpers; From eadb1bd9f6dbdc6e6ca30eae3b42a394ba43fb73 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 30 Jan 2026 17:10:54 -0300 Subject: [PATCH 17/91] fix hardhat error for local plugins --- hardhat/hardhat-exposed/plugin.ts | 2 +- hardhat/hardhat-oz-contracts-helpers/plugin.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index 9bd37321e0c..0ee6a55cbf3 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -9,7 +9,7 @@ const hardhatExposedPlugin: HardhatPlugin = { config: () => import('./hook-handlers/config.js'), hre: () => import('./hook-handlers/hre.js'), }, - npmPackage: 'hardhat-exposed', + npmPackage: null, }; export default hardhatExposedPlugin; diff --git a/hardhat/hardhat-oz-contracts-helpers/plugin.ts b/hardhat/hardhat-oz-contracts-helpers/plugin.ts index 3b213593b60..c4ca44eed24 100644 --- a/hardhat/hardhat-oz-contracts-helpers/plugin.ts +++ b/hardhat/hardhat-oz-contracts-helpers/plugin.ts @@ -9,7 +9,7 @@ const hardhatOzContractsHelpers: HardhatPlugin = { network: () => import('./hook-handlers/network.js'), }, dependencies: () => [import('@nomicfoundation/hardhat-ethers')], - npmPackage: 'hardhat-oz-contracts-helpers', + npmPackage: null, }; export default hardhatOzContractsHelpers; From 033bced4d1c4f68c7b626fc7b47e3fc6457d41b5 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 30 Jan 2026 17:11:56 -0300 Subject: [PATCH 18/91] use .ts imports --- hardhat.config.ts | 4 ++-- hardhat/hardhat-exposed/core/index.ts | 2 +- hardhat/hardhat-exposed/hook-handlers/hre.ts | 2 +- hardhat/hardhat-exposed/plugin.ts | 8 ++++---- .../hardhat-oz-contracts-helpers/hook-handlers/network.ts | 8 ++++---- hardhat/hardhat-oz-contracts-helpers/plugin.ts | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index ed58ae3d2d3..3480c2d64cc 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -7,8 +7,8 @@ import hardhatIgnoreWarnings from 'hardhat-ignore-warnings'; import hardhatMocha from '@nomicfoundation/hardhat-mocha'; import hardhatNetworkHelpers from '@nomicfoundation/hardhat-network-helpers'; import hardhatPredeploy from 'hardhat-predeploy'; -import hardhatExposed from './hardhat/hardhat-exposed/plugin.js'; -import hardhatOzContractsHelpers from './hardhat/hardhat-oz-contracts-helpers/plugin.js'; +import hardhatExposed from './hardhat/hardhat-exposed/plugin.ts'; +import hardhatOzContractsHelpers from './hardhat/hardhat-oz-contracts-helpers/plugin.ts'; import './hardhat/async-test-sanity.js'; // Parameters diff --git a/hardhat/hardhat-exposed/core/index.ts b/hardhat/hardhat-exposed/core/index.ts index e1f9725a1f5..264eb980458 100644 --- a/hardhat/hardhat-exposed/core/index.ts +++ b/hardhat/hardhat-exposed/core/index.ts @@ -22,7 +22,7 @@ import type { ASTDereferencer } from 'solidity-ast/utils.js'; import { findAll, astDereferencer } from 'solidity-ast/utils.js'; // Exposed code generation -import { formatLines, Lines, spaceBetween } from './format-lines'; +import { formatLines, type Lines, spaceBetween } from './format-lines.ts'; type ContractFilter = (node: ContractDefinition) => boolean; type ResolvedFile = { fsPath: string; content: string }; diff --git a/hardhat/hardhat-exposed/hook-handlers/hre.ts b/hardhat/hardhat-exposed/hook-handlers/hre.ts index a21c138e2b6..035bf6ac02f 100644 --- a/hardhat/hardhat-exposed/hook-handlers/hre.ts +++ b/hardhat/hardhat-exposed/hook-handlers/hre.ts @@ -5,7 +5,7 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import type { BuildOptions, SolidityBuildSystem } from 'hardhat/types/solidity'; import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; -import { getExposed, writeExposed } from '../core'; +import { getExposed, writeExposed } from '../core/index.ts'; const overrideBuild = (context: HookContext, runSuper: SolidityBuildSystem['build']) => diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index 0ee6a55cbf3..5c3651a9822 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -1,13 +1,13 @@ import type { HardhatPlugin } from 'hardhat/types/plugins'; -import type {} from './type-extensions.js'; +import type {} from './type-extensions.ts'; const hardhatExposedPlugin: HardhatPlugin = { id: 'hardhat-exposed', hookHandlers: { - clean: () => import('./hook-handlers/clean.js'), - config: () => import('./hook-handlers/config.js'), - hre: () => import('./hook-handlers/hre.js'), + clean: () => import('./hook-handlers/clean.ts'), + config: () => import('./hook-handlers/config.ts'), + hre: () => import('./hook-handlers/hre.ts'), }, npmPackage: null, }; diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts index c6fb0b2c0de..ecde42f585d 100644 --- a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts @@ -1,10 +1,10 @@ import type { HookContext, NetworkHooks } from 'hardhat/types/hooks'; import type { ChainType, NetworkConnection } from 'hardhat/types/network'; -import { impersonate } from '../../../test/helpers/account.js'; -import { getLocalChain } from '../../../test/helpers/chains.js'; -import { getSlot, getAddressInSlot, setSlot } from '../../../test/helpers/storage.js'; -import { clock, clockFromReceipt, increaseBy, increaseTo, duration } from '../../../test/helpers/time.js'; +import { impersonate } from '../../../test/helpers/account.ts'; +import { getLocalChain } from '../../../test/helpers/chains.ts'; +import { getSlot, getAddressInSlot, setSlot } from '../../../test/helpers/storage.ts'; +import { clock, clockFromReceipt, increaseBy, increaseTo, duration } from '../../../test/helpers/time.ts'; export default async (): Promise> => ({ newConnection: async ( diff --git a/hardhat/hardhat-oz-contracts-helpers/plugin.ts b/hardhat/hardhat-oz-contracts-helpers/plugin.ts index c4ca44eed24..27657e90216 100644 --- a/hardhat/hardhat-oz-contracts-helpers/plugin.ts +++ b/hardhat/hardhat-oz-contracts-helpers/plugin.ts @@ -1,12 +1,12 @@ import type { HardhatPlugin } from 'hardhat/types/plugins'; -import type {} from './type-extensions.js'; +import type {} from './type-extensions.ts'; const hardhatOzContractsHelpers: HardhatPlugin = { id: 'hardhat-oz-contracts-helpers', hookHandlers: { - hre: () => import('./hook-handlers/hre.js'), - network: () => import('./hook-handlers/network.js'), + hre: () => import('./hook-handlers/hre.ts'), + network: () => import('./hook-handlers/network.ts'), }, dependencies: () => [import('@nomicfoundation/hardhat-ethers')], npmPackage: null, From 9e6dfd75002f5ff0eb84647a929a747725516775 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 30 Jan 2026 17:12:36 -0300 Subject: [PATCH 19/91] add --paths to transpiler --- scripts/upgradeable/transpile.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/upgradeable/transpile.sh b/scripts/upgradeable/transpile.sh index 100153e1e3f..3bd6b26083f 100644 --- a/scripts/upgradeable/transpile.sh +++ b/scripts/upgradeable/transpile.sh @@ -20,6 +20,12 @@ if [ $build_info_num -ne 1 ]; then echo "found $build_info_num relevant build info files but expected just 1" exit 1 fi +paths="$(node < Date: Fri, 30 Jan 2026 17:20:26 -0300 Subject: [PATCH 20/91] fix heredoc --- scripts/upgradeable/transpile.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/upgradeable/transpile.sh b/scripts/upgradeable/transpile.sh index 3bd6b26083f..5bf5fce58f0 100644 --- a/scripts/upgradeable/transpile.sh +++ b/scripts/upgradeable/transpile.sh @@ -20,12 +20,14 @@ if [ $build_info_num -ne 1 ]; then echo "found $build_info_num relevant build info files but expected just 1" exit 1 fi -paths="$(node < Date: Fri, 30 Jan 2026 17:26:53 -0300 Subject: [PATCH 21/91] add project prefix --- scripts/upgradeable/transpile.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/upgradeable/transpile.sh b/scripts/upgradeable/transpile.sh index 5bf5fce58f0..af8eb0b0fd1 100644 --- a/scripts/upgradeable/transpile.sh +++ b/scripts/upgradeable/transpile.sh @@ -40,17 +40,17 @@ EOF npx @openzeppelin/upgrade-safe-transpiler -D \ --paths "$paths" \ -b "$build_info" \ - -i contracts/proxy/utils/Initializable.sol \ - -x 'contracts-exposed/**/*' \ - -x 'contracts/mocks/**/*Proxy*.sol' \ - -x 'contracts/proxy/**/*Proxy*.sol' \ - -x 'contracts/proxy/beacon/UpgradeableBeacon.sol' \ - -p 'contracts/access/manager/AccessManager.sol' \ - -p 'contracts/finance/VestingWallet.sol' \ - -p 'contracts/governance/TimelockController.sol' \ - -p 'contracts/metatx/ERC2771Forwarder.sol' \ + -i project/contracts/proxy/utils/Initializable.sol \ + -x 'project/contracts-exposed/**/*' \ + -x 'project/contracts/mocks/**/*Proxy*.sol' \ + -x 'project/contracts/proxy/**/*Proxy*.sol' \ + -x 'project/contracts/proxy/beacon/UpgradeableBeacon.sol' \ + -p 'project/contracts/access/manager/AccessManager.sol' \ + -p 'project/contracts/finance/VestingWallet.sol' \ + -p 'project/contracts/governance/TimelockController.sol' \ + -p 'project/contracts/metatx/ERC2771Forwarder.sol' \ -n \ - -N 'contracts/mocks/**/*' \ + -N 'project/contracts/mocks/**/*' \ -q '@openzeppelin/' # create alias to Initializable and UUPSUpgradeable From 09b2af1d929d07ffeb0acc1e483a07c9836234f2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 30 Jan 2026 21:38:24 +0100 Subject: [PATCH 22/91] Hardhat transpile action --- hardhat.config.ts | 2 + hardhat/hardhat-transpiler/plugin.ts | 23 +++++ hardhat/hardhat-transpiler/tasks/transpile.ts | 91 +++++++++++++++++++ scripts/upgradeable/transpile.sh | 43 +-------- 4 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 hardhat/hardhat-transpiler/plugin.ts create mode 100644 hardhat/hardhat-transpiler/tasks/transpile.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index 3480c2d64cc..965b7ad65f5 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -8,6 +8,7 @@ import hardhatMocha from '@nomicfoundation/hardhat-mocha'; import hardhatNetworkHelpers from '@nomicfoundation/hardhat-network-helpers'; import hardhatPredeploy from 'hardhat-predeploy'; import hardhatExposed from './hardhat/hardhat-exposed/plugin.ts'; +import hardhatTranspiler from './hardhat/hardhat-transpiler/plugin.ts'; import hardhatOzContractsHelpers from './hardhat/hardhat-oz-contracts-helpers/plugin.ts'; import './hardhat/async-test-sanity.js'; @@ -36,6 +37,7 @@ export default defineConfig({ hardhatPredeploy, // Local plugins hardhatExposed, + hardhatTranspiler, hardhatOzContractsHelpers, ], paths: { diff --git a/hardhat/hardhat-transpiler/plugin.ts b/hardhat/hardhat-transpiler/plugin.ts new file mode 100644 index 00000000000..0c1635e4509 --- /dev/null +++ b/hardhat/hardhat-transpiler/plugin.ts @@ -0,0 +1,23 @@ +import type { HardhatPlugin } from 'hardhat/types/plugins'; +import { ArgumentType } from 'hardhat/types/arguments'; +import { task } from 'hardhat/config'; + +// import type {} from './type-extensions.ts'; + +const hardhatTranspilerPlugin: HardhatPlugin = { + id: 'hardhat-transpiler', + tasks: [ + task('transpile', 'Transpile contracts.') + // .addOption({ + // name: "who", + // description: "Who is receiving the greeting.", + // type: ArgumentType.STRING, + // defaultValue: "Hardhat", + // }) + .setAction(() => import('./tasks/transpile.ts')) + .build(), + ], + npmPackage: null, +}; + +export default hardhatTranspilerPlugin; diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts new file mode 100644 index 00000000000..5b02c650415 --- /dev/null +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -0,0 +1,91 @@ +import assert from 'node:assert'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; + +import { transpile } from '@openzeppelin/upgrade-safe-transpiler/dist/index.js'; +import { findAlreadyInitializable } from '@openzeppelin/upgrade-safe-transpiler/dist/find-already-initializable.js'; + +interface TranspileOptions { + initializablePath?: string; + exclude?: string[]; + publicInitializers?: string[]; + solcVersion?: string; + skipWithInit?: boolean; + namespaced?: boolean; + namespaceExclude?: string[]; + peerProject?: string; +} + +const options: TranspileOptions = { + initializablePath: 'project/contracts/proxy/utils/Initializable.sol', + exclude: [ + 'project/contracts-exposed/**/*', + 'project/contracts/mocks/**/*Proxy*.sol', + 'project/contracts/proxy/**/*Proxy*.sol', + 'project/contracts/proxy/beacon/UpgradeableBeacon.sol', + ], + publicInitializers: [ + 'project/contracts/access/manager/AccessManager.sol', + 'project/contracts/finance/VestingWallet.sol', + 'project/contracts/governance/TimelockController.sol', + 'project/contracts/metatx/ERC2771Forwarder.sol', + ], + namespaced: true, + namespaceExclude: ['project/contracts/mocks/**/*'], + peerProject: '@openzeppelin/', +}; + +export default async function (taskArguments: TranspileOptions = {}, hre: HardhatRuntimeEnvironment) { + // TODO Use taskArguments to override options + + // Clean compilation (should result in a single build info) + await hre.tasks.getTask('clean').run(); + await hre.tasks.getTask('compile').run({ noTests: true }); + + const [buildId, ...rest] = await hre.artifacts.getAllBuildInfoIds(); + assert(buildId && rest.length == 0, 'No build info ids found'); + + // Load build info + const { input, solcVersion } = await hre.artifacts + .getBuildInfoPath(buildId) + .then(file => fs.readFile(file!, 'utf-8')) + .then(JSON.parse); + const { output } = await hre.artifacts + .getBuildInfoOutputPath(buildId) + .then(file => fs.readFile(file!, 'utf-8')) + .then(JSON.parse); + + // Run transpilation on the first source folder + const transpiled = await transpile( + input, + output, + { root: hre.config.paths.root, sources: hre.config.paths.sources.solidity.at(0)! }, + { ...options, solcVersion }, + ); + + // Write transpiled files to disk + await Promise.all( + transpiled.map(async t => { + const outputPath = path.join(hre.config.paths.root, t.path.replace(/^project\//, '')); + await fs.mkdir(path.dirname(outputPath), { recursive: true }); + await fs.writeFile(outputPath, t.source); + }), + ); + + // Delete originals files + const keep = new Set( + ([] as string[]) + .concat( + transpiled.map(t => t.path), + findAlreadyInitializable(output, options.initializablePath), + ) + .map(p => path.join(hre.config.paths.root, p.replace(/^project\//, ''))), + ); + await Promise.all( + Object.keys(output.sources) + .map(s => path.join(hre.config.paths.root, s.replace(/^project\//, ''))) + .filter(p => !keep.has(p)) + .map(p => fs.unlink(p).catch(() => {})), + ); +} diff --git a/scripts/upgradeable/transpile.sh b/scripts/upgradeable/transpile.sh index af8eb0b0fd1..a4c85773e76 100644 --- a/scripts/upgradeable/transpile.sh +++ b/scripts/upgradeable/transpile.sh @@ -10,48 +10,7 @@ bash "$DIRNAME/patch-apply.sh" sed -i'' -e "s//$VERSION/g" "contracts/package.json" git add contracts/package.json -npm run clean -npm run compile -- --no-tests - -build_info=($(jq -r '.input.sources | keys | if any(test("^contracts/mocks/.*\\bunreachable\\b")) then empty else input_filename end' artifacts/build-info/!(*.output).json)) -build_info_num=${#build_info[@]} - -if [ $build_info_num -ne 1 ]; then - echo "found $build_info_num relevant build info files but expected just 1" - exit 1 -fi - -paths="$(node <<'EOF' - import { config } from "hardhat"; - const { paths } = config; - paths.sources = paths.sources.solidity[0]; - console.log(JSON.stringify(paths)) -EOF -)" - -# -D: delete original and excluded files -# -b: use this build info file -# -i: use included Initializable -# -x: exclude some proxy-related contracts -# -p: emit public initializer -# -n: use namespaces -# -N: exclude from namespaces transformation -# -q: partial transpilation using @openzeppelin/contracts as peer project -npx @openzeppelin/upgrade-safe-transpiler -D \ - --paths "$paths" \ - -b "$build_info" \ - -i project/contracts/proxy/utils/Initializable.sol \ - -x 'project/contracts-exposed/**/*' \ - -x 'project/contracts/mocks/**/*Proxy*.sol' \ - -x 'project/contracts/proxy/**/*Proxy*.sol' \ - -x 'project/contracts/proxy/beacon/UpgradeableBeacon.sol' \ - -p 'project/contracts/access/manager/AccessManager.sol' \ - -p 'project/contracts/finance/VestingWallet.sol' \ - -p 'project/contracts/governance/TimelockController.sol' \ - -p 'project/contracts/metatx/ERC2771Forwarder.sol' \ - -n \ - -N 'project/contracts/mocks/**/*' \ - -q '@openzeppelin/' +npx hardhat transpile # create alias to Initializable and UUPSUpgradeable cp $DIRNAME/alias/*.sol contracts/proxy/utils/. From 3c2ecd3d8e702a67a33d727f498c899f52a0c3bb Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Sat, 31 Jan 2026 23:48:35 +0100 Subject: [PATCH 23/91] Use a JSON file for transpiler settings --- hardhat/hardhat-transpiler/plugin.ts | 14 ++-- hardhat/hardhat-transpiler/tasks/transpile.ts | 72 +++++++++---------- scripts/upgradeable/transpile.config.json | 19 +++++ scripts/upgradeable/transpile.sh | 4 +- 4 files changed, 64 insertions(+), 45 deletions(-) create mode 100644 scripts/upgradeable/transpile.config.json diff --git a/hardhat/hardhat-transpiler/plugin.ts b/hardhat/hardhat-transpiler/plugin.ts index 0c1635e4509..7fda8b05b43 100644 --- a/hardhat/hardhat-transpiler/plugin.ts +++ b/hardhat/hardhat-transpiler/plugin.ts @@ -2,18 +2,16 @@ import type { HardhatPlugin } from 'hardhat/types/plugins'; import { ArgumentType } from 'hardhat/types/arguments'; import { task } from 'hardhat/config'; -// import type {} from './type-extensions.ts'; - const hardhatTranspilerPlugin: HardhatPlugin = { id: 'hardhat-transpiler', tasks: [ task('transpile', 'Transpile contracts.') - // .addOption({ - // name: "who", - // description: "Who is receiving the greeting.", - // type: ArgumentType.STRING, - // defaultValue: "Hardhat", - // }) + .addOption({ + name: 'settings', + description: 'Path to the transpiler config file.', + type: ArgumentType.STRING_WITHOUT_DEFAULT, + defaultValue: undefined, + }) .setAction(() => import('./tasks/transpile.ts')) .build(), ], diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index 5b02c650415..78fa74e80bf 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -8,36 +8,31 @@ import { findAlreadyInitializable } from '@openzeppelin/upgrade-safe-transpiler/ interface TranspileOptions { initializablePath?: string; + deleteOriginals?: boolean; + skipWithInit?: boolean; exclude?: string[]; publicInitializers?: string[]; - solcVersion?: string; - skipWithInit?: boolean; namespaced?: boolean; namespaceExclude?: string[]; peerProject?: string; } -const options: TranspileOptions = { - initializablePath: 'project/contracts/proxy/utils/Initializable.sol', - exclude: [ - 'project/contracts-exposed/**/*', - 'project/contracts/mocks/**/*Proxy*.sol', - 'project/contracts/proxy/**/*Proxy*.sol', - 'project/contracts/proxy/beacon/UpgradeableBeacon.sol', - ], - publicInitializers: [ - 'project/contracts/access/manager/AccessManager.sol', - 'project/contracts/finance/VestingWallet.sol', - 'project/contracts/governance/TimelockController.sol', - 'project/contracts/metatx/ERC2771Forwarder.sol', - ], - namespaced: true, - namespaceExclude: ['project/contracts/mocks/**/*'], - peerProject: '@openzeppelin/', -}; +async function loadSettings(hre: HardhatRuntimeEnvironment, settings: string): Promise { + const makeProjectPath = (p: string) => + path.join('project', path.isAbsolute(p) ? path.relative(hre.config.paths.root, p) : p); + + const options: TranspileOptions = await fs.readFile(settings, 'utf-8').then(JSON.parse); + options.initializablePath = options.initializablePath && makeProjectPath(options.initializablePath); + options.exclude = options.exclude?.map(makeProjectPath); + options.publicInitializers = options.publicInitializers?.map(makeProjectPath); + options.namespaceExclude = options.namespaceExclude?.map(makeProjectPath); + + return options; +} -export default async function (taskArguments: TranspileOptions = {}, hre: HardhatRuntimeEnvironment) { - // TODO Use taskArguments to override options +export default async function ({ settings }: { settings?: string }, hre: HardhatRuntimeEnvironment) { + assert(settings, 'Transpile settings file must be provided'); + const options = await loadSettings(hre, settings); // Clean compilation (should result in a single build info) await hre.tasks.getTask('clean').run(); @@ -74,18 +69,23 @@ export default async function (taskArguments: TranspileOptions = {}, hre: Hardha ); // Delete originals files - const keep = new Set( - ([] as string[]) - .concat( - transpiled.map(t => t.path), - findAlreadyInitializable(output, options.initializablePath), - ) - .map(p => path.join(hre.config.paths.root, p.replace(/^project\//, ''))), - ); - await Promise.all( - Object.keys(output.sources) - .map(s => path.join(hre.config.paths.root, s.replace(/^project\//, ''))) - .filter(p => !keep.has(p)) - .map(p => fs.unlink(p).catch(() => {})), - ); + if (options.deleteOriginals) { + const keep = new Set( + ([] as string[]) + .concat( + transpiled.map(t => t.path), + findAlreadyInitializable(output, options.initializablePath), + ) + .map(p => path.join(hre.config.paths.root, p.replace(/^project\//, ''))), + ); + if (options.initializablePath && options.peerProject === undefined) { + keep.add(path.join(hre.config.paths.root, options.initializablePath.replace(/^project\//, ''))); + } + await Promise.all( + Object.keys(output.sources) + .map(s => path.join(hre.config.paths.root, s.replace(/^project\//, ''))) + .filter(p => !keep.has(p)) + .map(p => fs.unlink(p).catch(() => {})), + ); + } } diff --git a/scripts/upgradeable/transpile.config.json b/scripts/upgradeable/transpile.config.json new file mode 100644 index 00000000000..db77a1a2985 --- /dev/null +++ b/scripts/upgradeable/transpile.config.json @@ -0,0 +1,19 @@ +{ + "initializablePath": "contracts/proxy/utils/Initializable.sol", + "deleteOriginals": true, + "exclude": [ + "contracts-exposed/**/*", + "contracts/mocks/**/*Proxy*.sol", + "contracts/proxy/**/*Proxy*.sol", + "contracts/proxy/beacon/UpgradeableBeacon.sol" + ], + "publicInitializers": [ + "contracts/access/manager/AccessManager.sol", + "contracts/finance/VestingWallet.sol", + "contracts/governance/TimelockController.sol", + "contracts/metatx/ERC2771Forwarder.sol" + ], + "namespaced": true, + "namespaceExclude": ["contracts/mocks/**/*"], + "peerProject": "@openzeppelin/" +} diff --git a/scripts/upgradeable/transpile.sh b/scripts/upgradeable/transpile.sh index a4c85773e76..fa2f2c82de4 100644 --- a/scripts/upgradeable/transpile.sh +++ b/scripts/upgradeable/transpile.sh @@ -6,11 +6,13 @@ shopt -s extglob VERSION="$(jq -r .version contracts/package.json)" DIRNAME="$(dirname -- "${BASH_SOURCE[0]}")" +# apply patches to the codebase bash "$DIRNAME/patch-apply.sh" sed -i'' -e "s//$VERSION/g" "contracts/package.json" git add contracts/package.json -npx hardhat transpile +# run the transpiler +npx hardhat transpile --settings $DIRNAME/transpile.config.json # create alias to Initializable and UUPSUpgradeable cp $DIRNAME/alias/*.sol contracts/proxy/utils/. From 0471363ce13e0d24141ee1e81dabc527dd3af6bd Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 2 Feb 2026 17:57:54 +0100 Subject: [PATCH 24/91] hardhat 3.1.6 + use build hook for exposed --- .../hardhat-exposed/hook-handlers/config.ts | 61 +++++--- hardhat/hardhat-exposed/hook-handlers/hre.ts | 82 ---------- .../hardhat-exposed/hook-handlers/solidity.ts | 76 +++++++++ .../{core/index.ts => internal/expose.ts} | 146 ++++++++---------- .../{core => internal}/format-lines.ts | 2 +- .../{core => internal}/types.ts | 1 - hardhat/hardhat-exposed/plugin.ts | 3 +- hardhat/hardhat-exposed/type-extensions.ts | 2 +- .../hardhat-oz-contracts-helpers/plugin.ts | 1 - hardhat/hardhat-transpiler/plugin.ts | 1 - package-lock.json | 16 +- package.json | 2 +- 12 files changed, 195 insertions(+), 198 deletions(-) delete mode 100644 hardhat/hardhat-exposed/hook-handlers/hre.ts create mode 100644 hardhat/hardhat-exposed/hook-handlers/solidity.ts rename hardhat/hardhat-exposed/{core/index.ts => internal/expose.ts} (88%) rename hardhat/hardhat-exposed/{core => internal}/format-lines.ts (91%) rename hardhat/hardhat-exposed/{core => internal}/types.ts (92%) diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts index 473ba596b38..dffa8f909f8 100644 --- a/hardhat/hardhat-exposed/hook-handlers/config.ts +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -1,26 +1,47 @@ -import path from 'path'; -import type { HardhatConfig, HardhatUserConfig, ConfigurationVariableResolver } from 'hardhat/types/config'; +import path from 'node:path'; + import type { ConfigHooks } from 'hardhat/types/hooks'; export default async (): Promise> => ({ - resolveUserConfig: ( - userConfig: HardhatUserConfig, - resolveConfigurationVariable: ConfigurationVariableResolver, - next: ( - nextUserConfig: HardhatUserConfig, - nextResolveConfigurationVariable: ConfigurationVariableResolver, - ) => Promise, - ): Promise => - next(userConfig, resolveConfigurationVariable).then((resolvedConfig: HardhatConfig) => { - resolvedConfig.exposed = { - ...userConfig.exposed, - exclude: userConfig.exposed?.exclude ?? [], - include: userConfig.exposed?.include ?? ['**/*'], - outDir: userConfig.exposed?.outDir ?? 'contracts-exposed', + validateUserConfig: async userConfig => { + const results: Array<{ path: string[]; message: string }> = []; + + if (userConfig.exposed?.prefix !== undefined && typeof userConfig.exposed?.prefix !== 'string') { + results.push({ path: ['exposed', 'prefix'], message: 'Expected an optional string.' }); + } + if ( + userConfig.exposed?.exclude !== undefined && + (!Array.isArray(userConfig.exposed.exclude) || userConfig.exposed.exclude.some(e => typeof e !== 'string')) + ) { + results.push({ path: ['exposed', 'exclude'], message: 'Expected an optional string[].' }); + } + if ( + userConfig.exposed?.include !== undefined && + (!Array.isArray(userConfig.exposed.include) || userConfig.exposed.include.some(e => typeof e !== 'string')) + ) { + results.push({ path: ['exposed', 'include'], message: 'Expected an optional string[].' }); + } + if (userConfig.exposed?.outDir !== undefined && typeof userConfig.exposed?.outDir !== 'string') { + results.push({ path: ['exposed', 'outDir'], message: 'Expected an optional string.' }); + } + if (userConfig.exposed?.initializers !== undefined && typeof userConfig.exposed?.initializers !== 'boolean') { + results.push({ path: ['exposed', 'initializers'], message: 'Expected an optional boolean.' }); + } + return results; + }, + + resolveUserConfig: (userConfig, resolveConfigurationVariable, next) => + next(userConfig, resolveConfigurationVariable).then(partiallyResolvedConfig => { + const makeAbsolutePath = (p: string) => + path.isAbsolute(p) ? p : path.resolve(partiallyResolvedConfig.paths.root, p); + return { + ...partiallyResolvedConfig, + exposed: { + ...userConfig.exposed, + exclude: (userConfig.exposed?.exclude ?? []).map(makeAbsolutePath), + include: (userConfig.exposed?.include ?? ['**/*']).map(makeAbsolutePath), + outDir: makeAbsolutePath(userConfig.exposed?.outDir ?? 'contracts-exposed'), + }, }; - // Add exposed contracts path to Solidity sources, and exclude them from further processing - resolvedConfig.exposed.exclude.push(path.join(resolvedConfig.exposed.outDir, '**', '*')); - resolvedConfig.paths.sources.solidity.push(path.join(resolvedConfig.paths.root, resolvedConfig.exposed.outDir)); - return resolvedConfig; }), }); diff --git a/hardhat/hardhat-exposed/hook-handlers/hre.ts b/hardhat/hardhat-exposed/hook-handlers/hre.ts deleted file mode 100644 index 035bf6ac02f..00000000000 --- a/hardhat/hardhat-exposed/hook-handlers/hre.ts +++ /dev/null @@ -1,82 +0,0 @@ -import micromatch from 'micromatch'; - -import type { HardhatRuntimeEnvironmentHooks, HookContext } from 'hardhat/types/hooks'; -import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; -import type { BuildOptions, SolidityBuildSystem } from 'hardhat/types/solidity'; -import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; - -import { getExposed, writeExposed } from '../core/index.ts'; - -const overrideBuild = - (context: HookContext, runSuper: SolidityBuildSystem['build']) => - async (rootFilePaths: string[], options?: BuildOptions) => { - switch (options?.scope) { - case 'contracts': - const compilationJobsResult = await context.solidity.getCompilationJobs(rootFilePaths, options); - - if ('reason' in compilationJobsResult) break; - - // Determine which files to include - const include = (sourceName: string): boolean => { - const file = sourceName.replace(/^project\//g, ''); - return ( - compilationJobsResult.compilationJobsPerFile.has(file) && - context.config.exposed.include.some((p: string) => micromatch.isMatch(file, p)) && - !context.config.exposed.exclude.some((p: string) => micromatch.isMatch(file, p)) - ); - }; - - const spinner = createSpinner({ - text: `Generation of exposed contracts...`, - enabled: true, - }); - spinner.start(); - - // Expose selected contracts from each compilation job - const processed: Set = new Set(); - for (const job of compilationJobsResult.compilationJobsPerFile.values()) { - const key = await job.getBuildId(); - - // Avoid processing the same job multiple times - if (processed.has(key)) continue; - processed.add(key); - - // Override job settings - const oldEnabled = job.solcConfig.settings.optimizer.enabled; - const oldOutputSelection = job.solcConfig.settings.outputSelection; - job.solcConfig.settings.optimizer.enabled = false; - job.solcConfig.settings.outputSelection = { '*': { '': ['ast'] } }; - - // Run precompilation - const { output } = await context.solidity.runCompilationJob(job, options); - - // Restore job settings - job.solcConfig.settings.optimizer.enabled = oldEnabled; - job.solcConfig.settings.outputSelection = oldOutputSelection; - - // Generate and write exposed artifacts - const exposed = await getExposed(output, include, context.config); - await writeExposed(exposed); - - // Add exposed files to rootFilePaths for actual compilation - rootFilePaths.push(...exposed.keys()); - } - - // Remove duplicates - rootFilePaths = rootFilePaths.filter((value, index, array) => array.indexOf(value) === index); - - spinner.stop(); - break; - - case 'tests': - break; - } - - return await runSuper(rootFilePaths, options); - }; - -export default async (): Promise> => ({ - created: async (context: HookContext, hre: HardhatRuntimeEnvironment): Promise => { - hre.solidity.build = overrideBuild(context, hre.solidity.build.bind(hre.solidity)); - }, -}); diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts new file mode 100644 index 00000000000..bf7d3737f36 --- /dev/null +++ b/hardhat/hardhat-exposed/hook-handlers/solidity.ts @@ -0,0 +1,76 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import type { SolidityHooks } from 'hardhat/types/hooks'; +import { FileBuildResultType } from 'hardhat/types/solidity'; + +import { getExposedPath, generateExposedContracts } from '../internal/expose.ts'; + +export default async (): Promise> => ({ + build: async (context, rootPaths, options, next) => { + const includes = (rootPath: string) => + context.config.exposed.include.some(p => path.matchesGlob(rootPath, p)) && + !context.config.exposed.exclude.some(p => path.matchesGlob(rootPath, p)) && + !rootPath.startsWith(context.config.exposed.outDir) && + !rootPath.startsWith('npm:'); + + // Only apply expose logic when compiling contracts (skip for tests) + if (options?.scope === 'contracts' && rootPaths.some(includes)) { + // 1. Build the original contracts + const results = await next(context, rootPaths, options); + + // Return errors instead of ignoring! + if ('reason' in results) return results; + + // 2. Collect the files that need their exposed contracts regenerated + // - BUILD_SUCCESS (newly compiled) + // - CACHE_HIT with missing exposed file + const exposedFilesToRegenerate: Array<{ rootPath: string; buildId: string }> = []; + for (const [rootPath, result] of results) { + if (!includes(rootPath)) continue; + switch (result.type) { + case FileBuildResultType.BUILD_SUCCESS: + exposedFilesToRegenerate.push({ + rootPath, + buildId: await result.compilationJob.getBuildId(), + }); + break; + case FileBuildResultType.CACHE_HIT: + if (!fs.existsSync(getExposedPath(context, rootPath))) { + exposedFilesToRegenerate.push({ + rootPath, + buildId: result.buildId, + }); + } + break; + } + } + + // 3. Generate exposed contracts for the files that need it + if (exposedFilesToRegenerate.length > 0) { + await generateExposedContracts(context, exposedFilesToRegenerate); + } + + // 4. Build all exposed contracts + const exposedResults = await context.solidity.build( + rootPaths + .filter(includes) + .map(rootPath => getExposedPath(context, rootPath)) + .filter(fs.existsSync), + options, + ); + + // Return errors instead of ignoring! + if ('reason' in exposedResults) return exposedResults; + + // Merge ALL results + for (const [filePath, result] of exposedResults) { + results.set(filePath, result); + } + + return results; + } else { + return next(context, rootPaths, options); + } + }, +}); diff --git a/hardhat/hardhat-exposed/core/index.ts b/hardhat/hardhat-exposed/internal/expose.ts similarity index 88% rename from hardhat/hardhat-exposed/core/index.ts rename to hardhat/hardhat-exposed/internal/expose.ts index 264eb980458..7cc1bc39e91 100644 --- a/hardhat/hardhat-exposed/core/index.ts +++ b/hardhat/hardhat-exposed/internal/expose.ts @@ -1,11 +1,9 @@ -import assert from 'assert'; -import path from 'path'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; -import type { HardhatConfig } from 'hardhat/types/config'; -import type { CompilerOutput } from 'hardhat/types/solidity'; -import { ensureDir, remove, writeUtf8File } from '@nomicfoundation/hardhat-utils/fs'; - -import type {} from '../type-extensions'; +import type { HookContext } from 'hardhat/types/hooks'; +import { SolidityBuildInfoOutput } from 'hardhat/types/solidity'; // AST manipulation import type { @@ -22,101 +20,89 @@ import type { ASTDereferencer } from 'solidity-ast/utils.js'; import { findAll, astDereferencer } from 'solidity-ast/utils.js'; // Exposed code generation -import { formatLines, type Lines, spaceBetween } from './format-lines.ts'; - -type ContractFilter = (node: ContractDefinition) => boolean; -type ResolvedFile = { fsPath: string; content: string }; +import { formatLines, Lines, spaceBetween } from './format-lines.ts'; const exposedVersionPragma = '>=0.6.0'; const defaultPrefix = '$'; -// Tasks & Hooks -export async function writeExposed(exposed: Map) { - for (const [fsPath, content] of exposed.entries()) { - await ensureDir(path.dirname(fsPath)); - await writeUtf8File(fsPath, content); - } +export function getExposedPath(context: HookContext, rootFilePath: string): string { + return path.join(context.config.exposed.outDir, path.relative(context.config.paths.root, rootFilePath)); } -export function getExposed( - output: CompilerOutput, - include: (sourceName: string) => boolean, - config: HardhatConfig, -): Map { - const res = new Map(); - const deref = astDereferencer(output); - - for (const { ast } of Object.values(output.sources)) { - if (!include(ast.absolutePath)) { - continue; - } - - const exposedFile = getExposedFile(config, ast, deref); - if (exposedFile !== undefined) { - res.set(exposedFile.fsPath, exposedFile.content); - } - } +export async function generateExposedContracts( + context: HookContext, + rootFilesToRegenerate: Array<{ rootPath: string; buildId: string }>, +): Promise { + const generatedFiles: string[] = []; + + // Group root files by build ID + const rootFilesPathsByBuildId: { [buildId: string]: string[] } = {}; + rootFilesToRegenerate.forEach(({ rootPath, buildId }) => { + rootFilesPathsByBuildId[buildId] ??= []; + rootFilesPathsByBuildId[buildId].push(rootPath); + }); - return res; -} + // Process each build ID + for (const [buildId, rootFilePaths] of Object.entries(rootFilesPathsByBuildId)) { + // Get build info output path + const outputPath = await context.artifacts.getBuildInfoOutputPath(buildId); + if (outputPath === undefined) continue; + + // Build ast dereferencer + const { output } = JSON.parse(fs.readFileSync(outputPath, 'utf-8')) as SolidityBuildInfoOutput; + const deref = astDereferencer(output); + + // Generate exposed files for each root file in this build + for (const rootFilePath of rootFilePaths) { + const userSourceName = path.relative(context.config.paths.root, rootFilePath); + const inputSourceName = path.join('project', userSourceName); + const generatedFilePath = path.join(context.config.exposed.outDir, userSourceName); + + const { ast } = output.sources[inputSourceName]; + if (ast == undefined) continue; + + const relativizePath = ({ absolutePath }: SourceUnit) => + absolutePath.startsWith(path.join('npm', path.sep)) + ? path.relative('npm', absolutePath).replace(/@[a-zA-Z0-9\.-]+/, '') + : path.relative( + path.dirname(generatedFilePath), + path.join(context.config.paths.root, path.relative('project', absolutePath)), + ); -// Helpers -function getExposedPath(config: HardhatConfig) { - return path.join(config.paths.root, config.exposed.outDir); -} + const content = getExposedContent( + ast, + relativizePath, + deref, + context.config.exposed?.prefix, + context.config.exposed?.initializers, + ); -function getExposedFile( - config: HardhatConfig, - ast: SourceUnit, - deref: ASTDereferencer, - filter?: ContractFilter, -): ResolvedFile | undefined { - const exposedRootPath = getExposedPath(config); - const fsPath = path.join( - exposedRootPath, - ...(ast.absolutePath.startsWith('project/') - ? [path.relative('project', ast.absolutePath)] - : ['$_', ast.absolutePath]), - ); - const dirname = path.dirname(fsPath); - - const relativizePath = (p: string) => - (p.startsWith('project/') - ? path.relative(dirname, path.relative('project', p)) - : p.startsWith('npm/') - ? path.relative('npm', p).replace(/@[a-zA-Z0-9\.-]+/, '') - : p - ).replace(/\\/g, '/'); - - const content = getExposedContent( - ast, - relativizePath, - deref, - config.exposed?.initializers, - config.exposed?.prefix, - filter, - ); + if (content) { + fs.mkdirSync(path.dirname(generatedFilePath), { recursive: true }); + fs.writeFileSync(generatedFilePath, content); + // Add to list of generated files + generatedFiles.push(generatedFilePath); + } + } + } - return content === undefined ? undefined : { fsPath, content }; + return generatedFiles; } function getExposedContent( ast: SourceUnit, - relativizePath: (p: string) => string, + relativizePath: (source: SourceUnit) => string, deref: ASTDereferencer, - initializers = false, prefix = defaultPrefix, - filter?: ContractFilter, + initializers = false, ): string | undefined { if (prefix === '' || /^\d|[^0-9a-z_$]/i.test(prefix)) { throw new Error(`Prefix '${prefix}' is not valid`); } const contractPrefix = prefix.replace(/^./, c => c.toUpperCase()); - const imports = Array.from(getNeededImports(ast, deref), u => relativizePath(u.absolutePath)); - const contracts = [...findAll('ContractDefinition', ast)].filter( - c => filter?.(c) !== false && c.contractKind !== 'interface', - ); + const imports = Array.from(getNeededImports(ast, deref), u => relativizePath(u)); + const contracts = [...findAll('ContractDefinition', ast)].filter(c => c.contractKind !== 'interface'); return contracts.length === 0 ? undefined diff --git a/hardhat/hardhat-exposed/core/format-lines.ts b/hardhat/hardhat-exposed/internal/format-lines.ts similarity index 91% rename from hardhat/hardhat-exposed/core/format-lines.ts rename to hardhat/hardhat-exposed/internal/format-lines.ts index 52af7ab522b..c7418b1afe6 100644 --- a/hardhat/hardhat-exposed/core/format-lines.ts +++ b/hardhat/hardhat-exposed/internal/format-lines.ts @@ -1,6 +1,6 @@ export type Lines = string | typeof whitespace | Lines[]; -const whitespace = Symbol('whitespace'); +const whitespace: string = '\xa0'; //Symbol('whitespace'); export function formatLines(...lines: Lines[]): string { return [...indentEach(0, lines)].join('\n') + '\n'; diff --git a/hardhat/hardhat-exposed/core/types.ts b/hardhat/hardhat-exposed/internal/types.ts similarity index 92% rename from hardhat/hardhat-exposed/core/types.ts rename to hardhat/hardhat-exposed/internal/types.ts index d5732bc8b25..b8ac8ad7e6e 100644 --- a/hardhat/hardhat-exposed/core/types.ts +++ b/hardhat/hardhat-exposed/internal/types.ts @@ -4,7 +4,6 @@ export interface ExposedUserConfig { include?: string[]; outDir?: string; initializers?: boolean; - imports?: boolean; } export interface ExposedConfig extends ExposedUserConfig { diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index 5c3651a9822..e0c9f7970c3 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -7,9 +7,8 @@ const hardhatExposedPlugin: HardhatPlugin = { hookHandlers: { clean: () => import('./hook-handlers/clean.ts'), config: () => import('./hook-handlers/config.ts'), - hre: () => import('./hook-handlers/hre.ts'), + solidity: () => import('./hook-handlers/solidity.ts'), }, - npmPackage: null, }; export default hardhatExposedPlugin; diff --git a/hardhat/hardhat-exposed/type-extensions.ts b/hardhat/hardhat-exposed/type-extensions.ts index f614cd3c7e6..7c0321cc0f1 100644 --- a/hardhat/hardhat-exposed/type-extensions.ts +++ b/hardhat/hardhat-exposed/type-extensions.ts @@ -1,6 +1,6 @@ import 'hardhat/types/config'; -import { ExposedUserConfig, ExposedConfig } from './core/types'; +import { ExposedUserConfig, ExposedConfig } from './internal/types.ts'; declare module 'hardhat/types/config' { export interface HardhatUserConfig { diff --git a/hardhat/hardhat-oz-contracts-helpers/plugin.ts b/hardhat/hardhat-oz-contracts-helpers/plugin.ts index 27657e90216..f3fa25e6e07 100644 --- a/hardhat/hardhat-oz-contracts-helpers/plugin.ts +++ b/hardhat/hardhat-oz-contracts-helpers/plugin.ts @@ -9,7 +9,6 @@ const hardhatOzContractsHelpers: HardhatPlugin = { network: () => import('./hook-handlers/network.ts'), }, dependencies: () => [import('@nomicfoundation/hardhat-ethers')], - npmPackage: null, }; export default hardhatOzContractsHelpers; diff --git a/hardhat/hardhat-transpiler/plugin.ts b/hardhat/hardhat-transpiler/plugin.ts index 7fda8b05b43..7d0106ba375 100644 --- a/hardhat/hardhat-transpiler/plugin.ts +++ b/hardhat/hardhat-transpiler/plugin.ts @@ -15,7 +15,6 @@ const hardhatTranspilerPlugin: HardhatPlugin = { .setAction(() => import('./tasks/transpile.ts')) .build(), ], - npmPackage: null, }; export default hardhatTranspilerPlugin; diff --git a/package-lock.json b/package-lock.json index 8963cd6a00c..91f2416c685 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.5", + "hardhat": "^3.1.6", "hardhat-ignore-warnings": "^0.3.0-3", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", @@ -2175,9 +2175,9 @@ } }, "node_modules/@nomicfoundation/hardhat-vendored": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.0.tgz", - "integrity": "sha512-bzIOdG4iAuYSs9JSnaVOtH7qUKJ6W5+OtOiL8MlyFuLKYN2hjIisGO4pY5zR4N7xi/3RjfcnjVNz8tU0DPg2Cw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.1.tgz", + "integrity": "sha512-jBOAqmEAMJ8zdfiQmTLV+c0IaSyySqkDSJ9spTy8Ts/m/mO8w364TClyfn+p4ZpxBjyX4LMa3NfC402hoDtwCg==", "dev": true, "license": "MIT" }, @@ -4996,9 +4996,9 @@ } }, "node_modules/hardhat": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.5.tgz", - "integrity": "sha512-0Z0KI/m6wJYCMZgDK3QuVqR59lSa3aMu6QHKqnbIYXKu/phQ+YFKJZAY4zkUKX21ZjcrrRg25qLUzZw1bO6g/A==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.6.tgz", + "integrity": "sha512-72Wn3DaEByNtpsqI2oIZnsyxvaB2p2CC6mffQ18K6NgAgh6QzdpdMYLiIFtYBRDRY7U6BMxq7fj1ryMQxQvTBA==", "dev": true, "license": "MIT", "peer": true, @@ -5006,7 +5006,7 @@ "@nomicfoundation/edr": "0.12.0-next.22", "@nomicfoundation/hardhat-errors": "^3.0.6", "@nomicfoundation/hardhat-utils": "^3.0.6", - "@nomicfoundation/hardhat-vendored": "^3.0.0", + "@nomicfoundation/hardhat-vendored": "^3.0.1", "@nomicfoundation/hardhat-zod-utils": "^3.0.1", "@nomicfoundation/solidity-analyzer": "^0.1.1", "@sentry/core": "^9.4.0", diff --git a/package.json b/package.json index 77d405cf296..0b6f6c26825 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.5", + "hardhat": "^3.1.6", "hardhat-ignore-warnings": "^0.3.0-3", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", From de13540af23f906d874c8ed8bc481b7310ca14df Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 2 Feb 2026 18:25:27 +0100 Subject: [PATCH 25/91] Refactor transpile --- hardhat/hardhat-transpiler/tasks/transpile.ts | 100 ++++++++++-------- 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index 78fa74e80bf..c10df3c4797 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -34,58 +34,66 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat assert(settings, 'Transpile settings file must be provided'); const options = await loadSettings(hre, settings); - // Clean compilation (should result in a single build info) - await hre.tasks.getTask('clean').run(); - await hre.tasks.getTask('compile').run({ noTests: true }); - - const [buildId, ...rest] = await hre.artifacts.getAllBuildInfoIds(); - assert(buildId && rest.length == 0, 'No build info ids found'); - - // Load build info - const { input, solcVersion } = await hre.artifacts - .getBuildInfoPath(buildId) - .then(file => fs.readFile(file!, 'utf-8')) - .then(JSON.parse); - const { output } = await hre.artifacts - .getBuildInfoOutputPath(buildId) - .then(file => fs.readFile(file!, 'utf-8')) - .then(JSON.parse); - - // Run transpilation on the first source folder - const transpiled = await transpile( - input, - output, - { root: hre.config.paths.root, sources: hre.config.paths.sources.solidity.at(0)! }, - { ...options, solcVersion }, - ); - - // Write transpiled files to disk - await Promise.all( - transpiled.map(async t => { - const outputPath = path.join(hre.config.paths.root, t.path.replace(/^project\//, '')); - await fs.mkdir(path.dirname(outputPath), { recursive: true }); - await fs.writeFile(outputPath, t.source); - }), - ); - - // Delete originals files - if (options.deleteOriginals) { - const keep = new Set( + const { contractRootPaths } = await hre.tasks.getTask('compile').run({ noTests: true }); + const compilationJobs = await hre.solidity.getCompilationJobs(contractRootPaths); + assert('cacheHits' in compilationJobs, 'Compilation jobs not found'); + + const buildIds: Set = new Set(); + for (const { buildId } of compilationJobs.cacheHits.values()) { + buildIds.add(buildId); + } + + const keep: Set = new Set(); + const seen: Set = new Set(); + + for (const buildId of buildIds) { + // Load build info + const { input, solcVersion } = await hre.artifacts + .getBuildInfoPath(buildId) + .then(file => fs.readFile(file!, 'utf-8')) + .then(JSON.parse); + const { output } = await hre.artifacts + .getBuildInfoOutputPath(buildId) + .then(file => fs.readFile(file!, 'utf-8')) + .then(JSON.parse); + + // Run transpilation on the first source folder + const transpiled = await transpile( + input, + output, + { root: hre.config.paths.root, sources: hre.config.paths.sources.solidity.at(0)! }, + { ...options, solcVersion }, + ); + + // Write transpiled files to disk + await Promise.all( + transpiled.map(async t => { + const outputPath = path.join(hre.config.paths.root, t.path.replace(/^project\//, '')); + await fs.mkdir(path.dirname(outputPath), { recursive: true }); + await fs.writeFile(outputPath, t.source); + }), + ); + + // Delete originals files + if (options.deleteOriginals) { ([] as string[]) .concat( transpiled.map(t => t.path), findAlreadyInitializable(output, options.initializablePath), ) - .map(p => path.join(hre.config.paths.root, p.replace(/^project\//, ''))), - ); - if (options.initializablePath && options.peerProject === undefined) { - keep.add(path.join(hre.config.paths.root, options.initializablePath.replace(/^project\//, ''))); - } - await Promise.all( + .map(p => path.join(hre.config.paths.root, p.replace(/^project\//, ''))) + .forEach(p => keep.add(p)); + + if (options.initializablePath && options.peerProject === undefined) { + keep.add(path.join(hre.config.paths.root, options.initializablePath.replace(/^project\//, ''))); + } + Object.keys(output.sources) .map(s => path.join(hre.config.paths.root, s.replace(/^project\//, ''))) - .filter(p => !keep.has(p)) - .map(p => fs.unlink(p).catch(() => {})), - ); + .forEach(p => seen.add(p)); + } } + + // Perform delete + await Promise.all([...seen].filter(p => !keep.has(p)).map(p => fs.unlink(p).catch(() => {}))); } From 66d21a9b3e186654f973c9fa59c33b6f2345c11b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 2 Feb 2026 22:30:08 +0100 Subject: [PATCH 26/91] add flag for noExpose --- hardhat.config.ts | 1 - .../hardhat-exposed/hook-handlers/clean.ts | 2 ++ .../hardhat-exposed/hook-handlers/config.ts | 2 ++ .../hardhat-exposed/hook-handlers/solidity.ts | 11 ++++------ hardhat/hardhat-exposed/plugin.ts | 22 ++++++++++++++++++- .../hook-handlers/network.ts | 8 +++---- hardhat/hardhat-transpiler/tasks/transpile.ts | 2 +- 7 files changed, 34 insertions(+), 14 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 965b7ad65f5..9d4ad73419e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -88,7 +88,6 @@ export default defineConfig({ }, }, exposed: { - imports: true, initializers: true, include: ['contracts/**/*.sol'], exclude: ['contracts/vendor/**/*', '**/*WithInit.sol'], diff --git a/hardhat/hardhat-exposed/hook-handlers/clean.ts b/hardhat/hardhat-exposed/hook-handlers/clean.ts index f45210f80a1..1a515d8fc60 100644 --- a/hardhat/hardhat-exposed/hook-handlers/clean.ts +++ b/hardhat/hardhat-exposed/hook-handlers/clean.ts @@ -2,6 +2,8 @@ import fs from 'node:fs/promises'; import type { CleanHooks } from 'hardhat/types/hooks'; +import type {} from '../type-extensions'; + export default async (): Promise> => ({ onClean: context => fs.rm(context.config.exposed.outDir, { recursive: true, force: true }), }); diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts index dffa8f909f8..fcca8972819 100644 --- a/hardhat/hardhat-exposed/hook-handlers/config.ts +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -2,6 +2,8 @@ import path from 'node:path'; import type { ConfigHooks } from 'hardhat/types/hooks'; +import type {} from '../type-extensions'; + export default async (): Promise> => ({ validateUserConfig: async userConfig => { const results: Array<{ path: string[]; message: string }> = []; diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts index bf7d3737f36..d39970faf25 100644 --- a/hardhat/hardhat-exposed/hook-handlers/solidity.ts +++ b/hardhat/hardhat-exposed/hook-handlers/solidity.ts @@ -4,6 +4,8 @@ import path from 'node:path'; import type { SolidityHooks } from 'hardhat/types/hooks'; import { FileBuildResultType } from 'hardhat/types/solidity'; +import type {} from '../type-extensions'; + import { getExposedPath, generateExposedContracts } from '../internal/expose.ts'; export default async (): Promise> => ({ @@ -52,13 +54,8 @@ export default async (): Promise> => ({ } // 4. Build all exposed contracts - const exposedResults = await context.solidity.build( - rootPaths - .filter(includes) - .map(rootPath => getExposedPath(context, rootPath)) - .filter(fs.existsSync), - options, - ); + const exposedPaths = fs.globSync(path.join(context.config.exposed.outDir, '**', '*.sol')); + const exposedResults = await context.solidity.build(exposedPaths, options); // Return errors instead of ignoring! if ('reason' in exposedResults) return exposedResults; diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index e0c9f7970c3..a91d8eddeb5 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -1,4 +1,5 @@ import type { HardhatPlugin } from 'hardhat/types/plugins'; +import { overrideTask } from 'hardhat/config'; import type {} from './type-extensions.ts'; @@ -7,8 +8,27 @@ const hardhatExposedPlugin: HardhatPlugin = { hookHandlers: { clean: () => import('./hook-handlers/clean.ts'), config: () => import('./hook-handlers/config.ts'), - solidity: () => import('./hook-handlers/solidity.ts'), }, + tasks: [ + overrideTask('compile') + .addFlag({ name: 'noExpose', description: 'Skip generation of exposed contracts.' }) + .setAction(() => + Promise.resolve({ + default: (args, hre, runSuper) => + import('./hook-handlers/solidity.ts') + .then(hooks => hooks.default()) + .then(hooks => { + if (args.noExpose) { + hre.hooks.unregisterHandlers('solidity', hooks); + } else { + hre.hooks.registerHandlers('solidity', hooks); + } + }) + .then(() => runSuper(args)), + }), + ) + .build(), + ], }; export default hardhatExposedPlugin; diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts index ecde42f585d..c6fb0b2c0de 100644 --- a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts @@ -1,10 +1,10 @@ import type { HookContext, NetworkHooks } from 'hardhat/types/hooks'; import type { ChainType, NetworkConnection } from 'hardhat/types/network'; -import { impersonate } from '../../../test/helpers/account.ts'; -import { getLocalChain } from '../../../test/helpers/chains.ts'; -import { getSlot, getAddressInSlot, setSlot } from '../../../test/helpers/storage.ts'; -import { clock, clockFromReceipt, increaseBy, increaseTo, duration } from '../../../test/helpers/time.ts'; +import { impersonate } from '../../../test/helpers/account.js'; +import { getLocalChain } from '../../../test/helpers/chains.js'; +import { getSlot, getAddressInSlot, setSlot } from '../../../test/helpers/storage.js'; +import { clock, clockFromReceipt, increaseBy, increaseTo, duration } from '../../../test/helpers/time.js'; export default async (): Promise> => ({ newConnection: async ( diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index c10df3c4797..041763dddec 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -34,7 +34,7 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat assert(settings, 'Transpile settings file must be provided'); const options = await loadSettings(hre, settings); - const { contractRootPaths } = await hre.tasks.getTask('compile').run({ noTests: true }); + const { contractRootPaths } = await hre.tasks.getTask('compile').run({ noTests: true, noExpose: true }); const compilationJobs = await hre.solidity.getCompilationJobs(contractRootPaths); assert('cacheHits' in compilationJobs, 'Compilation jobs not found'); From b7d7c710dc3403e483d4076838fab6924f8c321d Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 2 Feb 2026 23:26:17 +0100 Subject: [PATCH 27/91] disable noExpose flag that was breaking tests --- hardhat/hardhat-exposed/plugin.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index a91d8eddeb5..9a9df2e0821 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -8,23 +8,14 @@ const hardhatExposedPlugin: HardhatPlugin = { hookHandlers: { clean: () => import('./hook-handlers/clean.ts'), config: () => import('./hook-handlers/config.ts'), + solidity: () => import('./hook-handlers/solidity.ts'), }, tasks: [ overrideTask('compile') .addFlag({ name: 'noExpose', description: 'Skip generation of exposed contracts.' }) .setAction(() => Promise.resolve({ - default: (args, hre, runSuper) => - import('./hook-handlers/solidity.ts') - .then(hooks => hooks.default()) - .then(hooks => { - if (args.noExpose) { - hre.hooks.unregisterHandlers('solidity', hooks); - } else { - hre.hooks.registerHandlers('solidity', hooks); - } - }) - .then(() => runSuper(args)), + default: (args, hre, runSuper) => runSuper(args), // TODO: pass flag to solidity hook handler to suppress exposed contract generation }), ) .build(), From 713cc11fb48f45e9bd4249e3758e25f99c43bacf Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 2 Feb 2026 23:42:43 +0100 Subject: [PATCH 28/91] migrate new test --- test/utils/SimulatedCall.test.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/utils/SimulatedCall.test.js b/test/utils/SimulatedCall.test.js index ab2e875543e..71917bfa513 100644 --- a/test/utils/SimulatedCall.test.js +++ b/test/utils/SimulatedCall.test.js @@ -1,6 +1,10 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; + +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const value = 42n; @@ -51,7 +55,7 @@ describe('SimulateCall', function () { ethers.Typed.bytes(this.target.interface.encodeFunctionData('mockFunctionWithArgsReturn', [10, 20])), ); - await expect(txPromise).to.changeEtherBalances([this.mock, this.simulator, this.target], [0n, 0n, 0n]); + await expect(txPromise).to.changeEtherBalances(ethers, [this.mock, this.simulator, this.target], [0n, 0n, 0n]); await expect(txPromise) .to.emit(this.mock, 'return$simulateCall_address_bytes') .withArgs(true, ethers.AbiCoder.defaultAbiCoder().encode(['uint256', 'uint256'], [10, 20])) @@ -66,7 +70,7 @@ describe('SimulateCall', function () { ethers.Typed.bytes(this.target.interface.encodeFunctionData('mockFunctionExtra')), ); - await expect(txPromise).to.changeEtherBalances([this.mock, this.simulator, this.target], [0n, 0n, 0n]); + await expect(txPromise).to.changeEtherBalances(ethers, [this.mock, this.simulator, this.target], [0n, 0n, 0n]); await expect(txPromise) .to.emit(this.mock, 'return$simulateCall_address_uint256_bytes') .withArgs(true, ethers.AbiCoder.defaultAbiCoder().encode(['address', 'uint256'], [this.mock.target, value])) @@ -79,7 +83,7 @@ describe('SimulateCall', function () { ethers.Typed.bytes(this.target.interface.encodeFunctionData('mockFunctionRevertsReason')), ); - await expect(txPromise).to.changeEtherBalances([this.mock, this.simulator, this.target], [0n, 0n, 0n]); + await expect(txPromise).to.changeEtherBalances(ethers, [this.mock, this.simulator, this.target], [0n, 0n, 0n]); await expect(txPromise) .to.emit(this.mock, 'return$simulateCall_address_bytes') .withArgs(false, this.target.interface.encodeErrorResult('Error', ['CallReceiverMock: reverting'])); @@ -92,7 +96,7 @@ describe('SimulateCall', function () { ethers.Typed.bytes(this.target.interface.encodeFunctionData('mockFunctionRevertsReason')), ); - await expect(txPromise).to.changeEtherBalances([this.mock, this.simulator, this.target], [0n, 0n, 0n]); + await expect(txPromise).to.changeEtherBalances(ethers, [this.mock, this.simulator, this.target], [0n, 0n, 0n]); await expect(txPromise) .to.emit(this.mock, 'return$simulateCall_address_uint256_bytes') .withArgs(false, this.target.interface.encodeErrorResult('Error', ['CallReceiverMock: reverting'])); From 5be397087a3d72f4cecf3c54075a9cc183ed07ee Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 3 Feb 2026 10:31:26 +0100 Subject: [PATCH 29/91] testing of upgradeable contracts --- contracts/mocks/Stateless.sol | 63 ------------------- hardhat.config.ts | 2 +- hardhat/hardhat-transpiler/tasks/transpile.ts | 23 +++---- scripts/upgradeable/upgradeable.patch | 46 ++++++++++---- 4 files changed, 43 insertions(+), 91 deletions(-) delete mode 100644 contracts/mocks/Stateless.sol diff --git a/contracts/mocks/Stateless.sol b/contracts/mocks/Stateless.sol deleted file mode 100644 index 0669c675248..00000000000 --- a/contracts/mocks/Stateless.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.26; - -// We keep these imports and a dummy contract just to we can run the test suite after transpilation. - -import {Accumulators} from "../utils/structs/Accumulators.sol"; -import {Address} from "../utils/Address.sol"; -import {Arrays} from "../utils/Arrays.sol"; -import {AuthorityUtils} from "../access/manager/AuthorityUtils.sol"; -import {Base58} from "../utils/Base58.sol"; -import {Base64} from "../utils/Base64.sol"; -import {BitMaps} from "../utils/structs/BitMaps.sol"; -import {Blockhash} from "../utils/Blockhash.sol"; -import {Bytes} from "../utils/Bytes.sol"; -import {CAIP2} from "../utils/CAIP2.sol"; -import {CAIP10} from "../utils/CAIP10.sol"; -import {Checkpoints} from "../utils/structs/Checkpoints.sol"; -import {CircularBuffer} from "../utils/structs/CircularBuffer.sol"; -import {Clones} from "../proxy/Clones.sol"; -import {Create2} from "../utils/Create2.sol"; -import {DoubleEndedQueue} from "../utils/structs/DoubleEndedQueue.sol"; -import {ECDSA} from "../utils/cryptography/ECDSA.sol"; -import {EIP7702Utils} from "../account/utils/EIP7702Utils.sol"; -import {EnumerableMap} from "../utils/structs/EnumerableMap.sol"; -import {EnumerableSet} from "../utils/structs/EnumerableSet.sol"; -import {ERC165} from "../utils/introspection/ERC165.sol"; -import {ERC165Checker} from "../utils/introspection/ERC165Checker.sol"; -import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol"; -import {ERC1155Holder} from "../token/ERC1155/utils/ERC1155Holder.sol"; -import {ERC1967Utils} from "../proxy/ERC1967/ERC1967Utils.sol"; -import {ERC4337Utils} from "../account/utils/draft-ERC4337Utils.sol"; -import {ERC7579Utils} from "../account/utils/draft-ERC7579Utils.sol"; -import {ERC7913P256Verifier} from "../utils/cryptography/verifiers/ERC7913P256Verifier.sol"; -import {ERC7913RSAVerifier} from "../utils/cryptography/verifiers/ERC7913RSAVerifier.sol"; -import {ERC7913WebAuthnVerifier} from "../utils/cryptography/verifiers/ERC7913WebAuthnVerifier.sol"; -import {Heap} from "../utils/structs/Heap.sol"; -import {InteroperableAddress} from "../utils/draft-InteroperableAddress.sol"; -import {LowLevelCall} from "../utils/LowLevelCall.sol"; -import {Math} from "../utils/math/Math.sol"; -import {Memory} from "../utils/Memory.sol"; -import {MerkleProof} from "../utils/cryptography/MerkleProof.sol"; -import {MessageHashUtils} from "../utils/cryptography/MessageHashUtils.sol"; -import {Nonces} from "../utils/Nonces.sol"; -import {NoncesKeyed} from "../utils/NoncesKeyed.sol"; -import {P256} from "../utils/cryptography/P256.sol"; -import {Packing} from "../utils/Packing.sol"; -import {Panic} from "../utils/Panic.sol"; -import {RelayedCall} from "../utils/RelayedCall.sol"; -import {RLP} from "../utils/RLP.sol"; -import {RSA} from "../utils/cryptography/RSA.sol"; -import {SafeCast} from "../utils/math/SafeCast.sol"; -import {SafeERC20} from "../token/ERC20/utils/SafeERC20.sol"; -import {ShortStrings} from "../utils/ShortStrings.sol"; -import {SignatureChecker} from "../utils/cryptography/SignatureChecker.sol"; -import {SignedMath} from "../utils/math/SignedMath.sol"; -import {SimulateCall} from "../utils/SimulateCall.sol"; -import {StorageSlot} from "../utils/StorageSlot.sol"; -import {Strings} from "../utils/Strings.sol"; -import {Time} from "../utils/types/Time.sol"; -import {TrieProof} from "../utils/cryptography/TrieProof.sol"; - -contract Dummy1234 {} diff --git a/hardhat.config.ts b/hardhat.config.ts index 9d4ad73419e..ba50439b704 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -90,6 +90,6 @@ export default defineConfig({ exposed: { initializers: true, include: ['contracts/**/*.sol'], - exclude: ['contracts/vendor/**/*', '**/*WithInit.sol'], + exclude: ['**/*WithInit.sol'], }, }); diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index 041763dddec..aa660f007f0 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -17,24 +17,11 @@ interface TranspileOptions { peerProject?: string; } -async function loadSettings(hre: HardhatRuntimeEnvironment, settings: string): Promise { - const makeProjectPath = (p: string) => - path.join('project', path.isAbsolute(p) ? path.relative(hre.config.paths.root, p) : p); - - const options: TranspileOptions = await fs.readFile(settings, 'utf-8').then(JSON.parse); - options.initializablePath = options.initializablePath && makeProjectPath(options.initializablePath); - options.exclude = options.exclude?.map(makeProjectPath); - options.publicInitializers = options.publicInitializers?.map(makeProjectPath); - options.namespaceExclude = options.namespaceExclude?.map(makeProjectPath); - - return options; -} - export default async function ({ settings }: { settings?: string }, hre: HardhatRuntimeEnvironment) { assert(settings, 'Transpile settings file must be provided'); - const options = await loadSettings(hre, settings); + const options: TranspileOptions = await fs.readFile(settings, 'utf-8').then(JSON.parse); - const { contractRootPaths } = await hre.tasks.getTask('compile').run({ noTests: true, noExpose: true }); + const { contractRootPaths } = await hre.tasks.getTask('compile').run({ noTests: true }); const compilationJobs = await hre.solidity.getCompilationJobs(contractRootPaths); assert('cacheHits' in compilationJobs, 'Compilation jobs not found'); @@ -57,6 +44,12 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat .then(file => fs.readFile(file!, 'utf-8')) .then(JSON.parse); + // Adjust paths to match transpiler expectations + input.sources = Object.fromEntries(Object.entries(input.sources).map(([k, v]) => [k.replace(/^project\//, ''), v])); + output.sources = Object.fromEntries(Object.entries(output.sources).map(([k, v]) => [k.replace(/^project\//, ''), v])); + output.contracts = Object.fromEntries(Object.entries(output.contracts).map(([k, v]) => [k.replace(/^project\//, ''), v])); + Object.values(output.sources).forEach((s: any) => { s.ast.absolutePath = s.ast.absolutePath.replace(/^project\//, ''); }); + // Run transpilation on the first source folder const transpiled = await transpile( input, diff --git a/scripts/upgradeable/upgradeable.patch b/scripts/upgradeable/upgradeable.patch index c1106277dbe..e5bcee15341 100644 --- a/scripts/upgradeable/upgradeable.patch +++ b/scripts/upgradeable/upgradeable.patch @@ -1,6 +1,6 @@ diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 -index 35ad097f..00000000 +index 35ad097ff..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,21 +0,0 @@ @@ -26,7 +26,7 @@ index 35ad097f..00000000 - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml -index 4018cef2..d343a53d 100644 +index 4018cef29..d343a53d8 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,8 @@ @@ -40,7 +40,7 @@ index 4018cef2..d343a53d 100644 about: Ask in the OpenZeppelin Forum diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 -index ff596b0c..00000000 +index ff596b0c3..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,14 +0,0 @@ @@ -59,7 +59,7 @@ index ff596b0c..00000000 - - diff --git a/README.md b/README.md -index 6a01f561..b01c6e88 100644 +index 6a01f5616..b01c6e88f 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ @@ -116,7 +116,7 @@ index 6a01f561..b01c6e88 100644 } ``` diff --git a/contracts/package.json b/contracts/package.json -index 3535a2f5..9a73abc2 100644 +index 3535a2f56..9a73abc22 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,5 +1,5 @@ @@ -146,7 +146,7 @@ index 3535a2f5..9a73abc2 100644 + } } diff --git a/contracts/utils/ReentrancyGuard.sol b/contracts/utils/ReentrancyGuard.sol -index c156fa1c..895e3934 100644 +index c156fa1cc..895e39342 100644 --- a/contracts/utils/ReentrancyGuard.sol +++ b/contracts/utils/ReentrancyGuard.sol @@ -36,6 +36,11 @@ abstract contract ReentrancyGuard { @@ -173,7 +173,7 @@ index c156fa1c..895e3934 100644 * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol -index 2bc45a4b..a5aa41d2 100644 +index 2bc45a4b2..a5aa41d21 100644 --- a/contracts/utils/cryptography/EIP712.sol +++ b/contracts/utils/cryptography/EIP712.sol @@ -4,7 +4,6 @@ @@ -305,8 +305,30 @@ index 2bc45a4b..a5aa41d2 100644 + return keccak256(bytes(_EIP712Version())); } } +diff --git a/hardhat.config.ts b/hardhat.config.ts +index ba50439b7..b7af88312 100644 +--- a/hardhat.config.ts ++++ b/hardhat.config.ts +@@ -41,7 +41,7 @@ export default defineConfig({ + hardhatOzContractsHelpers, + ], + paths: { +- sources: argv.src, ++ sources: [ argv.src, 'lib/openzeppelin-contracts/contracts' ], + }, + solidity: { + version: argv.compiler, +@@ -89,7 +89,7 @@ export default defineConfig({ + }, + exposed: { + initializers: true, +- include: ['contracts/**/*.sol'], ++ include: ['contracts/**/*.sol', 'lib/openzeppelin-contracts/contracts/**/*.sol'], + exclude: ['**/*WithInit.sol'], + }, + }); diff --git a/package.json b/package.json -index 77d405cf..d6b41c95 100644 +index 0b6f6c268..1bab4415d 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ @@ -319,7 +341,7 @@ index 77d405cf..d6b41c95 100644 "keywords": [ "solidity", diff --git a/remappings.txt b/remappings.txt -index a6be3bfd..d20f777f 100644 +index a6be3bfd6..d20f777f3 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,5 @@ @@ -330,7 +352,7 @@ index a6be3bfd..d20f777f 100644 forge-std=lib/forge-std/src halmos-cheatcodes=lib/halmos-cheatcodes/src diff --git a/test/account/AccountEIP7702.test.js b/test/account/AccountEIP7702.test.js -index dcb2cc86..df4ff523 100644 +index dcb2cc862..df4ff523a 100644 --- a/test/account/AccountEIP7702.test.js +++ b/test/account/AccountEIP7702.test.js @@ -28,8 +28,8 @@ async function fixture() { @@ -345,7 +367,7 @@ index dcb2cc86..df4ff523 100644 verifyingContract: mock.address, }; diff --git a/test/account/examples/AccountEIP7702WithModulesMock.test.js b/test/account/examples/AccountEIP7702WithModulesMock.test.js -index 2369040a..3fc0a517 100644 +index 2369040a2..3fc0a5172 100644 --- a/test/account/examples/AccountEIP7702WithModulesMock.test.js +++ b/test/account/examples/AccountEIP7702WithModulesMock.test.js @@ -38,8 +38,8 @@ async function fixture() { @@ -360,7 +382,7 @@ index 2369040a..3fc0a517 100644 verifyingContract: mock.address, }; diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js -index ab28c4e1..92e0f368 100644 +index ab28c4e1b..92e0f368b 100644 --- a/test/utils/cryptography/EIP712.test.js +++ b/test/utils/cryptography/EIP712.test.js @@ -50,27 +50,6 @@ describe('EIP712', function () { From 0308965548a590916c8d36cf79e9f035245a4da2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 3 Feb 2026 15:39:53 +0100 Subject: [PATCH 30/91] refactor expose include filter --- scripts/upgradeable/upgradeable.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/upgradeable/upgradeable.patch b/scripts/upgradeable/upgradeable.patch index e5bcee15341..5f2954bae51 100644 --- a/scripts/upgradeable/upgradeable.patch +++ b/scripts/upgradeable/upgradeable.patch @@ -306,7 +306,7 @@ index 2bc45a4b2..a5aa41d21 100644 } } diff --git a/hardhat.config.ts b/hardhat.config.ts -index ba50439b7..b7af88312 100644 +index ba50439b7..6ee16de43 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -41,7 +41,7 @@ export default defineConfig({ @@ -323,7 +323,7 @@ index ba50439b7..b7af88312 100644 exposed: { initializers: true, - include: ['contracts/**/*.sol'], -+ include: ['contracts/**/*.sol', 'lib/openzeppelin-contracts/contracts/**/*.sol'], ++ include: ['{,lib/openzeppelin-contracts/}contracts/**/*.sol'], exclude: ['**/*WithInit.sol'], }, }); From 13d8d4b1caef4df6c0cba7742d4a4c8ce1c6aa5c Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 3 Feb 2026 16:45:01 +0100 Subject: [PATCH 31/91] migrate task-test-get-files.js to skip test --- .../hook-handlers/test.ts | 24 ++++++++++++++++++ .../hardhat-oz-contracts-helpers/plugin.ts | 1 + hardhat/hardhat-transpiler/tasks/transpile.ts | 14 ++++++++--- hardhat/task-test-get-files.js | 25 ------------------- 4 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 hardhat/hardhat-oz-contracts-helpers/hook-handlers/test.ts delete mode 100644 hardhat/task-test-get-files.js diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/test.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/test.ts new file mode 100644 index 00000000000..c4495cba7d3 --- /dev/null +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/test.ts @@ -0,0 +1,24 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import type { TestHooks, HookContext } from 'hardhat/types/hooks'; + +const ignoredIfProxy: Set = new Set([ + 'test/proxy/beacon/BeaconProxy.test.js', + 'test/proxy/beacon/UpgradeableBeacon.test.js', + 'test/proxy/ERC1967/ERC1967Proxy.test.js', + 'test/proxy/transparent/ProxyAdmin.test.js', + 'test/proxy/transparent/TransparentUpgradeableProxy.test.js', + 'test/proxy/utils/UUPSUpgradeable.test.js', +]); + +export default async (): Promise> => ({ + registerFileForTestRunner: ( + context: HookContext, + filePath: string, + next: (nextContext: HookContext, filePath: string) => Promise, + ): Promise => { + const hasProxies = fs.existsSync(path.join(context.config.paths.root, 'contracts/proxy/Proxy.sol')); + return hasProxies || !ignoredIfProxy.has(filePath) ? next(context, filePath) : Promise.resolve('ignored'); + }, +}); diff --git a/hardhat/hardhat-oz-contracts-helpers/plugin.ts b/hardhat/hardhat-oz-contracts-helpers/plugin.ts index f3fa25e6e07..dd4b1112680 100644 --- a/hardhat/hardhat-oz-contracts-helpers/plugin.ts +++ b/hardhat/hardhat-oz-contracts-helpers/plugin.ts @@ -7,6 +7,7 @@ const hardhatOzContractsHelpers: HardhatPlugin = { hookHandlers: { hre: () => import('./hook-handlers/hre.ts'), network: () => import('./hook-handlers/network.ts'), + test: () => import('./hook-handlers/test.ts'), }, dependencies: () => [import('@nomicfoundation/hardhat-ethers')], }; diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index aa660f007f0..be6de8966e1 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -17,6 +17,10 @@ interface TranspileOptions { peerProject?: string; } +function transformKeys(obj: Record, fn: (key: string) => string): Record { + return Object.fromEntries(Object.entries(obj).map(([k, v]) => [fn(k), v])); +} + export default async function ({ settings }: { settings?: string }, hre: HardhatRuntimeEnvironment) { assert(settings, 'Transpile settings file must be provided'); const options: TranspileOptions = await fs.readFile(settings, 'utf-8').then(JSON.parse); @@ -45,10 +49,12 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat .then(JSON.parse); // Adjust paths to match transpiler expectations - input.sources = Object.fromEntries(Object.entries(input.sources).map(([k, v]) => [k.replace(/^project\//, ''), v])); - output.sources = Object.fromEntries(Object.entries(output.sources).map(([k, v]) => [k.replace(/^project\//, ''), v])); - output.contracts = Object.fromEntries(Object.entries(output.contracts).map(([k, v]) => [k.replace(/^project\//, ''), v])); - Object.values(output.sources).forEach((s: any) => { s.ast.absolutePath = s.ast.absolutePath.replace(/^project\//, ''); }); + input.sources = transformKeys(input.sources, k => k.replace(/^project\//, '')); + output.sources = transformKeys(output.sources, k => k.replace(/^project\//, '')); + output.contracts = transformKeys(output.contracts, k => k.replace(/^project\//, '')); + Object.values(output.sources).forEach((s: any) => { + s.ast.absolutePath = s.ast.absolutePath.replace(/^project\//, ''); + }); // Run transpilation on the first source folder const transpiled = await transpile( diff --git a/hardhat/task-test-get-files.js b/hardhat/task-test-get-files.js deleted file mode 100644 index 108f40a42c0..00000000000 --- a/hardhat/task-test-get-files.js +++ /dev/null @@ -1,25 +0,0 @@ -const { internalTask } = require('hardhat/config'); -const { TASK_TEST_GET_TEST_FILES } = require('hardhat/builtin-tasks/task-names'); - -// Modifies `hardhat test` to skip the proxy tests after proxies are removed by the transpiler for upgradeability. - -internalTask(TASK_TEST_GET_TEST_FILES).setAction(async (args, hre, runSuper) => { - const path = require('path'); - const { promises: fs } = require('fs'); - - const hasProxies = await fs - .access(path.join(hre.config.paths.sources, 'proxy/Proxy.sol')) - .then(() => true) - .catch(() => false); - - const ignoredIfProxy = [ - 'proxy/beacon/BeaconProxy.test.js', - 'proxy/beacon/UpgradeableBeacon.test.js', - 'proxy/ERC1967/ERC1967Proxy.test.js', - 'proxy/transparent/ProxyAdmin.test.js', - 'proxy/transparent/TransparentUpgradeableProxy.test.js', - 'proxy/utils/UUPSUpgradeable.test.js', - ].map(p => path.join(hre.config.paths.tests, p)); - - return (await runSuper(args)).filter(file => hasProxies || !ignoredIfProxy.includes(file)); -}); From eb7e57d4aaf3b24baefb0f5a26b8293dc11c0781 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 4 Feb 2026 10:01:00 +0100 Subject: [PATCH 32/91] use hardhat-ignore-warnings 0.3.0 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index eec194bfe74..2eb49f5b140 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "globals": "^17.0.0", "graphlib": "^2.1.8", "hardhat": "^3.1.6", - "hardhat-ignore-warnings": "^0.3.0-3", + "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", "interoperable-addresses": "^0.1.3", @@ -5075,9 +5075,9 @@ } }, "node_modules/hardhat-ignore-warnings": { - "version": "0.3.0-3", - "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.3.0-3.tgz", - "integrity": "sha512-tMPCJR3kz0RoglCM+RJHwyB6aGrdl20ahvk+AaY8u8JrLGFybT/DqjjPJadV8Bm6kn/TU3BdWxRDTXpOif+xKA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.3.0.tgz", + "integrity": "sha512-zpFICQhlRgN8NNq8WkQ0D86BfjrbwJOt+Tih1Mft0iXiqqlZoOI0cwcmc0bSb4wQZV4jWjfCk9g8x71Isp7MNA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index d5ca654920f..a7dd8dd786d 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "globals": "^17.0.0", "graphlib": "^2.1.8", "hardhat": "^3.1.6", - "hardhat-ignore-warnings": "^0.3.0-3", + "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.0", "husky": "^9.1.7", "interoperable-addresses": "^0.1.3", From 10ab3c2c73f6e54f3ce6917e83226603f5ddde29 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 4 Feb 2026 15:51:49 +0100 Subject: [PATCH 33/91] redesign exposed plugin: closer to original version + support imports option --- .../hardhat-exposed/hook-handlers/config.ts | 4 + .../hardhat-exposed/hook-handlers/solidity.ts | 53 +- hardhat/hardhat-exposed/internal/expose.ts | 473 ++++++++++-------- hardhat/hardhat-exposed/internal/types.ts | 2 + 4 files changed, 311 insertions(+), 221 deletions(-) diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts index fcca8972819..54e63f5574c 100644 --- a/hardhat/hardhat-exposed/hook-handlers/config.ts +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -29,6 +29,9 @@ export default async (): Promise> => ({ if (userConfig.exposed?.initializers !== undefined && typeof userConfig.exposed?.initializers !== 'boolean') { results.push({ path: ['exposed', 'initializers'], message: 'Expected an optional boolean.' }); } + if (userConfig.exposed?.imports !== undefined && typeof userConfig.exposed?.imports !== 'boolean') { + results.push({ path: ['exposed', 'imports'], message: 'Expected an optional boolean.' }); + } return results; }, @@ -40,6 +43,7 @@ export default async (): Promise> => ({ ...partiallyResolvedConfig, exposed: { ...userConfig.exposed, + prefix: userConfig.exposed?.prefix ?? '$', exclude: (userConfig.exposed?.exclude ?? []).map(makeAbsolutePath), include: (userConfig.exposed?.include ?? ['**/*']).map(makeAbsolutePath), outDir: makeAbsolutePath(userConfig.exposed?.outDir ?? 'contracts-exposed'), diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts index d39970faf25..78c1d9cc3bc 100644 --- a/hardhat/hardhat-exposed/hook-handlers/solidity.ts +++ b/hardhat/hardhat-exposed/hook-handlers/solidity.ts @@ -1,16 +1,17 @@ +import { assert } from 'node:console'; import fs from 'node:fs'; import path from 'node:path'; - import type { SolidityHooks } from 'hardhat/types/hooks'; -import { FileBuildResultType } from 'hardhat/types/solidity'; +import { FileBuildResultType, SolidityBuildInfoOutput } from 'hardhat/types/solidity'; -import type {} from '../type-extensions'; +import { getExposed } from '../internal/expose.ts'; -import { getExposedPath, generateExposedContracts } from '../internal/expose.ts'; +import type {} from '../type-extensions'; export default async (): Promise> => ({ build: async (context, rootPaths, options, next) => { const includes = (rootPath: string) => + rootPaths.includes(rootPath) && context.config.exposed.include.some(p => path.matchesGlob(rootPath, p)) && !context.config.exposed.exclude.some(p => path.matchesGlob(rootPath, p)) && !rootPath.startsWith(context.config.exposed.outDir) && @@ -24,38 +25,40 @@ export default async (): Promise> => ({ // Return errors instead of ignoring! if ('reason' in results) return results; - // 2. Collect the files that need their exposed contracts regenerated - // - BUILD_SUCCESS (newly compiled) - // - CACHE_HIT with missing exposed file - const exposedFilesToRegenerate: Array<{ rootPath: string; buildId: string }> = []; + // 2. Recover the build IDs, and the corresponding root files + const rootFilesPathsByBuildId: Set = new Set(); for (const [rootPath, result] of results) { if (!includes(rootPath)) continue; switch (result.type) { - case FileBuildResultType.BUILD_SUCCESS: - exposedFilesToRegenerate.push({ - rootPath, - buildId: await result.compilationJob.getBuildId(), - }); + case FileBuildResultType.BUILD_SUCCESS: { + rootFilesPathsByBuildId.add(await result.compilationJob.getBuildId()); break; - case FileBuildResultType.CACHE_HIT: - if (!fs.existsSync(getExposedPath(context, rootPath))) { - exposedFilesToRegenerate.push({ - rootPath, - buildId: result.buildId, - }); - } + } + case FileBuildResultType.CACHE_HIT: { + rootFilesPathsByBuildId.add(result.buildId); break; + } } } - // 3. Generate exposed contracts for the files that need it - if (exposedFilesToRegenerate.length > 0) { - await generateExposedContracts(context, exposedFilesToRegenerate); + // 3. Generate all exposed contracts + const exposedPaths: Set = new Set(); + for (const buildId of rootFilesPathsByBuildId) { + const outputPath = await context.artifacts.getBuildInfoOutputPath(buildId); + assert(outputPath, `No build info found for build ID ${buildId}`); + + const { output } = JSON.parse(fs.readFileSync(outputPath!, 'utf-8')) as SolidityBuildInfoOutput; + + const exposed = await getExposed(output, includes, context.config); + for (const [exposedPath, exposedContent] of exposed) { + fs.mkdirSync(path.dirname(exposedPath), { recursive: true }); + fs.writeFileSync(exposedPath, exposedContent); + exposedPaths.add(exposedPath); + } } // 4. Build all exposed contracts - const exposedPaths = fs.globSync(path.join(context.config.exposed.outDir, '**', '*.sol')); - const exposedResults = await context.solidity.build(exposedPaths, options); + const exposedResults = await context.solidity.build(Array.from(exposedPaths), options); // Return errors instead of ignoring! if ('reason' in exposedResults) return exposedResults; diff --git a/hardhat/hardhat-exposed/internal/expose.ts b/hardhat/hardhat-exposed/internal/expose.ts index 7cc1bc39e91..a5c49ecd431 100644 --- a/hardhat/hardhat-exposed/internal/expose.ts +++ b/hardhat/hardhat-exposed/internal/expose.ts @@ -1,9 +1,7 @@ import assert from 'node:assert/strict'; -import fs from 'node:fs'; import path from 'node:path'; - -import type { HookContext } from 'hardhat/types/hooks'; -import { SolidityBuildInfoOutput } from 'hardhat/types/solidity'; +import type { HardhatConfig } from 'hardhat/types/config'; +import type { CompilerOutput } from 'hardhat/types/solidity'; // AST manipulation import type { @@ -15,221 +13,278 @@ import type { StorageLocation, TypeName, UserDefinedTypeName, + ModifierDefinition, } from 'solidity-ast'; import type { ASTDereferencer } from 'solidity-ast/utils.js'; import { findAll, astDereferencer } from 'solidity-ast/utils.js'; -// Exposed code generation -import { formatLines, Lines, spaceBetween } from './format-lines.ts'; +import type { Lines } from './format-lines.ts'; +import { formatLines, spaceBetween } from './format-lines.ts'; const exposedVersionPragma = '>=0.6.0'; -const defaultPrefix = '$'; -export function getExposedPath(context: HookContext, rootFilePath: string): string { - return path.join(context.config.exposed.outDir, path.relative(context.config.paths.root, rootFilePath)); -} +type ContractFilter = (node: ContractDefinition) => boolean; +type ExposedFile = { + absolutePath: string; + content: string; +}; + +export function getExposed( + solcOutput: CompilerOutput, + include: (sourceName: string) => boolean, + config: HardhatConfig, +): Map { + const res = new Map(); + const deref = astDereferencer(solcOutput); + + const imports: Record> = {}; + + for (const { ast } of Object.values(solcOutput.sources)) { + const absolutePath = path.join(config.paths.root, ast.absolutePath.replace(/^project\//, '')); + if (!include(absolutePath)) { + continue; + } -export async function generateExposedContracts( - context: HookContext, - rootFilesToRegenerate: Array<{ rootPath: string; buildId: string }>, -): Promise { - const generatedFiles: string[] = []; - - // Group root files by build ID - const rootFilesPathsByBuildId: { [buildId: string]: string[] } = {}; - rootFilesToRegenerate.forEach(({ rootPath, buildId }) => { - rootFilesPathsByBuildId[buildId] ??= []; - rootFilesPathsByBuildId[buildId].push(rootPath); - }); + const exposedFile = getExposedFile(ast, deref, config); + if (exposedFile !== undefined) { + res.set(exposedFile.absolutePath, exposedFile.content); + } + + if (config.exposed.imports) { + const queue = new Set(findAll('ImportDirective', ast)); + for (const imp of queue) { + const absolutePath = path.join(config.paths.root, imp.absolutePath.replace(/^project\//, '')); + if (include(absolutePath)) { + continue; + } - // Process each build ID - for (const [buildId, rootFilePaths] of Object.entries(rootFilesPathsByBuildId)) { - // Get build info output path - const outputPath = await context.artifacts.getBuildInfoOutputPath(buildId); - if (outputPath === undefined) continue; - - // Build ast dereferencer - const { output } = JSON.parse(fs.readFileSync(outputPath, 'utf-8')) as SolidityBuildInfoOutput; - const deref = astDereferencer(output); - - // Generate exposed files for each root file in this build - for (const rootFilePath of rootFilePaths) { - const userSourceName = path.relative(context.config.paths.root, rootFilePath); - const inputSourceName = path.join('project', userSourceName); - const generatedFilePath = path.join(context.config.exposed.outDir, userSourceName); - - const { ast } = output.sources[inputSourceName]; - if (ast == undefined) continue; - - const relativizePath = ({ absolutePath }: SourceUnit) => - absolutePath.startsWith(path.join('npm', path.sep)) - ? path.relative('npm', absolutePath).replace(/@[a-zA-Z0-9\.-]+/, '') - : path.relative( - path.dirname(generatedFilePath), - path.join(context.config.paths.root, path.relative('project', absolutePath)), - ); - - const content = getExposedContent( - ast, - relativizePath, - deref, - context.config.exposed?.prefix, - context.config.exposed?.initializers, - ); - - if (content) { - fs.mkdirSync(path.dirname(generatedFilePath), { recursive: true }); - fs.writeFileSync(generatedFilePath, content); - // Add to list of generated files - generatedFiles.push(generatedFilePath); + const impUnit = deref('SourceUnit', imp.sourceUnit); + for (const indirectImp of findAll('ImportDirective', impUnit)) { + queue.add(indirectImp); + } + for (const { foreign } of imp.symbolAliases) { + const foreignId = impUnit.exportedSymbols[foreign.name]?.[0]; + assert(foreignId !== undefined); + const { node, sourceUnit } = deref.withSourceUnit('*', foreignId); + if (node.nodeType === 'ContractDefinition' && node.contractKind !== 'interface') { + imports[sourceUnit.absolutePath] ??= new Set(); + imports[sourceUnit.absolutePath]!.add(node); + } + } } } } - return generatedFiles; + for (const [absoluteImportedPath, contracts] of Object.entries(imports)) { + const { ast } = solcOutput.sources[absoluteImportedPath]; + assert(ast !== undefined); + + const exposedFile = getExposedFile(ast, deref, config, node => contracts.has(node)); + if (exposedFile !== undefined) { + res.set(exposedFile.absolutePath, exposedFile.content); + } + } + + return res; +} + +function getExposedFile( + ast: SourceUnit, + deref: ASTDereferencer, + config: HardhatConfig, + filter?: ContractFilter, +): ExposedFile | undefined { + const exposedPath = path.join( + config.exposed.outDir, + ...(ast.absolutePath.startsWith('project/') + ? [ast.absolutePath.replace(/^project\//, '')] + : ['$_', ast.absolutePath]), + ); + + const relativizePath = (p: string) => + (p.startsWith('project/') ? path.relative(path.dirname(exposedPath), p.replace(/^project\//, '')) : p).replace( + /\\/g, + '/', + ); + + const content = getExposedContent( + ast, + relativizePath, + deref, + config.exposed.prefix, + config.exposed.initializers, + filter, + ); + + return content === undefined ? undefined : { absolutePath: exposedPath, content }; } function getExposedContent( ast: SourceUnit, - relativizePath: (source: SourceUnit) => string, + relativizePath: (p: string) => string, deref: ASTDereferencer, - prefix = defaultPrefix, + prefix: string, initializers = false, + filter?: ContractFilter, ): string | undefined { if (prefix === '' || /^\d|[^0-9a-z_$]/i.test(prefix)) { throw new Error(`Prefix '${prefix}' is not valid`); } const contractPrefix = prefix.replace(/^./, c => c.toUpperCase()); - const imports = Array.from(getNeededImports(ast, deref), u => relativizePath(u)); - const contracts = [...findAll('ContractDefinition', ast)].filter(c => c.contractKind !== 'interface'); - - return contracts.length === 0 - ? undefined - : formatLines( - ...spaceBetween( - ['// SPDX-License-Identifier: UNLICENSED'], - [`pragma solidity ${exposedVersionPragma};`], - imports.map(i => `import "${i}";`), - - ...contracts.map(c => { - const isLibrary = c.contractKind === 'library'; - const contractHeader = [`contract ${contractPrefix}${c.name}`]; - if (!areFunctionsFullyImplemented(c, deref)) { - contractHeader.unshift('abstract'); - } - if (!isLibrary) { - contractHeader.push(`is ${c.name}`); - } - contractHeader.push('{'); - - const subset: Visibility[] = isLibrary ? ['internal', 'public', 'external'] : ['internal']; - - const hasReceiveFunction = getFunctions(c, deref, ['external']).some(fn => fn.kind === 'receive'); - const externalizableVariables = getVariables(c, deref, subset).filter( - v => v.typeName?.nodeType !== 'UserDefinedTypeName' || isTypeExternalizable(v.typeName, deref), - ); - const externalizableFunctions = getFunctions(c, deref, subset).filter(f => isExternalizable(f, deref)); - const returnedEventFunctions = externalizableFunctions.filter(fn => isNonViewWithReturns(fn)); - - const clashingFunctions: Record = {}; - for (const fn of externalizableFunctions) { - const id = getFunctionId(fn, c, deref); - clashingFunctions[id] ??= 0; - clashingFunctions[id] += 1; - } - const clashingEvents: Record = {}; - for (const fn of returnedEventFunctions) { - clashingEvents[fn.name] ??= 0; - clashingEvents[fn.name] += 1; - } + const imports = Array.from(getNeededImports(ast, deref), u => relativizePath(u.absolutePath)); + + const contracts = [...findAll('ContractDefinition', ast)].filter( + c => filter?.(c) !== false && c.contractKind !== 'interface', + ); + + if (contracts.length === 0) { + return undefined; + } + + return formatLines( + ...spaceBetween( + ['// SPDX-License-Identifier: UNLICENSED'], + [`pragma solidity ${exposedVersionPragma};`], + imports.map(i => `import "${i}";`), + + ...contracts.map(c => { + const isLibrary = c.contractKind === 'library'; + const contractHeader = [`contract ${contractPrefix}${c.name}`]; + if (!areFunctionsFullyImplemented(c, deref)) { + contractHeader.unshift('abstract'); + } + if (!isLibrary) { + contractHeader.push(`is ${c.name}`); + } + contractHeader.push('{'); + + const subset: Visibility[] = isLibrary ? ['internal', 'public', 'external'] : ['internal']; + + const hasReceiveFunction = getFunctions(c, deref, ['external']).some(fn => fn.kind === 'receive'); + const externalizableVariables = getVariables(c, deref, subset).filter( + v => + (v.typeName?.nodeType !== 'UserDefinedTypeName' && v.typeName?.nodeType !== 'ArrayTypeName') || + isTypeExternalizable(v.typeName, deref), + ); + const modifiers = getModifiers(c, deref); + const externalizableFunctions = getFunctions(c, deref, subset).filter(f => isExternalizable(f, deref)); + const returnedEventFunctions = externalizableFunctions.filter(fn => isNonViewWithReturns(fn)); + + const clashingFunctions: Record = {}; + for (const fn of externalizableFunctions) { + const id = getFunctionId(fn, c, deref); + clashingFunctions[id] ??= 0; + clashingFunctions[id] += 1; + } + + const clashingEvents: Record = {}; + for (const fn of returnedEventFunctions) { + clashingEvents[fn.name] ??= 0; + clashingEvents[fn.name]! += 1; + } - return [ - contractHeader.join(' '), - [`bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";\n`], - spaceBetween( - // slots for storage function parameters - ...getAllStorageArguments(externalizableFunctions, c, deref).map(a => [ - `mapping(uint256 => ${a.storageType}) internal ${prefix}${a.storageVar};`, - ]), - // events for internal returns - ...returnedEventFunctions.map(fn => { - const evName = - clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false); - const params = getFunctionReturnParameters(fn, c, deref, null); - return [`event return${prefix}${evName}(${params.map(printArgument).join(', ')});`]; - }), - // constructor - makeConstructor(c, deref, initializers), - // accessor to internal variables - ...externalizableVariables.map(v => [ - [ - 'function', - `${prefix}${v.name}(${getVarGetterArgs(v, c, deref).map(printArgument).join(', ')})`, - 'external', - v.mutability === 'mutable' || (v.mutability === 'immutable' && !v.value) ? 'view' : 'pure', - 'returns', - `(${getVarGetterReturnType(v, c, deref)})`, - '{', - ].join(' '), - [ - `return ${isLibrary ? c.name + '.' : ''}${v.name}${getVarGetterArgs(v, c, deref) - .map(a => `[${a.name}]`) - .join('')};`, - ], - '}', - ]), - // external functions - ...externalizableFunctions.map(fn => { - const fnName = - clashingFunctions[getFunctionId(fn, c, deref)] === 1 - ? fn.name - : getFunctionNameQualified(fn, c, deref, true); - const fnArgs = getFunctionArguments(fn, c, deref); - const fnRets = getFunctionReturnParameters(fn, c, deref); - const evName = - isNonViewWithReturns(fn) && - (clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false)); - - // function header - const header = ['function', `${prefix}${fnName}(${fnArgs.map(printArgument)})`, 'external']; - - if (fn.stateMutability === 'nonpayable') { - header.push('payable'); - } else if (fn.stateMutability === 'pure' && fnArgs.some(a => a.storageVar)) { - header.push('view'); - } else { - header.push(fn.stateMutability); - } - - if (fn.returnParameters.parameters.length > 0) { - header.push(`returns (${fnRets.map(printArgument).join(', ')})`); - } - - header.push('{'); - - // function body - const body = [ - (fnRets.length === 0 ? '' : `(${fnRets.map(p => p.name).join(', ')}) = `) + - `${isLibrary ? c.name : 'super'}.${fn.name}(${fnArgs.map(a => (a.storageVar ? `${prefix}${a.storageVar}[${a.name}]` : a.name))});`, - ]; - - if (evName) { - body.push(`emit return${prefix}${evName}(${fnRets.map(p => p.name).join(', ')});`); - } - - // return function - return [header.join(' '), body, `}`]; - }), - // receive function - !hasReceiveFunction ? ['receive() external payable {}'] : [], - ), - `}`, - ]; - }), - ), - ); + return [ + contractHeader.join(' '), + [`bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";\n`], + spaceBetween( + // slots for storage function parameters + ...getAllStorageArguments([...externalizableFunctions, ...modifiers], c, deref).map(a => [ + `mapping(uint256 => ${a.storageType}) internal ${prefix}${a.storageVar};`, + ]), + // events for internal returns + ...returnedEventFunctions.map(fn => { + const evName = clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false); + const params = getFunctionReturnParameters(fn, c, deref, null); + return [`event return${prefix}${evName}(${params.map(printArgument).join(', ')});`]; + }), + // constructor + makeConstructor(c, deref, initializers), + // accessor to internal variables + ...externalizableVariables.map(v => [ + [ + 'function', + `${prefix}${v.name}(${getVarGetterArgs(v, c, deref).map(printArgument).join(', ')})`, + 'external', + v.mutability === 'mutable' || (v.mutability === 'immutable' && !v.value) ? 'view' : 'pure', + 'returns', + `(${getVarGetterReturnType(v, c, deref)})`, + '{', + ].join(' '), + [ + `return ${isLibrary ? c.name + '.' : ''}${v.name}${getVarGetterArgs(v, c, deref) + .map(a => `[${a.name}]`) + .join('')};`, + ], + '}', + ]), + // modifiers + ...modifiers.map(m => { + const fnArgs = getFunctionArguments(m, c, deref); + + // function header + const header = [ + 'function', + `${prefix}${m.name}(${fnArgs.map(printArgument).join(', ')})`, + 'external', + 'payable', + `${m.name}(${fnArgs.map(a => (a.storageVar ? `${prefix}${a.storageVar}[${a.name}]` : a.name)).join(', ')})`, + '{}', + ]; + + return [header.join(' ')]; + }), + // external functions + ...externalizableFunctions.map(fn => { + const fnName = + clashingFunctions[getFunctionId(fn, c, deref)] === 1 + ? fn.name + : getFunctionNameQualified(fn, c, deref, true); + const fnArgs = getFunctionArguments(fn, c, deref); + const fnRets = getFunctionReturnParameters(fn, c, deref); + const evName = + isNonViewWithReturns(fn) && + (clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false)); + + // function header + const header = ['function', `${prefix}${fnName}(${fnArgs.map(printArgument)})`, 'external']; + + if (fn.stateMutability === 'nonpayable') { + header.push('payable'); + } else if (fn.stateMutability === 'pure' && fnArgs.some(a => a.storageVar)) { + header.push('view'); + } else { + header.push(fn.stateMutability); + } + + if (fn.returnParameters.parameters.length > 0) { + header.push(`returns (${fnRets.map(printArgument).join(', ')})`); + } + + header.push('{'); + + // function body + const body = [ + (fnRets.length === 0 ? '' : `(${fnRets.map(p => p.name).join(', ')}) = `) + + `${isLibrary ? c.name : 'super'}.${fn.name}(${fnArgs.map(a => (a.storageVar ? `${prefix}${a.storageVar}[${a.name}]` : a.name))});`, + ]; + + if (evName) { + body.push(`emit return${prefix}${evName}(${fnRets.map(p => p.name).join(', ')});`); + } + + // return function + return [header.join(' '), body, `}`]; + }), + // receive function + !hasReceiveFunction ? ['receive() external payable {}'] : [], + ), + `}`, + ]; + }), + ), + ); } // Note this is not the same as contract.fullyImplemented, because this does @@ -415,6 +470,9 @@ function isTypeExternalizable(typeName: TypeName | null | undefined, deref: ASTD } else { return typeDef.members.every(m => isTypeExternalizable(m.typeName, deref)); } + } else if (typeName.nodeType === 'ArrayTypeName' && typeName.length != undefined) { + const value = typeName.length.typeDescriptions.typeIdentifier?.match(/^t_rational_([^_]*)_by_1$/)?.[1]; + return value !== undefined && parseInt(value) < 2 ** 27; } else { return typeName.nodeType !== 'Mapping' && typeName.nodeType !== 'FunctionTypeName'; } @@ -435,7 +493,7 @@ interface Argument { const printArgument = (arg: Argument) => `${arg.type} ${arg.name}`; function getFunctionArguments( - fnDef: FunctionDefinition, + fnDef: FunctionDefinition | ModifierDefinition, context: ContractDefinition, deref: ASTDereferencer, ): Argument[] { @@ -456,7 +514,7 @@ function getFunctionArguments( } function getStorageArguments( - fn: FunctionDefinition, + fn: FunctionDefinition | ModifierDefinition, context: ContractDefinition, deref: ASTDereferencer, ): Required[] { @@ -466,7 +524,7 @@ function getStorageArguments( } function getAllStorageArguments( - fns: FunctionDefinition[], + fns: (FunctionDefinition | ModifierDefinition)[], context: ContractDefinition, deref: ASTDereferencer, ): Required[] { @@ -653,11 +711,34 @@ function getFunctions( return res; } +function getModifiers(contract: ContractDefinition, deref: ASTDereferencer): ModifierDefinition[] { + const parents = contract.linearizedBaseContracts.map(deref('ContractDefinition')); + + const overridden = new Set(); + const res = []; + + for (const parent of parents) { + for (const m of findAll('ModifierDefinition', parent)) { + if (!overridden.has(m.id)) { + res.push(m); + } + for (const b of m.baseModifiers ?? []) { + overridden.add(b); + } + } + } + + return res; +} + function* getNeededImports(ast: SourceUnit, deref: ASTDereferencer): Iterable { const needed = new Set( [ast].concat( [...findAll('ContractDefinition', ast)].flatMap(c => - c.linearizedBaseContracts.map(p => deref.withSourceUnit('ContractDefinition', p).sourceUnit), + c.linearizedBaseContracts.map(p => { + const { sourceUnit } = deref.withSourceUnit('ContractDefinition', p); + return sourceUnit; + }), ), ), ); diff --git a/hardhat/hardhat-exposed/internal/types.ts b/hardhat/hardhat-exposed/internal/types.ts index b8ac8ad7e6e..3311780143b 100644 --- a/hardhat/hardhat-exposed/internal/types.ts +++ b/hardhat/hardhat-exposed/internal/types.ts @@ -4,9 +4,11 @@ export interface ExposedUserConfig { include?: string[]; outDir?: string; initializers?: boolean; + imports?: boolean; } export interface ExposedConfig extends ExposedUserConfig { + prefix: string; exclude: string[]; include: string[]; outDir: string; From fffa3e8c8836bd8a806c549c1956c2cf91f97814 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 4 Feb 2026 16:11:44 +0100 Subject: [PATCH 34/91] update hardhat-predeploy --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2eb49f5b140..798b42860a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "graphlib": "^2.1.8", "hardhat": "^3.1.6", "hardhat-ignore-warnings": "^0.3.0", - "hardhat-predeploy": "^1.0.0", + "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", "interoperable-addresses": "^0.1.3", "lint-staged": "^16.0.0", @@ -5106,9 +5106,9 @@ } }, "node_modules/hardhat-predeploy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hardhat-predeploy/-/hardhat-predeploy-1.0.0.tgz", - "integrity": "sha512-hckkO0H8Ep5yk7mNqJAdbOt5tmq6/v8SNvJRRgrf8Mcj/5eCRtZ8h+z2adA4XfNbDOD0amXQh8ggc2hUEmJIvg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hardhat-predeploy/-/hardhat-predeploy-1.0.1.tgz", + "integrity": "sha512-PDrPNW5rfwgsYzycDIObzj38pAzbasV4yqI/vTwuxvIEyxsefffkMh1yl5nmBtkgq4eYxLwLT7dLEpoYPRSbug==", "dev": true, "license": "MIT", "peerDependencies": { diff --git a/package.json b/package.json index a7dd8dd786d..2f227a49612 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "graphlib": "^2.1.8", "hardhat": "^3.1.6", "hardhat-ignore-warnings": "^0.3.0", - "hardhat-predeploy": "^1.0.0", + "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", "interoperable-addresses": "^0.1.3", "lint-staged": "^16.0.0", From 008e9a05f15253e34ea5263c5df1256a5fcea446 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 4 Feb 2026 16:31:56 +0100 Subject: [PATCH 35/91] add exposed spinner --- .../hardhat-exposed/hook-handlers/solidity.ts | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts index 78c1d9cc3bc..ab71b6d6d68 100644 --- a/hardhat/hardhat-exposed/hook-handlers/solidity.ts +++ b/hardhat/hardhat-exposed/hook-handlers/solidity.ts @@ -1,8 +1,10 @@ import { assert } from 'node:console'; import fs from 'node:fs'; import path from 'node:path'; + import type { SolidityHooks } from 'hardhat/types/hooks'; import { FileBuildResultType, SolidityBuildInfoOutput } from 'hardhat/types/solidity'; +import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; import { getExposed } from '../internal/expose.ts'; @@ -11,31 +13,36 @@ import type {} from '../type-extensions'; export default async (): Promise> => ({ build: async (context, rootPaths, options, next) => { const includes = (rootPath: string) => - rootPaths.includes(rootPath) && context.config.exposed.include.some(p => path.matchesGlob(rootPath, p)) && !context.config.exposed.exclude.some(p => path.matchesGlob(rootPath, p)) && !rootPath.startsWith(context.config.exposed.outDir) && !rootPath.startsWith('npm:'); + // 1. Build the original contracts + const results = await next(context, rootPaths, options); + if ('reason' in results) return results; + // Only apply expose logic when compiling contracts (skip for tests) if (options?.scope === 'contracts' && rootPaths.some(includes)) { - // 1. Build the original contracts - const results = await next(context, rootPaths, options); - - // Return errors instead of ignoring! - if ('reason' in results) return results; + // Start spinner + const spinner = createSpinner({ text: `Generating exposed contracts...` }); + spinner.start(); // 2. Recover the build IDs, and the corresponding root files - const rootFilesPathsByBuildId: Set = new Set(); + const rootFilesPathsByBuildId: Record = {}; for (const [rootPath, result] of results) { if (!includes(rootPath)) continue; switch (result.type) { case FileBuildResultType.BUILD_SUCCESS: { - rootFilesPathsByBuildId.add(await result.compilationJob.getBuildId()); + const buildId = await result.compilationJob.getBuildId(); + rootFilesPathsByBuildId[buildId] ??= []; + rootFilesPathsByBuildId[buildId].push(rootPath); break; } case FileBuildResultType.CACHE_HIT: { - rootFilesPathsByBuildId.add(result.buildId); + const buildId = result.buildId; + rootFilesPathsByBuildId[buildId] ??= []; + rootFilesPathsByBuildId[buildId].push(rootPath); break; } } @@ -43,13 +50,17 @@ export default async (): Promise> => ({ // 3. Generate all exposed contracts const exposedPaths: Set = new Set(); - for (const buildId of rootFilesPathsByBuildId) { + for (const [buildId, buildRootPaths] of Object.entries(rootFilesPathsByBuildId)) { const outputPath = await context.artifacts.getBuildInfoOutputPath(buildId); assert(outputPath, `No build info found for build ID ${buildId}`); const { output } = JSON.parse(fs.readFileSync(outputPath!, 'utf-8')) as SolidityBuildInfoOutput; - const exposed = await getExposed(output, includes, context.config); + const exposed = await getExposed( + output, + (p: string) => buildRootPaths.includes(p) && includes(p), + context.config, + ); for (const [exposedPath, exposedContent] of exposed) { fs.mkdirSync(path.dirname(exposedPath), { recursive: true }); fs.writeFileSync(exposedPath, exposedContent); @@ -57,20 +68,20 @@ export default async (): Promise> => ({ } } + // Step spinner + spinner.stop(); + // 4. Build all exposed contracts const exposedResults = await context.solidity.build(Array.from(exposedPaths), options); - - // Return errors instead of ignoring! if ('reason' in exposedResults) return exposedResults; - // Merge ALL results + // Merge exposed results into the original run for (const [filePath, result] of exposedResults) { results.set(filePath, result); } - - return results; - } else { - return next(context, rootPaths, options); } + + // 5. Return all results + return results; }, }); From 119a16f1600d7888c381540859b1dde87c658ccb Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 6 Feb 2026 18:55:04 +0100 Subject: [PATCH 36/91] anvil free testing thanks to eth_getProof support --- package-lock.json | 72 +++++++------- package.json | 2 +- test/utils/cryptography/TrieProof.test.js | 110 +++++++++------------- 3 files changed, 79 insertions(+), 105 deletions(-) diff --git a/package-lock.json b/package-lock.json index 798b42860a2..ed845cb4c29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.6", + "hardhat": "^3.1.7", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", @@ -1824,28 +1824,28 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.22.tgz", - "integrity": "sha512-JigYWf2stjpDxSndBsxRoobQHK8kz4SAVaHtTIKQLIHbsBwymE8i120Ejne6Jk+Ndc5CsNINXB8/bK6vLPe9jA==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.23.tgz", + "integrity": "sha512-F2/6HZh8Q9RsgkOIkRrckldbhPjIZY7d4mT9LYuW68miwGQ5l7CkAgcz9fRRiurA0+YJhtsbx/EyrD9DmX9BOw==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.22", - "@nomicfoundation/edr-darwin-x64": "0.12.0-next.22", - "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.22", - "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.22", - "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.22", - "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.22", - "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.22" + "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.23", + "@nomicfoundation/edr-darwin-x64": "0.12.0-next.23", + "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.23", + "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.23", + "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.23", + "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.23", + "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.23" }, "engines": { "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.22.tgz", - "integrity": "sha512-TpEBSKyMZJEPvYwBPYclC2b+qobKjn1YhVa7aJ1R7RMPy5dJ/PqsrUK5UuUFFybBqoIorru5NTcsyCMWP5T/Fg==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.23.tgz", + "integrity": "sha512-Amh7mRoDzZyJJ4efqoePqdoZOzharmSOttZuJDlVE5yy07BoE8hL6ZRpa5fNYn0LCqn/KoWs8OHANWxhKDGhvQ==", "dev": true, "license": "MIT", "engines": { @@ -1853,9 +1853,9 @@ } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.22.tgz", - "integrity": "sha512-aK/+m8xUkR4u+czTVGU06nSFVH43AY6XCBoR2YjO8SglAAjCSTWK3WAfVb6FcsriMmKv4PrvoyHLMbMP+fXcGA==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.23.tgz", + "integrity": "sha512-9wn489FIQm7m0UCD+HhktjWx6vskZzeZD9oDc2k9ZvbBzdXwPp5tiDqUBJ+eQpByAzCDfteAJwRn2lQCE0U+Iw==", "dev": true, "license": "MIT", "engines": { @@ -1863,9 +1863,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.22.tgz", - "integrity": "sha512-W5vXMleG14hVzRYGPEwlHLJ6iiQE8Qh63Uj538nAz4YUI6wWSgUOZE7K2Gt1EdujZGnrt7kfDslgJ96n4nKQZw==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.23.tgz", + "integrity": "sha512-nlk5EejSzEUfEngv0Jkhqq3/wINIfF2ED9wAofc22w/V1DV99ASh9l3/e/MIHOQFecIZ9MDqt0Em9/oDyB1Uew==", "dev": true, "license": "MIT", "engines": { @@ -1873,9 +1873,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.22.tgz", - "integrity": "sha512-VDp7EB3iY8MH/fFVcgEzLDGYmtS6j2honNc0RNUCFECKPrdsngGrTG8p+YFxyVjq2m5GEsdyKo4e+BKhaUNPdg==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.23.tgz", + "integrity": "sha512-SJuPBp3Rc6vM92UtVTUxZQ/QlLhLfwTftt2XUiYohmGKB3RjGzpgduEFMCA0LEnucUckU6UHrJNFHiDm77C4PQ==", "dev": true, "license": "MIT", "engines": { @@ -1883,9 +1883,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.22.tgz", - "integrity": "sha512-XL6oA3ymRSQYyvg6hF1KIax6V/9vlWr5gJ8GPHVVODk1a/YfuEEY1osN5Zmo6aztUkSGKwSuac/3Ax7rfDDiSg==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.23.tgz", + "integrity": "sha512-NU+Qs3u7Qt6t3bJFdmmjd5CsvgI2bPPzO31KifM2Ez96/jsXYho5debtTQnimlb5NAqiHTSlxjh/F8ROcptmeQ==", "dev": true, "license": "MIT", "engines": { @@ -1893,9 +1893,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.22.tgz", - "integrity": "sha512-hmkRIXxWa9P0PwfXOAO6WUw11GyV5gpxcMunqWBTkwZ4QW/hi/CkXmlLo6VHd6ceCwpUNLhCGndBtrOPrNRi4A==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.23.tgz", + "integrity": "sha512-F78fZA2h6/ssiCSZOovlgIu0dUeI7ItKPsDDF3UUlIibef052GCXmliMinC90jVPbrjUADMd1BUwjfI0Z8OllQ==", "dev": true, "license": "MIT", "engines": { @@ -1903,9 +1903,9 @@ } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.22.tgz", - "integrity": "sha512-X7f+7KUMm00trsXAHCHJa+x1fc3QAbk2sBctyOgpET+GLrfCXbxqrccKi7op8f0zTweAVGg1Hsc8SjjC7kwFLw==", + "version": "0.12.0-next.23", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.23.tgz", + "integrity": "sha512-IfJZQJn7d/YyqhmguBIGoCKjE9dKjbu6V6iNEPApfwf5JyyjHYyyfkLU4rf7hygj57bfH4sl1jtQ6r8HnT62lw==", "dev": true, "license": "MIT", "engines": { @@ -5042,14 +5042,14 @@ } }, "node_modules/hardhat": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.6.tgz", - "integrity": "sha512-72Wn3DaEByNtpsqI2oIZnsyxvaB2p2CC6mffQ18K6NgAgh6QzdpdMYLiIFtYBRDRY7U6BMxq7fj1ryMQxQvTBA==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.7.tgz", + "integrity": "sha512-lvbNsMoJyVy96OqsDdrqoYTstfGqBoEzKnxn1oEC1TX5pG3uLM/N7zwdBHFCgiN3oNpsQMD61C1ClDVSUvLOLQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@nomicfoundation/edr": "0.12.0-next.22", + "@nomicfoundation/edr": "0.12.0-next.23", "@nomicfoundation/hardhat-errors": "^3.0.6", "@nomicfoundation/hardhat-utils": "^3.0.6", "@nomicfoundation/hardhat-vendored": "^3.0.1", diff --git a/package.json b/package.json index 2f227a49612..dc528249998 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.6", + "hardhat": "^3.1.7", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", diff --git a/test/utils/cryptography/TrieProof.test.js b/test/utils/cryptography/TrieProof.test.js index e1b28a5c398..8c923745a89 100644 --- a/test/utils/cryptography/TrieProof.test.js +++ b/test/utils/cryptography/TrieProof.test.js @@ -1,13 +1,15 @@ import { network } from 'hardhat'; import { expect } from 'chai'; -import { spawn } from 'child_process'; import { Enum } from '../../helpers/enums'; import { zip } from '../../helpers/iterate'; import { generators } from '../../helpers/random'; import { BlockTries } from '../../helpers/trie'; import { batchInBlock } from '../../helpers/txpool'; -const { ethers } = await network.connect(); +const { + ethers, + networkHelpers: { loadFixture }, +} = await network.connect(); const ProofError = Enum( 'NO_ERROR', // No error occurred during proof traversal @@ -26,43 +28,19 @@ const ProofError = Enum( 'INVALID_PROOF', // General failure during proof traversal ); -const ZeroBytes = generators.bytes.zero; - const sanitizeHexString = value => (value.length % 2 ? '0x0' : '0x') + value.replace(/0x/, ''); const encodeStorageLeaf = value => ethers.encodeRlp(ethers.stripZerosLeft(value)); -describe('TrieProof', function () { - before('start anvil node', async function () { - const port = 8546; - - // start process and create provider - this.process = await spawn('anvil', ['--port', port], { timeout: 30000 }); - await new Promise(resolve => this.process.stdout.once('data', resolve)); - this.provider = new ethers.JsonRpcProvider(`http://localhost:${port}`); - - // deploy mock on the hardhat network - this.mock = await ethers.deployContract('$TrieProof'); - }); - - beforeEach('use fresh storage contract with empty state for each test', async function () { - this.storage = await this.provider.getSigner(0).then(signer => ethers.deployContract('StorageSlotMock', signer)); - this.target = await this.provider.getSigner(0).then(signer => ethers.deployContract('CallReceiverMock', signer)); - - this.getProof = ({ - provider = this.provider, - address = this.storage.target, - storageKeys = [], - blockNumber = 'latest', - }) => - provider.send('eth_getProof', [ - address, - ethers.isHexString(storageKeys) ? [storageKeys] : storageKeys, - blockNumber, - ]); - }); +async function fixture() { + const mock = await ethers.deployContract('$TrieProof'); + const storage = await ethers.deployContract('StorageSlotMock'); + const target = await ethers.deployContract('CallReceiverMock'); + return { mock, storage, target }; +} - after('stop anvil node', async function () { - this.process.kill(); +describe('TrieProof', function () { + beforeEach(async function () { + Object.assign(this, await loadFixture(fixture)); }); describe('verify', function () { @@ -74,19 +52,16 @@ describe('TrieProof', function () { () => this.target.mockFunctionWithArgs(0, 1, { gasLimit: 100000 }), () => this.target.mockFunctionWithArgs(17, 42, { gasLimit: 100000 }), ], - this.provider, + ethers.provider, ); // for some reason ethers doesn't expose the transactionsRoot in blocks, so we fetch the block details via RPC instead. - const { transactionsRoot, receiptsRoot } = await this.provider.send('eth_getBlockByNumber', [ - txs.at(0).blockNumber, + const blockTries = await ethers.provider.getBlock('latest').then(block => BlockTries.from(block).ready()); + const { transactionsRoot, receiptsRoot } = await ethers.provider.send('eth_getBlockByNumber', [ + blockTries.block.number, false, ]); - const blockTries = await this.provider - .getBlock(txs.at(0).blockNumber) - .then(block => BlockTries.from(block).ready()); - // Sanity check trie roots expect(blockTries.transactionTrieRoot).to.equal(transactionsRoot); expect(blockTries.receiptTrieRoot).to.equal(receiptsRoot); @@ -141,18 +116,17 @@ describe('TrieProof', function () { ]) { it(title, async function () { // set storage state - const txs = await Promise.all( - Object.entries(slots).map(([slot, value]) => this.storage.setBytes32Slot(slot, value)), - ); + await Promise.all(Object.entries(slots).map(([slot, value]) => this.storage.setBytes32Slot(slot, value))); // get block that contains the latest storage changes - const { stateRoot, number: blockNumber } = await txs.at(-1).getBlock(); + const { stateRoot } = await ethers.provider.send('eth_getBlockByNumber', ['latest', false]); // build storage proofs for all storage slots (in that block) - const { accountProof, storageHash, storageProof, codeHash } = await this.getProof({ - storageKeys: Object.keys(slots), - blockNumber: ethers.toBeHex(blockNumber), - }); + const { accountProof, storageHash, storageProof, codeHash } = await ethers.provider.send('eth_getProof', [ + this.storage.target, + Object.keys(slots), + 'latest', + ]); // Verify account details in the block's state trie await expect( @@ -184,7 +158,7 @@ describe('TrieProof', function () { }); it('returns false for invalid proof', async function () { - await expect(this.mock.$verify(ZeroBytes, ethers.ZeroHash, '0x', [])).to.eventually.be.false; + await expect(this.mock.$verify(generators.bytes.zero, ethers.ZeroHash, '0x', [])).to.eventually.be.false; }); }); @@ -195,7 +169,7 @@ describe('TrieProof', function () { .withArgs(ProofError.EMPTY_KEY); await expect(this.mock.$tryTraverse(ethers.ZeroHash, '0x', [])).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.EMPTY_KEY, ]); }); @@ -207,7 +181,7 @@ describe('TrieProof', function () { const { storageHash, storageProof: [{ proof }], - } = await this.getProof({ storageKeys: [slot] }); + } = await ethers.provider.send('eth_getProof', [this.storage.target, [slot], 'latest']); // Correct root hash await expect(this.mock.$verify(encodeStorageLeaf(value), storageHash, ethers.keccak256(slot), proof)).to @@ -229,7 +203,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.INVALID_ROOT); await expect(this.mock.$tryTraverse(invalidHash, ethers.keccak256(slot), proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.INVALID_ROOT, ]); }); @@ -244,7 +218,7 @@ describe('TrieProof', function () { const { storageHash, storageProof: [{ proof }], - } = await this.getProof({ storageKeys: [slot] }); + } = await ethers.provider.send('eth_getProof', [this.storage.target, [slot], 'latest']); // Correct proof await expect(this.mock.$verify(encodeStorageLeaf(value), storageHash, ethers.keccak256(slot), proof)).to @@ -267,7 +241,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.INVALID_LARGE_NODE); await expect(this.mock.$tryTraverse(storageHash, ethers.keccak256(slot), proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.INVALID_LARGE_NODE, ]); }); @@ -283,7 +257,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.INVALID_SHORT_NODE); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.INVALID_SHORT_NODE, ]); }); @@ -296,7 +270,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.EMPTY_VALUE); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.EMPTY_VALUE, ]); }); @@ -312,7 +286,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.INVALID_EXTRA_PROOF_ELEMENT); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.INVALID_EXTRA_PROOF_ELEMENT, ]); }); @@ -331,7 +305,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.INVALID_PATH_REMAINDER); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.INVALID_PATH_REMAINDER, ]); }); @@ -345,7 +319,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.EMPTY_PATH); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), '0x00', proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.EMPTY_PATH, ]); }); @@ -363,7 +337,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.INVALID_PATH_REMAINDER); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.INVALID_PATH_REMAINDER, ]); }); @@ -379,7 +353,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.EMPTY_EXTENSION_PATH_REMAINDER); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.EMPTY_EXTENSION_PATH_REMAINDER, ]); }); @@ -397,7 +371,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.MISMATCH_LEAF_PATH_KEY_REMAINDER); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.MISMATCH_LEAF_PATH_KEY_REMAINDER, ]); }); @@ -411,7 +385,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.UNKNOWN_NODE_PREFIX); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.UNKNOWN_NODE_PREFIX, ]); }); @@ -424,7 +398,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.UNPARSEABLE_NODE); await expect(this.mock.$tryTraverse(ethers.keccak256(proof[0]), key, proof)).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.UNPARSEABLE_NODE, ]); }); @@ -434,7 +408,7 @@ describe('TrieProof', function () { .to.revertedWithCustomError(this.mock, 'TrieProofTraversalError') .withArgs(ProofError.INVALID_PROOF); await expect(this.mock.$tryTraverse(ethers.ZeroHash, '0x00', [])).to.eventually.deep.equal([ - ZeroBytes, + generators.bytes.zero, ProofError.INVALID_PROOF, ]); }); @@ -651,7 +625,7 @@ describe('TrieProof', function () { } await expect(this.mock.$tryTraverse(root, key, proof)).to.eventually.deep.equal([ - value ?? ZeroBytes, + value ?? generators.bytes.zero, error ?? ProofError.NO_ERROR, ]); }); From 34725db264330e72b25a10458e6bf5c53d64e61a Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 17 Feb 2026 15:01:49 +0100 Subject: [PATCH 37/91] fix account testing issue --- test/account/Account.behavior.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/account/Account.behavior.js b/test/account/Account.behavior.js index 2fd57258b25..a031009229e 100644 --- a/test/account/Account.behavior.js +++ b/test/account/Account.behavior.js @@ -58,8 +58,9 @@ export function shouldBehaveLikeAccountCore() { const operation = await this.mock.createUserOp(this.userOp).then(op => this.signUserOp(op)); const value = 42n; + // Forcing the gas limit here to work around a hardhat 3 issue with gas estimation await expect( - this.mockFromEntrypoint.validateUserOp(operation.packed, operation.hash(), value), + this.mockFromEntrypoint.validateUserOp(operation.packed, operation.hash(), value, { gasLimit: 200_000n }), ).to.changeEtherBalances(this.ethers, [this.mock, this.ethers.predeploy.entrypoint.v09], [-value, value]); }); }); From 3fd9f6e0899ed0cb371ce5c685e6b2d65bc4c0ad Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 20 Feb 2026 15:53:52 +0100 Subject: [PATCH 38/91] disable foundry tests in favor of hardhat solidity testing --- .github/actions/setup/action.yml | 2 +- .github/workflows/checks.yml | 15 ++++----------- foundry.toml | 11 ----------- scripts/checks/coverage.sh | 24 ------------------------ 4 files changed, 5 insertions(+), 47 deletions(-) delete mode 100755 scripts/checks/coverage.sh diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 3f5b7db5699..58eeb791e83 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -8,7 +8,7 @@ inputs: foundry: description: Whether to set up Foundry required: false - default: 'on' # 'off' | 'on' | + default: 'off' # 'off' | 'on' | java: description: Whether to set up Java required: false diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index d168c1e216b..736896ce551 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -37,6 +37,8 @@ jobs: - uses: actions/checkout@v6 - name: Set up environment uses: ./.github/actions/setup + with: + foundry: 'on' # needed by npm run test:pragma - name: Run tests and generate gas report run: npm run test - name: Check linearisation of the inheritance graph @@ -60,6 +62,8 @@ jobs: fetch-depth: 0 # Include history so patch conflicts are resolved automatically - name: Set up environment uses: ./.github/actions/setup + with: + foundry: 'on' # needed by npm run test:pragma - name: Copy non-upgradeable contracts as dependency run: | mkdir -p lib/openzeppelin-contracts @@ -78,17 +82,6 @@ jobs: with: token: ${{ github.token }} - tests-foundry: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - submodules: recursive - - name: Set up environment - uses: ./.github/actions/setup - - name: Run tests - run: forge test -vvv - coverage: runs-on: ubuntu-latest steps: diff --git a/foundry.toml b/foundry.toml index 3d9b8421ebc..47e068c5b4c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,15 +6,4 @@ optimizer_runs = 200 src = 'contracts' out = 'out' libs = ['node_modules', 'lib'] -test = 'test' cache_path = 'cache_forge' -fs_permissions = [{ access = "read", path = "./node_modules/hardhat-predeploy/bin" }] - -[lint] -exclude_lints = ["mixed-case-function", "asm-keccak256", "screaming-snake-case-immutable", "incorrect-shift", "mixed-case-variable"] -ignore = ["./contracts/interfaces/**/*.sol", "./contracts/mocks/Stateless.sol"] -lint_on_build = false - -[fuzz] -runs = 5000 -max_test_rejects = 150000 diff --git a/scripts/checks/coverage.sh b/scripts/checks/coverage.sh deleted file mode 100755 index fd8b9e84329..00000000000 --- a/scripts/checks/coverage.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -export COVERAGE=true -export FOUNDRY_FUZZ_RUNS=10 - -. scripts/set-max-old-space-size.sh - -# Hardhat coverage -hardhat coverage - -if [ "${CI:-"false"}" == "true" ]; then - # Foundry coverage - forge coverage --report lcov --ir-minimum - # Remove zero hits - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' '/,0/d' lcov.info - else - sed -i '/,0/d' lcov.info - fi -fi - -# Reports are then uploaded to Codecov automatically by workflow, and merged. From ad40b7625c24f903eed24d2ad03c046d0019febd Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 20 Feb 2026 16:04:45 +0100 Subject: [PATCH 39/91] update hardhat3 to fix eip7212 testing in solidity --- package-lock.json | 123 ++++++++++++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 76 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index ed845cb4c29..fa55d437d89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.7", + "hardhat": "^3.1.8", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", @@ -1824,28 +1824,28 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.23.tgz", - "integrity": "sha512-F2/6HZh8Q9RsgkOIkRrckldbhPjIZY7d4mT9LYuW68miwGQ5l7CkAgcz9fRRiurA0+YJhtsbx/EyrD9DmX9BOw==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.24.tgz", + "integrity": "sha512-/NwB9yX7uBs/FIJKHBZo2hVhP7g3v6LbE21JvTLvshgb+XscyaRRUmzB//ankxLGJ1TehtXAf/Qh/a19vgpiig==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.23", - "@nomicfoundation/edr-darwin-x64": "0.12.0-next.23", - "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.23", - "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.23", - "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.23", - "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.23", - "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.23" + "@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" }, "engines": { "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.23.tgz", - "integrity": "sha512-Amh7mRoDzZyJJ4efqoePqdoZOzharmSOttZuJDlVE5yy07BoE8hL6ZRpa5fNYn0LCqn/KoWs8OHANWxhKDGhvQ==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.24.tgz", + "integrity": "sha512-lYcD9IM52G0hk/3Sso2Rpdpyfafy3aHH0GsSy/FVog9UrEkmmU14AmccE18/zTL+UyV0yzYMDOmh6y83SD/lbg==", "dev": true, "license": "MIT", "engines": { @@ -1853,9 +1853,9 @@ } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.23.tgz", - "integrity": "sha512-9wn489FIQm7m0UCD+HhktjWx6vskZzeZD9oDc2k9ZvbBzdXwPp5tiDqUBJ+eQpByAzCDfteAJwRn2lQCE0U+Iw==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.24.tgz", + "integrity": "sha512-cHDJZlPDpDXJXxQDVM0TGzEuNvV3wW94gipEdjNxZHeC9T2/NU/5GUoQajMJgvCZ6PWDlRMwIBRtM1jC/ny5DA==", "dev": true, "license": "MIT", "engines": { @@ -1863,9 +1863,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.23.tgz", - "integrity": "sha512-nlk5EejSzEUfEngv0Jkhqq3/wINIfF2ED9wAofc22w/V1DV99ASh9l3/e/MIHOQFecIZ9MDqt0Em9/oDyB1Uew==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.24.tgz", + "integrity": "sha512-G/iln4W79CR9f68+crBZM1kBdmmK3IbQCD4b5u+iqby+H5BOLSPQmjeW9UREK5WSecnv7Oxr/ZTHHRq/w9pUPA==", "dev": true, "license": "MIT", "engines": { @@ -1873,9 +1873,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.23.tgz", - "integrity": "sha512-SJuPBp3Rc6vM92UtVTUxZQ/QlLhLfwTftt2XUiYohmGKB3RjGzpgduEFMCA0LEnucUckU6UHrJNFHiDm77C4PQ==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.24.tgz", + "integrity": "sha512-wt6UuOutufL3UTSyMiwPOyfRly3uQEFHASXqLsNjgp4qBrm0s+kkyaYpAe8h53lGzZmXIDOAbO0P/fwxnLCnWw==", "dev": true, "license": "MIT", "engines": { @@ -1883,9 +1883,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.23.tgz", - "integrity": "sha512-NU+Qs3u7Qt6t3bJFdmmjd5CsvgI2bPPzO31KifM2Ez96/jsXYho5debtTQnimlb5NAqiHTSlxjh/F8ROcptmeQ==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.24.tgz", + "integrity": "sha512-mHgkUSynINTnnIvZuZymJ4dMqjemGjdrzQ87rP5/SQQGRQVV82uDomSEglp9btSmbBWfPj4r4tWsV+a3844W0w==", "dev": true, "license": "MIT", "engines": { @@ -1893,9 +1893,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.23.tgz", - "integrity": "sha512-F78fZA2h6/ssiCSZOovlgIu0dUeI7ItKPsDDF3UUlIibef052GCXmliMinC90jVPbrjUADMd1BUwjfI0Z8OllQ==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.24.tgz", + "integrity": "sha512-E0XNSlPc8Hx5Nhowe5VIvAqVeT+1VUWSRqG0cZtYcpUgJZxTp8p03ojPtbyfjL4T+78GfnpmzkkLhB6S2jZ1FQ==", "dev": true, "license": "MIT", "engines": { @@ -1903,9 +1903,9 @@ } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.12.0-next.23", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.23.tgz", - "integrity": "sha512-IfJZQJn7d/YyqhmguBIGoCKjE9dKjbu6V6iNEPApfwf5JyyjHYyyfkLU4rf7hygj57bfH4sl1jtQ6r8HnT62lw==", + "version": "0.12.0-next.24", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.24.tgz", + "integrity": "sha512-PbtY2zWc4k8HK4gVnVbPohJnfrICboo6J91vxTlhnPKCWGvfGbsqLfDUAp91ExHHY+80qRfQnwaLbhJiIqLFGw==", "dev": true, "license": "MIT", "engines": { @@ -2429,6 +2429,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=14" } @@ -2722,7 +2723,6 @@ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2763,7 +2763,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3047,7 +3046,8 @@ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/browserify-aes": { "version": "1.2.0", @@ -3201,6 +3201,7 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -3227,7 +3228,6 @@ "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", @@ -3722,6 +3722,7 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -3829,6 +3830,7 @@ "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.3.1" } @@ -4059,7 +4061,6 @@ "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4483,7 +4484,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", @@ -4701,6 +4701,7 @@ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "bin": { "flat": "cli.js" } @@ -5042,14 +5043,13 @@ } }, "node_modules/hardhat": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.7.tgz", - "integrity": "sha512-lvbNsMoJyVy96OqsDdrqoYTstfGqBoEzKnxn1oEC1TX5pG3uLM/N7zwdBHFCgiN3oNpsQMD61C1ClDVSUvLOLQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.8.tgz", + "integrity": "sha512-VB1AQBEWgVQp7hd48zt4lb1bv5ndF9B05LQSaunOfu1RMjF3HErkiWFBbBPt0ZDUDv5ab+rgWnwCYwOeThFeHw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@nomicfoundation/edr": "0.12.0-next.23", + "@nomicfoundation/edr": "0.12.0-next.24", "@nomicfoundation/hardhat-errors": "^3.0.6", "@nomicfoundation/hardhat-utils": "^3.0.6", "@nomicfoundation/hardhat-vendored": "^3.0.1", @@ -5355,6 +5355,7 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "he": "bin/he" } @@ -5601,6 +5602,7 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -5611,6 +5613,7 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -5660,6 +5663,7 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -6063,6 +6067,7 @@ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -6080,6 +6085,7 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6464,6 +6470,7 @@ "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", @@ -6500,7 +6507,8 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "Python-2.0" + "license": "Python-2.0", + "peer": true }, "node_modules/mocha/node_modules/chokidar": { "version": "4.0.3", @@ -6508,6 +6516,7 @@ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -6524,6 +6533,7 @@ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -6539,6 +6549,7 @@ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -6556,6 +6567,7 @@ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -6577,6 +6589,7 @@ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -6593,6 +6606,7 @@ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -6606,6 +6620,7 @@ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -6621,7 +6636,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/mocha/node_modules/p-limit": { "version": "3.1.0", @@ -6629,6 +6645,7 @@ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -6645,6 +6662,7 @@ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -6661,6 +6679,7 @@ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -6678,6 +6697,7 @@ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 14.18.0" }, @@ -6692,6 +6712,7 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -6708,6 +6729,7 @@ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -6727,6 +6749,7 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -7269,7 +7292,6 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7524,6 +7546,7 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7837,6 +7860,7 @@ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -8792,7 +8816,8 @@ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/wrap-ansi": { "version": "7.0.0", @@ -8800,6 +8825,7 @@ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8907,6 +8933,7 @@ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -8917,6 +8944,7 @@ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -9010,7 +9038,6 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index dc528249998..b510d0fbf14 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.7", + "hardhat": "^3.1.8", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", From a618058c43e1991cb63d43cc08abff5a256e79d3 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 20 Feb 2026 16:23:57 +0100 Subject: [PATCH 40/91] remove dependency on default.allow_internal_expect_revert because hardhat doesn't support inline config --- test/utils/math/Math.t.sol | 41 +++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/test/utils/math/Math.t.sol b/test/utils/math/Math.t.sol index 4284e44a64b..8e30476578f 100644 --- a/test/utils/math/Math.t.sol +++ b/test/utils/math/Math.t.sol @@ -248,7 +248,11 @@ contract MathTest is Test { assertEq(xyLo, qdRemLo); } - /// forge-config: default.allow_internal_expect_revert = true + // expose the Math.mulDiv externally to support expectRevert + function mulDiv(uint256 x, uint256 y, uint256 d) external view returns (uint256) { + return Math.mulDiv(x, y, d); + } + function testMulDivDomain(uint256 x, uint256 y, uint256 d) public { (uint256 xyHi, ) = Math.mul512(x, y); @@ -257,18 +261,25 @@ contract MathTest is Test { // we are outside the scope of {testMulDiv}, we expect muldiv to revert vm.expectRevert(d == 0 ? stdError.divisionError : stdError.arithmeticError); - Math.mulDiv(x, y, d); + this.mulDiv(x, y, d); } // MOD EXP - /// forge-config: default.allow_internal_expect_revert = true + + // expose the Math.modExp externally to support expectRevert + function modExp(uint256 b, uint256 e, uint256 m) external view returns (uint256) { + return Math.modExp(b, e, m); + } + function testModExp(uint256 b, uint256 e, uint256 m) public { if (m == 0) { vm.expectRevert(stdError.divisionError); + uint256 result = this.modExp(b, e, m); + } else { + uint256 result = this.modExp(b, e, m); + assertLt(result, m); + assertEq(result, _nativeModExp(b, e, m)); } - uint256 result = Math.modExp(b, e, m); - assertLt(result, m); - assertEq(result, _nativeModExp(b, e, m)); } function testTryModExp(uint256 b, uint256 e, uint256 m) public view { @@ -282,16 +293,22 @@ contract MathTest is Test { } } - /// forge-config: default.allow_internal_expect_revert = true + // expose the Math.modExp externally to support expectRevert + function modExp(bytes memory b, bytes memory e, bytes memory m) external view returns (bytes memory) { + return Math.modExp(b, e, m); + } + function testModExpMemory(uint256 b, uint256 e, uint256 m) public { if (m == 0) { vm.expectRevert(stdError.divisionError); + bytes memory result = this.modExp(abi.encodePacked(b), abi.encodePacked(e), abi.encodePacked(m)); + } else { + bytes memory result = this.modExp(abi.encodePacked(b), abi.encodePacked(e), abi.encodePacked(m)); + assertEq(result.length, 0x20); + uint256 res = abi.decode(result, (uint256)); + assertLt(res, m); + assertEq(res, _nativeModExp(b, e, m)); } - bytes memory result = Math.modExp(abi.encodePacked(b), abi.encodePacked(e), abi.encodePacked(m)); - assertEq(result.length, 0x20); - uint256 res = abi.decode(result, (uint256)); - assertLt(res, m); - assertEq(res, _nativeModExp(b, e, m)); } function testTryModExpMemory(uint256 b, uint256 e, uint256 m) public view { From 2ae2edd52d4ca8b401f88d867cdaf3c3d7b40454 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 20 Feb 2026 16:25:59 +0100 Subject: [PATCH 41/91] fix solhint --- package-lock.json | 19 ++++--------------- package.json | 6 +++--- solhint.config.js => solhint.config.cjs | 0 3 files changed, 7 insertions(+), 18 deletions(-) rename solhint.config.js => solhint.config.cjs (100%) diff --git a/package-lock.json b/package-lock.json index fa55d437d89..789f68cacec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier-plugin-solidity": "^2.0.0", "rimraf": "^6.0.0", "semver": "^7.3.5", - "solhint": "^6.0.1", + "solhint": "^6.0.3", "solhint-plugin-openzeppelin": "file:scripts/solhint-custom", "solidity-ast": "^0.4.61", "undici": "^7.4.0", @@ -2820,16 +2820,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/antlr4": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.2.tgz", - "integrity": "sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=16" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -7995,16 +7985,15 @@ } }, "node_modules/solhint": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-6.0.1.tgz", - "integrity": "sha512-Lew5nhmkXqHPybzBzkMzvvWkpOJSSLTkfTZwRriWvfR2naS4YW2PsjVGaoX9tZFmHh7SuS+e2GEGo5FPYYmJ8g==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-6.0.3.tgz", + "integrity": "sha512-LYiy1bN8X9eUsti13mbS4fY6ILVxhP6VoOgqbHxCsHl5VPnxOWf7U1V9ZvgizxdInKBMW82D1FNJO+daAcWHbA==", "dev": true, "license": "MIT", "dependencies": { "@solidity-parser/parser": "^0.20.2", "ajv": "^6.12.6", "ajv-errors": "^1.0.1", - "antlr4": "^4.13.1-patch-1", "ast-parents": "^0.0.1", "better-ajv-errors": "^2.0.2", "chalk": "^4.1.2", diff --git a/package.json b/package.json index b510d0fbf14..7fc08f8138c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "lint:fix": "npm run lint:js:fix && npm run lint:sol:fix", "lint:js": "prettier --log-level warn --ignore-path .gitignore '**/*.{js,ts}' --check && eslint .", "lint:js:fix": "prettier --log-level warn --ignore-path .gitignore '**/*.{js,ts}' --write && eslint . --fix", - "lint:sol": "prettier --log-level warn --ignore-path .gitignore '{contracts,test}/**/*.sol' --check && solhint --config solhint.config.js --noPoster '{contracts,test}/**/*.sol'", + "lint:sol": "prettier --log-level warn --ignore-path .gitignore '{contracts,test}/**/*.sol' --check && solhint --config solhint.config.cjs --noPoster '{contracts,test}/**/*.sol'", "lint:sol:fix": "prettier --log-level warn --ignore-path .gitignore '{contracts,test}/**/*.sol' --write", "clean": "hardhat clean && rimraf build contracts/build", "prepack": "scripts/prepack.sh", @@ -89,7 +89,7 @@ "prettier-plugin-solidity": "^2.0.0", "rimraf": "^6.0.0", "semver": "^7.3.5", - "solhint": "^6.0.1", + "solhint": "^6.0.3", "solhint-plugin-openzeppelin": "file:scripts/solhint-custom", "solidity-ast": "^0.4.61", "undici": "^7.4.0", @@ -102,7 +102,7 @@ ], "{contracts,test}/**/*.sol": [ "prettier --log-level warn --ignore-path .gitignore --check", - "solhint --config solhint.config.js --noPoster" + "solhint --config solhint.config.cjs --noPoster" ] }, "type": "module" diff --git a/solhint.config.js b/solhint.config.cjs similarity index 100% rename from solhint.config.js rename to solhint.config.cjs From bfea5cc79060dbd54cb9393689a14cd3d774952d Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 26 Feb 2026 22:47:26 +0100 Subject: [PATCH 42/91] add changeset --- .changeset/spotty-teeth-show.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-teeth-show.md diff --git a/.changeset/spotty-teeth-show.md b/.changeset/spotty-teeth-show.md new file mode 100644 index 00000000000..7f7f955389f --- /dev/null +++ b/.changeset/spotty-teeth-show.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': minor +--- + +Migrate test suite and helpers to Hardhat 3 From ce3943be088ebcc919f378cb66ca1c2aa066cbff Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 26 Feb 2026 22:50:04 +0100 Subject: [PATCH 43/91] codespell --- hardhat/hardhat-exposed/internal/expose.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hardhat/hardhat-exposed/internal/expose.ts b/hardhat/hardhat-exposed/internal/expose.ts index a5c49ecd431..7e6fbfdb588 100644 --- a/hardhat/hardhat-exposed/internal/expose.ts +++ b/hardhat/hardhat-exposed/internal/expose.ts @@ -694,16 +694,16 @@ function getFunctions( ): FunctionDefinition[] { const parents = contract.linearizedBaseContracts.map(deref('ContractDefinition')); - const overriden = new Set(); + const overridden = new Set(); const res = []; for (const parent of parents) { for (const fn of findAll('FunctionDefinition', parent)) { - if (!overriden.has(fn.id) && (!subset || subset.includes(fn.visibility))) { + if (!overridden.has(fn.id) && (!subset || subset.includes(fn.visibility))) { res.push(fn); } for (const b of fn.baseFunctions ?? []) { - overriden.add(b); + overridden.add(b); } } } From d4648cc4079b177314f0a4879027ed4e1b36ae9b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 26 Feb 2026 22:57:42 +0100 Subject: [PATCH 44/91] fetch submodules --- .github/workflows/checks.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 736896ce551..04d973a564e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -35,6 +35,8 @@ jobs: GAS: true steps: - uses: actions/checkout@v6 + with: + submodules: recursive - name: Set up environment uses: ./.github/actions/setup with: @@ -59,6 +61,7 @@ jobs: steps: - uses: actions/checkout@v6 with: + submodules: recursive fetch-depth: 0 # Include history so patch conflicts are resolved automatically - name: Set up environment uses: ./.github/actions/setup @@ -86,6 +89,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + with: + submodules: recursive - name: Set up environment uses: ./.github/actions/setup - name: Run coverage @@ -98,6 +103,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + with: + submodules: recursive - name: Set up environment uses: ./.github/actions/setup - name: Compile harnesses From 37e96ffb4fb1edc333c9007729af4e5b01a13521 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 26 Feb 2026 23:05:55 +0100 Subject: [PATCH 45/91] install forge when running halmos --- .github/workflows/formal-verification.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/formal-verification.yml b/.github/workflows/formal-verification.yml index f8a10cac91c..e6bea02d375 100644 --- a/.github/workflows/formal-verification.yml +++ b/.github/workflows/formal-verification.yml @@ -55,6 +55,7 @@ jobs: - name: Set up environment uses: ./.github/actions/setup with: + foundry: 'on' python: 'on' python-requirements: 'fv-requirements.txt' - name: Run Halmos From b9ffcbbb2ed600216437e06c73fdcecff13e9699 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 26 Feb 2026 23:26:23 +0100 Subject: [PATCH 46/91] migrate new tests --- test/crosschain/BridgeERC1155.behavior.js | 84 ++++++++--------- test/crosschain/BridgeERC1155.test.js | 29 +++--- test/crosschain/BridgeERC721.behavior.js | 92 +++++++++++-------- test/crosschain/BridgeERC721.test.js | 29 +++--- .../extensions/ERC1155Crosschain.test.js | 21 +++-- .../extensions/ERC721Crosschain.test.js | 21 +++-- 6 files changed, 146 insertions(+), 130 deletions(-) diff --git a/test/crosschain/BridgeERC1155.behavior.js b/test/crosschain/BridgeERC1155.behavior.js index a36d6a38ab2..0b664676333 100644 --- a/test/crosschain/BridgeERC1155.behavior.js +++ b/test/crosschain/BridgeERC1155.behavior.js @@ -1,29 +1,29 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; const ids = [17n, 42n]; const values = [100n, 320n]; -function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCustodial = false } = {}) { +export function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCustodial = false } = {}) { describe('bridge ERC1155 like', function () { beforeEach(function () { // helper this.encodePayload = (from, to, ids, values) => ethers.AbiCoder.defaultAbiCoder().encode( ['bytes', 'bytes', 'uint256[]', 'uint256[]'], - [this.chain.toErc7930(from), to.target ?? to.address ?? to, ids, values], + [this.helpers.chain.toErc7930(from), to.target ?? to.address ?? to, ids, values], ); }); it('bridge setup', async function () { - await expect(this.bridgeA.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeA.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ this.gateway.target, - this.chain.toErc7930(this.bridgeB), + this.helpers.chain.toErc7930(this.bridgeB), ]); - await expect(this.bridgeB.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeB.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ this.gateway.target, - this.chain.toErc7930(this.bridgeA), + this.helpers.chain.toErc7930(this.bridgeA), ]); }); @@ -38,7 +38,7 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeA.connect(alice).getFunction('crosschainTransferFrom(address,bytes,uint256,uint256)')( alice, - this.chain.toErc7930(bruce), + this.helpers.chain.toErc7930(bruce), ids[0], values[0], ), @@ -54,12 +54,12 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust ) // crosschain transfer sent .to.emit(this.bridgeA, 'CrosschainMultiTokenTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), ids.slice(0, 1), values.slice(0, 1)) + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), ids.slice(0, 1), values.slice(0, 1)) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeB, 'CrosschainMultiTokenTransferReceived') - .withArgs(anyValue, this.chain.toErc7930(alice), bruce, ids.slice(0, 1), values.slice(0, 1)) + .withArgs(anyValue, this.helpers.chain.toErc7930(alice), bruce, ids.slice(0, 1), values.slice(0, 1)) // tokens are minted on chain B .to.emit(this.tokenB, 'TransferSingle') .withArgs( @@ -74,7 +74,7 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeB.connect(bruce).getFunction('crosschainTransferFrom(address,bytes,uint256,uint256)')( bruce, - this.chain.toErc7930(chris), + this.helpers.chain.toErc7930(chris), ids[0], values[0], ), @@ -90,12 +90,12 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust ) // crosschain transfer sent .to.emit(this.bridgeB, 'CrosschainMultiTokenTransferSent') - .withArgs(anyValue, bruce, this.chain.toErc7930(chris), ids.slice(0, 1), values.slice(0, 1)) + .withArgs(anyValue, bruce, this.helpers.chain.toErc7930(chris), ids.slice(0, 1), values.slice(0, 1)) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeA, 'CrosschainMultiTokenTransferReceived') - .withArgs(anyValue, this.chain.toErc7930(bruce), chris, ids.slice(0, 1), values.slice(0, 1)) + .withArgs(anyValue, this.helpers.chain.toErc7930(bruce), chris, ids.slice(0, 1), values.slice(0, 1)) // bridge on chain A releases custody of the token .to.emit(this.tokenA, 'TransferSingle') .withArgs( @@ -117,7 +117,7 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeA.connect(alice).getFunction('crosschainTransferFrom(address,bytes,uint256[],uint256[])')( alice, - this.chain.toErc7930(bruce), + this.helpers.chain.toErc7930(bruce), ids, values, ), @@ -133,12 +133,12 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust ) // crosschain transfer sent .to.emit(this.bridgeA, 'CrosschainMultiTokenTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), ids, values) + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), ids, values) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeB, 'CrosschainMultiTokenTransferReceived') - .withArgs(anyValue, this.chain.toErc7930(alice), bruce, ids, values) + .withArgs(anyValue, this.helpers.chain.toErc7930(alice), bruce, ids, values) // tokens are minted on chain B .to.emit(this.tokenB, 'TransferBatch') .withArgs( @@ -153,7 +153,7 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeB.connect(bruce).getFunction('crosschainTransferFrom(address,bytes,uint256[],uint256[])')( bruce, - this.chain.toErc7930(chris), + this.helpers.chain.toErc7930(chris), ids, values, ), @@ -169,12 +169,12 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust ) // crosschain transfer sent .to.emit(this.bridgeB, 'CrosschainMultiTokenTransferSent') - .withArgs(anyValue, bruce, this.chain.toErc7930(chris), ids, values) + .withArgs(anyValue, bruce, this.helpers.chain.toErc7930(chris), ids, values) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeA, 'CrosschainMultiTokenTransferReceived') - .withArgs(anyValue, this.chain.toErc7930(bruce), chris, ids, values) + .withArgs(anyValue, this.helpers.chain.toErc7930(bruce), chris, ids, values) // bridge on chain A releases custody of the token .to.emit(this.tokenA, 'TransferBatch') .withArgs( @@ -197,13 +197,13 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeA.connect(alice).getFunction('crosschainTransferFrom(address,bytes,uint256[],uint256[])')( alice, - this.chain.toErc7930(bruce), + this.helpers.chain.toErc7930(bruce), ids, values, ), ) .to.emit(this.bridgeA, 'CrosschainMultiTokenTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), ids, values); + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), ids, values); }); it('spender is allowed for all', async function () { @@ -216,13 +216,13 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeA.connect(chris).getFunction('crosschainTransferFrom(address,bytes,uint256[],uint256[])')( alice, - this.chain.toErc7930(bruce), + this.helpers.chain.toErc7930(bruce), ids, values, ), ) .to.emit(this.bridgeA, 'CrosschainMultiTokenTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), ids, values); + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), ids, values); }); }); @@ -237,7 +237,7 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeA.connect(chris).getFunction('crosschainTransferFrom(address,bytes,uint256[],uint256[])')( alice, - this.chain.toErc7930(bruce), + this.helpers.chain.toErc7930(bruce), ids, values, ), @@ -254,7 +254,7 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.bridgeA.connect(alice).getFunction('crosschainTransferFrom(address,bytes,uint256[],uint256[])')( alice, - this.chain.toErc7930(bruce), + this.helpers.chain.toErc7930(bruce), ids, values, ), @@ -273,12 +273,12 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust .connect(notGateway) .receiveMessage( ethers.ZeroHash, - this.chain.toErc7930(this.tokenB), + this.helpers.chain.toErc7930(this.tokenB), this.encodePayload(notGateway, notGateway, ids, values), ), ) .to.be.revertedWithCustomError(this.bridgeA, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(notGateway, this.chain.toErc7930(this.tokenB)); + .withArgs(notGateway, this.helpers.chain.toErc7930(this.tokenB)); }); it('only counterpart can send a crosschain message', async function () { @@ -287,47 +287,47 @@ function shouldBehaveLikeBridgeERC1155({ chainAIsCustodial = false, chainBIsCust await expect( this.gateway .connect(invalid) - .sendMessage(this.chain.toErc7930(this.bridgeA), this.encodePayload(invalid, invalid, ids, values), []), + .sendMessage( + this.helpers.chain.toErc7930(this.bridgeA), + this.encodePayload(invalid, invalid, ids, values), + [], + ), ) .to.be.revertedWithCustomError(this.bridgeA, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(this.gateway, this.chain.toErc7930(invalid)); + .withArgs(this.gateway, this.helpers.chain.toErc7930(invalid)); }); }); describe('reconfiguration', function () { it('updating a link emits an event', async function () { - const newGateway = await ethers.deployContract('$ERC7786GatewayMock'); - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newGateway = await this.ethers.deployContract('$ERC7786GatewayMock'); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(newGateway, newCounterpart, true)) .to.emit(this.bridgeA, 'LinkRegistered') .withArgs(newGateway, newCounterpart); - await expect(this.bridgeA.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeA.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ newGateway.target, newCounterpart, ]); }); it('cannot override configuration if "allowOverride" is false', async function () { - const newGateway = await ethers.deployContract('$ERC7786GatewayMock'); - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newGateway = await this.ethers.deployContract('$ERC7786GatewayMock'); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(newGateway, newCounterpart, false)) .to.be.revertedWithCustomError(this.bridgeA, 'LinkAlreadyRegistered') - .withArgs(this.chain.erc7930); + .withArgs(this.helpers.chain.erc7930); }); it('reject invalid gateway', async function () { const notAGateway = this.accounts[0]; - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(notAGateway, newCounterpart, false)).to.be.revertedWithoutReason(); }); }); }); } - -module.exports = { - shouldBehaveLikeBridgeERC1155, -}; diff --git a/test/crosschain/BridgeERC1155.test.js b/test/crosschain/BridgeERC1155.test.js index 03585a0308c..cc766855290 100644 --- a/test/crosschain/BridgeERC1155.test.js +++ b/test/crosschain/BridgeERC1155.test.js @@ -1,19 +1,20 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeBridgeERC1155 } from './BridgeERC1155.behavior'; -const { impersonate } = require('../helpers/account'); -const { getLocalChain } = require('../helpers/chains'); - -const { shouldBehaveLikeBridgeERC1155 } = require('./BridgeERC1155.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const chain = await getLocalChain(); const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await impersonate(gateway); + const gatewayAsEOA = await helpers.impersonate(gateway); // Chain A: legacy ERC1155 with bridge const tokenA = await ethers.deployContract('$ERC1155', ['https://token-cdn-domain/{id}.json']); @@ -22,21 +23,21 @@ async function fixture() { // Chain B: ERC1155 with native bridge integration const tokenB = await ethers.deployContract('$ERC1155Crosschain', [ 'https://token-cdn-domain/{id}.json', - [[gateway, chain.toErc7930(bridgeA)]], + [[gateway, helpers.chain.toErc7930(bridgeA)]], ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, chain.toErc7930(bridgeB)); + .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); - return { chain, accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; + return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('CrosschainBridgeERC1155', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('token getters', async function () { diff --git a/test/crosschain/BridgeERC721.behavior.js b/test/crosschain/BridgeERC721.behavior.js index 41cbdab7f9c..3bb0927cecd 100644 --- a/test/crosschain/BridgeERC721.behavior.js +++ b/test/crosschain/BridgeERC721.behavior.js @@ -1,28 +1,28 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs'); +import { ethers } from 'ethers'; +import { expect } from 'chai'; +import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; const tokenId = 42n; -function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCustodial = false } = {}) { +export function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCustodial = false } = {}) { describe('bridge ERC721 like', function () { beforeEach(function () { // helper this.encodePayload = (from, to, tokenId) => ethers.AbiCoder.defaultAbiCoder().encode( ['bytes', 'bytes', 'uint256'], - [this.chain.toErc7930(from), to.target ?? to.address ?? to, tokenId], + [this.helpers.chain.toErc7930(from), to.target ?? to.address ?? to, tokenId], ); }); it('bridge setup', async function () { - await expect(this.bridgeA.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeA.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ this.gateway.target, - this.chain.toErc7930(this.bridgeB), + this.helpers.chain.toErc7930(this.bridgeB), ]); - await expect(this.bridgeB.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeB.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ this.gateway.target, - this.chain.toErc7930(this.bridgeA), + this.helpers.chain.toErc7930(this.bridgeA), ]); }); @@ -33,35 +33,39 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto await this.tokenA.connect(alice).setApprovalForAll(this.bridgeA, true); // Alice sends tokens from chain A to Bruce on chain B. - await expect(this.bridgeA.connect(alice).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), tokenId)) + await expect( + this.bridgeA.connect(alice).crosschainTransferFrom(alice, this.helpers.chain.toErc7930(bruce), tokenId), + ) // bridge on chain A takes custody of the token .to.emit(this.tokenA, 'Transfer') .withArgs(alice, chainAIsCustodial ? this.bridgeA : ethers.ZeroAddress, tokenId) // crosschain transfer sent .to.emit(this.bridgeA, 'CrosschainNonFungibleTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), tokenId) + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), tokenId) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeB, 'CrosschainNonFungibleTransferReceived') - .withArgs(anyValue, this.chain.toErc7930(alice), bruce, tokenId) + .withArgs(anyValue, this.helpers.chain.toErc7930(alice), bruce, tokenId) // tokens are minted on chain B .to.emit(this.tokenB, 'Transfer') .withArgs(chainBIsCustodial ? this.bridgeB : ethers.ZeroAddress, bruce, tokenId); // Bruce sends tokens from chain B to Chris on chain A. - await expect(this.bridgeB.connect(bruce).crosschainTransferFrom(bruce, this.chain.toErc7930(chris), tokenId)) + await expect( + this.bridgeB.connect(bruce).crosschainTransferFrom(bruce, this.helpers.chain.toErc7930(chris), tokenId), + ) // tokens are burned on chain B .to.emit(this.tokenB, 'Transfer') .withArgs(bruce, chainBIsCustodial ? this.bridgeB : ethers.ZeroAddress, tokenId) // crosschain transfer sent .to.emit(this.bridgeB, 'CrosschainNonFungibleTransferSent') - .withArgs(anyValue, bruce, this.chain.toErc7930(chris), tokenId) + .withArgs(anyValue, bruce, this.helpers.chain.toErc7930(chris), tokenId) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeA, 'CrosschainNonFungibleTransferReceived') - .withArgs(anyValue, this.chain.toErc7930(bruce), chris, tokenId) + .withArgs(anyValue, this.helpers.chain.toErc7930(bruce), chris, tokenId) // bridge on chain A releases custody of the token .to.emit(this.tokenA, 'Transfer') .withArgs(chainAIsCustodial ? this.bridgeA : ethers.ZeroAddress, chris, tokenId); @@ -75,9 +79,11 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto await this.tokenA.$_mint(alice, tokenId); await this.tokenA.connect(alice).setApprovalForAll(this.bridgeA, true); - await expect(this.bridgeA.connect(alice).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), tokenId)) + await expect( + this.bridgeA.connect(alice).crosschainTransferFrom(alice, this.helpers.chain.toErc7930(bruce), tokenId), + ) .to.emit(this.bridgeA, 'CrosschainNonFungibleTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), tokenId); + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), tokenId); }); it('spender is allowed for all', async function () { @@ -89,14 +95,18 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto await this.tokenA.connect(alice).setApprovalForAll(chris, true); // david is not allowed - await expect(this.bridgeA.connect(david).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), tokenId)) + await expect( + this.bridgeA.connect(david).crosschainTransferFrom(alice, this.helpers.chain.toErc7930(bruce), tokenId), + ) .to.be.revertedWithCustomError(this.tokenA, 'ERC721InsufficientApproval') .withArgs(david, tokenId); // chris is allowed - await expect(this.bridgeA.connect(chris).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), tokenId)) + await expect( + this.bridgeA.connect(chris).crosschainTransferFrom(alice, this.helpers.chain.toErc7930(bruce), tokenId), + ) .to.emit(this.bridgeA, 'CrosschainNonFungibleTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), tokenId); + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), tokenId); }); it('spender is allowed for specific token', async function () { @@ -111,15 +121,17 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto // chris is not allowed to transfer otherTokenId await expect( - this.bridgeA.connect(chris).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), otherTokenId), + this.bridgeA.connect(chris).crosschainTransferFrom(alice, this.helpers.chain.toErc7930(bruce), otherTokenId), ) .to.be.revertedWithCustomError(this.tokenA, 'ERC721InsufficientApproval') .withArgs(chris, otherTokenId); // chris is allowed to transfer tokenId - await expect(this.bridgeA.connect(chris).crosschainTransferFrom(alice, this.chain.toErc7930(bruce), tokenId)) + await expect( + this.bridgeA.connect(chris).crosschainTransferFrom(alice, this.helpers.chain.toErc7930(bruce), tokenId), + ) .to.emit(this.bridgeA, 'CrosschainNonFungibleTransferSent') - .withArgs(anyValue, alice, this.chain.toErc7930(bruce), tokenId); + .withArgs(anyValue, alice, this.helpers.chain.toErc7930(bruce), tokenId); }); }); @@ -129,7 +141,9 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto const tokenId = 17n; await expect( - this.bridgeA.connect(alice).crosschainTransferFrom(ethers.ZeroAddress, this.chain.toErc7930(bruce), tokenId), + this.bridgeA + .connect(alice) + .crosschainTransferFrom(ethers.ZeroAddress, this.helpers.chain.toErc7930(bruce), tokenId), ) .to.be.revertedWithCustomError(this.tokenA, 'ERC721NonexistentToken') .withArgs(tokenId); @@ -143,7 +157,9 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto await this.tokenA.connect(alice).setApprovalForAll(this.bridgeA, true); await this.tokenA.connect(alice).setApprovalForAll(bruce, true); - await expect(this.bridgeA.connect(bruce).crosschainTransferFrom(bruce, this.chain.toErc7930(bruce), tokenId)) + await expect( + this.bridgeA.connect(bruce).crosschainTransferFrom(bruce, this.helpers.chain.toErc7930(bruce), tokenId), + ) .to.be.revertedWithCustomError(this.tokenA, 'ERC721IncorrectOwner') .withArgs(bruce, tokenId, alice); }); @@ -158,12 +174,12 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto .connect(notGateway) .receiveMessage( ethers.ZeroHash, - this.chain.toErc7930(this.tokenB), + this.helpers.chain.toErc7930(this.tokenB), this.encodePayload(notGateway, notGateway, tokenId), ), ) .to.be.revertedWithCustomError(this.bridgeA, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(notGateway, this.chain.toErc7930(this.tokenB)); + .withArgs(notGateway, this.helpers.chain.toErc7930(this.tokenB)); }); it('only counterpart can send a crosschain message', async function () { @@ -172,47 +188,43 @@ function shouldBehaveLikeBridgeERC721({ chainAIsCustodial = false, chainBIsCusto await expect( this.gateway .connect(invalid) - .sendMessage(this.chain.toErc7930(this.bridgeA), this.encodePayload(invalid, invalid, tokenId), []), + .sendMessage(this.helpers.chain.toErc7930(this.bridgeA), this.encodePayload(invalid, invalid, tokenId), []), ) .to.be.revertedWithCustomError(this.bridgeA, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(this.gateway, this.chain.toErc7930(invalid)); + .withArgs(this.gateway, this.helpers.chain.toErc7930(invalid)); }); }); describe('reconfiguration', function () { it('updating a link emits an event', async function () { - const newGateway = await ethers.deployContract('$ERC7786GatewayMock'); - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newGateway = await this.ethers.deployContract('$ERC7786GatewayMock'); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(newGateway, newCounterpart, true)) .to.emit(this.bridgeA, 'LinkRegistered') .withArgs(newGateway, newCounterpart); - await expect(this.bridgeA.getLink(this.chain.erc7930)).to.eventually.deep.equal([ + await expect(this.bridgeA.getLink(this.helpers.chain.erc7930)).to.eventually.deep.equal([ newGateway.target, newCounterpart, ]); }); it('cannot override configuration if "allowOverride" is false', async function () { - const newGateway = await ethers.deployContract('$ERC7786GatewayMock'); - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newGateway = await this.ethers.deployContract('$ERC7786GatewayMock'); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(newGateway, newCounterpart, false)) .to.be.revertedWithCustomError(this.bridgeA, 'LinkAlreadyRegistered') - .withArgs(this.chain.erc7930); + .withArgs(this.helpers.chain.erc7930); }); it('reject invalid gateway', async function () { const notAGateway = this.accounts[0]; - const newCounterpart = this.chain.toErc7930(this.accounts[0]); + const newCounterpart = this.helpers.chain.toErc7930(this.accounts[0]); await expect(this.bridgeA.$_setLink(notAGateway, newCounterpart, false)).to.be.revertedWithoutReason(); }); }); }); } - -module.exports = { - shouldBehaveLikeBridgeERC721, -}; diff --git a/test/crosschain/BridgeERC721.test.js b/test/crosschain/BridgeERC721.test.js index 00b8188dc79..a0c28f96877 100644 --- a/test/crosschain/BridgeERC721.test.js +++ b/test/crosschain/BridgeERC721.test.js @@ -1,19 +1,20 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeBridgeERC721 } from './BridgeERC721.behavior'; -const { impersonate } = require('../helpers/account'); -const { getLocalChain } = require('../helpers/chains'); - -const { shouldBehaveLikeBridgeERC721 } = require('./BridgeERC721.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const chain = await getLocalChain(); const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await impersonate(gateway); + const gatewayAsEOA = await helpers.impersonate(gateway); // Chain A: legacy ERC721 with bridge const tokenA = await ethers.deployContract('$ERC721', ['Token1', 'T1']); @@ -23,21 +24,21 @@ async function fixture() { const tokenB = await ethers.deployContract('$ERC721Crosschain', [ 'Token2', 'T2', - [[gateway, chain.toErc7930(bridgeA)]], + [[gateway, helpers.chain.toErc7930(bridgeA)]], ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, chain.toErc7930(bridgeB)); + .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); - return { chain, accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; + return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('CrosschainBridgeERC721', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); it('token getters', async function () { diff --git a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js index 7a50bf3fef3..c9938263fb0 100644 --- a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js +++ b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js @@ -1,14 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeBridgeERC1155 } from '../../../crosschain/BridgeERC1155.behavior'; -const { impersonate } = require('../../../helpers/account'); -const { getLocalChain } = require('../../../helpers/chains'); - -const { shouldBehaveLikeBridgeERC1155 } = require('../../../crosschain/BridgeERC1155.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers: { chain, impersonate }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const chain = await getLocalChain(); const accounts = await ethers.getSigners(); // Mock gateway @@ -31,12 +32,12 @@ async function fixture() { .to.emit(bridgeA, 'LinkRegistered') .withArgs(gateway, chain.toErc7930(bridgeB)); - return { chain, accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; + return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('ERC1155Crosschain', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeBridgeERC1155(); diff --git a/test/token/ERC721/extensions/ERC721Crosschain.test.js b/test/token/ERC721/extensions/ERC721Crosschain.test.js index 06f92676563..8a353a5eaf7 100644 --- a/test/token/ERC721/extensions/ERC721Crosschain.test.js +++ b/test/token/ERC721/extensions/ERC721Crosschain.test.js @@ -1,14 +1,15 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { shouldBehaveLikeBridgeERC721 } from '../../../crosschain/BridgeERC721.behavior'; -const { impersonate } = require('../../../helpers/account'); -const { getLocalChain } = require('../../../helpers/chains'); - -const { shouldBehaveLikeBridgeERC721 } = require('../../../crosschain/BridgeERC721.behavior'); +const connection = await network.connect(); +const { + ethers, + helpers: { chain, impersonate }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const chain = await getLocalChain(); const accounts = await ethers.getSigners(); // Mock gateway @@ -32,12 +33,12 @@ async function fixture() { .to.emit(bridgeA, 'LinkRegistered') .withArgs(gateway, chain.toErc7930(bridgeB)); - return { chain, accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; + return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('ERC721Crosschain', function () { beforeEach(async function () { - Object.assign(this, await loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeBridgeERC721(); From 331ef29f4a967dc40549ada5eef70525700358c1 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 26 Feb 2026 23:30:05 +0100 Subject: [PATCH 47/91] up --- .../extensions/ERC1155Crosschain.test.js | 16 +++++------ .../ERC20/extensions/ERC20Crosschain.test.js | 27 +++++++++---------- .../extensions/ERC721Crosschain.test.js | 16 +++++------ 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js index c9938263fb0..0e05626b1b9 100644 --- a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js +++ b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js @@ -3,18 +3,14 @@ import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC1155 } from '../../../crosschain/BridgeERC1155.behavior'; const connection = await network.connect(); -const { - ethers, - helpers: { chain, impersonate }, - networkHelpers: { loadFixture }, -} = connection; +const { ethers, helpers, networkHelpers } = connection; async function fixture() { const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await impersonate(gateway); + const gatewayAsEOA = await helpers.impersonate(gateway); // Chain A: ERC1155 with native bridge integration const tokenA = await ethers.deployContract('$ERC1155Crosschain', ['https://token-cdn-domain/{id}.json', []]); @@ -23,21 +19,21 @@ async function fixture() { // Chain B: ERC1155 with native bridge integration const tokenB = await ethers.deployContract('$ERC1155Crosschain', [ 'https://token-cdn-domain/{id}.json', - [[gateway, chain.toErc7930(bridgeA)]], + [[gateway, helpers.chain.toErc7930(bridgeA)]], ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, chain.toErc7930(bridgeB)); + .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('ERC1155Crosschain', function () { beforeEach(async function () { - Object.assign(this, connection, await loadFixture(fixture)); + Object.assign(this, connection, await networkHelpers.loadFixture(fixture)); }); shouldBehaveLikeBridgeERC1155(); diff --git a/test/token/ERC20/extensions/ERC20Crosschain.test.js b/test/token/ERC20/extensions/ERC20Crosschain.test.js index 6096ee5b6d9..4a2ad7b8900 100644 --- a/test/token/ERC20/extensions/ERC20Crosschain.test.js +++ b/test/token/ERC20/extensions/ERC20Crosschain.test.js @@ -4,18 +4,14 @@ import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs import { shouldBehaveLikeBridgeERC20 } from '../../../crosschain/BridgeERC20.behavior'; const connection = await network.connect(); -const { - ethers, - helpers: { chain, impersonate }, - networkHelpers: { loadFixture }, -} = connection; +const { ethers, helpers, networkHelpers } = connection; async function fixture() { const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await impersonate(gateway); + const gatewayAsEOA = await helpers.impersonate(gateway); // Chain A: legacy ERC20 with bridge const tokenA = await ethers.deployContract('$ERC20Crosschain', ['Token1', 'T1', []]); @@ -23,12 +19,15 @@ async function fixture() { // Chain B: ERC7802 with bridge const tokenB = await ethers.deployContract('$ERC20BridgeableMock', ['Token2', 'T2', ethers.ZeroAddress]); - const bridgeB = await ethers.deployContract('$BridgeERC7802', [[[gateway, chain.toErc7930(bridgeA)]], tokenB]); + const bridgeB = await ethers.deployContract('$BridgeERC7802', [ + [[gateway, helpers.chain.toErc7930(bridgeA)]], + tokenB, + ]); // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, chain.toErc7930(bridgeB)); + .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); await tokenB.$_setBridge(bridgeB); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; @@ -36,7 +35,7 @@ async function fixture() { describe('ERC20Crosschain', function () { beforeEach(async function () { - Object.assign(this, connection, await loadFixture(fixture)); + Object.assign(this, connection, await networkHelpers.loadFixture(fixture)); }); shouldBehaveLikeBridgeERC20(); @@ -50,18 +49,18 @@ describe('ERC20Crosschain', function () { await this.tokenA.connect(alice).approve(chris, ethers.MaxUint256); // Alice sends tokens from chain A to Bruce on chain B. - await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, chain.toErc7930(bruce), amount)) + await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, helpers.chain.toErc7930(bruce), amount)) // bridge on chain A takes custody of the funds .to.emit(this.tokenA, 'Transfer') .withArgs(alice, ethers.ZeroAddress, amount) // crosschain transfer sent .to.emit(this.tokenA, 'CrosschainFungibleTransferSent') - .withArgs(anyValue, alice, chain.toErc7930(bruce), amount) + .withArgs(anyValue, alice, helpers.chain.toErc7930(bruce), amount) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeB, 'CrosschainFungibleTransferReceived') - .withArgs(anyValue, chain.toErc7930(alice), bruce, amount) + .withArgs(anyValue, helpers.chain.toErc7930(alice), bruce, amount) // crosschain mint event .to.emit(this.tokenB, 'CrosschainMint') .withArgs(bruce, amount, this.bridgeB) @@ -76,7 +75,7 @@ describe('ERC20Crosschain', function () { await this.tokenA.$_mint(alice, amount); - await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, chain.toErc7930(bruce), amount)) + await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, helpers.chain.toErc7930(bruce), amount)) .to.be.revertedWithCustomError(this.tokenA, 'ERC20InsufficientAllowance') .withArgs(chris, 0n, amount); }); diff --git a/test/token/ERC721/extensions/ERC721Crosschain.test.js b/test/token/ERC721/extensions/ERC721Crosschain.test.js index 8a353a5eaf7..c5ff864810c 100644 --- a/test/token/ERC721/extensions/ERC721Crosschain.test.js +++ b/test/token/ERC721/extensions/ERC721Crosschain.test.js @@ -3,18 +3,14 @@ import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC721 } from '../../../crosschain/BridgeERC721.behavior'; const connection = await network.connect(); -const { - ethers, - helpers: { chain, impersonate }, - networkHelpers: { loadFixture }, -} = connection; +const { ethers, helpers, networkHelpers } = connection; async function fixture() { const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await impersonate(gateway); + const gatewayAsEOA = await helpers.impersonate(gateway); // Chain A: ERC721 with native bridge integration const tokenA = await ethers.deployContract('$ERC721Crosschain', ['Token1', 'T1', []]); @@ -24,21 +20,21 @@ async function fixture() { const tokenB = await ethers.deployContract('$ERC721Crosschain', [ 'Token2', 'T2', - [[gateway, chain.toErc7930(bridgeA)]], + [[gateway, helpers.chain.toErc7930(bridgeA)]], ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, chain.toErc7930(bridgeB)); + .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('ERC721Crosschain', function () { beforeEach(async function () { - Object.assign(this, connection, await loadFixture(fixture)); + Object.assign(this, connection, await networkHelpers.loadFixture(fixture)); }); shouldBehaveLikeBridgeERC721(); From d952f949c49b76772d444dc444c14949483fff5a Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 10 Apr 2026 17:13:28 +0200 Subject: [PATCH 48/91] update hardhat --- package-lock.json | 149 ++++++++++++++++++++++------------------------ package.json | 10 ++-- 2 files changed, 76 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5b71f3e695a..7278a10e243 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,10 +16,10 @@ "@eslint/compat": "^1.2.1", "@ethereumjs/mpt": "^10.1.0", "@noble/curves": "^2.0.1", - "@nomicfoundation/hardhat-ethers": "^4.0.4", - "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.2", - "@nomicfoundation/hardhat-mocha": "^3.0.9", - "@nomicfoundation/hardhat-network-helpers": "^3.0.3", + "@nomicfoundation/hardhat-ethers": "^4.0.7", + "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.4", + "@nomicfoundation/hardhat-mocha": "^3.0.15", + "@nomicfoundation/hardhat-network-helpers": "^3.0.4", "@openzeppelin/docs-utils": "^0.1.6", "@openzeppelin/merkle-tree": "^1.0.7", "@openzeppelin/upgrade-safe-transpiler": "^0.4.1", @@ -33,7 +33,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.8", + "hardhat": "^3.3.0", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", @@ -1602,28 +1602,28 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.24.tgz", - "integrity": "sha512-/NwB9yX7uBs/FIJKHBZo2hVhP7g3v6LbE21JvTLvshgb+XscyaRRUmzB//ankxLGJ1TehtXAf/Qh/a19vgpiig==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.29.tgz", + "integrity": "sha512-/c1LbBC3EFgOIKRup0lQ2SIqL9MLdqdOHDM9Mta5CyDOk9cQFlBSTpWCGDrh+p1BIsPFpVHqa4yjkBmZyL1aZA==", "dev": true, "license": "MIT", "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-darwin-arm64": "0.12.0-next.29", + "@nomicfoundation/edr-darwin-x64": "0.12.0-next.29", + "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.29", + "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.29", + "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.29", + "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.29", + "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.29" }, "engines": { "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.24.tgz", - "integrity": "sha512-lYcD9IM52G0hk/3Sso2Rpdpyfafy3aHH0GsSy/FVog9UrEkmmU14AmccE18/zTL+UyV0yzYMDOmh6y83SD/lbg==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.29.tgz", + "integrity": "sha512-qzVcAkUsrVT2Za9pLzTYL/eNLS09R+JSG+4LpQ56Wg3mkjbwItn/F6C/XbGqMbNiEGfLi5kVvtYOtT7yu04/Tg==", "dev": true, "license": "MIT", "engines": { @@ -1631,9 +1631,9 @@ } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.24.tgz", - "integrity": "sha512-cHDJZlPDpDXJXxQDVM0TGzEuNvV3wW94gipEdjNxZHeC9T2/NU/5GUoQajMJgvCZ6PWDlRMwIBRtM1jC/ny5DA==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.29.tgz", + "integrity": "sha512-P2BSYLsDoM1dGi/0NO3ps3l76NbvFDAnmCUS5SLLLdG/b8RUvWKHtfZFrQgy1KCPDFiiG5IlwzcmAwsPjsyOVQ==", "dev": true, "license": "MIT", "engines": { @@ -1641,9 +1641,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.24.tgz", - "integrity": "sha512-G/iln4W79CR9f68+crBZM1kBdmmK3IbQCD4b5u+iqby+H5BOLSPQmjeW9UREK5WSecnv7Oxr/ZTHHRq/w9pUPA==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.29.tgz", + "integrity": "sha512-3SKZIaZCCuY6fAHj6GpTYpQPj3S0LFO6YUUZw3aSg1joBmM9FkP9a1IiYQOBZnZqk0Fa+pHy2dHMVWDczQHX7g==", "dev": true, "license": "MIT", "engines": { @@ -1651,9 +1651,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.24.tgz", - "integrity": "sha512-wt6UuOutufL3UTSyMiwPOyfRly3uQEFHASXqLsNjgp4qBrm0s+kkyaYpAe8h53lGzZmXIDOAbO0P/fwxnLCnWw==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.29.tgz", + "integrity": "sha512-GGDJX3We8+XQ0L1Yy2i6ulbucQPO7HpQ5IYkxFLKL0H611ErErHLawrGIvfcfaDYfnFrC/3uxWEqMQ4GhrWbBQ==", "dev": true, "license": "MIT", "engines": { @@ -1661,9 +1661,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.24.tgz", - "integrity": "sha512-mHgkUSynINTnnIvZuZymJ4dMqjemGjdrzQ87rP5/SQQGRQVV82uDomSEglp9btSmbBWfPj4r4tWsV+a3844W0w==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.29.tgz", + "integrity": "sha512-d92L55iy/EzMlu341RgFG1Pqd6Mpd1MmcYi48Na2VtMs7rqRIcAUGeLgoooScLinM5JqTIb1uYVghehFrx98gA==", "dev": true, "license": "MIT", "engines": { @@ -1671,9 +1671,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.24.tgz", - "integrity": "sha512-E0XNSlPc8Hx5Nhowe5VIvAqVeT+1VUWSRqG0cZtYcpUgJZxTp8p03ojPtbyfjL4T+78GfnpmzkkLhB6S2jZ1FQ==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.29.tgz", + "integrity": "sha512-oOupaxyR6KUzvhJ0zsVU0yfeepB3hcTKxvowq2lPPwp6cMFzPY8PFe6uck+7+rXwog0dDkEa4R/RPEE9DtUelw==", "dev": true, "license": "MIT", "engines": { @@ -1681,9 +1681,9 @@ } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.12.0-next.24", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.24.tgz", - "integrity": "sha512-PbtY2zWc4k8HK4gVnVbPohJnfrICboo6J91vxTlhnPKCWGvfGbsqLfDUAp91ExHHY+80qRfQnwaLbhJiIqLFGw==", + "version": "0.12.0-next.29", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.29.tgz", + "integrity": "sha512-QWvz4tTt1Z5JYKXODtqqdxfIQMiWFPGLVIeVJlXl2HSPJAIWTMU+EiBhlGGxP0qoUotUbdJbe7lpG4gw3yKIcA==", "dev": true, "license": "MIT", "engines": { @@ -1691,46 +1691,46 @@ } }, "node_modules/@nomicfoundation/hardhat-errors": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.7.tgz", - "integrity": "sha512-l4RrzTfJ/WO0B9Te6i9161+pRbCSFXILanmHmgRfS4Bb4pDOjs+0eQf2I2cQrnqcJ6O/bxLvChPTQuad97dmQw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.10.tgz", + "integrity": "sha512-jfQ75/t21674cIQPbQmzRSY8YdusvQhKMsT+IGEoXlJuAzw+ngWsD7y7nHT8UKQ+liHWgMA+Am2843KwP0KtiA==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-utils": "^4.0.0" + "@nomicfoundation/hardhat-utils": "^4.0.2" } }, "node_modules/@nomicfoundation/hardhat-ethers": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-4.0.5.tgz", - "integrity": "sha512-DFTpsg6MBV/JNv8Rd0M/C70Wqk3giYSAYCrBu3PeerX1eNd4wuz5kUlsX4yVoNOPpM6Lva9z+30JRDEuFEt0bQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-4.0.7.tgz", + "integrity": "sha512-7wT4/ikhnL44fzJpe4Bh+bnsuWN55P02rbULCvhmcOOCbh/vouIrfVd008Te4+gwx+rYmJtL92vthj/I9dUd0g==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-errors": "^3.0.7", - "@nomicfoundation/hardhat-utils": "^4.0.0", + "@nomicfoundation/hardhat-errors": "^3.0.10", + "@nomicfoundation/hardhat-utils": "^4.0.2", "debug": "^4.3.2", "ethereum-cryptography": "^2.2.1", "ethers": "^6.14.0" }, "peerDependencies": { - "hardhat": "^3.0.7" + "hardhat": "^3.1.11" } }, "node_modules/@nomicfoundation/hardhat-ethers-chai-matchers": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers-chai-matchers/-/hardhat-ethers-chai-matchers-3.0.3.tgz", - "integrity": "sha512-X02s5neeaBsHiXYcPsXeqbHJ2bRytsBmQRkh0JyO6AYbkIY4pM9g+M4wnCUMWPnh1wQnLyT3Iqx+g8nJ8Ont6A==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers-chai-matchers/-/hardhat-ethers-chai-matchers-3.0.4.tgz", + "integrity": "sha512-ikChHtkA50U4uyKZ1bJav1Y3wNY4UcDtXw5b3zyYJLWC82UZ9LTkb8tf7wGbU0WQQognUFwkUk7XlWQcylHRZw==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-utils": "^4.0.0", + "@nomicfoundation/hardhat-utils": "^4.0.2", "@types/chai-as-promised": "^8.0.1", "chai-as-promised": "^8.0.0", "deep-eql": "^5.0.1" }, "peerDependencies": { - "@nomicfoundation/hardhat-ethers": "^4.0.0", + "@nomicfoundation/hardhat-ethers": "^4.0.7", "chai": ">=5.1.2 <7", "ethers": "^6.14.0", "hardhat": "^3.0.0" @@ -1858,22 +1858,22 @@ } }, "node_modules/@nomicfoundation/hardhat-mocha": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-mocha/-/hardhat-mocha-3.0.11.tgz", - "integrity": "sha512-02nuOnpsDeBzwgzf89M5uMkC74xUfuENOmZDgxE5LbQ7Ftq9K+mmHP8okCr6+m+i6xUgGLLAY7CZKeaAZKtIoQ==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-mocha/-/hardhat-mocha-3.0.15.tgz", + "integrity": "sha512-nQfLCilO2GEJtFCoJX/ZUgGLTd06Yq5LJAeyIhwdZDWUPrfNMoLCIz6HFWQklhXN8KYOW7aMM+asuFp4nx3lpw==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-errors": "^3.0.7", - "@nomicfoundation/hardhat-utils": "^4.0.0", - "@nomicfoundation/hardhat-zod-utils": "^3.0.2", + "@nomicfoundation/hardhat-errors": "^3.0.10", + "@nomicfoundation/hardhat-utils": "^4.0.2", + "@nomicfoundation/hardhat-zod-utils": "^3.0.3", "chalk": "^5.3.0", "debug": "^4.3.2", "tsx": "^4.19.3", "zod": "^3.23.8" }, "peerDependencies": { - "hardhat": "^3.0.12", + "hardhat": "^3.2.0", "mocha": "^11.0.0" } }, @@ -1905,9 +1905,9 @@ } }, "node_modules/@nomicfoundation/hardhat-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.0.0.tgz", - "integrity": "sha512-Deu4od7flcM89K+SEAxmOyn7FFWGiEILrGjoxYl/Gus0tctgpLNaK3M4LIjrJ25ci8LBjGVe3i28XZA4+QGQHQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.0.2.tgz", + "integrity": "sha512-6lK0+9ygB7wGDlu11DAmTuZxhnoGbyGZC2aRaKKehW0vDNTS9iT7GUimgVvlO3B23E/HwwRKOOLmSVPC4qmmQw==", "dev": true, "license": "MIT", "dependencies": { @@ -2017,9 +2017,9 @@ "license": "MIT" }, "node_modules/@nomicfoundation/hardhat-zod-utils": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.2.tgz", - "integrity": "sha512-EtMIhi7jtpeQYd+pRQBNlxthi8OPVr/t32yn+VHHp6nwS5wgXLh6/KpvFZfJj5mBAUbOtogB7YQ4n5fpOeuggA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.3.tgz", + "integrity": "sha512-WER4/UKLpm7/nz1asvNR7EKZKKBW+48Hw7GOdcd3Rhdr3VTNuTaeIxCJpl6YxTTg+Eq/sPAWX0mr25+USs6KWw==", "dev": true, "license": "MIT", "dependencies": { @@ -4440,17 +4440,17 @@ } }, "node_modules/hardhat": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.1.10.tgz", - "integrity": "sha512-+J3LmO5j3r8bYRIiImaTT6WtT0EKcR0nfFxWq/bokAKZq7GKYf6ErKSrOuH+gFIqo+CfnrkxcgbPY20P5vuuSQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.3.0.tgz", + "integrity": "sha512-TtejTl3ZeELwlcProbWtnsnPE2wUP2VDxzsTZp/IAgQ277kz3eQtXMpRnacefNmSve/oUAYYHxZtNQEQvaLzHg==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr": "0.12.0-next.24", - "@nomicfoundation/hardhat-errors": "^3.0.7", - "@nomicfoundation/hardhat-utils": "^4.0.0", + "@nomicfoundation/edr": "0.12.0-next.29", + "@nomicfoundation/hardhat-errors": "^3.0.10", + "@nomicfoundation/hardhat-utils": "^4.0.2", "@nomicfoundation/hardhat-vendored": "^3.0.1", - "@nomicfoundation/hardhat-zod-utils": "^3.0.2", + "@nomicfoundation/hardhat-zod-utils": "^3.0.3", "@nomicfoundation/solidity-analyzer": "^0.1.1", "@sentry/core": "^9.4.0", "adm-zip": "^0.4.16", @@ -4831,13 +4831,6 @@ "node": ">= 4" } }, - "node_modules/immutable": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.8.tgz", - "integrity": "sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==", - "dev": true, - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.1", "dev": true, diff --git a/package.json b/package.json index 2378dfcaa5f..a0d151d2935 100644 --- a/package.json +++ b/package.json @@ -59,10 +59,10 @@ "@eslint/compat": "^1.2.1", "@ethereumjs/mpt": "^10.1.0", "@noble/curves": "^2.0.1", - "@nomicfoundation/hardhat-ethers": "^4.0.4", - "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.2", - "@nomicfoundation/hardhat-mocha": "^3.0.9", - "@nomicfoundation/hardhat-network-helpers": "^3.0.3", + "@nomicfoundation/hardhat-ethers": "^4.0.7", + "@nomicfoundation/hardhat-ethers-chai-matchers": "^3.0.4", + "@nomicfoundation/hardhat-mocha": "^3.0.15", + "@nomicfoundation/hardhat-network-helpers": "^3.0.4", "@openzeppelin/docs-utils": "^0.1.6", "@openzeppelin/merkle-tree": "^1.0.7", "@openzeppelin/upgrade-safe-transpiler": "^0.4.1", @@ -76,7 +76,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.1.8", + "hardhat": "^3.3.0", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", From c95eaa9a86ea357336d08d24b03c95caa0f95868 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 10 Apr 2026 17:20:39 +0200 Subject: [PATCH 49/91] hardhat now supports forge-config & hardhat-config overrides --- hardhat.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index ba50439b704..d23f1ad7dbf 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -67,8 +67,8 @@ export default defineConfig({ test: { solidity: { fuzz: { - // runs: 5000, - // maxTestRejects: 150000, + runs: 5000, + maxTestRejects: 150000, }, fsPermissions: { readDirectory: ['node_modules/hardhat-predeploy/bin'], From f9c3d33c7dd4bdc9d421f3fb4920b01c7eb43d81 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 10 Apr 2026 17:26:39 +0200 Subject: [PATCH 50/91] generate gas report --- .github/workflows/checks.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 04d973a564e..7053a70a4f0 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -42,7 +42,7 @@ jobs: with: foundry: 'on' # needed by npm run test:pragma - name: Run tests and generate gas report - run: npm run test + run: npm run test --gas-stats-json gasReport.json - name: Check linearisation of the inheritance graph run: npm run test:inheritance - name: Check pragma validity @@ -53,6 +53,7 @@ jobs: uses: ./.github/actions/gas-compare with: token: ${{ github.token }} + report: gasReport.json tests-upgradeable: runs-on: ubuntu-latest From 28f0b80696081c2e665abd350dc0d2b53283bb23 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 10 Apr 2026 18:07:38 +0200 Subject: [PATCH 51/91] update gas comparaison script --- scripts/checks/compareGasReports.js | 107 +++++++++++----------------- 1 file changed, 43 insertions(+), 64 deletions(-) diff --git a/scripts/checks/compareGasReports.js b/scripts/checks/compareGasReports.js index 20fe969092a..f1e4bb78be5 100755 --- a/scripts/checks/compareGasReports.js +++ b/scripts/checks/compareGasReports.js @@ -1,10 +1,12 @@ #!/usr/bin/env node -const fs = require('fs'); -const chalk = require('chalk'); +import fs from 'fs'; +import chalk from 'chalk'; -const { hideBin } = require('yargs/helpers'); -const { argv } = require('yargs/yargs')(hideBin(process.argv)) +import { hideBin } from 'yargs/helpers'; +import yargs from 'yargs/yargs'; + +const { argv } = yargs(hideBin(process.argv)) .env('') .options({ style: { @@ -26,21 +28,25 @@ const { argv } = require('yargs/yargs')(hideBin(process.argv)) const BASE_TX_COST = 21000; // Utilities -function sum(...args) { - return args.reduce((a, b) => a + b, 0); -} - -function average(...args) { - return sum(...args) / args.length; -} - -function variation(current, previous, offset = 0) { - return { - value: current, - delta: current - previous, - prcnt: (100 * (current - previous)) / (previous - offset), - }; -} +// const sum = (...args) => args.reduce((a, b) => a + b, 0); +// const average = (...args) => sum(...args) / args.length; +const variation = (current, previous, offset = 0) => ({ + value: current - offset, + delta: current - previous, + prcnt: (100 * (current - previous)) / (previous - offset), +}); + +const variations = (current, previous, offset = 0) => + current.min == current.max && previous.min == previous.max + ? { + avg: variation(current.avg, previous.avg, offset), + } + : { + min: variation(current.min, previous.min, offset), + max: variation(current.max, previous.max, offset), + avg: variation(current.avg, previous.avg, offset), + median: variation(current.median, previous.median, offset), + }; // Report class class Report { @@ -51,54 +57,25 @@ class Report { // Compare two reports static compare(update, ref, opts = { hideEqual: true, strictTesting: false }) { - if (JSON.stringify(update.options?.solcInfo) !== JSON.stringify(ref.options?.solcInfo)) { - console.warn('WARNING: Reports produced with non matching metadata'); - } - - // gasReporter 1.0.0 uses ".info", but 2.0.0 uses ".data" - const updateInfo = update.info ?? update.data; - const refInfo = ref.info ?? ref.data; - - const deployments = updateInfo.deployments - .map(contract => - Object.assign(contract, { previousVersion: refInfo.deployments.find(({ name }) => name === contract.name) }), - ) - .filter(contract => contract.gasData?.length && contract.previousVersion?.gasData?.length) - .flatMap(contract => [ - { - contract: contract.name, - method: '[bytecode length]', - avg: variation(contract.bytecode.length / 2 - 1, contract.previousVersion.bytecode.length / 2 - 1), - }, + return Object.entries(update.contracts) + .filter(([key]) => key in ref.contracts) + .flatMap(([key, contract]) => [ { - contract: contract.name, - method: '[construction cost]', - avg: variation( - ...[contract.gasData, contract.previousVersion.gasData].map(x => Math.round(average(...x))), - BASE_TX_COST, - ), + contract: contract.contractName, + method: '[constructor]', + ...variations(contract.deployment, ref.contracts[key].deployment, BASE_TX_COST), }, + ...Object.entries(contract.functions ?? {}) + .filter(([method]) => method in ref.contracts[key].functions) + .filter(([method, data]) => !opts.strictTesting || data.count === ref.contracts[key].functions[method].count) + .map(([method, currentData]) => ({ + contract: contract.contractName, + method, + ...variations(currentData, ref.contracts[key].functions[method], BASE_TX_COST), + })), ]) - .sort((a, b) => `${a.contract}:${a.method}`.localeCompare(`${b.contract}:${b.method}`)); - - const methods = Object.keys(updateInfo.methods) - .filter(key => refInfo.methods[key]) - .filter(key => updateInfo.methods[key].numberOfCalls > 0) - .filter( - key => !opts.strictTesting || updateInfo.methods[key].numberOfCalls === refInfo.methods[key].numberOfCalls, - ) - .map(key => ({ - contract: refInfo.methods[key].contract, - method: refInfo.methods[key].fnSig, - min: variation(...[updateInfo, refInfo].map(x => Math.min(...x.methods[key].gasData)), BASE_TX_COST), - max: variation(...[updateInfo, refInfo].map(x => Math.max(...x.methods[key].gasData)), BASE_TX_COST), - avg: variation(...[updateInfo, refInfo].map(x => Math.round(average(...x.methods[key].gasData))), BASE_TX_COST), - })) - .sort((a, b) => `${a.contract}:${a.method}`.localeCompare(`${b.contract}:${b.method}`)); - - return [] - .concat(deployments, methods) - .filter(row => !opts.hideEqual || row.min?.delta || row.max?.delta || row.avg?.delta); + .sort((a, b) => `${a.contract}:${a.method}`.localeCompare(`${b.contract}:${b.method}`)) + .filter(row => !opts.hideEqual || row.min?.delta || row.max?.delta || row.avg?.delta || row.median?.delta); } } @@ -130,6 +107,7 @@ function formatCmpShell(rows) { { txt: 'Method', length: methodLength }, { txt: 'Min', length: 30 }, { txt: 'Max', length: 30 }, + { txt: 'Median', length: 30 }, { txt: 'Avg', length: 30 }, { txt: '', length: 0 }, ]; @@ -150,6 +128,7 @@ function formatCmpShell(rows) { entry.method.padEnd(methodLength), ...formatCellShell(entry.min), ...formatCellShell(entry.max), + ...formatCellShell(entry.median), ...formatCellShell(entry.avg), '', ] From e7abecd33e435844f89b6611d053e43bccea1247 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 22 Apr 2026 13:58:08 +0200 Subject: [PATCH 52/91] use hardhat 3.4.0 and network.create() --- package-lock.json | 46 +++++++------- package.json | 2 +- test/access/AccessControl.test.js | 2 +- test/access/Ownable.test.js | 2 +- test/access/Ownable2Step.test.js | 2 +- .../AccessControlDefaultAdminRules.test.js | 2 +- .../AccessControlEnumerable.test.js | 2 +- test/access/manager/AccessManaged.test.js | 2 +- test/access/manager/AccessManager.test.js | 2 +- test/access/manager/AuthorityUtils.test.js | 2 +- test/account/Account.test.js | 2 +- test/account/AccountECDSA.test.js | 2 +- test/account/AccountEIP7702.test.js | 2 +- test/account/AccountERC7913.test.js | 2 +- test/account/AccountMultiSigner.test.js | 2 +- .../AccountMultiSignerWeighted.test.js | 2 +- test/account/AccountP256.test.js | 2 +- test/account/AccountRSA.test.js | 2 +- test/account/AccountWebAuthn.test.js | 2 +- .../AccountEIP7702WithModulesMock.test.js | 2 +- .../account/extensions/AccountERC7579.test.js | 2 +- .../extensions/AccountERC7579Hooked.test.js | 2 +- test/account/utils/EIP7702Utils.test.js | 2 +- test/account/utils/draft-ERC4337Utils.test.js | 2 +- test/account/utils/draft-ERC7579Utils.test.js | 2 +- test/crosschain/BridgeERC1155.test.js | 2 +- test/crosschain/BridgeERC20.test.js | 2 +- test/crosschain/BridgeERC721.test.js | 2 +- test/crosschain/CrosschainExecutor.test.js | 62 ++++++++++--------- test/crosschain/ERC7786Recipient.test.js | 2 +- test/finance/VestingWallet.test.js | 2 +- test/finance/VestingWalletCliff.test.js | 2 +- test/governance/Governor.test.js | 2 +- test/governance/TimelockController.test.js | 2 +- .../GovernorCountingFractional.test.js | 2 +- .../GovernorCountingOverridable.test.js | 2 +- .../extensions/GovernorCrosschain.test.js | 48 +++++++------- .../extensions/GovernorERC721.test.js | 2 +- .../extensions/GovernorNoncesKeyed.test.js | 2 +- .../GovernorPreventLateQuorum.test.js | 2 +- .../GovernorProposalGuardian.test.js | 2 +- .../GovernorSequentialProposalId.test.js | 2 +- .../extensions/GovernorStorage.test.js | 2 +- .../extensions/GovernorSuperQuorum.test.js | 2 +- .../extensions/GovernorTimelockAccess.test.js | 4 +- .../GovernorTimelockCompound.test.js | 2 +- .../GovernorTimelockControl.test.js | 2 +- .../GovernorVotesQuorumFraction.test.js | 2 +- .../GovernorVotesSuperQuorumFraction.test.js | 2 +- .../extensions/GovernorWithParams.test.js | 2 +- test/governance/utils/Votes.test.js | 2 +- test/governance/utils/VotesExtended.test.js | 2 +- test/metatx/ERC2771Context.test.js | 2 +- test/metatx/ERC2771Forwarder.test.js | 2 +- test/proxy/Clones.test.js | 2 +- test/proxy/ERC1967/ERC1967Proxy.test.js | 2 +- test/proxy/ERC1967/ERC1967Utils.test.js | 2 +- test/proxy/beacon/BeaconProxy.test.js | 2 +- test/proxy/beacon/UpgradeableBeacon.test.js | 2 +- test/proxy/transparent/ProxyAdmin.test.js | 2 +- .../TransparentUpgradeableProxy.test.js | 2 +- test/proxy/utils/Initializable.test.js | 2 +- test/proxy/utils/UUPSUpgradeable.test.js | 2 +- test/sanity.test.js | 2 +- test/token/ERC1155/ERC1155.test.js | 2 +- .../extensions/ERC1155Burnable.test.js | 2 +- .../extensions/ERC1155Crosschain.test.js | 2 +- .../extensions/ERC1155Pausable.test.js | 2 +- .../ERC1155/extensions/ERC1155Supply.test.js | 2 +- .../extensions/ERC1155URIStorage.test.js | 2 +- .../token/ERC1155/utils/ERC1155Holder.test.js | 2 +- test/token/ERC1155/utils/ERC1155Utils.test.js | 2 +- test/token/ERC20/ERC20.test.js | 2 +- test/token/ERC20/extensions/ERC1363.test.js | 2 +- .../ERC20/extensions/ERC20Burnable.test.js | 2 +- .../ERC20/extensions/ERC20Capped.test.js | 2 +- .../ERC20/extensions/ERC20Crosschain.test.js | 2 +- .../ERC20/extensions/ERC20FlashMint.test.js | 2 +- .../ERC20/extensions/ERC20Pausable.test.js | 2 +- .../ERC20/extensions/ERC20Permit.test.js | 2 +- .../token/ERC20/extensions/ERC20Votes.test.js | 2 +- .../ERC20/extensions/ERC20Wrapper.test.js | 2 +- test/token/ERC20/extensions/ERC4626.test.js | 2 +- .../extensions/draft-ERC20Bridgeable.test.js | 2 +- .../draft-ERC20TemporaryApproval.test.js | 2 +- test/token/ERC20/utils/SafeERC20.test.js | 2 +- test/token/ERC6909/ERC6909.test.js | 2 +- .../extensions/ERC6909ContentURI.test.js | 2 +- .../extensions/ERC6909Metadata.test.js | 2 +- .../extensions/ERC6909TokenSupply.test.js | 2 +- test/token/ERC721/ERC721.test.js | 2 +- test/token/ERC721/ERC721Enumerable.test.js | 2 +- .../ERC721/extensions/ERC721Burnable.test.js | 2 +- .../extensions/ERC721Consecutive.test.js | 2 +- .../extensions/ERC721Crosschain.test.js | 2 +- .../ERC721/extensions/ERC721Pausable.test.js | 2 +- .../ERC721/extensions/ERC721Royalty.test.js | 2 +- .../extensions/ERC721URIStorage.test.js | 2 +- .../ERC721/extensions/ERC721Votes.test.js | 2 +- .../ERC721/extensions/ERC721Wrapper.test.js | 2 +- test/token/ERC721/utils/ERC721Holder.test.js | 2 +- test/token/ERC721/utils/ERC721Utils.test.js | 2 +- test/utils/Address.test.js | 2 +- test/utils/Arrays.test.js | 2 +- test/utils/Base58.test.js | 2 +- test/utils/Base64.test.js | 2 +- test/utils/Blockhash.test.js | 2 +- test/utils/Bytes.test.js | 2 +- test/utils/CAIP.test.js | 2 +- test/utils/Calldata.test.js | 2 +- test/utils/Context.test.js | 2 +- test/utils/Create2.test.js | 2 +- test/utils/LowLevelCall.test.js | 2 +- test/utils/Memory.test.js | 2 +- test/utils/Multicall.test.js | 2 +- test/utils/Nonces.test.js | 2 +- test/utils/NoncesKeyed.test.js | 2 +- test/utils/Packing.test.js | 2 +- test/utils/Panic.test.js | 2 +- test/utils/Pausable.test.js | 2 +- test/utils/RLP.test.js | 2 +- test/utils/ReentrancyGuard.test.js | 2 +- test/utils/RelayedCall.test.js | 2 +- test/utils/ShortStrings.test.js | 2 +- test/utils/SimulatedCall.test.js | 2 +- test/utils/SlotDerivation.test.js | 2 +- test/utils/StorageSlot.test.js | 2 +- test/utils/Strings.test.js | 2 +- test/utils/TransientSlot.test.js | 2 +- test/utils/cryptography/ECDSA.test.js | 2 +- test/utils/cryptography/EIP712.test.js | 2 +- test/utils/cryptography/ERC7739.test.js | 2 +- test/utils/cryptography/ERC7739Utils.test.js | 2 +- test/utils/cryptography/MerkleProof.test.js | 2 +- .../cryptography/MessageHashUtils.test.js | 2 +- test/utils/cryptography/P256.test.js | 2 +- test/utils/cryptography/RSA.test.js | 2 +- .../cryptography/SignatureChecker.test.js | 2 +- test/utils/cryptography/TrieProof.test.js | 2 +- test/utils/draft-InteroperableAddress.test.js | 2 +- test/utils/introspection/ERC165.test.js | 2 +- .../utils/introspection/ERC165Checker.test.js | 2 +- test/utils/math/Math.test.js | 2 +- test/utils/math/SafeCast.test.js | 2 +- test/utils/math/SignedMath.test.js | 2 +- test/utils/structs/BitMap.test.js | 2 +- test/utils/structs/Checkpoints.test.js | 2 +- test/utils/structs/CircularBuffer.test.js | 2 +- test/utils/structs/DoubleEndedQueue.test.js | 2 +- test/utils/structs/EnumerableMap.test.js | 2 +- test/utils/structs/EnumerableSet.test.js | 2 +- test/utils/structs/Heap.test.js | 2 +- test/utils/structs/MerkleTree.test.js | 2 +- test/utils/types/Time.test.js | 2 +- 154 files changed, 235 insertions(+), 225 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7278a10e243..3a432aa8712 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.3.0", + "hardhat": "^3.4.0", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", @@ -1691,13 +1691,13 @@ } }, "node_modules/@nomicfoundation/hardhat-errors": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.10.tgz", - "integrity": "sha512-jfQ75/t21674cIQPbQmzRSY8YdusvQhKMsT+IGEoXlJuAzw+ngWsD7y7nHT8UKQ+liHWgMA+Am2843KwP0KtiA==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.11.tgz", + "integrity": "sha512-XEKplQ+FhZD1PgIGSj62scoqB/y+uG8x+V+U68m1a+4L1I46y4/gZQGIuMLkRZeqhPHsLle6ykDB+vn8qtwqzw==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-utils": "^4.0.2" + "@nomicfoundation/hardhat-utils": "^4.0.3" } }, "node_modules/@nomicfoundation/hardhat-ethers": { @@ -1905,9 +1905,9 @@ } }, "node_modules/@nomicfoundation/hardhat-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.0.2.tgz", - "integrity": "sha512-6lK0+9ygB7wGDlu11DAmTuZxhnoGbyGZC2aRaKKehW0vDNTS9iT7GUimgVvlO3B23E/HwwRKOOLmSVPC4qmmQw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.0.3.tgz", + "integrity": "sha512-JLEexWvugRK6kJioPTeisIcmmWm6yHv9n5GRLboXvhAndJNL1Z/FrQLZYZaDSmkHPE5aRK6t+zK7Rh92Yar6hQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2010,21 +2010,21 @@ } }, "node_modules/@nomicfoundation/hardhat-vendored": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.1.tgz", - "integrity": "sha512-jBOAqmEAMJ8zdfiQmTLV+c0IaSyySqkDSJ9spTy8Ts/m/mO8w364TClyfn+p4ZpxBjyX4LMa3NfC402hoDtwCg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.2.tgz", + "integrity": "sha512-v65aSwA0k15QzMUL4cFXT2CPnrjrxR1BE2V24BjNCPnlhwI/2e1Gfy7TaIGSor5yZGeiQ5ScI+wP/ovKxQBr0g==", "dev": true, "license": "MIT" }, "node_modules/@nomicfoundation/hardhat-zod-utils": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.3.tgz", - "integrity": "sha512-WER4/UKLpm7/nz1asvNR7EKZKKBW+48Hw7GOdcd3Rhdr3VTNuTaeIxCJpl6YxTTg+Eq/sPAWX0mr25+USs6KWw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.4.tgz", + "integrity": "sha512-yCiycXDEEjbNgNVQaUoGYOee6+ljYUnIOWMtYc/dYDuwlHutWr9xg/KgkgMkiZZ1R2WrZAEqsSaeZTnH7Oyz9Q==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-errors": "^3.0.7", - "@nomicfoundation/hardhat-utils": "^4.0.0" + "@nomicfoundation/hardhat-errors": "^3.0.11", + "@nomicfoundation/hardhat-utils": "^4.0.3" }, "peerDependencies": { "zod": "^3.23.8" @@ -4440,17 +4440,17 @@ } }, "node_modules/hardhat": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.3.0.tgz", - "integrity": "sha512-TtejTl3ZeELwlcProbWtnsnPE2wUP2VDxzsTZp/IAgQ277kz3eQtXMpRnacefNmSve/oUAYYHxZtNQEQvaLzHg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.4.0.tgz", + "integrity": "sha512-3QDQY4jOK4DXavkUm7itL5O9fY1AKo9CzDly9E6cQV4X1TpknMzbenpluzKjiSjpRsIBXhyV6EuGl0vaXug7Og==", "dev": true, "license": "MIT", "dependencies": { "@nomicfoundation/edr": "0.12.0-next.29", - "@nomicfoundation/hardhat-errors": "^3.0.10", - "@nomicfoundation/hardhat-utils": "^4.0.2", - "@nomicfoundation/hardhat-vendored": "^3.0.1", - "@nomicfoundation/hardhat-zod-utils": "^3.0.3", + "@nomicfoundation/hardhat-errors": "^3.0.11", + "@nomicfoundation/hardhat-utils": "^4.0.3", + "@nomicfoundation/hardhat-vendored": "^3.0.2", + "@nomicfoundation/hardhat-zod-utils": "^3.0.4", "@nomicfoundation/solidity-analyzer": "^0.1.1", "@sentry/core": "^9.4.0", "adm-zip": "^0.4.16", diff --git a/package.json b/package.json index a0d151d2935..e08f9bc9731 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.3.0", + "hardhat": "^3.4.0", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", diff --git a/test/access/AccessControl.test.js b/test/access/AccessControl.test.js index c71ac59489f..843b01658bb 100644 --- a/test/access/AccessControl.test.js +++ b/test/access/AccessControl.test.js @@ -1,7 +1,7 @@ import { network } from 'hardhat'; import { DEFAULT_ADMIN_ROLE, shouldBehaveLikeAccessControl } from './AccessControl.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/access/Ownable.test.js b/test/access/Ownable.test.js index c3e11393281..6ce733dde59 100644 --- a/test/access/Ownable.test.js +++ b/test/access/Ownable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [owner, other] = await ethers.getSigners(); diff --git a/test/access/Ownable2Step.test.js b/test/access/Ownable2Step.test.js index aed907bec28..ae3c37dff6c 100644 --- a/test/access/Ownable2Step.test.js +++ b/test/access/Ownable2Step.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [owner, accountA, accountB] = await ethers.getSigners(); diff --git a/test/access/extensions/AccessControlDefaultAdminRules.test.js b/test/access/extensions/AccessControlDefaultAdminRules.test.js index 5ce1a458531..6ed4a0ace93 100644 --- a/test/access/extensions/AccessControlDefaultAdminRules.test.js +++ b/test/access/extensions/AccessControlDefaultAdminRules.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeAccessControlDefaultAdminRules, } from '../AccessControl.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers, diff --git a/test/access/extensions/AccessControlEnumerable.test.js b/test/access/extensions/AccessControlEnumerable.test.js index 5cda4dba1a1..1c9ec49047a 100644 --- a/test/access/extensions/AccessControlEnumerable.test.js +++ b/test/access/extensions/AccessControlEnumerable.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeAccessControlEnumerable, } from '../AccessControl.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/access/manager/AccessManaged.test.js b/test/access/manager/AccessManaged.test.js index 68098c565bf..027a9612444 100644 --- a/test/access/manager/AccessManaged.test.js +++ b/test/access/manager/AccessManaged.test.js @@ -5,7 +5,7 @@ const { ethers, helpers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [admin, roleMember, other] = await ethers.getSigners(); diff --git a/test/access/manager/AccessManager.test.js b/test/access/manager/AccessManager.test.js index 434eb2de62b..cd383277f59 100644 --- a/test/access/manager/AccessManager.test.js +++ b/test/access/manager/AccessManager.test.js @@ -32,7 +32,7 @@ import { testAsGetAccess, } from './AccessManager.predicate'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { impersonate, time }, diff --git a/test/access/manager/AuthorityUtils.test.js b/test/access/manager/AuthorityUtils.test.js index f4cecffb167..4ccd32a2bb4 100644 --- a/test/access/manager/AuthorityUtils.test.js +++ b/test/access/manager/AuthorityUtils.test.js @@ -5,7 +5,7 @@ import { MAX_UINT32, MAX_UINT64 } from '../../helpers/constants'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [user, other] = await ethers.getSigners(); diff --git a/test/account/Account.test.js b/test/account/Account.test.js index 2dfc1d8ed38..3d476fa486f 100644 --- a/test/account/Account.test.js +++ b/test/account/Account.test.js @@ -7,7 +7,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountECDSA.test.js b/test/account/AccountECDSA.test.js index e228dcce199..5f9c58b7ebd 100644 --- a/test/account/AccountECDSA.test.js +++ b/test/account/AccountECDSA.test.js @@ -6,7 +6,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountEIP7702.test.js b/test/account/AccountEIP7702.test.js index dcb2cc862fb..98aae7492b6 100644 --- a/test/account/AccountEIP7702.test.js +++ b/test/account/AccountEIP7702.test.js @@ -6,7 +6,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountERC7913.test.js b/test/account/AccountERC7913.test.js index a391b71e0ad..0cf880f58c8 100644 --- a/test/account/AccountERC7913.test.js +++ b/test/account/AccountERC7913.test.js @@ -7,7 +7,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountMultiSigner.test.js b/test/account/AccountMultiSigner.test.js index 6fca664747f..aa09d95acf9 100644 --- a/test/account/AccountMultiSigner.test.js +++ b/test/account/AccountMultiSigner.test.js @@ -9,7 +9,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountMultiSignerWeighted.test.js b/test/account/AccountMultiSignerWeighted.test.js index 599b8017949..ca35a669dc6 100644 --- a/test/account/AccountMultiSignerWeighted.test.js +++ b/test/account/AccountMultiSignerWeighted.test.js @@ -9,7 +9,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountP256.test.js b/test/account/AccountP256.test.js index b23a89b598e..c84796dc3f3 100644 --- a/test/account/AccountP256.test.js +++ b/test/account/AccountP256.test.js @@ -7,7 +7,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountRSA.test.js b/test/account/AccountRSA.test.js index 9e8a30c73f3..ef2990ba870 100644 --- a/test/account/AccountRSA.test.js +++ b/test/account/AccountRSA.test.js @@ -7,7 +7,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/AccountWebAuthn.test.js b/test/account/AccountWebAuthn.test.js index 9888b1817b7..6041cf35f14 100644 --- a/test/account/AccountWebAuthn.test.js +++ b/test/account/AccountWebAuthn.test.js @@ -7,7 +7,7 @@ import { shouldBehaveLikeAccountCore, shouldBehaveLikeAccountHolder } from './Ac import { shouldBehaveLikeERC1271 } from '../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from './extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/examples/AccountEIP7702WithModulesMock.test.js b/test/account/examples/AccountEIP7702WithModulesMock.test.js index 2369040a288..bd30d77e0c4 100644 --- a/test/account/examples/AccountEIP7702WithModulesMock.test.js +++ b/test/account/examples/AccountEIP7702WithModulesMock.test.js @@ -8,7 +8,7 @@ import { shouldBehaveLikeAccountERC7579 } from '../extensions/AccountERC7579.beh import { shouldBehaveLikeERC1271 } from '../../utils/cryptography/ERC1271.behavior'; import { shouldBehaveLikeERC7821 } from '../extensions/ERC7821.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture, setBalance }, diff --git a/test/account/extensions/AccountERC7579.test.js b/test/account/extensions/AccountERC7579.test.js index f563cbc68f1..d98c95e51a9 100644 --- a/test/account/extensions/AccountERC7579.test.js +++ b/test/account/extensions/AccountERC7579.test.js @@ -6,7 +6,7 @@ import { shouldBehaveLikeAccountCore } from '../Account.behavior'; import { shouldBehaveLikeAccountERC7579 } from './AccountERC7579.behavior'; import { shouldBehaveLikeERC1271 } from '../../utils/cryptography/ERC1271.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/extensions/AccountERC7579Hooked.test.js b/test/account/extensions/AccountERC7579Hooked.test.js index e0055230755..3ba7df99e1d 100644 --- a/test/account/extensions/AccountERC7579Hooked.test.js +++ b/test/account/extensions/AccountERC7579Hooked.test.js @@ -6,7 +6,7 @@ import { shouldBehaveLikeAccountCore } from '../Account.behavior'; import { shouldBehaveLikeAccountERC7579 } from './AccountERC7579.behavior'; import { shouldBehaveLikeERC1271 } from '../../utils/cryptography/ERC1271.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/account/utils/EIP7702Utils.test.js b/test/account/utils/EIP7702Utils.test.js index 11c578f0f3e..8f042820b73 100644 --- a/test/account/utils/EIP7702Utils.test.js +++ b/test/account/utils/EIP7702Utils.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); // [NOTE] // diff --git a/test/account/utils/draft-ERC4337Utils.test.js b/test/account/utils/draft-ERC4337Utils.test.js index f2b62d1163f..1db35a0716e 100644 --- a/test/account/utils/draft-ERC4337Utils.test.js +++ b/test/account/utils/draft-ERC4337Utils.test.js @@ -8,7 +8,7 @@ const { ethers, helpers: { time }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const ADDRESS_ONE = '0x0000000000000000000000000000000000000001'; diff --git a/test/account/utils/draft-ERC7579Utils.test.js b/test/account/utils/draft-ERC7579Utils.test.js index cf14bbad59a..d485ccc90b7 100644 --- a/test/account/utils/draft-ERC7579Utils.test.js +++ b/test/account/utils/draft-ERC7579Utils.test.js @@ -16,7 +16,7 @@ import { selector } from '../../helpers/methods'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const fixture = async () => { const [sender] = await ethers.getSigners(); diff --git a/test/crosschain/BridgeERC1155.test.js b/test/crosschain/BridgeERC1155.test.js index cc766855290..c199cefc0e9 100644 --- a/test/crosschain/BridgeERC1155.test.js +++ b/test/crosschain/BridgeERC1155.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC1155 } from './BridgeERC1155.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers, diff --git a/test/crosschain/BridgeERC20.test.js b/test/crosschain/BridgeERC20.test.js index c91294139e2..b5b444ef71c 100644 --- a/test/crosschain/BridgeERC20.test.js +++ b/test/crosschain/BridgeERC20.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC20 } from './BridgeERC20.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers, diff --git a/test/crosschain/BridgeERC721.test.js b/test/crosschain/BridgeERC721.test.js index a0c28f96877..a082d1ce545 100644 --- a/test/crosschain/BridgeERC721.test.js +++ b/test/crosschain/BridgeERC721.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC721 } from './BridgeERC721.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers, diff --git a/test/crosschain/CrosschainExecutor.test.js b/test/crosschain/CrosschainExecutor.test.js index f957d5eee86..553aedc2969 100644 --- a/test/crosschain/CrosschainExecutor.test.js +++ b/test/crosschain/CrosschainExecutor.test.js @@ -1,32 +1,36 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; -const { getLocalChain } = require('../helpers/chains'); -const { - CALL_TYPE_SINGLE, +import { + CALL_TYPE_CALL, CALL_TYPE_BATCH, CALL_TYPE_DELEGATE, encodeMode, encodeSingle, encodeBatch, encodeDelegate, -} = require('../helpers/erc7579'); +} from '../helpers/erc7579'; + +const connection = await network.create(); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { - const chain = await getLocalChain(); const [admin, other] = await ethers.getSigners(); const gateway = await ethers.deployContract('$ERC7786GatewayMock'); const target = await ethers.deployContract('CallReceiverMock'); // Deploy executor - const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [gateway, chain.toErc7930(admin)]); + const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [gateway, helpers.chain.toErc7930(admin)]); const remoteExecute = (from, target, mode, data) => gateway.connect(from).sendMessage(target, ethers.concat([mode, data]), []); - return { chain, gateway, target, executor, admin, other, remoteExecute }; + return { gateway, target, executor, admin, other, remoteExecute }; } describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { @@ -36,15 +40,15 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { it('setup', async function () { await expect(this.executor.gateway()).to.eventually.equal(this.gateway); - await expect(this.executor.controller()).to.eventually.equal(this.chain.toErc7930(this.admin)); + await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.admin)); }); describe('crosschain operation', function () { it('support single mode', async function () { - const mode = encodeMode({ callType: CALL_TYPE_SINGLE }); + const mode = encodeMode({ callType: CALL_TYPE_CALL }); const data = encodeSingle(this.target, 0n, this.target.interface.encodeFunctionData('mockFunctionExtra')); - await expect(this.remoteExecute(this.admin, this.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) .to.emit(this.target, 'MockFunctionCalledExtra') .withArgs(this.executor, 0n); }); @@ -56,7 +60,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { [this.target, 0n, this.target.interface.encodeFunctionData('mockFunctionExtra')], ); - await expect(this.remoteExecute(this.admin, this.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) .to.emit(this.target, 'MockFunctionCalledWithArgs') .withArgs(42, '0x1234') .to.emit(this.target, 'MockFunctionCalledExtra') @@ -67,7 +71,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { const mode = encodeMode({ callType: CALL_TYPE_DELEGATE }); const data = encodeDelegate(this.target, this.target.interface.encodeFunctionData('mockFunctionExtra')); - await expect(this.remoteExecute(this.admin, this.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) .to.emit(this.target.attach(this.executor.target), 'MockFunctionCalledExtra') .withArgs(this.gateway, 0n); }); @@ -76,7 +80,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { const mode = encodeMode({ callType: '0x42' }); const data = '0x'; - await expect(this.remoteExecute(this.admin, this.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) .to.be.revertedWithCustomError(this.executor, 'ERC7579UnsupportedCallType') .withArgs('0x42'); }); @@ -88,56 +92,56 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { }); it('through a crosschain call: success', async function () { - const mode = encodeMode({ callType: CALL_TYPE_SINGLE }); + const mode = encodeMode({ callType: CALL_TYPE_CALL }); const data = encodeSingle( this.executor, 0n, this.executor.interface.encodeFunctionData('reconfigure', [ this.newGateway.target, - this.chain.toErc7930(this.other), + helpers.chain.toErc7930(this.other), ]), ); - await expect(this.remoteExecute(this.admin, this.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) .to.emit(this.executor, 'CrosschainControllerSet') - .withArgs(this.newGateway, this.chain.toErc7930(this.other)); + .withArgs(this.newGateway, helpers.chain.toErc7930(this.other)); await expect(this.executor.gateway()).to.eventually.equal(this.newGateway); - await expect(this.executor.controller()).to.eventually.equal(this.chain.toErc7930(this.other)); + await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.other)); }); it('through the internal setter: success', async function () { - await expect(this.executor.$_setup(this.newGateway, this.chain.toErc7930(this.other))) + await expect(this.executor.$_setup(this.newGateway, helpers.chain.toErc7930(this.other))) .to.emit(this.executor, 'CrosschainControllerSet') - .withArgs(this.newGateway, this.chain.toErc7930(this.other)); + .withArgs(this.newGateway, helpers.chain.toErc7930(this.other)); await expect(this.executor.gateway()).to.eventually.equal(this.newGateway); - await expect(this.executor.controller()).to.eventually.equal(this.chain.toErc7930(this.other)); + await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.other)); }); it('with an invalid new gateway: revert', async function () { // directly using the internal setter - await expect(this.executor.$_setup(this.other, this.chain.toErc7930(this.other))).to.be.reverted; + await expect(this.executor.$_setup(this.other, helpers.chain.toErc7930(this.other))).to.be.revert(ethers); // through a crosschain call - const mode = encodeMode({ callType: CALL_TYPE_SINGLE }); + const mode = encodeMode({ callType: CALL_TYPE_CALL }); const data = encodeSingle( this.executor, 0n, this.executor.interface.encodeFunctionData('reconfigure', [ this.other.address, - this.chain.toErc7930(this.other), + helpers.chain.toErc7930(this.other), ]), ); await expect( - this.remoteExecute(this.admin, this.chain.toErc7930(this.executor), mode, data), + this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data), ).to.be.revertedWithCustomError(this.executor, 'FailedCall'); }); it('is access controlled', async function () { await expect( - this.executor.reconfigure(this.newGateway, this.chain.toErc7930(this.other)), + this.executor.reconfigure(this.newGateway, helpers.chain.toErc7930(this.other)), ).to.be.revertedWithCustomError(this.executor, 'AccessRestricted'); }); }); diff --git a/test/crosschain/ERC7786Recipient.test.js b/test/crosschain/ERC7786Recipient.test.js index 456ae19c1f5..96bd22438e1 100644 --- a/test/crosschain/ERC7786Recipient.test.js +++ b/test/crosschain/ERC7786Recipient.test.js @@ -6,7 +6,7 @@ const { ethers, helpers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const value = 42n; const payload = generators.hexBytes(128); diff --git a/test/finance/VestingWallet.test.js b/test/finance/VestingWallet.test.js index 303215af03e..297967ae7fe 100644 --- a/test/finance/VestingWallet.test.js +++ b/test/finance/VestingWallet.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { min } from '../helpers/math'; import { envSetup, shouldBehaveLikeVesting } from './VestingWallet.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/finance/VestingWalletCliff.test.js b/test/finance/VestingWalletCliff.test.js index 8c7ba32a754..9c0aa693e4d 100644 --- a/test/finance/VestingWalletCliff.test.js +++ b/test/finance/VestingWalletCliff.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { min } from '../helpers/math'; import { envSetup, shouldBehaveLikeVesting } from './VestingWallet.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/Governor.test.js b/test/governance/Governor.test.js index 5dacd86c705..399a80d6435 100644 --- a/test/governance/Governor.test.js +++ b/test/governance/Governor.test.js @@ -6,7 +6,7 @@ import { GovernorHelper } from '../helpers/governance'; import { shouldSupportInterfaces } from '../utils/introspection/SupportsInterface.behavior'; import { shouldBehaveLikeERC6372 } from './utils/ERC6372.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/TimelockController.test.js b/test/governance/TimelockController.test.js index 3f13c3d9e33..e620c9c175b 100644 --- a/test/governance/TimelockController.test.js +++ b/test/governance/TimelockController.test.js @@ -5,7 +5,7 @@ import { GovernorHelper } from '../helpers/governance'; import { OperationState } from '../helpers/enums'; import { shouldSupportInterfaces } from '../utils/introspection/SupportsInterface.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/extensions/GovernorCountingFractional.test.js b/test/governance/extensions/GovernorCountingFractional.test.js index 5301c18b36d..2bb1ba66a1c 100644 --- a/test/governance/extensions/GovernorCountingFractional.test.js +++ b/test/governance/extensions/GovernorCountingFractional.test.js @@ -5,7 +5,7 @@ import { GovernorHelper } from '../../helpers/governance'; import { zip } from '../../helpers/iterate'; import { sum } from '../../helpers/math'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/governance/extensions/GovernorCountingOverridable.test.js b/test/governance/extensions/GovernorCountingOverridable.test.js index 40e8103164e..ca954015d7f 100644 --- a/test/governance/extensions/GovernorCountingOverridable.test.js +++ b/test/governance/extensions/GovernorCountingOverridable.test.js @@ -4,7 +4,7 @@ import { VoteType } from '../../helpers/enums'; import { getDomain, OverrideBallot } from '../../helpers/eip712'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture, mine }, diff --git a/test/governance/extensions/GovernorCrosschain.test.js b/test/governance/extensions/GovernorCrosschain.test.js index c974dad954c..7c90f9b34a7 100644 --- a/test/governance/extensions/GovernorCrosschain.test.js +++ b/test/governance/extensions/GovernorCrosschain.test.js @@ -1,11 +1,16 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +import { network } from 'hardhat'; +import { expect } from 'chai'; -const { getLocalChain } = require('../../helpers/chains'); -const { CALL_TYPE_SINGLE, encodeMode, encodeSingle } = require('../../helpers/erc7579'); -const { GovernorHelper } = require('../../helpers/governance'); -const { VoteType } = require('../../helpers/enums'); +import { CALL_TYPE_CALL, encodeMode, encodeSingle } from '../../helpers/erc7579'; +import { GovernorHelper } from '../../helpers/governance'; +import { VoteType } from '../../helpers/enums'; + +const connection = await network.create(); +const { + ethers, + helpers, + networkHelpers: { loadFixture }, +} = connection; const name = 'OZ-Governor'; const version = '1'; @@ -17,7 +22,6 @@ const votingPeriod = 16n; const value = ethers.parseEther('1'); async function fixture() { - const chain = await getLocalChain(); const [owner, proposer, voter1, voter2, voter3, voter4] = await ethers.getSigners(); const gateway = await ethers.deployContract('$ERC7786GatewayMock'); @@ -35,19 +39,21 @@ async function fixture() { ]); // Deploy executor - const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [gateway, chain.toErc7930(governor)]); + const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [ + gateway, + helpers.chain.toErc7930(governor), + ]); await owner.sendTransaction({ to: governor, value }); await token.$_mint(owner, tokenSupply); - const helper = new GovernorHelper(governor, 'blocknumber'); + const helper = new GovernorHelper(connection, governor, 'blocknumber'); await helper.connect(owner).delegate({ token: token, to: voter1, value: ethers.parseEther('10') }); await helper.connect(owner).delegate({ token: token, to: voter2, value: ethers.parseEther('7') }); await helper.connect(owner).delegate({ token: token, to: voter3, value: ethers.parseEther('5') }); await helper.connect(owner).delegate({ token: token, to: voter4, value: ethers.parseEther('2') }); return { - chain, owner, proposer, voter1, @@ -75,8 +81,8 @@ describe('GovernorCrosschain', function () { target: this.governor.target, data: this.governor.interface.encodeFunctionData('relayCrosschain(address,bytes,bytes32,bytes)', [ this.gateway.target, - this.chain.toErc7930(this.executor), - encodeMode({ callType: CALL_TYPE_SINGLE }), + helpers.chain.toErc7930(this.executor), + encodeMode({ callType: CALL_TYPE_CALL }), encodeSingle(this.receiver, 0n, this.receiver.interface.encodeFunctionData('mockFunctionExtra')), ]), }, @@ -97,8 +103,8 @@ describe('GovernorCrosschain', function () { await expect( this.governor.getFunction('relayCrosschain(address,bytes,bytes32,bytes)')( this.gateway, - this.chain.toErc7930(this.executor), - encodeMode({ callType: CALL_TYPE_SINGLE }), + helpers.chain.toErc7930(this.executor), + encodeMode({ callType: CALL_TYPE_CALL }), encodeSingle(this.receiver, 0n, this.receiver.interface.encodeFunctionData('mockFunctionExtra')), ), ).to.be.revertedWithCustomError(this.governor, 'GovernorOnlyExecutor'); @@ -116,7 +122,7 @@ describe('GovernorCrosschain', function () { // Before reconfiguration await expect(this.executor.gateway()).to.eventually.equal(this.gateway); - await expect(this.executor.controller()).to.eventually.equal(this.chain.toErc7930(this.governor)); + await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.governor)); // Propose reconfiguration this.helper.setProposal( @@ -125,14 +131,14 @@ describe('GovernorCrosschain', function () { target: this.governor.target, data: this.governor.interface.encodeFunctionData('relayCrosschain(address,bytes,bytes32,bytes)', [ this.gateway.target, - this.chain.toErc7930(this.executor), - encodeMode({ callType: CALL_TYPE_SINGLE }), + helpers.chain.toErc7930(this.executor), + encodeMode({ callType: CALL_TYPE_CALL }), encodeSingle( this.executor, 0n, this.executor.interface.encodeFunctionData('reconfigure', [ this.gateway.target, - this.chain.toErc7930(newGovernor), + helpers.chain.toErc7930(newGovernor), ]), ), ]), @@ -149,10 +155,10 @@ describe('GovernorCrosschain', function () { await expect(this.helper.execute()) .to.emit(this.executor, 'CrosschainControllerSet') - .withArgs(this.gateway, this.chain.toErc7930(newGovernor)); + .withArgs(this.gateway, helpers.chain.toErc7930(newGovernor)); // After reconfiguration await expect(this.executor.gateway()).to.eventually.equal(this.gateway); - await expect(this.executor.controller()).to.eventually.equal(this.chain.toErc7930(newGovernor)); + await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(newGovernor)); }); }); diff --git a/test/governance/extensions/GovernorERC721.test.js b/test/governance/extensions/GovernorERC721.test.js index e8b5c7e5ab0..e3cf4ac1595 100644 --- a/test/governance/extensions/GovernorERC721.test.js +++ b/test/governance/extensions/GovernorERC721.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/governance/extensions/GovernorNoncesKeyed.test.js b/test/governance/extensions/GovernorNoncesKeyed.test.js index ee484cbed7d..f504cf92ee1 100644 --- a/test/governance/extensions/GovernorNoncesKeyed.test.js +++ b/test/governance/extensions/GovernorNoncesKeyed.test.js @@ -5,7 +5,7 @@ import { VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; import { shouldBehaveLikeNoncesKeyed } from '../../utils/Nonces.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/governance/extensions/GovernorPreventLateQuorum.test.js b/test/governance/extensions/GovernorPreventLateQuorum.test.js index e69f883bb16..1d71fb71296 100644 --- a/test/governance/extensions/GovernorPreventLateQuorum.test.js +++ b/test/governance/extensions/GovernorPreventLateQuorum.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { ProposalState, VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/extensions/GovernorProposalGuardian.test.js b/test/governance/extensions/GovernorProposalGuardian.test.js index 28494b615bb..15255f8c082 100644 --- a/test/governance/extensions/GovernorProposalGuardian.test.js +++ b/test/governance/extensions/GovernorProposalGuardian.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { ProposalState } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { impersonate }, diff --git a/test/governance/extensions/GovernorSequentialProposalId.test.js b/test/governance/extensions/GovernorSequentialProposalId.test.js index afaea7d4dfe..252fe0cdbc1 100644 --- a/test/governance/extensions/GovernorSequentialProposalId.test.js +++ b/test/governance/extensions/GovernorSequentialProposalId.test.js @@ -5,7 +5,7 @@ import { VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; import { range } from '../../helpers/iterate'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/governance/extensions/GovernorStorage.test.js b/test/governance/extensions/GovernorStorage.test.js index 9a275f9a584..4fdbb77c7b0 100644 --- a/test/governance/extensions/GovernorStorage.test.js +++ b/test/governance/extensions/GovernorStorage.test.js @@ -5,7 +5,7 @@ import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic import { VoteType } from '../../helpers/enums'; import { GovernorHelper, timelockSalt } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/governance/extensions/GovernorSuperQuorum.test.js b/test/governance/extensions/GovernorSuperQuorum.test.js index f67faabeb0c..2f0094821b5 100644 --- a/test/governance/extensions/GovernorSuperQuorum.test.js +++ b/test/governance/extensions/GovernorSuperQuorum.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { ProposalState, VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/extensions/GovernorTimelockAccess.test.js b/test/governance/extensions/GovernorTimelockAccess.test.js index c1982bbd7b2..5d656519323 100644 --- a/test/governance/extensions/GovernorTimelockAccess.test.js +++ b/test/governance/extensions/GovernorTimelockAccess.test.js @@ -7,7 +7,7 @@ import { GovernorHelper } from '../../helpers/governance'; import { max } from '../../helpers/math'; import { selector } from '../../helpers/methods'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, @@ -248,7 +248,7 @@ describe('GovernorTimelockAccess', function () { await this.helper.waitForDeadline(); // No need for queuing, so it should not revert - await expect(this.helper.execute()).to.not.be.reverted; + await expect(this.helper.execute()).to.not.be.revert(ethers); }); it('does need to queue proposals with base delay', async function () { diff --git a/test/governance/extensions/GovernorTimelockCompound.test.js b/test/governance/extensions/GovernorTimelockCompound.test.js index b3c8e8f490d..a507da15b74 100644 --- a/test/governance/extensions/GovernorTimelockCompound.test.js +++ b/test/governance/extensions/GovernorTimelockCompound.test.js @@ -4,7 +4,7 @@ import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs import { ProposalState, VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/extensions/GovernorTimelockControl.test.js b/test/governance/extensions/GovernorTimelockControl.test.js index d731a8e1d9f..15789ef5c9e 100644 --- a/test/governance/extensions/GovernorTimelockControl.test.js +++ b/test/governance/extensions/GovernorTimelockControl.test.js @@ -5,7 +5,7 @@ import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic import { OperationState, ProposalState, VoteType } from '../../helpers/enums'; import { GovernorHelper, timelockSalt } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/extensions/GovernorVotesQuorumFraction.test.js b/test/governance/extensions/GovernorVotesQuorumFraction.test.js index aadcc82b4b6..ea7ee6b0ff4 100644 --- a/test/governance/extensions/GovernorVotesQuorumFraction.test.js +++ b/test/governance/extensions/GovernorVotesQuorumFraction.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { ProposalState, VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js b/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js index c26808dc9dc..160f906dd96 100644 --- a/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js +++ b/test/governance/extensions/GovernorVotesSuperQuorumFraction.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { ProposalState, VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/extensions/GovernorWithParams.test.js b/test/governance/extensions/GovernorWithParams.test.js index 56781d4ec2e..e390087e154 100644 --- a/test/governance/extensions/GovernorWithParams.test.js +++ b/test/governance/extensions/GovernorWithParams.test.js @@ -4,7 +4,7 @@ import { getDomain, ExtendedBallot } from '../../helpers/eip712'; import { VoteType } from '../../helpers/enums'; import { GovernorHelper } from '../../helpers/governance'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/governance/utils/Votes.test.js b/test/governance/utils/Votes.test.js index 3b37cf556ed..5f46dd89811 100644 --- a/test/governance/utils/Votes.test.js +++ b/test/governance/utils/Votes.test.js @@ -4,7 +4,7 @@ import { zip } from '../../helpers/iterate'; import { sum } from '../../helpers/math'; import { shouldBehaveLikeVotes } from './Votes.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/governance/utils/VotesExtended.test.js b/test/governance/utils/VotesExtended.test.js index e0e970177e4..8cddf6aedcf 100644 --- a/test/governance/utils/VotesExtended.test.js +++ b/test/governance/utils/VotesExtended.test.js @@ -4,7 +4,7 @@ import { zip } from '../../helpers/iterate'; import { sum } from '../../helpers/math'; import { shouldBehaveLikeVotes } from './Votes.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/metatx/ERC2771Context.test.js b/test/metatx/ERC2771Context.test.js index 69332faf106..87058ce3229 100644 --- a/test/metatx/ERC2771Context.test.js +++ b/test/metatx/ERC2771Context.test.js @@ -8,7 +8,7 @@ const { ethers, helpers: { impersonate }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [sender, other] = await ethers.getSigners(); diff --git a/test/metatx/ERC2771Forwarder.test.js b/test/metatx/ERC2771Forwarder.test.js index c2984ad80ff..e0c6a900103 100644 --- a/test/metatx/ERC2771Forwarder.test.js +++ b/test/metatx/ERC2771Forwarder.test.js @@ -7,7 +7,7 @@ const { ethers, helpers: { time }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [sender, refundReceiver, another, ...accounts] = await ethers.getSigners(); diff --git a/test/proxy/Clones.test.js b/test/proxy/Clones.test.js index 5b2ec65b7fe..78850d50fea 100644 --- a/test/proxy/Clones.test.js +++ b/test/proxy/Clones.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { generators } from '../helpers/random'; import { shouldBehaveLikeClone } from './Clones.behaviour'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/proxy/ERC1967/ERC1967Proxy.test.js b/test/proxy/ERC1967/ERC1967Proxy.test.js index b62e53db87f..efe5e914f70 100644 --- a/test/proxy/ERC1967/ERC1967Proxy.test.js +++ b/test/proxy/ERC1967/ERC1967Proxy.test.js @@ -1,7 +1,7 @@ import { network } from 'hardhat'; import { shouldBehaveLikeProxy } from '../Proxy.behaviour'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/proxy/ERC1967/ERC1967Utils.test.js b/test/proxy/ERC1967/ERC1967Utils.test.js index 9a1f6045b94..6ec37d25979 100644 --- a/test/proxy/ERC1967/ERC1967Utils.test.js +++ b/test/proxy/ERC1967/ERC1967Utils.test.js @@ -6,7 +6,7 @@ const { ethers, helpers: { storage }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [, admin, anotherAccount] = await ethers.getSigners(); diff --git a/test/proxy/beacon/BeaconProxy.test.js b/test/proxy/beacon/BeaconProxy.test.js index e2451e1f249..1fb807d9b94 100644 --- a/test/proxy/beacon/BeaconProxy.test.js +++ b/test/proxy/beacon/BeaconProxy.test.js @@ -6,7 +6,7 @@ const { ethers, helpers: { storage }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [admin, other] = await ethers.getSigners(); diff --git a/test/proxy/beacon/UpgradeableBeacon.test.js b/test/proxy/beacon/UpgradeableBeacon.test.js index 5cbcbc7c428..c805a3ed998 100644 --- a/test/proxy/beacon/UpgradeableBeacon.test.js +++ b/test/proxy/beacon/UpgradeableBeacon.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [admin, other] = await ethers.getSigners(); diff --git a/test/proxy/transparent/ProxyAdmin.test.js b/test/proxy/transparent/ProxyAdmin.test.js index 2467c97bd7a..38d969cae54 100644 --- a/test/proxy/transparent/ProxyAdmin.test.js +++ b/test/proxy/transparent/ProxyAdmin.test.js @@ -6,7 +6,7 @@ const { ethers, helpers: { storage }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [admin, other] = await ethers.getSigners(); diff --git a/test/proxy/transparent/TransparentUpgradeableProxy.test.js b/test/proxy/transparent/TransparentUpgradeableProxy.test.js index d9aa31ea733..853c791dea2 100644 --- a/test/proxy/transparent/TransparentUpgradeableProxy.test.js +++ b/test/proxy/transparent/TransparentUpgradeableProxy.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { shouldBehaveLikeProxy } from '../Proxy.behaviour'; import { shouldBehaveLikeTransparentUpgradeableProxy } from './TransparentUpgradeableProxy.behaviour'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/proxy/utils/Initializable.test.js b/test/proxy/utils/Initializable.test.js index e74aa0efc37..4f84e76074e 100644 --- a/test/proxy/utils/Initializable.test.js +++ b/test/proxy/utils/Initializable.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { MAX_UINT64 } from '../../helpers/constants'; -const { ethers } = await network.connect(); +const { ethers } = await network.create(); describe('Initializable', function () { describe('basic testing without inheritance', function () { diff --git a/test/proxy/utils/UUPSUpgradeable.test.js b/test/proxy/utils/UUPSUpgradeable.test.js index 38a6d7fa04c..7b3a55128a0 100644 --- a/test/proxy/utils/UUPSUpgradeable.test.js +++ b/test/proxy/utils/UUPSUpgradeable.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { ImplementationLabel } from '../../helpers/storage'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { storage }, diff --git a/test/sanity.test.js b/test/sanity.test.js index 51e02e94666..8bcf03a52be 100644 --- a/test/sanity.test.js +++ b/test/sanity.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture, mine }, -} = await network.connect(); +} = await network.create(); async function fixture() { return {}; diff --git a/test/token/ERC1155/ERC1155.test.js b/test/token/ERC1155/ERC1155.test.js index ec3a995714a..a1803cd4778 100644 --- a/test/token/ERC1155/ERC1155.test.js +++ b/test/token/ERC1155/ERC1155.test.js @@ -4,7 +4,7 @@ import { RevertType } from '../../helpers/enums'; import { zip } from '../../helpers/iterate'; import { shouldBehaveLikeERC1155 } from './ERC1155.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC1155/extensions/ERC1155Burnable.test.js b/test/token/ERC1155/extensions/ERC1155Burnable.test.js index 74e57ae4e17..0268c18fd38 100644 --- a/test/token/ERC1155/extensions/ERC1155Burnable.test.js +++ b/test/token/ERC1155/extensions/ERC1155Burnable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const ids = [42n, 1137n]; const values = [3000n, 9902n]; diff --git a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js index 0e05626b1b9..e0ab76148ca 100644 --- a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js +++ b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC1155 } from '../../../crosschain/BridgeERC1155.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers, networkHelpers } = connection; async function fixture() { diff --git a/test/token/ERC1155/extensions/ERC1155Pausable.test.js b/test/token/ERC1155/extensions/ERC1155Pausable.test.js index f91ae5fed06..dce2baa7f11 100644 --- a/test/token/ERC1155/extensions/ERC1155Pausable.test.js +++ b/test/token/ERC1155/extensions/ERC1155Pausable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [holder, operator, receiver, other] = await ethers.getSigners(); diff --git a/test/token/ERC1155/extensions/ERC1155Supply.test.js b/test/token/ERC1155/extensions/ERC1155Supply.test.js index 170aa6f2231..9866ff3da06 100644 --- a/test/token/ERC1155/extensions/ERC1155Supply.test.js +++ b/test/token/ERC1155/extensions/ERC1155Supply.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [holder] = await ethers.getSigners(); diff --git a/test/token/ERC1155/extensions/ERC1155URIStorage.test.js b/test/token/ERC1155/extensions/ERC1155URIStorage.test.js index 2c1100bf8fd..ea86913034d 100644 --- a/test/token/ERC1155/extensions/ERC1155URIStorage.test.js +++ b/test/token/ERC1155/extensions/ERC1155URIStorage.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const erc1155Uri = 'https://token.com/nfts/'; const baseUri = 'https://token.com/'; diff --git a/test/token/ERC1155/utils/ERC1155Holder.test.js b/test/token/ERC1155/utils/ERC1155Holder.test.js index ecfc285a50c..9112fb1796a 100644 --- a/test/token/ERC1155/utils/ERC1155Holder.test.js +++ b/test/token/ERC1155/utils/ERC1155Holder.test.js @@ -5,7 +5,7 @@ import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsIn const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const ids = [1n, 2n, 3n]; const values = [1000n, 2000n, 3000n]; diff --git a/test/token/ERC1155/utils/ERC1155Utils.test.js b/test/token/ERC1155/utils/ERC1155Utils.test.js index 66baf0f0fed..57a8d3d0263 100644 --- a/test/token/ERC1155/utils/ERC1155Utils.test.js +++ b/test/token/ERC1155/utils/ERC1155Utils.test.js @@ -6,7 +6,7 @@ import { RevertType } from '../../../helpers/enums'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const firstTokenId = 1n; const secondTokenId = 2n; diff --git a/test/token/ERC20/ERC20.test.js b/test/token/ERC20/ERC20.test.js index dca0ab1f8a1..43db634a201 100644 --- a/test/token/ERC20/ERC20.test.js +++ b/test/token/ERC20/ERC20.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; import { shouldBehaveLikeERC20, shouldBehaveLikeERC20Transfer, shouldBehaveLikeERC20Approve } from './ERC20.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC20/extensions/ERC1363.test.js b/test/token/ERC20/extensions/ERC1363.test.js index b338c3226d4..b3e73edd580 100644 --- a/test/token/ERC20/extensions/ERC1363.test.js +++ b/test/token/ERC20/extensions/ERC1363.test.js @@ -4,7 +4,7 @@ import { shouldBehaveLikeERC20, shouldBehaveLikeERC20Transfer, shouldBehaveLikeE import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; import { RevertType } from '../../../helpers/enums'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC20/extensions/ERC20Burnable.test.js b/test/token/ERC20/extensions/ERC20Burnable.test.js index 258aefaf261..9a8d729f94e 100644 --- a/test/token/ERC20/extensions/ERC20Burnable.test.js +++ b/test/token/ERC20/extensions/ERC20Burnable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/ERC20Capped.test.js b/test/token/ERC20/extensions/ERC20Capped.test.js index f8e34a6a33f..392100cd97f 100644 --- a/test/token/ERC20/extensions/ERC20Capped.test.js +++ b/test/token/ERC20/extensions/ERC20Capped.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/ERC20Crosschain.test.js b/test/token/ERC20/extensions/ERC20Crosschain.test.js index 4a2ad7b8900..abcfd3488a2 100644 --- a/test/token/ERC20/extensions/ERC20Crosschain.test.js +++ b/test/token/ERC20/extensions/ERC20Crosschain.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs'; import { shouldBehaveLikeBridgeERC20 } from '../../../crosschain/BridgeERC20.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers, networkHelpers } = connection; async function fixture() { diff --git a/test/token/ERC20/extensions/ERC20FlashMint.test.js b/test/token/ERC20/extensions/ERC20FlashMint.test.js index f973c2ea060..39381def620 100644 --- a/test/token/ERC20/extensions/ERC20FlashMint.test.js +++ b/test/token/ERC20/extensions/ERC20FlashMint.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/ERC20Pausable.test.js b/test/token/ERC20/extensions/ERC20Pausable.test.js index f020fc47c27..bfa18715fc5 100644 --- a/test/token/ERC20/extensions/ERC20Pausable.test.js +++ b/test/token/ERC20/extensions/ERC20Pausable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/ERC20Permit.test.js b/test/token/ERC20/extensions/ERC20Permit.test.js index 5a099d2f7b0..64465b1a3ec 100644 --- a/test/token/ERC20/extensions/ERC20Permit.test.js +++ b/test/token/ERC20/extensions/ERC20Permit.test.js @@ -6,7 +6,7 @@ const { ethers, helpers: { time }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/ERC20Votes.test.js b/test/token/ERC20/extensions/ERC20Votes.test.js index 819fa15145e..4e3ee88844c 100644 --- a/test/token/ERC20/extensions/ERC20Votes.test.js +++ b/test/token/ERC20/extensions/ERC20Votes.test.js @@ -4,7 +4,7 @@ import { Delegation, getDomain } from '../../../helpers/eip712'; import { batchInBlock } from '../../../helpers/txpool'; import { shouldBehaveLikeVotes } from '../../../governance/utils/Votes.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/token/ERC20/extensions/ERC20Wrapper.test.js b/test/token/ERC20/extensions/ERC20Wrapper.test.js index 9e6df1db164..8bebf468cab 100644 --- a/test/token/ERC20/extensions/ERC20Wrapper.test.js +++ b/test/token/ERC20/extensions/ERC20Wrapper.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeERC20 } from '../ERC20.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC20/extensions/ERC4626.test.js b/test/token/ERC20/extensions/ERC4626.test.js index a8056161730..1e542edca8e 100644 --- a/test/token/ERC20/extensions/ERC4626.test.js +++ b/test/token/ERC20/extensions/ERC4626.test.js @@ -6,7 +6,7 @@ import { Enum } from '../../../helpers/enums'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'My Token'; const symbol = 'MTKN'; diff --git a/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js b/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js index 7b72fcc1c5c..8c3b07c5d2e 100644 --- a/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js +++ b/test/token/ERC20/extensions/draft-ERC20Bridgeable.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { shouldBehaveLikeERC20 } from '../ERC20.behavior'; import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsInterface.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js b/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js index ebd25a6cedd..f4e7a323658 100644 --- a/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js +++ b/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { max, min } from '../../../helpers/math'; import { shouldBehaveLikeERC20 } from '../ERC20.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC20/utils/SafeERC20.test.js b/test/token/ERC20/utils/SafeERC20.test.js index 77679ee57f4..b55151b1754 100644 --- a/test/token/ERC20/utils/SafeERC20.test.js +++ b/test/token/ERC20/utils/SafeERC20.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'ERC20Mock'; const symbol = 'ERC20Mock'; diff --git a/test/token/ERC6909/ERC6909.test.js b/test/token/ERC6909/ERC6909.test.js index e757cc56e34..e3ed34d4c81 100644 --- a/test/token/ERC6909/ERC6909.test.js +++ b/test/token/ERC6909/ERC6909.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeERC6909 } from './ERC6909.behavior'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [holder, operator, recipient, other] = await ethers.getSigners(); diff --git a/test/token/ERC6909/extensions/ERC6909ContentURI.test.js b/test/token/ERC6909/extensions/ERC6909ContentURI.test.js index d7ea9cf1cf1..0437a81762e 100644 --- a/test/token/ERC6909/extensions/ERC6909ContentURI.test.js +++ b/test/token/ERC6909/extensions/ERC6909ContentURI.test.js @@ -5,7 +5,7 @@ import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsIn const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { token: await ethers.deployContract('$ERC6909ContentURI') }; diff --git a/test/token/ERC6909/extensions/ERC6909Metadata.test.js b/test/token/ERC6909/extensions/ERC6909Metadata.test.js index 7dc2f7ec828..4a734dc9c19 100644 --- a/test/token/ERC6909/extensions/ERC6909Metadata.test.js +++ b/test/token/ERC6909/extensions/ERC6909Metadata.test.js @@ -5,7 +5,7 @@ import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsIn const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { token: await ethers.deployContract('$ERC6909Metadata') }; diff --git a/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js b/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js index 3a16cad13b7..3750061dfba 100644 --- a/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js +++ b/test/token/ERC6909/extensions/ERC6909TokenSupply.test.js @@ -6,7 +6,7 @@ import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsIn const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [holder, operator, recipient, other] = await ethers.getSigners(); diff --git a/test/token/ERC721/ERC721.test.js b/test/token/ERC721/ERC721.test.js index b617241a2bb..02d1c5246bc 100644 --- a/test/token/ERC721/ERC721.test.js +++ b/test/token/ERC721/ERC721.test.js @@ -1,7 +1,7 @@ import { network } from 'hardhat'; import { shouldBehaveLikeERC721, shouldBehaveLikeERC721Metadata } from './ERC721.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC721/ERC721Enumerable.test.js b/test/token/ERC721/ERC721Enumerable.test.js index 9d6920ae671..861cf35333a 100644 --- a/test/token/ERC721/ERC721Enumerable.test.js +++ b/test/token/ERC721/ERC721Enumerable.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeERC721Enumerable, } from './ERC721.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC721/extensions/ERC721Burnable.test.js b/test/token/ERC721/extensions/ERC721Burnable.test.js index 75798f3b197..2e7fdb3c35a 100644 --- a/test/token/ERC721/extensions/ERC721Burnable.test.js +++ b/test/token/ERC721/extensions/ERC721Burnable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Consecutive.test.js b/test/token/ERC721/extensions/ERC721Consecutive.test.js index 338e001f4ac..b64fd48706d 100644 --- a/test/token/ERC721/extensions/ERC721Consecutive.test.js +++ b/test/token/ERC721/extensions/ERC721Consecutive.test.js @@ -5,7 +5,7 @@ import { sum } from '../../../helpers/math'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Crosschain.test.js b/test/token/ERC721/extensions/ERC721Crosschain.test.js index c5ff864810c..c2d186adc72 100644 --- a/test/token/ERC721/extensions/ERC721Crosschain.test.js +++ b/test/token/ERC721/extensions/ERC721Crosschain.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC721 } from '../../../crosschain/BridgeERC721.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers, networkHelpers } = connection; async function fixture() { diff --git a/test/token/ERC721/extensions/ERC721Pausable.test.js b/test/token/ERC721/extensions/ERC721Pausable.test.js index df77e958fae..445bc09746a 100644 --- a/test/token/ERC721/extensions/ERC721Pausable.test.js +++ b/test/token/ERC721/extensions/ERC721Pausable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Royalty.test.js b/test/token/ERC721/extensions/ERC721Royalty.test.js index 2f41d7f3de4..54d5d3a2d3f 100644 --- a/test/token/ERC721/extensions/ERC721Royalty.test.js +++ b/test/token/ERC721/extensions/ERC721Royalty.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeERC2981 } from '../../common/ERC2981.behavior'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721URIStorage.test.js b/test/token/ERC721/extensions/ERC721URIStorage.test.js index cb2bd6e576b..bbb629cec1a 100644 --- a/test/token/ERC721/extensions/ERC721URIStorage.test.js +++ b/test/token/ERC721/extensions/ERC721URIStorage.test.js @@ -5,7 +5,7 @@ import { shouldSupportInterfaces } from '../../../utils/introspection/SupportsIn const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/extensions/ERC721Votes.test.js b/test/token/ERC721/extensions/ERC721Votes.test.js index d146bfe2c9c..0617863d14e 100644 --- a/test/token/ERC721/extensions/ERC721Votes.test.js +++ b/test/token/ERC721/extensions/ERC721Votes.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeVotes } from '../../../governance/utils/Votes.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, helpers: { time }, diff --git a/test/token/ERC721/extensions/ERC721Wrapper.test.js b/test/token/ERC721/extensions/ERC721Wrapper.test.js index bf7f72a0f1f..541292af51f 100644 --- a/test/token/ERC721/extensions/ERC721Wrapper.test.js +++ b/test/token/ERC721/extensions/ERC721Wrapper.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; import { shouldBehaveLikeERC721 } from '../ERC721.behavior'; -const connection = await network.connect(); +const connection = await network.create(); const { ethers, networkHelpers: { loadFixture }, diff --git a/test/token/ERC721/utils/ERC721Holder.test.js b/test/token/ERC721/utils/ERC721Holder.test.js index a706bae3c50..26d370e0133 100644 --- a/test/token/ERC721/utils/ERC721Holder.test.js +++ b/test/token/ERC721/utils/ERC721Holder.test.js @@ -1,7 +1,7 @@ import { network } from 'hardhat'; import { expect } from 'chai'; -const { ethers } = await network.connect(); +const { ethers } = await network.create(); const name = 'Non Fungible Token'; const symbol = 'NFT'; diff --git a/test/token/ERC721/utils/ERC721Utils.test.js b/test/token/ERC721/utils/ERC721Utils.test.js index fd72d30c4a8..380215323fc 100644 --- a/test/token/ERC721/utils/ERC721Utils.test.js +++ b/test/token/ERC721/utils/ERC721Utils.test.js @@ -6,7 +6,7 @@ import { RevertType } from '../../../helpers/enums'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const tokenId = 1n; diff --git a/test/utils/Address.test.js b/test/utils/Address.test.js index 3a1b139956b..05fd1f1a289 100644 --- a/test/utils/Address.test.js +++ b/test/utils/Address.test.js @@ -5,7 +5,7 @@ import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const fakeContract = { interface: ethers.Interface.from(['error SomeCustomErrorWithoutArgs()']) }; const returndata = fakeContract.interface.encodeErrorResult('SomeCustomErrorWithoutArgs'); diff --git a/test/utils/Arrays.test.js b/test/utils/Arrays.test.js index 71b170ead35..11856c2ebe4 100644 --- a/test/utils/Arrays.test.js +++ b/test/utils/Arrays.test.js @@ -7,7 +7,7 @@ import { TYPES } from '../../scripts/generate/templates/Arrays.opts'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); // See https://en.cppreference.com/w/cpp/algorithm/lower_bound const lowerBound = (array, value) => { diff --git a/test/utils/Base58.test.js b/test/utils/Base58.test.js index ba2d3e1d7d2..1c8dfb46808 100644 --- a/test/utils/Base58.test.js +++ b/test/utils/Base58.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$Base58') }; diff --git a/test/utils/Base64.test.js b/test/utils/Base64.test.js index b3e916d9237..bbd46505cb9 100644 --- a/test/utils/Base64.test.js +++ b/test/utils/Base64.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); // Replace "+/" with "-_" in the char table, and remove the padding // see https://datatracker.ietf.org/doc/html/rfc4648#section-5 diff --git a/test/utils/Blockhash.test.js b/test/utils/Blockhash.test.js index 1fd1bcee18d..6fa4d33c0f3 100644 --- a/test/utils/Blockhash.test.js +++ b/test/utils/Blockhash.test.js @@ -5,7 +5,7 @@ const { ethers, helpers: { impersonate, time }, networkHelpers: { loadFixture, setCode }, -} = await network.connect(); +} = await network.create(); const SYSTEM_ADDRESS = '0xfffffffffffffffffffffffffffffffffffffffe'; const HISTORY_SERVE_WINDOW = 8191; diff --git a/test/utils/Bytes.test.js b/test/utils/Bytes.test.js index 7bb3bd28ecc..e90f9dbcb4f 100644 --- a/test/utils/Bytes.test.js +++ b/test/utils/Bytes.test.js @@ -6,7 +6,7 @@ import { generators } from '../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); // Helper functions for fixed bytes types const bytes32 = value => ethers.toBeHex(value, 32); diff --git a/test/utils/CAIP.test.js b/test/utils/CAIP.test.js index 7eda8247a94..a6d4fc2faa7 100644 --- a/test/utils/CAIP.test.js +++ b/test/utils/CAIP.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { CHAINS, getLocalChain } from '../helpers/chains'; import { generators } from '../helpers/random'; -const { ethers } = await network.connect(); +const { ethers } = await network.create(); describe('CAIP utilities', function () { before(async function () { diff --git a/test/utils/Calldata.test.js b/test/utils/Calldata.test.js index 04376713827..8351af59ff0 100644 --- a/test/utils/Calldata.test.js +++ b/test/utils/Calldata.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$Calldata') }; diff --git a/test/utils/Context.test.js b/test/utils/Context.test.js index 6f5d0f1d074..4305d768a72 100644 --- a/test/utils/Context.test.js +++ b/test/utils/Context.test.js @@ -4,7 +4,7 @@ import { shouldBehaveLikeRegularContext } from './Context.behavior'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [sender] = await ethers.getSigners(); diff --git a/test/utils/Create2.test.js b/test/utils/Create2.test.js index 6ac361cc993..f5ed30bb400 100644 --- a/test/utils/Create2.test.js +++ b/test/utils/Create2.test.js @@ -6,7 +6,7 @@ import { RevertType } from '../helpers/enums'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [deployer, other] = await ethers.getSigners(); diff --git a/test/utils/LowLevelCall.test.js b/test/utils/LowLevelCall.test.js index 1edc756495d..2f13db9ef2c 100644 --- a/test/utils/LowLevelCall.test.js +++ b/test/utils/LowLevelCall.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const value = ethers.parseEther('1'); const returnValue1 = ethers.id('hello'); diff --git a/test/utils/Memory.test.js b/test/utils/Memory.test.js index e66442b9408..714aef9d453 100644 --- a/test/utils/Memory.test.js +++ b/test/utils/Memory.test.js @@ -6,7 +6,7 @@ import { generators } from '../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const formatSlice = ({ length, ptr = 0xa0 }) => ethers.toBeHex((ethers.toBigInt(length) << 128n) | ethers.toBigInt(ptr), 32); diff --git a/test/utils/Multicall.test.js b/test/utils/Multicall.test.js index f231a3720b3..ec6837d9257 100644 --- a/test/utils/Multicall.test.js +++ b/test/utils/Multicall.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [holder, alice, bruce] = await ethers.getSigners(); diff --git a/test/utils/Nonces.test.js b/test/utils/Nonces.test.js index 81863d14d5e..f02220d90bf 100644 --- a/test/utils/Nonces.test.js +++ b/test/utils/Nonces.test.js @@ -4,7 +4,7 @@ import { shouldBehaveLikeNonces } from './Nonces.behavior'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$Nonces') }; diff --git a/test/utils/NoncesKeyed.test.js b/test/utils/NoncesKeyed.test.js index c73ce0a96c2..9d040242d56 100644 --- a/test/utils/NoncesKeyed.test.js +++ b/test/utils/NoncesKeyed.test.js @@ -4,7 +4,7 @@ import { shouldBehaveLikeNonces, shouldBehaveLikeNoncesKeyed } from './Nonces.be const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$NoncesKeyed') }; diff --git a/test/utils/Packing.test.js b/test/utils/Packing.test.js index 9a245c8ea1a..0226c65a12a 100644 --- a/test/utils/Packing.test.js +++ b/test/utils/Packing.test.js @@ -6,7 +6,7 @@ import { SIZES } from '../../scripts/generate/templates/Packing.opts'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$Packing') }; diff --git a/test/utils/Panic.test.js b/test/utils/Panic.test.js index 4d3ca729323..32093bf6a47 100644 --- a/test/utils/Panic.test.js +++ b/test/utils/Panic.test.js @@ -5,7 +5,7 @@ import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$Panic') }; diff --git a/test/utils/Pausable.test.js b/test/utils/Pausable.test.js index 0c64b51b48a..d092850e7c8 100644 --- a/test/utils/Pausable.test.js +++ b/test/utils/Pausable.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [pauser] = await ethers.getSigners(); diff --git a/test/utils/RLP.test.js b/test/utils/RLP.test.js index a418db7ddef..c7129181a0f 100644 --- a/test/utils/RLP.test.js +++ b/test/utils/RLP.test.js @@ -7,7 +7,7 @@ import { generators } from '../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const mock = await ethers.deployContract('$RLP'); diff --git a/test/utils/ReentrancyGuard.test.js b/test/utils/ReentrancyGuard.test.js index 3c6c8c4ed7b..0d9c0bbac1f 100644 --- a/test/utils/ReentrancyGuard.test.js +++ b/test/utils/ReentrancyGuard.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); for (const variant of ['', 'Transient']) { describe(`Reentrancy${variant}Guard`, function () { diff --git a/test/utils/RelayedCall.test.js b/test/utils/RelayedCall.test.js index f644e09c572..9ef6ee2e46f 100644 --- a/test/utils/RelayedCall.test.js +++ b/test/utils/RelayedCall.test.js @@ -5,7 +5,7 @@ const { ethers, helpers: { impersonate }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [admin, receiver, other] = await ethers.getSigners(); diff --git a/test/utils/ShortStrings.test.js b/test/utils/ShortStrings.test.js index 053185672aa..2438fc5914b 100644 --- a/test/utils/ShortStrings.test.js +++ b/test/utils/ShortStrings.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const FALLBACK_SENTINEL = ethers.zeroPadValue('0xFF', 32); diff --git a/test/utils/SimulatedCall.test.js b/test/utils/SimulatedCall.test.js index 71917bfa513..a2e89e93ad9 100644 --- a/test/utils/SimulatedCall.test.js +++ b/test/utils/SimulatedCall.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const value = 42n; diff --git a/test/utils/SlotDerivation.test.js b/test/utils/SlotDerivation.test.js index 519d3587bfb..5529387d882 100644 --- a/test/utils/SlotDerivation.test.js +++ b/test/utils/SlotDerivation.test.js @@ -6,7 +6,7 @@ import { generators } from '../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const [account] = await ethers.getSigners(); diff --git a/test/utils/StorageSlot.test.js b/test/utils/StorageSlot.test.js index 4525286e650..8da0cdb6fdb 100644 --- a/test/utils/StorageSlot.test.js +++ b/test/utils/StorageSlot.test.js @@ -5,7 +5,7 @@ import { generators } from '../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const slot = ethers.id('some.storage.slot'); const otherSlot = ethers.id('some.other.storage.slot'); diff --git a/test/utils/Strings.test.js b/test/utils/Strings.test.js index ccfcaba7254..68a1d418ba7 100644 --- a/test/utils/Strings.test.js +++ b/test/utils/Strings.test.js @@ -5,7 +5,7 @@ import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$Strings') }; diff --git a/test/utils/TransientSlot.test.js b/test/utils/TransientSlot.test.js index a192be4fe26..0271d79de9e 100644 --- a/test/utils/TransientSlot.test.js +++ b/test/utils/TransientSlot.test.js @@ -5,7 +5,7 @@ import { generators } from '../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const slot = ethers.id('some.storage.slot'); const otherSlot = ethers.id('some.other.storage.slot'); diff --git a/test/utils/cryptography/ECDSA.test.js b/test/utils/cryptography/ECDSA.test.js index 06dbaf78100..644dbbc4455 100644 --- a/test/utils/cryptography/ECDSA.test.js +++ b/test/utils/cryptography/ECDSA.test.js @@ -5,7 +5,7 @@ import { secp256k1 } from '@noble/curves/secp256k1.js'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const TEST_MESSAGE = ethers.id('OpenZeppelin'); const WRONG_MESSAGE = ethers.id('Nope'); diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js index ab28c4e1b24..507cebcdd86 100644 --- a/test/utils/cryptography/EIP712.test.js +++ b/test/utils/cryptography/EIP712.test.js @@ -6,7 +6,7 @@ import { formatType } from '../../helpers/eip712-types'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const LENGTHS = { short: ['A Name', '1'], diff --git a/test/utils/cryptography/ERC7739.test.js b/test/utils/cryptography/ERC7739.test.js index 8252008bc65..1e59b906246 100644 --- a/test/utils/cryptography/ERC7739.test.js +++ b/test/utils/cryptography/ERC7739.test.js @@ -2,7 +2,7 @@ import { network } from 'hardhat'; import { shouldBehaveLikeERC1271 } from './ERC1271.behavior'; import { NonNativeSigner, P256SigningKey, RSASHA256SigningKey } from '../../helpers/signers'; -const { ethers } = await network.connect(); +const { ethers } = await network.create(); describe('ERC7739', function () { describe('for an ECDSA signer', function () { diff --git a/test/utils/cryptography/ERC7739Utils.test.js b/test/utils/cryptography/ERC7739Utils.test.js index 43fd71718ac..b4c1337b800 100644 --- a/test/utils/cryptography/ERC7739Utils.test.js +++ b/test/utils/cryptography/ERC7739Utils.test.js @@ -6,7 +6,7 @@ import { ERC4337Utils, PersonalSign } from '../../helpers/erc7739'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const details = ERC4337Utils.getContentsDetail({ Permit }); diff --git a/test/utils/cryptography/MerkleProof.test.js b/test/utils/cryptography/MerkleProof.test.js index 0617801f082..93fc07c913f 100644 --- a/test/utils/cryptography/MerkleProof.test.js +++ b/test/utils/cryptography/MerkleProof.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; import { SimpleMerkleTree } from '@openzeppelin/merkle-tree'; -const { ethers } = await network.connect(); +const { ethers } = await network.create(); // generate bytes32 leaves from a string const toLeaves = (str, separator = '') => str.split(separator).map(e => ethers.keccak256(ethers.toUtf8Bytes(e))); diff --git a/test/utils/cryptography/MessageHashUtils.test.js b/test/utils/cryptography/MessageHashUtils.test.js index 2c36f93c514..e5a06fe0b1e 100644 --- a/test/utils/cryptography/MessageHashUtils.test.js +++ b/test/utils/cryptography/MessageHashUtils.test.js @@ -6,7 +6,7 @@ import { generators } from '../../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$MessageHashUtils') }; diff --git a/test/utils/cryptography/P256.test.js b/test/utils/cryptography/P256.test.js index e1460f75831..573489284ae 100644 --- a/test/utils/cryptography/P256.test.js +++ b/test/utils/cryptography/P256.test.js @@ -6,7 +6,7 @@ import { p256 } from '@noble/curves/nist.js'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const N = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n; diff --git a/test/utils/cryptography/RSA.test.js b/test/utils/cryptography/RSA.test.js index b3de596abc5..0e61b3adaed 100644 --- a/test/utils/cryptography/RSA.test.js +++ b/test/utils/cryptography/RSA.test.js @@ -5,7 +5,7 @@ import { parse } from './RSA.helper'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$RSA') }; diff --git a/test/utils/cryptography/SignatureChecker.test.js b/test/utils/cryptography/SignatureChecker.test.js index 90e1e5b44b0..76b6c5f0382 100644 --- a/test/utils/cryptography/SignatureChecker.test.js +++ b/test/utils/cryptography/SignatureChecker.test.js @@ -6,7 +6,7 @@ import { P256SigningKey, NonNativeSigner } from '../../helpers/signers'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const TEST_MESSAGE = ethers.id('OpenZeppelin'); const TEST_MESSAGE_HASH = ethers.hashMessage(TEST_MESSAGE); diff --git a/test/utils/cryptography/TrieProof.test.js b/test/utils/cryptography/TrieProof.test.js index e28ff4e1e9c..081ac084109 100644 --- a/test/utils/cryptography/TrieProof.test.js +++ b/test/utils/cryptography/TrieProof.test.js @@ -10,7 +10,7 @@ import { batchInBlock } from '../../helpers/txpool'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const ProofError = Enum( 'NO_ERROR', // No error occurred during proof traversal diff --git a/test/utils/draft-InteroperableAddress.test.js b/test/utils/draft-InteroperableAddress.test.js index 9888ed1eb4d..f24a9ba2076 100644 --- a/test/utils/draft-InteroperableAddress.test.js +++ b/test/utils/draft-InteroperableAddress.test.js @@ -8,7 +8,7 @@ const { ethers, helpers: { chain }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$InteroperableAddress') }; diff --git a/test/utils/introspection/ERC165.test.js b/test/utils/introspection/ERC165.test.js index 96842e45331..97d7bfd389c 100644 --- a/test/utils/introspection/ERC165.test.js +++ b/test/utils/introspection/ERC165.test.js @@ -4,7 +4,7 @@ import { shouldSupportInterfaces } from './SupportsInterface.behavior'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$ERC165') }; diff --git a/test/utils/introspection/ERC165Checker.test.js b/test/utils/introspection/ERC165Checker.test.js index a1dcd204465..8911f0258eb 100644 --- a/test/utils/introspection/ERC165Checker.test.js +++ b/test/utils/introspection/ERC165Checker.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const DUMMY_ID = '0xdeadbeef'; const DUMMY_ID_2 = '0xcafebabe'; diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index 99ed847ecdb..e41ac5a59b5 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -9,7 +9,7 @@ import { product, range } from '../../helpers/iterate'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const RoundingDown = [Rounding.Floor, Rounding.Trunc]; const RoundingUp = [Rounding.Ceil, Rounding.Expand]; diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index 487082daae2..4b03b16d0fa 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -5,7 +5,7 @@ import { range } from '../../helpers/iterate'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$SafeCast') }; diff --git a/test/utils/math/SignedMath.test.js b/test/utils/math/SignedMath.test.js index 8e6454baead..65be7958ddd 100644 --- a/test/utils/math/SignedMath.test.js +++ b/test/utils/math/SignedMath.test.js @@ -5,7 +5,7 @@ import { min, max } from '../../helpers/math'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function testCommutative(fn, lhs, rhs, expected, ...extra) { expect(await fn(lhs, rhs, ...extra)).to.deep.equal(expected); diff --git a/test/utils/structs/BitMap.test.js b/test/utils/structs/BitMap.test.js index 830383fd96d..d7571df5b2a 100644 --- a/test/utils/structs/BitMap.test.js +++ b/test/utils/structs/BitMap.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { bitmap: await ethers.deployContract('$BitMaps') }; diff --git a/test/utils/structs/Checkpoints.test.js b/test/utils/structs/Checkpoints.test.js index b83085fc52d..984bcbec05b 100644 --- a/test/utils/structs/Checkpoints.test.js +++ b/test/utils/structs/Checkpoints.test.js @@ -5,7 +5,7 @@ import { OPTS } from '../../../scripts/generate/templates/Checkpoints.opts'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); describe('Checkpoints', function () { for (const opt of OPTS) { diff --git a/test/utils/structs/CircularBuffer.test.js b/test/utils/structs/CircularBuffer.test.js index 970c187fff6..389fdcee5a8 100644 --- a/test/utils/structs/CircularBuffer.test.js +++ b/test/utils/structs/CircularBuffer.test.js @@ -6,7 +6,7 @@ import { generators } from '../../helpers/random'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const LENGTH = 4; diff --git a/test/utils/structs/DoubleEndedQueue.test.js b/test/utils/structs/DoubleEndedQueue.test.js index ef16c8afc8f..dfcc046f8ba 100644 --- a/test/utils/structs/DoubleEndedQueue.test.js +++ b/test/utils/structs/DoubleEndedQueue.test.js @@ -5,7 +5,7 @@ import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { const mock = await ethers.deployContract('$DoubleEndedQueue'); diff --git a/test/utils/structs/EnumerableMap.test.js b/test/utils/structs/EnumerableMap.test.js index d511c0be347..b5e0ffa1621 100644 --- a/test/utils/structs/EnumerableMap.test.js +++ b/test/utils/structs/EnumerableMap.test.js @@ -7,7 +7,7 @@ import { shouldBehaveLikeMap } from './EnumerableMap.behavior'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); // Add Bytes32ToBytes32Map that must be tested but is not part of the generated types. MAP_TYPES.unshift(toMapTypeDescr({ key: typeDescr({ type: 'bytes32' }), value: typeDescr({ type: 'bytes32' }) })); diff --git a/test/utils/structs/EnumerableSet.test.js b/test/utils/structs/EnumerableSet.test.js index 6fcdff4396c..893193c9ba7 100644 --- a/test/utils/structs/EnumerableSet.test.js +++ b/test/utils/structs/EnumerableSet.test.js @@ -7,7 +7,7 @@ import { shouldBehaveLikeSet } from './EnumerableSet.behavior'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const getMethods = (mock, fnSigs) => mapValues( diff --git a/test/utils/structs/Heap.test.js b/test/utils/structs/Heap.test.js index 969a488e32d..6af62e6605d 100644 --- a/test/utils/structs/Heap.test.js +++ b/test/utils/structs/Heap.test.js @@ -5,7 +5,7 @@ import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); async function fixture() { return { mock: await ethers.deployContract('$Heap') }; diff --git a/test/utils/structs/MerkleTree.test.js b/test/utils/structs/MerkleTree.test.js index 0f0534125b4..b4262beb852 100644 --- a/test/utils/structs/MerkleTree.test.js +++ b/test/utils/structs/MerkleTree.test.js @@ -8,7 +8,7 @@ import { range } from '../../helpers/iterate'; const { ethers, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const DEPTH = 4; // 16 slots diff --git a/test/utils/types/Time.test.js b/test/utils/types/Time.test.js index eeb14d8f7eb..bd00378e93a 100644 --- a/test/utils/types/Time.test.js +++ b/test/utils/types/Time.test.js @@ -7,7 +7,7 @@ const { ethers, helpers: { time }, networkHelpers: { loadFixture }, -} = await network.connect(); +} = await network.create(); const MAX_UINT32 = (1n << 32n) - 1n; const MAX_UINT48 = (1n << 48n) - 1n; From f76b4ffcbf4934db51b2d8fd8b254c2fb74384a0 Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Tue, 21 Apr 2026 09:50:54 -0300 Subject: [PATCH 53/91] Optimize the `hre.artifacts.readArtifact` overload This method used to call readArtifact with a try/catch to check if an artifact existed. This is slower than calling `hre.artifacts.artifactExists` as the errors thrown by `readArtifact` compute the list of similar artifact names. This commit changes the overload to check if an artifact exists using `artifactExists` first, and only calling `readArtifact` if that's positive. --- .../hook-handlers/hre.ts | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts index ad91e1e16dd..7ec950e738d 100644 --- a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts @@ -3,31 +3,26 @@ import type { HardhatRuntimeEnvironmentHooks, HookContext } from 'hardhat/types/ import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import type { ArtifactManager } from 'hardhat/types/artifacts'; -const suffixes = ['UpgradeableWithInit', 'Upgradeable', '']; - -function isExpectedError(e: unknown, suffix: string): boolean { - return HardhatError.isHardhatError(e) && e.number === 1000 && suffix !== ''; -} +const suffixes = ['UpgradeableWithInit', 'Upgradeable']; const overrideReadArtifact = - (runSuper: ArtifactManager['readArtifact']) => + (artifactExists: ArtifactManager['artifactExists'], runSuper: ArtifactManager['readArtifact']) => async (contractNameOrFullyQualifiedName: ContractNameT) => { for (const suffix of suffixes) { - try { + const artifactWithSuffix = contractNameOrFullyQualifiedName + suffix; + if (await artifactExists(artifactWithSuffix)) { return await runSuper((contractNameOrFullyQualifiedName + suffix) as ContractNameT); - } catch (e) { - if (isExpectedError(e, suffix)) { - continue; - } else { - throw e; - } } } - throw new Error('Unreachable'); + + return await runSuper(contractNameOrFullyQualifiedName as ContractNameT); }; export default async (): Promise> => ({ created: async (context: HookContext, hre: HardhatRuntimeEnvironment): Promise => { - hre.artifacts.readArtifact = overrideReadArtifact(hre.artifacts.readArtifact.bind(hre.artifacts)); + hre.artifacts.readArtifact = overrideReadArtifact( + hre.artifacts.artifactExists.bind(hre.artifacts), + hre.artifacts.readArtifact.bind(hre.artifacts), + ); }, }); From 1c3fcfbce77d19f00c551adc477402919d3da789 Mon Sep 17 00:00:00 2001 From: John Kane Date: Tue, 5 May 2026 21:46:20 +0100 Subject: [PATCH 54/91] refactor: leverage extracted variable --- hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts index 7ec950e738d..c5421494501 100644 --- a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts @@ -11,7 +11,7 @@ const overrideReadArtifact = for (const suffix of suffixes) { const artifactWithSuffix = contractNameOrFullyQualifiedName + suffix; if (await artifactExists(artifactWithSuffix)) { - return await runSuper((contractNameOrFullyQualifiedName + suffix) as ContractNameT); + return await runSuper(artifactWithSuffix as ContractNameT); } } From 5ca912747a0282e2152df59efa2ad9e6bffdeb0a Mon Sep 17 00:00:00 2001 From: John Kane Date: Tue, 5 May 2026 22:16:45 +0100 Subject: [PATCH 55/91] refactor: switch from loop to functional pattern --- .../hook-handlers/hre.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts index c5421494501..1678b422873 100644 --- a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts @@ -1,4 +1,3 @@ -import { HardhatError } from '@nomicfoundation/hardhat-errors'; import type { HardhatRuntimeEnvironmentHooks, HookContext } from 'hardhat/types/hooks'; import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; import type { ArtifactManager } from 'hardhat/types/artifacts'; @@ -7,16 +6,15 @@ const suffixes = ['UpgradeableWithInit', 'Upgradeable']; const overrideReadArtifact = (artifactExists: ArtifactManager['artifactExists'], runSuper: ArtifactManager['readArtifact']) => - async (contractNameOrFullyQualifiedName: ContractNameT) => { - for (const suffix of suffixes) { - const artifactWithSuffix = contractNameOrFullyQualifiedName + suffix; - if (await artifactExists(artifactWithSuffix)) { - return await runSuper(artifactWithSuffix as ContractNameT); - } - } - - return await runSuper(contractNameOrFullyQualifiedName as ContractNameT); - }; + (contractNameOrFullyQualifiedName: ContractNameT) => + suffixes + .map(suffix => contractNameOrFullyQualifiedName + suffix) + .reduce>( + (acc, artifactWithSuffix) => + acc.then(result => result || artifactExists(artifactWithSuffix).then(exists => exists && artifactWithSuffix)), + Promise.resolve(undefined), + ) + .then(artifactWithSuffix => runSuper((artifactWithSuffix || contractNameOrFullyQualifiedName) as ContractNameT)); export default async (): Promise> => ({ created: async (context: HookContext, hre: HardhatRuntimeEnvironment): Promise => { From a6cce3e8f8df0c150d4c259850cf475360cbe42b Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Tue, 21 Apr 2026 09:57:28 -0300 Subject: [PATCH 56/91] Revamp `hardhat-exposed`. This commit revamps how `hardhat-exposed` works in Hardhat 3, making it closer to how the Hardhat 2 version works. This new version works like this: - It adds a new task that generates the exposed contracts, taking the compilation cache into account to avoid regenerating contracts that already exist. - The new task behavior is inspired in the Hardhat 2 version of the plugin: - It gets the uncached compilation jobs using `hre.solidity` - It creates a new build info for each one of them, which only return the AST of the contracts - It uses the output of those build infos to generate the exposed contracts - It overrides the `build` task to call the new task before running the original implementation. - The plugin now adds `config.exposed.outDir` to `config.paths.sources.solidity` so that the `build` task also builds them. - This new version removes the `imports` functionality, as the way Hardhat 3 treats npm compilation roots is incompatible with it. I also did some updates to the `expose.ts` contract to realign some of the names of its functions and variables to Hardhat 3 and this version of the plugin. --- .../hardhat-exposed/hook-handlers/clean.ts | 2 +- .../hardhat-exposed/hook-handlers/config.ts | 17 +- .../hardhat-exposed/hook-handlers/solidity.ts | 87 -------- .../hardhat-exposed/internal/build-info.ts | 26 +++ hardhat/hardhat-exposed/internal/expose.ts | 204 ++++++++---------- hardhat/hardhat-exposed/internal/types.ts | 4 +- hardhat/hardhat-exposed/plugin.ts | 17 +- hardhat/hardhat-exposed/tasks/build.ts | 23 ++ .../tasks/generate-exposed-contracts.ts | 104 +++++++++ 9 files changed, 260 insertions(+), 224 deletions(-) delete mode 100644 hardhat/hardhat-exposed/hook-handlers/solidity.ts create mode 100644 hardhat/hardhat-exposed/internal/build-info.ts create mode 100644 hardhat/hardhat-exposed/tasks/build.ts create mode 100644 hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts diff --git a/hardhat/hardhat-exposed/hook-handlers/clean.ts b/hardhat/hardhat-exposed/hook-handlers/clean.ts index 1a515d8fc60..dfe0c71c348 100644 --- a/hardhat/hardhat-exposed/hook-handlers/clean.ts +++ b/hardhat/hardhat-exposed/hook-handlers/clean.ts @@ -2,7 +2,7 @@ import fs from 'node:fs/promises'; import type { CleanHooks } from 'hardhat/types/hooks'; -import type {} from '../type-extensions'; +export type * from '../type-extensions.ts'; export default async (): Promise> => ({ onClean: context => fs.rm(context.config.exposed.outDir, { recursive: true, force: true }), diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts index 54e63f5574c..c7be1e19e2b 100644 --- a/hardhat/hardhat-exposed/hook-handlers/config.ts +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import type { ConfigHooks } from 'hardhat/types/hooks'; -import type {} from '../type-extensions'; +export type * from '../type-extensions.ts'; export default async (): Promise> => ({ validateUserConfig: async userConfig => { @@ -29,9 +29,6 @@ export default async (): Promise> => ({ if (userConfig.exposed?.initializers !== undefined && typeof userConfig.exposed?.initializers !== 'boolean') { results.push({ path: ['exposed', 'initializers'], message: 'Expected an optional boolean.' }); } - if (userConfig.exposed?.imports !== undefined && typeof userConfig.exposed?.imports !== 'boolean') { - results.push({ path: ['exposed', 'imports'], message: 'Expected an optional boolean.' }); - } return results; }, @@ -39,14 +36,22 @@ export default async (): Promise> => ({ next(userConfig, resolveConfigurationVariable).then(partiallyResolvedConfig => { const makeAbsolutePath = (p: string) => path.isAbsolute(p) ? p : path.resolve(partiallyResolvedConfig.paths.root, p); + const outDir = makeAbsolutePath(userConfig.exposed?.outDir ?? 'contracts-exposed'); return { ...partiallyResolvedConfig, + paths: { + ...partiallyResolvedConfig.paths, + sources: { + ...partiallyResolvedConfig.paths.sources, + solidity: [...partiallyResolvedConfig.paths.sources.solidity, outDir], + }, + }, exposed: { - ...userConfig.exposed, prefix: userConfig.exposed?.prefix ?? '$', exclude: (userConfig.exposed?.exclude ?? []).map(makeAbsolutePath), include: (userConfig.exposed?.include ?? ['**/*']).map(makeAbsolutePath), - outDir: makeAbsolutePath(userConfig.exposed?.outDir ?? 'contracts-exposed'), + outDir, + initializers: userConfig.exposed?.initializers ?? false, }, }; }), diff --git a/hardhat/hardhat-exposed/hook-handlers/solidity.ts b/hardhat/hardhat-exposed/hook-handlers/solidity.ts deleted file mode 100644 index ab71b6d6d68..00000000000 --- a/hardhat/hardhat-exposed/hook-handlers/solidity.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { assert } from 'node:console'; -import fs from 'node:fs'; -import path from 'node:path'; - -import type { SolidityHooks } from 'hardhat/types/hooks'; -import { FileBuildResultType, SolidityBuildInfoOutput } from 'hardhat/types/solidity'; -import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; - -import { getExposed } from '../internal/expose.ts'; - -import type {} from '../type-extensions'; - -export default async (): Promise> => ({ - build: async (context, rootPaths, options, next) => { - const includes = (rootPath: string) => - context.config.exposed.include.some(p => path.matchesGlob(rootPath, p)) && - !context.config.exposed.exclude.some(p => path.matchesGlob(rootPath, p)) && - !rootPath.startsWith(context.config.exposed.outDir) && - !rootPath.startsWith('npm:'); - - // 1. Build the original contracts - const results = await next(context, rootPaths, options); - if ('reason' in results) return results; - - // Only apply expose logic when compiling contracts (skip for tests) - if (options?.scope === 'contracts' && rootPaths.some(includes)) { - // Start spinner - const spinner = createSpinner({ text: `Generating exposed contracts...` }); - spinner.start(); - - // 2. Recover the build IDs, and the corresponding root files - const rootFilesPathsByBuildId: Record = {}; - for (const [rootPath, result] of results) { - if (!includes(rootPath)) continue; - switch (result.type) { - case FileBuildResultType.BUILD_SUCCESS: { - const buildId = await result.compilationJob.getBuildId(); - rootFilesPathsByBuildId[buildId] ??= []; - rootFilesPathsByBuildId[buildId].push(rootPath); - break; - } - case FileBuildResultType.CACHE_HIT: { - const buildId = result.buildId; - rootFilesPathsByBuildId[buildId] ??= []; - rootFilesPathsByBuildId[buildId].push(rootPath); - break; - } - } - } - - // 3. Generate all exposed contracts - const exposedPaths: Set = new Set(); - for (const [buildId, buildRootPaths] of Object.entries(rootFilesPathsByBuildId)) { - const outputPath = await context.artifacts.getBuildInfoOutputPath(buildId); - assert(outputPath, `No build info found for build ID ${buildId}`); - - const { output } = JSON.parse(fs.readFileSync(outputPath!, 'utf-8')) as SolidityBuildInfoOutput; - - const exposed = await getExposed( - output, - (p: string) => buildRootPaths.includes(p) && includes(p), - context.config, - ); - for (const [exposedPath, exposedContent] of exposed) { - fs.mkdirSync(path.dirname(exposedPath), { recursive: true }); - fs.writeFileSync(exposedPath, exposedContent); - exposedPaths.add(exposedPath); - } - } - - // Step spinner - spinner.stop(); - - // 4. Build all exposed contracts - const exposedResults = await context.solidity.build(Array.from(exposedPaths), options); - if ('reason' in exposedResults) return exposedResults; - - // Merge exposed results into the original run - for (const [filePath, result] of exposedResults) { - results.set(filePath, result); - } - } - - // 5. Return all results - return results; - }, -}); diff --git a/hardhat/hardhat-exposed/internal/build-info.ts b/hardhat/hardhat-exposed/internal/build-info.ts new file mode 100644 index 00000000000..cf95b1384d3 --- /dev/null +++ b/hardhat/hardhat-exposed/internal/build-info.ts @@ -0,0 +1,26 @@ +import { CompilationJob, SolidityBuildInfo } from 'hardhat/types/solidity'; + +export async function compilationJobToAstOnlyBuildInfo(compilationJob: CompilationJob): Promise { + const originalBuildId = await compilationJob.getBuildId(); + const originalSolcInput = await compilationJob.getSolcInput(); + + return { + _format: 'hh3-sol-build-info-1', + id: originalBuildId + '-ast-only', + solcVersion: compilationJob.solcConfig.version, + solcLongVersion: compilationJob.solcLongVersion, + input: { + ...originalSolcInput, + settings: { + ...originalSolcInput.settings, + optimizer: { enabled: false }, + outputSelection: { + '*': { + '': ['ast'], + }, + }, + }, + }, + userSourceNameMap: compilationJob.dependencyGraph.getRootsUserSourceNameMap(), + }; +} diff --git a/hardhat/hardhat-exposed/internal/expose.ts b/hardhat/hardhat-exposed/internal/expose.ts index 7e6fbfdb588..4b634180ba9 100644 --- a/hardhat/hardhat-exposed/internal/expose.ts +++ b/hardhat/hardhat-exposed/internal/expose.ts @@ -1,7 +1,5 @@ -import assert from 'node:assert/strict'; -import path from 'node:path'; import type { HardhatConfig } from 'hardhat/types/config'; -import type { CompilerOutput } from 'hardhat/types/solidity'; +import type { CompilerOutput, SolidityBuildInfo } from 'hardhat/types/solidity'; // AST manipulation import type { @@ -20,112 +18,84 @@ import { findAll, astDereferencer } from 'solidity-ast/utils.js'; import type { Lines } from './format-lines.ts'; import { formatLines, spaceBetween } from './format-lines.ts'; +import assert from 'node:assert'; +import path from 'node:path'; const exposedVersionPragma = '>=0.6.0'; -type ContractFilter = (node: ContractDefinition) => boolean; type ExposedFile = { absolutePath: string; content: string; }; export function getExposed( + solidityBuildInfo: SolidityBuildInfo, solcOutput: CompilerOutput, - include: (sourceName: string) => boolean, config: HardhatConfig, ): Map { const res = new Map(); const deref = astDereferencer(solcOutput); - const imports: Record> = {}; - - for (const { ast } of Object.values(solcOutput.sources)) { - const absolutePath = path.join(config.paths.root, ast.absolutePath.replace(/^project\//, '')); - if (!include(absolutePath)) { - continue; - } + for (const [sourceName, inputSourceName] of Object.entries(solidityBuildInfo.userSourceNameMap)) { + const ast = solcOutput.sources[inputSourceName].ast; - const exposedFile = getExposedFile(ast, deref, config); + const exposedFile = getExposedFile(sourceName, inputSourceName, ast, deref, config); if (exposedFile !== undefined) { res.set(exposedFile.absolutePath, exposedFile.content); } - - if (config.exposed.imports) { - const queue = new Set(findAll('ImportDirective', ast)); - for (const imp of queue) { - const absolutePath = path.join(config.paths.root, imp.absolutePath.replace(/^project\//, '')); - if (include(absolutePath)) { - continue; - } - - const impUnit = deref('SourceUnit', imp.sourceUnit); - for (const indirectImp of findAll('ImportDirective', impUnit)) { - queue.add(indirectImp); - } - for (const { foreign } of imp.symbolAliases) { - const foreignId = impUnit.exportedSymbols[foreign.name]?.[0]; - assert(foreignId !== undefined); - const { node, sourceUnit } = deref.withSourceUnit('*', foreignId); - if (node.nodeType === 'ContractDefinition' && node.contractKind !== 'interface') { - imports[sourceUnit.absolutePath] ??= new Set(); - imports[sourceUnit.absolutePath]!.add(node); - } - } - } - } } - for (const [absoluteImportedPath, contracts] of Object.entries(imports)) { - const { ast } = solcOutput.sources[absoluteImportedPath]; - assert(ast !== undefined); + return res; +} - const exposedFile = getExposedFile(ast, deref, config, node => contracts.has(node)); - if (exposedFile !== undefined) { - res.set(exposedFile.absolutePath, exposedFile.content); - } +function getImportPathFromExposedContract( + exposedFileAbsolutePath: string, + importedFileInputSourceName: string, +): string { + let importPath: string; + + if (importedFileInputSourceName.startsWith('project/')) { + importPath = path.relative( + path.dirname(exposedFileAbsolutePath), + importedFileInputSourceName.replace(/^project\//, ''), + ); + } else { + importPath = importedFileInputSourceName; } - return res; + // Normalize windows paths to unix paths + return importPath.replaceAll(/\\/g, '/'); } function getExposedFile( + sourceName: string, + inputSourceName: string, ast: SourceUnit, deref: ASTDereferencer, config: HardhatConfig, - filter?: ContractFilter, ): ExposedFile | undefined { - const exposedPath = path.join( + const exposedFileAbsolutePath = path.join( config.exposed.outDir, - ...(ast.absolutePath.startsWith('project/') - ? [ast.absolutePath.replace(/^project\//, '')] - : ['$_', ast.absolutePath]), + inputSourceName.startsWith('project/') ? sourceName : inputSourceName.replace('npm:', ''), ); - const relativizePath = (p: string) => - (p.startsWith('project/') ? path.relative(path.dirname(exposedPath), p.replace(/^project\//, '')) : p).replace( - /\\/g, - '/', - ); - const content = getExposedContent( + exposedFileAbsolutePath, ast, - relativizePath, deref, config.exposed.prefix, config.exposed.initializers, - filter, ); - return content === undefined ? undefined : { absolutePath: exposedPath, content }; + return content === undefined ? undefined : { absolutePath: exposedFileAbsolutePath, content }; } function getExposedContent( + exposedFileAbsolutePath: string, ast: SourceUnit, - relativizePath: (p: string) => string, deref: ASTDereferencer, prefix: string, - initializers = false, - filter?: ContractFilter, + initializers: boolean, ): string | undefined { if (prefix === '' || /^\d|[^0-9a-z_$]/i.test(prefix)) { throw new Error(`Prefix '${prefix}' is not valid`); @@ -133,12 +103,12 @@ function getExposedContent( const contractPrefix = prefix.replace(/^./, c => c.toUpperCase()); - const imports = Array.from(getNeededImports(ast, deref), u => relativizePath(u.absolutePath)); - - const contracts = [...findAll('ContractDefinition', ast)].filter( - c => filter?.(c) !== false && c.contractKind !== 'interface', + const imports = Array.from(getNeededImports(ast, deref), u => + getImportPathFromExposedContract(exposedFileAbsolutePath, u.absolutePath), ); + const contracts = [...findAll('ContractDefinition', ast)].filter(c => c.contractKind !== 'interface'); + if (contracts.length === 0) { return undefined; } @@ -172,9 +142,18 @@ function getExposedContent( const externalizableFunctions = getFunctions(c, deref, subset).filter(f => isExternalizable(f, deref)); const returnedEventFunctions = externalizableFunctions.filter(fn => isNonViewWithReturns(fn)); + // Pre-compute arguments per function/modifier to avoid redundant AST traversals + const argsCache = new Map(); + for (const fn of externalizableFunctions) { + argsCache.set(fn.id, getFunctionArguments(fn, c, deref)); + } + for (const m of modifiers) { + argsCache.set(m.id, getFunctionArguments(m, c, deref)); + } + const clashingFunctions: Record = {}; for (const fn of externalizableFunctions) { - const id = getFunctionId(fn, c, deref); + const id = getFunctionIdFromArgs(fn, argsCache.get(fn.id)!); clashingFunctions[id] ??= 0; clashingFunctions[id] += 1; } @@ -185,43 +164,44 @@ function getExposedContent( clashingEvents[fn.name]! += 1; } + const allStorageArgs = getAllStorageArgumentsFromCache([...externalizableFunctions, ...modifiers], argsCache); + return [ contractHeader.join(' '), [`bytes32 public constant __hh_exposed_bytecode_marker = "hardhat-exposed";\n`], spaceBetween( // slots for storage function parameters - ...getAllStorageArguments([...externalizableFunctions, ...modifiers], c, deref).map(a => [ - `mapping(uint256 => ${a.storageType}) internal ${prefix}${a.storageVar};`, - ]), + ...allStorageArgs.map(a => [`mapping(uint256 => ${a.storageType}) internal ${prefix}${a.storageVar};`]), // events for internal returns ...returnedEventFunctions.map(fn => { - const evName = clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false); + const fnArgs = argsCache.get(fn.id)!; + const evName = + clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualifiedFromArgs(fn, fnArgs, false); const params = getFunctionReturnParameters(fn, c, deref, null); return [`event return${prefix}${evName}(${params.map(printArgument).join(', ')});`]; }), // constructor makeConstructor(c, deref, initializers), // accessor to internal variables - ...externalizableVariables.map(v => [ - [ - 'function', - `${prefix}${v.name}(${getVarGetterArgs(v, c, deref).map(printArgument).join(', ')})`, - 'external', - v.mutability === 'mutable' || (v.mutability === 'immutable' && !v.value) ? 'view' : 'pure', - 'returns', - `(${getVarGetterReturnType(v, c, deref)})`, - '{', - ].join(' '), - [ - `return ${isLibrary ? c.name + '.' : ''}${v.name}${getVarGetterArgs(v, c, deref) - .map(a => `[${a.name}]`) - .join('')};`, - ], - '}', - ]), + ...externalizableVariables.map(v => { + const varArgs = getVarGetterArgs(v, c, deref); + return [ + [ + 'function', + `${prefix}${v.name}(${varArgs.map(printArgument).join(', ')})`, + 'external', + v.mutability === 'mutable' || (v.mutability === 'immutable' && !v.value) ? 'view' : 'pure', + 'returns', + `(${getVarGetterReturnType(v, c, deref)})`, + '{', + ].join(' '), + [`return ${isLibrary ? c.name + '.' : ''}${v.name}${varArgs.map(a => `[${a.name}]`).join('')};`], + '}', + ]; + }), // modifiers ...modifiers.map(m => { - const fnArgs = getFunctionArguments(m, c, deref); + const fnArgs = argsCache.get(m.id)!; // function header const header = [ @@ -237,15 +217,15 @@ function getExposedContent( }), // external functions ...externalizableFunctions.map(fn => { + const fnArgs = argsCache.get(fn.id)!; const fnName = - clashingFunctions[getFunctionId(fn, c, deref)] === 1 + clashingFunctions[getFunctionIdFromArgs(fn, fnArgs)] === 1 ? fn.name - : getFunctionNameQualified(fn, c, deref, true); - const fnArgs = getFunctionArguments(fn, c, deref); + : getFunctionNameQualifiedFromArgs(fn, fnArgs, true); const fnRets = getFunctionReturnParameters(fn, c, deref); const evName = isNonViewWithReturns(fn) && - (clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualified(fn, c, deref, false)); + (clashingEvents[fn.name] === 1 ? fn.name : getFunctionNameQualifiedFromArgs(fn, fnArgs, false)); // function header const header = ['function', `${prefix}${fnName}(${fnArgs.map(printArgument)})`, 'external']; @@ -306,24 +286,18 @@ function areFunctionsFullyImplemented(contract: ContractDefinition, deref: ASTDe return abstractFunctionIds.size === 0; } -function getFunctionId(fn: FunctionDefinition, context: ContractDefinition, deref: ASTDereferencer): string { - const abiTypes = getFunctionArguments(fn, context, deref).map(a => a.abiType); - return fn.name + abiTypes.join(','); +function getFunctionIdFromArgs(fn: FunctionDefinition, args: Argument[]): string { + return fn.name + args.map(a => a.abiType).join(','); } -function getFunctionNameQualified( - fn: FunctionDefinition, - context: ContractDefinition, - deref: ASTDereferencer, - onlyConflicting: boolean, -): string { - let args = getFunctionArguments(fn, context, deref); +function getFunctionNameQualifiedFromArgs(fn: FunctionDefinition, args: Argument[], onlyConflicting: boolean): string { + let filtered = args; if (onlyConflicting) { - args = args.filter(a => a.type !== a.abiType || a.storageType !== undefined); + filtered = args.filter(a => a.type !== a.abiType || a.storageType !== undefined); } return ( fn.name + - args + filtered .map(arg => arg.storageType ?? arg.type) .map(type => type.replace(/ .*/, '').replace(/[^0-9a-zA-Z$_]+/g, '_')) // sanitize .join('_') @@ -513,22 +487,14 @@ function getFunctionArguments( }); } -function getStorageArguments( - fn: FunctionDefinition | ModifierDefinition, - context: ContractDefinition, - deref: ASTDereferencer, -): Required[] { - return getFunctionArguments(fn, context, deref).filter( - (a): a is Required => !!(a.storageVar && a.storageType), - ); -} - -function getAllStorageArguments( +function getAllStorageArgumentsFromCache( fns: (FunctionDefinition | ModifierDefinition)[], - context: ContractDefinition, - deref: ASTDereferencer, + argsCache: Map, ): Required[] { - return [...new Map(fns.flatMap(fn => getStorageArguments(fn, context, deref)).map(a => [a.storageVar, a])).values()]; + const storageArgs = fns.flatMap(fn => + (argsCache.get(fn.id) ?? []).filter((a): a is Required => !!(a.storageVar && a.storageType)), + ); + return [...new Map(storageArgs.map(a => [a.storageVar, a])).values()]; } function getFunctionReturnParameters( diff --git a/hardhat/hardhat-exposed/internal/types.ts b/hardhat/hardhat-exposed/internal/types.ts index 3311780143b..d4627ea8275 100644 --- a/hardhat/hardhat-exposed/internal/types.ts +++ b/hardhat/hardhat-exposed/internal/types.ts @@ -4,12 +4,12 @@ export interface ExposedUserConfig { include?: string[]; outDir?: string; initializers?: boolean; - imports?: boolean; } -export interface ExposedConfig extends ExposedUserConfig { +export interface ExposedConfig { prefix: string; exclude: string[]; include: string[]; outDir: string; + initializers: boolean; } diff --git a/hardhat/hardhat-exposed/plugin.ts b/hardhat/hardhat-exposed/plugin.ts index 9a9df2e0821..8fe4897db26 100644 --- a/hardhat/hardhat-exposed/plugin.ts +++ b/hardhat/hardhat-exposed/plugin.ts @@ -1,23 +1,22 @@ import type { HardhatPlugin } from 'hardhat/types/plugins'; -import { overrideTask } from 'hardhat/config'; +import { overrideTask, task } from 'hardhat/config'; -import type {} from './type-extensions.ts'; +export type * from './type-extensions.ts'; const hardhatExposedPlugin: HardhatPlugin = { id: 'hardhat-exposed', hookHandlers: { clean: () => import('./hook-handlers/clean.ts'), config: () => import('./hook-handlers/config.ts'), - solidity: () => import('./hook-handlers/solidity.ts'), }, tasks: [ - overrideTask('compile') + overrideTask('build') .addFlag({ name: 'noExpose', description: 'Skip generation of exposed contracts.' }) - .setAction(() => - Promise.resolve({ - default: (args, hre, runSuper) => runSuper(args), // TODO: pass flag to solidity hook handler to suppress exposed contract generation - }), - ) + .setAction(() => import('./tasks/build.ts')) + .build(), + task('generate-exposed-contracts', 'Generates the exposed contracts') + .addFlag({ name: 'force', description: 'Generate all contracts, ignoring the compilation cache' }) + .setAction(() => import('./tasks/generate-exposed-contracts.ts')) .build(), ], }; diff --git a/hardhat/hardhat-exposed/tasks/build.ts b/hardhat/hardhat-exposed/tasks/build.ts new file mode 100644 index 00000000000..716bb635029 --- /dev/null +++ b/hardhat/hardhat-exposed/tasks/build.ts @@ -0,0 +1,23 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import { TaskArguments } from 'hardhat/types/tasks'; + +export interface BuildOverrideArguments { + noExpose: boolean; +} + +export default async function build( + args: BuildOverrideArguments, + hre: HardhatRuntimeEnvironment, + runSuper: (taskArguments: TaskArguments) => Promise, +) { + if (args.noExpose !== true) { + const exposeTask = hre.tasks.getTask('generate-exposed-contracts'); + + // `force` is inherited from `build` + const force = 'force' in args ? args.force : false; + + await exposeTask.run({ force }); + } + + return runSuper(args); +} diff --git a/hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts b/hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts new file mode 100644 index 00000000000..c5e64346a55 --- /dev/null +++ b/hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts @@ -0,0 +1,104 @@ +import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; + +import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import { SolidityBuildInfo } from 'hardhat/types/solidity'; +import assert from 'node:assert'; +import path from 'node:path'; +import fs from 'node:fs'; +import { getExposed } from '../internal/expose'; +import { compilationJobToAstOnlyBuildInfo } from '../internal/build-info'; +import { Result } from 'hardhat/types/utils'; +import { errorResult, successfulResult } from 'hardhat/utils/result'; + +export type * from '../type-extensions'; + +export interface GenerateExposedContractsArguments { + force: boolean; +} + +export default async function generateExposedContracts( + args: GenerateExposedContractsArguments, + hre: HardhatRuntimeEnvironment, +): Promise> { + const rootPaths = await hre.solidity.getRootFilePaths(); + + const includes = async (rootPath: string) => + hre.config.exposed.include.some(p => path.matchesGlob(rootPath, p)) && + !hre.config.exposed.exclude.some(p => path.matchesGlob(rootPath, p)) && + !rootPath.startsWith(hre.config.exposed.outDir) && + (await hre.solidity.getScope(rootPath)) === 'contracts'; + + const inclusionResults = await Promise.all(rootPaths.map(root => includes(root))); + const rootPathsToExpose = rootPaths.filter((root, i) => { + if (!inclusionResults[i]) return false; + + // sanity check: No exposed contract should be a root file to expose + assert( + !root.startsWith(hre.config.exposed.outDir), + 'A root file to be exposed must not be part of the in the hardhat-exposed outDir', + ); + + return true; + }); + + const compilationJobs = await hre.solidity.getCompilationJobs(rootPathsToExpose, { force: args.force }); + + if (!compilationJobs.success) { + console.error("Failed to generate exposed contracts: couldn't get the compilations jobs"); + console.error(compilationJobs.formattedReason); + return errorResult(); + } + + const filteredRootPathsToExpose = rootPathsToExpose.filter(p => !compilationJobs.cacheHits.has(p) || args.force); + + if (filteredRootPathsToExpose.length === 0) { + return successfulResult(); + } + + const astOnlyBuildInfos = new Set(); + const compilationJobIdToAstOnlyBuildInfo = new Map(); + for (const rootPath of filteredRootPathsToExpose) { + const compilationJob = compilationJobs.compilationJobsPerFile.get(rootPath)!; + const compilationJobId = await compilationJob.getBuildId(); + + if (compilationJobIdToAstOnlyBuildInfo.has(compilationJobId)) { + continue; + } + + const astOnlyBuildInfo = await compilationJobToAstOnlyBuildInfo(compilationJob); + compilationJobIdToAstOnlyBuildInfo.set(compilationJobId, astOnlyBuildInfo); + astOnlyBuildInfos.add(astOnlyBuildInfo); + } + + const exposedPaths: Set = new Set(); + const spinner = createSpinner({ text: `Generating exposed contracts...` }); + spinner.start(); + + try { + for (const buildInfo of astOnlyBuildInfos) { + // Sanity check: No exposed contract should be included as part of the + // sources of the ast-only build-info + for (const inputSourceName of Object.keys(buildInfo.input.sources)) { + assert( + !inputSourceName.startsWith(hre.config.exposed.outDir), + 'No exposed contract should be included in the ast-only compilation jobs', + ); + } + + const buildOutput = await hre.solidity.compileBuildInfo(buildInfo); + + const exposed = await getExposed(buildInfo, buildOutput, hre.config); + + for (const [exposedPath, exposedContent] of exposed) { + fs.mkdirSync(path.dirname(exposedPath), { recursive: true }); + fs.writeFileSync(exposedPath, exposedContent); + exposedPaths.add(exposedPath); + } + } + } finally { + spinner.stop(); + } + + console.log(`Generated ${exposedPaths.size} exposed contract files`); + return successfulResult(); +} From 2aa835bef9cccf3d6b03c699ac508bb5d1d8d43b Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Thu, 23 Apr 2026 18:41:26 -0300 Subject: [PATCH 57/91] TS cleanup --- .../hardhat-exposed/hook-handlers/config.ts | 47 ++++++++++--------- .../hardhat-exposed/internal/build-info.ts | 2 +- hardhat/hardhat-exposed/tasks/build.ts | 6 +-- .../tasks/generate-exposed-contracts.ts | 10 ++-- hardhat/hardhat-exposed/type-extensions.ts | 2 +- .../hook-handlers/network.ts | 2 + .../hardhat-oz-contracts-helpers/plugin.ts | 2 +- 7 files changed, 38 insertions(+), 33 deletions(-) diff --git a/hardhat/hardhat-exposed/hook-handlers/config.ts b/hardhat/hardhat-exposed/hook-handlers/config.ts index c7be1e19e2b..2b5ac87f05d 100644 --- a/hardhat/hardhat-exposed/hook-handlers/config.ts +++ b/hardhat/hardhat-exposed/hook-handlers/config.ts @@ -32,27 +32,30 @@ export default async (): Promise> => ({ return results; }, - resolveUserConfig: (userConfig, resolveConfigurationVariable, next) => - next(userConfig, resolveConfigurationVariable).then(partiallyResolvedConfig => { - const makeAbsolutePath = (p: string) => - path.isAbsolute(p) ? p : path.resolve(partiallyResolvedConfig.paths.root, p); - const outDir = makeAbsolutePath(userConfig.exposed?.outDir ?? 'contracts-exposed'); - return { - ...partiallyResolvedConfig, - paths: { - ...partiallyResolvedConfig.paths, - sources: { - ...partiallyResolvedConfig.paths.sources, - solidity: [...partiallyResolvedConfig.paths.sources.solidity, outDir], - }, - }, - exposed: { - prefix: userConfig.exposed?.prefix ?? '$', - exclude: (userConfig.exposed?.exclude ?? []).map(makeAbsolutePath), - include: (userConfig.exposed?.include ?? ['**/*']).map(makeAbsolutePath), - outDir, - initializers: userConfig.exposed?.initializers ?? false, + resolveUserConfig: async (userConfig, resolveConfigurationVariable, next) => { + const partiallyResolvedConfig = await next(userConfig, resolveConfigurationVariable); + + const makeAbsolutePath = (p: string) => + path.isAbsolute(p) ? p : path.resolve(partiallyResolvedConfig.paths.root, p); + + const outDir = makeAbsolutePath(userConfig.exposed?.outDir ?? 'contracts-exposed'); + + return { + ...partiallyResolvedConfig, + paths: { + ...partiallyResolvedConfig.paths, + sources: { + ...partiallyResolvedConfig.paths.sources, + solidity: [...partiallyResolvedConfig.paths.sources.solidity, outDir], }, - }; - }), + }, + exposed: { + prefix: userConfig.exposed?.prefix ?? '$', + exclude: (userConfig.exposed?.exclude ?? []).map(makeAbsolutePath), + include: (userConfig.exposed?.include ?? ['**/*']).map(makeAbsolutePath), + outDir, + initializers: userConfig.exposed?.initializers ?? false, + }, + }; + }, }); diff --git a/hardhat/hardhat-exposed/internal/build-info.ts b/hardhat/hardhat-exposed/internal/build-info.ts index cf95b1384d3..8be806af26e 100644 --- a/hardhat/hardhat-exposed/internal/build-info.ts +++ b/hardhat/hardhat-exposed/internal/build-info.ts @@ -1,4 +1,4 @@ -import { CompilationJob, SolidityBuildInfo } from 'hardhat/types/solidity'; +import type { CompilationJob, SolidityBuildInfo } from 'hardhat/types/solidity'; export async function compilationJobToAstOnlyBuildInfo(compilationJob: CompilationJob): Promise { const originalBuildId = await compilationJob.getBuildId(); diff --git a/hardhat/hardhat-exposed/tasks/build.ts b/hardhat/hardhat-exposed/tasks/build.ts index 716bb635029..a54692db572 100644 --- a/hardhat/hardhat-exposed/tasks/build.ts +++ b/hardhat/hardhat-exposed/tasks/build.ts @@ -1,5 +1,5 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; -import { TaskArguments } from 'hardhat/types/tasks'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { TaskArguments } from 'hardhat/types/tasks'; export interface BuildOverrideArguments { noExpose: boolean; @@ -19,5 +19,5 @@ export default async function build( await exposeTask.run({ force }); } - return runSuper(args); + return await runSuper(args); } diff --git a/hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts b/hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts index c5e64346a55..f44df90c36c 100644 --- a/hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts +++ b/hardhat/hardhat-exposed/tasks/generate-exposed-contracts.ts @@ -1,13 +1,13 @@ import { createSpinner } from '@nomicfoundation/hardhat-utils/spinner'; -import { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; -import { SolidityBuildInfo } from 'hardhat/types/solidity'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types/hre'; +import type { SolidityBuildInfo } from 'hardhat/types/solidity'; +import type { Result } from 'hardhat/types/utils'; import assert from 'node:assert'; import path from 'node:path'; import fs from 'node:fs'; -import { getExposed } from '../internal/expose'; -import { compilationJobToAstOnlyBuildInfo } from '../internal/build-info'; -import { Result } from 'hardhat/types/utils'; +import { getExposed } from '../internal/expose.ts'; +import { compilationJobToAstOnlyBuildInfo } from '../internal/build-info.ts'; import { errorResult, successfulResult } from 'hardhat/utils/result'; export type * from '../type-extensions'; diff --git a/hardhat/hardhat-exposed/type-extensions.ts b/hardhat/hardhat-exposed/type-extensions.ts index 7c0321cc0f1..e158f545519 100644 --- a/hardhat/hardhat-exposed/type-extensions.ts +++ b/hardhat/hardhat-exposed/type-extensions.ts @@ -1,6 +1,6 @@ import 'hardhat/types/config'; -import { ExposedUserConfig, ExposedConfig } from './internal/types.ts'; +import type { ExposedUserConfig, ExposedConfig } from './internal/types.ts'; declare module 'hardhat/types/config' { export interface HardhatUserConfig { diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts index c6fb0b2c0de..15e6337b7d9 100644 --- a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/network.ts @@ -6,6 +6,8 @@ import { getLocalChain } from '../../../test/helpers/chains.js'; import { getSlot, getAddressInSlot, setSlot } from '../../../test/helpers/storage.js'; import { clock, clockFromReceipt, increaseBy, increaseTo, duration } from '../../../test/helpers/time.js'; +export type * from '../type-extensions.js'; + export default async (): Promise> => ({ newConnection: async ( context: HookContext, diff --git a/hardhat/hardhat-oz-contracts-helpers/plugin.ts b/hardhat/hardhat-oz-contracts-helpers/plugin.ts index dd4b1112680..62a3a1e9a08 100644 --- a/hardhat/hardhat-oz-contracts-helpers/plugin.ts +++ b/hardhat/hardhat-oz-contracts-helpers/plugin.ts @@ -1,6 +1,6 @@ import type { HardhatPlugin } from 'hardhat/types/plugins'; -import type {} from './type-extensions.ts'; +export type * from './type-extensions.ts'; const hardhatOzContractsHelpers: HardhatPlugin = { id: 'hardhat-oz-contracts-helpers', From 452b09c7462cedb710075f7e335cbb06d2c93324 Mon Sep 17 00:00:00 2001 From: John Kane Date: Wed, 6 May 2026 11:33:06 +0100 Subject: [PATCH 58/91] refactor: apply expression based approach --- hardhat/hardhat-exposed/internal/expose.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/hardhat/hardhat-exposed/internal/expose.ts b/hardhat/hardhat-exposed/internal/expose.ts index 4b634180ba9..adeca55cf8c 100644 --- a/hardhat/hardhat-exposed/internal/expose.ts +++ b/hardhat/hardhat-exposed/internal/expose.ts @@ -52,19 +52,11 @@ function getImportPathFromExposedContract( exposedFileAbsolutePath: string, importedFileInputSourceName: string, ): string { - let importPath: string; - - if (importedFileInputSourceName.startsWith('project/')) { - importPath = path.relative( - path.dirname(exposedFileAbsolutePath), - importedFileInputSourceName.replace(/^project\//, ''), - ); - } else { - importPath = importedFileInputSourceName; - } - - // Normalize windows paths to unix paths - return importPath.replaceAll(/\\/g, '/'); + return ( + importedFileInputSourceName.startsWith('project/') + ? path.relative(path.dirname(exposedFileAbsolutePath), importedFileInputSourceName.replace(/^project\//, '')) + : importedFileInputSourceName + ).replaceAll(/\\/g, '/'); // Normalize windows paths to unix paths } function getExposedFile( From 5575911e1ca4c04f18294594982e4ac4cba4ebf1 Mon Sep 17 00:00:00 2001 From: John Kane Date: Wed, 6 May 2026 11:39:43 +0100 Subject: [PATCH 59/91] Update hardhat/hardhat-exposed/tasks/build.ts Co-authored-by: Hadrien Croubois --- hardhat/hardhat-exposed/tasks/build.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hardhat/hardhat-exposed/tasks/build.ts b/hardhat/hardhat-exposed/tasks/build.ts index a54692db572..6542774690c 100644 --- a/hardhat/hardhat-exposed/tasks/build.ts +++ b/hardhat/hardhat-exposed/tasks/build.ts @@ -11,12 +11,9 @@ export default async function build( runSuper: (taskArguments: TaskArguments) => Promise, ) { if (args.noExpose !== true) { - const exposeTask = hre.tasks.getTask('generate-exposed-contracts'); - - // `force` is inherited from `build` - const force = 'force' in args ? args.force : false; - - await exposeTask.run({ force }); + await hre.tasks + .getTask('generate-exposed-contracts') + .run({ force: args.force ?? false }); } return await runSuper(args); From 2bc24b95489f9aa18b262a623a82eadef8f8c62f Mon Sep 17 00:00:00 2001 From: John Kane Date: Wed, 6 May 2026 11:52:33 +0100 Subject: [PATCH 60/91] refactor: guard for type system --- hardhat/hardhat-exposed/tasks/build.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hardhat/hardhat-exposed/tasks/build.ts b/hardhat/hardhat-exposed/tasks/build.ts index 6542774690c..b344b20bc9c 100644 --- a/hardhat/hardhat-exposed/tasks/build.ts +++ b/hardhat/hardhat-exposed/tasks/build.ts @@ -11,9 +11,7 @@ export default async function build( runSuper: (taskArguments: TaskArguments) => Promise, ) { if (args.noExpose !== true) { - await hre.tasks - .getTask('generate-exposed-contracts') - .run({ force: args.force ?? false }); + await hre.tasks.getTask('generate-exposed-contracts').run({ force: 'force' in args ? args.force : false }); } return await runSuper(args); From 7bfa7bf05a46d2998b41fddfe65a40006b327d22 Mon Sep 17 00:00:00 2001 From: John Kane Date: Wed, 6 May 2026 15:53:14 +0100 Subject: [PATCH 61/91] refactor: apply functional style --- hardhat/hardhat-exposed/internal/expose.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hardhat/hardhat-exposed/internal/expose.ts b/hardhat/hardhat-exposed/internal/expose.ts index adeca55cf8c..592d312d2e3 100644 --- a/hardhat/hardhat-exposed/internal/expose.ts +++ b/hardhat/hardhat-exposed/internal/expose.ts @@ -283,13 +283,10 @@ function getFunctionIdFromArgs(fn: FunctionDefinition, args: Argument[]): string } function getFunctionNameQualifiedFromArgs(fn: FunctionDefinition, args: Argument[], onlyConflicting: boolean): string { - let filtered = args; - if (onlyConflicting) { - filtered = args.filter(a => a.type !== a.abiType || a.storageType !== undefined); - } return ( fn.name + - filtered + args + .filter(a => !onlyConflicting || a.type !== a.abiType || a.storageType !== undefined) .map(arg => arg.storageType ?? arg.type) .map(type => type.replace(/ .*/, '').replace(/[^0-9a-zA-Z$_]+/g, '_')) // sanitize .join('_') From 67b8436b11db6f4d3f5a37039a26b54651064b60 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 6 May 2026 17:02:22 +0200 Subject: [PATCH 62/91] simply typescript types --- hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts index 1678b422873..a24e66ccefc 100644 --- a/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts +++ b/hardhat/hardhat-oz-contracts-helpers/hook-handlers/hre.ts @@ -9,10 +9,10 @@ const overrideReadArtifact = (contractNameOrFullyQualifiedName: ContractNameT) => suffixes .map(suffix => contractNameOrFullyQualifiedName + suffix) - .reduce>( + .reduce>( (acc, artifactWithSuffix) => acc.then(result => result || artifactExists(artifactWithSuffix).then(exists => exists && artifactWithSuffix)), - Promise.resolve(undefined), + Promise.resolve(false), ) .then(artifactWithSuffix => runSuper((artifactWithSuffix || contractNameOrFullyQualifiedName) as ContractNameT)); From 593e23ebd092e200e2dc39c11264daba60a5a9bf Mon Sep 17 00:00:00 2001 From: John Kane Date: Thu, 23 Apr 2026 10:52:10 +0100 Subject: [PATCH 63/91] chore: update to hardhat 3.4.4 Update to the latest version of Hardhat to get coverage bug fixes. --- package-lock.json | 114 ++++++++++++++++++++-------------------------- package.json | 2 +- 2 files changed, 50 insertions(+), 66 deletions(-) diff --git a/package-lock.json b/package-lock.json index 564c176d7fa..7fcf01d143b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.4.0", + "hardhat": "^3.4.4", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", @@ -1602,28 +1602,28 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.29.tgz", - "integrity": "sha512-/c1LbBC3EFgOIKRup0lQ2SIqL9MLdqdOHDM9Mta5CyDOk9cQFlBSTpWCGDrh+p1BIsPFpVHqa4yjkBmZyL1aZA==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.31.tgz", + "integrity": "sha512-4I2R1qFsCLiKelOhqSahkz0+MnazdV+33iN0NEeen6rHaQoTYSLCpsMyb9Uj4MfunYa5QLSz2WrtO2f9E7Fegg==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.29", - "@nomicfoundation/edr-darwin-x64": "0.12.0-next.29", - "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.29", - "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.29", - "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.29", - "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.29", - "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.29" + "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.31", + "@nomicfoundation/edr-darwin-x64": "0.12.0-next.31", + "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.31", + "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.31", + "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.31", + "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.31", + "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.31" }, "engines": { "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.29.tgz", - "integrity": "sha512-qzVcAkUsrVT2Za9pLzTYL/eNLS09R+JSG+4LpQ56Wg3mkjbwItn/F6C/XbGqMbNiEGfLi5kVvtYOtT7yu04/Tg==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.31.tgz", + "integrity": "sha512-z2gEimnvx6SinJsHsat89USFt+0sclSY4PkeI/FBFlAshiEHYKjFpN5svdC3ghOrgIFctGt7lSzfrbiP+KhVZg==", "dev": true, "license": "MIT", "engines": { @@ -1631,9 +1631,9 @@ } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.29.tgz", - "integrity": "sha512-P2BSYLsDoM1dGi/0NO3ps3l76NbvFDAnmCUS5SLLLdG/b8RUvWKHtfZFrQgy1KCPDFiiG5IlwzcmAwsPjsyOVQ==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.31.tgz", + "integrity": "sha512-WbiIuARMO59XY8ZFs8ZFHildyMf5tnWIOt9S1wttcoYkiXwYwI2tC9JIrZ3rziv4DttWIV5aPxp23G7W0A1t4g==", "dev": true, "license": "MIT", "engines": { @@ -1641,9 +1641,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.29.tgz", - "integrity": "sha512-3SKZIaZCCuY6fAHj6GpTYpQPj3S0LFO6YUUZw3aSg1joBmM9FkP9a1IiYQOBZnZqk0Fa+pHy2dHMVWDczQHX7g==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.31.tgz", + "integrity": "sha512-Q9p/wk6DEjGeLqp/RXiCQ8vR5irZpF6emXKElkt6jxjrOYd6VnFPc6I8v8M1Lc620aT6pn2mRwKDuKcktuqcFA==", "dev": true, "license": "MIT", "engines": { @@ -1651,9 +1651,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.29.tgz", - "integrity": "sha512-GGDJX3We8+XQ0L1Yy2i6ulbucQPO7HpQ5IYkxFLKL0H611ErErHLawrGIvfcfaDYfnFrC/3uxWEqMQ4GhrWbBQ==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.31.tgz", + "integrity": "sha512-/5TYcR+NpkfxYKQbhLFN5Vj36GUqP6NqCkYqfdKuv2+0r3y4hadeU3p3WhCKr5YGrnSJwrxBnGBZwyfeHJHP+g==", "dev": true, "license": "MIT", "engines": { @@ -1661,9 +1661,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.29.tgz", - "integrity": "sha512-d92L55iy/EzMlu341RgFG1Pqd6Mpd1MmcYi48Na2VtMs7rqRIcAUGeLgoooScLinM5JqTIb1uYVghehFrx98gA==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.31.tgz", + "integrity": "sha512-T1EqWrja6oWglHSi0TtmITaic4DaAT+7u3yoOrjMh2oDQU+tM6tHNENWyZ244Neru23bg0i9ZnO0o8nI9uL99g==", "dev": true, "license": "MIT", "engines": { @@ -1671,9 +1671,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.29.tgz", - "integrity": "sha512-oOupaxyR6KUzvhJ0zsVU0yfeepB3hcTKxvowq2lPPwp6cMFzPY8PFe6uck+7+rXwog0dDkEa4R/RPEE9DtUelw==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.31.tgz", + "integrity": "sha512-gvQV4DVmnYobOuqsxuPjPuzdmpxR8yEbV75JOw5luVU6te6SBrPcmk8NNn12ZkAlTbyuDc81c7aRd4A1RfEmuw==", "dev": true, "license": "MIT", "engines": { @@ -1681,9 +1681,9 @@ } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.12.0-next.29", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.29.tgz", - "integrity": "sha512-QWvz4tTt1Z5JYKXODtqqdxfIQMiWFPGLVIeVJlXl2HSPJAIWTMU+EiBhlGGxP0qoUotUbdJbe7lpG4gw3yKIcA==", + "version": "0.12.0-next.31", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.31.tgz", + "integrity": "sha512-xUDeakZGAZF010k0mhAm387gny0gdKq2pHv+7MxqVvuQumW0PNKQfBNfl7+bNgt9Bc4o1/FBP89hUR5tHXU0Cg==", "dev": true, "license": "MIT", "engines": { @@ -1691,13 +1691,13 @@ } }, "node_modules/@nomicfoundation/hardhat-errors": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.11.tgz", - "integrity": "sha512-XEKplQ+FhZD1PgIGSj62scoqB/y+uG8x+V+U68m1a+4L1I46y4/gZQGIuMLkRZeqhPHsLle6ykDB+vn8qtwqzw==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.12.tgz", + "integrity": "sha512-2viEq1D19FHWKpfB2vVeL0R6d+iZR2E5h0EhKQQMc1ukDUV2fel/7fRjlWuCOx2CFC+5mHL2saRcN8KlYsX8hg==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-utils": "^4.0.3" + "@nomicfoundation/hardhat-utils": "^4.1.0" } }, "node_modules/@nomicfoundation/hardhat-ethers": { @@ -1905,14 +1905,13 @@ } }, "node_modules/@nomicfoundation/hardhat-utils": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.0.3.tgz", - "integrity": "sha512-JLEexWvugRK6kJioPTeisIcmmWm6yHv9n5GRLboXvhAndJNL1Z/FrQLZYZaDSmkHPE5aRK6t+zK7Rh92Yar6hQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.1.0.tgz", + "integrity": "sha512-qze9X5P8LIB36rjS3cFhD7asG82pjZDmJAYfCQBSDhMYJM1HDZrYKgKfJLEE36TmrhjgQ/hlNO0DikDq0e2VYg==", "dev": true, "license": "MIT", "dependencies": { "@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", @@ -2010,9 +2009,9 @@ } }, "node_modules/@nomicfoundation/hardhat-vendored": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.2.tgz", - "integrity": "sha512-v65aSwA0k15QzMUL4cFXT2CPnrjrxR1BE2V24BjNCPnlhwI/2e1Gfy7TaIGSor5yZGeiQ5ScI+wP/ovKxQBr0g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.3.tgz", + "integrity": "sha512-VzxwR1Yz8zAztiSIkjFai/XyqfuMMvX95ppXxWlJ1ci0TiHu6sut1oOAD+VJVCq+LHNpr2fWMUcugZq9uKbicg==", "dev": true, "license": "MIT" }, @@ -4440,23 +4439,21 @@ } }, "node_modules/hardhat": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.4.0.tgz", - "integrity": "sha512-3QDQY4jOK4DXavkUm7itL5O9fY1AKo9CzDly9E6cQV4X1TpknMzbenpluzKjiSjpRsIBXhyV6EuGl0vaXug7Og==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.4.4.tgz", + "integrity": "sha512-m7EbNA++jZu3h8KuLxHY/vCMTbWfnlXPHO+iKO3v8XoHOBiplVIy5V4MsblcursS694r1fINaFC26a6xd7kY8Q==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr": "0.12.0-next.29", - "@nomicfoundation/hardhat-errors": "^3.0.11", - "@nomicfoundation/hardhat-utils": "^4.0.3", - "@nomicfoundation/hardhat-vendored": "^3.0.2", + "@nomicfoundation/edr": "0.12.0-next.31", + "@nomicfoundation/hardhat-errors": "^3.0.12", + "@nomicfoundation/hardhat-utils": "^4.1.0", + "@nomicfoundation/hardhat-vendored": "^3.0.3", "@nomicfoundation/hardhat-zod-utils": "^3.0.4", "@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", @@ -4561,19 +4558,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/hardhat/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/hardhat/node_modules/chokidar": { "version": "4.0.3", "dev": true, diff --git a/package.json b/package.json index bf5fbc5b3ee..9ceafdcf155 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.4.0", + "hardhat": "^3.4.4", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", From 2089f5f8806b0a8708a585876b391661d7bde5b4 Mon Sep 17 00:00:00 2001 From: John Kane Date: Thu, 23 Apr 2026 11:37:08 +0100 Subject: [PATCH 64/91] chore: tweak gas comparison CI step The CI job for test includes a gas report generation flag, it was being incorrectly passed through `npm run`. --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 0f0b4e1562d..3a9dcae405d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -42,7 +42,7 @@ jobs: with: foundry: 'on' # needed by npm run test:pragma - name: Run tests and generate gas report - run: npm run test --gas-stats-json gasReport.json + run: npm run test -- --gas-stats-json gasReport.json - name: Check linearisation of the inheritance graph run: npm run test:inheritance - name: Check pragma validity From f223159ce6812fb10a5cf8375b53909a6aa3975a Mon Sep 17 00:00:00 2001 From: John Kane Date: Mon, 27 Apr 2026 14:24:04 +0100 Subject: [PATCH 65/91] chore: update compare layout and extract layout scripts to ESM Update the scripts to import using ESM. Update any API usage or assumptions that have changed in Hardhat 3 from Hardhat 2. --- scripts/checks/compare-layout.js | 10 ++++++---- scripts/checks/extract-layout.js | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/scripts/checks/compare-layout.js b/scripts/checks/compare-layout.js index 69a1e77fd70..4d4770ddfb6 100755 --- a/scripts/checks/compare-layout.js +++ b/scripts/checks/compare-layout.js @@ -1,10 +1,12 @@ #!/usr/bin/env node -const fs = require('fs'); -const { getStorageUpgradeReport } = require('@openzeppelin/upgrades-core/dist/storage'); +import fs from 'fs'; +import { getStorageUpgradeReport } from '@openzeppelin/upgrades-core/dist/storage/index.js'; -const { hideBin } = require('yargs/helpers'); -const { argv } = require('yargs/yargs')(hideBin(process.argv)) +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +const { argv } = yargs(hideBin(process.argv)) .env('') .options({ ref: { type: 'string', required: true }, diff --git a/scripts/checks/extract-layout.js b/scripts/checks/extract-layout.js index 1a38af19677..d83a8d634bb 100644 --- a/scripts/checks/extract-layout.js +++ b/scripts/checks/extract-layout.js @@ -1,16 +1,21 @@ -const fs = require('fs'); -const { findAll, astDereferencer, srcDecoder } = require('solidity-ast/utils'); -const { extractStorageLayout } = require('@openzeppelin/upgrades-core/dist/storage/extract'); +import fs from 'fs'; +import { findAll, astDereferencer, srcDecoder } from 'solidity-ast/utils.js'; +import { extractStorageLayout } from '@openzeppelin/upgrades-core/dist/storage/extract.js'; -const { hideBin } = require('yargs/helpers'); -const { argv } = require('yargs/yargs')(hideBin(process.argv)); +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +const { _: artifacts } = yargs(hideBin(process.argv)).argv; +const inputArtifacts = artifacts.filter(p => !p.endsWith('.output.json')); const skipPath = ['contracts/mocks/', 'contracts-exposed/']; const skipKind = ['interface', 'library']; function extractLayouts(path) { const layout = {}; - const { input, output } = JSON.parse(fs.readFileSync(path)); + const { input } = JSON.parse(fs.readFileSync(path)); + const outputPath = path.replace(/\.json$/, '.output.json'); + const { output } = JSON.parse(fs.readFileSync(outputPath)); const decoder = srcDecoder(input, output); const deref = astDereferencer(output); @@ -36,4 +41,4 @@ function extractLayouts(path) { return layout; } -console.log(JSON.stringify(Object.assign(...argv._.map(extractLayouts)))); +console.log(JSON.stringify(Object.assign({}, ...inputArtifacts.map(extractLayouts)))); From 0b1191ed783f580d0fe6904e898f0c558c55e655 Mon Sep 17 00:00:00 2001 From: John Kane Date: Thu, 23 Apr 2026 16:28:17 +0100 Subject: [PATCH 66/91] fix: deal with deployment null in gas comparison script If the json structure produced by `--gas-stats-json` does not have a deployment entry (`deployment: null`) then skip the recording of the constructor. Now both sides, for deployments, must exist for a comparison to take place. --- scripts/checks/compareGasReports.js | 38 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/scripts/checks/compareGasReports.js b/scripts/checks/compareGasReports.js index f1e4bb78be5..5f384b23c2e 100755 --- a/scripts/checks/compareGasReports.js +++ b/scripts/checks/compareGasReports.js @@ -59,21 +59,29 @@ class Report { static compare(update, ref, opts = { hideEqual: true, strictTesting: false }) { return Object.entries(update.contracts) .filter(([key]) => key in ref.contracts) - .flatMap(([key, contract]) => [ - { - contract: contract.contractName, - method: '[constructor]', - ...variations(contract.deployment, ref.contracts[key].deployment, BASE_TX_COST), - }, - ...Object.entries(contract.functions ?? {}) - .filter(([method]) => method in ref.contracts[key].functions) - .filter(([method, data]) => !opts.strictTesting || data.count === ref.contracts[key].functions[method].count) - .map(([method, currentData]) => ({ - contract: contract.contractName, - method, - ...variations(currentData, ref.contracts[key].functions[method], BASE_TX_COST), - })), - ]) + .flatMap(([key, contract]) => { + const refContract = ref.contracts[key]; + const refFunctions = refContract.functions ?? {}; + return [ + ...(contract.deployment && refContract.deployment + ? [ + { + contract: contract.contractName, + method: '[constructor]', + ...variations(contract.deployment, refContract.deployment, BASE_TX_COST), + }, + ] + : []), + ...Object.entries(contract.functions ?? {}) + .filter(([method]) => method in refFunctions) + .filter(([method, data]) => !opts.strictTesting || data.count === refFunctions[method].count) + .map(([method, currentData]) => ({ + contract: contract.contractName, + method, + ...variations(currentData, refFunctions[method], BASE_TX_COST), + })), + ]; + }) .sort((a, b) => `${a.contract}:${a.method}`.localeCompare(`${b.contract}:${b.method}`)) .filter(row => !opts.hideEqual || row.min?.delta || row.max?.delta || row.avg?.delta || row.median?.delta); } From 3cc085ffb0e4a1e7aaf079ac96df19050cc876e1 Mon Sep 17 00:00:00 2001 From: John Kane Date: Mon, 27 Apr 2026 14:53:50 +0100 Subject: [PATCH 67/91] chore: don't compare on previous json files if none present The comparison step with previous runs was failing if the file was not downloadable. As the CI has never been run with the new report names this will inevitably fail leading to a chicken and egg problem. This change switches it from the previous step successfully running to a comparison on file successfully downloaded for the base branch. If there is no report from the base branch the CI should pass, generating a report that can be compared against in the future. --- .github/actions/gas-compare/action.yml | 2 +- .github/actions/storage-layout/action.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/gas-compare/action.yml b/.github/actions/gas-compare/action.yml index 94931d16f20..3ba07ca80cf 100644 --- a/.github/actions/gas-compare/action.yml +++ b/.github/actions/gas-compare/action.yml @@ -31,7 +31,7 @@ runs: continue-on-error: true id: reference - name: Compare reports - if: steps.reference.outcome == 'success' && github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && hashFiles(inputs.ref_report) != '' run: | node scripts/checks/compareGasReports.js ${{ inputs.report }} ${{ inputs.ref_report }} >> $GITHUB_STEP_SUMMARY env: diff --git a/.github/actions/storage-layout/action.yml b/.github/actions/storage-layout/action.yml index 69a649428bc..88b127d7ff7 100644 --- a/.github/actions/storage-layout/action.yml +++ b/.github/actions/storage-layout/action.yml @@ -39,7 +39,7 @@ runs: continue-on-error: true id: reference - name: Compare layouts - if: steps.reference.outcome == 'success' && github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && hashFiles(inputs.ref_layout) != '' run: | node scripts/checks/compare-layout.js --head ${{ inputs.layout }} --ref ${{ inputs.ref_layout }} shell: bash From 7e6ea2eb0ec9ae5a9b4da269ced4ff19ba4fedff Mon Sep 17 00:00:00 2001 From: John Kane Date: Fri, 24 Apr 2026 17:21:22 +0100 Subject: [PATCH 68/91] fix(transpile): scope transpiler input to the main contracts tree The peer-project copy under lib/openzeppelin-contracts/contracts/ is the non-upgradeable reference that @openzeppelin/contracts/ remaps to; it is consumed by the transpiled output's peer imports, not processed by the transpiler. Hardhat 3 compiles both trees into one build-info, which caused the transpiler to traverse the peer copy and trip on contracts it was never meant to transform. We now filter input.sources (and the delete-phase `seen` set so peer files aren't deleted) to sources under the configured main source root only, matching Hardhat 2's behaviour. --- hardhat/hardhat-transpiler/tasks/transpile.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index be6de8966e1..c82e4471b85 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -56,11 +56,20 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat s.ast.absolutePath = s.ast.absolutePath.replace(/^project\//, ''); }); + const mainSources = hre.config.paths.sources.solidity.at(0)!; + const mainSourcesRel = path.relative(hre.config.paths.root, mainSources); + + // Peer-project sources are compiled so downstream imports resolve, but they must not + // enter the transpiler's transform set — the transpiler expects only main-project ASTs. + input.sources = Object.fromEntries( + Object.entries(input.sources).filter(([k]) => k.startsWith(mainSourcesRel + '/')), + ); + // Run transpilation on the first source folder const transpiled = await transpile( input, output, - { root: hre.config.paths.root, sources: hre.config.paths.sources.solidity.at(0)! }, + { root: hre.config.paths.root, sources: mainSources }, { ...options, solcVersion }, ); @@ -88,6 +97,7 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat } Object.keys(output.sources) + .filter(s => s.startsWith(mainSourcesRel + '/')) .map(s => path.join(hre.config.paths.root, s.replace(/^project\//, ''))) .forEach(p => seen.add(p)); } From bb179c1688bfa9aa7c2ff06669f07f1bea4dba3d Mon Sep 17 00:00:00 2001 From: John Kane Date: Fri, 24 Apr 2026 20:57:55 +0100 Subject: [PATCH 69/91] fix(transpile): delete peer copies of already-initializable files Already-initializable files are kept as-is in the main tree by the transpiler. Under Hardhat 3's two-entry paths.sources (i.e. [contracts, lib/openzeppelin-contracts/contracts]) their peer-tree copies still compile, producing duplicate artifacts and tripping HHE1001 on bare-name deploys. Instead we extend the existing delete phase to also unlink the peer mirror of each already-initializable file. --- hardhat/hardhat-transpiler/tasks/transpile.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index c82e4471b85..d92b5679ae0 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -84,10 +84,12 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat // Delete originals files if (options.deleteOriginals) { + const alreadyInitializable = findAlreadyInitializable(output, options.initializablePath); + ([] as string[]) .concat( transpiled.map(t => t.path), - findAlreadyInitializable(output, options.initializablePath), + alreadyInitializable, ) .map(p => path.join(hre.config.paths.root, p.replace(/^project\//, ''))) .forEach(p => keep.add(p)); @@ -100,6 +102,22 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat .filter(s => s.startsWith(mainSourcesRel + '/')) .map(s => path.join(hre.config.paths.root, s.replace(/^project\//, ''))) .forEach(p => seen.add(p)); + + // Already-initializable files are kept as-is in the main tree (not renamed, not + // transpiled). Their peer-tree copies, compiled alongside main, would produce + // duplicate artifacts for every contract inside (triggering HHE1001 at deploy + // time). Add the peer path for each already-initializable file to the delete set + // so the main tree's copy is the sole source of truth. + if (options.peerProject !== undefined) { + const peerSources = hre.config.paths.sources.solidity[1]; + if (peerSources !== undefined) { + const peerSourcesRel = path.relative(hre.config.paths.root, peerSources); + alreadyInitializable + .filter(p => p.startsWith(mainSourcesRel + '/')) + .map(p => path.join(hre.config.paths.root, peerSourcesRel, path.relative(mainSourcesRel, p))) + .forEach(p => seen.add(p)); + } + } } } From 95ed1e2469d9dedec89fed33911a0f8e63aec1b5 Mon Sep 17 00:00:00 2001 From: John Kane Date: Mon, 27 Apr 2026 11:22:07 +0100 Subject: [PATCH 70/91] chore: exclude mocks/tests/entire peer dep from linearization check The linearization check passes when run from tests, but with the additions from transpilation it fails as Hardhat 3 compiles the peer dep tree (lib/openzeppelin-contracts) alongside the main contracts. This leads to duplication in the linearization graph and conflicts. It also pulls in `.t.sol` files into build-info during `npx hardhat test`. So we intentionally exclude the peer dependencies (including mocks) and tests from the linearization check. --- scripts/checks/inheritance-ordering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checks/inheritance-ordering.js b/scripts/checks/inheritance-ordering.js index 513a80f74e1..1fa9a380f8d 100755 --- a/scripts/checks/inheritance-ordering.js +++ b/scripts/checks/inheritance-ordering.js @@ -11,7 +11,7 @@ import { hideBin } from 'yargs/helpers'; const { _: artifacts } = yargs(hideBin(process.argv)).argv; // files to skip -const skipPatterns = ['contracts-exposed/**', 'contracts/mocks/**']; +const skipPatterns = ['contracts-exposed/**', 'contracts/mocks/**', 'lib/openzeppelin-contracts/**', 'test/**']; for (const artifact of artifacts) { const { output: solcOutput } = JSON.parse( From 1a9aa138ef340d5d995bfcd03f5c299872dfb2b8 Mon Sep 17 00:00:00 2001 From: John Kane Date: Fri, 24 Apr 2026 18:40:25 +0100 Subject: [PATCH 71/91] =?UTF-8?q?fix(tests):=20use=20@openzeppelin/contrac?= =?UTF-8?q?ts/=E2=80=A6=20imports=20in=20.t.sol=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple Foundry tests still referenced `contracts/` via relative paths (`../../../contracts/…`). Hardhat 3 compiles .t.sol tests as part of `hardhat test`, and on the upgradeable variant those relative paths resolve to files the transpile step deletes (stateless libraries like `Math.sol`, re-exported from the peer package instead). Swap them to the `@openzeppelin/contracts/…` form every other .t.sol already uses: on the main repo it remaps to `contracts/` (no change), on the upgradeable variant it remaps to `lib/openzeppelin-contracts/contracts/` where the peer copy is preserved. --- ...GovernorSuperQuorumGreaterThanQuorum.t.sol | 20 +++++++++---------- test/utils/Blockhash.t.sol | 2 +- test/utils/draft-InteroperableAddress.t.sol | 2 +- test/utils/math/SignedMath.t.sol | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/test/governance/extensions/GovernorSuperQuorumGreaterThanQuorum.t.sol b/test/governance/extensions/GovernorSuperQuorumGreaterThanQuorum.t.sol index eb0409c8e3a..964c7724865 100644 --- a/test/governance/extensions/GovernorSuperQuorumGreaterThanQuorum.t.sol +++ b/test/governance/extensions/GovernorSuperQuorumGreaterThanQuorum.t.sol @@ -3,16 +3,16 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {GovernorVotesSuperQuorumFractionMock} from "../../../contracts/mocks/governance/GovernorVotesSuperQuorumFractionMock.sol"; -import {GovernorVotesQuorumFraction} from "../../../contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; -import {GovernorVotesSuperQuorumFraction} from "../../../contracts/governance/extensions/GovernorVotesSuperQuorumFraction.sol"; -import {GovernorSettings} from "../../../contracts/governance/extensions/GovernorSettings.sol"; -import {GovernorVotes} from "../../../contracts/governance/extensions/GovernorVotes.sol"; -import {Governor} from "../../../contracts/governance/Governor.sol"; -import {IVotes} from "../../../contracts/governance/utils/IVotes.sol"; -import {ERC20VotesExtendedTimestampMock} from "../../../contracts/mocks/token/ERC20VotesAdditionalCheckpointsMock.sol"; -import {EIP712} from "../../../contracts/utils/cryptography/EIP712.sol"; -import {ERC20} from "../../../contracts/token/ERC20/ERC20.sol"; +import {GovernorVotesSuperQuorumFractionMock} from "@openzeppelin/contracts/mocks/governance/GovernorVotesSuperQuorumFractionMock.sol"; +import {GovernorVotesQuorumFraction} from "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; +import {GovernorVotesSuperQuorumFraction} from "@openzeppelin/contracts/governance/extensions/GovernorVotesSuperQuorumFraction.sol"; +import {GovernorSettings} from "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; +import {GovernorVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; +import {Governor} from "@openzeppelin/contracts/governance/Governor.sol"; +import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol"; +import {ERC20VotesExtendedTimestampMock} from "@openzeppelin/contracts/mocks/token/ERC20VotesAdditionalCheckpointsMock.sol"; +import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract TokenMock is ERC20VotesExtendedTimestampMock { constructor() ERC20("Mock Token", "MTK") EIP712("Mock Token", "1") {} diff --git a/test/utils/Blockhash.t.sol b/test/utils/Blockhash.t.sol index e585ccba8b1..bd36aa6e47c 100644 --- a/test/utils/Blockhash.t.sol +++ b/test/utils/Blockhash.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.24; import {Test} from "forge-std/Test.sol"; -import {Blockhash} from "../../contracts/utils/Blockhash.sol"; +import {Blockhash} from "@openzeppelin/contracts/utils/Blockhash.sol"; contract BlockhashTest is Test { uint256 internal startingBlock; diff --git a/test/utils/draft-InteroperableAddress.t.sol b/test/utils/draft-InteroperableAddress.t.sol index 8fdf400d14b..98de4b745f9 100644 --- a/test/utils/draft-InteroperableAddress.t.sol +++ b/test/utils/draft-InteroperableAddress.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.26; import {Test} from "forge-std/Test.sol"; -import {InteroperableAddress} from "../../contracts/utils/draft-InteroperableAddress.sol"; +import {InteroperableAddress} from "@openzeppelin/contracts/utils/draft-InteroperableAddress.sol"; contract InteroperableAddressTest is Test { using InteroperableAddress for bytes; diff --git a/test/utils/math/SignedMath.t.sol b/test/utils/math/SignedMath.t.sol index aa567d61a6f..2af3d0fd4aa 100644 --- a/test/utils/math/SignedMath.t.sol +++ b/test/utils/math/SignedMath.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {Math} from "../../../contracts/utils/math/Math.sol"; -import {SignedMath} from "../../../contracts/utils/math/SignedMath.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol"; contract SignedMathTest is Test { function testSymbolicTernary(bool f, int256 a, int256 b) public pure { From d6afa0e8f875715c1f7a87f7aa688872e982f131 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 May 2026 15:01:59 +0200 Subject: [PATCH 72/91] test migration for Create3 --- test/utils/Create3.test.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/utils/Create3.test.js b/test/utils/Create3.test.js index ea624251032..0f4d0df1025 100644 --- a/test/utils/Create3.test.js +++ b/test/utils/Create3.test.js @@ -1,9 +1,12 @@ -const { ethers } = require('hardhat'); -const { expect } = require('chai'); -const { loadFixture, takeSnapshot } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); - -const { RevertType } = require('../helpers/enums'); +import { network } from 'hardhat'; +import { expect } from 'chai'; +import { PANIC_CODES } from '@nomicfoundation/hardhat-ethers-chai-matchers/panic'; +import { RevertType } from '../helpers/enums'; + +const { + ethers, + networkHelpers: { loadFixture, takeSnapshot }, +} = await network.create(); const PROXY_INITCODE_HASH = '0xd61bbde0460e6c48ddd99fb8b7e1ad36529d2ec79cbac1db0300b3d26ddcdc2a'; const getCreate3Address = (deployer, salt) => From c35db4848bad064d1ffa0c487b06f92670a97715 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 May 2026 15:05:30 +0200 Subject: [PATCH 73/91] Fix transpilation script. Missing cleanup --- scripts/upgradeable/transpile.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/upgradeable/transpile.sh b/scripts/upgradeable/transpile.sh index fa2f2c82de4..cb1799bcecc 100644 --- a/scripts/upgradeable/transpile.sh +++ b/scripts/upgradeable/transpile.sh @@ -12,6 +12,7 @@ sed -i'' -e "s//$VERSION/g" "contracts/package.json" git add contracts/package.json # run the transpiler +npm run clean npx hardhat transpile --settings $DIRNAME/transpile.config.json # create alias to Initializable and UUPSUpgradeable From 839bceb7d3c787266266e6dc5a4fda64ca7233a7 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 May 2026 16:27:49 +0200 Subject: [PATCH 74/91] Update action.yml --- .github/actions/gas-compare/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/actions/gas-compare/action.yml b/.github/actions/gas-compare/action.yml index 3ba07ca80cf..ed77d26921e 100644 --- a/.github/actions/gas-compare/action.yml +++ b/.github/actions/gas-compare/action.yml @@ -29,7 +29,6 @@ runs: GITHUB_TOKEN: ${{ inputs.token }} shell: bash continue-on-error: true - id: reference - name: Compare reports if: github.event_name == 'pull_request' && hashFiles(inputs.ref_report) != '' run: | From 967deecb365eb678106edeb50e9c7fbd0d443eaf Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 May 2026 16:28:21 +0200 Subject: [PATCH 75/91] Update action.yml --- .github/actions/storage-layout/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/actions/storage-layout/action.yml b/.github/actions/storage-layout/action.yml index 88b127d7ff7..ea7e201993f 100644 --- a/.github/actions/storage-layout/action.yml +++ b/.github/actions/storage-layout/action.yml @@ -37,7 +37,6 @@ runs: GITHUB_TOKEN: ${{ inputs.token }} shell: bash continue-on-error: true - id: reference - name: Compare layouts if: github.event_name == 'pull_request' && hashFiles(inputs.ref_layout) != '' run: | From d75faac1d563b948a919c2c954c59e195639adf4 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 May 2026 17:16:32 +0200 Subject: [PATCH 76/91] skip exposed contracts from coverage --- .codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.codecov.yml b/.codecov.yml index 4cec4ef7d5f..bca18179aa4 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -14,3 +14,4 @@ ignore: - "test" - "contracts/mocks" - "contracts/vendor" + - "contracts-exposed" From a53631b8f47e255b7aa51584773684d002031faa Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 May 2026 18:14:59 +0200 Subject: [PATCH 77/91] script naming consistency --- .github/actions/gas-compare/action.yml | 2 +- scripts/checks/{compareGasReports.js => compare-gas-reports.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/checks/{compareGasReports.js => compare-gas-reports.js} (100%) diff --git a/.github/actions/gas-compare/action.yml b/.github/actions/gas-compare/action.yml index ed77d26921e..95eab646436 100644 --- a/.github/actions/gas-compare/action.yml +++ b/.github/actions/gas-compare/action.yml @@ -32,7 +32,7 @@ runs: - name: Compare reports if: github.event_name == 'pull_request' && hashFiles(inputs.ref_report) != '' run: | - node scripts/checks/compareGasReports.js ${{ inputs.report }} ${{ inputs.ref_report }} >> $GITHUB_STEP_SUMMARY + node scripts/checks/compare-gas-reports.js ${{ inputs.report }} ${{ inputs.ref_report }} >> $GITHUB_STEP_SUMMARY env: STYLE: markdown shell: bash diff --git a/scripts/checks/compareGasReports.js b/scripts/checks/compare-gas-reports.js similarity index 100% rename from scripts/checks/compareGasReports.js rename to scripts/checks/compare-gas-reports.js From a96ae511910e2f797a6d1e63bf357eb6cf0270c0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 May 2026 12:36:27 +0200 Subject: [PATCH 78/91] refactor transpile.ts --- hardhat/hardhat-transpiler/tasks/transpile.ts | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/hardhat/hardhat-transpiler/tasks/transpile.ts b/hardhat/hardhat-transpiler/tasks/transpile.ts index d92b5679ae0..065c1f0de5c 100644 --- a/hardhat/hardhat-transpiler/tasks/transpile.ts +++ b/hardhat/hardhat-transpiler/tasks/transpile.ts @@ -17,10 +17,16 @@ interface TranspileOptions { peerProject?: string; } +// Map function, key the keys intact and transform values function transformKeys(obj: Record, fn: (key: string) => string): Record { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [fn(k), v])); } +// Filter function, only keep entries where fn returns true +function filterRecord(obj: Record, fn: (key: string, value: U) => boolean): Record { + return Object.fromEntries(Object.entries(obj).filter(([k, v]) => fn(k, v))); +} + export default async function ({ settings }: { settings?: string }, hre: HardhatRuntimeEnvironment) { assert(settings, 'Transpile settings file must be provided'); const options: TranspileOptions = await fs.readFile(settings, 'utf-8').then(JSON.parse); @@ -48,22 +54,25 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat .then(file => fs.readFile(file!, 'utf-8')) .then(JSON.parse); - // Adjust paths to match transpiler expectations - input.sources = transformKeys(input.sources, k => k.replace(/^project\//, '')); - output.sources = transformKeys(output.sources, k => k.replace(/^project\//, '')); - output.contracts = transformKeys(output.contracts, k => k.replace(/^project\//, '')); - Object.values(output.sources).forEach((s: any) => { - s.ast.absolutePath = s.ast.absolutePath.replace(/^project\//, ''); - }); - const mainSources = hre.config.paths.sources.solidity.at(0)!; const mainSourcesRel = path.relative(hre.config.paths.root, mainSources); - // Peer-project sources are compiled so downstream imports resolve, but they must not - // enter the transpiler's transform set — the transpiler expects only main-project ASTs. - input.sources = Object.fromEntries( - Object.entries(input.sources).filter(([k]) => k.startsWith(mainSourcesRel + '/')), + // Adjust paths to match transpiler expectations + input.sources = filterRecord( + transformKeys(input.sources, k => k.replace(/^project\//, '')), + k => k.startsWith(mainSourcesRel + '/'), + ); + output.sources = filterRecord( + transformKeys(output.sources, k => k.replace(/^project\//, '')), + k => k.startsWith(mainSourcesRel + '/'), + ); + output.contracts = filterRecord( + transformKeys(output.contracts, k => k.replace(/^project\//, '')), + k => k.startsWith(mainSourcesRel + '/'), ); + Object.values(output.sources).forEach((s: any) => { + s.ast.absolutePath = s.ast.absolutePath.replace(/^project\//, ''); + }); // Run transpilation on the first source folder const transpiled = await transpile( @@ -86,6 +95,7 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat if (options.deleteOriginals) { const alreadyInitializable = findAlreadyInitializable(output, options.initializablePath); + // keep all newly transpiled files, plus any already-initializable files (even if not transpiled, to avoid deleting them) ([] as string[]) .concat( transpiled.map(t => t.path), @@ -94,29 +104,27 @@ export default async function ({ settings }: { settings?: string }, hre: Hardhat .map(p => path.join(hre.config.paths.root, p.replace(/^project\//, ''))) .forEach(p => keep.add(p)); + // If we have an initializablePath and no peer project, we need to preserve the initializablePath. if (options.initializablePath && options.peerProject === undefined) { keep.add(path.join(hre.config.paths.root, options.initializablePath.replace(/^project\//, ''))); } Object.keys(output.sources) - .filter(s => s.startsWith(mainSourcesRel + '/')) .map(s => path.join(hre.config.paths.root, s.replace(/^project\//, ''))) .forEach(p => seen.add(p)); - // Already-initializable files are kept as-is in the main tree (not renamed, not - // transpiled). Their peer-tree copies, compiled alongside main, would produce - // duplicate artifacts for every contract inside (triggering HHE1001 at deploy - // time). Add the peer path for each already-initializable file to the delete set - // so the main tree's copy is the sole source of truth. - if (options.peerProject !== undefined) { - const peerSources = hre.config.paths.sources.solidity[1]; - if (peerSources !== undefined) { - const peerSourcesRel = path.relative(hre.config.paths.root, peerSources); - alreadyInitializable - .filter(p => p.startsWith(mainSourcesRel + '/')) - .map(p => path.join(hre.config.paths.root, peerSourcesRel, path.relative(mainSourcesRel, p))) - .forEach(p => seen.add(p)); - } + // If we have a peer project, initializable file present in the main source must be removed from the peer source + // to avoid conflicts. Since we don't know which source location may contain the peer project, we check all the + // solidity source file. Note that if the file does not exist, it will simply be ignored when we try to delete it. + if (options.peerProject) { + alreadyInitializable + .filter(f => f.startsWith(mainSourcesRel + '/')) + .flatMap(f => + hre.config.paths.sources.solidity + .slice(1) + .map(peerSources => path.join(peerSources, path.relative(mainSourcesRel, f))), + ) + .forEach(p => seen.add(p)); } } } From df1e340fc7fc37a0cb9bc0142758a78bcdd9d1e9 Mon Sep 17 00:00:00 2001 From: John Kane Date: Mon, 4 May 2026 12:27:49 +0100 Subject: [PATCH 79/91] test: bump gas usage to allow coverage run The account behavior test has a set gas because estimate gas fails in this scenario due to the inner call. Instead we set the gas explicitly so no estimate is needed. However, the value was too low to run under `--coverage` as this adds additional gas consuming instrumentation. Similarly the timelock controller test sets the gas estimate explicitly, but will fail as the value is to low when running under `--coverage`. --- test/account/Account.behavior.js | 2 +- test/governance/TimelockController.test.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/account/Account.behavior.js b/test/account/Account.behavior.js index a031009229e..317febdcc24 100644 --- a/test/account/Account.behavior.js +++ b/test/account/Account.behavior.js @@ -60,7 +60,7 @@ export function shouldBehaveLikeAccountCore() { // Forcing the gas limit here to work around a hardhat 3 issue with gas estimation await expect( - this.mockFromEntrypoint.validateUserOp(operation.packed, operation.hash(), value, { gasLimit: 200_000n }), + this.mockFromEntrypoint.validateUserOp(operation.packed, operation.hash(), value, { gasLimit: 2_000_000n }), ).to.changeEtherBalances(this.ethers, [this.mock, this.ethers.predeploy.entrypoint.v09], [-value, value]); }); }); diff --git a/test/governance/TimelockController.test.js b/test/governance/TimelockController.test.js index e620c9c175b..44738e309d6 100644 --- a/test/governance/TimelockController.test.js +++ b/test/governance/TimelockController.test.js @@ -1143,11 +1143,14 @@ describe('TimelockController', function () { await this.mock.getTimestamp(operation.id).then(time.increaseTo.timestamp); + // Outer gas budget must leave enough for the FailedCall revert site after the inner OOGs. + // Instrumented bytecode under `--coverage` adds per-statement probes, so 100k is too tight; + // 500k gives headroom while still well below the EIP-7825 (Osaka) per-tx cap of 2^24. await expect( this.mock .connect(this.executor) .execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, { - gasLimit: '100000', + gasLimit: '500000', }), ).to.be.revertedWithCustomError(this.mock, 'FailedCall'); }); From e945300cafdc5d7d285d77741b7567212f22aa9c Mon Sep 17 00:00:00 2001 From: John Kane Date: Wed, 20 May 2026 19:02:27 +0100 Subject: [PATCH 80/91] chore: bump Hardhat version to 3.5 --- package-lock.json | 116 ++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 56 insertions(+), 62 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1397f18067..1e1f130ffc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.4.4", + "hardhat": "^3.5.0", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", @@ -1602,28 +1602,28 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.31.tgz", - "integrity": "sha512-4I2R1qFsCLiKelOhqSahkz0+MnazdV+33iN0NEeen6rHaQoTYSLCpsMyb9Uj4MfunYa5QLSz2WrtO2f9E7Fegg==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.33.tgz", + "integrity": "sha512-+jwZcGgUKVUykqP+hbVEXClpqZbIdo/MztP/6Xp8DDmB8c+WxJvwCDmPrnfkV+s3NNj2lSDCyOZoPw9/UNcmXw==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.31", - "@nomicfoundation/edr-darwin-x64": "0.12.0-next.31", - "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.31", - "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.31", - "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.31", - "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.31", - "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.31" + "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.33", + "@nomicfoundation/edr-darwin-x64": "0.12.0-next.33", + "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.33", + "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.33", + "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.33", + "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.33", + "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.33" }, "engines": { "node": ">= 20" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.31.tgz", - "integrity": "sha512-z2gEimnvx6SinJsHsat89USFt+0sclSY4PkeI/FBFlAshiEHYKjFpN5svdC3ghOrgIFctGt7lSzfrbiP+KhVZg==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.33.tgz", + "integrity": "sha512-CEaDltNmTRLyJPMwSVHNtYuXthJ3vm44SkRAQLiDYuhLbbzxAWmkCdnvKyo5QPmaRKxBGFwtahCJz2biBM43Ig==", "dev": true, "license": "MIT", "engines": { @@ -1631,9 +1631,9 @@ } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.31.tgz", - "integrity": "sha512-WbiIuARMO59XY8ZFs8ZFHildyMf5tnWIOt9S1wttcoYkiXwYwI2tC9JIrZ3rziv4DttWIV5aPxp23G7W0A1t4g==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.33.tgz", + "integrity": "sha512-3V5XgzKLXFO+Cw0sWoO3ug2hHYL3VThskA1Iu98EPO44P4w0S6tW3bdsIH2OJPD+zi5RIHjg8H6HDZ+xeipXAA==", "dev": true, "license": "MIT", "engines": { @@ -1641,9 +1641,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.31.tgz", - "integrity": "sha512-Q9p/wk6DEjGeLqp/RXiCQ8vR5irZpF6emXKElkt6jxjrOYd6VnFPc6I8v8M1Lc620aT6pn2mRwKDuKcktuqcFA==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.33.tgz", + "integrity": "sha512-5+1wkqlUEem9yhKX2AwSINlaiNk6NYfm4WbvaeEAmDZivLEDhW56BNe5+fuue1B4Fa50OONaimw9wEe3d1xYNA==", "dev": true, "license": "MIT", "engines": { @@ -1651,9 +1651,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.31.tgz", - "integrity": "sha512-/5TYcR+NpkfxYKQbhLFN5Vj36GUqP6NqCkYqfdKuv2+0r3y4hadeU3p3WhCKr5YGrnSJwrxBnGBZwyfeHJHP+g==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.33.tgz", + "integrity": "sha512-/Zr+9HxJfOSjtSTr3PkErSrGkP40j5op2koY1vVwsC1CqRiNnxKfXpwa6WH8zEUzIkEAGLq5ZwjD251irrf1uA==", "dev": true, "license": "MIT", "engines": { @@ -1661,9 +1661,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.31.tgz", - "integrity": "sha512-T1EqWrja6oWglHSi0TtmITaic4DaAT+7u3yoOrjMh2oDQU+tM6tHNENWyZ244Neru23bg0i9ZnO0o8nI9uL99g==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.33.tgz", + "integrity": "sha512-KrK/eUlVJo6FdHOBx9kXnzq8g+j8A2218ZVoVI/TJYgZFRXkhUI0tBeDmmdmfcRtk8J4Hw5QGRJVIXA2TAWUZQ==", "dev": true, "license": "MIT", "engines": { @@ -1671,9 +1671,9 @@ } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.31.tgz", - "integrity": "sha512-gvQV4DVmnYobOuqsxuPjPuzdmpxR8yEbV75JOw5luVU6te6SBrPcmk8NNn12ZkAlTbyuDc81c7aRd4A1RfEmuw==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.33.tgz", + "integrity": "sha512-o6Kmj4YKdRNZ6U4wn2hd0wyh3r2Pu0/RjBBiqs+zeeRopI/san7H4n6kIxe+8PbqlAcC48MI+oWLSNwnkqONwg==", "dev": true, "license": "MIT", "engines": { @@ -1681,9 +1681,9 @@ } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.12.0-next.31", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.31.tgz", - "integrity": "sha512-xUDeakZGAZF010k0mhAm387gny0gdKq2pHv+7MxqVvuQumW0PNKQfBNfl7+bNgt9Bc4o1/FBP89hUR5tHXU0Cg==", + "version": "0.12.0-next.33", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.33.tgz", + "integrity": "sha512-PPiOTOIShzhK7+i+QMDz6/0m2JcEHBMqCthLj8dru2RFhXT1Jory35u12awaVS2fU91flBg7zz7Qju4v8hrnLw==", "dev": true, "license": "MIT", "engines": { @@ -1691,13 +1691,13 @@ } }, "node_modules/@nomicfoundation/hardhat-errors": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.12.tgz", - "integrity": "sha512-2viEq1D19FHWKpfB2vVeL0R6d+iZR2E5h0EhKQQMc1ukDUV2fel/7fRjlWuCOx2CFC+5mHL2saRcN8KlYsX8hg==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-errors/-/hardhat-errors-3.0.13.tgz", + "integrity": "sha512-h0nWNzKbmP6XhINMgSUfGixevzksT8BQPK08KNnsr/8kQORAeYzsv6bf0CLUD8mZfmBEP45m4QMlNP6xcoc0Ow==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-utils": "^4.1.0" + "@nomicfoundation/hardhat-utils": "^4.1.2" } }, "node_modules/@nomicfoundation/hardhat-ethers": { @@ -1905,9 +1905,9 @@ } }, "node_modules/@nomicfoundation/hardhat-utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.1.0.tgz", - "integrity": "sha512-qze9X5P8LIB36rjS3cFhD7asG82pjZDmJAYfCQBSDhMYJM1HDZrYKgKfJLEE36TmrhjgQ/hlNO0DikDq0e2VYg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-utils/-/hardhat-utils-4.1.2.tgz", + "integrity": "sha512-jUQfbyIoTLHmSua+BMhIqUIk7uDwL2bhTtJgfwRoVooM3TTvm5rIdRK+eNkvZcZR/wAL/SBQOq/r9yOekj4Unw==", "dev": true, "license": "MIT", "dependencies": { @@ -2009,21 +2009,21 @@ } }, "node_modules/@nomicfoundation/hardhat-vendored": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.3.tgz", - "integrity": "sha512-VzxwR1Yz8zAztiSIkjFai/XyqfuMMvX95ppXxWlJ1ci0TiHu6sut1oOAD+VJVCq+LHNpr2fWMUcugZq9uKbicg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-vendored/-/hardhat-vendored-3.0.4.tgz", + "integrity": "sha512-RO8Otj1FvRvxJmXzkxh1vTwK/+cqSVPYLqY6RrWkmzHEEcxnAwAFsBYdW7xyTEyW/pVbSSNd2gs3aoGdGZaoNA==", "dev": true, "license": "MIT" }, "node_modules/@nomicfoundation/hardhat-zod-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.4.tgz", - "integrity": "sha512-yCiycXDEEjbNgNVQaUoGYOee6+ljYUnIOWMtYc/dYDuwlHutWr9xg/KgkgMkiZZ1R2WrZAEqsSaeZTnH7Oyz9Q==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-zod-utils/-/hardhat-zod-utils-3.0.5.tgz", + "integrity": "sha512-A1G9Jcizf/vYcGMtqkf+st94zBPTDB+bXXlojOMu77gmBZYbywY0k7hdRM2B4uJY+8nM0oe0sNVGVkARITXdcw==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/hardhat-errors": "^3.0.11", - "@nomicfoundation/hardhat-utils": "^4.0.3" + "@nomicfoundation/hardhat-errors": "^3.0.13", + "@nomicfoundation/hardhat-utils": "^4.1.2" }, "peerDependencies": { "zod": "^3.23.8" @@ -4458,17 +4458,17 @@ } }, "node_modules/hardhat": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.4.4.tgz", - "integrity": "sha512-m7EbNA++jZu3h8KuLxHY/vCMTbWfnlXPHO+iKO3v8XoHOBiplVIy5V4MsblcursS694r1fINaFC26a6xd7kY8Q==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-3.5.0.tgz", + "integrity": "sha512-JT5YMXluyCD6RePp1ySezgZD9zZelS0v/wtarkQHb44pGqWqvVz1E68aEKSsbdMDRjHSHvW7/ILNJ3N1wt/MqQ==", "dev": true, "license": "MIT", "dependencies": { - "@nomicfoundation/edr": "0.12.0-next.31", - "@nomicfoundation/hardhat-errors": "^3.0.12", - "@nomicfoundation/hardhat-utils": "^4.1.0", - "@nomicfoundation/hardhat-vendored": "^3.0.3", - "@nomicfoundation/hardhat-zod-utils": "^3.0.4", + "@nomicfoundation/edr": "0.12.0-next.33", + "@nomicfoundation/hardhat-errors": "^3.0.13", + "@nomicfoundation/hardhat-utils": "^4.1.2", + "@nomicfoundation/hardhat-vendored": "^3.0.4", + "@nomicfoundation/hardhat-zod-utils": "^3.0.5", "@nomicfoundation/solidity-analyzer": "^0.1.1", "@sentry/core": "^9.4.0", "adm-zip": "^0.4.16", @@ -7185,9 +7185,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -7205,9 +7202,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ diff --git a/package.json b/package.json index 16ec868721d..61db142c27f 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "glob": "^13.0.0", "globals": "^17.0.0", "graphlib": "^2.1.8", - "hardhat": "^3.4.4", + "hardhat": "^3.5.0", "hardhat-ignore-warnings": "^0.3.0", "hardhat-predeploy": "^1.0.1", "husky": "^9.1.7", From 0d3ae5abde7cce48da12b78da576c4fc091dec6d Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 May 2026 22:13:32 +0200 Subject: [PATCH 81/91] Apply suggestion from @Amxx --- test/governance/TimelockController.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/governance/TimelockController.test.js b/test/governance/TimelockController.test.js index 44738e309d6..6fd59d7dcb6 100644 --- a/test/governance/TimelockController.test.js +++ b/test/governance/TimelockController.test.js @@ -1150,7 +1150,7 @@ describe('TimelockController', function () { this.mock .connect(this.executor) .execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, { - gasLimit: '500000', + gasLimit: '500_000n', }), ).to.be.revertedWithCustomError(this.mock, 'FailedCall'); }); From 02db468cf7e28d75f9cc93110527ab0ea7326d5e Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 10:31:39 +0200 Subject: [PATCH 82/91] fix test --- test/governance/TimelockController.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/governance/TimelockController.test.js b/test/governance/TimelockController.test.js index 6fd59d7dcb6..7e5c1b9869f 100644 --- a/test/governance/TimelockController.test.js +++ b/test/governance/TimelockController.test.js @@ -1150,7 +1150,7 @@ describe('TimelockController', function () { this.mock .connect(this.executor) .execute(operation.target, operation.value, operation.data, operation.predecessor, operation.salt, { - gasLimit: '500_000n', + gasLimit: 500_000n, }), ).to.be.revertedWithCustomError(this.mock, 'FailedCall'); }); From 1388c35c21851eba808bb8012e6b894d82b0d483 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 11:08:40 +0200 Subject: [PATCH 83/91] Update inheritance-ordering.js --- scripts/checks/inheritance-ordering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checks/inheritance-ordering.js b/scripts/checks/inheritance-ordering.js index 1fa9a380f8d..e61b0041836 100755 --- a/scripts/checks/inheritance-ordering.js +++ b/scripts/checks/inheritance-ordering.js @@ -11,7 +11,7 @@ import { hideBin } from 'yargs/helpers'; const { _: artifacts } = yargs(hideBin(process.argv)).argv; // files to skip -const skipPatterns = ['contracts-exposed/**', 'contracts/mocks/**', 'lib/openzeppelin-contracts/**', 'test/**']; +const skipPatterns = ['contracts-exposed/**', 'contracts/mocks/**', 'lib/openzeppelin-contracts/contracts/mocks/**', 'test/**']; for (const artifact of artifacts) { const { output: solcOutput } = JSON.parse( From 0d25b23d9c404fd73c7f422cbd243a37a1336d8b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 13:46:06 +0200 Subject: [PATCH 84/91] update --- scripts/checks/inheritance-ordering.js | 7 ++++++- test/utils/CAIP.test.js | 28 +++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/scripts/checks/inheritance-ordering.js b/scripts/checks/inheritance-ordering.js index e61b0041836..5460369cd93 100755 --- a/scripts/checks/inheritance-ordering.js +++ b/scripts/checks/inheritance-ordering.js @@ -11,7 +11,12 @@ import { hideBin } from 'yargs/helpers'; const { _: artifacts } = yargs(hideBin(process.argv)).argv; // files to skip -const skipPatterns = ['contracts-exposed/**', 'contracts/mocks/**', 'lib/openzeppelin-contracts/contracts/mocks/**', 'test/**']; +const skipPatterns = [ + 'contracts-exposed/**', + 'contracts/mocks/**', + 'lib/openzeppelin-contracts/contracts/mocks/**', + 'test/**', +]; for (const artifact of artifacts) { const { output: solcOutput } = JSON.parse( diff --git a/test/utils/CAIP.test.js b/test/utils/CAIP.test.js index a6d4fc2faa7..87e66247623 100644 --- a/test/utils/CAIP.test.js +++ b/test/utils/CAIP.test.js @@ -1,32 +1,30 @@ import { network } from 'hardhat'; import { expect } from 'chai'; -import { CHAINS, getLocalChain } from '../helpers/chains'; +import { CHAINS } from '../helpers/chains'; import { generators } from '../helpers/random'; -const { ethers } = await network.create(); +const { + ethers, + helpers: { chain }, +} = await network.create(); describe('CAIP utilities', function () { - before(async function () { - this.local = await getLocalChain(ethers.provider); - }); - describe('CAIP-2', function () { before(async function () { this.mock = await ethers.deployContract('$CAIP2'); }); it('local()', async function () { - const { caip2 } = this.local; - expect(await this.mock.$local()).to.equal(caip2); + await expect(this.mock.$local()).to.eventually.equal(chain.caip2); }); for (const { namespace, reference, caip2 } of Object.values(CHAINS)) { it(`format(${namespace}, ${reference})`, async function () { - expect(await this.mock.$format(namespace, reference)).to.equal(caip2); + await expect(this.mock.$format(namespace, reference)).to.eventually.equal(caip2); }); it(`parse(${caip2})`, async function () { - expect(await this.mock.$parse(caip2)).to.deep.equal([namespace, reference]); + await expect(this.mock.$parse(caip2)).to.eventually.deep.equal([namespace, reference]); }); } }); @@ -39,19 +37,21 @@ describe('CAIP utilities', function () { }); it(`local(${account})`, async function () { - const caip10 = this.local.toCaip10(account); - expect(await this.mock.$local(ethers.Typed.address(account))).to.equal(caip10); + const caip10 = chain.toCaip10(account); + await expect(this.mock.$local(ethers.Typed.address(account))).to.eventually.equal(caip10); }); for (const { caip2, toCaip10 } of Object.values(CHAINS)) { const caip10 = toCaip10(account); it(`format(${caip2}, ${account})`, async function () { - expect(await this.mock.$format(ethers.Typed.string(caip2), ethers.Typed.string(account))).to.equal(caip10); + await expect(this.mock.$format(ethers.Typed.string(caip2), ethers.Typed.string(account))).to.eventually.equal( + caip10, + ); }); it(`parse(${caip10})`, async function () { - expect(await this.mock.$parse(caip10)).to.deep.equal([caip2, account]); + await expect(this.mock.$parse(caip10)).to.eventually.deep.equal([caip2, account]); }); } }); From da0563095331f6af743ff7b5e296c13cea2f49f4 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 13:52:05 +0200 Subject: [PATCH 85/91] revert unecessary changes to solidity tests --- test/utils/math/Math.t.sol | 41 +++++++++++--------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/test/utils/math/Math.t.sol b/test/utils/math/Math.t.sol index 8e30476578f..4284e44a64b 100644 --- a/test/utils/math/Math.t.sol +++ b/test/utils/math/Math.t.sol @@ -248,11 +248,7 @@ contract MathTest is Test { assertEq(xyLo, qdRemLo); } - // expose the Math.mulDiv externally to support expectRevert - function mulDiv(uint256 x, uint256 y, uint256 d) external view returns (uint256) { - return Math.mulDiv(x, y, d); - } - + /// forge-config: default.allow_internal_expect_revert = true function testMulDivDomain(uint256 x, uint256 y, uint256 d) public { (uint256 xyHi, ) = Math.mul512(x, y); @@ -261,25 +257,18 @@ contract MathTest is Test { // we are outside the scope of {testMulDiv}, we expect muldiv to revert vm.expectRevert(d == 0 ? stdError.divisionError : stdError.arithmeticError); - this.mulDiv(x, y, d); + Math.mulDiv(x, y, d); } // MOD EXP - - // expose the Math.modExp externally to support expectRevert - function modExp(uint256 b, uint256 e, uint256 m) external view returns (uint256) { - return Math.modExp(b, e, m); - } - + /// forge-config: default.allow_internal_expect_revert = true function testModExp(uint256 b, uint256 e, uint256 m) public { if (m == 0) { vm.expectRevert(stdError.divisionError); - uint256 result = this.modExp(b, e, m); - } else { - uint256 result = this.modExp(b, e, m); - assertLt(result, m); - assertEq(result, _nativeModExp(b, e, m)); } + uint256 result = Math.modExp(b, e, m); + assertLt(result, m); + assertEq(result, _nativeModExp(b, e, m)); } function testTryModExp(uint256 b, uint256 e, uint256 m) public view { @@ -293,22 +282,16 @@ contract MathTest is Test { } } - // expose the Math.modExp externally to support expectRevert - function modExp(bytes memory b, bytes memory e, bytes memory m) external view returns (bytes memory) { - return Math.modExp(b, e, m); - } - + /// forge-config: default.allow_internal_expect_revert = true function testModExpMemory(uint256 b, uint256 e, uint256 m) public { if (m == 0) { vm.expectRevert(stdError.divisionError); - bytes memory result = this.modExp(abi.encodePacked(b), abi.encodePacked(e), abi.encodePacked(m)); - } else { - bytes memory result = this.modExp(abi.encodePacked(b), abi.encodePacked(e), abi.encodePacked(m)); - assertEq(result.length, 0x20); - uint256 res = abi.decode(result, (uint256)); - assertLt(res, m); - assertEq(res, _nativeModExp(b, e, m)); } + bytes memory result = Math.modExp(abi.encodePacked(b), abi.encodePacked(e), abi.encodePacked(m)); + assertEq(result.length, 0x20); + uint256 res = abi.decode(result, (uint256)); + assertLt(res, m); + assertEq(res, _nativeModExp(b, e, m)); } function testTryModExpMemory(uint256 b, uint256 e, uint256 m) public view { From d08d2c5d5b40dd7f14250b3188eb49510cf75962 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 13:52:40 +0200 Subject: [PATCH 86/91] Apply suggestion from @Amxx --- test/utils/cryptography/TrieProof.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/utils/cryptography/TrieProof.test.js b/test/utils/cryptography/TrieProof.test.js index 081ac084109..5f7ed522293 100644 --- a/test/utils/cryptography/TrieProof.test.js +++ b/test/utils/cryptography/TrieProof.test.js @@ -49,9 +49,9 @@ describe('TrieProof', function () { // Multiple transactions/events in a block const txs = await batchInBlock( [ - () => this.target.mockFunction({ gasLimit: 100000 }), - () => this.target.mockFunctionWithArgs(0, 1, { gasLimit: 100000 }), - () => this.target.mockFunctionWithArgs(17, 42, { gasLimit: 100000 }), + () => this.target.mockFunction({ gasLimit: 100_000n }), + () => this.target.mockFunctionWithArgs(0, 1, { gasLimit: 100_000n }), + () => this.target.mockFunctionWithArgs(17, 42, { gasLimit: 100_000n }), ], ethers.provider, ); From cbf2c9908153b773ea5f6fba2db3c85254fca5f5 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 14:45:31 +0200 Subject: [PATCH 87/91] more updates / cleanup --- test/helpers/governance.js | 30 +++++++++++-------- .../extensions/ERC1155Crosschain.test.js | 16 ++++++---- .../ERC20/extensions/ERC20Crosschain.test.js | 27 +++++++++-------- .../token/ERC20/extensions/ERC20Votes.test.js | 6 ++-- .../extensions/ERC721Crosschain.test.js | 16 ++++++---- 5 files changed, 55 insertions(+), 40 deletions(-) diff --git a/test/helpers/governance.js b/test/helpers/governance.js index c68f5f3eb3f..e7a4cba6c28 100644 --- a/test/helpers/governance.js +++ b/test/helpers/governance.js @@ -70,18 +70,24 @@ export class GovernorHelper { } /// Proposal lifecycle - async delegate(delegation) { - await delegation.token.connect(delegation.to).delegate(delegation.to); - if (delegation.value !== undefined) { - await delegation.token.connect(this.governor.runner).transfer(delegation.to, delegation.value); - } - if (delegation.tokenId !== undefined) { - await delegation.token - .ownerOf(delegation.tokenId) - .then(owner => - delegation.token.connect(this.governor.runner).transferFrom(owner, delegation.to, delegation.tokenId), - ); - } + delegate(delegation) { + return delegation.token + .connect(delegation.to) + .delegate(delegation.to) + .then( + () => + delegation.value === undefined || + delegation.token.connect(this.governor.runner).transfer(delegation.to, delegation.value), + ) + .then( + () => + delegation.tokenId === undefined || + delegation.token + .ownerOf(delegation.tokenId) + .then(owner => + delegation.token.connect(this.governor.runner).transferFrom(owner, delegation.to, delegation.tokenId), + ), + ); } propose() { diff --git a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js index 33de0fd4adc..839a06cff7d 100644 --- a/test/token/ERC1155/extensions/ERC1155Crosschain.test.js +++ b/test/token/ERC1155/extensions/ERC1155Crosschain.test.js @@ -3,14 +3,18 @@ import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC1155 } from '../../../crosschain/BridgeERC1155.behavior'; const connection = await network.create(); -const { ethers, helpers, networkHelpers } = connection; +const { + ethers, + helpers: { chain, impersonate }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await helpers.impersonate(gateway); + const gatewayAsEOA = await impersonate(gateway); // Chain A: ERC1155 with native bridge integration const tokenA = await ethers.deployContract('$ERC1155Crosschain', [[], 'https://token-cdn-domain/{id}.json']); @@ -18,22 +22,22 @@ async function fixture() { // Chain B: ERC1155 with native bridge integration const tokenB = await ethers.deployContract('$ERC1155Crosschain', [ - [[gateway, helpers.chain.toErc7930(bridgeA)]], + [[gateway, chain.toErc7930(bridgeA)]], 'https://token-cdn-domain/{id}.json', ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); + .withArgs(gateway, chain.toErc7930(bridgeB)); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('ERC1155Crosschain', function () { beforeEach(async function () { - Object.assign(this, connection, await networkHelpers.loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeBridgeERC1155(); diff --git a/test/token/ERC20/extensions/ERC20Crosschain.test.js b/test/token/ERC20/extensions/ERC20Crosschain.test.js index abcfd3488a2..9faf82cb0d4 100644 --- a/test/token/ERC20/extensions/ERC20Crosschain.test.js +++ b/test/token/ERC20/extensions/ERC20Crosschain.test.js @@ -4,14 +4,18 @@ import { anyValue } from '@nomicfoundation/hardhat-ethers-chai-matchers/withArgs import { shouldBehaveLikeBridgeERC20 } from '../../../crosschain/BridgeERC20.behavior'; const connection = await network.create(); -const { ethers, helpers, networkHelpers } = connection; +const { + ethers, + helpers: { chain, impersonate }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await helpers.impersonate(gateway); + const gatewayAsEOA = await impersonate(gateway); // Chain A: legacy ERC20 with bridge const tokenA = await ethers.deployContract('$ERC20Crosschain', ['Token1', 'T1', []]); @@ -19,15 +23,12 @@ async function fixture() { // Chain B: ERC7802 with bridge const tokenB = await ethers.deployContract('$ERC20BridgeableMock', ['Token2', 'T2', ethers.ZeroAddress]); - const bridgeB = await ethers.deployContract('$BridgeERC7802', [ - [[gateway, helpers.chain.toErc7930(bridgeA)]], - tokenB, - ]); + const bridgeB = await ethers.deployContract('$BridgeERC7802', [[[gateway, chain.toErc7930(bridgeA)]], tokenB]); // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); + .withArgs(gateway, chain.toErc7930(bridgeB)); await tokenB.$_setBridge(bridgeB); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; @@ -35,7 +36,7 @@ async function fixture() { describe('ERC20Crosschain', function () { beforeEach(async function () { - Object.assign(this, connection, await networkHelpers.loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeBridgeERC20(); @@ -49,18 +50,18 @@ describe('ERC20Crosschain', function () { await this.tokenA.connect(alice).approve(chris, ethers.MaxUint256); // Alice sends tokens from chain A to Bruce on chain B. - await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, helpers.chain.toErc7930(bruce), amount)) + await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, chain.toErc7930(bruce), amount)) // bridge on chain A takes custody of the funds .to.emit(this.tokenA, 'Transfer') .withArgs(alice, ethers.ZeroAddress, amount) // crosschain transfer sent .to.emit(this.tokenA, 'CrosschainFungibleTransferSent') - .withArgs(anyValue, alice, helpers.chain.toErc7930(bruce), amount) + .withArgs(anyValue, alice, chain.toErc7930(bruce), amount) // ERC-7786 event .to.emit(this.gateway, 'MessageSent') // crosschain transfer received .to.emit(this.bridgeB, 'CrosschainFungibleTransferReceived') - .withArgs(anyValue, helpers.chain.toErc7930(alice), bruce, amount) + .withArgs(anyValue, chain.toErc7930(alice), bruce, amount) // crosschain mint event .to.emit(this.tokenB, 'CrosschainMint') .withArgs(bruce, amount, this.bridgeB) @@ -75,7 +76,7 @@ describe('ERC20Crosschain', function () { await this.tokenA.$_mint(alice, amount); - await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, helpers.chain.toErc7930(bruce), amount)) + await expect(this.tokenA.connect(chris).crosschainTransferFrom(alice, chain.toErc7930(bruce), amount)) .to.be.revertedWithCustomError(this.tokenA, 'ERC20InsufficientAllowance') .withArgs(chris, 0n, amount); }); diff --git a/test/token/ERC20/extensions/ERC20Votes.test.js b/test/token/ERC20/extensions/ERC20Votes.test.js index 9e89a32d752..c7e26f238ae 100644 --- a/test/token/ERC20/extensions/ERC20Votes.test.js +++ b/test/token/ERC20/extensions/ERC20Votes.test.js @@ -406,9 +406,9 @@ describe('ERC20Votes', function () { const [t1, t2, t3] = await batchInBlock( [ - () => this.token.connect(this.recipient).delegate(this.other1, { gasLimit: 200000 }), - () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200000 }), - () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200000 }), + () => this.token.connect(this.recipient).delegate(this.other1, { gasLimit: 200_000n }), + () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200_000n }), + () => this.token.connect(this.recipient).transfer(this.other2, 10n, { gasLimit: 200_000n }), ], ethers.provider, ); diff --git a/test/token/ERC721/extensions/ERC721Crosschain.test.js b/test/token/ERC721/extensions/ERC721Crosschain.test.js index e3cebcf2d97..4534b584418 100644 --- a/test/token/ERC721/extensions/ERC721Crosschain.test.js +++ b/test/token/ERC721/extensions/ERC721Crosschain.test.js @@ -3,14 +3,18 @@ import { expect } from 'chai'; import { shouldBehaveLikeBridgeERC721 } from '../../../crosschain/BridgeERC721.behavior'; const connection = await network.create(); -const { ethers, helpers, networkHelpers } = connection; +const { + ethers, + helpers: { chain, impersonate }, + networkHelpers: { loadFixture }, +} = connection; async function fixture() { const accounts = await ethers.getSigners(); // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await helpers.impersonate(gateway); + const gatewayAsEOA = await impersonate(gateway); // Chain A: ERC721 with native bridge integration const tokenA = await ethers.deployContract('$ERC721Crosschain', [[], 'Token1', 'T1']); @@ -18,23 +22,23 @@ async function fixture() { // Chain B: ERC721 with native bridge integration const tokenB = await ethers.deployContract('$ERC721Crosschain', [ - [[gateway, helpers.chain.toErc7930(bridgeA)]], + [[gateway, chain.toErc7930(bridgeA)]], 'Token2', 'T2', ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); + .withArgs(gateway, chain.toErc7930(bridgeB)); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } describe('ERC721Crosschain', function () { beforeEach(async function () { - Object.assign(this, connection, await networkHelpers.loadFixture(fixture)); + Object.assign(this, connection, await loadFixture(fixture)); }); shouldBehaveLikeBridgeERC721(); From 330f381db39b9c6757801bcc6dc730be025375b5 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 15:02:22 +0200 Subject: [PATCH 88/91] get file patterns from package.json --- scripts/checks/inheritance-ordering.js | 30 ++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/scripts/checks/inheritance-ordering.js b/scripts/checks/inheritance-ordering.js index 5460369cd93..7d2d887ab0c 100755 --- a/scripts/checks/inheritance-ordering.js +++ b/scripts/checks/inheritance-ordering.js @@ -10,13 +10,10 @@ import { hideBin } from 'yargs/helpers'; const { _: artifacts } = yargs(hideBin(process.argv)).argv; -// files to skip -const skipPatterns = [ - 'contracts-exposed/**', - 'contracts/mocks/**', - 'lib/openzeppelin-contracts/contracts/mocks/**', - 'test/**', -]; +// only consider files in the package: take pattern from package.json +const { files: patterns } = JSON.parse( + fs.readFileSync(path.resolve(import.meta.dirname, '../../', 'package.json'), 'utf-8'), +); for (const artifact of artifacts) { const { output: solcOutput } = JSON.parse( @@ -28,16 +25,17 @@ for (const artifact of artifacts) { const linearized = []; for (const source in solcOutput?.contracts ?? []) { - if (match.any(source.replace(/^project\//, ''), skipPatterns)) continue; - for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) { - names[contractDef.id] = contractDef.name; - linearized.push(contractDef.linearizedBaseContracts); + if (match.all(source.replace(/^project/, ''), patterns)) { + for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) { + names[contractDef.id] = contractDef.name; + linearized.push(contractDef.linearizedBaseContracts); - contractDef.linearizedBaseContracts.forEach((c1, i, contracts) => - contracts.slice(i + 1).forEach(c2 => { - graph.setEdge(c1, c2); - }), - ); + contractDef.linearizedBaseContracts.forEach((c1, i, contracts) => + contracts.slice(i + 1).forEach(c2 => { + graph.setEdge(c1, c2); + }), + ); + } } } From 402ab1b623a358051c6457eef4e778720285ed3c Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 15:21:37 +0200 Subject: [PATCH 89/91] more tests rewrite --- .../AccessControlDefaultAdminRules.test.js | 6 +-- test/access/manager/AccessManaged.test.js | 12 +++--- test/crosschain/BridgeERC1155.test.js | 10 ++--- test/crosschain/BridgeERC20.test.js | 13 +++---- test/crosschain/BridgeERC721.test.js | 10 ++--- test/crosschain/CrosschainExecutor.test.js | 39 +++++++++---------- test/crosschain/ERC7786Recipient.test.js | 24 +++++------- .../extensions/GovernorCrosschain.test.js | 21 +++++----- 8 files changed, 60 insertions(+), 75 deletions(-) diff --git a/test/access/extensions/AccessControlDefaultAdminRules.test.js b/test/access/extensions/AccessControlDefaultAdminRules.test.js index 6ed4a0ace93..9953bed97ff 100644 --- a/test/access/extensions/AccessControlDefaultAdminRules.test.js +++ b/test/access/extensions/AccessControlDefaultAdminRules.test.js @@ -8,12 +8,12 @@ import { const connection = await network.create(); const { ethers, - helpers, + helpers: { time }, networkHelpers: { loadFixture }, } = connection; async function fixture() { - const delay = helpers.time.duration.hours(10); + const delay = time.duration.hours(10); const [defaultAdmin, ...accounts] = await ethers.getSigners(); const mock = await ethers.deployContract('$AccessControlDefaultAdminRules', [delay, defaultAdmin]); return { mock, defaultAdmin, delay, accounts }; @@ -31,5 +31,5 @@ describe('AccessControlDefaultAdminRules', function () { }); shouldBehaveLikeAccessControl(); - shouldBehaveLikeAccessControlDefaultAdminRules({ helpers }); + shouldBehaveLikeAccessControlDefaultAdminRules(); }); diff --git a/test/access/manager/AccessManaged.test.js b/test/access/manager/AccessManaged.test.js index 027a9612444..52613231aa3 100644 --- a/test/access/manager/AccessManaged.test.js +++ b/test/access/manager/AccessManaged.test.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; const { ethers, - helpers, + helpers: { impersonate, time }, networkHelpers: { loadFixture }, } = await network.create(); @@ -16,7 +16,7 @@ async function fixture() { const anotherAuthority = await ethers.deployContract('$AccessManager', [admin]); const authorityObserveIsConsuming = await ethers.deployContract('$AuthorityObserveIsConsuming'); - await helpers.impersonate(authority.target); + await impersonate(authority.target); const authorityAsSigner = await ethers.getSigner(authority.target); return { @@ -83,18 +83,18 @@ describe('AccessManaged', function () { it('succeeds if the operation is scheduled', async function () { // Arguments - const delay = helpers.time.duration.hours(12); + const delay = time.duration.hours(12); const fn = this.managed.interface.getFunction(this.selector); const calldata = this.managed.interface.encodeFunctionData(fn, []); // Schedule - const scheduledAt = (await helpers.time.clock.timestamp()) + 1n; + const scheduledAt = (await time.clock.timestamp()) + 1n; const when = scheduledAt + delay; - await helpers.time.increaseTo.timestamp(scheduledAt, false); + await time.increaseTo.timestamp(scheduledAt, false); await this.authority.connect(this.roleMember).schedule(this.managed, calldata, when); // Set execution date - await helpers.time.increaseTo.timestamp(when, false); + await time.increaseTo.timestamp(when, false); // Shouldn't revert await this.managed.connect(this.roleMember)[this.selector](); diff --git a/test/crosschain/BridgeERC1155.test.js b/test/crosschain/BridgeERC1155.test.js index c6fcc707fee..8ef9f0e29b6 100644 --- a/test/crosschain/BridgeERC1155.test.js +++ b/test/crosschain/BridgeERC1155.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeBridgeERC1155 } from './BridgeERC1155.behavior'; const connection = await network.create(); const { ethers, - helpers, + helpers: { chain, impersonate }, networkHelpers: { loadFixture }, } = connection; @@ -14,7 +14,7 @@ async function fixture() { // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await helpers.impersonate(gateway); + const gatewayAsEOA = await impersonate(gateway); // Chain A: legacy ERC1155 with bridge const tokenA = await ethers.deployContract('$ERC1155', ['https://token-cdn-domain/{id}.json']); @@ -22,15 +22,15 @@ async function fixture() { // Chain B: ERC1155 with native bridge integration const tokenB = await ethers.deployContract('$ERC1155Crosschain', [ - [[gateway, helpers.chain.toErc7930(bridgeA)]], + [[gateway, chain.toErc7930(bridgeA)]], 'https://token-cdn-domain/{id}.json', ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); + .withArgs(gateway, chain.toErc7930(bridgeB)); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } diff --git a/test/crosschain/BridgeERC20.test.js b/test/crosschain/BridgeERC20.test.js index b5b444ef71c..e9e3e8b1bfc 100644 --- a/test/crosschain/BridgeERC20.test.js +++ b/test/crosschain/BridgeERC20.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeBridgeERC20 } from './BridgeERC20.behavior'; const connection = await network.create(); const { ethers, - helpers, + helpers: { chain, impersonate }, networkHelpers: { loadFixture }, } = connection; @@ -14,7 +14,7 @@ async function fixture() { // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await helpers.impersonate(gateway); + const gatewayAsEOA = await impersonate(gateway); // Chain A: legacy ERC20 with bridge const tokenA = await ethers.deployContract('$ERC20', ['Token1', 'T1']); @@ -22,15 +22,12 @@ async function fixture() { // Chain B: ERC7802 with bridge (preconfigured link to bridgeA) const tokenB = await ethers.deployContract('$ERC20BridgeableMock', ['Token2', 'T2', ethers.ZeroAddress]); - const bridgeB = await ethers.deployContract('$BridgeERC7802', [ - [[gateway, helpers.chain.toErc7930(bridgeA)]], - tokenB, - ]); + const bridgeB = await ethers.deployContract('$BridgeERC7802', [[[gateway, chain.toErc7930(bridgeA)]], tokenB]); // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); + .withArgs(gateway, chain.toErc7930(bridgeB)); await tokenB.$_setBridge(bridgeB); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; diff --git a/test/crosschain/BridgeERC721.test.js b/test/crosschain/BridgeERC721.test.js index 861dce8a2ba..cc4d21ab6f3 100644 --- a/test/crosschain/BridgeERC721.test.js +++ b/test/crosschain/BridgeERC721.test.js @@ -5,7 +5,7 @@ import { shouldBehaveLikeBridgeERC721 } from './BridgeERC721.behavior'; const connection = await network.create(); const { ethers, - helpers, + helpers: { chain, impersonate }, networkHelpers: { loadFixture }, } = connection; @@ -14,7 +14,7 @@ async function fixture() { // Mock gateway const gateway = await ethers.deployContract('$ERC7786GatewayMock'); - const gatewayAsEOA = await helpers.impersonate(gateway); + const gatewayAsEOA = await impersonate(gateway); // Chain A: legacy ERC721 with bridge const tokenA = await ethers.deployContract('$ERC721', ['Token1', 'T1']); @@ -22,16 +22,16 @@ async function fixture() { // Chain B: ERC721 with native bridge integration const tokenB = await ethers.deployContract('$ERC721Crosschain', [ - [[gateway, helpers.chain.toErc7930(bridgeA)]], + [[gateway, chain.toErc7930(bridgeA)]], 'Token2', 'T2', ]); const bridgeB = tokenB; // self bridge // deployment check + counterpart setup - await expect(bridgeA.$_setLink(gateway, helpers.chain.toErc7930(bridgeB), false)) + await expect(bridgeA.$_setLink(gateway, chain.toErc7930(bridgeB), false)) .to.emit(bridgeA, 'LinkRegistered') - .withArgs(gateway, helpers.chain.toErc7930(bridgeB)); + .withArgs(gateway, chain.toErc7930(bridgeB)); return { accounts, gateway, gatewayAsEOA, tokenA, tokenB, bridgeA, bridgeB }; } diff --git a/test/crosschain/CrosschainExecutor.test.js b/test/crosschain/CrosschainExecutor.test.js index 553aedc2969..342e91575c6 100644 --- a/test/crosschain/CrosschainExecutor.test.js +++ b/test/crosschain/CrosschainExecutor.test.js @@ -14,7 +14,7 @@ import { const connection = await network.create(); const { ethers, - helpers, + helpers: { chain }, networkHelpers: { loadFixture }, } = connection; @@ -25,7 +25,7 @@ async function fixture() { const target = await ethers.deployContract('CallReceiverMock'); // Deploy executor - const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [gateway, helpers.chain.toErc7930(admin)]); + const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [gateway, chain.toErc7930(admin)]); const remoteExecute = (from, target, mode, data) => gateway.connect(from).sendMessage(target, ethers.concat([mode, data]), []); @@ -40,7 +40,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { it('setup', async function () { await expect(this.executor.gateway()).to.eventually.equal(this.gateway); - await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.admin)); + await expect(this.executor.controller()).to.eventually.equal(chain.toErc7930(this.admin)); }); describe('crosschain operation', function () { @@ -48,7 +48,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { const mode = encodeMode({ callType: CALL_TYPE_CALL }); const data = encodeSingle(this.target, 0n, this.target.interface.encodeFunctionData('mockFunctionExtra')); - await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, chain.toErc7930(this.executor), mode, data)) .to.emit(this.target, 'MockFunctionCalledExtra') .withArgs(this.executor, 0n); }); @@ -60,7 +60,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { [this.target, 0n, this.target.interface.encodeFunctionData('mockFunctionExtra')], ); - await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, chain.toErc7930(this.executor), mode, data)) .to.emit(this.target, 'MockFunctionCalledWithArgs') .withArgs(42, '0x1234') .to.emit(this.target, 'MockFunctionCalledExtra') @@ -71,7 +71,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { const mode = encodeMode({ callType: CALL_TYPE_DELEGATE }); const data = encodeDelegate(this.target, this.target.interface.encodeFunctionData('mockFunctionExtra')); - await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, chain.toErc7930(this.executor), mode, data)) .to.emit(this.target.attach(this.executor.target), 'MockFunctionCalledExtra') .withArgs(this.gateway, 0n); }); @@ -80,7 +80,7 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { const mode = encodeMode({ callType: '0x42' }); const data = '0x'; - await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, chain.toErc7930(this.executor), mode, data)) .to.be.revertedWithCustomError(this.executor, 'ERC7579UnsupportedCallType') .withArgs('0x42'); }); @@ -98,50 +98,47 @@ describe('CrosschainRemoteController & CrosschainRemoteExecutor', function () { 0n, this.executor.interface.encodeFunctionData('reconfigure', [ this.newGateway.target, - helpers.chain.toErc7930(this.other), + chain.toErc7930(this.other), ]), ); - await expect(this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data)) + await expect(this.remoteExecute(this.admin, chain.toErc7930(this.executor), mode, data)) .to.emit(this.executor, 'CrosschainControllerSet') - .withArgs(this.newGateway, helpers.chain.toErc7930(this.other)); + .withArgs(this.newGateway, chain.toErc7930(this.other)); await expect(this.executor.gateway()).to.eventually.equal(this.newGateway); - await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.other)); + await expect(this.executor.controller()).to.eventually.equal(chain.toErc7930(this.other)); }); it('through the internal setter: success', async function () { - await expect(this.executor.$_setup(this.newGateway, helpers.chain.toErc7930(this.other))) + await expect(this.executor.$_setup(this.newGateway, chain.toErc7930(this.other))) .to.emit(this.executor, 'CrosschainControllerSet') - .withArgs(this.newGateway, helpers.chain.toErc7930(this.other)); + .withArgs(this.newGateway, chain.toErc7930(this.other)); await expect(this.executor.gateway()).to.eventually.equal(this.newGateway); - await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.other)); + await expect(this.executor.controller()).to.eventually.equal(chain.toErc7930(this.other)); }); it('with an invalid new gateway: revert', async function () { // directly using the internal setter - await expect(this.executor.$_setup(this.other, helpers.chain.toErc7930(this.other))).to.be.revert(ethers); + await expect(this.executor.$_setup(this.other, chain.toErc7930(this.other))).to.be.revert(ethers); // through a crosschain call const mode = encodeMode({ callType: CALL_TYPE_CALL }); const data = encodeSingle( this.executor, 0n, - this.executor.interface.encodeFunctionData('reconfigure', [ - this.other.address, - helpers.chain.toErc7930(this.other), - ]), + this.executor.interface.encodeFunctionData('reconfigure', [this.other.address, chain.toErc7930(this.other)]), ); await expect( - this.remoteExecute(this.admin, helpers.chain.toErc7930(this.executor), mode, data), + this.remoteExecute(this.admin, chain.toErc7930(this.executor), mode, data), ).to.be.revertedWithCustomError(this.executor, 'FailedCall'); }); it('is access controlled', async function () { await expect( - this.executor.reconfigure(this.newGateway, helpers.chain.toErc7930(this.other)), + this.executor.reconfigure(this.newGateway, chain.toErc7930(this.other)), ).to.be.revertedWithCustomError(this.executor, 'AccessRestricted'); }); }); diff --git a/test/crosschain/ERC7786Recipient.test.js b/test/crosschain/ERC7786Recipient.test.js index 96bd22438e1..e9da9939ba1 100644 --- a/test/crosschain/ERC7786Recipient.test.js +++ b/test/crosschain/ERC7786Recipient.test.js @@ -4,7 +4,7 @@ import { generators } from '../helpers/random'; const { ethers, - helpers, + helpers: { chain }, networkHelpers: { loadFixture }, } = await network.create(); @@ -29,42 +29,36 @@ describe('ERC7786Recipient', function () { it('receives gateway relayed messages', async function () { await expect( - this.gateway - .connect(this.sender) - .sendMessage(helpers.chain.toErc7930(this.receiver), payload, attributes, { value }), + this.gateway.connect(this.sender).sendMessage(chain.toErc7930(this.receiver), payload, attributes, { value }), ) .to.emit(this.gateway, 'MessageSent') .withArgs( ethers.ZeroHash, - helpers.chain.toErc7930(this.sender), - helpers.chain.toErc7930(this.receiver), + chain.toErc7930(this.sender), + chain.toErc7930(this.receiver), payload, value, attributes, ) .to.emit(this.receiver, 'MessageReceived') - .withArgs(this.gateway, ethers.toBeHex(1n, 32n), helpers.chain.toErc7930(this.sender), payload, value); + .withArgs(this.gateway, ethers.toBeHex(1n, 32n), chain.toErc7930(this.sender), payload, value); }); it('receive multiple similar messages', async function () { for (let i = 1n; i < 5n; ++i) { await expect( - this.gateway - .connect(this.sender) - .sendMessage(helpers.chain.toErc7930(this.receiver), payload, attributes, { value }), + this.gateway.connect(this.sender).sendMessage(chain.toErc7930(this.receiver), payload, attributes, { value }), ) .to.emit(this.receiver, 'MessageReceived') - .withArgs(this.gateway, ethers.toBeHex(i, 32n), helpers.chain.toErc7930(this.sender), payload, value); + .withArgs(this.gateway, ethers.toBeHex(i, 32n), chain.toErc7930(this.sender), payload, value); } }); it('unauthorized call', async function () { await expect( - this.receiver - .connect(this.notAGateway) - .receiveMessage(ethers.ZeroHash, helpers.chain.toErc7930(this.sender), payload), + this.receiver.connect(this.notAGateway).receiveMessage(ethers.ZeroHash, chain.toErc7930(this.sender), payload), ) .to.be.revertedWithCustomError(this.receiver, 'ERC7786RecipientUnauthorizedGateway') - .withArgs(this.notAGateway, helpers.chain.toErc7930(this.sender)); + .withArgs(this.notAGateway, chain.toErc7930(this.sender)); }); }); diff --git a/test/governance/extensions/GovernorCrosschain.test.js b/test/governance/extensions/GovernorCrosschain.test.js index de5292ad7c3..2dd61c28083 100644 --- a/test/governance/extensions/GovernorCrosschain.test.js +++ b/test/governance/extensions/GovernorCrosschain.test.js @@ -8,7 +8,7 @@ import { VoteType } from '../../helpers/enums'; const connection = await network.create(); const { ethers, - helpers, + helpers: { chain }, networkHelpers: { loadFixture }, } = connection; @@ -39,10 +39,7 @@ async function fixture() { ]); // Deploy executor - const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [ - gateway, - helpers.chain.toErc7930(governor), - ]); + const executor = await ethers.deployContract('$CrosschainRemoteExecutor', [gateway, chain.toErc7930(governor)]); await owner.sendTransaction({ to: governor, value }); await token.$_mint(owner, tokenSupply); @@ -81,7 +78,7 @@ describe('GovernorCrosschain', function () { target: this.governor.target, data: this.governor.interface.encodeFunctionData('relayCrosschain(address,bytes,bytes32,bytes)', [ this.gateway.target, - helpers.chain.toErc7930(this.executor), + chain.toErc7930(this.executor), encodeMode({ callType: CALL_TYPE_CALL }), encodeSingle(this.receiver, 0n, this.receiver.interface.encodeFunctionData('mockFunctionExtra')), ]), @@ -103,7 +100,7 @@ describe('GovernorCrosschain', function () { await expect( this.governor.getFunction('relayCrosschain(address,bytes,bytes32,bytes)')( this.gateway, - helpers.chain.toErc7930(this.executor), + chain.toErc7930(this.executor), encodeMode({ callType: CALL_TYPE_CALL }), encodeSingle(this.receiver, 0n, this.receiver.interface.encodeFunctionData('mockFunctionExtra')), ), @@ -122,7 +119,7 @@ describe('GovernorCrosschain', function () { // Before reconfiguration await expect(this.executor.gateway()).to.eventually.equal(this.gateway); - await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(this.governor)); + await expect(this.executor.controller()).to.eventually.equal(chain.toErc7930(this.governor)); // Propose reconfiguration this.helper.setProposal( @@ -131,14 +128,14 @@ describe('GovernorCrosschain', function () { target: this.governor.target, data: this.governor.interface.encodeFunctionData('relayCrosschain(address,bytes,bytes32,bytes)', [ this.gateway.target, - helpers.chain.toErc7930(this.executor), + chain.toErc7930(this.executor), encodeMode({ callType: CALL_TYPE_CALL }), encodeSingle( this.executor, 0n, this.executor.interface.encodeFunctionData('reconfigure', [ this.gateway.target, - helpers.chain.toErc7930(newGovernor), + chain.toErc7930(newGovernor), ]), ), ]), @@ -155,10 +152,10 @@ describe('GovernorCrosschain', function () { await expect(this.helper.execute()) .to.emit(this.executor, 'CrosschainControllerSet') - .withArgs(this.gateway, helpers.chain.toErc7930(newGovernor)); + .withArgs(this.gateway, chain.toErc7930(newGovernor)); // After reconfiguration await expect(this.executor.gateway()).to.eventually.equal(this.gateway); - await expect(this.executor.controller()).to.eventually.equal(helpers.chain.toErc7930(newGovernor)); + await expect(this.executor.controller()).to.eventually.equal(chain.toErc7930(newGovernor)); }); }); From db1ee9caf7aa3fab7b9e7530550d9a581fc4c6d0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 15:59:06 +0200 Subject: [PATCH 90/91] Update compare-gas-reports.js --- scripts/checks/compare-gas-reports.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/checks/compare-gas-reports.js b/scripts/checks/compare-gas-reports.js index 5f384b23c2e..42aff7abe47 100755 --- a/scripts/checks/compare-gas-reports.js +++ b/scripts/checks/compare-gas-reports.js @@ -28,8 +28,6 @@ const { argv } = yargs(hideBin(process.argv)) const BASE_TX_COST = 21000; // Utilities -// const sum = (...args) => args.reduce((a, b) => a + b, 0); -// const average = (...args) => sum(...args) / args.length; const variation = (current, previous, offset = 0) => ({ value: current - offset, delta: current - previous, From fe1f2230e15b776c91880040ff754943d5b5a1ba Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 21 May 2026 17:29:52 +0200 Subject: [PATCH 91/91] Update TimelockController.test.js --- test/governance/TimelockController.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/governance/TimelockController.test.js b/test/governance/TimelockController.test.js index 7e5c1b9869f..dd4d6d13a3f 100644 --- a/test/governance/TimelockController.test.js +++ b/test/governance/TimelockController.test.js @@ -5,12 +5,11 @@ import { GovernorHelper } from '../helpers/governance'; import { OperationState } from '../helpers/enums'; import { shouldSupportInterfaces } from '../utils/introspection/SupportsInterface.behavior'; -const connection = await network.create(); const { ethers, helpers: { time }, networkHelpers: { loadFixture }, -} = connection; +} = await network.create(); const salt = '0x025e7b0be353a74631ad648c667493c0e1cd31caa4cc2d3520fdc171ea0cc726'; // a random value