Skip to content

Commit 572496a

Browse files
fix: smart transactions in redesigned confirmations (#28273)
## **Description** Ensure smart transaction feature flags are refreshed when redesigned confirmations are shown. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28273?quickstart=1) ## **Related issues** ## **Manual testing steps** 1. Install fresh extension. 2. Create transaction using redesigned confirmation. 3. Ensure smart transaction is performed. ## **Screenshots/Recordings** ### **Before** ### **After** ## **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/develop/.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/develop/.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 4a53aea commit 572496a

File tree

4 files changed

+177
-0
lines changed

4 files changed

+177
-0
lines changed

test/data/confirmations/helper.ts

+1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export const getMockConfirmState = (args: RootState = { metamask: {} }) => ({
133133
...args.metamask,
134134
preferences: {
135135
...mockState.metamask.preferences,
136+
...(args.metamask?.preferences as Record<string, unknown>),
136137
redesignedTransactionsEnabled: true,
137138
redesignedConfirmationsEnabled: true,
138139
isRedesignedConfirmationsDeveloperEnabled: true,

ui/pages/confirmations/components/confirm/info/info.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TransactionType } from '@metamask/transaction-controller';
22
import React, { useMemo } from 'react';
33
import { useConfirmContext } from '../../../context/confirm';
44
import { SignatureRequestType } from '../../../types/confirm';
5+
import { useSmartTransactionFeatureFlags } from '../../../hooks/useSmartTransactionFeatureFlags';
56
import ApproveInfo from './approve/approve';
67
import BaseTransactionInfo from './base-transaction-info/base-transaction-info';
78
import NativeTransferInfo from './native-transfer/native-transfer';
@@ -15,6 +16,9 @@ import TypedSignInfo from './typed-sign/typed-sign';
1516
const Info = () => {
1617
const { currentConfirmation } = useConfirmContext();
1718

19+
// TODO: Create TransactionInfo and SignatureInfo components.
20+
useSmartTransactionFeatureFlags();
21+
1822
const ConfirmationInfoComponentMap = useMemo(
1923
() => ({
2024
[TransactionType.contractInteraction]: () => BaseTransactionInfo,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { act } from 'react-dom/test-utils';
2+
import { useDispatch } from 'react-redux';
3+
import { CHAIN_IDS, TransactionMeta } from '@metamask/transaction-controller';
4+
import { Hex } from '@metamask/utils';
5+
import {
6+
fetchSmartTransactionsLiveness,
7+
setSwapsFeatureFlags,
8+
} from '../../../store/actions';
9+
import { renderHookWithConfirmContextProvider } from '../../../../test/lib/confirmations/render-helpers';
10+
import { genUnapprovedContractInteractionConfirmation } from '../../../../test/data/confirmations/contract-interaction';
11+
import { getMockConfirmStateForTransaction } from '../../../../test/data/confirmations/helper';
12+
import { mockNetworkState } from '../../../../test/stub/networks';
13+
import { fetchSwapsFeatureFlags } from '../../swaps/swaps.util';
14+
import { useSmartTransactionFeatureFlags } from './useSmartTransactionFeatureFlags';
15+
16+
jest.mock('react-redux', () => ({
17+
...jest.requireActual('react-redux'),
18+
useDispatch: jest.fn(),
19+
}));
20+
21+
jest.mock('../../../store/actions', () => ({
22+
...jest.requireActual('../../../store/actions'),
23+
setSwapsFeatureFlags: jest.fn(),
24+
fetchSmartTransactionsLiveness: jest.fn(),
25+
}));
26+
27+
jest.mock('../../swaps/swaps.util', () => ({
28+
...jest.requireActual('../../swaps/swaps.util'),
29+
fetchSwapsFeatureFlags: jest.fn(),
30+
}));
31+
32+
async function runHook({
33+
smartTransactionsOptInStatus,
34+
chainId,
35+
confirmation,
36+
}: {
37+
smartTransactionsOptInStatus: boolean;
38+
chainId: Hex;
39+
confirmation?: Partial<TransactionMeta>;
40+
}) {
41+
const transaction =
42+
(confirmation as TransactionMeta) ??
43+
genUnapprovedContractInteractionConfirmation({
44+
chainId,
45+
});
46+
47+
const state = getMockConfirmStateForTransaction(transaction, {
48+
metamask: {
49+
...mockNetworkState({ chainId, id: 'Test' }),
50+
selectedNetworkClientId: 'Test',
51+
preferences: {
52+
smartTransactionsOptInStatus,
53+
},
54+
},
55+
});
56+
57+
renderHookWithConfirmContextProvider(
58+
() => useSmartTransactionFeatureFlags(),
59+
state,
60+
);
61+
62+
await act(async () => {
63+
// Intentionally empty
64+
});
65+
}
66+
67+
describe('useSmartTransactionFeatureFlags', () => {
68+
const setSwapsFeatureFlagsMock = jest.mocked(setSwapsFeatureFlags);
69+
const fetchSwapsFeatureFlagsMock = jest.mocked(fetchSwapsFeatureFlags);
70+
const fetchSmartTransactionsLivenessMock = jest.mocked(
71+
fetchSmartTransactionsLiveness,
72+
);
73+
const useDispatchMock = jest.mocked(useDispatch);
74+
75+
beforeEach(() => {
76+
jest.resetAllMocks();
77+
useDispatchMock.mockReturnValue(jest.fn());
78+
fetchSwapsFeatureFlagsMock.mockResolvedValue({});
79+
fetchSmartTransactionsLivenessMock.mockReturnValue(() => Promise.resolve());
80+
});
81+
82+
it('updates feature flags', async () => {
83+
await runHook({
84+
smartTransactionsOptInStatus: true,
85+
chainId: CHAIN_IDS.MAINNET,
86+
});
87+
88+
expect(setSwapsFeatureFlagsMock).toHaveBeenCalledTimes(1);
89+
expect(setSwapsFeatureFlagsMock).toHaveBeenCalledWith({});
90+
});
91+
92+
it('does not update feature flags if smart transactions disabled', async () => {
93+
await runHook({
94+
smartTransactionsOptInStatus: false,
95+
chainId: CHAIN_IDS.MAINNET,
96+
});
97+
98+
expect(setSwapsFeatureFlagsMock).not.toHaveBeenCalled();
99+
});
100+
101+
it('does not update feature flags if chain not supported', async () => {
102+
await runHook({
103+
smartTransactionsOptInStatus: true,
104+
chainId: CHAIN_IDS.ARBITRUM,
105+
});
106+
107+
expect(setSwapsFeatureFlagsMock).not.toHaveBeenCalled();
108+
});
109+
110+
it('does not update feature flags if confirmation is not transaction', async () => {
111+
await runHook({
112+
smartTransactionsOptInStatus: true,
113+
chainId: CHAIN_IDS.MAINNET,
114+
confirmation: {},
115+
});
116+
117+
expect(setSwapsFeatureFlagsMock).not.toHaveBeenCalled();
118+
});
119+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { useDispatch, useSelector } from 'react-redux';
2+
import { useEffect } from 'react';
3+
import { TransactionMeta } from '@metamask/transaction-controller';
4+
import log from 'loglevel';
5+
import {
6+
getCurrentChainSupportsSmartTransactions,
7+
getSmartTransactionsPreferenceEnabled,
8+
} from '../../../../shared/modules/selectors';
9+
import { fetchSwapsFeatureFlags } from '../../swaps/swaps.util';
10+
import {
11+
fetchSmartTransactionsLiveness,
12+
setSwapsFeatureFlags,
13+
} from '../../../store/actions';
14+
import { useConfirmContext } from '../context/confirm';
15+
16+
export function useSmartTransactionFeatureFlags() {
17+
const dispatch = useDispatch();
18+
const { currentConfirmation } = useConfirmContext<TransactionMeta>();
19+
const { id: transactionId, txParams } = currentConfirmation ?? {};
20+
const isTransaction = Boolean(txParams);
21+
22+
const smartTransactionsPreferenceEnabled = useSelector(
23+
getSmartTransactionsPreferenceEnabled,
24+
);
25+
26+
const currentChainSupportsSmartTransactions = useSelector(
27+
getCurrentChainSupportsSmartTransactions,
28+
);
29+
30+
useEffect(() => {
31+
if (
32+
!isTransaction ||
33+
!transactionId ||
34+
!smartTransactionsPreferenceEnabled ||
35+
!currentChainSupportsSmartTransactions
36+
) {
37+
return;
38+
}
39+
40+
Promise.all([fetchSwapsFeatureFlags(), fetchSmartTransactionsLiveness()()])
41+
.then(([swapsFeatureFlags]) => {
42+
dispatch(setSwapsFeatureFlags(swapsFeatureFlags));
43+
})
44+
.catch((error) => {
45+
log.debug('Error updating smart transaction feature flags', error);
46+
});
47+
}, [
48+
isTransaction,
49+
transactionId,
50+
smartTransactionsPreferenceEnabled,
51+
currentChainSupportsSmartTransactions,
52+
]);
53+
}

0 commit comments

Comments
 (0)