okx / okx/xlayer-reth

debug_traceTransaction returns null for transactions preceded by a gasless (zero-fee) tx in the same block

Open
#277 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
17
Forks
6
PR merge metrics
No merged PRs in 30d

Description

Summary

debug_traceTransaction returns {"result": null} (HTTP 200) for a large fraction of X Layer mainnet transactions, even though the transactions exist, succeeded on-chain, and are traceable via other methods. Through some investigation this appears to be caused by the gasless (zero-fee) transaction feature interacting badly with the transaction-replay path used by the RPC tracer.

Environment

xlayer-reth-node Version: 0.1.0-dev (dbc1495)   # tag v0.0.6.3
Upstream Reth Version: 5101851b

Observed behavior

For a transaction that is not the first non-deposit transaction in its block, debug_traceTransaction returns null:

# Example on X Layer mainnet: tx at block 64,564,751, index 5
curl -s -X POST "$RPC" -H 'Content-Type: application/json' -d '{
  "jsonrpc":"2.0","id":1,"method":"debug_traceTransaction",
  "params":["0xfeba105bc2c9a813712fe78f6619be7bea4601ddc8df99943f843737ffe8cd06",
            {"tracer":"callTracer"}]
}'
# -> {"jsonrpc":"2.0","id":1,"result":null}

The transaction clearly exists and succeeded:

  • eth_getTransactionByHash / eth_getTransactionReceipt return it (status: 0x1, transactionIndex: 0x5).
  • debug_traceBlockByNumber on the same block returns a full, correct trace for all transactions in the block, including this one.
  • The parity-style trace_transaction on the same hash does not return null but surfaces an error instead:
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"max fee per gas less than block base fee"}}

So the state and history are present; only the single-transaction trace path fails.

Reproduction pattern (root cause)

Tracing each transaction in block 64,564,751 individually with debug_traceTransaction shows a clear boundary:

index tx type effective gas price debug_traceTransaction
0 126 (deposit) 0 OK
1 2 (EIP-1559) maxFeePerGas = 0 OK (traced directly)
2..8 mixed null

Index 1 is a gasless transaction: a type 0x2 tx with maxFeePerGas = 0 / maxPriorityFeePerGas = 0, included in a block whose baseFeePerGas = 20000000.

  • Tracing index 1 directly works, because the tracer disables the base-fee check for the target transaction.
  • Tracing any transaction at index ≥ 2 fails, because debug_traceTransaction first replays the preceding transactions in the block to reconstruct the pre-state — and replaying the gasless index-1 tx goes through the normal execution path with base-fee validation enabled. The zero-fee tx is rejected with max fee per gas less than block base fee, the replay aborts, and debug_traceTransaction swallows the error and returns null.

In other words: any transaction that is preceded in its block by a gasless (zero-fee) transaction cannot be traced with debug_traceTransaction / trace_transaction / trace_get / trace_replayTransaction. Since gasless transactions are common, this affects a large share of trace requests. Block-level tracers (debug_traceBlockByNumber / debug_traceBlockByHash) are unaffected because they execute the block through the block executor, which already applies the gasless base-fee relaxation.

Suggested cause / fix

The gasless base-fee relaxation is applied in the block executor (OpBlockExecutor::execute_transaction_without_commit) and is also re-implemented for the flashblocks builder. The builder code even documents this explicitly — see crates/builder/src/flashblocks/context.rs, transact_maybe_gasless / is_gasless:

"Mirrors the gasless detection in the upstream block executor … The flashblocks builder executes pool transactions directly via Evm::transact rather than through the block executor, so the detection and base-fee relaxation have to be replicated here, otherwise zero-priced (whitelisted) transactions would be rejected by base-fee validation even when gasless is enabled."

The RPC tracing/replay path (the eth/debug/trace namespaces, delegated to the upstream OpEthApi replay_transactions_until) is a third place that executes transactions via Evm::transact outside the block executor, and it does not replicate the gasless detection. As a result, preceding gasless transactions are rejected during replay.

Two possible directions:

  1. Apply the same gasless detection (set_gasless(true) when the tx is a whitelisted zero-fee tx) when replaying preceding transactions in the RPC tracing path, mirroring transact_maybe_gasless.
  2. Disable base-fee validation for all transactions replayed during tracing (the tracer already disables it for the target transaction; extending this to the replayed predecessors would also resolve it and is arguably correct for tracing in general).

Impact

debug_traceTransaction, trace_transaction, trace_get, and trace_replayTransaction return null / error for any transaction positioned after a gasless transaction in the same block, despite the data being fully available on the node.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the RPC tracing replay entry point, replay_transactions_until, and compare its transaction execution with crates/builder/src/flashblocks/context.rs, especially transact_maybe_gasless and is_gasless. Reproduce the provided block 64,564,751 case with debug_traceTransaction, then verify that transactions after a gasless transaction trace successfully without changing block-level tracing behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.