Skip to content

fix: Support gasless withdraw even with no gas fee tokens in EOA#8146

Open
dan437 wants to merge 14 commits intomainfrom
predict-balance-only
Open

fix: Support gasless withdraw even with no gas fee tokens in EOA#8146
dan437 wants to merge 14 commits intomainfrom
predict-balance-only

Conversation

@dan437
Copy link
Contributor

@dan437 dan437 commented Mar 9, 2026

Explanation

When a user has no native POL and no USDC.e in their EOA (tokens are in a Predict Safe), gasless withdrawals to non-USDC.e tokens fail with FAILED_INSUFFICIENT_FUNDS. The gas station simulation returns no fee tokens because the EOA balance is zero at quote time.

This PR skips the simulation for post-quote flows and builds a synthetic gas fee token by taking the estimated gas cost in USD (from calculateGasCost), applying a 10% buffer, and converting to USDC.e via its USD exchange rate. It also implements two-phase quoting: the first quote determines the gas cost, then a second quote is fetched with the source amount reduced by that cost. Source balance validation is skipped for post-quote flows since tokens move from Safe to EOA during batch execution.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Changes quote computation and gas-fee-token eligibility logic for Relay post-quote flows and alters submit-time balance validation, which could affect fee calculations and transaction batching behavior in edge cases.

Overview
Fixes gasless withdraw for post-quote flows (notably Predict Withdraw) by introducing two-phase Relay quoting: fetch a phase-1 quote to compute source-network gas cost in the gas fee token, then re-fetch a phase-2 quote with sourceTokenAmount reduced by that cost (with fallbacks to phase 1 if the adjusted amount is non-positive, phase 2 fails, or phase 2 loses gas-fee-token eligibility).

For Predict Withdraw specifically, gas estimation and gas-station simulation now use refundTo (proxy/Safe) as the effective from, including clearing any Relay-provided gas values when overriding from to force re-estimation. Relay submit additionally skips source token balance validation for post-quote flows since funds become available only after the original transaction executes in the batch. Adds isPredictWithdrawTransaction helper and updates the package changelog with the fix.

Written by Cursor Bugbot for commit c0550af. This will update automatically on new commits. Configure here.

@dan437 dan437 requested a review from a team as a code owner March 9, 2026 12:59
dan437 added 3 commits March 9, 2026 14:05
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
@dan437 dan437 requested a review from a team as a code owner March 9, 2026 13:11
dan437 added 7 commits March 10, 2026 10:07
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
…balance-only

Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

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.

dan437 added 2 commits March 11, 2026 16:59
Signed-off-by: dan437 <80175477+dan437@users.noreply.github.com>
* @param transaction - Transaction metadata.
* @returns `true` when the transaction is a Predict withdrawal.
*/
export function isPredictWithdrawTransaction(
Copy link
Member

Choose a reason for hiding this comment

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

Won't this transaction type always be top level?

fullRequest,
);

if (
Copy link
Member

Choose a reason for hiding this comment

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

To clarify, won't it always be the inverse in this scenario?

We don't have gas station to begin with, then we get it after we fix the quote?

Should we early return to skip all this if the user can already do gas station using token balance from their EOA?

messenger,
request.isPostQuote ? transaction : undefined,
);
request.isPostQuote && transaction.txParams.to
Copy link
Member

Choose a reason for hiding this comment

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

Why do we check to is present here?

: undefined;

if (paramGasLimit) {
if (paramGasLimit && !fromOverride) {
Copy link
Member

Choose a reason for hiding this comment

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

Makes sense, since we know param value in this gas is the 2.5M.

...singleParams,
gas: singleParams.gas ? toHex(singleParams.gas) : undefined,
gas:
!fromOverride && singleParams.gas
Copy link
Member

Choose a reason for hiding this comment

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

Also makes sense, since we want to force a estimation and not use the param value.

sourceChainId,
sourceTokenAddress,
},
totalGasEstimate: relayOnlyGas.totalGasEstimate,
Copy link
Member

Choose a reason for hiding this comment

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

Why are we only extrapolating to cover the total Relay gas limit, what about our original withdraw transaction gas that we added in totalGasEstimate?

totalGasEstimate: relayOnlyGas.totalGasEstimate,
// Simulation estimates gas for one relay step; force totalItemCount >= 2
// so normalization scales the cost to cover all relay gas.
totalItemCount: Math.max(
Copy link
Member

Choose a reason for hiding this comment

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

For predict withdraw, won't this always be relayParams.length + 1?

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.

[Bug]: Gasless withdraw doesn't work if a user doesn't have any gas fee token in EOA, only their Predict balance

2 participants