Skip to content

refactor: clamp legacy gas price and improve nonce management#378

Open
nahimterrazas wants to merge 2 commits into
mainfrom
h-18-27-30-fee-nonce-hardening
Open

refactor: clamp legacy gas price and improve nonce management#378
nahimterrazas wants to merge 2 commits into
mainfrom
h-18-27-30-fee-nonce-hardening

Conversation

@nahimterrazas
Copy link
Copy Markdown
Collaborator

@nahimterrazas nahimterrazas commented Jun 2, 2026

Summary

Testing Process

Checklist

  • Add a reference to related issues in the PR description.
  • Add unit tests if applicable.

Summary by CodeRabbit

  • Bug Fixes
    • Refined gas price resolution to clamp legacy gas-price fallbacks to the active fee policy cap, ensuring consistent fee parameters.
    • Improved nonce cache management to prevent the cache from moving below locally allocated nonces, preventing nonce conflicts.
    • Expanded fee-related error classification to handle additional "max fee below block base fee" scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

Warning

Review limit reached

@nahimterrazas, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 17 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b9978dc-84bc-47e5-86c7-55b356400e1e

📥 Commits

Reviewing files that changed from the base of the PR and between 89140bd and 4af328a.

📒 Files selected for processing (1)
  • crates/solver-delivery/src/implementations/evm/alloy.rs
📝 Walkthrough

Walkthrough

This PR hardens fee safety and nonce cache integrity in the EVM solver-delivery module. It introduces legacy gas price clamping to cap fallback gas prices against configured policy limits, applies this clamping in fee parameter resolution, and strengthens nonce allocation to prevent rollback from reissuing already-handed-out nonces while improving error classification for rollback decisions.

Changes

Fee and Nonce Safety

Layer / File(s) Summary
Legacy gas price clamping helper
crates/solver-delivery/src/implementations/evm/fees.rs
clamp_legacy_gas_price_to_cap utility returns the minimum of input gas price and configured policy cap, with unit test validating clamping behavior when cap is present.
Fee resolution with clamped fallbacks
crates/solver-delivery/src/implementations/evm/alloy.rs
Fee parameter resolution imports and applies clamping in two RPC fallback paths: eth_feeHistory failure logs uncapped/capped gas price and returns legacy FeeParams with capped value; base-fee resolution fallback to legacy path similarly clamps and logs the legacy gas price.
Nonce cache integrity and error classification
crates/solver-delivery/src/implementations/evm/nonce.rs, crates/solver-delivery/src/implementations/evm/alloy.rs
reset_next_nonce clamps provided nonce against local high-water mark to prevent reissuing already-allocated nonces; classify_submission_outcome expands DefinitelyRejected detection to include "fee cap below block base fee" error phrasings; comprehensive tests validate nonce clamping semantics and new error classifications; updated alloy test reflects high-water cache preservation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • shahnami
  • pepebndc

🐰 Nonces now clamped tight, fees capped with care,
No rollback regrets or price-cap snares,
Error strings decoded, the paths run true—
Fee safety and nonces, both shiny and new!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only empty section headings with no actual content and unchecked checklist items, failing to meet the template requirements. Fill in the Summary section with details about the changes, document the testing approach, and address or remove the unchecked checklist items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: clamping legacy gas price and improving nonce management, which aligns with the file-level summaries.
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 h-18-27-30-fee-nonce-hardening

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.

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/solver-delivery/src/implementations/evm/fees.rs (1)

582-596: ⚡ Quick win

Consider adding test cases for uncapped scenarios.

The current test validates the capping behavior when gas_price > cap, but it doesn't cover:

  1. When gas_price <= cap (should return gas_price unchanged)
  2. When gas_price_cap is None (should return gas_price unchanged)
🧪 Suggested additional test cases
#[test]
fn gas_price_cap_passes_through_when_below_cap() {
	let policy = ChainFeePolicy {
		speed: FeeSpeed::Fast,
		min_priority_fee_per_gas: Some(100_000_000),
		priority_fee_fallback: 100_000_000,
		quote_cost_strategy: FeeCostStrategy::BufferedEffective125,
		gas_price_cap: Some(2_500_000_000),
	};

	assert_eq!(
		super::clamp_legacy_gas_price_to_cap(1_000_000_000, &policy),
		1_000_000_000
	);
}

#[test]
fn gas_price_cap_passes_through_when_none() {
	let policy = ChainFeePolicy {
		speed: FeeSpeed::Fast,
		min_priority_fee_per_gas: Some(100_000_000),
		priority_fee_fallback: 100_000_000,
		quote_cost_strategy: FeeCostStrategy::BufferedEffective125,
		gas_price_cap: None,
	};

	assert_eq!(
		super::clamp_legacy_gas_price_to_cap(10_000_000_000, &policy),
		10_000_000_000
	);
}
🤖 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/solver-delivery/src/implementations/evm/fees.rs` around lines 582 -
596, Add two unit tests to cover uncapped scenarios for
clamp_legacy_gas_price_to_cap: (1) a test (e.g.,
gas_price_cap_passes_through_when_below_cap) that constructs a ChainFeePolicy
with gas_price_cap = Some(2_500_000_000) and asserts
clamp_legacy_gas_price_to_cap(1_000_000_000, &policy) returns 1_000_000_000, and
(2) a test (e.g., gas_price_cap_passes_through_when_none) that sets
gas_price_cap = None and asserts clamp_legacy_gas_price_to_cap(10_000_000_000,
&policy) returns 10_000_000_000; keep other policy fields identical to existing
tests so you exercise the pass-through behavior when below the cap and when no
cap is set.
🤖 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/solver-delivery/src/implementations/evm/fees.rs`:
- Around line 582-596: Add two unit tests to cover uncapped scenarios for
clamp_legacy_gas_price_to_cap: (1) a test (e.g.,
gas_price_cap_passes_through_when_below_cap) that constructs a ChainFeePolicy
with gas_price_cap = Some(2_500_000_000) and asserts
clamp_legacy_gas_price_to_cap(1_000_000_000, &policy) returns 1_000_000_000, and
(2) a test (e.g., gas_price_cap_passes_through_when_none) that sets
gas_price_cap = None and asserts clamp_legacy_gas_price_to_cap(10_000_000_000,
&policy) returns 10_000_000_000; keep other policy fields identical to existing
tests so you exercise the pass-through behavior when below the cap and when no
cap is set.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0e4ad63-1ae7-4079-bf08-51343e40f9d7

📥 Commits

Reviewing files that changed from the base of the PR and between 801619b and 89140bd.

📒 Files selected for processing (3)
  • crates/solver-delivery/src/implementations/evm/alloy.rs
  • crates/solver-delivery/src/implementations/evm/fees.rs
  • crates/solver-delivery/src/implementations/evm/nonce.rs

Copy link
Copy Markdown
Collaborator

@pepebndc pepebndc left a comment

Choose a reason for hiding this comment

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

Validated against scan findings H-18, H-27, H-30. H-30 and H-18 are correctly fixed, but H-27 is only partially resolved — requesting changes.

H-30 (legacy gas cap) — ✅ fixed. clamp_legacy_gas_price_to_cap is wired into both legacy fallback branches in get_fee_params (alloy.rs ~2721 and ~2753).

H-18 (nonce-reuse safety) — ✅ fixed. reset_next_nonce now returns local.max(next), so a stale-pending rollback can't move the cache below the local high-water mark.

H-27 (fee-cap/base-fee misclassification → nonce wedge) — ⚠️ major gap. The reclassification to DefinitelyRejected is correct, but the rollback it now triggers is neutralized by this same PR's H-18 clamp. For the common single-in-flight-tx case (cache at N+1, chain pending=N because the tx never entered the pool), reset_next_nonce returns max(N+1, N) = N+1, so nonce N is never reclaimed. next_nonce_for uses the forward-only set_next_nonce and the drift monitor only logs — nothing frees the leaked nonce, so the permanent nonce gap / liveness wedge the finding describes still occurs. The PR's own renamed test apply_nonce_cache_action_rollback_with_pending_preserves_high_water_cache demonstrates the rollback is now a no-op (cache stays 101 when pending=100). The added H-27 tests only assert classification strings, not end-to-end nonce reclamation.

Asks:

  1. Reconcile H-18 vs H-27: after a DefinitelyRejected (never-broadcast) outcome, the leaked nonce must actually be reclaimed for the lone-in-flight case. Consider scoping the rollback to the rejected attempt's nonce rather than relying on the high-water clamp, and add a test asserting the next next_nonce_for returns N (not N+1).

Minor (non-blocking):

  • H-18's clamp removes the backward-reset that the nonce-too-low resync path still depends on; the comments at alloy.rs:1851-1854 / 723 and the resync trace log now contradict actual behavior — please update or rescope.
  • H-27 matches 3 substring phrasings rather than structured RPC error codes; other providers' fee-cap strings fall back to Ambiguous.

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