Skip to content

client/asset/eth: handle swap fee rates below the network base fee#6

Open
peterzen wants to merge 4 commits into
masterfrom
evm-swap-basefee-check
Open

client/asset/eth: handle swap fee rates below the network base fee#6
peterzen wants to merge 4 commits into
masterfrom
evm-swap-basefee-check

Conversation

@peterzen

@peterzen peterzen commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Prevents stuck EVM swap settlements when the network base fee exceeds the server-assigned fee rate (the tx's gas fee cap).

Two commits: the first refuses to broadcast an unminable swap tx, leaving the nonce unspent so core can retry. The second improves on refusal: raise the fee cap to 2*baseFee funds permitting (the same rescue Redeem already uses; a higher cap is protocol-legal since ValidateFeeRate only enforces a floor), refusing only when balance cannot cover a minable cap. Applies to all EVM-family wallets (eth, polygon).

This is also the client-side prerequisite for live server fee-rate assignment (see companion server PR).

peterzen and others added 2 commits July 3, 2026 10:01
The server-assigned swap fee rate becomes the transaction's gas fee cap
verbatim. If the network's base fee exceeds it at broadcast time, the tx
cannot be mined until the base fee recedes, and meanwhile occupies the nonce,
blocking every subsequent transaction from the wallet until it is mined or
replaced.

Check the assigned rate against the current base fee before initiation and
error instead of broadcasting, leaving the nonce unspent. Core retries the
swap, which proceeds if the base fee recedes within the broadcast timeout.
The already-initiated recovery path is unaffected. Redeem already handles
this condition by raising its fee cap, funds permitting.

Co-Authored-By: Claude Fable 5 <[email protected]>
Rather than refusing outright when the server-assigned swap fee rate is
below the network's current base fee, attempt to raise the tx's gas fee cap
to 2*baseFee, funds permitting, mirroring the existing rescue in Redeem.
Under EIP-1559 the fee cap is not the fee paid, and the server's
ValidateFeeRate only requires the cap to be at least the assigned rate, so
a higher cap is protocol-legal. Refusal remains as a last resort when
available balance cannot cover a minable cap.

This also prepares for live (non-maximal) server fee rate assignment, where
a fee spike between match time and broadcast time would otherwise strand
honest traders.

Co-Authored-By: Claude Fable 5 <[email protected]>
@peterzen peterzen changed the title client/asset/eth: refuse to broadcast unminable swap txs client/asset/eth: handle swap fee rates below the network base fee Jul 3, 2026
@peterzen
peterzen requested a review from Copilot July 3, 2026 11:18

Copilot AI 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.

Pull request overview

This PR improves EVM-family (ETH/Polygon) swap broadcasting reliability under EIP-1559 by preventing initiation transactions from being broadcast with a gas fee cap below the network base fee, and by “rescuing” the cap upward (funds permitting) to keep swaps minable.

Changes:

  • Add swap fee-cap rescue logic to raise the swap tx gas fee cap to 2*baseFee when the server-assigned rate is below the current base fee, and refuse broadcast when a minable cap cannot be funded.
  • Update both ETH and token Swap paths to use currentNetworkFees base fee + improved error messaging and minability checks.
  • Extend Swap tests to validate fee-cap rescue behavior and refusal when unfunded.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
client/asset/eth/eth.go Adds fee-cap rescue + minability guard for swap initiations when base fee exceeds assigned cap.
client/asset/eth/eth_test.go Adds assertions for rescued fee cap and refusal behavior; captures tx option fee/tip caps for verification.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread client/asset/eth/eth.go Outdated
Comment on lines +3769 to +3774
additionalFundsNeeded := (2 * baseFeeGwei * gasLimit) - feesReserved
if bal.Available > additionalFundsNeeded {
feeRateGwei = 2 * baseFeeGwei
} else {
feeRateGwei = (bal.Available + feesReserved) / gasLimit
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 0eede22: the rescue budget is now computed in big.Int (the base fee is provider-reported, so absurd values must not overflow the decision), with a zero-gasLimit guard before the division and an IsUint64 sanity check on the result.

Comment on lines +228 to 231
n.lastTxOptsMaxFeeRate = maxFeeRate
n.lastTxOptsTipRate = tipRate
txOpts := newTxOpts(ctx, n.addr, val, maxGas, maxFeeRate, dexeth.GweiToWei(2))
txOpts.Nonce = big.NewInt(1)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Declined, with a comment added in 0eede22 explaining why: honoring the passed tipRate changes fee-derived decisions across existing fixtures tuned to the fixed 2 gwei tip (TestGaslessRedeem's submit/no-submit expectations flip). The new lastTxOptsTipRate capture field exposes the passed tip for assertions without perturbing unrelated tests, which is what this PR's assertions use.

peterzen and others added 2 commits July 3, 2026 19:49
Address review: the base fee is provider-reported, so compute the rescue
budget in big.Int to keep absurd values from overflowing the decision, and
guard the gasLimit division.

Co-Authored-By: Claude Fable 5 <[email protected]>
The initial maxFeeRate value derived from swaps.FeeRate was never read now
that the rescue result determines the fee cap; declare it at the point of
use instead. Fixes the ineffassign CI failure.

Co-Authored-By: Claude Fable 5 <[email protected]>
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