Skip to content

fix(rpc): align Morph transaction and receipt serialization with morph-geth#114

Merged
panos-xyz merged 9 commits into
mainfrom
fix/rpc-morph-serialization
May 26, 2026
Merged

fix(rpc): align Morph transaction and receipt serialization with morph-geth#114
panos-xyz merged 9 commits into
mainfrom
fix/rpc-morph-serialization

Conversation

@panos-xyz
Copy link
Copy Markdown
Contributor

@panos-xyz panos-xyz commented May 26, 2026

Summary

Fixes 8 RPC serialization issues discovered during cross-client replay testing against morph-geth.
These were found by running morph-cross-client-tests replay over 12,150 hoodi blocks and the full
268-block mainnet hardfork target set — all 8 fixes are required to reach 0 mismatches.

  • fix(rpc): align receipt extension field serialization — Morph receipt fields (l1Fee, feeTokenID, feeRate, tokenScale, feeLimit, version, reference, memo) were missing or mis-typed
  • fix(rpc): encode receipt version as quantity — version was emitted as a non-quantity hex
  • fix(rpc): derive receipt type from transaction envelope — receipt type was always 0x0 for typed txs
  • fix(rpc): remove duplicate transaction extension fields — MorphTx objects emitted version/feeTokenId twice (duplicate JSON keys)
  • fix(rpc): emit MorphTx gas as rpc gas field — gas field was omitted from MorphTx transaction objects
  • fix(rpc): align MorphTxFields fee token id key — key was feeTokenId in some paths, feeTokenID in others; aligned to feeTokenID
  • fix(rpc): omit MorphTx v0 version field — version=0 should be omitted (matches morph-geth behavior)
  • fix(rpc): align default priority fee with morph-geth — default gas tip cap diverged from morph-geth's behavior

Test plan

  • cargo test -p morph-cct-rpc (existing RPC tests)
  • End-to-end replay: 2,000 contiguous hoodi blocks → 0 mismatches
  • End-to-end replay: 150-block hardfork-window target set → 0 mismatches
  • End-to-end replay: 10,000-block contiguous sweep → 0 mismatches
  • Mainnet 268-block hardfork target set (Morph203/Viridian/Emerald/Jade) → 0 mismatches

Summary by CodeRabbit

  • New Features

    • Adds a Morph-specific default gas-price suggestion for node startup.
  • Bug Fixes

    • RPC receipts now derive their type from the transaction envelope and expose missing numeric metadata as explicit zero values.
    • Transaction/receipt version now appears as a hex-quantity in RPC responses.
  • Chores

    • Canonicalized JSON field names and serialization behavior for Morph transactions/receipts; tests updated and a workspace dependency added.

Review Change Stack

Expose Morph receipt extension keys consistently with morph-geth so RPC parity does not report schema-only missing-field diffs for absent metadata.

Constraint: Preserve morph-geth-compatible receipt JSON contract
Confidence: high
Scope-risk: narrow
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, reopen this pull request to trigger a review.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dcaf61a8-9d2d-4fab-a86d-0eb634b43e85

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors Morph RPC transaction/receipt serde and types; adds MORPH_DEFAULT_PRIORITY_FEE and applies it to CLI gas price defaults; updates MorphTx JSON naming/omission rules; converts MorphRpcTransaction to a type alias; makes Morph receipt numeric fields use RPC numeric types (explicit nulls when unset); and updates tests and receipt envelope selection logic.

Changes

Morph RPC Serialization and Type Refactoring

