From 45d1f599ecff7b8fe9e1943ac7bb2c8b873cab12 Mon Sep 17 00:00:00 2001 From: test Date: Fri, 2 Oct 2026 19:49:25 -0400 Subject: [PATCH] evm: execute with the prague hardfork so current solc output runs The executor used shanghai, which rejects MCOPY, TLOAD and TSTORE as invalid opcodes. Current solc emits them by default. --- packages/evm/CHANGELOG.md | 6 ++++++ packages/evm/src/executor.test.ts | 28 ++++++++++++++++++++++++++++ packages/evm/src/executor.ts | 12 +++++++++--- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/evm/CHANGELOG.md b/packages/evm/CHANGELOG.md index 556378a727..387c70f32d 100644 --- a/packages/evm/CHANGELOG.md +++ b/packages/evm/CHANGELOG.md @@ -7,6 +7,12 @@ specification itself are tracked in the root ## Unreleased +### Fixed + +- The executor now runs with the Prague hardfork instead of Shanghai, so + bytecode from current `solc` (which uses `MCOPY`, `TLOAD` and `TSTORE`) + executes. + ## 0.1.0-preview.0 — 2026-09-21 The version scheme changed: prerelease versions are now `preview.`, and diff --git a/packages/evm/src/executor.test.ts b/packages/evm/src/executor.test.ts index 48fbfc131e..263f8fcb40 100644 --- a/packages/evm/src/executor.test.ts +++ b/packages/evm/src/executor.test.ts @@ -29,6 +29,18 @@ const returnValueCode = "602a60005260206000f3"; // RETURN => f3 const constructorCode = "65602a600055006000526006601af3"; +// mcopy: PUSH1 0x2a PUSH1 0x00 MSTORE +// PUSH1 0x20 PUSH1 0x00 PUSH1 0x20 MCOPY +// PUSH1 0x20 PUSH1 0x20 RETURN +// Copies a word from offset 0 to offset 0x20 and returns it. +const mcopyCode = "602a600052" + "602060006020" + "5e" + "60206020f3"; + +// transient: PUSH1 0x2a PUSH1 0x01 TSTORE +// PUSH1 0x01 TLOAD PUSH1 0x00 MSTORE +// PUSH1 0x20 PUSH1 0x00 RETURN +// Stores 42 in transient slot 1, loads it and returns it. +const transientCode = "602a60015d" + "60015c600052" + "60206000f3"; + describe("Executor", () => { let executor: Executor; @@ -131,4 +143,20 @@ describe("Executor", () => { expect(after).not.toEqual(before); }); }); + + describe("cancun opcodes", () => { + it("executes MCOPY", async () => { + const result = await executor.executeCode(mcopyCode); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + expect(bytesToHex(result.returnValue)).toBe("2a".padStart(64, "0")); + }); + + it("executes TSTORE and TLOAD", async () => { + const result = await executor.executeCode(transientCode); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + expect(bytesToHex(result.returnValue)).toBe("2a".padStart(64, "0")); + }); + }); }); diff --git a/packages/evm/src/executor.ts b/packages/evm/src/executor.ts index 6a85073cf6..810eb4b87c 100644 --- a/packages/evm/src/executor.ts +++ b/packages/evm/src/executor.ts @@ -8,12 +8,18 @@ import { EVM } from "@ethereumjs/evm"; import type { InterpreterStep } from "@ethereumjs/evm"; import { SimpleStateManager } from "@ethereumjs/statemanager"; -import { Common, Mainnet } from "@ethereumjs/common"; +import { Common, Hardfork, Mainnet } from "@ethereumjs/common"; import { Address, Account } from "@ethereumjs/util"; import { hexToBytes, bytesToHex } from "ethereum-cryptography/utils"; import type { TraceStep, TraceHandler } from "#trace"; +/** + * Hardfork used for execution. Current solc targets prague by default, + * so this must be at least cancun (MCOPY, TLOAD, TSTORE, ...). + */ +const hardfork = Hardfork.Prague; + /** * Options for executing a contract call. */ @@ -75,7 +81,7 @@ export class Executor { constructor() { const common = new Common({ chain: Mainnet, - hardfork: "shanghai", + hardfork, }); this.stateManager = new SimpleStateManager(); this.evm = new EVM({ @@ -299,7 +305,7 @@ export class Executor { this.evm = new EVM({ common: new Common({ chain: Mainnet, - hardfork: "shanghai", + hardfork, }), stateManager: this.stateManager, });