Skip to content

Commit e966f98

Browse files
authored
feat: Implement redesigned send flow feature flag (#35801)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** <!-- 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? --> This PR aims to add feature flag for redesigned send flow for BIP44. As new send flow will be relying on `multichainAccountsState2` (aka BIP44), the centralised hook to determine redesigned send flow has also dependency to that flag. Please see `useRedesignedSendFlow.ts` [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/35801?quickstart=1) ## **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: ## **Related issues** Fixes: #35799 ## **Manual testing steps** No user facing changes ## **Screenshots/Recordings** <!-- 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** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/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-extension/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.
1 parent b243cf2 commit e966f98

File tree

16 files changed

+347
-33
lines changed

16 files changed

+347
-33
lines changed

test/e2e/mock-e2e.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,22 @@ async function setupMocking(
10061006
};
10071007
});
10081008

1009+
await server
1010+
.forGet('https://client-config.api.cx.metamask.io/v1/flags')
1011+
.thenCallback(() => {
1012+
return {
1013+
ok: true,
1014+
statusCode: 200,
1015+
json: [
1016+
{
1017+
sendRedesign: {
1018+
enabled: false,
1019+
},
1020+
},
1021+
],
1022+
};
1023+
});
1024+
10091025
// On Ramp Content
10101026
const ON_RAMP_CONTENT = fs.readFileSync(ON_RAMP_CONTENT_PATH);
10111027
await server

ui/components/app/assets/nfts/nft-details/nft-details.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import {
8585
} from '../../../../../../app/scripts/lib/util';
8686
import useGetAssetImageUrl from '../../../../../hooks/useGetAssetImageUrl';
8787
import { getImageForChainId } from '../../../../../selectors/multichain';
88+
import { useRedesignedSendFlow } from '../../../../../pages/confirmations/hooks/useRedesignedSendFlow';
8889
import useFetchNftDetailsFromTokenURI from '../../../../../hooks/useFetchNftDetailsFromTokenURI';
8990
import { navigateToSendRoute } from '../../../../../pages/confirmations/utils/send';
9091
import NftDetailInformationRow from './nft-detail-information-row';
@@ -129,6 +130,7 @@ export function NftDetailsComponent({
129130
const trackEvent = useContext(MetaMetricsContext);
130131
const currency = useSelector(getCurrentCurrency);
131132
const selectedNativeConversionRate = useSelector(getConversionRate);
133+
const { enabled: isSendRedesignEnabled } = useRedesignedSendFlow();
132134

133135
const nftNetworkConfigs = useSelector(getNetworkConfigurationsByChainId);
134136
const nftChainNetwork = nftNetworkConfigs[nftChainId as Hex];
@@ -335,7 +337,10 @@ export function NftDetailsComponent({
335337
}),
336338
);
337339
// We only allow sending one NFT at a time
338-
navigateToSendRoute(history, { address: nft.address, chainId: nftChainId });
340+
navigateToSendRoute(history, isSendRedesignEnabled, {
341+
address: nft.address,
342+
chainId: nftChainId,
343+
});
339344
};
340345

