Skip to content

Commit

Permalink
chore: MMI adds mmi flow to new transactions confirmations view (#26817)
Browse files Browse the repository at this point in the history
<!--
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**

The MMI tx flow is working again, we were missing the support for the
new redesign of the transactions confirmation UI.

## **Related issues**

Fixes:

## **Manual testing steps**

1. Go to this page...
2.
3.

## **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**

- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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.
  • Loading branch information
zone-live authored Sep 3, 2024
1 parent 0a90e50 commit 9c24dac
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 2 deletions.
10 changes: 9 additions & 1 deletion ui/hooks/useMMIConfirmations.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { TransactionType } from '@metamask/transaction-controller';
import {
TransactionMeta,
TransactionType,
} from '@metamask/transaction-controller';
import { useSelector } from 'react-redux';

import { currentConfirmationSelector } from '../pages/confirmations/selectors';
import { useMMICustodySignMessage } from './useMMICustodySignMessage';
import { useMMICustodySendTransaction } from './useMMICustodySendTransaction';

export function useMMIConfirmations() {
const { custodySignFn } = useMMICustodySignMessage();
const { custodyTransactionFn } = useMMICustodySendTransaction();

const currentConfirmation = useSelector(currentConfirmationSelector);

return {
Expand All @@ -15,5 +21,7 @@ export function useMMIConfirmations() {
currentConfirmation.type === TransactionType.signTypedData) &&
Boolean(currentConfirmation?.custodyId),
mmiOnSignCallback: () => custodySignFn(currentConfirmation),
mmiOnTransactionCallback: () =>
custodyTransactionFn(currentConfirmation as TransactionMeta),
};
}
101 changes: 101 additions & 0 deletions ui/hooks/useMMICustodySendTransaction.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { renderHook } from '@testing-library/react-hooks';
import { useHistory } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { mmiActionsFactory } from '../store/institutional/institution-background';
import { updateAndApproveTx } from '../store/actions';
import { useMMICustodySendTransaction } from './useMMICustodySendTransaction';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: jest.fn(),
}));

jest.mock('react-redux', () => ({
useDispatch: jest.fn(),
useSelector: jest.fn(),
}));

jest.mock('@metamask-institutional/extension', () => ({
showCustodianDeepLink: jest.fn(),
}));

jest.mock('../store/institutional/institution-background', () => ({
mmiActionsFactory: jest.fn(),
}));

jest.mock('../selectors', () => ({
getAccountType: jest.fn(),
}));

jest.mock('../store/actions', () => ({
updateAndApproveTx: jest.fn(),
}));

