Skip to content

Commit 9975c8c

Browse files
fix(perps): route deposit flow to redesigned confirmations with perps header cp-7.69.0 (MetaMask#27106)
## **Description** The perps deposit flow was using `navigateToConfirmation({ stack: Routes.PERPS.ROOT })`, which did not correctly route users to the redesigned confirmations screen after initiating a deposit. Users could end up on the wrong confirmation stack or without the perps-specific header. This change updates the flow so that when the user taps the Perps balance token to deposit: 1. The current approval/confirmation is dismissed via `handleReject()` from `useApprovalRequest`. 2. `depositWithConfirmation()` runs to start the deposit flow. 3. On success, navigation goes to `REDESIGNED_CONFIRMATIONS` with `showPerpsHeader: true` instead of the previous PERPS.ROOT stack. Dependencies on `useConfirmNavigation` are removed in favor of `useNavigation` and `useApprovalRequest` so the deposit action and post-deposit navigation are explicit and aligned with the redesigned confirmations screen. ## **Changelog** CHANGELOG entry: Fixed perps deposit flow so it routes to the redesigned confirmations screen with the perps header after a successful deposit. ## **Related issues** Fixes: https://consensys.slack.com/archives/C092T3GPHQD/p1772719051867319 ## **Manual testing steps** ```gherkin Feature: Perps deposit from pay-with / token list Scenario: user initiates perps deposit from token selector Given user is on a confirmation screen with "Perps balance" in the token list (e.g. perps deposit and order) When user taps "Perps balance" to deposit Then the current confirmation is dismissed, deposit flow runs, and on success the user is taken to the redesigned confirmations screen with the perps header visible ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <img width="1206" height="2622" alt="simulator_screenshot_1025DB15-7197-4622-964F-F1F05143DFC8" src="https://github.com/user-attachments/assets/5e3dde3e-e91b-4966-945e-8a1b910933e0" /> <!-- [screenshots/recordings] --> ## **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 - [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. ## **Pre-merge reviewer checklist** - [ ] 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** > Changes the perps deposit tap-handler to dismiss the current approval and navigate to a different confirmation route, so regressions could strand users on the wrong screen or break the deposit flow sequencing. > > **Overview** > Fixes the **Perps “Add funds”** action from the synthetic “Perps balance” token so it no longer uses `useConfirmNavigation`/`Routes.PERPS.ROOT`. > > The handler now calls `useApprovalRequest().onReject()` to dismiss the current approval, runs `depositWithConfirmation()`, and then navigates via React Navigation to `Routes.FULL_SCREEN_CONFIRMATIONS.REDESIGNED_CONFIRMATIONS` with `showPerpsHeader: true`. Tests were updated to mock the new hooks (`useNavigation`, `useApprovalRequest`) and assert the new call order/route using `waitFor`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 48571b0. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent c9e0ded commit 9975c8c

2 files changed

Lines changed: 75 additions & 45 deletions

File tree

app/components/UI/Perps/hooks/usePerpsBalanceTokenFilter.test.ts

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { renderHook } from '@testing-library/react-native';
1+
import { renderHook, waitFor } from '@testing-library/react-native';
22
import { useSelector } from 'react-redux';
33
import { TransactionType } from '@metamask/transaction-controller';
44
import { usePerpsBalanceTokenFilter } from './usePerpsBalanceTokenFilter';
@@ -10,8 +10,12 @@ import {
1010
isHighlightedItemOutsideAssetList,
1111
} from '../../../Views/confirmations/types/token';
1212
import { usePerpsTrading } from './usePerpsTrading';
13-
import { useConfirmNavigation } from '../../../Views/confirmations/hooks/useConfirmNavigation';
1413
import { usePerpsPaymentToken } from './usePerpsPaymentToken';
14+
import Routes from '../../../../constants/navigation/Routes';
15+
import { useNavigation } from '@react-navigation/native';
16+
import useApprovalRequest from '../../../Views/confirmations/hooks/useApprovalRequest';
17+
import { selectPerpsAccountState } from '../selectors/perpsController';
18+
import { selectPerpsPayWithAnyTokenAllowlistAssets } from '../selectors/featureFlags';
1519

1620
jest.mock('../../../../../locales/i18n', () => ({
1721
strings: jest.fn((key: string) => key),
@@ -22,8 +26,12 @@ jest.mock(
2226
);
2327
jest.mock('./useIsPerpsBalanceSelected');
2428
jest.mock('./usePerpsTrading');
25-
jest.mock('../../../Views/confirmations/hooks/useConfirmNavigation', () => ({
26-
useConfirmNavigation: jest.fn(),
29+
jest.mock('../../../Views/confirmations/hooks/useApprovalRequest', () => ({
30+
__esModule: true,
31+
default: jest.fn(),
32+
}));
33+
jest.mock('@react-navigation/native', () => ({
34+
useNavigation: jest.fn(),
2735
}));
2836
jest.mock('./usePerpsPaymentToken');
2937
jest.mock('./usePerpsNetworkManagement', () => ({
@@ -58,17 +66,21 @@ const mockUseSelector = useSelector as jest.MockedFunction<typeof useSelector>;
5866
const mockUsePerpsTrading = usePerpsTrading as jest.MockedFunction<
5967
typeof usePerpsTrading
6068
>;
61-
const mockUseConfirmNavigation = useConfirmNavigation as jest.MockedFunction<
62-
typeof useConfirmNavigation
63-
>;
6469
const mockUsePerpsPaymentToken = usePerpsPaymentToken as jest.MockedFunction<
6570
typeof usePerpsPaymentToken
6671
>;
72+
const mockUseNavigation = useNavigation as jest.MockedFunction<
73+
typeof useNavigation
74+
>;
75+
const mockUseApprovalRequest = useApprovalRequest as jest.MockedFunction<
76+
typeof useApprovalRequest
77+
>;
6778

6879
describe('usePerpsBalanceTokenFilter', () => {
6980
const chainId = '0xa4b1';
7081
const mockDepositWithConfirmation = jest.fn().mockResolvedValue(undefined);
71-
const mockNavigateToConfirmation = jest.fn();
82+
const mockNavigate = jest.fn();
83+
const mockOnReject = jest.fn();
7284
const mockOnPerpsPaymentTokenChange = jest.fn();
7385

7486
beforeEach(() => {
@@ -77,10 +89,10 @@ describe('usePerpsBalanceTokenFilter', () => {
7789
mockUseIsPerpsBalanceSelected.mockReturnValue(false);
7890
mockUseSelector.mockImplementation(
7991
(selector: (state: unknown) => unknown) => {
80-
if (selector.name === 'selectPerpsAccountState') {
92+
if (selector === selectPerpsAccountState) {
8193
return { availableBalance: '1500.00' };
8294
}
83-
if (selector.name === 'selectPerpsPayWithAnyTokenAllowlistAssets') {
95+
if (selector === selectPerpsPayWithAnyTokenAllowlistAssets) {
8496
return [];
8597
}
8698
return undefined;
@@ -89,9 +101,12 @@ describe('usePerpsBalanceTokenFilter', () => {
89101
mockUsePerpsTrading.mockReturnValue({
90102
depositWithConfirmation: mockDepositWithConfirmation,
91103
} as unknown as ReturnType<typeof usePerpsTrading>);
92-
mockUseConfirmNavigation.mockReturnValue({
93-
navigateToConfirmation: mockNavigateToConfirmation,
94-
} as unknown as ReturnType<typeof useConfirmNavigation>);
104+
mockUseNavigation.mockReturnValue({
105+
navigate: mockNavigate,
106+
} as unknown as ReturnType<typeof useNavigation>);
107+
mockUseApprovalRequest.mockReturnValue({
108+
onReject: mockOnReject,
109+
} as unknown as ReturnType<typeof useApprovalRequest>);
95110
mockUsePerpsPaymentToken.mockReturnValue({
96111
onPaymentTokenChange: mockOnPerpsPaymentTokenChange,
97112
} as unknown as ReturnType<typeof usePerpsPaymentToken>);
@@ -200,9 +215,8 @@ describe('usePerpsBalanceTokenFilter', () => {
200215
it('uses zero balance when perps account is null', () => {
201216
mockUseSelector.mockImplementation(
202217
(selector: (state: unknown) => unknown) => {
203-
if (selector.name === 'selectPerpsAccountState') return null;
204-
if (selector.name === 'selectPerpsPayWithAnyTokenAllowlistAssets')
205-
return [];
218+
if (selector === selectPerpsAccountState) return null;
219+
if (selector === selectPerpsPayWithAnyTokenAllowlistAssets) return [];
206220
return undefined;
207221
},
208222
);
@@ -222,10 +236,9 @@ describe('usePerpsBalanceTokenFilter', () => {
222236
it('clears isSelected on other tokens when perps balance is selected', () => {
223237
mockUseSelector.mockImplementation(
224238
(selector: (state: unknown) => unknown) => {
225-
if (selector.name === 'selectPerpsAccountState')
239+
if (selector === selectPerpsAccountState)
226240
return { availableBalance: '1500.00' };
227-
if (selector.name === 'selectPerpsPayWithAnyTokenAllowlistAssets')
228-
return [];
241+
if (selector === selectPerpsPayWithAnyTokenAllowlistAssets) return [];
229242
return undefined;
230243
},
231244
);
@@ -255,10 +268,9 @@ describe('usePerpsBalanceTokenFilter', () => {
255268
it('keeps token isSelected when perps balance is not selected', () => {
256269
mockUseSelector.mockImplementation(
257270
(selector: (state: unknown) => unknown) => {
258-
if (selector.name === 'selectPerpsAccountState')
271+
if (selector === selectPerpsAccountState)
259272
return { availableBalance: '1500.00' };
260-
if (selector.name === 'selectPerpsPayWithAnyTokenAllowlistAssets')
261-
return [];
273+
if (selector === selectPerpsPayWithAnyTokenAllowlistAssets) return [];
262274
return undefined;
263275
},
264276
);
@@ -280,14 +292,15 @@ describe('usePerpsBalanceTokenFilter', () => {
280292

281293
it('filters to only allowlisted tokens when allowlist is set', () => {
282294
const allowlistKey = `${chainId}.0xusdc`.toLowerCase();
283-
let callIndex = 0;
284-
mockUseSelector.mockImplementation(() => {
285-
callIndex += 1;
286-
// Hook calls selectPerpsAccountState first, then selectPerpsPayWithAnyTokenAllowlistAssets
287-
if (callIndex === 1) return { availableBalance: '100.00' };
288-
if (callIndex === 2) return [allowlistKey];
289-
return [];
290-
});
295+
mockUseSelector.mockImplementation(
296+
(selector: (state: unknown) => unknown) => {
297+
if (selector === selectPerpsAccountState)
298+
return { availableBalance: '100.00' };
299+
if (selector === selectPerpsPayWithAnyTokenAllowlistAssets)
300+
return [allowlistKey];
301+
return [];
302+
},
303+
);
291304
const inputTokens: AssetType[] = [
292305
{
293306
address: '0xusdc',
@@ -314,7 +327,7 @@ describe('usePerpsBalanceTokenFilter', () => {
314327
expect((output[1] as AssetType).symbol).toBe('USDC');
315328
});
316329

317-
it('calls navigateToConfirmation and depositWithConfirmation when Add funds is pressed', async () => {
330+
it('calls onReject, depositWithConfirmation and navigation.navigate when Add funds is pressed', async () => {
318331
mockUseSelector.mockReturnValue({
319332
availableBalance: '500.00',
320333
});
@@ -336,12 +349,17 @@ describe('usePerpsBalanceTokenFilter', () => {
336349
expect(isHighlightedItemOutsideAssetList(highlightedAction)).toBe(true);
337350
if (isHighlightedItemOutsideAssetList(highlightedAction)) {
338351
highlightedAction.actions?.[0]?.onPress();
339-
// handlePerpsDepositPress is async (ensureArbitrumNetworkExists().then(...))
340-
await Promise.resolve();
341-
expect(mockNavigateToConfirmation).toHaveBeenCalledWith({
342-
stack: expect.any(String),
343-
});
344-
expect(mockDepositWithConfirmation).toHaveBeenCalledTimes(1);
352+
await waitFor(
353+
() => {
354+
expect(mockOnReject).toHaveBeenCalledTimes(1);
355+
expect(mockDepositWithConfirmation).toHaveBeenCalledTimes(1);
356+
expect(mockNavigate).toHaveBeenCalledWith(
357+
Routes.FULL_SCREEN_CONFIRMATIONS.REDESIGNED_CONFIRMATIONS,
358+
{ showPerpsHeader: true },
359+
);
360+
},
361+
{ timeout: 2000 },
362+
);
345363
}
346364
});
347365

app/components/UI/Perps/hooks/usePerpsBalanceTokenFilter.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ import { useIsPerpsBalanceSelected } from './useIsPerpsBalanceSelected';
1919
import { usePerpsPaymentToken } from './usePerpsPaymentToken';
2020
import Routes from '../../../../constants/navigation/Routes';
2121
import { usePerpsTrading } from './usePerpsTrading';
22+
import { useNavigation } from '@react-navigation/native';
23+
import useApprovalRequest from '../../../Views/confirmations/hooks/useApprovalRequest';
2224
import { usePerpsNetworkManagement } from './usePerpsNetworkManagement';
23-
import { useConfirmNavigation } from '../../../Views/confirmations/hooks/useConfirmNavigation';
2425

2526
/** URI for the perps balance token icon, shared with PerpsPayRow and pay-with modal. */
2627
const resolvedPerpsIcon = Image.resolveAssetSource(perpsPayTokenIcon);
@@ -46,27 +47,38 @@ export function usePerpsBalanceTokenFilter(): (
4647
const formatFiat = useFiatFormatter({ currency: 'usd' });
4748

4849
const { depositWithConfirmation } = usePerpsTrading();
49-
const { ensureArbitrumNetworkExists } = usePerpsNetworkManagement();
50-
const { navigateToConfirmation } = useConfirmNavigation();
5150

5251
const isPerpsDepositAndOrder = hasTransactionType(transactionMeta, [
5352
TransactionType.perpsDepositAndOrder,
5453
]);
5554

55+
const { onReject: handleReject } = useApprovalRequest();
56+
const { ensureArbitrumNetworkExists } = usePerpsNetworkManagement();
57+
58+
const navigation = useNavigation();
59+
5660
const handlePerpsDepositPress = useCallback(() => {
5761
ensureArbitrumNetworkExists()
5862
.then(() => {
59-
navigateToConfirmation({ stack: Routes.PERPS.ROOT });
63+
handleReject();
6064
return depositWithConfirmation();
6165
})
62-
.catch((_err) => {
66+
.then(() => {
67+
navigation.navigate(
68+
Routes.FULL_SCREEN_CONFIRMATIONS.REDESIGNED_CONFIRMATIONS,
69+
{
70+
showPerpsHeader: true,
71+
},
72+
);
73+
})
74+
.catch(() => {
6375
// Deposit flow handles errors (e.g. user rejection or missing network).
64-
// ensureArbitrumNetworkExists errors are logged inside the hook itself.
6576
});
6677
}, [
67-
ensureArbitrumNetworkExists,
68-
navigateToConfirmation,
78+
navigation,
6979
depositWithConfirmation,
80+
handleReject,
81+
ensureArbitrumNetworkExists,
7082
]);
7183

7284
const { onPaymentTokenChange: onPerpsPaymentTokenChange } =

0 commit comments

Comments
 (0)