fix(quick-buy): show all held tokens in Pay with and split Pay with/Receive flows#31195
Conversation
…eceive flows The "Pay with" picker only offered a curated stablecoin/native allowlist, so held tokens like CAKE, UP and dogwifhat were missing. It now sources every tradable token the user holds across the bridge-enabled source chains. Also splits the previously combined screen into clearly separated "Pay with" (buy) and "Receive" (sell) flows over a shared presentational list, extracts a reusable `enrichTokenBalance` helper and `tokenKey` util, and fixes fiat display: balances are USD-denominated so they are now formatted as USD instead of being mislabeled with the user's selected currency symbol. Co-authored-by: Cursor <cursoragent@cursor.com>
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
Auto-formatter import ordering only; no logic changes. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduces a new utility function `isStablecoinSymbol` to validate stablecoin symbols, ensuring case-insensitive matching. This functionality is integrated into the `usePayWithTokens` and `useReceiveTokens` hooks to apply a $1 fallback rate for stablecoins only. Additionally, comprehensive tests for the stablecoin validation logic have been added to ensure accuracy and reliability.
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
SmokePerps: Directly relevant - the Perps Add Funds flow uses QuickBuy for token selection. The changed hooks ( SmokeSwap: Relevant because QuickBuy uses bridge/swap infrastructure ( SmokeConfirmations: Required per SmokePerps tag description - "Add Funds deposits are on-chain transactions" that go through confirmations. SmokeWalletPlatform: Required per SmokePerps tag description - "Perps is also a section inside the Trending tab (SmokeWalletPlatform)". No E2E spec files directly test QuickBuy/SocialLeaderboard, so the risk is medium - the changes are internal refactoring with a meaningful behavioral change in token sourcing. Confidence is moderate because no direct E2E tests exist for this feature path. Performance Test Selection: |
Refactors the `enrichTokenBalance` function to remove the fallback exchange rate option, streamlining the handling of tokens without market prices. Updates tests to reflect the new behavior, ensuring that tokens with no resolvable price return null or a zero balance as appropriate. Adjusts related tests in `usePayWithTokens` and `useReceiveTokens` to align with the changes in token enrichment logic.
…iceable tokens Adds new test cases to the `enrichTokenBalance` function to verify that it correctly handles unheld tokens by returning a zero balance when lenient, and retains the actual balance for held but unpriceable tokens. Updates existing test descriptions for clarity and consistency. This ensures comprehensive coverage of token enrichment scenarios.
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.
Reviewed by Cursor Bugbot for commit f31322e. Configure here.
| // Scope holdings to networks the user has enabled in wallet settings. | ||
| if (!isChainEnabled(token.chainId)) continue; | ||
| const enrichment = enrichTokenBalance(token, deps); | ||
| if (!enrichment) continue; |
There was a problem hiding this comment.
Pay-with list vs balance account
Medium Severity
usePayWithTokens builds the picker from useTokensWithBalance (account-group EOA and selectAccountTokensAcrossChainsForAddress), but enrichTokenBalance reads balances using selectSelectedInternalAccountByScope(EVM_SCOPE). When those addresses differ, held tokens can be dropped or priced from the wrong wallet despite appearing in the Bridge token list.
Reviewed by Cursor Bugbot for commit f31322e. Configure here.


Description
In the QuickBuy bottom sheet, the Pay with picker only listed a curated allowlist of native tokens and stablecoins. Tokens the user actually held (e.g. CAKE, UP, dogwifhat) were missing, so they couldn't pay with them.
This PR:
usePayWithTokensnow sources every tradable token the user holds across the bridge-enabled source chains (sameuseTokensWithBalancesource the Bridge/Swap pickers use), instead of a hardcoded candidate list.tradeMode. It is now split intoQuickBuyPayWithScreen(buy / "Pay with") andQuickBuyReceiveScreen(sell / "Receive"), both rendering a shared presentationalQuickBuyTokenSelectList, routed by a smallQuickBuyTokenSelectScreendispatcher.enrichTokenBalancehelper reused by both the "Pay with" and "Receive" hooks, plus a sharedtokenKeyutil.currencyExchangeRateis USD-per-token and the amount entry renders$). The balance fiat was computed fromusdConversionRatebut formatted with the user's selected currency, mislabeling a USD value as e.g.€2000.00. Fiat fields are now formatted as USD to match the value and the rest of the flow.Changelog
CHANGELOG entry: Fixed QuickBuy "Pay with" not showing all tokens the user holds, and corrected the fiat amount currency shown for non-USD users.
Related issues
Fixes: TSA-609
Manual testing steps
Screenshots/Recordings
Before
After
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist
Made with Cursor
Note
Medium Risk
Changes which tokens can fund trades and how USD rates are computed for amount entry; sell receive stables behavior is preserved but enrichment rules (no stable $1 fallback on Pay with) differ from the old hook.
Overview
Fixes QuickBuy Pay with so users see all held, tradable tokens (via Bridge’s
useTokensWithBalance) instead of a hardcoded native/stable allowlist, with strict pricing so only tokens with a resolvable USD rate appear.Pay with and Receive are split: buy uses
QuickBuyPayWithScreen, sell usesQuickBuyReceiveScreen, both backed by sharedQuickBuyTokenSelectListand routed byQuickBuyTokenSelectScreenon the existingpayWithscreen.Token balance/fiat/exchange-rate logic moves into pure
enrichTokenBalance(used by newusePayWithTokensanduseReceiveTokens);sourceTokenCandidates/useSourceTokenOptionsare removed. Fiat labels in the picker are formatted as USD to match QuickBuy’s USD-first amount flow (fixes wrong currency symbol for non-USD display settings).Reviewed by Cursor Bugbot for commit f31322e. Bugbot is set up for automated code reviews on this repo. Configure here.