describe('useMMICustodySendTransaction', () => {
it('handles custody account type', async () => {
const dispatch = jest.fn();

useDispatch.mockReturnValue(dispatch);
useSelector.mockReturnValue('custody');
mmiActionsFactory.mockReturnValue({
setWaitForConfirmDeepLinkDialog: jest.fn(),
});

const { result } = renderHook(() => useMMICustodySendTransaction());
await result.current.custodyTransactionFn({
id: '5f2868d0-6480-11ef-a924-4325b6aee02e',
txParams: {
from: '0xe8f748699e2fe0f8133081914473e80bb60df71a',
data: '0x97c5ed1e00000',
gas: '0x8609',
to: '0xfe7a0f0c76c136b9b438dcb27de9a1b618c016fc',
value: '0x0',
maxFeePerGas: '0xb232c6726',
maxPriorityFeePerGas: '0x59682f00',
},
type: 'contractInteraction',
networkClientId: 'sepolia',
});

expect(dispatch).toHaveBeenCalled();
expect(updateAndApproveTx).toHaveBeenCalledWith(
expect.objectContaining({
id: '5f2868d0-6480-11ef-a924-4325b6aee02e',
}),
true,
'',
);
});

it('handles non-custody account type', async () => {
const dispatch = jest.fn();
const mockHistoryPush = jest.fn();
useHistory.mockReturnValue({ push: mockHistoryPush });

useDispatch.mockReturnValue(dispatch);
useSelector.mockReturnValue('non-custody');
mmiActionsFactory.mockReturnValue({
setWaitForConfirmDeepLinkDialog: jest.fn(),
});

const { result } = renderHook(() => useMMICustodySendTransaction());
await result.current.custodyTransactionFn({
id: '5f2868d0-6480-11ef-a924-4325b6aee02e',
txParams: {
from: '0xe8f748699e2fe0f8133081914473e80bb60df71a',
data: '0x97c5ed1e00000',
gas: '0x8609',
to: '0xfe7a0f0c76c136b9b438dcb27de9a1b618c016fc',
value: '0x0',
maxFeePerGas: '0xb232c6726',
maxPriorityFeePerGas: '0x59682f00',
},
type: 'contractInteraction',
networkClientId: 'sepolia',
});

expect(dispatch).toHaveBeenCalled();
expect(mockHistoryPush).toHaveBeenCalled();
expect(updateAndApproveTx).toHaveBeenCalled();
});
});
63 changes: 63 additions & 0 deletions ui/hooks/useMMICustodySendTransaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { TransactionMeta } from '@metamask/transaction-controller';
import { showCustodianDeepLink } from '@metamask-institutional/extension';
import { updateAndApproveTx } from '../store/actions';
import { AccountType, CustodyStatus } from '../../shared/constants/custody';
import { getMostRecentOverviewPage } from '../ducks/history/history';
import { clearConfirmTransaction } from '../ducks/confirm-transaction/confirm-transaction.duck';
import { getAccountType } from '../selectors/selectors';
import { mmiActionsFactory } from '../store/institutional/institution-background';
import { showCustodyConfirmLink } from '../store/institutional/institution-actions';

type MMITransactionMeta = TransactionMeta & {
txParams: { from: string };
custodyStatus: CustodyStatus;
metadata: Record<string, unknown>;
};

export function useMMICustodySendTransaction() {
const dispatch = useDispatch();
const history = useHistory();
const mmiActions = mmiActionsFactory();
const accountType = useSelector(getAccountType);
const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage);

const custodyTransactionFn = async (_transactionData: TransactionMeta) => {
const confirmation = _transactionData as MMITransactionMeta;

if (confirmation && accountType === AccountType.CUSTODY) {
confirmation.custodyStatus = CustodyStatus.CREATED;
confirmation.metadata = confirmation.metadata || {};

dispatch(mmiActions.setWaitForConfirmDeepLinkDialog(true));

const txId = confirmation.id;
const fromAddress = confirmation.txParams.from;
const closeNotification = false;

await dispatch(updateAndApproveTx(confirmation, true, ''));
showCustodianDeepLink({
dispatch,
mmiActions,
txId,
fromAddress,
closeNotification,
isSignature: false,
custodyId: '',
onDeepLinkFetched: () => undefined,
onDeepLinkShown: () => {
dispatch(clearConfirmTransaction());
},
showCustodyConfirmLink,
});
} else {
// Non Custody accounts follow normal flow
await dispatch(updateAndApproveTx(_transactionData, true, ''));
dispatch(clearConfirmTransaction());
history.push(mostRecentOverviewPage);
}
};

