Skip to content

fix(revm): make L1 fee fallback encoding tx-type exact#131

Merged
panos-xyz merged 3 commits into
mainfrom
fix/l1-fee-fallback-tx-type
Jun 15, 2026
Merged

fix(revm): make L1 fee fallback encoding tx-type exact#131
panos-xyz merged 3 commits into
mainfrom
fix/l1-fee-fallback-tx-type

Conversation

@panos-xyz

@panos-xyz panos-xyz commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • MorphTxEnv::encode_for_l1_fee (used by eth_call / eth_estimateGas when no signed bytes exist) previously inferred the envelope type from field shapes. This mis-encoded explicit EIP-2930 transactions with empty access lists as legacy, and dropped EIP-7702 authorization lists entirely.
  • The env's tx_type is always populated by both callers (alloy-evm's try_into_tx_env via minimal_tx_type(), and the statetest schema), so the encoder now dispatches on TransactionType directly: EIP-2930 / EIP-1559 / EIP-7702 are re-encoded exactly (including authorization_list, re-signed with the fee-sizing placeholder for recovered authorizations); everything else stays legacy.
  • go-ethereum's current EstimateL1DataFeeForMessage still sizes untyped txs as dynamic-fee envelopes whenever a base fee exists. That quirk is mirrored only in the morph-statetest harness to keep state-root parity with geth-generated fixtures, and can be dropped once geth derives the envelope from the actual tx type.

Test plan

  • New unit tests: EIP-2930 with empty access list keeps its type byte; EIP-7702 envelope round-trips its authorization list.
  • Existing geth byte-parity tests (encode_for_l1_fee_dynamic_matches_go_ethereum_bytes, placeholder-signature shape) still pass.
  • cargo nextest run -p morph-revm -p morph-rpc -p morph-statetest — 154 passed.
  • cargo clippy --all-targets -- -D warnings and cargo fmt --all -- --check clean.

Closes #128

Summary by CodeRabbit

  • Bug Fixes

    • L1 fee encoding now matches go-ethereum for transactions without an explicit type.
    • Transaction envelope reconstruction preserves typed fields and access/authorization lists when sizing L1 fees.
    • Setting an access list correctly upgrades legacy-like transactions to the appropriate typed envelope.
  • Tests

    • Added encode/decode assertions to verify fee-size encoding and preservation of access/authorization lists.