341346
const getDateCreatedTimestamp = (dateString: string) => {

ui/components/app/wallet-overview/coin-buttons.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import { getCurrentChainId } from '../../../../shared/modules/selectors/networks
6767
import { MultichainNetworks } from '../../../../shared/constants/multichain/networks';
6868
import { trace, TraceName } from '../../../../shared/lib/trace';
6969
import { navigateToSendRoute } from '../../../pages/confirmations/utils/send';
70+
import { useRedesignedSendFlow } from '../../../pages/confirmations/hooks/useRedesignedSendFlow';
7071
///: BEGIN:ONLY_INCLUDE_IF(multichain)
7172
import { useHandleSendNonEvm } from './hooks/useHandleSendNonEvm';
7273
///: END:ONLY_INCLUDE_IF
@@ -107,6 +108,7 @@ const CoinButtons = ({
107108
>;
108109
const currentChainId = useSelector(getCurrentChainId);
109110
const displayNewIconButtons = process.env.REMOVE_GNS;
111+
const { enabled: isSendRedesignEnabled } = useRedesignedSendFlow();
110112

111113
// Multichain accounts feature flag and selected account group
112114
const isMultichainAccountsState2Enabled = useSelector(
@@ -276,7 +278,7 @@ const CoinButtons = ({
276278
);
277279

278280
///: BEGIN:ONLY_INCLUDE_IF(multichain)
279-
if (!isEvmAccountType(account.type) && !process.env.SEND_REDESIGN_ENABLED) {
281+
if (!isEvmAccountType(account.type) && !isSendRedesignEnabled) {
280282
await handleSendNonEvm();
281283
// Early return, not to let the non-EVM flow slip into the native send flow.
282284
return;
@@ -290,11 +292,12 @@ const CoinButtons = ({
290292
if (trackingLocation !== 'home') {
291293
params = { chainId: chainId.toString() };
292294
}
293-
navigateToSendRoute(history, params);
295+
navigateToSendRoute(history, isSendRedesignEnabled, params);
294296
}, [
295297
chainId,
296298
account,
297299
setCorrectChain,
300+
isSendRedesignEnabled,
298301
///: BEGIN:ONLY_INCLUDE_IF(multichain)
299302
handleSendNonEvm,
300303
///: END:ONLY_INCLUDE_IF

ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-nft-tab.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
updateSendAsset,
3939
} from '../../../../ducks/send';
4040
import { getNftImage } from '../../../../helpers/utils/nfts';
41+
import { useRedesignedSendFlow } from '../../../../pages/confirmations/hooks/useRedesignedSendFlow';
4142
import { navigateToSendRoute } from '../../../../pages/confirmations/utils/send';
4243
import { NFT } from './types';
4344

@@ -67,6 +68,7 @@ export function AssetPickerModalNftTab({
6768
const nftsStillFetchingIndication = useSelector(
6869
getNftIsStillFetchingIndication,
6970
);
71+
const { enabled: isSendRedesignEnabled } = useRedesignedSendFlow();
7072

7173
const { currentlyOwnedNfts } = useNfts({
7274
overridePopularNetworkFilter: true,
@@ -128,7 +130,7 @@ export function AssetPickerModalNftTab({
128130
skipComputeEstimatedGasLimit: false,
129131
}),
130132
);
131-
navigateToSendRoute(history, {
133+
navigateToSendRoute(history, isSendRedesignEnabled, {
132134
address: nft.address,
133135
chainId: nft.chainId,
134136
});

ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ jest.mock('lodash', () => ({
103103
}),
104104
}));
105105

106+
jest.mock(
107+
'../../../../pages/confirmations/hooks/useRedesignedSendFlow',
108+
() => ({
109+
useRedesignedSendFlow: jest.fn().mockReturnValue({ enabled: false }),
110+
}),
111+
);
112+
106113
describe('AssetPickerModal', () => {
107114
const useSelectorMock = useSelector as jest.Mock;
108115
const useI18nContextMock = useI18nContext as jest.Mock;

ui/pages/asset/components/token-buttons.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { Asset } from '../types/asset';
5757
import { getIsUnifiedUIEnabled } from '../../../ducks/bridge/selectors';
5858
import { navigateToSendRoute } from '../../confirmations/utils/send';
5959
import { isEvmChainId } from '../../../../shared/lib/asset-utils';
60+
import { useRedesignedSendFlow } from '../../confirmations/hooks/useRedesignedSendFlow';
6061

6162
const TokenButtons = ({
6263
token,
@@ -76,7 +77,7 @@ const TokenButtons = ({
7677
const isMultichainAccountsState2Enabled = useSelector(
7778
getIsMultichainAccountsState2Enabled,
7879
);
79-
80+
const { enabled: isSendRedesignEnabled } = useRedesignedSendFlow();
8081
const { chainId: multichainChainId } = useSelector(
8182
getSelectedMultichainNetworkConfiguration,
8283
);
@@ -194,7 +195,7 @@ const TokenButtons = ({
194195
);
195196

196197
///: BEGIN:ONLY_INCLUDE_IF(multichain)
197-
if (!isEvmAccountType(account.type) && !process.env.SEND_REDESIGN_ENABLED) {
198+
if (!isEvmAccountType(account.type) && !isSendRedesignEnabled) {
198199
await handleSendNonEvm();
199200
// Early return, not to let the non-EVM flow slip into the native send flow.
200201
return;
@@ -209,7 +210,7 @@ const TokenButtons = ({
209210
details: token,
210211
}),
211212
);
212-
navigateToSendRoute(history, {
213+
navigateToSendRoute(history, isSendRedesignEnabled, {
213214
address: token.address,
214215
chainId: token.chainId,
215216
});
@@ -231,6 +232,7 @@ const TokenButtons = ({
231232
///: BEGIN:ONLY_INCLUDE_IF(multichain)
232233
handleSendNonEvm,
233234
///: END:ONLY_INCLUDE_IF
235+
isSendRedesignEnabled,
234236
]);
235237

236238
const isTestnet = useSelector(getMultichainIsTestnet);

ui/pages/confirmations/components/confirm/header/wallet-initiated-header.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ const render = (
1212
return renderWithConfirmContextProvider(<WalletInitiatedHeader />, store);
1313
};
1414

15+
jest.mock('../../../hooks/useRedesignedSendFlow', () => ({
16+
useRedesignedSendFlow: jest.fn().mockReturnValue({ enabled: false }),
17+
}));
18+
1519
describe('<WalletInitiatedHeader />', () => {
1620
it('should match snapshot', () => {
1721
const { container } = render();

ui/pages/confirmations/components/confirm/header/wallet-initiated-header.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ import { useI18nContext } from '../../../../../hooks/useI18nContext';
2929
import { showSendTokenPage } from '../../../../../store/actions';
3030
import { useConfirmContext } from '../../../context/confirm';
3131
import { navigateToSendRoute } from '../../../utils/send';
32+
import { useRedesignedSendFlow } from '../../../hooks/useRedesignedSendFlow';
3233
import { AdvancedDetailsButton } from './advanced-details-button';
3334

3435
export const WalletInitiatedHeader = () => {
3536
const t = useI18nContext();
3637
const dispatch = useDispatch();
3738
const history = useHistory();
39+
const { enabled: isSendRedesignEnabled } = useRedesignedSendFlow();
3840

3941
const { currentConfirmation } = useConfirmContext<TransactionMeta>();
4042

@@ -63,8 +65,8 @@ export const WalletInitiatedHeader = () => {
6365
await dispatch(editExistingTransaction(assetType, id.toString()));
6466
dispatch(clearConfirmTransaction());
6567
dispatch(showSendTokenPage());
66-
navigateToSendRoute(history);
67-
}, [currentConfirmation, dispatch, history]);
68+
navigateToSendRoute(history, isSendRedesignEnabled);
69+
}, [currentConfirmation, dispatch, history, isSendRedesignEnabled]);
6870

6971
return (
7072
<Box

ui/pages/confirmations/hooks/useConfirmSendNavigation.test.ts

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import mockState from '../../../../test/data/mock-state.json';
22
import { renderHookWithProvider } from '../../../../test/lib/render-helpers';
33
import * as ConfirmContext from '../context/confirm';
4-
4+
import { useRedesignedSendFlow } from './useRedesignedSendFlow';
55
import { useConfirmSendNavigation } from './useConfirmSendNavigation';
66

7+
const mockUseRedesignedSendFlow = jest.mocked(useRedesignedSendFlow);
8+
79
const mockHistory = {
810
goBack: jest.fn(),
911
push: jest.fn(),
@@ -23,34 +25,89 @@ jest.mock('react-redux', () => ({
2325
},
2426
}));
2527

26-
function renderHook() {
27-
const { result } = renderHookWithProvider(
28-
useConfirmSendNavigation,
29-
mockState,
30-
);
31-
return result.current;
32-
}
28+
jest.mock('./useRedesignedSendFlow');
3329

3430
describe('useConfirmSendNavigation', () => {
3531
afterEach(() => {
3632
jest.clearAllMocks();
3733
});
3834

39-
it('result returns method navigateBackIfSend', () => {
35+
const renderHook = () => {
36+
const { result } = renderHookWithProvider(
37+
useConfirmSendNavigation,
38+
mockState,
39+
);
40+
return result.current;
41+
};
42+
43+
it('returns navigateBackIfSend method', () => {
4044
jest
4145
.spyOn(ConfirmContext, 'useConfirmContext')
4246
.mockReturnValue({} as unknown as ConfirmContext.ConfirmContextType);
47+
mockUseRedesignedSendFlow.mockReturnValue({ enabled: false });
48+
4349
const result = renderHook();
50+
4451
expect(result.navigateBackIfSend).toBeDefined();
4552
});
4653

47-
// eslint-disable-next-line mocha/no-skipped-tests
48-
it.skip('result returns method handleBack to goto previous page', () => {
54+
it('does not navigate back when send redesign is disabled', () => {
55+
jest.spyOn(ConfirmContext, 'useConfirmContext').mockReturnValue({
56+
currentConfirmation: { origin: 'metamask', type: 'simpleSend' },
57+
} as unknown as ConfirmContext.ConfirmContextType);
58+
mockUseRedesignedSendFlow.mockReturnValue({ enabled: false });
59+
60+
const result = renderHook();
61+
result.navigateBackIfSend();
62+
63+
expect(mockHistory.goBack).not.toHaveBeenCalled();
64+
});
65+
66+
it('navigates back when send redesign is enabled and confirmation is metamask simpleSend', () => {
4967
jest.spyOn(ConfirmContext, 'useConfirmContext').mockReturnValue({
5068
currentConfirmation: { origin: 'metamask', type: 'simpleSend' },
5169
} as unknown as ConfirmContext.ConfirmContextType);
70+
mockUseRedesignedSendFlow.mockReturnValue({ enabled: true });
71+
5272
const result = renderHook();
5373
result.navigateBackIfSend();
74+
5475
expect(mockHistory.goBack).toHaveBeenCalled();
5576
});
77+
78+
it('does not navigate back when send redesign is enabled but origin is not metamask', () => {
79+
jest.spyOn(ConfirmContext, 'useConfirmContext').mockReturnValue({
80+
currentConfirmation: { origin: 'dapp', type: 'simpleSend' },
81+
} as unknown as ConfirmContext.ConfirmContextType);
82+
mockUseRedesignedSendFlow.mockReturnValue({ enabled: true });
83+
84+
const result = renderHook();
85+
result.navigateBackIfSend();
86+
87+
expect(mockHistory.goBack).not.toHaveBeenCalled();
88+
});
89+
90+
it('does not navigate back when send redesign is enabled but type is not simpleSend', () => {
91+
jest.spyOn(ConfirmContext, 'useConfirmContext').mockReturnValue({
92+
currentConfirmation: { origin: 'metamask', type: 'contractInteraction' },
93+
} as unknown as ConfirmContext.ConfirmContextType);
94+
mockUseRedesignedSendFlow.mockReturnValue({ enabled: true });
95+
96+
const result = renderHook();
97+
result.navigateBackIfSend();
98+
99+
expect(mockHistory.goBack).not.toHaveBeenCalled();
100+
});
101+
102+
it('does not navigate back when send redesign is enabled but both origin and type do not match', () => {
103+
jest.spyOn(ConfirmContext, 'useConfirmContext').mockReturnValue({
104+
currentConfirmation: { origin: 'dapp', type: 'contractInteraction' },
105+
} as unknown as ConfirmContext.ConfirmContextType);
106+
mockUseRedesignedSendFlow.mockReturnValue({ enabled: true });
107+
108+
const result = renderHook();
109+
result.navigateBackIfSend();
110+
111+
expect(mockHistory.goBack).not.toHaveBeenCalled();
112+
});
56113
});

ui/pages/confirmations/hooks/useConfirmSendNavigation.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ import { useCallback } from 'react';
33
import { useHistory } from 'react-router-dom';
44

55
import { useConfirmContext } from '../context/confirm';
6+
import { useRedesignedSendFlow } from './useRedesignedSendFlow';
67

78
export const useConfirmSendNavigation = () => {
89
const history = useHistory();
910
const { currentConfirmation } = useConfirmContext<TransactionMeta>();
11+
const { enabled: isSendRedesignEnabled } = useRedesignedSendFlow();
1012

1113
const navigateBackIfSend = useCallback(() => {
12-
if (!process.env.SEND_REDESIGN_ENABLED) {
14+
if (!isSendRedesignEnabled) {
1315
return;
1416
}
1517
const { origin, type } = currentConfirmation;
1618
if (origin === 'metamask' && type === 'simpleSend') {
1719
history.goBack();
1820
}
19-
}, [currentConfirmation, history]);
21+
}, [currentConfirmation, history, isSendRedesignEnabled]);
2022

2123
return { navigateBackIfSend };
2224
};

0 commit comments

Comments
 (0)