return { custodyTransactionFn };
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ describe('ConfirmFooter', () => {
.spyOn(MMIConfirmations, 'useMMIConfirmations')
.mockImplementation(() => ({
mmiOnSignCallback: () => Promise.resolve(),
mmiOnTransactionCallback: () => Promise.resolve(),
mmiSubmitDisabled: true,
}));
const { getAllByRole } = render();
Expand All @@ -172,6 +173,7 @@ describe('ConfirmFooter', () => {
.spyOn(MMIConfirmations, 'useMMIConfirmations')
.mockImplementation(() => ({
mmiOnSignCallback: mockFn,
mmiOnTransactionCallback: mockFn,
mmiSubmitDisabled: false,
}));
const { getAllByRole } = render();
Expand Down
15 changes: 14 additions & 1 deletion ui/pages/confirmations/components/confirm/footer/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
import { TransactionMeta } from '@metamask/transaction-controller';
///: END:ONLY_INCLUDE_IF
import { ethErrors, serializeError } from 'eth-rpc-errors';
import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
Expand All @@ -22,7 +24,9 @@ import useAlerts from '../../../../../hooks/useAlerts';
import {
rejectPendingApproval,
resolvePendingApproval,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
updateAndApproveTx,
///: END:ONLY_INCLUDE_IF
} from '../../../../../store/actions';
import { confirmSelector } from '../../../selectors';
import { REDESIGN_DEV_TRANSACTION_TYPES } from '../../../utils';
Expand Down Expand Up @@ -109,7 +113,8 @@ const Footer = () => {
const { from } = getConfirmationSender(currentConfirmation);

///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
const { mmiOnSignCallback, mmiSubmitDisabled } = useMMIConfirmations();
const { mmiOnTransactionCallback, mmiOnSignCallback, mmiSubmitDisabled } =
useMMIConfirmations();
///: END:ONLY_INCLUDE_IF

const hardwareWalletRequiresConnection = useSelector((state) => {
Expand Down Expand Up @@ -144,6 +149,7 @@ const Footer = () => {
(type) => type === currentConfirmation?.type,
);
if (isTransactionConfirmation) {
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const mergeTxDataWithNonce = (transactionData: TransactionMeta) =>
customNonceValue
? { ...transactionData, customNonceValue }
Expand All @@ -152,8 +158,15 @@ const Footer = () => {
const updatedTx = mergeTxDataWithNonce(
currentConfirmation as TransactionMeta,
);
///: END:ONLY_INCLUDE_IF

///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
mmiOnTransactionCallback();
///: END:ONLY_INCLUDE_IF

///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
dispatch(updateAndApproveTx(updatedTx, true, ''));
///: END:ONLY_INCLUDE_IF
} else {
dispatch(resolvePendingApproval(currentConfirmation.id, undefined));

Expand Down
8 changes: 8 additions & 0 deletions ui/pages/confirmations/hooks/useConfirmationAlerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { useNetworkBusyAlerts } from './alerts/transactions/useNetworkBusyAlerts
import { useNoGasPriceAlerts } from './alerts/transactions/useNoGasPriceAlerts';
import { usePendingTransactionAlerts } from './alerts/transactions/usePendingTransactionAlerts';
import { useQueuedConfirmationsAlerts } from './alerts/transactions/useQueuedConfirmationsAlerts';
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
import { useSigningOrSubmittingAlerts } from './alerts/transactions/useSigningOrSubmittingAlerts';
///: END:ONLY_INCLUDE_IF
import useConfirmationOriginAlerts from './alerts/useConfirmationOriginAlerts';
import useBlockaidAlerts from './alerts/useBlockaidAlerts';

Expand All @@ -32,7 +34,9 @@ function useTransactionAlerts(): Alert[] {
const networkBusyAlerts = useNetworkBusyAlerts();
const noGasPriceAlerts = useNoGasPriceAlerts();
const pendingTransactionAlerts = usePendingTransactionAlerts();
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const signingOrSubmittingAlerts = useSigningOrSubmittingAlerts();
///: END:ONLY_INCLUDE_IF
const queuedConfirmationsAlerts = useQueuedConfirmationsAlerts();

return useMemo(
Expand All @@ -44,7 +48,9 @@ function useTransactionAlerts(): Alert[] {
...networkBusyAlerts,
...noGasPriceAlerts,
...pendingTransactionAlerts,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
...signingOrSubmittingAlerts,
///: END:ONLY_INCLUDE_IF
...queuedConfirmationsAlerts,
],
[
Expand All @@ -55,7 +61,9 @@ function useTransactionAlerts(): Alert[] {
networkBusyAlerts,
noGasPriceAlerts,
pendingTransactionAlerts,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
signingOrSubmittingAlerts,
///: END:ONLY_INCLUDE_IF
queuedConfirmationsAlerts,
],
);
Expand Down

0 comments on commit 9c24dac

Please sign in to comment.