diff --git a/.changeset/short-mangos-watch.md b/.changeset/short-mangos-watch.md new file mode 100644 index 00000000000..7588d86b02c --- /dev/null +++ b/.changeset/short-mangos-watch.md @@ -0,0 +1,5 @@ +--- +"hardhat": patch +--- + +Added support for Node v24. diff --git a/.github/workflows/hardhat-ci.yml b/.github/workflows/hardhat-ci.yml index 0ff219bd23a..d9e8582070e 100644 --- a/.github/workflows/hardhat-ci.yml +++ b/.github/workflows/hardhat-ci.yml @@ -71,7 +71,7 @@ jobs: matrix: package: ${{ fromJson(needs.list-packages.outputs.packages) }} os: [ubuntu-latest, macos-latest, windows-latest] - node: [18, 20, 22] + node: [20, 22, 24] exclude: - package: hardhat-vyper os: windows-latest diff --git a/.github/workflows/hardhat-core-ci.yml b/.github/workflows/hardhat-core-ci.yml index 7a645344e30..c9be35c0e3a 100644 --- a/.github/workflows/hardhat-core-ci.yml +++ b/.github/workflows/hardhat-core-ci.yml @@ -112,9 +112,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # TODO: Currently there is no @types/node for Node 22, + # TODO: Currently there is no @types/node for Node 24, # include it when it is available. - node: [18, 20] + node: [20, 22] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-env diff --git a/packages/hardhat-chai-matchers/test/contracts.ts b/packages/hardhat-chai-matchers/test/contracts.ts index 85e5f6e5194..a00ee122a5e 100644 --- a/packages/hardhat-chai-matchers/test/contracts.ts +++ b/packages/hardhat-chai-matchers/test/contracts.ts @@ -1,9 +1,12 @@ import { BaseContract, - BaseContractMethod, + // eslint-disable-next-line prettier/prettier + type BaseContractMethod, ContractTransactionResponse, - BigNumberish, - AddressLike, + // eslint-disable-next-line prettier/prettier + type BigNumberish, + // eslint-disable-next-line prettier/prettier + type AddressLike, } from "ethers"; export type MatchersContract = BaseContract & { diff --git a/packages/hardhat-core/src/internal/cli/constants.ts b/packages/hardhat-core/src/internal/cli/constants.ts index 96eb66fedc4..13e0b796c25 100644 --- a/packages/hardhat-core/src/internal/cli/constants.ts +++ b/packages/hardhat-core/src/internal/cli/constants.ts @@ -1 +1,7 @@ -export const SUPPORTED_NODE_VERSIONS = ["^18.0.0", "^20.0.0", "^22.0.0"]; +// TODO: Remove 18 on June 30, 2025 +export const SUPPORTED_NODE_VERSIONS = [ + "^18.0.0", + "^20.0.0", + "^22.0.0", + "^24.0.0", +]; diff --git a/packages/hardhat-core/src/internal/core/config/config-loading.ts b/packages/hardhat-core/src/internal/core/config/config-loading.ts index 3c5ed971b4d..1729d22669b 100644 --- a/packages/hardhat-core/src/internal/core/config/config-loading.ts +++ b/packages/hardhat-core/src/internal/core/config/config-loading.ts @@ -33,6 +33,7 @@ export function importCsjOrEsModule(filePath: string): any { // because Hardhat configs can only be CJS but a .js extension will be interpreted as ESM. // The kind of error we get in these cases depends on the Node.js version. const node20Heuristic = e.code === "ERR_REQUIRE_ESM"; + // Also works for node v24 const node22Heuristic = e.message === "module is not defined" || e.message === "require is not defined"; diff --git a/packages/hardhat-core/test/internal/cli/is-node-version-to-warn-on.ts b/packages/hardhat-core/test/internal/cli/is-node-version-to-warn-on.ts index f81dc718dcd..5bd6195e99a 100644 --- a/packages/hardhat-core/test/internal/cli/is-node-version-to-warn-on.ts +++ b/packages/hardhat-core/test/internal/cli/is-node-version-to-warn-on.ts @@ -11,13 +11,16 @@ describe("isNodeVersionToWarnOn", function () { assert.isFalse(isNodeVersionToWarnOn("v22.0.0")); assert.isFalse(isNodeVersionToWarnOn("v22.3.0")); - }); - it("Should not warn on even newer versions even if they are unsupported", function () { assert.isFalse(isNodeVersionToWarnOn("v24.0.0")); assert.isFalse(isNodeVersionToWarnOn("v24.3.0")); }); + it("Should not warn on even newer versions even if they are unsupported", function () { + assert.isFalse(isNodeVersionToWarnOn("v26.0.0")); + assert.isFalse(isNodeVersionToWarnOn("v26.3.0")); + }); + it("Should warn on unsupported older node versions", function () { assert(isNodeVersionToWarnOn("v10.0.0")); assert(isNodeVersionToWarnOn("v10.24.1")); diff --git a/packages/hardhat-ethers/test/helpers.ts b/packages/hardhat-ethers/test/helpers.ts index 2e1f5730709..ecc8d0cd7c8 100644 --- a/packages/hardhat-ethers/test/helpers.ts +++ b/packages/hardhat-ethers/test/helpers.ts @@ -1,5 +1,6 @@ import { assert } from "chai"; -import { ContractRunner, Signer } from "ethers"; +// eslint-disable-next-line prettier/prettier +import { type ContractRunner, type Signer } from "ethers"; export function assertWithin( value: number | bigint, diff --git a/packages/hardhat-ignition-core/test/execution/future-processor/utils.ts b/packages/hardhat-ignition-core/test/execution/future-processor/utils.ts index 26a011ccec9..73dbbb48d73 100644 --- a/packages/hardhat-ignition-core/test/execution/future-processor/utils.ts +++ b/packages/hardhat-ignition-core/test/execution/future-processor/utils.ts @@ -93,14 +93,17 @@ function setupMockJsonRpcClient( } class MockJsonRpcClient implements JsonRpcClient { - private _blockNumber = 10; + #blockNumber = 10; + #transactions: { [key: string]: TransactionReceipt }; + #sendTransaction: (transactionParams: TransactionParams) => Promise; constructor( - private _sendTransaction: ( - transactionParams: TransactionParams - ) => Promise, - private _transactions: { [key: string]: TransactionReceipt } - ) {} + sendTransaction: (transactionParams: TransactionParams) => Promise, + transactions: { [key: string]: TransactionReceipt } + ) { + this.#sendTransaction = sendTransaction; + this.#transactions = transactions; + } public async getChainId(): Promise { return 31337; @@ -113,7 +116,7 @@ class MockJsonRpcClient implements JsonRpcClient { } public async getLatestBlock(): Promise { - const blockNumber = this._blockNumber++; + const blockNumber = this.#blockNumber++; return { hash: `0xblockhash-${blockNumber}`, @@ -152,7 +155,7 @@ class MockJsonRpcClient implements JsonRpcClient { public async sendTransaction( transactionParams: TransactionParams ): Promise { - return this._sendTransaction(transactionParams); + return this.#sendTransaction(transactionParams); } public async sendRawTransaction(_presignedTx: string): Promise { @@ -181,11 +184,11 @@ class MockJsonRpcClient implements JsonRpcClient { txHash: string ): Promise { assertIgnitionInvariant( - txHash in this._transactions, + txHash in this.#transactions, `No transaction registered in test for the hash ${txHash}` ); - return this._transactions[txHash]; + return this.#transactions[txHash]; } public async getCode(_address: string): Promise { diff --git a/packages/hardhat-ignition-core/test/reconciliation/helpers.ts b/packages/hardhat-ignition-core/test/reconciliation/helpers.ts index 78846816530..b3dc319181b 100644 --- a/packages/hardhat-ignition-core/test/reconciliation/helpers.ts +++ b/packages/hardhat-ignition-core/test/reconciliation/helpers.ts @@ -92,26 +92,30 @@ class MockArtifactResolver implements ArtifactResolver { } export class ArtifactMapResolver extends MockArtifactResolver { - constructor( - private readonly _artifactMap: { [artifactId: string]: Artifact } = {} - ) { + readonly #artifactMap: { [artifactId: string]: Artifact }; + + constructor(artifactMap: { [artifactId: string]: Artifact } = {}) { super(); + + this.#artifactMap = artifactMap; } public async loadArtifact(contractName: string): Promise { - return this._artifactMap[contractName]; + return this.#artifactMap[contractName]; } } export class ArtifactMapDeploymentLoader extends MockDeploymentLoader { - constructor( - private readonly _artifactMap: { [artifactId: string]: Artifact } = {} - ) { + readonly #artifactMap: { [artifactId: string]: Artifact }; + + constructor(artifactMap: { [artifactId: string]: Artifact } = {}) { super(); + + this.#artifactMap = artifactMap; } public async loadArtifact(contractName: string): Promise { - return this._artifactMap[contractName]; + return this.#artifactMap[contractName]; } } diff --git a/packages/hardhat-ignition-core/test/track-transaction.ts b/packages/hardhat-ignition-core/test/track-transaction.ts index a22efb20b7a..167ded0ac2b 100644 --- a/packages/hardhat-ignition-core/test/track-transaction.ts +++ b/packages/hardhat-ignition-core/test/track-transaction.ts @@ -4,6 +4,7 @@ import path from "path"; import { EIP1193Provider, RequestArguments, trackTransaction } from "../src"; import { NetworkTransaction } from "../src/internal/execution/types/jsonrpc"; import { JournalMessageType } from "../src/internal/execution/types/messages"; +import { ERRORS } from "../src/internal/errors-list"; const mockFullTx = { hash: "0x1a3eb512e21fc849f8e8733b250ce49b61178c9c4a670063f969db59eda4a59f", @@ -23,10 +24,16 @@ const mockFullTx = { }; class MockEIP1193Provider implements EIP1193Provider { + public fullTx: NetworkTransaction | null = null; + public confirmations: number = 6; + constructor( - public fullTx: NetworkTransaction | null = null, - public confirmations: number = 6 - ) {} + fullTx: NetworkTransaction | null = null, + confirmations: number = 6 + ) { + this.fullTx = fullTx; + this.confirmations = confirmations; + } public async request(args: RequestArguments): Promise { if (args.method === "eth_getTransactionByHash") { @@ -218,9 +225,7 @@ If this is not the expected behavior, please edit your Hardhat Ignition module a mockFullTx.hash, new MockEIP1193Provider(mockFullTx) ), - `The transaction hash that you provided was already present in your deployment. - -Please double check the error you are getting when running Hardhat Ignition, and the instructions it's providing.` + ERRORS.TRACK_TRANSACTION.KNOWN_TRANSACTION.message ); }); @@ -238,9 +243,7 @@ Please double check the error you are getting when running Hardhat Ignition, and hash, new MockEIP1193Provider({ ...mockFullTx, hash }, 2) ), - `The transaction you provided doesn't have enough confirmations yet. - -Please try again later.` + ERRORS.TRACK_TRANSACTION.INSUFFICIENT_CONFIRMATIONS.message ); }); @@ -274,9 +277,7 @@ Please double check the error you are getting when running Hardhat Ignition, and mockFullTx.hash, new MockEIP1193Provider({ ...mockFullTx, value: "0x11" }, 2) ), - `The transaction you provided doesn't have enough confirmations yet. - -Please try again later.` + ERRORS.TRACK_TRANSACTION.INSUFFICIENT_CONFIRMATIONS.message ); }); }); diff --git a/packages/hardhat-ignition/test/test-helpers/test-ignition-helper.ts b/packages/hardhat-ignition/test/test-helpers/test-ignition-helper.ts index 36d4498e9ed..a5d750fa85f 100644 --- a/packages/hardhat-ignition/test/test-helpers/test-ignition-helper.ts +++ b/packages/hardhat-ignition/test/test-helpers/test-ignition-helper.ts @@ -43,17 +43,21 @@ export interface TypeChainViemContractByName { export class TestIgnitionHelper { public type = "test"; - private _provider: EIP1193Provider; - private _deploymentDir: string | undefined; + #hre: HardhatRuntimeEnvironment; + #config?: Partial; + #provider: EIP1193Provider; + #deploymentDir: string | undefined; constructor( - private _hre: HardhatRuntimeEnvironment, - private _config?: Partial, + hre: HardhatRuntimeEnvironment, + config?: Partial, provider?: EIP1193Provider, deploymentDir?: string ) { - this._provider = provider ?? this._hre.network.provider; - this._deploymentDir = deploymentDir; + this.#hre = hre; + this.#config = config; + this.#provider = provider ?? this.#hre.network.provider; + this.#deploymentDir = deploymentDir; } public async deploy< @@ -86,21 +90,21 @@ export class TestIgnitionHelper { ): Promise< IgnitionModuleResultsTToViemContracts > { - const accounts = (await this._hre.network.provider.request({ + const accounts = (await this.#hre.network.provider.request({ method: "eth_accounts", })) as string[]; - const artifactResolver = new HardhatArtifactResolver(this._hre); + const artifactResolver = new HardhatArtifactResolver(this.#hre); const resolvedConfig: Partial = { - ...this._config, + ...this.#config, ...perDeployConfig, }; const result = await deploy({ config: resolvedConfig, - provider: this._provider, - deploymentDir: this._deploymentDir, + provider: this.#provider, + deploymentDir: this.#deploymentDir, artifactResolver, ignitionModule, deploymentParameters: parameters, @@ -118,11 +122,11 @@ export class TestIgnitionHelper { const publicClient = createPublicClient({ chain: hardhat, - transport: custom(this._hre.network.provider), + transport: custom(this.#hre.network.provider), }); return this._toViemContracts( - this._hre, + this.#hre, ignitionModule, result, publicClient diff --git a/packages/hardhat-ignition/test/test-helpers/use-ignition-project.ts b/packages/hardhat-ignition/test/test-helpers/use-ignition-project.ts index 7f2bbceb96b..bb67d180acd 100644 --- a/packages/hardhat-ignition/test/test-helpers/use-ignition-project.ts +++ b/packages/hardhat-ignition/test/test-helpers/use-ignition-project.ts @@ -182,14 +182,22 @@ function setupIgnitionHelperRiggedToThrow( } export class TestChainHelper { + #hre: HardhatRuntimeEnvironment; + #deployPromise: Promise; + #exitFn: () => void; + constructor( - private _hre: HardhatRuntimeEnvironment, - private _deployPromise: Promise, - private _exitFn: () => void - ) {} + hre: HardhatRuntimeEnvironment, + deployPromise: Promise, + exitFn: () => void + ) { + this.#hre = hre; + this.#deployPromise = deployPromise; + this.#exitFn = exitFn; + } public async waitForPendingTxs(expectedCount: number) { - await waitForPendingTxs(this._hre, expectedCount, this._deployPromise); + await waitForPendingTxs(this.#hre, expectedCount, this.#deployPromise); } /** @@ -201,24 +209,24 @@ export class TestChainHelper { */ public async mineBlock(pendingTxToAwait: number = 0) { if (pendingTxToAwait > 0) { - await waitForPendingTxs(this._hre, pendingTxToAwait, this._deployPromise); + await waitForPendingTxs(this.#hre, pendingTxToAwait, this.#deployPromise); } - return this._hre.network.provider.send("evm_mine"); + return this.#hre.network.provider.send("evm_mine"); } public async clearMempool(pendingTxToAwait: number = 0) { if (pendingTxToAwait > 0) { - await waitForPendingTxs(this._hre, pendingTxToAwait, this._deployPromise); + await waitForPendingTxs(this.#hre, pendingTxToAwait, this.#deployPromise); } - return clearPendingTransactionsFromMemoryPool(this._hre); + return clearPendingTransactionsFromMemoryPool(this.#hre); } /** * Exit from the deploy on the next block tick. */ public exitDeploy() { - this._exitFn(); + this.#exitFn(); } } diff --git a/packages/hardhat-solpp/test/tests.ts b/packages/hardhat-solpp/test/tests.ts index 5da69c1ee40..4b8bdb56769 100644 --- a/packages/hardhat-solpp/test/tests.ts +++ b/packages/hardhat-solpp/test/tests.ts @@ -102,22 +102,23 @@ describe("Solpp plugin", function () { }); // This test skipped because solpp won't fail if a contract has an non-defined symbol. - describe.skip("fail-project", function () { - useEnvironment("fail-project"); - - it("should fail when symbol does not exist", async function () { - const contractPath = join(this.env.config.paths.sources, "A.sol"); - const content = readFileSync(contractPath).toString(); - const files = [[contractPath, content]]; - const opts = {}; - - await expectErrorAsync(async () => - this.env.run("hardhat-solpp:run-solpp", { - files, - opts, - }) - ); - }); - }); + // The test is commented out because it causes the test suite to fail on Windows after the update to Node 24 + // describe.skip("fail-project", function () { + // useEnvironment("fail-project"); + + // it("should fail when symbol does not exist", async function () { + // const contractPath = join(this.env.config.paths.sources, "A.sol"); + // const content = readFileSync(contractPath).toString(); + // const files = [[contractPath, content]]; + // const opts = {}; + + // await expectErrorAsync(async () => + // this.env.run("hardhat-solpp:run-solpp", { + // files, + // opts, + // }) + // ); + // }); + // }); }); });