Skip to content

Commit 5d7085d

Browse files
authored
feat: add support for fiat payment methods in PayWithModal (#30184)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** Adds a **Bank and card** section to the redesigned Pay With bottom sheet so users can pay a confirmation with a fiat payment method (Debit/Credit card, Apple Pay, Google Pay, etc.) in addition to crypto. The new section wraps the existing `useFiatPaymentHighlightedActions` hook so it inherits the same gating and selection behavior the legacy `PayWithModal` already uses for fiat methods — same data source, same controller writes, new UI surface. When a user taps a fiat method, the controller's `selectedPaymentMethodId` updates and the bottom sheet closes. Tapping an already-selected fiat row just dismisses the sheet without deselecting. The section renders above the existing Crypto section. When the new bottom sheet is enabled, the legacy `PayWithModal` no longer surfaces fiat highlighted items — it continues to serve as the underlying token picker reachable via "Other assets". The Bank and card section becomes the single source of truth for fiat payment methods in this flow. A few related fixes that surfaced during integration: - **Mutually-exclusive selection between fiat and crypto.** When a fiat method is selected, crypto rows in the same sheet no longer show a checkmark. This mirrors the existing legacy modal behavior so selection semantics stay consistent between the old and new pickers. - **Sheet dismissal coordination.** Both pay-token and fiat-payment changes can dismiss the sheet; a shared latch ensures only one dismissal fires per controller write, preventing the sheet from popping the screen behind it when both values change at once. - **Icon color consistency.** Fiat payment method icons now render with the same token color as other secondary icons in the sheet for visual parity. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/CONF-1364 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** https://github.com/user-attachments/assets/55109bed-881c-448c-9334-9fd396186bea <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [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 - [x] 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. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] 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** > Moderate UI/state-flow changes in the confirmations payment picker: selection semantics and navigation dismissal now react to both token and fiat method changes, which could cause regressions in how pay methods are applied or screens are popped. > > **Overview** > Adds a new **Bank and card** section to the redesigned Pay With bottom sheet via `usePayWithFiatSection`, mapping `useFiatPaymentHighlightedActions` into selectable rows (with `PaymentMethodIcon`) and ordering it before the crypto section. > > Updates crypto-row selection behavior to be *mutually exclusive* with fiat selection (hides/clears crypto checkmarks/rows when a fiat method is selected) and replaces `useDismissOnPayTokenChange` with `useDismissOnPaymentChange` so the bottom sheet dismisses once on either pay-token or fiat-method changes. > > When the new bottom sheet is enabled (`isPayWithBottomSheetEnabled`), `PayWithModal` now suppresses fiat highlighted actions so it functions only as the “Other assets” token picker; tests and the `en.json` string for `confirm.pay_with_bottom_sheet.bank_and_card` are added/updated accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit cfde84e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 6f9a18e commit 5d7085d

16 files changed

Lines changed: 797 additions & 186 deletions

app/components/Views/confirmations/components/modals/pay-with-bottom-sheet/pay-with-bottom-sheet.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jest.mock('../../../../../../../locales/i18n', () => ({
1212
}));
1313

1414
jest.mock('../../../hooks/pay/usePayWithSections');
15-
jest.mock('../../../hooks/pay/useDismissOnPayTokenChange');
15+
jest.mock('../../../hooks/pay/useDismissOnPaymentChange');
1616

1717
jest.mock('@react-navigation/native', () => ({
1818
...jest.requireActual('@react-navigation/native'),

app/components/Views/confirmations/components/modals/pay-with-bottom-sheet/pay-with-bottom-sheet.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from '@metamask/design-system-react-native';
1111
import { strings } from '../../../../../../../locales/i18n';
1212
import PayWithSection from '../../UI/pay-with-section';
13-
import { useDismissOnPayTokenChange } from '../../../hooks/pay/useDismissOnPayTokenChange';
13+
import { useDismissOnPaymentChange } from '../../../hooks/pay/useDismissOnPaymentChange';
1414
import { usePayWithSections } from '../../../hooks/pay/usePayWithSections';
1515

1616
export const PAY_WITH_BOTTOM_SHEET_TEST_ID = 'pay-with-bottom-sheet';
@@ -19,7 +19,7 @@ export function PayWithBottomSheet() {
1919
const sheetRef = useRef<BottomSheetRef>(null);
2020
const navigation = useNavigation();
2121
const { sections } = usePayWithSections();
22-
useDismissOnPayTokenChange();
22+
useDismissOnPaymentChange();
2323

2424
const handleGoBack = useCallback(() => {
2525
navigation.goBack();

app/components/Views/confirmations/components/modals/pay-with-modal/pay-with-modal.test.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ import { EthAccountType, SolAccountType } from '@metamask/keyring-api';
2929
import { Hex } from '@metamask/utils';
3030
import { useTransactionMetadataRequest } from '../../../hooks/transactions/useTransactionMetadataRequest';
3131
import { EMPTY_ADDRESS } from '../../../../../../constants/transaction';
32-
import { getAvailableTokens } from '../../../utils/transaction-pay';
32+
import {
33+
getAvailableTokens,
34+
isPayWithBottomSheetEnabled,
35+
} from '../../../utils/transaction-pay';
3336
import { useWithdrawTokenFilter } from '../../../hooks/pay/useWithdrawTokenFilter';
3437
import { usePerpsPaymentToken } from '../../../../../UI/Perps/hooks/usePerpsPaymentToken';
3538
import { usePerpsBalanceTokenFilter } from '../../../../../UI/Perps/hooks/usePerpsBalanceTokenFilter';
@@ -59,7 +62,11 @@ jest.mock('../../../hooks/pay/useTransactionPayWithdraw');
5962
jest.mock('../../../hooks/pay/useFiatPaymentHighlightedActions');
6063
jest.mock('../../../hooks/pay/useWithdrawTokenFilter');
6164
jest.mock('../../../hooks/transactions/useTransactionMetadataRequest');
62-
jest.mock('../../../utils/transaction-pay');
65+
jest.mock('../../../utils/transaction-pay', () => ({
66+
...jest.requireActual('../../../utils/transaction-pay'),
67+
getAvailableTokens: jest.fn(),
68+
isPayWithBottomSheetEnabled: jest.fn(),
69+
}));
6370
jest.mock('../../../../../UI/Perps/hooks/usePerpsPaymentToken');
6471
jest.mock('../../../../../UI/Perps/hooks/usePerpsBalanceTokenFilter');
6572
jest.mock('../../../../../UI/Predict/hooks/usePredictPaymentToken');
@@ -210,6 +217,9 @@ describe('PayWithModal', () => {
210217
const useTransactionPayTokenMock = jest.mocked(useTransactionPayToken);
211218
const useTransactionPayWithdrawMock = jest.mocked(useTransactionPayWithdraw);
212219
const getAvailableTokensMock = jest.mocked(getAvailableTokens);
220+
const isPayWithBottomSheetEnabledMock = jest.mocked(
221+
isPayWithBottomSheetEnabled,
222+
);
213223
const useWithdrawTokenFilterMock = jest.mocked(useWithdrawTokenFilter);
214224
const useTransactionPayRequiredTokensMock = jest.mocked(
215225
useTransactionPayRequiredTokens,
@@ -238,6 +248,7 @@ describe('PayWithModal', () => {
238248
jest.mocked(useTransactionPayFiatPayment).mockReturnValue(undefined);
239249
jest.mocked(useFiatPaymentHighlightedActions).mockReturnValue([]);
240250

251+
isPayWithBottomSheetEnabledMock.mockReturnValue(false);
241252
getAvailableTokensMock.mockReturnValue(TOKENS_MOCK);
242253
useWithdrawTokenFilterMock.mockReturnValue(
243254
jest.fn((tokens: AssetType[]) => tokens),
@@ -548,5 +559,26 @@ describe('PayWithModal', () => {
548559

549560
expect(getByText('Credit Card')).toBeDefined();
550561
});
562+
563+
it('suppresses fiat highlighted actions when the new Pay With bottom sheet is enabled', () => {
564+
const fiatAction = {
565+
position: 'outside_of_asset_list' as const,
566+
icon: 'card-icon',
567+
paymentType: 'debit-credit-card',
568+
name: 'Credit Card',
569+
name_description: '5-10 min',
570+
action: jest.fn(),
571+
isSelected: false,
572+
};
573+
574+
jest
575+
.mocked(useFiatPaymentHighlightedActions)
576+
.mockReturnValue([fiatAction]);
577+
isPayWithBottomSheetEnabledMock.mockReturnValue(true);
578+
579+
const { queryByText } = render();
580+
581+
expect(queryByText('Credit Card')).toBeNull();
582+
});
551583
});
552584
});

app/components/Views/confirmations/components/modals/pay-with-modal/pay-with-modal.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useRef } from 'react';
1+
import React, { useCallback, useMemo, useRef } from 'react';
22
import { Hex } from '@metamask/utils';
33
import Engine from '../../../../../../core/Engine';
44
import { useTransactionPayToken } from '../../../hooks/pay/useTransactionPayToken';
@@ -21,7 +21,10 @@ import {
2121
useTransactionPayRequiredTokens,
2222
} from '../../../hooks/pay/useTransactionPayData';
2323
import { useFiatPaymentHighlightedActions } from '../../../hooks/pay/useFiatPaymentHighlightedActions';
24-
import { getAvailableTokens } from '../../../utils/transaction-pay';
24+
import {
25+
getAvailableTokens,
26+
isPayWithBottomSheetEnabled,
27+
} from '../../../utils/transaction-pay';
2528
import { useTransactionPayBlockedTokens } from '../../../hooks/pay/useTransactionPayBlockedTokens';
2629
import { useTransactionMetadataRequest } from '../../../hooks/transactions/useTransactionMetadataRequest';
2730
import { TransactionType } from '@metamask/transaction-controller';
@@ -48,6 +51,17 @@ export function PayWithModal() {
4851
const requiredTokens = useTransactionPayRequiredTokens();
4952
const fiatPayment = useTransactionPayFiatPayment();
5053
const fiatHighlightedActions = useFiatPaymentHighlightedActions();
54+
/**
55+
* Suppress fiat highlighted items in the modal when the new Pay With
56+
* bottom sheet is enabled. In that mode the Bank/Card section is the single
57+
* source of truth for fiat payment methods, while this modal continues to
58+
* serve as the crypto/tokens picker via the "Other assets" entry point.
59+
* Remove this gate at CONF-1313 GA along with the env util.
60+
*/
61+
const effectiveFiatHighlightedActions = useMemo(
62+
() => (isPayWithBottomSheetEnabled() ? [] : fiatHighlightedActions),
63+
[fiatHighlightedActions],
64+
);
5165
const bottomSheetRef = useRef<BottomSheetRef>(null);
5266
const { filterAllowedTokens: musdTokenFilter } = useMusdConversionTokens();
5367
const { onPaymentTokenChange: onMusdPaymentTokenChange } =
@@ -200,14 +214,14 @@ export function PayWithModal() {
200214

201215
const wrappedTokens = wrapHighlightedItemCallbacks(filteredTokens);
202216
const wrappedFiatActions = wrapHighlightedItemCallbacks(
203-
fiatHighlightedActions,
217+
effectiveFiatHighlightedActions,
204218
);
205219

206220
return [...wrappedFiatActions, ...wrappedTokens];
207221
},
208222
[
209223
blockedTokens,
210-
fiatHighlightedActions,
224+
effectiveFiatHighlightedActions,
211225
fiatPayment,
212226
withdrawTokenFilter,
213227
musdTokenFilter,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { usePayWithCryptoSection } from './usePayWithCryptoSection';
2+
export { usePayWithFiatSection } from './usePayWithFiatSection';

app/components/Views/confirmations/hooks/pay/sections/usePayWithCryptoSection.test.tsx

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { useTransactionMetadataRequest } from '../../transactions/useTransaction
1212
import { useLastUsedPaymentMethod } from '../useLastUsedPaymentMethod';
1313
import { usePayWithPreferredToken } from '../usePayWithPreferredToken';
1414
import { usePayWithSelectedToken } from '../usePayWithSelectedToken';
15+
import { useTransactionPayFiatPayment } from '../useTransactionPayData';
16+
import { useTransactionPayToken } from '../useTransactionPayToken';
1517
import { usePayWithCryptoSection } from './usePayWithCryptoSection';
1618

1719
jest.mock('@react-navigation/native', () => ({
@@ -38,6 +40,8 @@ jest.mock('../../transactions/useTransactionMetadataRequest');
3840
jest.mock('../useLastUsedPaymentMethod');
3941
jest.mock('../usePayWithPreferredToken');
4042
jest.mock('../usePayWithSelectedToken');
43+
jest.mock('../useTransactionPayData');
44+
jest.mock('../useTransactionPayToken');
4145

4246
const TOKEN_MOCK: TransactionPaymentToken = {
4347
address: '0x1234567890abcdef1234567890abcdef12345678' as Hex,
@@ -67,9 +71,14 @@ describe('usePayWithCryptoSection', () => {
6771
const usePayWithPreferredTokenMock = jest.mocked(usePayWithPreferredToken);
6872
const usePayWithSelectedTokenMock = jest.mocked(usePayWithSelectedToken);
6973
const useLastUsedPaymentMethodMock = jest.mocked(useLastUsedPaymentMethod);
74+
const useTransactionPayFiatPaymentMock = jest.mocked(
75+
useTransactionPayFiatPayment,
76+
);
77+
const useTransactionPayTokenMock = jest.mocked(useTransactionPayToken);
7078
const navigateMock = jest.fn();
7179
const goBackMock = jest.fn();
7280
const selectTokenMock = jest.fn();
81+
const setPayTokenMock = jest.fn();
7382
const isLastUsedMock = jest.fn().mockReturnValue(false);
7483

7584
beforeEach(() => {
@@ -102,6 +111,11 @@ describe('usePayWithCryptoSection', () => {
102111
lastUsedToken: undefined,
103112
isLastUsed: isLastUsedMock,
104113
});
114+
useTransactionPayFiatPaymentMock.mockReturnValue(undefined);
115+
useTransactionPayTokenMock.mockReturnValue({
116+
payToken: TOKEN_MOCK,
117+
setPayToken: setPayTokenMock,
118+
});
105119
});
106120

107121
it('passes route preferred payment token params to both pay-with hooks', () => {
@@ -381,7 +395,7 @@ describe('usePayWithCryptoSection', () => {
381395
result.current?.rows[0].onPress?.();
382396
});
383397

384-
expect(selectTokenMock).toHaveBeenCalledWith({
398+
expect(setPayTokenMock).toHaveBeenCalledWith({
385399
address: TOKEN_MOCK.address,
386400
chainId: TOKEN_MOCK.chainId,
387401
});
@@ -395,7 +409,25 @@ describe('usePayWithCryptoSection', () => {
395409
result.current?.rows[0].onPress?.();
396410
});
397411

398-
expect(selectTokenMock).toHaveBeenCalledWith({
412+
expect(setPayTokenMock).toHaveBeenCalledWith({
413+
address: TOKEN_MOCK.address,
414+
chainId: TOKEN_MOCK.chainId,
415+
});
416+
expect(goBackMock).toHaveBeenCalledTimes(1);
417+
});
418+
419+
it('clears the fiat selection when the preferred token row is pressed while a fiat method is active', () => {
420+
useTransactionPayFiatPaymentMock.mockReturnValue({
421+
selectedPaymentMethodId: 'pm-card',
422+
});
423+
424+
const { result } = renderHook(() => usePayWithCryptoSection());
425+
426+
act(() => {
427+
result.current?.rows[0].onPress?.();
428+
});
429+
430+
expect(setPayTokenMock).toHaveBeenCalledWith({
399431
address: TOKEN_MOCK.address,
400432
chainId: TOKEN_MOCK.chainId,
401433
});
@@ -513,4 +545,53 @@ describe('usePayWithCryptoSection', () => {
513545

514546
expect(otherAssetsRow?.isLastUsed ?? false).toBe(false);
515547
});
548+
549+
it('suppresses the preferred token row checkmark when a fiat payment method is selected', () => {
550+
useTransactionPayFiatPaymentMock.mockReturnValue({
551+
selectedPaymentMethodId: 'pm-card',
552+
});
553+
554+
const { result } = renderHook(() => usePayWithCryptoSection());
555+
556+
const preferredRow = result.current?.rows.find(
557+
(row) => row.id === 'crypto-preferred-token',
558+
);
559+
560+
expect(preferredRow).toEqual(
561+
expect.objectContaining({
562+
isSelected: false,
563+
trailingElement: 'none',
564+
}),
565+
);
566+
});
567+
568+
it('hides the user-selected token row when a fiat payment method is selected', () => {
569+
const distinctSelectedToken = {
570+
...TOKEN_MOCK,
571+
address: SELECTED_TOKEN_MOCK.address,
572+
symbol: SELECTED_TOKEN_MOCK.symbol,
573+
balanceUsd: SELECTED_TOKEN_MOCK.balanceUsd,
574+
};
575+
usePayWithPreferredTokenMock.mockReturnValue({
576+
hasTokens: true,
577+
preferredToken: TOKEN_MOCK,
578+
selectedToken: distinctSelectedToken,
579+
});
580+
usePayWithSelectedTokenMock.mockReturnValue({
581+
isSelectedDistinctFromAutomatic: true,
582+
selectedToken: SELECTED_TOKEN_MOCK,
583+
selectToken: selectTokenMock,
584+
});
585+
useTransactionPayFiatPaymentMock.mockReturnValue({
586+
selectedPaymentMethodId: 'pm-card',
587+
});
588+
589+
const { result } = renderHook(() => usePayWithCryptoSection());
590+
591+
const selectedRow = result.current?.rows.find(
592+
(row) => row.id === 'crypto-selected-token',
593+
);
594+
595+
expect(selectedRow).toBeUndefined();
596+
});
516597
});

app/components/Views/confirmations/hooks/pay/sections/usePayWithCryptoSection.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import { SetPayTokenRequest } from '../useAutomaticTransactionPayToken';
2424
import { useLastUsedPaymentMethod } from '../useLastUsedPaymentMethod';
2525
import { usePayWithPreferredToken } from '../usePayWithPreferredToken';
2626
import { usePayWithSelectedToken } from '../usePayWithSelectedToken';
27+
import { useTransactionPayFiatPayment } from '../useTransactionPayData';
28+
import { useTransactionPayToken } from '../useTransactionPayToken';
2729
import { useTransactionMetadataRequest } from '../../transactions/useTransactionMetadataRequest';
2830

2931
interface PayWithCryptoSectionParams {
@@ -59,9 +61,11 @@ export function usePayWithCryptoSection(): PayWithSectionConfig | null {
5961
const {
6062
isSelectedDistinctFromAutomatic,
6163
selectedToken: selectedTokenDisplay,
62-
selectToken,
6364
} = usePayWithSelectedToken({ preferredToken: resolvedPreferredToken });
65+
const { setPayToken } = useTransactionPayToken();
6466
const { isLastUsed } = useLastUsedPaymentMethod();
67+
const fiatPayment = useTransactionPayFiatPayment();
68+
const hasFiatPaymentSelected = Boolean(fiatPayment?.selectedPaymentMethodId);
6569

6670
const handleOtherAssetsPress = useCallback(() => {
6771
navigation.navigate(Routes.CONFIRMATION_PAY_WITH_MODAL);
@@ -71,12 +75,12 @@ export function usePayWithCryptoSection(): PayWithSectionConfig | null {
7175
if (!preferredToken) {
7276
return;
7377
}
74-
selectToken({
78+
setPayToken({
7579
address: preferredToken.address,
7680
chainId: preferredToken.chainId,
7781
});
7882
navigation.goBack();
79-
}, [navigation, preferredToken, selectToken]);
83+
}, [navigation, preferredToken, setPayToken]);
8084

8185
const preferredTokenBalance = useMemo(
8286
() => formatFiat(new BigNumber(preferredToken?.balanceUsd ?? '0')),
@@ -96,10 +100,9 @@ export function usePayWithCryptoSection(): PayWithSectionConfig | null {
96100
const rows: PayWithRowConfig[] = [];
97101

98102
if (preferredToken) {
99-
const isPreferredTokenSelected = isMatchingPayToken(
100-
selectedToken,
101-
preferredToken,
102-
);
103+
const isPreferredTokenSelected =
104+
!hasFiatPaymentSelected &&
105+
isMatchingPayToken(selectedToken, preferredToken);
103106

104107
rows.push({
105108
id: 'crypto-preferred-token',
@@ -120,7 +123,11 @@ export function usePayWithCryptoSection(): PayWithSectionConfig | null {
120123
});
121124
}
122125

123-
if (isSelectedDistinctFromAutomatic && selectedTokenDisplay) {
126+
if (
127+
isSelectedDistinctFromAutomatic &&
128+
selectedTokenDisplay &&
129+
!hasFiatPaymentSelected
130+
) {
124131
rows.push({
125132
id: 'crypto-selected-token',
126133
icon: React.createElement(TokenIcon, {
@@ -167,6 +174,7 @@ export function usePayWithCryptoSection(): PayWithSectionConfig | null {
167174
}, [
168175
handleOtherAssetsPress,
169176
handlePreferredTokenPress,
177+
hasFiatPaymentSelected,
170178
hasTokens,
171179
isLastUsed,
172180
isSelectedDistinctFromAutomatic,

0 commit comments

Comments
 (0)