-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Use code 3 for JSON-RPC responses where there has been a revert #8104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4a1208f
58c6d15
693af7b
38469a8
e27a7ad
fdbe457
6f69bf0
e8a096b
c2b356e
2f315c7
604a1e1
fa00124
aee237a
4a78a6d
ccd22ea
ebb6b1d
6e54860
b854121
b5e2f69
5cb67ab
752401d
92ba0a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| --- | ||
| "@nomicfoundation/hardhat-ethers": patch | ||
| "@nomicfoundation/hardhat-ignition-viem": patch | ||
| "@nomicfoundation/hardhat-ignition": patch | ||
| "@nomicfoundation/hardhat-toolbox-viem": patch | ||
| "@nomicfoundation/hardhat-viem-assertions": patch | ||
| "@nomicfoundation/hardhat-viem": patch | ||
| "hardhat": patch | ||
| --- | ||
|
|
||
| Use code 3 for JSON-RPC revert error codes to align with standard node behavior and preserve error causes in viem/ethers. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,6 +51,6 @@ | |
| "prettier": "3.2.5", | ||
| "rimraf": "^5.0.5", | ||
| "typescript": "~5.8.0", | ||
| "viem": "^2.43.0" | ||
| "viem": "^2.47.6" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import type { HardhatEthers } from "../src/types.js"; | ||
| import type { JsonRpcServer } from "hardhat/types/network"; | ||
|
|
||
| import assert from "node:assert/strict"; | ||
| import { after, before, beforeEach, describe, it } from "node:test"; | ||
|
|
||
| import { ensureError } from "@nomicfoundation/hardhat-utils/error"; | ||
|
|
||
| import { initializeTestEthers, spawnTestRpcServer } from "./helpers/helpers.js"; | ||
|
|
||
| let ethers: HardhatEthers; | ||
|
|
||
| describe("revert error cause chain", () => { | ||
| describe("in-process EDR network", async () => { | ||
| beforeEach(async () => { | ||
| ({ ethers } = await initializeTestEthers([ | ||
| { artifactName: "Contract", fileName: "error-messages" }, | ||
| ])); | ||
| }); | ||
|
|
||
| it("should have SolidityError as the thrown error", async () => { | ||
| const contract = await ethers.deployContract("Contract"); | ||
|
|
||
| let caughtError: Error | undefined; | ||
| try { | ||
| await contract.revertsWithReasonString(); | ||
| } catch (error) { | ||
| ensureError(error); | ||
| caughtError = error; | ||
| } | ||
|
|
||
| assert.ok( | ||
| caughtError !== undefined && caughtError.name === "SolidityError", | ||
| `Expected SolidityError, got ${caughtError?.name}`, | ||
| ); | ||
| assert.ok( | ||
| "code" in caughtError && caughtError.code === 3, | ||
| `Expected error code 3, got ${"code" in caughtError ? String(caughtError.code) : "undefined"}`, | ||
| ); | ||
| assert.ok( | ||
| caughtError.message.includes("some reason"), | ||
| `Expected message to include "some reason", got: "${caughtError.message}"`, | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe("hardhat node (HTTP)", async () => { | ||
| let server: JsonRpcServer; | ||
| let port: number; | ||
| let address: string; | ||
|
|
||
| before(async () => { | ||
| ({ server, port, address } = await spawnTestRpcServer()); | ||
| }); | ||
|
|
||
| after(async () => { | ||
| await server.close(); | ||
| }); | ||
|
|
||
| beforeEach(async () => { | ||
| ({ ethers } = await initializeTestEthers( | ||
| [{ artifactName: "Contract", fileName: "error-messages" }], | ||
| { | ||
| networks: { | ||
| localhost: { type: "http", url: `http://${address}:${port}` }, | ||
| }, | ||
| }, | ||
| )); | ||
| }); | ||
|
|
||
| it("should have ProviderError as the thrown error", async () => { | ||
| const contract = await ethers.deployContract("Contract"); | ||
|
|
||
| let caughtError: Error | undefined; | ||
| try { | ||
| await contract.revertsWithReasonString(); | ||
| } catch (error) { | ||
| ensureError(error); | ||
| caughtError = error; | ||
| } | ||
|
|
||
| assert.ok( | ||
| caughtError !== undefined && caughtError.name === "ProviderError", | ||
| `Expected ProviderError, got ${caughtError?.name}`, | ||
| ); | ||
| assert.ok( | ||
| "code" in caughtError && caughtError.code === 3, | ||
| `Expected error code 3, got ${"code" in caughtError ? String(caughtError.code) : "undefined"}`, | ||
| ); | ||
| assert.ok( | ||
| caughtError.message.includes("some reason"), | ||
| `Expected message to include "some reason", got: "${caughtError.message}"`, | ||
| ); | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.0; | ||
|
|
||
| contract Revert { | ||
| function alwaysRevert() external pure { | ||
| revert("Intentional revert for testing purposes"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| import type { HardhatViemHelpers } from "@nomicfoundation/hardhat-viem/types"; | ||
| import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; | ||
| import type { JsonRpcServer } from "hardhat/types/network"; | ||
|
|
||
| import assert from "node:assert/strict"; | ||
| import { after, before, beforeEach, describe, it } from "node:test"; | ||
|
|
||
| import { useEphemeralFixtureProject } from "@nomicfoundation/hardhat-test-utils"; | ||
| import { ensureError } from "@nomicfoundation/hardhat-utils/error"; | ||
| import { createHardhatRuntimeEnvironment } from "hardhat/hre"; | ||
| import { | ||
| ContractFunctionExecutionError, | ||
| ContractFunctionRevertedError, | ||
| } from "viem"; | ||
|
|
||
| import hardhatViem from "../src/index.js"; | ||
|
|
||
| describe("revert error cause chain", () => { | ||
| useEphemeralFixtureProject("default-ts-project"); | ||
|
|
||
| describe("in-process hardhat network", () => { | ||
| let hre: HardhatRuntimeEnvironment; | ||
| let viem: HardhatViemHelpers; | ||
|
|
||
| before(async () => { | ||
| hre = await createHardhatRuntimeEnvironment({ | ||
| plugins: [hardhatViem], | ||
| }); | ||
|
|
||
| await hre.tasks.getTask("build").run({}); | ||
| }); | ||
|
|
||
| beforeEach(async () => { | ||
| ({ viem } = await hre.network.connect()); | ||
| }); | ||
|
|
||
| it("should have ContractFunctionExecutionError as the thrown error", async () => { | ||
| const contract = await viem.deployContract("Revert"); | ||
|
|
||
| let caughtError: Error | undefined; | ||
| try { | ||
| await contract.read.alwaysRevert(); | ||
| } catch (error) { | ||
| ensureError(error); | ||
| caughtError = error; | ||
| } | ||
|
|
||
| assert.ok( | ||
| caughtError !== undefined && | ||
| caughtError instanceof ContractFunctionExecutionError, | ||
| `Expected ContractFunctionExecutionError, got ${caughtError?.name}`, | ||
| ); | ||
| assert.ok( | ||
| caughtError.message.includes("Intentional revert for testing purposes"), | ||
| `Expected message to include "Intentional revert for testing purposes", got: "${caughtError.message}"`, | ||
| ); | ||
| }); | ||
|
|
||
| it("should have SolidityError in cause chain", async () => { | ||
| const contract = await viem.deployContract("Revert"); | ||
|
|
||
| let caughtError: Error | undefined; | ||
| try { | ||
| await contract.read.alwaysRevert(); | ||
| } catch (error) { | ||
| ensureError(error); | ||
| caughtError = error; | ||
| } | ||
|
|
||
| let current: Error | undefined = caughtError; | ||
| let solidityError: Error | undefined; | ||
|
|
||
| while (current !== undefined) { | ||
| if (current.name === "SolidityError") { | ||
| solidityError = current; | ||
| break; | ||
| } | ||
|
|
||
| current = current.cause instanceof Error ? current.cause : undefined; | ||
| } | ||
|
|
||
| assert.ok( | ||
| solidityError !== undefined, | ||
| "Expected to find SolidityError in the cause chain", | ||
| ); | ||
| assert.ok( | ||
| "code" in solidityError && solidityError.code === 3, | ||
| `Expected error code 3, got ${"code" in solidityError ? String(solidityError.code) : "undefined"}`, | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe("hardhat node (HTTP)", () => { | ||
| let hre: HardhatRuntimeEnvironment; | ||
| let viem: HardhatViemHelpers; | ||
| let server: JsonRpcServer; | ||
| let address: string; | ||
| let port: number; | ||
|
|
||
| before(async () => { | ||
|
ChristopherDedominici marked this conversation as resolved.
|
||
| hre = await createHardhatRuntimeEnvironment({ | ||
| plugins: [hardhatViem], | ||
| networks: { | ||
| localhost: { type: "http", url: "http://127.0.0.1:0" }, | ||
| }, | ||
| }); | ||
|
|
||
| await hre.tasks.getTask("build").run({}); | ||
|
|
||
| server = await hre.network.createServer(); | ||
| ({ address, port } = await server.listen()); | ||
| }); | ||
|
|
||
| after(async () => { | ||
| await server.close(); | ||
| }); | ||
|
|
||
| beforeEach(async () => { | ||
| ({ viem } = await hre.network.connect({ | ||
| network: "localhost", | ||
| override: { | ||
| url: `http://${address}:${port}`, | ||
| }, | ||
|
Comment on lines
+110
to
+123
|
||
| })); | ||
| }); | ||
|
|
||
| it("should have ContractFunctionExecutionError as the thrown error", async () => { | ||
| const contract = await viem.deployContract("Revert"); | ||
|
|
||
| let caughtError: Error | undefined; | ||
| try { | ||
| await contract.read.alwaysRevert(); | ||
| } catch (error) { | ||
| ensureError(error); | ||
| caughtError = error; | ||
| } | ||
|
|
||
| assert.ok( | ||
| caughtError !== undefined && | ||
| caughtError instanceof ContractFunctionExecutionError, | ||
| `Expected ContractFunctionExecutionError, got ${caughtError?.name}`, | ||
| ); | ||
| assert.ok( | ||
| caughtError.message.includes("Intentional revert for testing purposes"), | ||
| `Expected message to include "Intentional revert for testing purposes", got: "${caughtError.message}"`, | ||
| ); | ||
|
|
||
| let current: Error | undefined = caughtError; | ||
| let errorWithCode3: Error | undefined; | ||
|
|
||
| while (current !== undefined) { | ||
| if ("code" in current && current.code === 3) { | ||
| errorWithCode3 = current; | ||
| break; | ||
| } | ||
|
|
||
| current = current.cause instanceof Error ? current.cause : undefined; | ||
| } | ||
|
|
||
| assert.ok( | ||
| errorWithCode3 !== undefined, | ||
| "Expected to find an error with code 3 in the cause chain", | ||
| ); | ||
|
|
||
| const cause = caughtError.cause; | ||
| ensureError(cause, ContractFunctionRevertedError); | ||
| assert.ok( | ||
| "raw" in cause && cause.raw !== undefined, | ||
| "Expected raw revert data to be present on ContractFunctionRevertedError", | ||
| ); | ||
| }); | ||
|
ChristopherDedominici marked this conversation as resolved.
|
||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this tests should be present in
hardhat-viem, not this package.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done here: 5cb67ab