The simulation-path fallback encoder (eth_call/eth_estimateGas) inferred
the envelope type from field shapes, mis-encoding explicit EIP-2930 txs
with empty access lists as legacy and dropping EIP-7702 authorization
lists entirely. The env's tx_type is always populated by both callers
(alloy-evm's try_into_tx_env and the statetest schema), so dispatch on
it directly and add the missing EIP-7702 envelope.

go-ethereum's current EstimateL1DataFeeForMessage still sizes untyped
txs as dynamic-fee envelopes whenever a base fee exists; that quirk is
mirrored only in the morph-statetest harness to keep state-root parity
with geth-generated fixtures, and can be dropped once geth derives the
envelope from the actual tx type.

Closes #128
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR makes L1-fee fallback encoding respect explicit tx types (EIP-2930, EIP-1559, EIP-7702), preserves type-specific fields in rebuilt envelopes, delegates access-list setting to upstream tx env, and forces Eip1559 for untyped state-test txs when a base fee exists.

Changes

L1 Fee Type Preservation

Layer / File(s) Summary
Envelope rebuild for typed L1 fees
crates/revm/src/tx.rs
build_ethereum_envelope_for_l1_fee uses TransactionType::from(self.inner.tx_type) to reconstruct the correct MorphTxEnvelope variant (EIP-2930, EIP-1559, EIP-7702, Legacy), preserving typed fields. TransactionEnvMut::set_access_list now delegates to self.inner.set_access_list(...).
Tests for type-preserving envelope encoding
crates/revm/src/tx.rs
Imports Decodable2718, updates EIP-1559 test to set tx_type explicitly, and adds roundtrip tests asserting that encode_for_l1_fee preserves empty EIP-2930 access lists and EIP-7702 authorization list contents after decode.
State test schema untyped tx handler
bin/morph-statetest/src/schema.rs
When state-test JSON omits type and a base fee exists, clone the tx and force the cloned env's tx_type to Eip1559 before calling encode_for_l1_fee, matching go-ethereum behavior for untyped dynamic-fee envelopes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related issues

Suggested reviewers

  • anylots
  • chengwenxi

Poem

"🐰 I hopped through bytes and types so fair,
I fixed envelopes with utmost care,
EIP-2930, 1559, and 7702 too,
Access lists and auths kept true,
Now fees and tests all sing anew!"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(revm): make L1 fee fallback encoding tx-type exact' clearly and concisely describes the main change: improving L1 fee fallback encoding to use exact transaction types instead of inferring from field shapes.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #128: preserves explicit tx types in build_ethereum_envelope_for_l1_fee, adds EIP-7702 fallback encoding with authorization_list, ensures EIP-2930 typed encoding for empty access lists, delegates set_access_list for tx_type upgrade, and includes focused unit tests for EIP-2930 and EIP-7702 round-trips.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue scope: modifications to encode_for_l1_fee and build_ethereum_envelope_for_l1_fee for tx-type exactness, set_access_list delegation for upstream consistency, and new unit tests validating the encoding behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/l1-fee-fallback-tx-type

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
crates/revm/src/tx.rs (1)

727-759: 💤 Low value

Consider adding test coverage for recovered authorization path.

The test covers Either::Left(signed) authorizations which are cloned directly. For completeness, consider adding a test case with Either::Right(RecoveredAuthorization) to verify the re-signing with placeholder signature path (lines 223-225) produces valid output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/revm/src/tx.rs` around lines 727 - 759, Add a test that mirrors
encode_for_l1_fee_preserves_eip7702_authorization_list but uses
Either::Right(RecoveredAuthorization{...}) instead of Either::Left(signed) to
exercise the recovered-then-re-signed path; construct a MorphTxEnv with
TransactionType::Eip7702 and authorization_list containing Either::Right(a
RecoveredAuthorization for the same chain_id/address/nonce), call
tx.encode_for_l1_fee(chain_id), decode with MorphTxEnvelope::decode_2718 and
assert that decoded.tx().authorization_list contains the expected signed
authorization (i.e., the RecoveredAuthorization was re-signed/converted to the
same shape as a signed authorization). Ensure you reference the
RecoveredAuthorization type and the encode_for_l1_fee /
MorphTxEnvelope::decode_2718 flow when adding the new test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/revm/src/tx.rs`:
- Around line 727-759: Add a test that mirrors
encode_for_l1_fee_preserves_eip7702_authorization_list but uses
Either::Right(RecoveredAuthorization{...}) instead of Either::Left(signed) to
exercise the recovered-then-re-signed path; construct a MorphTxEnv with
TransactionType::Eip7702 and authorization_list containing Either::Right(a
RecoveredAuthorization for the same chain_id/address/nonce), call
tx.encode_for_l1_fee(chain_id), decode with MorphTxEnvelope::decode_2718 and
assert that decoded.tx().authorization_list contains the expected signed
authorization (i.e., the RecoveredAuthorization was re-signed/converted to the
same shape as a signed authorization). Ensure you reference the
RecoveredAuthorization type and the encode_for_l1_fee /
MorphTxEnvelope::decode_2718 flow when adding the new test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be1bcd67-4b49-473a-95f7-391a00b6abb8

📥 Commits

Reviewing files that changed from the base of the PR and between b6c47fb and 1ad4102.

📒 Files selected for processing (2)
  • bin/morph-statetest/src/schema.rs
  • crates/revm/src/tx.rs

Upstream TxEnv::set_access_list upgrades tx_type from Legacy to Eip2930;
the MorphTxEnv override only assigned the field, leaving the type byte
stale. Now that the type byte is authoritative for fallback L1 fee
encoding, delegate to the inner impl to stay consistent with upstream.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
crates/revm/src/tx.rs (1)

729-761: 💤 Low value

Consider adding test coverage for recovered authorization re-signing.

The test verifies the Either::Left (pre-signed) authorization path, but the Either::Right (recovered) path at lines 223-225 isn't exercised. That branch re-signs recovered authorizations with the placeholder signature.

A supplemental test case could populate authorization_list with an Either::Right(RecoveredAuthorization) to verify the conversion logic produces valid EIP-2718 output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/revm/src/tx.rs` around lines 729 - 761, The test
encode_for_l1_fee_preserves_eip7702_authorization_list covers Either::Left
(pre-signed) authorizations but not the Either::Right (recovered) branch that
re-signs recovered authorizations (the logic around encode_for_l1_fee /
encode_for_l1_fee’s handling of RecoveredAuthorization). Add a new test similar
to the existing one that constructs a MorphTxEnv with authorization_list
containing Either::Right(RecoveredAuthorization { chain_id, address, nonce,
signature_placeholder }) (or the appropriate RecoveredAuthorization
constructor), call tx.encode_for_l1_fee(...) with the matching chain id, decode
via MorphTxEnvelope::decode_2718 and assert the resulting
decoded.tx().authorization_list contains the expected re-signed Authorization
(i.e., matches the placeholder-resigned form), thereby exercising the re-signing
branch in encode_for_l1_fee.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/revm/src/tx.rs`:
- Around line 729-761: The test
encode_for_l1_fee_preserves_eip7702_authorization_list covers Either::Left
(pre-signed) authorizations but not the Either::Right (recovered) branch that
re-signs recovered authorizations (the logic around encode_for_l1_fee /
encode_for_l1_fee’s handling of RecoveredAuthorization). Add a new test similar
to the existing one that constructs a MorphTxEnv with authorization_list
containing Either::Right(RecoveredAuthorization { chain_id, address, nonce,
signature_placeholder }) (or the appropriate RecoveredAuthorization
constructor), call tx.encode_for_l1_fee(...) with the matching chain id, decode
via MorphTxEnvelope::decode_2718 and assert the resulting
decoded.tx().authorization_list contains the expected re-signed Authorization
(i.e., matches the placeholder-resigned form), thereby exercising the re-signing
branch in encode_for_l1_fee.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1203e38-21b3-481a-a2b2-bb17d7be36d6

📥 Commits

Reviewing files that changed from the base of the PR and between 1ad4102 and f324fa6.

📒 Files selected for processing (1)
  • crates/revm/src/tx.rs

@panos-xyz panos-xyz merged commit 1f146a5 into main Jun 15, 2026
12 checks passed
@panos-xyz panos-xyz deleted the fix/l1-fee-fallback-tx-type branch June 15, 2026 02:59
@panos-xyz panos-xyz mentioned this pull request Jun 22, 2026
3 tasks
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.

fix(revm): make L1 fee fallback encoding tx-type exact

2 participants