Layer / File(s) Summary
Default Priority Fee Constant and CLI Setup
crates/chainspec/src/constants.rs, bin/morph-reth/Cargo.toml, bin/morph-reth/src/main.rs
MORPH_DEFAULT_PRIORITY_FEE constant added and wired into CLI via apply_morph_cli_defaults() to set rpc.gas_price_oracle.default_suggested_fee when missing; alloy-primitives added as a workspace dependency to support U256 usage.
MorphTx Primitive Serialization
crates/primitives/src/transaction/morph_transaction.rs
MorphTx serde updated: omit version when zero, canonicalize feeTokenID (accept feeTokenId alias), and serialize gas while accepting gasLimit; adds unit test asserting canonical feeTokenID.
RPC Type Definitions and Serialization
crates/rpc/src/types/transaction.rs, crates/rpc/src/types/receipt.rs
MorphRpcTransaction replaced by type MorphRpcTransaction = RpcTransaction<MorphTxEnvelope>; MorphRpcReceipt.version changed to Option<U64> and Morph extension fields no longer skip-serialize so absent fields emit explicit null; tests updated to assert JSON-RPC quantity encoding and presence of nulls.
Receipt Conversion and Envelope Selection
crates/rpc/src/eth/receipt.rs
Receipt conversion now derives RPC envelope variant from transaction tx.ty() via morph_tx_type_from_u8; log mapping centralized; numeric Morph receipt metadata defaults to zero for RPC outputs while reference/memo remain null when unset.
Receipt and Transaction Conversion Tests
crates/rpc/src/eth/receipt.rs, crates/rpc/src/eth/transaction.rs, crates/node/tests/it/rpc.rs
Tests updated/extended: validate zero-defaulting of numeric Morph fields, ensure historical receipt type follows transaction envelope type, adapt transaction tests to inspect embedded MorphTxEnvelope variants, and change assertions to expect RPC numeric types (hex-quantity strings) for version.

Sequence Diagram(s)

