1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 18:00:18 +01:00

Accept SignController approval request from frontend (#19184)

This commit is contained in:
OGPoyraz 2023-06-20 15:37:09 +02:00 committed by Dan J Miller
parent 524ab017c7
commit 64cbe1f2f8
25 changed files with 281 additions and 848 deletions

View File

@ -1,18 +0,0 @@
diff --git a/dist/SignatureController.js b/dist/SignatureController.js
index b39d274f4547ab4e8b647293199ec21c4a9e38ca..288e55c97c3e4a234874dd8b8986ba77576b0dc4 100644
--- a/dist/SignatureController.js
+++ b/dist/SignatureController.js
@@ -308,12 +308,12 @@ _SignatureController_keyringController = new WeakMap(), _SignatureController_isE
const messageId = msgParams.metamaskId;
try {
const cleanMessageParams = yield messageManager.approveMessage(msgParams);
+ __classPrivateFieldGet(this, _SignatureController_instances, "m", _SignatureController_acceptApproval).call(this, messageId);
const signature = yield getSignature(cleanMessageParams);
this.hub.emit(`${methodName}:signed`, { signature, messageId });
if (!cleanMessageParams.deferSetAsSigned) {
messageManager.setMessageStatusSigned(messageId, signature);
}
- __classPrivateFieldGet(this, _SignatureController_instances, "m", _SignatureController_acceptApproval).call(this, messageId);
return __classPrivateFieldGet(this, _SignatureController_getAllState, "f").call(this);
}
catch (error) {

View File

@ -45,7 +45,7 @@ export type CoreMessage = AbstractMessage & {
}; };
export type StateMessage = Required< export type StateMessage = Required<
Omit<AbstractMessage, 'securityProviderResponse' | 'metadata'> Omit<AbstractMessage, 'securityProviderResponse' | 'metadata' | 'error'>
>; >;
export type DecryptMessageControllerState = { export type DecryptMessageControllerState = {

View File

@ -45,7 +45,7 @@ export type CoreMessage = AbstractMessage & {
}; };
export type StateMessage = Required< export type StateMessage = Required<
Omit<AbstractMessage, 'securityProviderResponse' | 'metadata'> Omit<AbstractMessage, 'securityProviderResponse' | 'metadata' | 'error'>
> & { > & {
msgParams: string; msgParams: string;
}; };

View File

@ -1258,11 +1258,7 @@ export default class MetamaskController extends EventEmitter {
this.signatureController = new SignatureController({ this.signatureController = new SignatureController({
messenger: this.controllerMessenger.getRestricted({ messenger: this.controllerMessenger.getRestricted({
name: 'SignatureController', name: 'SignatureController',
allowedActions: [ allowedActions: [`${this.approvalController.name}:addRequest`],
`${this.approvalController.name}:addRequest`,
`${this.approvalController.name}:acceptRequest`,
`${this.approvalController.name}:rejectRequest`,
],
}), }),
keyringController: this.keyringController, keyringController: this.keyringController,
isEthSignEnabled: () => isEthSignEnabled: () =>
@ -2248,27 +2244,6 @@ export default class MetamaskController extends EventEmitter {
updatePreviousGasParams: updatePreviousGasParams:
txController.updatePreviousGasParams.bind(txController), txController.updatePreviousGasParams.bind(txController),
// signatureController
signMessage: this.signatureController.signMessage.bind(
this.signatureController,
),
cancelMessage: this.signatureController.cancelMessage.bind(
this.signatureController,
),
signPersonalMessage: this.signatureController.signPersonalMessage.bind(
this.signatureController,
),
cancelPersonalMessage:
this.signatureController.cancelPersonalMessage.bind(
this.signatureController,
),
signTypedMessage: this.signatureController.signTypedMessage.bind(
this.signatureController,
),
cancelTypedMessage: this.signatureController.cancelTypedMessage.bind(
this.signatureController,
),
// decryptMessageController // decryptMessageController
decryptMessage: this.decryptMessageController.decryptMessage.bind( decryptMessage: this.decryptMessageController.decryptMessage.bind(
this.decryptMessageController, this.decryptMessageController,

View File

@ -1718,7 +1718,8 @@
"browserify>buffer": true, "browserify>buffer": true,
"browserify>events": true, "browserify>events": true,
"eth-rpc-errors": true, "eth-rpc-errors": true,
"ethereumjs-util": true "ethereumjs-util": true,
"lodash": true
} }
}, },
"@metamask/smart-transactions-controller": { "@metamask/smart-transactions-controller": {

View File

@ -1908,7 +1908,8 @@
"browserify>buffer": true, "browserify>buffer": true,
"browserify>events": true, "browserify>events": true,
"eth-rpc-errors": true, "eth-rpc-errors": true,
"ethereumjs-util": true "ethereumjs-util": true,
"lodash": true
} }
}, },
"@metamask/smart-transactions-controller": { "@metamask/smart-transactions-controller": {

View File

@ -1908,7 +1908,8 @@
"browserify>buffer": true, "browserify>buffer": true,
"browserify>events": true, "browserify>events": true,
"eth-rpc-errors": true, "eth-rpc-errors": true,
"ethereumjs-util": true "ethereumjs-util": true,
"lodash": true
} }
}, },
"@metamask/smart-transactions-controller": { "@metamask/smart-transactions-controller": {

View File

@ -1718,7 +1718,8 @@
"browserify>buffer": true, "browserify>buffer": true,
"browserify>events": true, "browserify>events": true,
"eth-rpc-errors": true, "eth-rpc-errors": true,
"ethereumjs-util": true "ethereumjs-util": true,
"lodash": true
} }
}, },
"@metamask/smart-transactions-controller": { "@metamask/smart-transactions-controller": {

View File

@ -196,8 +196,7 @@
"fast-json-patch@^3.1.1": "patch:fast-json-patch@npm%3A3.1.1#./.yarn/patches/fast-json-patch-npm-3.1.1-7e8bb70a45.patch", "fast-json-patch@^3.1.1": "patch:fast-json-patch@npm%3A3.1.1#./.yarn/patches/fast-json-patch-npm-3.1.1-7e8bb70a45.patch",
"request@^2.83.0": "patch:request@npm%3A2.88.2#./.yarn/patches/request-npm-2.88.2-f4a57c72c4.patch", "request@^2.83.0": "patch:request@npm%3A2.88.2#./.yarn/patches/request-npm-2.88.2-f4a57c72c4.patch",
"request@^2.88.2": "patch:request@npm%3A2.88.2#./.yarn/patches/request-npm-2.88.2-f4a57c72c4.patch", "request@^2.88.2": "patch:request@npm%3A2.88.2#./.yarn/patches/request-npm-2.88.2-f4a57c72c4.patch",
"request@^2.85.0": "patch:request@npm%3A2.88.2#./.yarn/patches/request-npm-2.88.2-f4a57c72c4.patch", "request@^2.85.0": "patch:request@npm%3A2.88.2#./.yarn/patches/request-npm-2.88.2-f4a57c72c4.patch"
"@metamask/signature-controller@^3.0.0": "patch:@metamask/signature-controller@npm%3A3.0.0#./.yarn/patches/@metamask-signature-controller-npm-3.0.0-8771b6885e.patch"
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.10.0",
@ -248,7 +247,7 @@
"@metamask/jazzicon": "^2.0.0", "@metamask/jazzicon": "^2.0.0",
"@metamask/key-tree": "^7.0.0", "@metamask/key-tree": "^7.0.0",
"@metamask/logo": "^3.1.1", "@metamask/logo": "^3.1.1",
"@metamask/message-manager": "^6.0.0", "@metamask/message-manager": "^7.0.0",
"@metamask/metamask-eth-abis": "^3.0.0", "@metamask/metamask-eth-abis": "^3.0.0",
"@metamask/notification-controller": "^3.0.0", "@metamask/notification-controller": "^3.0.0",
"@metamask/obs-store": "^8.1.0", "@metamask/obs-store": "^8.1.0",
@ -261,7 +260,7 @@
"@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.34.0-flask.1", "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.34.0-flask.1",
"@metamask/safe-event-emitter": "^2.0.0", "@metamask/safe-event-emitter": "^2.0.0",
"@metamask/scure-bip39": "^2.0.3", "@metamask/scure-bip39": "^2.0.3",
"@metamask/signature-controller": "^3.0.0", "@metamask/signature-controller": "^4.0.1",
"@metamask/slip44": "^3.0.0", "@metamask/slip44": "^3.0.0",
"@metamask/smart-transactions-controller": "^3.1.0", "@metamask/smart-transactions-controller": "^3.1.0",
"@metamask/snaps-controllers": "^0.32.2", "@metamask/snaps-controllers": "^0.32.2",

View File

@ -1,5 +1,6 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import { getCurrentQRHardwareState } from '../../../selectors'; import { getCurrentQRHardwareState } from '../../../selectors';
import Popover from '../../ui/popover'; import Popover from '../../ui/popover';
import { useI18nContext } from '../../../hooks/useI18nContext'; import { useI18nContext } from '../../../hooks/useI18nContext';
@ -7,11 +8,8 @@ import {
cancelSyncQRHardware as cancelSyncQRHardwareAction, cancelSyncQRHardware as cancelSyncQRHardwareAction,
cancelQRHardwareSignRequest as cancelQRHardwareSignRequestAction, cancelQRHardwareSignRequest as cancelQRHardwareSignRequestAction,
cancelTx, cancelTx,
cancelPersonalMsg, rejectPendingApproval,
cancelMsg,
cancelTypedMsg,
} from '../../../store/actions'; } from '../../../store/actions';
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import QRHardwareWalletImporter from './qr-hardware-wallet-importer'; import QRHardwareWalletImporter from './qr-hardware-wallet-importer';
import QRHardwareSignRequest from './qr-hardware-sign-request'; import QRHardwareSignRequest from './qr-hardware-sign-request';
@ -43,25 +41,13 @@ const QRHardwarePopover = () => {
); );
const signRequestCancel = useCallback(() => { const signRequestCancel = useCallback(() => {
let action = cancelTx; dispatch(
switch (_txData.type) { rejectPendingApproval(
case MESSAGE_TYPE.PERSONAL_SIGN: { _txData.id,
action = cancelPersonalMsg; serializeError(ethErrors.provider.userRejectedRequest()),
break; ),
} );
case MESSAGE_TYPE.ETH_SIGN: { dispatch(cancelTx(_txData));
action = cancelMsg;
break;
}
case MESSAGE_TYPE.ETH_SIGN_TYPED_DATA: {
action = cancelTypedMsg;
break;
}
default: {
action = cancelTx;
}
}
dispatch(action(_txData));
dispatch(cancelQRHardwareSignRequestAction()); dispatch(cancelQRHardwareSignRequestAction());
}, [dispatch, _txData]); }, [dispatch, _txData]);

View File

@ -96,6 +96,7 @@ const SignatureRequestOriginalWarning = ({
<Button <Button
className="signature-request-warning__footer__sign-button" className="signature-request-warning__footer__sign-button"
type="danger-primary" type="danger-primary"
data-testid="signature-warning-sign-button"
onClick={onSubmit} onClick={onSubmit}
> >
{t('sign')} {t('sign')}

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
import { ObjectInspector } from 'react-inspector'; import { ObjectInspector } from 'react-inspector';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import LedgerInstructionField from '../ledger-instruction-field'; import LedgerInstructionField from '../ledger-instruction-field';
import { MESSAGE_TYPE } from '../../../../shared/constants/app'; import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import { import {
@ -49,18 +50,19 @@ export default class SignatureRequestOriginal extends Component {
address: PropTypes.string.isRequired, address: PropTypes.string.isRequired,
name: PropTypes.string, name: PropTypes.string,
}).isRequired, }).isRequired,
cancel: PropTypes.func.isRequired,
clearConfirmTransaction: PropTypes.func.isRequired, clearConfirmTransaction: PropTypes.func.isRequired,
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
mostRecentOverviewPage: PropTypes.string.isRequired, mostRecentOverviewPage: PropTypes.string.isRequired,
sign: PropTypes.func.isRequired,
txData: PropTypes.object.isRequired, txData: PropTypes.object.isRequired,
subjectMetadata: PropTypes.object, subjectMetadata: PropTypes.object,
hardwareWalletRequiresConnection: PropTypes.bool, hardwareWalletRequiresConnection: PropTypes.bool,
isLedgerWallet: PropTypes.bool, isLedgerWallet: PropTypes.bool,
messagesCount: PropTypes.number, messagesCount: PropTypes.number,
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired, showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
cancelAll: PropTypes.func.isRequired, cancelAllApprovals: PropTypes.func.isRequired,
rejectPendingApproval: PropTypes.func.isRequired,
resolvePendingApproval: PropTypes.func.isRequired,
completedTx: PropTypes.func.isRequired,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
selectedAccount: PropTypes.object, selectedAccount: PropTypes.object,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
@ -230,33 +232,48 @@ export default class SignatureRequestOriginal extends Component {
); );
}; };
onSubmit = async (event) => { onSubmit = async () => {
const { clearConfirmTransaction, history, mostRecentOverviewPage, sign } = const {
this.props; clearConfirmTransaction,
history,
mostRecentOverviewPage,
resolvePendingApproval,
completedTx,
txData: { id },
} = this.props;
await sign(event); await resolvePendingApproval(id);
completedTx(id);
clearConfirmTransaction(); clearConfirmTransaction();
history.push(mostRecentOverviewPage); history.push(mostRecentOverviewPage);
}; };
onCancel = async (event) => { onCancel = async () => {
const { clearConfirmTransaction, history, mostRecentOverviewPage, cancel } = const {
this.props; clearConfirmTransaction,
history,
mostRecentOverviewPage,
rejectPendingApproval,
txData: { id },
} = this.props;
await cancel(event); await rejectPendingApproval(
id,
serializeError(ethErrors.provider.userRejectedRequest()),
);
clearConfirmTransaction(); clearConfirmTransaction();
history.push(mostRecentOverviewPage); history.push(mostRecentOverviewPage);
}; };
renderFooter = () => { renderFooter = () => {
const { const {
cancel,
sign,
clearConfirmTransaction, clearConfirmTransaction,
history, history,
mostRecentOverviewPage, mostRecentOverviewPage,
txData: { type }, txData: { type, id },
hardwareWalletRequiresConnection, hardwareWalletRequiresConnection,
rejectPendingApproval,
resolvePendingApproval,
} = this.props; } = this.props;
const { t } = this.context; const { t } = this.context;
@ -264,16 +281,19 @@ export default class SignatureRequestOriginal extends Component {
<PageContainerFooter <PageContainerFooter
cancelText={t('reject')} cancelText={t('reject')}
submitText={t('sign')} submitText={t('sign')}
onCancel={async (event) => { onCancel={async () => {
await cancel(event); await rejectPendingApproval(
id,
serializeError(ethErrors.provider.userRejectedRequest()),
);
clearConfirmTransaction(); clearConfirmTransaction();
history.push(mostRecentOverviewPage); history.push(mostRecentOverviewPage);
}} }}
onSubmit={async (event) => { onSubmit={async () => {
if (type === MESSAGE_TYPE.ETH_SIGN) { if (type === MESSAGE_TYPE.ETH_SIGN) {
this.setState({ showSignatureRequestWarning: true }); this.setState({ showSignatureRequestWarning: true });
} else { } else {
await sign(event); await resolvePendingApproval(id);
clearConfirmTransaction(); clearConfirmTransaction();
history.push(mostRecentOverviewPage); history.push(mostRecentOverviewPage);
} }
@ -285,19 +305,19 @@ export default class SignatureRequestOriginal extends Component {
handleCancelAll = () => { handleCancelAll = () => {
const { const {
cancelAll,
clearConfirmTransaction, clearConfirmTransaction,
history, history,
mostRecentOverviewPage, mostRecentOverviewPage,
showRejectTransactionsConfirmationModal, showRejectTransactionsConfirmationModal,
messagesCount, messagesCount,
cancelAllApprovals,
} = this.props; } = this.props;
const unapprovedTxCount = messagesCount; const unapprovedTxCount = messagesCount;
showRejectTransactionsConfirmationModal({ showRejectTransactionsConfirmationModal({
unapprovedTxCount, unapprovedTxCount,
onSubmit: async () => { onSubmit: async () => {
await cancelAll(); await cancelAllApprovals();
clearConfirmTransaction(); clearConfirmTransaction();
history.push(mostRecentOverviewPage); history.push(mostRecentOverviewPage);
}, },

View File

@ -1,9 +1,14 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { compose } from 'redux'; import { compose } from 'redux';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import {
import { MESSAGE_TYPE } from '../../../../shared/constants/app'; goHome,
import { goHome, cancelMsgs, showModal } from '../../../store/actions'; showModal,
resolvePendingApproval,
rejectPendingApproval,
rejectAllMessages,
completedTx,
} from '../../../store/actions';
import { import {
accountsWithSendEtherInfoSelector, accountsWithSendEtherInfoSelector,
getSubjectMetadata, getSubjectMetadata,
@ -65,44 +70,30 @@ function mapDispatchToProps(dispatch) {
}), }),
); );
}, },
cancelAll: (messagesList) => dispatch(cancelMsgs(messagesList)), completedTx: (txId) => dispatch(completedTx(txId)),
resolvePendingApproval: (id) => {
dispatch(resolvePendingApproval(id));
},
rejectPendingApproval: (id, error) =>
dispatch(rejectPendingApproval(id, error)),
cancelAllApprovals: (messagesList) => {
dispatch(rejectAllMessages(messagesList));
},
}; };
} }
function mergeProps(stateProps, dispatchProps, ownProps) { function mergeProps(stateProps, dispatchProps, ownProps) {
const { const { txData } = ownProps;
signPersonalMessage,
signTypedMessage,
cancelPersonalMessage,
cancelTypedMessage,
signMessage,
cancelMessage,
txData,
} = ownProps;
const { allAccounts, messagesList, ...otherStateProps } = stateProps; const { allAccounts, messagesList, ...otherStateProps } = stateProps;
const { const {
type,
msgParams: { from }, msgParams: { from },
} = txData; } = txData;
const fromAccount = getAccountByAddress(allAccounts, from); const fromAccount = getAccountByAddress(allAccounts, from);
const { cancelAll: dispatchCancelAll } = dispatchProps; const { cancelAllApprovals: dispatchCancelAllApprovals } = dispatchProps;
let cancel;
let sign;
if (type === MESSAGE_TYPE.PERSONAL_SIGN) {
cancel = cancelPersonalMessage;
sign = signPersonalMessage;
} else if (type === MESSAGE_TYPE.ETH_SIGN_TYPED_DATA) {
cancel = cancelTypedMessage;
sign = signTypedMessage;
} else if (type === MESSAGE_TYPE.ETH_SIGN) {
cancel = cancelMessage;
sign = signMessage;
}
return { return {
...ownProps, ...ownProps,
@ -110,9 +101,8 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
...dispatchProps, ...dispatchProps,
fromAccount, fromAccount,
txData, txData,
cancel, cancelAllApprovals: () =>
sign, dispatchCancelAllApprovals(valuesFor(messagesList)),
cancelAll: () => dispatchCancelAll(valuesFor(messagesList)),
}; };
} }

View File

@ -1,13 +1,25 @@
import React from 'react'; import React from 'react';
import configureMockStore from 'redux-mock-store'; import configureMockStore from 'redux-mock-store';
import { fireEvent, screen } from '@testing-library/react'; import { fireEvent, screen } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { MESSAGE_TYPE } from '../../../../shared/constants/app'; import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import mockState from '../../../../test/data/mock-state.json'; import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/lib/render-helpers'; import { renderWithProvider } from '../../../../test/lib/render-helpers';
import configureStore from '../../../store/store'; import configureStore from '../../../store/store';
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants'; import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
import {
resolvePendingApproval,
rejectPendingApproval,
completedTx,
} from '../../../store/actions';
import SignatureRequestOriginal from '.'; import SignatureRequestOriginal from '.';
jest.mock('../../../store/actions', () => ({
resolvePendingApproval: jest.fn().mockReturnValue({ type: 'test' }),
rejectPendingApproval: jest.fn().mockReturnValue({ type: 'test' }),
completedTx: jest.fn().mockReturnValue({ type: 'test' }),
}));
const MOCK_SIGN_DATA = JSON.stringify({ const MOCK_SIGN_DATA = JSON.stringify({
domain: { domain: {
name: 'happydapp.website', name: 'happydapp.website',
@ -92,12 +104,30 @@ describe('SignatureRequestOriginal', () => {
expect(screen.getByText('Signature request')).toBeInTheDocument(); expect(screen.getByText('Signature request')).toBeInTheDocument();
}); });
it('should render warning for eth sign when sign button clicked', () => { it('should render warning for eth sign when sign button clicked', async () => {
render(); render();
const signButton = screen.getByTestId('page-container-footer-next'); const signButton = screen.getByTestId('page-container-footer-next');
fireEvent.click(signButton); fireEvent.click(signButton);
expect(screen.getByText('Your funds may be at risk')).toBeInTheDocument(); expect(screen.getByText('Your funds may be at risk')).toBeInTheDocument();
const secondSignButton = screen.getByTestId(
'signature-warning-sign-button',
);
await act(async () => {
fireEvent.click(secondSignButton);
});
expect(resolvePendingApproval).toHaveBeenCalledTimes(1);
expect(completedTx).toHaveBeenCalledTimes(1);
});
it('should cancel approval when user reject signing', async () => {
render();
const rejectButton = screen.getByTestId('page-container-footer-cancel');
await act(async () => {
fireEvent.click(rejectButton);
});
expect(rejectPendingApproval).toHaveBeenCalledTimes(1);
}); });
it('should escape RTL character in label or value', () => { it('should escape RTL character in label or value', () => {

View File

@ -4,6 +4,7 @@ import { useSelector, useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import log from 'loglevel'; import log from 'loglevel';
import { isValidSIWEOrigin } from '@metamask/controller-utils'; import { isValidSIWEOrigin } from '@metamask/controller-utils';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import { BannerAlert, Text } from '../../component-library'; import { BannerAlert, Text } from '../../component-library';
import Popover from '../../ui/popover'; import Popover from '../../ui/popover';
import Checkbox from '../../ui/check-box'; import Checkbox from '../../ui/check-box';
@ -25,27 +26,28 @@ import {
SEVERITIES, SEVERITIES,
TextVariant, TextVariant,
} from '../../../helpers/constants/design-system'; } from '../../../helpers/constants/design-system';
import {
resolvePendingApproval,
rejectPendingApproval,
rejectAllMessages,
completedTx,
showModal,
} from '../../../store/actions';
import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message'; import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message';
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants'; import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation'; import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation';
import { getMostRecentOverviewPage } from '../../../ducks/history/history'; import { getMostRecentOverviewPage } from '../../../ducks/history/history';
import { showModal, cancelMsgs } from '../../../store/actions';
import LedgerInstructionField from '../ledger-instruction-field'; import LedgerInstructionField from '../ledger-instruction-field';
import SignatureRequestHeader from '../signature-request-header'; import SignatureRequestHeader from '../signature-request-header';
import Header from './signature-request-siwe-header'; import Header from './signature-request-siwe-header';
import Message from './signature-request-siwe-message'; import Message from './signature-request-siwe-message';
export default function SignatureRequestSIWE({ export default function SignatureRequestSIWE({ txData }) {
txData,
cancelPersonalMessage,
signPersonalMessage,
}) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const history = useHistory();
const t = useContext(I18nContext); const t = useContext(I18nContext);
const allAccounts = useSelector(accountsWithSendEtherInfoSelector); const allAccounts = useSelector(accountsWithSendEtherInfoSelector);
const subjectMetadata = useSelector(getSubjectMetadata); const subjectMetadata = useSelector(getSubjectMetadata);
@ -59,6 +61,7 @@ export default function SignatureRequestSIWE({
origin, origin,
siwe: { parsedMessage }, siwe: { parsedMessage },
}, },
id,
} = txData; } = txData;
const isLedgerWallet = useSelector((state) => isAddressLedger(state, from)); const isLedgerWallet = useSelector((state) => isAddressLedger(state, from));
@ -82,27 +85,27 @@ export default function SignatureRequestSIWE({
(txData?.securityProviderResponse && (txData?.securityProviderResponse &&
Object.keys(txData.securityProviderResponse).length === 0); Object.keys(txData.securityProviderResponse).length === 0);
const onSign = useCallback( const onSign = useCallback(async () => {
async (event) => { try {
try { await dispatch(resolvePendingApproval(id, null));
await signPersonalMessage(event); dispatch(completedTx(id));
} catch (e) { } catch (e) {
log.error(e); log.error(e);
} }
}, }, [id, dispatch]);
[signPersonalMessage],
);
const onCancel = useCallback( const onCancel = useCallback(async () => {
async (event) => { try {
try { await dispatch(
await cancelPersonalMessage(event); rejectPendingApproval(
} catch (e) { id,
log.error(e); serializeError(ethErrors.provider.userRejectedRequest()),
} ),
}, );
[cancelPersonalMessage], } catch (e) {
); log.error(e);
}
}, []);
const handleCancelAll = () => { const handleCancelAll = () => {
const unapprovedTxCount = messagesCount; const unapprovedTxCount = messagesCount;
@ -112,7 +115,7 @@ export default function SignatureRequestSIWE({
name: 'REJECT_TRANSACTIONS', name: 'REJECT_TRANSACTIONS',
unapprovedTxCount, unapprovedTxCount,
onSubmit: async () => { onSubmit: async () => {
await dispatch(cancelMsgs(valuesFor(messagesList))); await dispatch(rejectAllMessages(valuesFor(messagesList)));
dispatch(clearConfirmTransaction()); dispatch(clearConfirmTransaction());
history.push(mostRecentOverviewPage); history.push(mostRecentOverviewPage);
}, },
@ -242,12 +245,4 @@ SignatureRequestSIWE.propTypes = {
* The display content of transaction data * The display content of transaction data
*/ */
txData: PropTypes.object.isRequired, txData: PropTypes.object.isRequired,
/**
* Handler for cancel button
*/
cancelPersonalMessage: PropTypes.func.isRequired,
/**
* Handler for sign button
*/
signPersonalMessage: PropTypes.func.isRequired,
}; };

View File

@ -1,6 +1,7 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { memoize } from 'lodash'; import { memoize } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import LedgerInstructionField from '../ledger-instruction-field'; import LedgerInstructionField from '../ledger-instruction-field';
import { import {
sanitizeMessage, sanitizeMessage,
@ -62,14 +63,7 @@ export default class SignatureRequest extends PureComponent {
* Check if the wallet is ledget wallet or not * Check if the wallet is ledget wallet or not
*/ */
isLedgerWallet: PropTypes.bool, isLedgerWallet: PropTypes.bool,
/**
* Handler for cancel button
*/
cancel: PropTypes.func.isRequired,
/**
* Handler for sign button
*/
sign: PropTypes.func.isRequired,
/** /**
* Whether the hardware wallet requires a connection disables the sign button if true. * Whether the hardware wallet requires a connection disables the sign button if true.
*/ */
@ -92,10 +86,14 @@ export default class SignatureRequest extends PureComponent {
history: PropTypes.object, history: PropTypes.object,
mostRecentOverviewPage: PropTypes.string, mostRecentOverviewPage: PropTypes.string,
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired, showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
cancelAll: PropTypes.func.isRequired, cancelAllApprovals: PropTypes.func.isRequired,
resolvePendingApproval: PropTypes.func.isRequired,
rejectPendingApproval: PropTypes.func.isRequired,
completedTx: PropTypes.func.isRequired,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
showCustodianDeepLink: PropTypes.func, showCustodianDeepLink: PropTypes.func,
isNotification: PropTypes.bool, isNotification: PropTypes.bool,
mmiOnSignCallback: PropTypes.func,
// Used to show a warning if the signing account is not the selected account // Used to show a warning if the signing account is not the selected account
// Largely relevant for contract wallet custodians // Largely relevant for contract wallet custodians
selectedAccount: PropTypes.object, selectedAccount: PropTypes.object,
@ -150,18 +148,18 @@ export default class SignatureRequest extends PureComponent {
handleCancelAll = () => { handleCancelAll = () => {
const { const {
cancelAll,
clearConfirmTransaction, clearConfirmTransaction,
history, history,
mostRecentOverviewPage, mostRecentOverviewPage,
showRejectTransactionsConfirmationModal, showRejectTransactionsConfirmationModal,
unapprovedMessagesCount, unapprovedMessagesCount,
cancelAllApprovals,
} = this.props; } = this.props;
showRejectTransactionsConfirmationModal({ showRejectTransactionsConfirmationModal({
unapprovedTxCount: unapprovedMessagesCount, unapprovedTxCount: unapprovedMessagesCount,
onSubmit: async () => { onSubmit: async () => {
await cancelAll(); await cancelAllApprovals();
clearConfirmTransaction(); clearConfirmTransaction();
history.push(mostRecentOverviewPage); history.push(mostRecentOverviewPage);
}, },
@ -174,10 +172,9 @@ export default class SignatureRequest extends PureComponent {
txData: { txData: {
msgParams: { data, origin, version }, msgParams: { data, origin, version },
type, type,
id,
}, },
fromAccount: { address, balance, name }, fromAccount: { address, balance, name },
cancel,
sign,
isLedgerWallet, isLedgerWallet,
hardwareWalletRequiresConnection, hardwareWalletRequiresConnection,
chainId, chainId,
@ -188,6 +185,9 @@ export default class SignatureRequest extends PureComponent {
currentCurrency, currentCurrency,
conversionRate, conversionRate,
unapprovedMessagesCount, unapprovedMessagesCount,
resolvePendingApproval,
rejectPendingApproval,
completedTx,
} = this.props; } = this.props;
const { t, trackEvent } = this.context; const { t, trackEvent } = this.context;
@ -221,8 +221,15 @@ export default class SignatureRequest extends PureComponent {
.toBase(10) .toBase(10)
.toString(); .toString();
const onSign = (event) => { const onSign = async () => {
sign(event); await resolvePendingApproval(id);
completedTx(id);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
if (this.props.mmiOnSignCallback) {
await this.props.mmiOnSignCallback(txData);
}
///: END:ONLY_INCLUDE_IN
trackEvent({ trackEvent({
category: MetaMetricsEventCategory.Transactions, category: MetaMetricsEventCategory.Transactions,
event: 'Confirm', event: 'Confirm',
@ -235,8 +242,11 @@ export default class SignatureRequest extends PureComponent {
}); });
}; };
const onCancel = (event) => { const onCancel = async () => {
cancel(event); await rejectPendingApproval(
id,
serializeError(ethErrors.provider.userRejectedRequest()),
);
trackEvent({ trackEvent({
category: MetaMetricsEventCategory.Transactions, category: MetaMetricsEventCategory.Transactions,
event: 'Cancel', event: 'Cancel',

View File

@ -13,6 +13,7 @@ import {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getAccountType, getAccountType,
getSelectedAccount, getSelectedAccount,
unapprovedTypedMessagesSelector,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
} from '../../../selectors'; } from '../../../selectors';
import { import {
@ -29,16 +30,20 @@ import {
setTypedMessageInProgress, setTypedMessageInProgress,
} from '../../../store/institutional/institution-background'; } from '../../../store/institutional/institution-background';
import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { checkForUnapprovedTypedMessages } from '../../../store/institutional/institution-actions';
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
import { import {
MESSAGE_TYPE,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_NOTIFICATION,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
} from '../../../../shared/constants/app'; } from '../../../../shared/constants/app';
import { import {
cancelMsgs,
showModal, showModal,
resolvePendingApproval,
rejectPendingApproval,
rejectAllMessages,
completedTx,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
goHome, goHome,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
@ -89,6 +94,7 @@ function mapStateToProps(state, ownProps) {
accountType: getAccountType(state), accountType: getAccountType(state),
isNotification: envType === ENVIRONMENT_TYPE_NOTIFICATION, isNotification: envType === ENVIRONMENT_TYPE_NOTIFICATION,
selectedAccount: getSelectedAccount(state), selectedAccount: getSelectedAccount(state),
unapprovedTypedMessages: unapprovedTypedMessagesSelector(state),
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
}; };
} }
@ -143,6 +149,10 @@ mapDispatchToProps = mmiMapDispatchToProps;
mapDispatchToProps = function (dispatch) { mapDispatchToProps = function (dispatch) {
return { return {
resolvePendingApproval: (id) => dispatch(resolvePendingApproval(id)),
completedTx: (id) => dispatch(completedTx(id)),
rejectPendingApproval: (id, error) =>
dispatch(rejectPendingApproval(id, error)),
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()), clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
showRejectTransactionsConfirmationModal: ({ showRejectTransactionsConfirmationModal: ({
onSubmit, onSubmit,
@ -157,8 +167,9 @@ mapDispatchToProps = function (dispatch) {
}), }),
); );
}, },
cancelAll: (unconfirmedMessagesList) => cancelAllApprovals: (unconfirmedMessagesList) => {
dispatch(cancelMsgs(unconfirmedMessagesList)), dispatch(rejectAllMessages(unconfirmedMessagesList));
},
}; };
}; };
@ -180,49 +191,33 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
accountType, accountType,
isNotification, isNotification,
unapprovedTypedMessages,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
} = stateProps; } = stateProps;
const { const { txData } = ownProps;
signPersonalMessage,
signTypedMessage, const {
cancelPersonalMessage, cancelAll: dispatchCancelAll,
cancelTypedMessage, cancelAllApprovals: dispatchCancelAllApprovals,
signMessage, } = dispatchProps;
cancelMessage,
txData,
} = ownProps;
const { cancelAll: dispatchCancelAll } = dispatchProps;
const { const {
type,
msgParams: { from }, msgParams: { from },
} = txData; } = txData;
const fromAccount = getAccountByAddress(allAccounts, from); const fromAccount = getAccountByAddress(allAccounts, from);
let cancel;
let sign;
if (type === MESSAGE_TYPE.PERSONAL_SIGN) {
cancel = cancelPersonalMessage;
sign = signPersonalMessage;
} else if (type === MESSAGE_TYPE.ETH_SIGN_TYPED_DATA) {
cancel = cancelTypedMessage;
sign = signTypedMessage;
} else if (type === MESSAGE_TYPE.ETH_SIGN) {
cancel = cancelMessage;
sign = signMessage;
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const signFn = async (...opts) => { const mmiOnSignCallback = async (_msgData) => {
if (accountType === 'custody') { if (accountType === 'custody') {
try { try {
let msgData = opts; let msgData = _msgData;
let id = opts.custodyId; let id = _msgData.custodyId;
if (!opts.custodyId) { if (!_msgData.custodyId) {
msgData = await sign(opts); msgData = checkForUnapprovedTypedMessages(
_msgData,
unapprovedTypedMessages,
);
id = msgData.custodyId; id = msgData.custodyId;
} }
dispatchProps.showCustodianDeepLink({ dispatchProps.showCustodianDeepLink({
@ -235,7 +230,6 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
await dispatchProps.setMsgInProgress(msgData.metamaskId); await dispatchProps.setMsgInProgress(msgData.metamaskId);
await dispatchProps.setWaitForConfirmDeepLinkDialog(true); await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
await goHome(); await goHome();
return msgData;
} catch (err) { } catch (err) {
await dispatchProps.setWaitForConfirmDeepLinkDialog(true); await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
await dispatchProps.showTransactionsFailedModal({ await dispatchProps.showTransactionsFailedModal({
@ -243,11 +237,8 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
closeNotification: true, closeNotification: true,
operationFailed: true, operationFailed: true,
}); });
return null;
} }
} }
return sign(opts);
}; };
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
@ -256,14 +247,6 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
...dispatchProps, ...dispatchProps,
fromAccount, fromAccount,
txData, txData,
cancel,
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
sign,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
// eslint-disable-next-line no-dupe-keys
sign: signFn,
///: END:ONLY_INCLUDE_IN
isLedgerWallet, isLedgerWallet,
hardwareWalletRequiresConnection, hardwareWalletRequiresConnection,
chainId, chainId,
@ -276,6 +259,11 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
unapprovedMessagesCount, unapprovedMessagesCount,
mostRecentOverviewPage, mostRecentOverviewPage,
cancelAll: () => dispatchCancelAll(valuesFor(unconfirmedMessagesList)), cancelAll: () => dispatchCancelAll(valuesFor(unconfirmedMessagesList)),
cancelAllApprovals: () =>
dispatchCancelAllApprovals(valuesFor(unconfirmedMessagesList)),
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
mmiOnSignCallback,
///: END:ONLY_INCLUDE_IN
}; };
} }

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import sinon from 'sinon'; import sinon from 'sinon';
import { fireEvent, screen } from '@testing-library/react'; import { fireEvent, screen, act } from '@testing-library/react';
import configureMockStore from 'redux-mock-store'; import configureMockStore from 'redux-mock-store';
import { renderWithProvider } from '../../../../test/lib/render-helpers'; import { renderWithProvider } from '../../../../test/lib/render-helpers';
import SignatureRequest from './signature-request.container'; import SignatureRequest from './signature-request.container';
@ -90,6 +90,7 @@ describe('Signature Request', () => {
clearConfirmTransaction: sinon.spy(), clearConfirmTransaction: sinon.spy(),
cancelMessage: sinon.spy(), cancelMessage: sinon.spy(),
cancel: sinon.stub().resolves(), cancel: sinon.stub().resolves(),
rejectPendingApproval: sinon.stub().resolves(),
showRejectTransactionsConfirmationModal: sinon.stub().resolves(), showRejectTransactionsConfirmationModal: sinon.stub().resolves(),
cancelAll: sinon.stub().resolves(), cancelAll: sinon.stub().resolves(),
providerConfig: { providerConfig: {
@ -97,6 +98,9 @@ describe('Signature Request', () => {
}, },
unapprovedMessagesCount: 2, unapprovedMessagesCount: 2,
sign: sinon.stub().resolves(), sign: sinon.stub().resolves(),
cancelAllApprovals: sinon.stub().resolves(),
resolvePendingApproval: sinon.stub().resolves(),
completedTx: sinon.stub().resolves(),
txData: { txData: {
msgParams: { msgParams: {
id: 1, id: 1,
@ -162,22 +166,24 @@ describe('Signature Request', () => {
propsWithFiat.clearConfirmTransaction.resetHistory(); propsWithFiat.clearConfirmTransaction.resetHistory();
}); });
it('cancel', () => { it('cancel', async () => {
const cancelButton = screen.getByTestId('page-container-footer-cancel'); const cancelButton = screen.getByTestId('page-container-footer-cancel');
fireEvent.click(cancelButton); await act(() => {
expect(propsWithFiat.cancel.calledOnce).toStrictEqual(true); fireEvent.click(cancelButton);
});
expect(propsWithFiat.rejectPendingApproval.calledOnce).toStrictEqual(
true,
);
}); });
it('sign', () => { it('sign', async () => {
const signButton = screen.getByTestId('page-container-footer-next'); const signButton = screen.getByTestId('page-container-footer-next');
fireEvent.click(signButton); await act(() => {
expect(propsWithFiat.sign.calledOnce).toStrictEqual(true); fireEvent.click(signButton);
}); });
expect(propsWithFiat.resolvePendingApproval.calledOnce).toStrictEqual(
it('cancelAll', () => { true,
const cancelAll = screen.getByTestId('signature-request-reject-all'); );
fireEvent.click(cancelAll);
expect(propsWithFiat.cancelAll.calledOnce).toStrictEqual(false);
}); });
it('have user warning', () => { it('have user warning', () => {

View File

@ -22,12 +22,6 @@ import { TransactionStatus } from '../../../shared/constants/transaction';
import { getSendTo } from '../../ducks/send'; import { getSendTo } from '../../ducks/send';
import { getProviderConfig } from '../../ducks/metamask/metamask'; import { getProviderConfig } from '../../ducks/metamask/metamask';
const SIGN_MESSAGE_TYPE = {
MESSAGE: 'message',
PERSONAL: 'personal',
TYPED: 'typed',
};
const signatureSelect = (txData) => { const signatureSelect = (txData) => {
const { const {
type, type,
@ -49,12 +43,6 @@ const signatureSelect = (txData) => {
return SignatureRequestOriginal; return SignatureRequestOriginal;
}; };
const stopPropagation = (event) => {
if (event?.stopPropagation) {
event.stopPropagation();
}
};
const ConfirmTxScreen = ({ match }) => { const ConfirmTxScreen = ({ match }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { navigateToMostRecentOverviewPage } = useRouting(); const { navigateToMostRecentOverviewPage } = useRouting();
@ -182,34 +170,6 @@ const ConfirmTxScreen = ({ match }) => {
return <Loading />; return <Loading />;
} }
const signMessage = (type) => (event) => {
stopPropagation(event);
const params = txData.msgParams;
params.metamaskId = txData.id;
let action;
if (type === SIGN_MESSAGE_TYPE.MESSAGE) {
action = actions.signMsg;
} else if (type === SIGN_MESSAGE_TYPE.PERSONAL) {
action = actions.signPersonalMsg;
} else {
action = actions.signTypedMsg;
}
return dispatch(action?.(params));
};
const cancelMessage = (type) => (event) => {
stopPropagation(event);
let action;
if (type === SIGN_MESSAGE_TYPE.MESSAGE) {
action = actions.cancelMsg;
} else if (type === SIGN_MESSAGE_TYPE.PERSONAL) {
action = actions.cancelPersonalMsg;
} else {
action = actions.cancelTypedMsg;
}
return dispatch(action(txData));
};
const SigComponent = signatureSelect(txData); const SigComponent = signatureSelect(txData);
return ( return (
@ -219,12 +179,6 @@ const ConfirmTxScreen = ({ match }) => {
identities={identities} identities={identities}
currentCurrency={currentCurrency} currentCurrency={currentCurrency}
blockGasLimit={blockGasLimit} blockGasLimit={blockGasLimit}
signMessage={signMessage(SIGN_MESSAGE_TYPE.MESSAGE)}
signPersonalMessage={signMessage(SIGN_MESSAGE_TYPE.PERSONAL)}
signTypedMessage={signMessage(SIGN_MESSAGE_TYPE.TYPED)}
cancelMessage={cancelMessage(SIGN_MESSAGE_TYPE.MESSAGE)}
cancelPersonalMessage={cancelMessage(SIGN_MESSAGE_TYPE.PERSONAL)}
cancelTypedMessage={cancelMessage(SIGN_MESSAGE_TYPE.TYPED)}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
selectedAccount={selectedAccount} selectedAccount={selectedAccount}
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN

View File

@ -650,176 +650,6 @@ describe('Actions', () => {
}); });
}); });
describe('#signMsg', () => {
const msgParams = {
metamaskId: 123,
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
};
afterEach(() => {
sinon.restore();
});
it('calls signMsg in background', async () => {
const store = mockStore();
const signMessage = background.signMessage.callsFake((_, cb) =>
cb(null, defaultState.metamask),
);
_setBackgroundConnection(background);
await store.dispatch(actions.signMsg(msgParams));
expect(signMessage.callCount).toStrictEqual(1);
});
it('errors when signMessage in background throws', async () => {
const store = mockStore();
background.signMessage.callsFake((_, cb) => cb(new Error('error')));
_setBackgroundConnection(background);
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', payload: undefined },
{ type: 'DISPLAY_WARNING', payload: 'error' },
{ type: 'HIDE_LOADING_INDICATION' },
];
await expect(store.dispatch(actions.signMsg(msgParams))).rejects.toThrow(
'error',
);
expect(store.getActions()).toStrictEqual(expectedActions);
});
});
describe('#signPersonalMsg', () => {
const msgParams = {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
};
afterEach(() => {
sinon.restore();
});
it('calls signPersonalMessage', async () => {
const store = mockStore();
const signPersonalMessage = background.signPersonalMessage.callsFake(
(_, cb) => cb(null, defaultState.metamask),
);
_setBackgroundConnection(background);
await store.dispatch(actions.signPersonalMsg(msgParams));
expect(signPersonalMessage.callCount).toStrictEqual(1);
});
it('throws if signPersonalMessage throws', async () => {
const store = mockStore();
background.signPersonalMessage.callsFake((_, cb) => {
cb(new Error('error'));
});
_setBackgroundConnection(background);
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', payload: undefined },
{ type: 'DISPLAY_WARNING', payload: 'error' },
{ type: 'HIDE_LOADING_INDICATION' },
];
await expect(
store.dispatch(actions.signPersonalMsg(msgParams)),
).rejects.toThrow('error');
expect(store.getActions()).toStrictEqual(expectedActions);
});
});
describe('#signTypedMsg', () => {
const msgParamsV3 = {
from: '0x0DCD5D886577d5081B0c52e242Ef29E70Be3E7bc',
data: JSON.stringify({
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
domain: {
name: 'Ether Mainl',
version: '1',
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
message: {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
},
}),
};
afterEach(() => {
sinon.restore();
});
it('calls signTypedMsg in background with no error', async () => {
const store = mockStore();
const signTypedMsg = background.signTypedMessage.callsFake((_, cb) =>
cb(null, defaultState.metamask),
);
_setBackgroundConnection(background);
await store.dispatch(actions.signTypedMsg(msgParamsV3));
expect(signTypedMsg.callCount).toStrictEqual(1);
});
it('returns expected actions with error', async () => {
const store = mockStore();
background.signTypedMessage.callsFake((_, cb) => cb(new Error('error')));
_setBackgroundConnection(background);
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', payload: undefined },
{ type: 'DISPLAY_WARNING', payload: 'error' },
{ type: 'HIDE_LOADING_INDICATION' },
];
await expect(store.dispatch(actions.signTypedMsg())).rejects.toThrow(
'error',
);
expect(store.getActions()).toStrictEqual(expectedActions);
});
});
describe('#updateTransaction', () => { describe('#updateTransaction', () => {
const txParams = { const txParams = {
from: '0x1', from: '0x1',
@ -2069,61 +1899,6 @@ describe('Actions', () => {
}); });
}); });
describe('#cancelMsgs', () => {
it('creates COMPLETED_TX with the cancelled messages IDs', async () => {
const store = mockStore();
const cancelTypedMessageStub = sinon.stub().callsFake((_, cb) => cb());
const cancelPersonalMessageStub = sinon.stub().callsFake((_, cb) => cb());
background.getApi.returns({
cancelTypedMessage: cancelTypedMessageStub,
cancelPersonalMessage: cancelPersonalMessageStub,
getState: sinon.stub().callsFake((cb) =>
cb(null, {
currentLocale: 'test',
selectedAddress: '0xFirstAddress',
providerConfig: {
chainId: '0x1',
},
accounts: {
'0xFirstAddress': {
balance: '0x0',
},
},
cachedBalances: {
'0x1': {
'0xFirstAddress': '0x0',
},
},
}),
),
});
const msgsList = [
{ id: 7648683973086304, status: 'unapproved', type: 'personal_sign' },
{
id: 7648683973086303,
status: 'unapproved',
type: 'eth_signTypedData',
},
];
_setBackgroundConnection(background.getApi());
await store.dispatch(actions.cancelMsgs(msgsList));
const resultantActions = store.getActions();
const expectedActions = resultantActions.filter(
(action) => action.type === 'COMPLETED_TX',
);
expect(expectedActions[0].value.id).toStrictEqual(msgsList[0].id);
expect(expectedActions[1].value.id).toStrictEqual(msgsList[1].id);
});
});
describe('Desktop', () => { describe('Desktop', () => {
describe('#setDesktopEnabled', () => { describe('#setDesktopEnabled', () => {
it('calls background setDesktopEnabled method', async () => { it('calls background setDesktopEnabled method', async () => {

View File

@ -4,6 +4,7 @@ import { captureException } from '@sentry/browser';
import { capitalize, isEqual } from 'lodash'; import { capitalize, isEqual } from 'lodash';
import { ThunkAction } from 'redux-thunk'; import { ThunkAction } from 'redux-thunk';
import { Action, AnyAction } from 'redux'; import { Action, AnyAction } from 'redux';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import { Hex, Json } from '@metamask/utils'; import { Hex, Json } from '@metamask/utils';
import { import {
AssetsContractController, AssetsContractController,
@ -15,14 +16,12 @@ import { PayloadAction } from '@reduxjs/toolkit';
import { GasFeeController } from '@metamask/gas-fee-controller'; import { GasFeeController } from '@metamask/gas-fee-controller';
import { PermissionsRequest } from '@metamask/permission-controller'; import { PermissionsRequest } from '@metamask/permission-controller';
import { NonEmptyArray } from '@metamask/controller-utils'; import { NonEmptyArray } from '@metamask/controller-utils';
import { ethErrors } from 'eth-rpc-errors';
import { getMethodDataAsync } from '../helpers/utils/transactions.util'; import { getMethodDataAsync } from '../helpers/utils/transactions.util';
import switchDirection from '../../shared/lib/switch-direction'; import switchDirection from '../../shared/lib/switch-direction';
import { import {
ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_NOTIFICATION,
ORIGIN_METAMASK, ORIGIN_METAMASK,
POLLING_TOKEN_ENVIRONMENT_TYPES, POLLING_TOKEN_ENVIRONMENT_TYPES,
MESSAGE_TYPE,
} from '../../shared/constants/app'; } from '../../shared/constants/app';
import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util'; import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util';
import { import {
@ -91,10 +90,7 @@ import { CustomGasSettings } from '../../app/scripts/controllers/transactions';
import { ThemeType } from '../../shared/constants/preferences'; import { ThemeType } from '../../shared/constants/preferences';
import * as actionConstants from './actionConstants'; import * as actionConstants from './actionConstants';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { import { updateCustodyState } from './institutional/institution-actions';
checkForUnapprovedTypedMessages,
updateCustodyState,
} from './institutional/institution-actions';
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
import { import {
generateActionId, generateActionId,
@ -641,76 +637,6 @@ export function setCurrentCurrency(
}; };
} }
export function signMsg(
msgData: TemporaryMessageDataType['msgParams'],
): ThunkAction<
Promise<TemporaryMessageDataType['msgParams']>,
MetaMaskReduxState,
unknown,
AnyAction
> {
log.debug('action - signMsg');
return async (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());
log.debug(`actions calling background.signMessage`);
let newState;
try {
newState = await submitRequestToBackground<
MetaMaskReduxState['metamask']
>('signMessage', [msgData]);
} catch (error) {
logErrorWithMessage(error);
dispatch(displayWarning(error));
throw error;
} finally {
dispatch(hideLoadingIndication());
}
dispatch(updateMetamaskState(newState));
dispatch(completedTx(msgData.metamaskId));
dispatch(closeCurrentNotificationWindow());
return msgData;
};
}
export function signPersonalMsg(
msgData: TemporaryMessageDataType['msgParams'],
): ThunkAction<
Promise<TemporaryMessageDataType['msgParams']>,
MetaMaskReduxState,
unknown,
AnyAction
> {
log.debug('action - signPersonalMsg');
return async (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());
log.debug(`actions calling background.signPersonalMessage`);
let newState;
try {
newState = await submitRequestToBackground<
MetaMaskReduxState['metamask']
>('signPersonalMessage', [msgData]);
} catch (error) {
logErrorWithMessage(error);
dispatch(displayWarning(error));
throw error;
} finally {
dispatch(hideLoadingIndication());
}
dispatch(updateMetamaskState(newState));
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
if (newState.unapprovedTypedMessages) {
return checkForUnapprovedTypedMessages(msgData, newState);
}
///: END:ONLY_INCLUDE_IN
dispatch(completedTx(msgData.metamaskId));
dispatch(closeCurrentNotificationWindow());
return msgData;
};
}
export function decryptMsgInline( export function decryptMsgInline(
decryptedMsgData: TemporaryMessageDataType['msgParams'], decryptedMsgData: TemporaryMessageDataType['msgParams'],
): ThunkAction< ): ThunkAction<
@ -805,44 +731,6 @@ export function encryptionPublicKeyMsg(
}; };
} }
export function signTypedMsg(
msgData: TemporaryMessageDataType['msgParams'],
): ThunkAction<
Promise<TemporaryMessageDataType['msgParams']>,
MetaMaskReduxState,
unknown,
AnyAction
> {
log.debug('action - signTypedMsg');
return async (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());
log.debug(`actions calling background.signTypedMessage`);
let newState: MetaMaskReduxState['metamask'];
try {
newState = await submitRequestToBackground<
MetaMaskReduxState['metamask']
>('signTypedMessage', [msgData]);
} catch (error) {
logErrorWithMessage(error);
dispatch(displayWarning(error));
throw error;
} finally {
dispatch(hideLoadingIndication());
}
dispatch(updateMetamaskState(newState));
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
if (newState.unapprovedTypedMessages) {
return checkForUnapprovedTypedMessages(msgData, newState);
}
///: END:ONLY_INCLUDE_IN
dispatch(completedTx(msgData.metamaskId));
dispatch(closeCurrentNotificationWindow());
return msgData;
};
}
export function updateCustomNonce(value: string) { export function updateCustomNonce(value: string) {
return { return {
type: actionConstants.UPDATE_CUSTOM_NONCE, type: actionConstants.UPDATE_CUSTOM_NONCE,
@ -1330,156 +1218,6 @@ export async function disableDesktop() {
} }
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
export function cancelMsg(
msgData: TemporaryMessageDataType,
): ThunkAction<
Promise<TemporaryMessageDataType>,
MetaMaskReduxState,
unknown,
AnyAction
> {
return async (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());
let newState;
try {
newState = await submitRequestToBackground<
MetaMaskReduxState['metamask']
>('cancelMessage', [msgData.id]);
} finally {
dispatch(hideLoadingIndication());
}
dispatch(updateMetamaskState(newState));
dispatch(completedTx(msgData.id));
dispatch(closeCurrentNotificationWindow());
return msgData;
};
}
/**
* Cancels all of the given messages
*
* @param msgDataList - a list of msg data objects
* @returns
*/
export function cancelMsgs(
msgDataList: TemporaryMessageDataType[],
): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> {
return async (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());
try {
const msgIds = msgDataList.map(({ id }) => id);
const cancellations = msgDataList.map(
({ id, type }) =>
new Promise<void>((resolve, reject) => {
switch (type) {
case MESSAGE_TYPE.ETH_SIGN_TYPED_DATA:
callBackgroundMethod('cancelTypedMessage', [id], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
return;
case MESSAGE_TYPE.PERSONAL_SIGN:
callBackgroundMethod('cancelPersonalMessage', [id], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
return;
case MESSAGE_TYPE.ETH_DECRYPT:
callBackgroundMethod('cancelDecryptMessage', [id], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
return;
case MESSAGE_TYPE.ETH_GET_ENCRYPTION_PUBLIC_KEY:
callBackgroundMethod(
'cancelEncryptionPublicKey',
[id],
(err) => {
if (err) {
reject(err);
return;
}
resolve();
},
);
return;
case MESSAGE_TYPE.ETH_SIGN:
callBackgroundMethod('cancelMessage', [id], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
return;
default:
reject(
new Error(
`MetaMask Message Signature: Unknown message type: ${id}`,
),
);
}
}),
);
await Promise.all(cancellations);
const newState = await updateMetamaskStateFromBackground();
dispatch(updateMetamaskState(newState));
msgIds.forEach((id) => {
dispatch(completedTx(id));
});
} catch (err) {
logErrorWithMessage(err);
} finally {
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
closeNotificationPopup();
} else {
dispatch(hideLoadingIndication());
}
}
};
}
export function cancelPersonalMsg(
msgData: TemporaryMessageDataType,
): ThunkAction<
Promise<TemporaryMessageDataType>,
MetaMaskReduxState,
unknown,
AnyAction
> {
return async (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());
let newState;
try {
newState = await submitRequestToBackground<
MetaMaskReduxState['metamask']
>('cancelPersonalMessage', [msgData.id]);
} finally {
dispatch(hideLoadingIndication());
}
dispatch(updateMetamaskState(newState));
dispatch(completedTx(msgData.id));
dispatch(closeCurrentNotificationWindow());
return msgData;
};
}
export function cancelDecryptMsg( export function cancelDecryptMsg(
msgData: TemporaryMessageDataType, msgData: TemporaryMessageDataType,
): ThunkAction< ): ThunkAction<
@ -1534,33 +1272,6 @@ export function cancelEncryptionPublicKeyMsg(
}; };
} }
export function cancelTypedMsg(
msgData: TemporaryMessageDataType,
): ThunkAction<
Promise<TemporaryMessageDataType>,
MetaMaskReduxState,
unknown,
AnyAction
> {
return async (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());
let newState;
try {
newState = await submitRequestToBackground<
MetaMaskReduxState['metamask']
>('cancelTypedMessage', [msgData.id]);
} finally {
dispatch(hideLoadingIndication());
}
dispatch(updateMetamaskState(newState));
dispatch(completedTx(msgData.id));
dispatch(closeCurrentNotificationWindow());
return msgData;
};
}
export function cancelTx( export function cancelTx(
txMeta: TransactionMeta, txMeta: TransactionMeta,
_showLoadingIndication = true, _showLoadingIndication = true,
@ -3724,6 +3435,34 @@ export function rejectPendingApproval(
}; };
} }
/**
* Rejects all approvals for the given messages
*
* @param messageList - The list of messages to reject
*/
export function rejectAllMessages(
messageList: [],
): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> {
return async (dispatch: MetaMaskReduxDispatch) => {
const userRejectionError = serializeError(
ethErrors.provider.userRejectedRequest(),
);
await Promise.all(
messageList.map(
async ({ id }) =>
await submitRequestToBackground('rejectPendingApproval', [
id,
userRejectionError,
]),
),
);
const { pendingApprovals } = await forceUpdateMetamaskState(dispatch);
if (Object.values(pendingApprovals).length === 0) {
dispatch(closeCurrentNotificationWindow());
}
};
}
export function setFirstTimeFlowType( export function setFirstTimeFlowType(
type: 'create' | 'import', type: 'create' | 'import',
): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> { ): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> {

View File

@ -166,11 +166,9 @@ describe('#checkForUnapprovedTypedMessages', () => {
status: 'unapproved', status: 'unapproved',
}; };
expect( expect(checkForUnapprovedTypedMessages(messageData, { msg: 'msg' })).toBe(
checkForUnapprovedTypedMessages(messageData, { messageData,
unapprovedTypedMessages: { msg: 'msg' }, );
}),
).toBe(messageData);
}); });
}); });

View File

@ -9,6 +9,7 @@ import {
CombinedBackgroundAndReduxState, CombinedBackgroundAndReduxState,
MetaMaskReduxState, MetaMaskReduxState,
TemporaryMessageDataType, TemporaryMessageDataType,
MessagesIndexedById,
} from '../store'; } from '../store';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils'; import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
@ -110,19 +111,16 @@ export function updateCustodyState(
export function checkForUnapprovedTypedMessages( export function checkForUnapprovedTypedMessages(
msgData: TemporaryMessageDataType['msgParams'], msgData: TemporaryMessageDataType['msgParams'],
newState: MetaMaskReduxState['metamask'], unapprovedTypedMessages: MessagesIndexedById,
) { ) {
const custodianUnapprovedMessages = Object.keys( const custodianUnapprovedMessages = Object.keys(unapprovedTypedMessages)
newState.unapprovedTypedMessages, .map((key) => unapprovedTypedMessages[key])
)
.map((key) => newState.unapprovedTypedMessages[key])
.filter((message) => message.custodyId && message.status === 'unapproved'); .filter((message) => message.custodyId && message.status === 'unapproved');
if (custodianUnapprovedMessages && custodianUnapprovedMessages.length > 0) { if (custodianUnapprovedMessages && custodianUnapprovedMessages.length > 0) {
return { return {
...msgData, ...msgData,
custodyId: custodyId: unapprovedTypedMessages[msgData.metamaskId]?.custodyId,
newState.unapprovedTypedMessages[msgData.metamaskId]?.custodyId,
}; };
} }

View File

@ -29,7 +29,7 @@ export interface TemporaryMessageDataType {
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
} }
interface MessagesIndexedById { export interface MessagesIndexedById {
[id: string]: TemporaryMessageDataType; [id: string]: TemporaryMessageDataType;
} }

View File

@ -4333,9 +4333,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@metamask/message-manager@npm:^6.0.0": "@metamask/message-manager@npm:^7.0.0":
version: 6.0.0 version: 7.0.0
resolution: "@metamask/message-manager@npm:6.0.0" resolution: "@metamask/message-manager@npm:7.0.0"
dependencies: dependencies:
"@metamask/base-controller": ^3.0.0 "@metamask/base-controller": ^3.0.0
"@metamask/controller-utils": ^4.0.0 "@metamask/controller-utils": ^4.0.0
@ -4345,7 +4345,7 @@ __metadata:
ethereumjs-util: ^7.0.10 ethereumjs-util: ^7.0.10
jsonschema: ^1.2.4 jsonschema: ^1.2.4
uuid: ^8.3.2 uuid: ^8.3.2
checksum: f1601145317739e06cb83aefb99cb12ecd717a164103e6094a753197dbf1b62e8eaa1e4aff8dd849d67d21358933cba7a6e1551eede0fcc32b7657496c4a2f2e checksum: 1a7b785159d93153ef966a82bcdf9beea1a641cd663d2c1719cf6b671364f18b98c84795d23fa9872ec5e2bd9f08162c2c4d7369f46a81d12fd496b6433818a0
languageName: node languageName: node
linkType: hard linkType: hard
@ -4683,39 +4683,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@metamask/signature-controller@npm:3.0.0": "@metamask/signature-controller@npm:^4.0.1":
version: 3.0.0 version: 4.0.1
resolution: "@metamask/signature-controller@npm:3.0.0" resolution: "@metamask/signature-controller@npm:4.0.1"
dependencies: dependencies:
"@metamask/approval-controller": ^3.0.0 "@metamask/approval-controller": ^3.3.0
"@metamask/base-controller": ^3.0.0 "@metamask/base-controller": ^3.0.0
"@metamask/controller-utils": ^4.0.0 "@metamask/controller-utils": ^4.0.0
"@metamask/message-manager": ^6.0.0 "@metamask/message-manager": ^7.0.0
"@metamask/utils": ^5.0.2 "@metamask/utils": ^5.0.2
eth-rpc-errors: ^4.0.2 eth-rpc-errors: ^4.0.2
ethereumjs-util: ^7.0.10 ethereumjs-util: ^7.0.10
immer: ^9.0.6 immer: ^9.0.6
lodash: ^4.17.21
peerDependencies: peerDependencies:
"@metamask/approval-controller": ^3.0.0 "@metamask/approval-controller": ^3.3.0
checksum: 9afc2b8bc09d7cc6bab9d3e0bed1e70fd852cb78b0b07321afadac395cc0d7775697f99e1a846edd68afe8279929e3f763f09aa9b91eac682f0caed3e1c4fd5f checksum: 8d510fe3761f2c0ac39bb6b9891497012e3608559480aee11f8f76556573521b01ecde334f1ffd71e8fc674243ae7fa111d0f2aaa36f4466d2e9466998af6618
languageName: node
linkType: hard
"@metamask/signature-controller@patch:@metamask/signature-controller@npm%3A3.0.0#./.yarn/patches/@metamask-signature-controller-npm-3.0.0-8771b6885e.patch::locator=metamask-crx%40workspace%3A.":
version: 3.0.0
resolution: "@metamask/signature-controller@patch:@metamask/signature-controller@npm%3A3.0.0#./.yarn/patches/@metamask-signature-controller-npm-3.0.0-8771b6885e.patch::version=3.0.0&hash=7fee95&locator=metamask-crx%40workspace%3A."
dependencies:
"@metamask/approval-controller": ^3.0.0
"@metamask/base-controller": ^3.0.0
"@metamask/controller-utils": ^4.0.0
"@metamask/message-manager": ^6.0.0
"@metamask/utils": ^5.0.2
eth-rpc-errors: ^4.0.2
ethereumjs-util: ^7.0.10
immer: ^9.0.6
peerDependencies:
"@metamask/approval-controller": ^3.0.0
checksum: 379cceaceaa4e120bbf9547b74d709f2a93117a17ec460e22e8935a54a1d10528f435563f6d9dcb1c4b9f521286033b23d5ec14063ea1c6f753953f5e0984a70
languageName: node languageName: node
linkType: hard linkType: hard
@ -24363,7 +24346,7 @@ __metadata:
"@metamask/jazzicon": ^2.0.0 "@metamask/jazzicon": ^2.0.0
"@metamask/key-tree": ^7.0.0 "@metamask/key-tree": ^7.0.0
"@metamask/logo": ^3.1.1 "@metamask/logo": ^3.1.1
"@metamask/message-manager": ^6.0.0 "@metamask/message-manager": ^7.0.0
"@metamask/metamask-eth-abis": ^3.0.0 "@metamask/metamask-eth-abis": ^3.0.0
"@metamask/notification-controller": ^3.0.0 "@metamask/notification-controller": ^3.0.0
"@metamask/obs-store": ^8.1.0 "@metamask/obs-store": ^8.1.0
@ -24377,7 +24360,7 @@ __metadata:
"@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.34.0-flask.1" "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.34.0-flask.1"
"@metamask/safe-event-emitter": ^2.0.0 "@metamask/safe-event-emitter": ^2.0.0
"@metamask/scure-bip39": ^2.0.3 "@metamask/scure-bip39": ^2.0.3
"@metamask/signature-controller": ^3.0.0 "@metamask/signature-controller": ^4.0.1
"@metamask/slip44": ^3.0.0 "@metamask/slip44": ^3.0.0
"@metamask/smart-transactions-controller": ^3.1.0 "@metamask/smart-transactions-controller": ^3.1.0
"@metamask/snaps-controllers": ^0.32.2 "@metamask/snaps-controllers": ^0.32.2