Conversation
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/max-amount-with-gas-station-fallback.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/max-amount-with-gas-station-fallback.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/max-amount-with-gas-station-fallback.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/max-amount-with-gas-station-fallback.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/max-amount-with-gas-station-fallback.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/types.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-quotes.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/gas-station.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/max-amount-with-gas-station-fallback.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Outdated
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Show resolved
Hide resolved
| return primaryEstimate; | ||
| } | ||
|
|
||
| const probeCost = await getProbeGasCostInSourceTokenRaw(context); |
There was a problem hiding this comment.
How can the above fail?
If no gas fee tokens are returned from the original quote, then should we not just fallback at that point?
Why would it help to try get another quote with a smaller amount?
There was a problem hiding this comment.
From testing, at full max, fee-token estimation/simulation can return no usable gas-fee-token result (or no match) because the transaction is too tight at the balance boundary.
With a smaller probe amount, the same route often becomes estimable and returns either:
isSourceGasFeeToken directly, or a usable getGasFeeTokens result we can normalize.
So this is a recovery path, not a bypass. It lets us rescue cases that would otherwise unnecessarily fall back.
There was a problem hiding this comment.
You're absolutely right, I'm missing the obvious, we will never get gas fee tokens in the original request since it's max so there is no remaining balance post-transaction for gas.
In that case, can we save time and skip the original identical request and go straight to the probe?
There was a problem hiding this comment.
Great question. We’re not doing an extra "original identical" quote request here. At this point, phase-1 is already fetched (and needed for fallback + phase-1/phase-2 validation), so we first try to derive gas cost from that existing quote (isSourceGasFeeToken or getGasFeeTokens via gas-station).
Probe is intentionally a recovery fallback when that estimate is unavailable. Going straight to probe would add an extra quote call for all max requests (typically 2 calls to 3), increasing latency/failure surface, while many flows already succeed without probing.
Should we add a short inline comment in getInitialGasCostEstimate to make this ordering explicit?
There was a problem hiding this comment.
For future ref, after discussion we changed this to fully reliant on the probe quote in this commit: 3efa3b0#diff-d11fa4fc2725e2c73d5d3c98eb76b3c31767c71109d9955199fcdd15b33b490d
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Outdated
Show resolved
Hide resolved
| return primaryEstimate; | ||
| } | ||
|
|
||
| const probeCost = await getProbeGasCostInSourceTokenRaw(context); |
There was a problem hiding this comment.
You're absolutely right, I'm missing the obvious, we will never get gas fee tokens in the original request since it's max so there is no remaining balance post-transaction for gas.
In that case, can we save time and skip the original identical request and go straight to the probe?
packages/transaction-pay-controller/src/strategy/relay/relay-max-gas-station.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Explanation
This PR introduces a dedicated max-amount gas-station fallback flow for Relay, extracted into a focused helper and wired into quote orchestration. The goal was fixing max-amount mUSD conversion failures caused by gas-fee-token estimation dead-ends but in fact it fixes for any kind of intent.
The helper (
getMaxAmountQuoteWithGasStationFallback) now implements a clear two-phase flow with explicit fallback points:maxGaslessEnabledisfalse.PROBE_AMOUNT_PERCENTAGE = 0.25) to discover gas fee token + amount.TransactionController:getGasFeeTokensandcalculateGasFeeTokenCostto normalize source-token gas fee amount.twoPhaseQuoteForMaxAmount = trueand return phase-2.References
gasless.max.mov
Checklist
Note
Medium Risk
Changes max-amount Relay quote computation and gas-fee-token costing logic, which can affect user spend limits and fee selection; behavior is guarded with multiple validation/fallback paths and extensive new tests.
Overview
Relay max-amount quotes now use a dedicated two-phase fallback: request the normal max quote, estimate gas cost in the source token via a probe quote and gas-station (
TransactionController:getGasFeeTokens), then request an adjusted max quote and only accept it if gas limits are compatible and affordability checks pass; otherwise it falls back to the original quote.Gas-station handling was extracted and reused via new
getGasStationEligibility/getGasStationCostInSourceTokenRaw, including normalization for multi-item gas limits and support for decimal simulation fields, and quotes are tagged withmetamask.isMaxGasStationwhen the adjusted path is used (with submit behavior covered by tests).Written by Cursor Bugbot for commit bb72ffa. This will update automatically on new commits. Configure here.