diff --git a/.changeset/funny-socks-worry.md b/.changeset/funny-socks-worry.md new file mode 100644 index 00000000000..7e31295a781 --- /dev/null +++ b/.changeset/funny-socks-worry.md @@ -0,0 +1,5 @@ +--- +"hardhat": patch +--- + +Added support for EDR structured Solidity test cheatcode errors. diff --git a/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.ts b/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.ts index 1585890920c..bd19497235c 100644 --- a/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.ts +++ b/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.ts @@ -25,10 +25,12 @@ import type { InternalFunctionCallStackEntry, ContractCallRunOutOfGasError, CheatcodeErrorStackTraceEntry, + CheatcodeErrorDetails, } from "@nomicfoundation/edr"; import { StackTraceEntryType, + CheatcodeErrorCode, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, @@ -42,6 +44,7 @@ import { export { SourceReference, StackTraceEntryType, + CheatcodeErrorCode, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, @@ -77,6 +80,7 @@ export type { ContractTooLargeErrorStackTraceEntry, InternalFunctionCallStackEntry, ContractCallRunOutOfGasError, + CheatcodeErrorDetails, }; export type SolidityStackTraceEntry = diff --git a/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts b/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts index 8357cb5ee01..d2f83616baa 100644 --- a/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts +++ b/packages/hardhat/src/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts @@ -11,6 +11,7 @@ import { panicErrorCodeToMessage } from "@nomicfoundation/hardhat-utils/panic-er import { StackTraceEntryType, + CheatcodeErrorCode, CONSTRUCTOR_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, @@ -266,9 +267,25 @@ function getMessageFromLastStackTraceEntry( return `VM Exception while processing transaction: ${panicMessage}`; case StackTraceEntryType.CUSTOM_ERROR: - case StackTraceEntryType.CHEATCODE_ERROR: return `VM Exception while processing transaction: ${stackTraceEntry.message}`; + case StackTraceEntryType.CHEATCODE_ERROR: { + let message = stackTraceEntry.message; + + if (stackTraceEntry.details !== undefined) { + switch (stackTraceEntry.details.code) { + case CheatcodeErrorCode.UnsupportedCheatcode: + message = `Cheatcode '${stackTraceEntry.details.cheatcode}' is not supported by Hardhat.`; + break; + case CheatcodeErrorCode.MissingCheatcode: + message = `Cheatcode '${stackTraceEntry.details.cheatcode}' is not yet available in this version of Hardhat.`; + break; + } + } + + return `VM Exception while processing transaction: ${message}`; + } + case StackTraceEntryType.OTHER_EXECUTION_ERROR: // TODO: What if there was returnData? return `Transaction reverted and Hardhat couldn't infer the reason.`; diff --git a/packages/hardhat/src/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.ts b/packages/hardhat/src/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.ts index 0486cd88c5e..7fc18527ac0 100644 --- a/packages/hardhat/src/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.ts +++ b/packages/hardhat/src/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.ts @@ -2,7 +2,10 @@ import type { SolidityStackTraceEntry } from "../network-manager/edr/stack-trace import { panicErrorCodeToMessage } from "@nomicfoundation/hardhat-utils/panic-errors"; -import { StackTraceEntryType } from "../network-manager/edr/stack-traces/solidity-stack-trace.js"; +import { + StackTraceEntryType, + CheatcodeErrorCode, +} from "../network-manager/edr/stack-traces/solidity-stack-trace.js"; export function getMessageFromLastStackTraceEntry( stackTraceEntry: SolidityStackTraceEntry, @@ -64,8 +67,18 @@ export function getMessageFromLastStackTraceEntry( case StackTraceEntryType.CUSTOM_ERROR: return stackTraceEntry.message; - case StackTraceEntryType.CHEATCODE_ERROR: + case StackTraceEntryType.CHEATCODE_ERROR: { + if (stackTraceEntry.details !== undefined) { + switch (stackTraceEntry.details.code) { + case CheatcodeErrorCode.UnsupportedCheatcode: + return `Cheatcode '${stackTraceEntry.details.cheatcode}' is not supported by Hardhat.`; + case CheatcodeErrorCode.MissingCheatcode: + return `Cheatcode '${stackTraceEntry.details.cheatcode}' is not yet available in this version of Hardhat.`; + } + } + return stackTraceEntry.message; + } case StackTraceEntryType.CONTRACT_TOO_LARGE_ERROR: return "Trying to deploy a contract whose code is too large"; diff --git a/packages/hardhat/test/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts b/packages/hardhat/test/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts index c944eb104f7..89a4abfebc4 100644 --- a/packages/hardhat/test/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts +++ b/packages/hardhat/test/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.ts @@ -1,12 +1,14 @@ -import type { - SolidityStackTraceEntry, - StackTraceEntryType, -} from "../../../../../../src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.js"; +import type { SolidityStackTraceEntry } from "../../../../../../src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.js"; import assert from "node:assert/strict"; import { describe, it } from "node:test"; -import { SolidityCallSite } from "../../../../../../src/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.js"; +import { CheatcodeErrorCode, StackTraceEntryType } from "@nomicfoundation/edr"; + +import { + createSolidityErrorWithStackTrace, + SolidityCallSite, +} from "../../../../../../src/internal/builtin-plugins/network-manager/edr/stack-traces/stack-trace-solidity-errors.js"; describe("SolidityCallSite", function () { describe("toString", function () { @@ -58,3 +60,76 @@ describe("SolidityCallSite", function () { }); }); }); + +describe("createSolidityErrorWithStackTrace", () => { + const dummySourceReference = { + sourceName: "Test.t.sol", + sourceContent: "", + line: 1, + range: [0, 0], + }; + + describe("CHEATCODE_ERROR", () => { + it("returns the raw message when details is undefined", () => { + const entry: SolidityStackTraceEntry = { + type: StackTraceEntryType.CHEATCODE_ERROR, + message: "cheatcode 'broadcast(address)' is not supported", + sourceReference: dummySourceReference, + }; + + const error = createSolidityErrorWithStackTrace( + "fallback", + [entry], + "0x", + ); + assert.equal( + error.message, + "VM Exception while processing transaction: cheatcode 'broadcast(address)' is not supported", + ); + }); + + it("returns a Hardhat-specific message for unsupported cheatcodes", () => { + const entry: SolidityStackTraceEntry = { + type: StackTraceEntryType.CHEATCODE_ERROR, + message: "cheatcode 'broadcast(address)' is not supported", + sourceReference: dummySourceReference, + details: { + code: CheatcodeErrorCode.UnsupportedCheatcode, + cheatcode: "broadcast(address)", + }, + }; + + const error = createSolidityErrorWithStackTrace( + "fallback", + [entry], + "0x", + ); + assert.equal( + error.message, + "VM Exception while processing transaction: Cheatcode 'broadcast(address)' is not supported by Hardhat.", + ); + }); + + it("returns a Hardhat-specific message for missing cheatcodes", () => { + const entry: SolidityStackTraceEntry = { + type: StackTraceEntryType.CHEATCODE_ERROR, + message: "unknown cheatcode with selector '0x12345678'", + sourceReference: dummySourceReference, + details: { + code: CheatcodeErrorCode.MissingCheatcode, + cheatcode: "someNewCheatcode(uint256)", + }, + }; + + const error = createSolidityErrorWithStackTrace( + "fallback", + [entry], + "0x", + ); + assert.equal( + error.message, + "VM Exception while processing transaction: Cheatcode 'someNewCheatcode(uint256)' is not yet available in this version of Hardhat.", + ); + }); + }); +}); diff --git a/packages/hardhat/test/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.ts b/packages/hardhat/test/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.ts new file mode 100644 index 00000000000..bdaf4ebc3eb --- /dev/null +++ b/packages/hardhat/test/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.ts @@ -0,0 +1,66 @@ +import type { SolidityStackTraceEntry } from "../../../../src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.js"; + +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; + +import { CheatcodeErrorCode, StackTraceEntryType } from "@nomicfoundation/edr"; + +import { getMessageFromLastStackTraceEntry } from "../../../../src/internal/builtin-plugins/solidity-test/stack-trace-solidity-errors.js"; + +describe("getMessageFromLastStackTraceEntry", () => { + const dummySourceReference = { + sourceName: "Test.t.sol", + sourceContent: "", + line: 1, + range: [0, 0], + }; + + describe("CHEATCODE_ERROR", () => { + it("returns the raw message when details is undefined", () => { + const entry: SolidityStackTraceEntry = { + type: StackTraceEntryType.CHEATCODE_ERROR, + message: "cheatcode 'broadcast(address)' is not supported", + sourceReference: dummySourceReference, + }; + + assert.equal( + getMessageFromLastStackTraceEntry(entry), + "cheatcode 'broadcast(address)' is not supported", + ); + }); + + it("returns a Hardhat-specific message for unsupported cheatcodes", () => { + const entry: SolidityStackTraceEntry = { + type: StackTraceEntryType.CHEATCODE_ERROR, + message: "cheatcode 'broadcast(address)' is not supported", + sourceReference: dummySourceReference, + details: { + code: CheatcodeErrorCode.UnsupportedCheatcode, + cheatcode: "broadcast(address)", + }, + }; + + assert.equal( + getMessageFromLastStackTraceEntry(entry), + "Cheatcode 'broadcast(address)' is not supported by Hardhat.", + ); + }); + + it("returns a Hardhat-specific message for missing cheatcodes", () => { + const entry: SolidityStackTraceEntry = { + type: StackTraceEntryType.CHEATCODE_ERROR, + message: "unknown cheatcode with selector '0x12345678'", + sourceReference: dummySourceReference, + details: { + code: CheatcodeErrorCode.MissingCheatcode, + cheatcode: "someNewCheatcode(uint256)", + }, + }; + + assert.equal( + getMessageFromLastStackTraceEntry(entry), + "Cheatcode 'someNewCheatcode(uint256)' is not yet available in this version of Hardhat.", + ); + }); + }); +});