diff --git a/contracts/Tracing.sol b/contracts/Tracing.sol index 2f6a3b1..033b941 100644 --- a/contracts/Tracing.sol +++ b/contracts/Tracing.sol @@ -28,6 +28,18 @@ contract TracingCaller { this.start(counter - 1); } + function destruct() external { + address recipient = msg.sender; + assembly { + selfdestruct(recipient) + } + } + + function create_and_destruct() public { + TracingCallee newCallee = new TracingCallee(); + newCallee.destruct(); + } + function create() external returns (address) { TracingCallee newCallee = new TracingCallee(); return address(newCallee); @@ -52,6 +64,13 @@ contract TracingCallee { emit CalleeCalled(counter); } + function destruct() external { + address recipient = msg.sender; + assembly { + selfdestruct(recipient) + } + } + function failingFunction() external payable { require(false, "This function always fails"); } diff --git a/deno.json b/deno.json index e054ce6..b517812 100644 --- a/deno.json +++ b/deno.json @@ -23,15 +23,15 @@ "imports": { "@std/cli": "jsr:@std/cli@^1.0.23", "@std/encoding": "jsr:@std/encoding@^1.0.10", - "viem": "npm:viem@^2.38.3", - "viem/": "npm:/viem@^2.38.3/", + "viem": "npm:viem@^2.39.0", + "viem/": "npm:/viem@^2.39.0/", "solc": "npm:solc@^0.8.30", - "@parity/resolc": "npm:@parity/resolc@^0.4.1", + "@parity/resolc": "npm:@parity/resolc@^0.5.0", "@std/assert": "jsr:@std/assert@^1.0.15", "@std/expect": "jsr:@std/expect@^1.0.17", "@std/testing": "jsr:@std/testing@^1.0.16", "@std/path": "jsr:@std/path@^1.1.2", - "@std/log": "jsr:@std/log@^0.224" + "@std/log": "jsr:@std/log@^0.224.14" }, "compilerOptions": { "strict": true diff --git a/deno.lock b/deno.lock index 9d5996d..5eb4757 100644 --- a/deno.lock +++ b/deno.lock @@ -13,14 +13,14 @@ "jsr:@std/internal@^1.0.10": "1.0.12", "jsr:@std/internal@^1.0.12": "1.0.12", "jsr:@std/io@~0.225.2": "0.225.2", - "jsr:@std/log@0.224": "0.224.14", + "jsr:@std/log@~0.224.14": "0.224.14", "jsr:@std/path@^1.1.1": "1.1.2", "jsr:@std/path@^1.1.2": "1.1.2", "jsr:@std/testing@^1.0.16": "1.0.16", - "npm:@parity/resolc@~0.4.1": "0.4.1", + "npm:@parity/resolc@0.5": "0.5.0", "npm:@types/node@*": "24.2.0", "npm:solc@~0.8.30": "0.8.30", - "npm:viem@^2.38.3": "2.38.3_ws@8.18.3" + "npm:viem@^2.39.0": "2.39.0_ws@8.18.3" }, "jsr": { "@std/assert@1.0.15": { @@ -104,10 +104,10 @@ "@noble/hashes@1.8.0": { "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==" }, - "@parity/resolc@0.4.1": { - "integrity": "sha512-GdeJnCVlnRrrHjQk0vr+8mmkiJ2LiLwe7CEQBSKslHqK3t7dhjJORR1kfxoksTM4LSyftHUYmPszeIY0r1pv3w==", + "@parity/resolc@0.5.0": { + "integrity": "sha512-zSkg5xfkK/rLj+vvf2jinSFX/ih/gJ44DOV4r6Gksz7k0t1Jpg3GtchCnCfGA/aQZXoRWRsCradX3JqE6wgl4w==", "dependencies": [ - "@types/node@22.18.11", + "@types/node@22.19.1", "commander@13.1.0", "package-json", "resolve-pkg", @@ -150,8 +150,8 @@ "@scure/base" ] }, - "@types/node@22.18.11": { - "integrity": "sha512-Gd33J2XIrXurb+eT2ktze3rJAfAp9ZNjlBdh4SVgyrKEOADwCbdUDaK7QgJno8Ue4kcajscsKqu6n8OBG3hhCQ==", + "@types/node@22.19.1": { + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dependencies": [ "undici-types@6.21.0" ] @@ -205,8 +205,8 @@ "js-sha3@0.8.0": { "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, - "ky@1.12.0": { - "integrity": "sha512-YRLmSUHCwOJRBMArtqMRLOmO7fewn3yOoui6aB8ERkRVXupa0UiaQaKbIXteMt4jUElhbdqTMsLFHs8APxxUoQ==" + "ky@1.14.0": { + "integrity": "sha512-Rczb6FMM6JT0lvrOlP5WUOCB7s9XKxzwgErzhKlKde1bEV90FXplV1o87fpt4PU/asJFiqjYJxAJyzJhcrxOsQ==" }, "memorystream@0.3.1": { "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==" @@ -309,8 +309,8 @@ "undici-types@7.10.0": { "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" }, - "viem@2.38.3_ws@8.18.3": { - "integrity": "sha512-By2TutLv07iNHHtWqHHzjGipevYsfGqT7KQbGEmqLco1qTJxKnvBbSviqiu6/v/9REV6Q/FpmIxf2Z7/l5AbcQ==", + "viem@2.39.0_ws@8.18.3": { + "integrity": "sha512-rCN+IfnMESlrg/iPyyVL+M9NS/BHzyyNy72470tFmbTuscY3iPaZGMtJDcHKKV8TC6HV9DjWk0zWX6cpu0juyA==", "dependencies": [ "@noble/curves", "@noble/hashes", @@ -332,12 +332,12 @@ "jsr:@std/cli@^1.0.23", "jsr:@std/encoding@^1.0.10", "jsr:@std/expect@^1.0.17", - "jsr:@std/log@0.224", + "jsr:@std/log@~0.224.14", "jsr:@std/path@^1.1.2", "jsr:@std/testing@^1.0.16", - "npm:@parity/resolc@~0.4.1", + "npm:@parity/resolc@0.5", "npm:solc@~0.8.30", - "npm:viem@^2.38.3" + "npm:viem@^2.39.0" ] } } diff --git a/src/__snapshots__/all-tests.ts.snap b/src/__snapshots__/all-tests.ts.snap index 5f0cd5e..b75e153 100644 --- a/src/__snapshots__/all-tests.ts.snap +++ b/src/__snapshots__/all-tests.ts.snap @@ -467,6 +467,73 @@ snapshot[`call-trace debug_traceCall 1`] = ` } `; +snapshot[`call-trace selfdestruct 1`] = ` +{ + calls: [ + { + from: "", + gas: "", + gasUsed: "", + input: "0x", + to: "0x0000000000000000000000000000000000000000", + type: "SELFDESTRUCT", + value: "", + }, + ], + from: "0x0000000000000000000000000000000000000000", + gas: "", + gasUsed: "", + input: "0x2b68b9c6", + to: "", + type: "CALL", + value: "0x0", +} +`; + +snapshot[`call-trace create_and_destruct 1`] = ` +{ + calls: [ + { + from: "", + gas: "", + gasUsed: "", + input: "", + output: "", + to: "", + type: "CREATE", + value: "0x0", + }, + { + calls: [ + { + from: "", + gas: "", + gasUsed: "", + input: "0x", + to: "", + type: "SELFDESTRUCT", + value: "", + }, + ], + from: "", + gas: "", + gasUsed: "", + input: "0x2b68b9c6", + to: "", + type: "CALL", + value: "0x0", + }, + ], + from: "0x0000000000000000000000000000000000000000", + gas: "", + gasUsed: "", + input: "0xfadbb018", + to: "", + type: "CALL", + value: "0x0", +} +`; + snapshot[`prestate deploy_contract.diff 1`] = ` { post: { @@ -1050,3 +1117,57 @@ snapshot[`prestate trace_tx_write_storage_twice.no_diff 1`] = ` }, } `; + +snapshot[`prestate selfdestruct.diff 1`] = ` +{ + post: { + "": { + balance: "", + }, + }, + pre: { + "": { + balance: "", + code: "", + nonce: "", + }, + }, +} +`; + +snapshot[`prestate selfdestruct.no_diff 1`] = ` +{ + "": { + balance: "", + code: "", + nonce: "", + }, +} +`; + +snapshot[`prestate create_and_destruct.diff 1`] = ` +{ + post: { + "": { + nonce: "", + }, + }, + pre: { + "": { + balance: "", + code: "", + nonce: "", + }, + }, +} +`; + +snapshot[`prestate create_and_destruct.no_diff 1`] = ` +{ + "": { + balance: "", + code: "", + nonce: "", + }, +} +`; diff --git a/src/test-setup.ts b/src/test-setup.ts index 0e59bc3..ee57438 100644 --- a/src/test-setup.ts +++ b/src/test-setup.ts @@ -57,6 +57,14 @@ export async function setupTests() { ] await killProcessOnPort(8545) + + const versionOutput = await new Deno.Command(geth, { + args: ['version'], + stdout: 'piped', + stderr: 'piped', + }).output() + const version = new TextDecoder().decode(versionOutput.stdout) + console.log(version) console.log('🚀 Start geth...') const gethProcess = new Deno.Command(geth, { args: gethArgs, diff --git a/src/tracing-call-trace.test.ts b/src/tracing-call-trace.test.ts index 74bd5ab..f6ec238 100644 --- a/src/tracing-call-trace.test.ts +++ b/src/tracing-call-trace.test.ts @@ -239,3 +239,43 @@ Deno.test('call-trace debug_traceCall', opts, async (t) => { await matchFixture(t, res) }) + +Deno.test('call-trace selfdestruct', opts, async (t) => { + const tracingCallerAddr = await getTracingCallerAddr() + const res = await env.debugClient.traceCall( + { + to: tracingCallerAddr, + data: encodeFunctionData({ + abi: TracingCallerAbi, + functionName: 'destruct', + args: [], + }), + }, + 'callTracer', + { withLog: true }, + ) + + await matchFixture(t, res) +}) + +Deno.test( + 'call-trace create_and_destruct', + opts, + async (t) => { + const tracingCallerAddr = await getTracingCallerAddr() + const res = await env.debugClient.traceCall( + { + to: tracingCallerAddr, + data: encodeFunctionData({ + abi: TracingCallerAbi, + functionName: 'create_and_destruct', + args: [], + }), + }, + 'callTracer', + { withLog: true }, + ) + + await matchFixture(t, res) + }, +) diff --git a/src/tracing-prestate.test.ts b/src/tracing-prestate.test.ts index c499c3e..7f56793 100644 --- a/src/tracing-prestate.test.ts +++ b/src/tracing-prestate.test.ts @@ -16,6 +16,7 @@ import { getPretraceFixtureReceipt, getTracingCallerAddr, } from './deploy_contracts.ts' +import { TracingCallerAbi } from '../codegen/abi/TracingCaller.ts' const getBlock = memoized(async () => { const receipt = await getPretraceFixtureReceipt() @@ -445,3 +446,47 @@ Deno.test( }) }), ) + +Deno.test( + 'prestate selfdestruct', + opts, + withDiffModes(async (t, config, diffMode) => { + const tracingCallerAddr = await getTracingCallerAddr() + const res = await env.debugClient.traceCall( + { + to: tracingCallerAddr, + data: encodeFunctionData({ + abi: TracingCallerAbi, + functionName: 'destruct', + args: [], + }), + }, + 'prestateTracer', + config, + ) + await matchFixture(t, res, diffMode) + }), +) + +Deno.test( + 'prestate create_and_destruct', + opts, + withDiffModes( + async (t, config, diffMode) => { + const tracingCallerAddr = await getTracingCallerAddr() + const res = await env.debugClient.traceCall( + { + to: tracingCallerAddr, + data: encodeFunctionData({ + abi: TracingCallerAbi, + functionName: 'create_and_destruct', + args: [], + }), + }, + 'prestateTracer', + config, + ) + await matchFixture(t, res, diffMode) + }, + ), +)