From 62c57182c4c1cbc63729a9fc90f88ef7af353cb8 Mon Sep 17 00:00:00 2001 From: Wodann Date: Tue, 16 Dec 2025 03:37:42 +0000 Subject: [PATCH 1/5] refactor: remove custom debug_* logic --- .../hardhat-network/provider/provider.ts | 6 -- .../provider/utils/convertToEdr.ts | 56 ------------------- 2 files changed, 62 deletions(-) diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts index 7e4276a5896..bd8d110bdd8 100644 --- a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts @@ -67,7 +67,6 @@ import { MempoolOrder, } from "./node-types"; import { - edrRpcDebugTraceToHardhat, edrTracingMessageResultToMinimalEVMResult, edrTracingMessageToMinimalMessage, edrTracingStepToMinimalInterpreterStep, @@ -479,11 +478,6 @@ export class EdrProviderWrapper // e.g. `HardhatNetwork/2.19.0/@nomicfoundation/edr/0.2.0-dev` if (args.method === "web3_clientVersion") { return clientVersion(response.result); - } else if ( - args.method === "debug_traceTransaction" || - args.method === "debug_traceCall" - ) { - return edrRpcDebugTraceToHardhat(response.result); } else { return response.result; } diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts index 7fc17b29c04..4dc5ebf3db2 100644 --- a/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts @@ -174,62 +174,6 @@ export function ethereumjsMempoolOrderToEdrMineOrdering( } } -export function edrRpcDebugTraceToHardhat( - rpcDebugTrace: DebugTraceResult -): RpcDebugTraceOutput { - const structLogs = rpcDebugTrace.structLogs.map((log) => { - const result: RpcStructLog = { - depth: Number(log.depth), - gas: Number(log.gas), - gasCost: Number(log.gasCost), - op: log.opName, - pc: Number(log.pc), - }; - - if (log.memory !== undefined) { - result.memory = log.memory; - } - - if (log.stack !== undefined) { - // Remove 0x prefix which is required by EIP-3155, but not expected by Hardhat. - result.stack = log.stack?.map((item) => item.slice(2)); - } - - if (log.storage !== undefined) { - result.storage = Object.fromEntries( - Object.entries(log.storage).map(([key, value]) => { - return [key.slice(2), value.slice(2)]; - }) - ); - } - - if (log.error !== undefined) { - result.error = { - message: log.error, - }; - } - - return result; - }); - - // REVM trace adds initial STOP that Hardhat doesn't expect - if (structLogs.length > 0 && structLogs[0].op === "STOP") { - structLogs.shift(); - } - - let returnValue = rpcDebugTrace.output?.toString() ?? ""; - if (returnValue === "0x") { - returnValue = ""; - } - - return { - failed: !rpcDebugTrace.pass, - gas: Number(rpcDebugTrace.gasUsed), - returnValue, - structLogs, - }; -} - export function edrTracingStepToMinimalInterpreterStep( step: TracingStep ): MinimalInterpreterStep { From e390f53d635bb447156c4b831ab148d961510106 Mon Sep 17 00:00:00 2001 From: Wodann Date: Mon, 2 Feb 2026 22:47:22 +0000 Subject: [PATCH 2/5] WIP: refactor: unify tracing architecture --- .../hardhat-network/provider/provider.ts | 131 ++++++++++-------- 1 file changed, 72 insertions(+), 59 deletions(-) diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts index bd8d110bdd8..f8b7c5bc5e6 100644 --- a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts @@ -386,75 +386,88 @@ export class EdrProviderWrapper response = responseObject.data; } - const needsTraces = - this._node._vm.evm.events.eventNames().length > 0 || - this._node._vm.events.eventNames().length > 0; - - if (needsTraces) { - const rawTraces = responseObject.traces; - for (const rawTrace of rawTraces) { - // For other consumers in JS we need to marshall the entire trace over FFI - const trace = rawTrace.trace; - - // beforeTx event - if (this._node._vm.events.listenerCount("beforeTx") > 0) { - this._node._vm.events.emit("beforeTx"); - } - - for (const traceItem of trace) { - // step event - if ("pc" in traceItem) { - if (this._node._vm.evm.events.listenerCount("step") > 0) { - this._node._vm.evm.events.emit( - "step", - edrTracingStepToMinimalInterpreterStep(traceItem) - ); - } - } - // afterMessage event - else if ("executionResult" in traceItem) { - if (this._node._vm.evm.events.listenerCount("afterMessage") > 0) { - this._node._vm.evm.events.emit( - "afterMessage", - edrTracingMessageResultToMinimalEVMResult(traceItem) - ); - } - } - // beforeMessage event - else { - if (this._node._vm.evm.events.listenerCount("beforeMessage") > 0) { - this._node._vm.evm.events.emit( - "beforeMessage", - edrTracingMessageToMinimalMessage(traceItem) - ); - } - } - } - - // afterTx event - if (this._node._vm.events.listenerCount("afterTx") > 0) { - this._node._vm.events.emit("afterTx"); - } - } - } + // TODO: re-enable tracing events once EDR adds backwards compatibility support + // const needsTraces = + // this._node._vm.evm.events.eventNames().length > 0 || + // this._node._vm.events.eventNames().length > 0; + + // if (needsTraces) { + // const rawTraces = responseObject.traces; + // for (const rawTrace of rawTraces) { + // // For other consumers in JS we need to marshall the entire trace over FFI + // const trace = rawTrace.trace; + + // // beforeTx event + // if (this._node._vm.events.listenerCount("beforeTx") > 0) { + // this._node._vm.events.emit("beforeTx"); + // } + + // for (const traceItem of trace) { + // // step event + // if ("pc" in traceItem) { + // if (this._node._vm.evm.events.listenerCount("step") > 0) { + // this._node._vm.evm.events.emit( + // "step", + // edrTracingStepToMinimalInterpreterStep(traceItem) + // ); + // } + // } + // // afterMessage event + // else if ("executionResult" in traceItem) { + // if (this._node._vm.evm.events.listenerCount("afterMessage") > 0) { + // this._node._vm.evm.events.emit( + // "afterMessage", + // edrTracingMessageResultToMinimalEVMResult(traceItem) + // ); + // } + // } + // // beforeMessage event + // else { + // if (this._node._vm.evm.events.listenerCount("beforeMessage") > 0) { + // this._node._vm.evm.events.emit( + // "beforeMessage", + // edrTracingMessageToMinimalMessage(traceItem) + // ); + // } + // } + // } + + // // afterTx event + // if (this._node._vm.events.listenerCount("afterTx") > 0) { + // this._node._vm.events.emit("afterTx"); + // } + // } + // } if (isErrorResponse(response)) { let error; - let stackTrace: SolidityStackTrace | null = null; - try { - stackTrace = responseObject.stackTrace(); - } catch (e) { - log("Failed to get stack trace: %O", e); - } + const stackTrace = responseObject.stackTrace(); - if (stackTrace !== null) { - error = encodeSolidityStackTrace(response.error.message, stackTrace); + if (stackTrace?.kind === "StackTrace") { + error = encodeSolidityStackTrace( + response.error.message, + stackTrace.entries + ); // Pass data and transaction hash from the original error (error as any).data = response.error.data?.data ?? undefined; (error as any).transactionHash = response.error.data?.transactionHash ?? undefined; } else { + if (stackTrace !== null) { + switch (stackTrace.kind) { + case "UnexpectedError": + log( + "Failed to get stack trace due to error: %O", + stackTrace.errorMessage + ); + break; + case "HeuristicFailed": + log("Failed to get stack trace due to failing heuristics"); + break; + } + } + if (response.error.code === InvalidArgumentsError.CODE) { error = new InvalidArgumentsError(response.error.message); } else { From 6746fa4334c874980cf18925c20d8180f2e20006 Mon Sep 17 00:00:00 2001 From: Wodann Date: Mon, 2 Feb 2026 22:51:08 +0000 Subject: [PATCH 3/5] fix: outdated typing on skipped TS test --- packages/hardhat-core/test/builtin-tasks/compile.ts | 4 ++-- packages/hardhat-core/test/helpers/compilation.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/hardhat-core/test/builtin-tasks/compile.ts b/packages/hardhat-core/test/builtin-tasks/compile.ts index f89635e1237..b27c5eb1b80 100644 --- a/packages/hardhat-core/test/builtin-tasks/compile.ts +++ b/packages/hardhat-core/test/builtin-tasks/compile.ts @@ -1,4 +1,4 @@ -import { getLatestSupportedSolcVersion } from "@nomicfoundation/edr"; +import { latestSupportedSolidityVersion } from "@nomicfoundation/edr"; import { assert, expect } from "chai"; import ci from "ci-info"; import * as fsExtra from "fs-extra"; @@ -57,7 +57,7 @@ describe("compile task", function () { // Test to check that the last version of solc is being tested const userConfigSolcVersion = this.env.userConfig.solidity; - const lastSolcVersion = getLatestSupportedSolcVersion(); + const lastSolcVersion = latestSupportedSolidityVersion(); assert.equal( userConfigSolcVersion, diff --git a/packages/hardhat-core/test/helpers/compilation.ts b/packages/hardhat-core/test/helpers/compilation.ts index 0ec5f3a4b3c..fde07acfd13 100644 --- a/packages/hardhat-core/test/helpers/compilation.ts +++ b/packages/hardhat-core/test/helpers/compilation.ts @@ -1,4 +1,4 @@ -import { getLatestSupportedSolcVersion } from "@nomicfoundation/edr"; +import { latestSupportedSolidityVersion } from "@nomicfoundation/edr"; import semver from "semver"; import { @@ -161,7 +161,7 @@ async function downloadCompiler(solidityVersion: string): Promise { } export const getNextUnsupportedVersion = () => - semver.inc(getLatestSupportedSolcVersion(), "patch")!; + semver.inc(latestSupportedSolidityVersion(), "patch")!; export const getNextNextUnsupportedVersion = () => semver.inc(getNextUnsupportedVersion(), "patch")!; From b2896f357eaf387210c6e7d00eac4a96ff5e56ea Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 19 Feb 2026 18:13:56 +0000 Subject: [PATCH 4/5] refactor: use new traces API of EDR --- .../hardhat-network/provider/provider.ts | 100 +++++++++--------- .../provider/utils/convertToEdr.ts | 44 +++----- 2 files changed, 63 insertions(+), 81 deletions(-) diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts index f8b7c5bc5e6..791e9b4bbc5 100644 --- a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts @@ -386,58 +386,54 @@ export class EdrProviderWrapper response = responseObject.data; } - // TODO: re-enable tracing events once EDR adds backwards compatibility support - // const needsTraces = - // this._node._vm.evm.events.eventNames().length > 0 || - // this._node._vm.events.eventNames().length > 0; - - // if (needsTraces) { - // const rawTraces = responseObject.traces; - // for (const rawTrace of rawTraces) { - // // For other consumers in JS we need to marshall the entire trace over FFI - // const trace = rawTrace.trace; - - // // beforeTx event - // if (this._node._vm.events.listenerCount("beforeTx") > 0) { - // this._node._vm.events.emit("beforeTx"); - // } - - // for (const traceItem of trace) { - // // step event - // if ("pc" in traceItem) { - // if (this._node._vm.evm.events.listenerCount("step") > 0) { - // this._node._vm.evm.events.emit( - // "step", - // edrTracingStepToMinimalInterpreterStep(traceItem) - // ); - // } - // } - // // afterMessage event - // else if ("executionResult" in traceItem) { - // if (this._node._vm.evm.events.listenerCount("afterMessage") > 0) { - // this._node._vm.evm.events.emit( - // "afterMessage", - // edrTracingMessageResultToMinimalEVMResult(traceItem) - // ); - // } - // } - // // beforeMessage event - // else { - // if (this._node._vm.evm.events.listenerCount("beforeMessage") > 0) { - // this._node._vm.evm.events.emit( - // "beforeMessage", - // edrTracingMessageToMinimalMessage(traceItem) - // ); - // } - // } - // } - - // // afterTx event - // if (this._node._vm.events.listenerCount("afterTx") > 0) { - // this._node._vm.events.emit("afterTx"); - // } - // } - // } + const needsTraces = + this._node._vm.evm.events.eventNames().length > 0 || + this._node._vm.events.eventNames().length > 0; + + if (needsTraces) { + const rawTraces = responseObject.traces(); + for (const trace of rawTraces) { + // beforeTx event + if (this._node._vm.events.listenerCount("beforeTx") > 0) { + this._node._vm.events.emit("beforeTx"); + } + + for (const traceItem of trace) { + // step event + if ("pc" in traceItem) { + if (this._node._vm.evm.events.listenerCount("step") > 0) { + this._node._vm.evm.events.emit( + "step", + edrTracingStepToMinimalInterpreterStep(traceItem) + ); + } + } + // afterMessage event + else if ("execResult" in traceItem) { + if (this._node._vm.evm.events.listenerCount("afterMessage") > 0) { + this._node._vm.evm.events.emit( + "afterMessage", + edrTracingMessageResultToMinimalEVMResult(traceItem) + ); + } + } + // beforeMessage event + else { + if (this._node._vm.evm.events.listenerCount("beforeMessage") > 0) { + this._node._vm.evm.events.emit( + "beforeMessage", + edrTracingMessageToMinimalMessage(traceItem) + ); + } + } + } + + // afterTx event + if (this._node._vm.events.listenerCount("afterTx") > 0) { + this._node._vm.events.emit("afterTx"); + } + } + } if (isErrorResponse(response)) { let error; diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts index 4dc5ebf3db2..c6be433f86f 100644 --- a/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts @@ -178,53 +178,39 @@ export function edrTracingStepToMinimalInterpreterStep( step: TracingStep ): MinimalInterpreterStep { const minimalInterpreterStep: MinimalInterpreterStep = { - pc: Number(step.pc), + pc: step.pc, depth: step.depth, opcode: { - name: step.opcode, + name: step.opcode.name, }, stack: step.stack, + memory: step.memory, }; - if (step.memory !== undefined) { - minimalInterpreterStep.memory = step.memory; - } - return minimalInterpreterStep; } export function edrTracingMessageResultToMinimalEVMResult( tracingMessageResult: TracingMessageResult ): MinimalEVMResult { - const { result, contractAddress } = tracingMessageResult.executionResult; - - // only SuccessResult has logs - const success = "logs" in result; + const execResult = tracingMessageResult.execResult; const minimalEVMResult: MinimalEVMResult = { execResult: { - executionGasUsed: result.gasUsed, - success, + success: execResult.success, + executionGasUsed: execResult.executionGasUsed, + contractAddress: + execResult.contractAddress !== undefined + ? new Address(execResult.contractAddress) + : undefined, + reason: execResult.reason, + output: + execResult.output !== undefined + ? Buffer.from(execResult.output) + : undefined, }, }; - // only success and exceptional halt have reason - if ("reason" in result) { - minimalEVMResult.execResult.reason = result.reason; - } - if ("output" in result) { - const { output } = result; - if (output instanceof Uint8Array) { - minimalEVMResult.execResult.output = Buffer.from(output); - } else { - minimalEVMResult.execResult.output = Buffer.from(output.returnValue); - } - } - - if (contractAddress !== undefined) { - minimalEVMResult.execResult.contractAddress = new Address(contractAddress); - } - return minimalEVMResult; } From c46cf5b4e132d456e1ebebe0fd40a669d08f3a2d Mon Sep 17 00:00:00 2001 From: Wodann Date: Fri, 20 Feb 2026 17:04:03 +0000 Subject: [PATCH 5/5] fix: always enable traces --- .../src/internal/hardhat-network/provider/provider.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts index 791e9b4bbc5..c14f1cb43d3 100644 --- a/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts @@ -21,7 +21,11 @@ import type { AccountOverride, } from "@nomicfoundation/edr"; import { privateToAddress } from "@ethereumjs/util"; -import { ContractDecoder, precompileP256Verify } from "@nomicfoundation/edr"; +import { + ContractDecoder, + IncludeTraces, + precompileP256Verify, +} from "@nomicfoundation/edr"; import picocolors from "picocolors"; import debug from "debug"; import { EventEmitter } from "events"; @@ -267,7 +271,9 @@ export class EdrProviderWrapper }, }, networkId: BigInt(config.networkId), - observability: {}, + observability: { + includeCallTraces: IncludeTraces.All, + }, ownedAccounts, // Turn off the Osaka EIP-7825 per transaction gas limit for HH2 // when being run from `solidity-coverage`.