Commit a9111b3
feat(ramp): Converts payment methods and quotes to use react-query for requ… (#27448)
## **Description**
Fixes the "Token Not Available" modal and payment methods loading
behavior in the Buy flow. Three issues are addressed:
### 1. React-query migration for request status tracking
Previously, the controller only exposed `isLoading` (boolean) and `data`
(array). This made it impossible to distinguish between:
- **Never fetched yet** (idle) — `isLoading: false`, `data: []`
- **Fetched successfully with no results** (token genuinely unavailable)
— `isLoading: false`, `data: []`
This ambiguity caused the app to flash the "Token Not Available" modal
immediately when switching providers, before payment methods had even
been fetched.
By using react-query, we now have explicit status tracking (`idle` |
`loading` | `success` | `error`) plus `isFetching` for background
refetches. The "Token Not Available" modal only shows when
`paymentMethodsStatus === 'success' && !paymentMethodsFetching &&
paymentMethods.length === 0`.
### 2. Fix token unavailability detection across all Buy entry points
The `isTokenUnavailable` check in `BuildQuote` previously relied solely
on `params?.assetId` (route navigation params). This worked for the
**token buy** flow (`Home → Tokens → Token Info → Buy`) which passes
`assetId` via `createBuildQuoteNavDetails`, but failed for the **home
buy** flow (`Home → Buy → Token Selection → BuildQuote`) where
`params.assetId` could be missing or stale.
The fix: treat `route.params.assetId` as bootstrapping input only. Once
on the BuildQuote screen, derive the effective asset ID from the
controller state (`selectedToken?.assetId`) as the source of truth,
falling back to `params?.assetId`.
### 3. Fix navigation behavior for three distinct Buy flow origins
The "Token Not Available" modal previously had a single
dismiss/change-token behavior regardless of how the user entered the Buy
flow. This caused a crash (`cannot read property 'address' of
undefined`) when navigating back via `navigation.navigate('Asset')`
without params from the token info flow.
Introduced `BuyFlowOrigin` type (`'tokenInfo' | 'homeTokenList' |
undefined`) to distinguish the three entry points and handle navigation
correctly:
- **Token info flow** (`tokenInfo`): "Change token" → Tokens Full View;
dismiss → `goBack()` twice (exits ramp flow, preserves Asset screen
params)
- **Home token list flow** (`homeTokenList`): "Change token" → Home;
dismiss → Home
- **Default/home buy flow** (`undefined`): "Change token" → Token
Selection; dismiss → Token Selection
Additional fixes:
- 600ms debounce on modal navigation to prevent flashing when
`isTokenUnavailable` is briefly true due to stale cached data
- `lastShownUnavailableKeyRef` dedup pattern (`providerId:assetId`) to
prevent duplicate modal navigations
- `focusTrigger` counter via `useFocusEffect` to force modal
re-evaluation on screen re-focus (e.g., after returning from provider
picker)
- `isOnBuildQuoteScreen` guard to prevent effects firing when screen is
in background
- Payment pill disabled when token is unavailable
(`onPress={isTokenUnavailable ? undefined : handlePaymentPillPress}`)
### Changes
- **New `queries/paymentMethods.ts`** — react-query `queryOptions` for
fetching payment methods via `RampsController.getPaymentMethods`, keyed
by region/fiat/assetId/providerId
- **New `queries/quotes.ts`** — react-query `queryOptions` for fetching
quotes via `RampsController.getQuotes`, keyed by
assetId/amount/wallet/paymentMethod/provider
- **New `queries/index.ts`** — barrel export combining both query
definitions
- **Updated `useRampsPaymentMethods.ts`** — rewired to use `useQuery`
instead of reading from Redux store; exposes `status`, `isFetching`,
`isSuccess`, `isLoading`, and `error`
- **Updated `useRampsQuotes.ts`** — rewired to use `useQuery` with
proper `enabled` gating; exposes `status`, `isSuccess`, `loading`, and
`error`
- **Updated `useRampsController.ts`** — passes through
`paymentMethodsStatus` and `paymentMethodsFetching` from the payment
methods hook
- **Updated `BuildQuote.tsx`** — derives `effectiveAssetId` from
controller state (source of truth) with route params as fallback; uses
`paymentMethodsStatus === 'success' && !paymentMethodsFetching` check
before determining token unavailability; 600ms debounced modal
navigation with dedup; `BuyFlowOrigin` passthrough; disabled payment
pill when unavailable
- **Updated `TokenNotAvailableModal.tsx`** — handles three flows via
`buyFlowOrigin` param with correct navigation for each
- **Updated `useRampNavigation.ts`** — passes `buyFlowOrigin` through to
`createBuildQuoteNavDetails`
- **Updated `useTokenActions.ts`** — passes `{ buyFlowOrigin:
'tokenInfo' }` to `goToBuy`
- **Updated `PopularTokenRow.tsx`** — passes `{ buyFlowOrigin:
'homeTokenList' }` to `goToBuy`
- **Unit tests** — added/updated tests for `BuildQuote`,
`TokenNotAvailableModal`, `useRampNavigation`, `useRampsPaymentMethods`,
`useRampsQuotes`, `useRampsController`, and query definitions
## **Changelog**
CHANGELOG entry: Fixed false "Token Not Available" errors during Buy
flow when payment methods are still loading after provider change; fixed
missing "Token Not Available" modal in home buy flow; fixed crash when
navigating back from "Token Not Available" modal in token info buy flow
## **Related issues**
Refs:
[TRAM-3314](https://consensyssoftware.atlassian.net/browse/TRAM-3314)
## **Manual testing steps**
### Step-by-step walkthrough for testing
**Test 1 — Home buy flow (the fixed path):**
1. Launch the app in the iOS
2. Tap **Buy** on the home screen
3. In the Token Selection screen, pick a niche token (e.g. **TRX on
Tron**)
4. On the BuildQuote screen, verify the **"Token Not Available" modal**
appears after loading
5. Press "Change token" — should go back to token selection
6. Press "Change provider" — should open provider picker
**Test 2 — Token buy flow (existing path, verify no regression + crash
fix):**
1. From the Home screen, tap on a token in the Tokens list (e.g.
**TRX**)
2. On the Token Info screen (with graph), tap **Buy**
3. Verify the **"Token Not Available" modal** appears after loading
4. Press "Change token" — should go to Tokens Full View
5. Dismiss the modal — should go back to Token Info (NOT crash)
**Test 3 — Home token list buy flow:**
1. From the Home screen, tap the **Buy** button next to a token in the
token list
2. If the token is unavailable, verify the modal appears
3. Press "Change token" — should go to Home screen
4. Dismiss — should go to Home screen
**Test 4 — Re-selecting tokens:**
1. Enter via token buy flow (Token Info → Buy) with a supported token
2. Go back to token selection
3. Select a token that is unavailable with the current provider
4. Verify the "Token Not Available" modal appears (previously it did NOT
in this path)
**Test 5 — Modal re-appears after provider change:**
1. Enter Buy flow with an unavailable token
2. After modal appears, tap "Change provider"
3. Select another provider that also doesn't support the token
4. Verify the modal appears again (previously it only showed once due to
dedup)
## **Screenshots/Recordings**
### **Before**
<!-- Video/screenshot showing the missing modal in home buy flow -->
https://github.com/user-attachments/assets/efff2d42-698c-4084-8e64-8857852bd34a
### **After**
<!-- Video/screenshot showing the modal appearing correctly in both
flows -->
https://github.com/user-attachments/assets/e8803901-9d83-4803-a324-1787c0a630ea
https://github.com/user-attachments/assets/e2f8b51a-4e12-44c6-8d75-d83d8083929e
https://github.com/user-attachments/assets/37a56f6c-af24-4cf4-85a6-00929312b41f
### **After rebasing 7.71.0**
https://github.com/user-attachments/assets/db1da8ea-3641-4302-95cf-39735b6d9260
https://github.com/user-attachments/assets/d9598eda-2f5a-4788-b523-6e3b0738a189
https://github.com/user-attachments/assets/930866b3-0a75-454c-b5cc-96e132a9df7e
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches the core Buy flow by changing how payment methods/quotes are
fetched and how the Token Unavailable modal is triggered/navigated,
which can affect ramp availability and navigation behavior across entry
points.
>
> **Overview**
> **Improves Buy-flow reliability by migrating `paymentMethods` and
`quotes` fetching to `@tanstack/react-query`** (new `rampsQueries` with
stable keys/options) and exposing richer request state (`status`,
`isFetching`, `isSuccess`) through `useRampsPaymentMethods`,
`useRampsQuotes`, and `useRampsController`.
>
> **Fixes Token Unavailable handling in `BuildQuote`** by deriving the
effective token from controller state (not just route params), gating
the modal on settled payment-methods state, and adding focus-aware,
de-duped, 600ms-debounced modal navigation; it also disables the
payment-method pill when the token is unavailable.
>
> **Adds `BuyFlowOrigin` plumbing (`tokenInfo` | `homeTokenList`)**
through `useRampNavigation`/callers and updates `TokenNotAvailableModal`
to return the user to the correct screen on change-token/dismiss for
each entry path. Tests are updated/added to cover the new query state
and navigation behaviors, and the Ramp routes are wrapped in a
`QueryClientProvider`.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
45f4c0a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: George Weiler <george.weiler@consensys.net>1 parent 2c92ecc commit a9111b3
28 files changed
Lines changed: 1178 additions & 426 deletions
File tree
- app/components
- UI
- Ramp
- Aggregator/Views/Settings
- Views
- BuildQuote
- Modals
- ProviderSelectionModal
- TokenNotAvailableModal
- Settings/RegionSelector
- TokenSelection
- hooks
- queries
- TokenDetails/hooks
- Views
- Homepage/Sections/Tokens/components
- confirmations/components/info/custom-amount-info
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
| 139 | + | |
| 140 | + | |
139 | 141 | | |
140 | 142 | | |
141 | 143 | | |
| |||
Lines changed: 190 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
89 | 90 | | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
90 | 98 | | |
91 | 99 | | |
92 | 100 | | |
| |||
354 | 362 | | |
355 | 363 | | |
356 | 364 | | |
| 365 | + | |
357 | 366 | | |
358 | 367 | | |
359 | 368 | | |
360 | 369 | | |
361 | 370 | | |
| 371 | + | |
| 372 | + | |
362 | 373 | | |
363 | 374 | | |
364 | 375 | | |
| |||
676 | 687 | | |
677 | 688 | | |
678 | 689 | | |
| 690 | + | |
679 | 691 | | |
680 | 692 | | |
| 693 | + | |
| 694 | + | |
681 | 695 | | |
| 696 | + | |
| 697 | + | |
682 | 698 | | |
683 | 699 | | |
684 | 700 | | |
| |||
843 | 859 | | |
844 | 860 | | |
845 | 861 | | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
| 883 | + | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
| 901 | + | |
| 902 | + | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
| 972 | + | |
| 973 | + | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
| 981 | + | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
| 1033 | + | |
| 1034 | + | |
| 1035 | + | |
846 | 1036 | | |
0 commit comments