Skip to content

fix(evm): close evm2 parity gaps for Ethereum and Tempo - #17740

Merged
mablr merged 18 commits into
mablr/evm2-migrationfrom
evm2-eth-tempo-parity
Oct 22, 2026
Merged

mablr merged 18 commits into
mablr/evm2-migrationfrom
evm2-eth-tempo-parity

Conversation

@mablr

@mablr mablr commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Stacked on #17541. Restores master behavior on master's own Ethereum and Tempo tests where the evm2 migration had drifted, without changing master's assertions.

It restores the four anvil tests the master merge dropped (#17716, #17719, #17731) and the two #17719 changes they exposed: eth_call under Amsterdam keeps the transaction gas cap, and a memory reset keeps the EIP-8037 state gas reservoir. The storage hook depth test gets master's vm.isIsolateMode() ? 1024 : 1023 back. deployCode charges its caller as in #17732, with CREATE/CREATE2, initcode and EIP-8037 state gas charged once even when isolated, and reverts with the constructor's code on a failed code deposit, except as the root of an isolated transaction. Anvil reports Tempo validation errors as -32603, and the fork revert diagnostic compares against the isolated transaction's target again.

On the lifecycle side, fork switches, rolls and isolated fork-switching calls keep warmth as REVM's per-fork journals did, so Fork2.t.sol gas now matches a master build in both isolation modes. Snapshots capture and revert only the fork active when they were taken, and tracing a Tempo AA transaction no longer panics on the nonce log emitted before the root frame. The remaining warmth differences are listed as known gaps in docs/dev/evm2-migration.md.

Closes OSS-1192. Refs OSS-1155, OSS-1157, OSS-1158 (only the Tempo trace panic; step semantics remain open) and OSS-1180.

mablr and others added 6 commits October 22, 2026 13:44
The master merge ported most of #17719 but missed two spots: eth_call
still lifted the tx gas limit cap under Amsterdam, and a memory reset
rebuilt the cfg without set_cfg_spec. Both dropped the EIP-8037 state
gas reservoir. Restore the four master tests the merge dropped from

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
#17716, #17719 and #17731.
evm2 now starts isolated calls at the initial call depth (OSS-1191), so
the isolate-dependent depth from master holds again.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
Port #17732 to evm2. Cheatcodes now get a gas tracker that is returned
to the caller, and deployCode charges it as the CREATE and CREATE2
opcodes do: create and EIP-3860 initcode costs with the initcode size
limit, EIP-8037 create state gas for an empty destination, 63/64
forwarding, and the create frame's unused gas and reservoir. Isolated
snapshot corrections apply at the cheatcode caller's depth.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
Master returns every Tempo transaction validation error as an internal
error prefixed with 'tempo transaction error', not only the value
transfer one. Restore that mapping and master's cast assertion.
Master isolates depth-1 calls and creates as nested transactions whose
kind replaces the test transaction's, so its missing-contract revert
diagnostic compares against the isolated call's target and is skipped
inside an isolated create. evm2 nested transactions have no kind, so
the cheatcodes only knew the root call target and diagnosed calls made
from constructors. Track the isolated transaction's target instead.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
The evm2 lifecycle resumed a selected or rolled fork with only the base
warm set, so accounts and slots the running transaction had warmed paid
cold costs again. Port REVM's per-fork journal warmth: a selected fork
resumes with the warmth it had earlier in the transaction plus the
persistent accounts', a rolled fork warms every account the transaction
loaded (with slots for persistent and created ones), and isolated calls
that switch forks keep the outer transaction's warmth. Isolation cooling
before static calls still cools carried warmth.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
mablr and others added 12 commits October 22, 2026 14:56
As on master, a state snapshot restores only the context that was active
when it was taken, together with the environment, journal and active
fork selection. Other forks keep the writes made on them after the
snapshot, so a revert from another fork first parks the running state in
its own context.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
Tempo's handler increments a 2D nonce, emitting the NonceManager event,
while it validates the transaction, before the root frame starts. evm2
reports that log to the inspectors, and the tracer, which has no call to
attach it to yet, panicked. As on master, where REVM only reports logs of
running frames, the tracer now only sees logs while the root frame runs;
the transaction still records the log.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
When revertToState runs while another fork than the snapshot's is
active, master replaces the running journal without storing it back, so
that fork keeps only what reached its fork database. Do the same instead
of parking the running transaction's changes in the fork's context.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
deployCode charges its caller the EIP-8037 account-creation state gas
before running the create frame, but an isolated synthetic create did
not pass that charge to the nested transaction, which charged it again.
As master does with charged_create_state_gas, carry the charge into the
synthetic create so the nested transaction credits it.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
When an isolated call switches forks, master replaces the running
transaction's state with the nested transaction's, so the running
transaction's own warmth does not survive and the nested transaction's
does only if it succeeds. The stack instead reinstated the outer warmth
even when the call reverted, which made selecting the original fork back
4500 gas cheaper than on master in Fork2.t.sol. Keep the nested
transaction's warmth, captured when its root frame succeeds, and none
from the running transaction.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
A context that resumed with warmth from earlier in the transaction holds
it in the state's prewarm set, not in loaded accounts, so suspending it
again without touching that warmth dropped it: switching A, B, A, B, A
turned a warm non-persistent account or slot cold. Keep the warmth a
context resumed with when it is suspended again, as master's per-fork
journals do.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
Keeping a context's restored warmth across suspensions also handed it to
the next fork as the warmth of persistent accounts, which made an
account created by an isolated CREATE and then made persistent warm on
the other fork, 2500 gas below master in Fork2's testMarkPersistent.
Persistent accounts again carry only the warmth the suspended visit
touched; the context itself still keeps its restored warmth.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
An isolated call that switches forks and then reverts left the slots it
had accessed before switching cold, because only a successful call's
warmth is installed. As on master, where the switch moved those slots
into the new fork's journal beyond the reach of the revert, they now
stay warm while the call's accounts stay cold.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
When a deployCode constructor returned code that could not be deposited,
for example because too little gas was left for the code deposit, evm2
drops the returned output and the cheatcode reverted with no data. As on
master, where REVM kept the output, the cheatcode now reverts with the
code the constructor returned.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
An isolated deployCode runs its create as the root of a nested
transaction, where master reports a failed code deposit with no revert
data. The returned-code fallback applied there too and reverted with the
constructor's code. It now applies only when the create did not start
its own isolated transaction.

Amp-Thread-ID: https://ampcode.com/threads/T-01a16369-8789-707c-b8fe-668c73ac7ed1
Co-authored-by: Amp <amp@ampcode.com>
@mablr
mablr marked this pull request as ready for review October 22, 2026 16:31
@mablr mablr changed the title fix(evm): restore Ethereum and Tempo master parity on evm2 fix(evm): close evm2 parity gapsfor Ethereum and Tempo Oct 22, 2026
@mablr mablr changed the title fix(evm): close evm2 parity gapsfor Ethereum and Tempo fix(evm): close evm2 parity gaps for Ethereum and Tempo Oct 22, 2026
@mablr
mablr merged commit 084b42b into mablr/evm2-migration Oct 22, 2026
22 of 24 checks passed
@mablr
mablr deleted the evm2-eth-tempo-parity branch October 22, 2026 16:37
@github-project-automation github-project-automation Bot moved this to Done in Foundry Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant