Skip to content

feat(evm-simulation): capture internal native ETH via traceTransfers on eth_simulateV1#803

Merged
Foulks-Plb merged 6 commits into
mainfrom
albuquerque-v1
Jun 30, 2026
Merged

feat(evm-simulation): capture internal native ETH via traceTransfers on eth_simulateV1#803
Foulks-Plb merged 6 commits into
mainfrom
albuquerque-v1

Conversation

@Foulks-Plb

@Foulks-Plb Foulks-Plb commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What

Enable traceTransfers on the eth_simulateV1 simulation backend so native-ETH moves are captured in assetChanges — including ETH moved through internal calls (e.g. a WETH.withdraw refund, a swap that pays out ETH), which previously emitted no log and was invisible on this path.

Why

The eth_simulateV1 backend derived native ETH only from each transaction's top-level value. ETH moved internally was silently dropped, so assetChanges was less complete than the Tenderly backend (the old JSDoc spelled this out as a known caveat: "For full native-ETH accounting use Tenderly"). This closes that gap — both backends now report the full net native-ETH delta.

How

  • eth-simulate-v1.ts — pass traceTransfers: true to simulateCalls. The node then synthesizes every native-ETH move (top-level and internal) as a Transfer event emitted from the native sentinel. assetChanges is now derived entirely from parsed transfer logs; the top-level value accounting is removed because traceTransfers already logs it and keeping both would double-count.
  • parsing/transfers.ts — new normalizeTransferToken helper collapses the native sentinel (0xeee…eee, lowercase) to viem's ethAddress constant rather than its checksummed form, so native deltas key consistently across backends (groupAssetChanges keys tokens by exact string). Real ERC20 tokens stay checksummed. Tenderly emits no such logs, so the path is inert there.

Validation

Verified empirically against a local anvil 1.7.1 devnet that eth_simulateV1 + traceTransfers emits native moves as Transfer logs at the 0xeee… sentinel (= viem's ethAddress), confirming the address-normalization choice. Anvil is what the fork suite runs against.

  • Unit tests: traceTransfers flag is set; native ETH accounted from synthetic logs; no double-count when a tx has value but no native log; parser normalizes the sentinel to ethAddress.
  • Fork test (mainnet, opt-in): WETH.withdraw nets sender −1 WETH / +1 ETH — the internal refund the old path missed.
  • tsc --noEmit, Biome, and the full evm-simulation unit suite (226 tests) pass.

🤖 Generated with Claude Code


Open in Devin Review

…on eth_simulateV1

Enable `traceTransfers` on the `eth_simulateV1` backend so the node
synthesizes native-ETH moves (top-level value *and* ETH moved through
internal calls, e.g. a `WETH.withdraw` refund) as `Transfer` logs from the
native sentinel. `parseTransfers` normalizes that sentinel to viem's
`ethAddress`, and `assetChanges` now derives native ETH entirely from these
logs instead of top-level transaction `value` — closing the coverage gap
where the eth_simulateV1 path missed internally-moved ETH (Tenderly already
reported it) while avoiding double-counting the top-level value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Foulks-Plb Foulks-Plb requested a review from a team as a code owner June 17, 2026 16:06
@Foulks-Plb Foulks-Plb self-assigned this Jun 17, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Foulks-Plb added a commit that referenced this pull request Jun 17, 2026
Apply PR #795 review resolutions: allowlist (GeneralAdapter1 + Permit2)
instead of trust-list with dispatcher; spender-only signature check
(amount/expiration out of model); per-asset conformance instead of
cross-asset net value; standard ERC-20 only; EIP-7702 out of scope;
native-ETH gap closed via #803; inflation-attack robustness likely
out of scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Foulks-Plb

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 5d689e99a2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Rubilmax
Rubilmax previously approved these changes Jun 24, 2026
@Foulks-Plb Foulks-Plb enabled auto-merge (squash) June 25, 2026 08:54
Foulks-Plb and others added 2 commits June 30, 2026 11:04
The merge of main into albuquerque-v1 left a stale vitest resolution in
pnpm-lock.yaml, breaking `pnpm install --frozen-lockfile` (no entry for
vitest@4.1.8...). Re-aligned the entry with main's resolved versions
(vitest@4.1.9, @types/node@26.0.1, vite@8.1.0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s not overflow

The sender ETH balance state override was pinned at maxUint256 on both the
eth_simulateV1 and Tenderly backends. When a simulated call paid native ETH
back to the sender (e.g. a WETH.withdraw refund), the recipient balance
overflowed uint256 and the EVM reverted the value transfer — surfacing as a
SimulationRevertedError. This is exactly the internal-refund case the
traceTransfers work added a fork test for.

Use maxUint256 / 2n instead: still astronomically larger than the total ETH
supply (so it still dodges false insufficient-gas reverts) while leaving room
for any realistic inbound native ETH. Updates both backends and their unit
tests; folds the note into the existing eth-simulate-trace-transfers changeset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Foulks-Plb Foulks-Plb merged commit 7157a55 into main Jun 30, 2026
10 of 11 checks passed
@Foulks-Plb Foulks-Plb deleted the albuquerque-v1 branch June 30, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants