Skip to content

Commit 8a03f66

Browse files
authored
fix(ramps): Preserve user-entered amount during Transak navigation reset -> cp-7.71.0 (#27742)
## **Description** Fixes the Buy flow amount reverting from the user-entered value back to the default $100 during the Transak native provider loading transition. When a user enters a custom amount (e.g. $30) and taps Continue, the Transak routing callbacks use `navigation.reset()` to rebuild the navigation stack with a fresh `BuildQuote` screen as the base route. This fresh instance initialized with `DEFAULT_AMOUNT = 100`, causing a visible flash of $100 during the transition to the checkout/KYC screen. The fix passes the current `quote.fiatAmount` as a route param (`amount`) to the `AMOUNT_INPUT` base route in every `navigation.reset()` call. `BuildQuote` now reads `params?.amount` as the initial state, preserving the user-entered amount through stack resets. ## **Changelog** CHANGELOG entry: Fixed Buy flow amount input reverting to $100 during Transak native provider checkout transition. ## **Related issues** Fixes: [TRAM-3348](https://consensyssoftware.atlassian.net/browse/TRAM-3348) ## **Manual testing steps** ```gherkin Feature: Amount persists through Transak native provider checkout transition Scenario: Custom amount does not revert to default during Continue loading Given the user is on the Buy screen with Transak Native provider And the default amount is $100 When the user changes the amount to $30 And the user taps Continue Then the displayed amount remains $30 during the loading transition And the amount does not flash back to $100 Scenario: Default amount is preserved when no custom amount is entered Given the user is on the Buy screen with Transak Native provider And the default amount is $100 When the user taps Continue without changing the amount Then the displayed amount remains $100 throughout the flow Scenario: Amount persists when navigating back from KYC/checkout screens Given the user entered $50 and proceeded through Continue When the user navigates back to the Buy screen Then the amount input shows $50 (not $100) ``` ## **Screenshots/Recordings** ### **Before** <!-- Screenshot/video showing amount reverting from $30 to $100 during loading --> https://github.com/user-attachments/assets/ba7fb42b-c43a-43f8-b438-0484090d7895 ### **After** <!-- Screenshot/video showing amount staying at $30 during loading transition --> https://github.com/user-attachments/assets/6d60c1ae-f0eb-448b-b7f3-297efbce85df https://github.com/user-attachments/assets/099ebf69-face-4bb0-8568-80357f59b35e <img width="523" height="877" alt="Screenshot 2026-03-20 175035" src="https://github.com/user-attachments/assets/6bebcd4a-c04d-40c6-90d4-7e50ed683824" /> ## **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 - [x] 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** > Updates Transak native-provider navigation/reset logic and initial screen state; mistakes could regress Buy/KYC routing or amount display during transitions, but changes are localized and covered by tests. > > **Overview** > Fixes the Transak native Buy flow so the user-entered fiat amount is preserved when the app uses `navigation.reset()` during KYC/checkout transitions. > > `BuildQuote` now supports an `amount` route param to initialize the amount state (and to prevent region defaults from overriding it), and `useTransakRouting` propagates this amount through all reset-based navigation paths (KYC approved → checkout, KYC forms, additional verification, verify identity, and KYC webview). Tests are updated/added to assert the amount is passed through routing callbacks and used as the initial displayed value. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit a8bdee0. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 2bcd74c commit 8a03f66

6 files changed

Lines changed: 207 additions & 54 deletions

File tree

app/components/UI/Ramp/Views/BuildQuote/BuildQuote.test.tsx

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,41 @@ describe('BuildQuote', () => {
402402
});
403403
});
404404