(No additional sequence diagrams generated beyond those embedded in the review stack artifact; primary flows are captured there.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Suggested reviewers

  • chengwenxi

Poem

🐰 A rabbit hops through RPC types and keys,
Serde trims a field and names align with ease,
Defaults fill zeros where silence used to be,
Hex quantities sparkle in the RPC sea,
Morph's JSON hums a tidy, cheerful breeze.

🚥 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 accurately describes the main objective of the PR: fixing RPC serialization issues for Morph transactions and receipts to align with morph-geth behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/rpc-morph-serialization

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.

@panos-xyz panos-xyz force-pushed the fix/rpc-morph-serialization branch from c8c7da9 to 5f8878d Compare May 26, 2026 03:23
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@crates/primitives/src/transaction/morph_transaction.rs`:
- Around line 46-49: The serde attribute for the fee token field only renames to
"feeTokenID" and thus drops older "feeTokenId" inputs; update the serde
attributes that currently have rename = "feeTokenID" to also include alias =
"feeTokenId" (e.g. serde(default, with = "alloy_serde::quantity", rename =
"feeTokenID", alias = "feeTokenId")) for the fee field(s) referenced in the file
(the same attribute block around the fee field at the top and the similar block
at lines ~140-143) so deserialization accepts the old spelling while continuing
to emit "feeTokenID".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3f28872c-372d-4357-a749-35bb4af3fa17

📥 Commits

Reviewing files that changed from the base of the PR and between 18e7961 and 5f8878d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • bin/morph-reth/Cargo.toml
  • bin/morph-reth/src/main.rs
  • crates/chainspec/src/constants.rs
  • crates/primitives/src/transaction/morph_transaction.rs
  • crates/rpc/src/eth/receipt.rs
  • crates/rpc/src/eth/transaction.rs
  • crates/rpc/src/types/receipt.rs
  • crates/rpc/src/types/transaction.rs

Comment thread crates/primitives/src/transaction/morph_transaction.rs
@panos-xyz panos-xyz force-pushed the fix/rpc-morph-serialization branch from 5f8878d to 736f04a Compare May 26, 2026 05:20
panos-xyz and others added 7 commits May 26, 2026 14:43
Use the same JSON-RPC quantity encoding as morph-geth for the Morph receipt version extension field so replay can parse deployed reth receipts.

Constraint: Preserve morph-geth-compatible receipt JSON contract
Confidence: high
Scope-risk: narrow
Use the transaction envelope type when rendering receipt RPC responses so historical receipts decoded through a legacy storage wrapper still report their canonical transaction type.

Constraint: Preserve morph-geth-compatible receipt JSON contract
Confidence: high
Scope-risk: narrow
Use the consensus transaction envelope as the single RPC serialization source for Morph transaction extension fields. This prevents L1Msg and MorphTx responses from emitting duplicate flattened JSON keys while preserving geth-compatible feeTokenID casing.

Constraint: Keep receipt RPC fixes on the current phase branch intact
Rejected: Keep wrapper fields with serde skip | leaves duplicate in-memory state
Confidence: high
Scope-risk: moderate
Align MorphTx JSON with standard eth transaction responses by serializing gas_limit as gas while accepting gasLimit as an input alias. This lets replay parse MorphTx blocks using the same schema as morph-geth.

Constraint: Preserve Rust/internal gas_limit field and RLP encoding semantics
Rejected: Normalize gasLimit in the replay harness | hides an RPC schema divergence
Confidence: high
Scope-risk: narrow
Adds MORPH_DEFAULT_PRIORITY_FEE constant (1_000_000 wei) and wires it into
the reth GPO config as default_suggested_fee. Without this, reth falls back
to its own 1 gwei default while morph-geth inherits 1_000_000 wei from
upstream go-ethereum's miner.DefaultConfig.GasPrice — a 1000x divergence
in eth_maxPriorityFeePerGas whenever the GPO has no usable block samples,
which is the common case on Morph L2 (tip is typically 0 and gets filtered
out by ignore_price).
@panos-xyz panos-xyz force-pushed the fix/rpc-morph-serialization branch from 736f04a to 11443be Compare May 26, 2026 06:43
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/rpc/src/eth/receipt.rs (1)

116-126: 💤 Low value

Consider adding a comment about the EIP-4844 fallback behavior.

Type 3 (EIP-4844 blob transactions) is not explicitly matched and falls through to Legacy. Since Morph doesn't support blob transactions, this is functionally correct, but a brief comment would clarify intent for future readers.

💡 Suggested clarification
 fn morph_tx_type_from_u8(tx_type: u8) -> MorphTxType {
     match tx_type {
         0 => MorphTxType::Legacy,
         1 => MorphTxType::Eip2930,
         2 => MorphTxType::Eip1559,
         4 => MorphTxType::Eip7702,
         L1_TX_TYPE_ID => MorphTxType::L1Msg,
         MORPH_TX_TYPE_ID => MorphTxType::Morph,
+        // EIP-4844 (type 3) and other unknown types fall back to Legacy.
+        // Morph does not support blob transactions.
         _ => MorphTxType::Legacy,
     }
 }
🤖 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/rpc/src/eth/receipt.rs` around lines 116 - 126, The match in
morph_tx_type_from_u8 does not handle EIP-4844 (type 3) explicitly and currently
falls back to MorphTxType::Legacy; add a brief inline comment near the match arm
(or above the function) stating that type 3 (EIP-4844 blob transactions)
intentionally falls back to Legacy because Morph does not support blob
transactions, and mention the EIP-4844 type id (3) and that L1_TX_TYPE_ID and
MORPH_TX_TYPE_ID remain explicit to avoid confusion when inspecting MorphTxType,
MorphTxType::Legacy, L1_TX_TYPE_ID, and MORPH_TX_TYPE_ID.
🤖 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/rpc/src/eth/receipt.rs`:
- Around line 116-126: The match in morph_tx_type_from_u8 does not handle
EIP-4844 (type 3) explicitly and currently falls back to MorphTxType::Legacy;
add a brief inline comment near the match arm (or above the function) stating
that type 3 (EIP-4844 blob transactions) intentionally falls back to Legacy
because Morph does not support blob transactions, and mention the EIP-4844 type
id (3) and that L1_TX_TYPE_ID and MORPH_TX_TYPE_ID remain explicit to avoid
confusion when inspecting MorphTxType, MorphTxType::Legacy, L1_TX_TYPE_ID, and
MORPH_TX_TYPE_ID.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ab21fc56-187a-48bb-bd05-2400128c7c61

📥 Commits

Reviewing files that changed from the base of the PR and between 736f04a and 11443be.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • bin/morph-reth/Cargo.toml
  • bin/morph-reth/src/main.rs
  • crates/chainspec/src/constants.rs
  • crates/node/tests/it/rpc.rs
  • crates/primitives/src/transaction/morph_transaction.rs
  • crates/rpc/src/eth/receipt.rs
  • crates/rpc/src/eth/transaction.rs
  • crates/rpc/src/types/receipt.rs
  • crates/rpc/src/types/transaction.rs
✅ Files skipped from review due to trivial changes (1)
  • bin/morph-reth/Cargo.toml

@panos-xyz panos-xyz merged commit 08557ca into main May 26, 2026
13 checks passed
@panos-xyz panos-xyz deleted the fix/rpc-morph-serialization branch May 26, 2026 07:57
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