Skip to content

fix(evm): buffer arbitrum gasPrice/maxFeePerGas to prevent base-fee-drift tx failures#1282

Merged
kaladinlight merged 2 commits into
developfrom
fix/arbitrum-gas-fee-headroom
Jul 10, 2026
Merged

fix(evm): buffer arbitrum gasPrice/maxFeePerGas to prevent base-fee-drift tx failures#1282
kaladinlight merged 2 commits into
developfrom
fix/arbitrum-gas-fee-headroom

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Jul 10, 2026

Copy link
Copy Markdown
Member

Problem

Arbitrum /fees was occasionally returning suspect data (identical slow/average/fast tiers, maxPriorityFeePerGas: 0), and clients were hitting transaction failures:

max fee per gas less than block base fee: maxFeePerGas: 20072000 baseFee: 20110000

Root cause

Arbitrum's sequencer orders transactions first-come-first-served and ignores priority fees, so eth_feeHistory reports 0 rewards at every percentile (confirmed against a live RPC — 0/100 blocks non-zero at percentiles 50/70/90). Two consequences in getGasFees:

  1. maxPriorityFeePerGas falls back to minPriorityFee (0 for Arbitrum), collapsing all three tiers to identical values.
  2. gasPrice = baseFee + 0 = baseFee — pinned to the bare base fee with zero headroom. Arbitrum's base fee jitters ~1% block-to-block, so any upward tick between estimation and inclusion rejects the tx. The rejected value 20072000 matches gasPrice exactly (not maxFeePerGas, which was baseFee × 2 ≈ 40M), confirming the client consumes the gasPrice field for Arbitrum.

This is a regression from the Blocknative → eth_feeHistory swap (#1281); Blocknative's price carried predictive headroom.

Fix

Make the base-fee and gas-price tier multipliers configurable per chain (same pattern as minPriorityFee), defaulting to the exact prior behavior so no other coinstack changes. Arbitrum opts into per-tier buffers:

  • gasPriceMultiplier: [1.1, 1.2, 1.5] — modest headroom so legacy/gasPrice txs stay above the base fee as it drifts, without meaningfully overpaying (gasPrice is the amount actually paid).
  • baseFeeMultiplier: [1.5, 2, 3] — larger cap-only headroom on maxFeePerGas (a cap, so no overpay) and restores tier differentiation.

In the failing scenario, slow.gasPrice becomes 22079200, comfortably above the 20110000 base fee that rejected the tx. Invariants verified: maxFeePerGas ≥ gasPrice, gasPrice > baseFee, and tiers monotonically increasing.

Scope

Validated against live RPCs that Optimism and Base are unaffected — both show non-zero priority fees at every percentile (100/100 blocks) and stable base fees, so gasPrice already has ample headroom there. Change is Arbitrum-only; all other chains keep their current behavior via the defaults.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements
    • Updated gas fee estimates to use tier-specific multipliers for slow, average, and fast transaction speeds.
    • Improved gas price and maximum fee calculations for Arbitrum transactions.
    • Arbitrum gas-related endpoints now provide more differentiated fee estimates across speed options.

…rift tx failures

Arbitrum's sequencer orders transactions first-come-first-served and
ignores priority fees, so eth_feeHistory reports 0 rewards at every
percentile. That collapsed all three fee tiers to identical values and,
more importantly, set gasPrice to the bare base fee with zero headroom.
Clients submitting that gasPrice failed with "max fee per gas less than
block base fee" on any upward base-fee tick.

Make the base fee and gas price tier multipliers configurable per chain
(defaulting to the previous behavior, so no other coinstack changes) and
opt Arbitrum into per-tier buffers: modest headroom on gasPrice to
survive base-fee drift without overpaying legacy txs, and larger cap-only
headroom on maxFeePerGas. Verified Optimism and Base have non-zero
priority fees and stable base fees, so they are unaffected and unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kaladinlight
kaladinlight requested a review from a team as a code owner July 10, 2026 17:49
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kaladinlight, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 83eb6331-2c50-4d1b-bafa-4aae9d594388

📥 Commits

Reviewing files that changed from the base of the PR and between c246b14 and 07940ac.

📒 Files selected for processing (1)
  • node/coinstacks/common/api/src/evm/moralisService.ts
📝 Walkthrough

Walkthrough

MoralisService now supports per-tier base-fee and gas-price multipliers, applies them to gas estimates, and allows the Arbitrum controller to provide chain-specific multiplier arrays.

Changes

Gas multiplier configuration

Layer / File(s) Summary
Multiplier contract and initialization
node/coinstacks/common/api/src/evm/moralisService.ts
Adds optional per-tier multiplier arguments, default arrays, persisted fields, and constructor initialization.
Tiered fee calculation and Arbitrum wiring
node/coinstacks/common/api/src/evm/moralisService.ts, node/coinstacks/arbitrum/api/src/controller.ts
getGasFees applies slow, average, and fast multipliers; Arbitrum configures [1.5, 2, 3] base-fee and [1.1, 1.2, 1.5] gas-price multipliers.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Poem

A rabbit hops where gas fees grow,
With slow, average, fast in a row.
Arbitrum brings its numbers bright,
Service turns tiers into speed and might.
Hop, hop—estimates take flight!

🚥 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 summarizes the Arbitrum gas-fee buffering change and its purpose of preventing base-fee-drift transaction failures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/arbitrum-gas-fee-headroom

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.

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

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 `@node/coinstacks/common/api/src/evm/moralisService.ts`:
- Around line 398-399: Add constructor-time validation in the relevant Moralis
service class to ensure the user-supplied baseFeeMultiplier and
gasPriceMultiplier arrays each have at least REWARD_PERCENTILES.length entries
before getGasFees() can index them; reject invalid configurations with a clear
error.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0f483718-d873-4c26-bd0a-3eff12de4ed6

📥 Commits

Reviewing files that changed from the base of the PR and between f5c7f66 and c246b14.

📒 Files selected for processing (2)
  • node/coinstacks/arbitrum/api/src/controller.ts
  • node/coinstacks/common/api/src/evm/moralisService.ts

Comment thread node/coinstacks/common/api/src/evm/moralisService.ts
Type baseFeeMultiplier and gasPriceMultiplier as [number, number, number]
so a wrong-length array is caught at compile time rather than silently
producing NaN fees when getGasFees indexes a missing tier. Addresses
CodeRabbit review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kaladinlight
kaladinlight merged commit 2cf011d into develop Jul 10, 2026
3 checks passed
@kaladinlight
kaladinlight deleted the fix/arbitrum-gas-fee-headroom branch July 10, 2026 18:13
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.

1 participant