405+
describe('amount param initialization', () => {
406+
it('uses DEFAULT_AMOUNT (100) when no amount param is provided', () => {
407+
mockUseParams.mockReturnValue({});
408+
409+
const { getByTestId } = renderWithProvider(<BuildQuote />, {
410+
state: initialRootState,
411+
});
412+
413+
const amountInput = getByTestId(BuildQuoteSelectors.AMOUNT_INPUT);
414+
expect(amountInput.props.children).toContain('100');
415+
});
416+
417+
it('uses amount param as initial value when provided via route params', () => {
418+
mockUseParams.mockReturnValue({ amount: 30 });
419+
420+
const { getByTestId } = renderWithProvider(<BuildQuote />, {
421+
state: initialRootState,
422+
});
423+
424+
const amountInput = getByTestId(BuildQuoteSelectors.AMOUNT_INPUT);
425+
expect(amountInput.props.children).toContain('30');
426+
});
427+
428+
it('does not override amount with region default when amount param is provided', () => {
429+
mockUseParams.mockReturnValue({ amount: 50 });
430+
431+
const { getByTestId } = renderWithProvider(<BuildQuote />, {
432+
state: initialRootState,
433+
});
434+
435+
const amountInput = getByTestId(BuildQuoteSelectors.AMOUNT_INPUT);
436+
expect(amountInput.props.children).toContain('50');
437+
});
438+
});
439+
405440
describe('navigateAfterExternalBrowser', () => {
406441
it('resets to BuildQuote when returnDestination is buildQuote (Android external browser path)', async () => {
407442
mockDeviceIsAndroid.mockReturnValue(true);
@@ -725,7 +760,7 @@ describe('BuildQuote', () => {
725760
'/payments/debit-credit-card',
726761
'100',
727762
);
728-
expect(mockRouteAfterAuth).toHaveBeenCalledWith(MOCK_TRANSAK_QUOTE);
763+
expect(mockRouteAfterAuth).toHaveBeenCalledWith(MOCK_TRANSAK_QUOTE, 100);
729764
});
730765

731766
it('navigates to VerifyIdentity when user has no token', async () => {

app/components/UI/Ramp/Views/BuildQuote/BuildQuote.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ export interface BuildQuoteParams {
109109
nativeFlowError?: string;
110110
/** Which flow the user used to enter the Buy screen. */
111111
buyFlowOrigin?: BuyFlowOrigin;
112+
/** Pre-fill the amount input (e.g. when restoring state after a navigation reset). */
113+
amount?: number;
112114
}
113115

114116
/**
@@ -148,13 +150,17 @@ function BuildQuote() {
148150
const { formatCurrency } = useFormatters();
149151
const cursorOpacity = useBlinkingCursor();
150152

151-
const [amount, setAmount] = useState<string>(() => String(DEFAULT_AMOUNT));
152-
const [amountAsNumber, setAmountAsNumber] = useState<number>(DEFAULT_AMOUNT);
153-
const [userHasEnteredAmount, setUserHasEnteredAmount] = useState(false);
153+
const params = useParams<BuildQuoteParams>();
154+
const initialAmount = params?.amount ?? DEFAULT_AMOUNT;
155+
156+
const [amount, setAmount] = useState<string>(() => String(initialAmount));
157+
const [amountAsNumber, setAmountAsNumber] = useState<number>(initialAmount);
158+
const [userHasEnteredAmount, setUserHasEnteredAmount] = useState(
159+
params?.amount != null,
160+
);
154161
const [keyboardIsDirty, setKeyboardIsDirty] = useState(false);
155162
const [isContinueLoading, setIsContinueLoading] = useState(false);
156163
const [rampsError, setRampsError] = useState<string | null>(null);
157-
const params = useParams<BuildQuoteParams>();
158164

159165
useEffect(() => {
160166
if (params?.nativeFlowError) {
@@ -573,7 +579,7 @@ function BuildQuote() {
573579
if (!quote) {
574580
throw new Error(strings('deposit.buildQuote.unexpectedError'));
575581
}
576-
await transakRouteAfterAuth(quote);
582+
await transakRouteAfterAuth(quote, amountAsNumber);
577583
} else {
578584
navigation.navigate(
579585
...createV2VerifyIdentityNavDetails({

app/components/UI/Ramp/Views/NativeFlow/AdditionalVerification.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ describe('V2AdditionalVerification', () => {
7171

7272
expect(mockNavigateToKycWebview).toHaveBeenCalledWith({
7373
kycUrl: 'https://kyc.example.com',
74+
amount: 100,
7475
});
7576
});
7677

app/components/UI/Ramp/Views/NativeFlow/AdditionalVerification.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface V2AdditionalVerificationParams {
2727

2828
const V2AdditionalVerification = () => {
2929
const navigation = useNavigation();
30-
const { kycUrl } = useParams<V2AdditionalVerificationParams>();
30+
const { kycUrl, quote } = useParams<V2AdditionalVerificationParams>();
3131

3232
const { styles, theme } = useStyles(styleSheet, {});
3333

@@ -46,8 +46,8 @@ const V2AdditionalVerification = () => {
4646
}, [navigation, theme]);
4747

4848
const handleContinuePress = useCallback(() => {
49-
navigateToKycWebview({ kycUrl });
50-
}, [navigateToKycWebview, kycUrl]);
49+
navigateToKycWebview({ kycUrl, amount: quote?.fiatAmount });
50+
}, [navigateToKycWebview, kycUrl, quote?.fiatAmount]);
5151

5252
return (
5353
<ScreenLayout>

0 commit comments

Comments
 (0)