1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +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<
Omit<AbstractMessage, 'securityProviderResponse' | 'metadata'>
Omit<AbstractMessage, 'securityProviderResponse' | 'metadata' | 'error'>
>;
export type DecryptMessageControllerState = {

View File

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

View File

@ -1258,11 +1258,7 @@ export default class MetamaskController extends EventEmitter {
this.signatureController = new SignatureController({
messenger: this.controllerMessenger.getRestricted({
name: 'SignatureController',
allowedActions: [
`${this.approvalController.name}:addRequest`,
`${this.approvalController.name}:acceptRequest`,
`${this.approvalController.name}:rejectRequest`,
],
allowedActions: [`${this.approvalController.name}:addRequest`],
}),
keyringController: this.keyringController,
isEthSignEnabled: () =>
@ -2248,27 +2244,6 @@ export default class MetamaskController extends EventEmitter {
updatePreviousGasParams:
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
decryptMessage: this.decryptMessageController.decryptMessage.bind(
this.decryptMessageController,

View File

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

View File

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

View File

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

View File

@ -1718,7 +1718,8 @@
"browserify>buffer": true,
"browserify>events": true,
"eth-rpc-errors": true,
"ethereumjs-util": true
"ethereumjs-util": true,
"lodash": true
}
},
"@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",
"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.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"
"request@^2.85.0": "patch:request@npm%3A2.88.2#./.yarn/patches/request-npm-2.88.2-f4a57c72c4.patch"
},
"dependencies": {
"@actions/core": "^1.10.0",
@ -248,7 +247,7 @@
"@metamask/jazzicon": "^2.0.0",
"@metamask/key-tree": "^7.0.0",
"@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/notification-controller": "^3.0.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/safe-event-emitter": "^2.0.0",
"@metamask/scure-bip39": "^2.0.3",
"@metamask/signature-controller": "^3.0.0",
"@metamask/signature-controller": "^4.0.1",
"@metamask/slip44": "^3.0.0",
"@metamask/smart-transactions-controller": "^3.1.0",
"@metamask/snaps-controllers": "^0.32.2",

View File

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

View File

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

View File

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

View File

@ -1,9 +1,14 @@
import { connect } from 'react-redux';
import { compose } from 'redux';
import { withRouter } from 'react-router-dom';
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import { goHome, cancelMsgs, showModal } from '../../../store/actions';
import {
goHome,
showModal,
resolvePendingApproval,
rejectPendingApproval,
rejectAllMessages,
completedTx,
} from '../../../store/actions';
import {
accountsWithSendEtherInfoSelector,
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) {
const {
signPersonalMessage,
signTypedMessage,
cancelPersonalMessage,
cancelTypedMessage,
signMessage,
cancelMessage,
txData,
} = ownProps;
const { txData } = ownProps;
const { allAccounts, messagesList, ...otherStateProps } = stateProps;
const {
type,
msgParams: { from },
} = txData;
const fromAccount = getAccountByAddress(allAccounts, from);
const { cancelAll: dispatchCancelAll } = 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;
}
const { cancelAllApprovals: dispatchCancelAllApprovals } = dispatchProps;
return {
...ownProps,
@ -110,9 +101,8 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
...dispatchProps,
fromAccount,
txData,
cancel,
sign,
cancelAll: () => dispatchCancelAll(valuesFor(messagesList)),
cancelAllApprovals: () =>
dispatchCancelAllApprovals(valuesFor(messagesList)),
};
}

View File

@ -1,13 +1,25 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import { fireEvent, screen } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import configureStore from '../../../store/store';
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 '.';
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({
domain: {
name: 'happydapp.website',
@ -92,12 +104,30 @@ describe('SignatureRequestOriginal', () => {
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();
const signButton = screen.getByTestId('page-container-footer-next');
fireEvent.click(signButton);
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', () => {

View File

@ -4,6 +4,7 @@ import { useSelector, useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import log from 'loglevel';
import { isValidSIWEOrigin } from '@metamask/controller-utils';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import { BannerAlert, Text } from '../../component-library';
import Popover from '../../ui/popover';
import Checkbox from '../../ui/check-box';
@ -25,27 +26,28 @@ import {
SEVERITIES,
TextVariant,
} 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 { 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 { getMostRecentOverviewPage } from '../../../ducks/history/history';
import { showModal, cancelMsgs } from '../../../store/actions';
import LedgerInstructionField from '../ledger-instruction-field';
import SignatureRequestHeader from '../signature-request-header';
import Header from './signature-request-siwe-header';
import Message from './signature-request-siwe-message';
export default function SignatureRequestSIWE({
txData,
cancelPersonalMessage,
signPersonalMessage,
}) {
export default function SignatureRequestSIWE({ txData }) {
const dispatch = useDispatch();
const history = useHistory();
const t = useContext(I18nContext);
const allAccounts = useSelector(accountsWithSendEtherInfoSelector);
const subjectMetadata = useSelector(getSubjectMetadata);
@ -59,6 +61,7 @@ export default function SignatureRequestSIWE({
origin,
siwe: { parsedMessage },
},
id,
} = txData;
const isLedgerWallet = useSelector((state) => isAddressLedger(state, from));
@ -82,27 +85,27 @@ export default function SignatureRequestSIWE({
(txData?.securityProviderResponse &&
Object.keys(txData.securityProviderResponse).length === 0);
const onSign = useCallback(
async (event) => {
try {
await signPersonalMessage(event);
} catch (e) {
log.error(e);
}
},
[signPersonalMessage],
);
const onSign = useCallback(async () => {
try {
await dispatch(resolvePendingApproval(id, null));
dispatch(completedTx(id));
} catch (e) {
log.error(e);
}
}, [id, dispatch]);
const onCancel = useCallback(
async (event) => {
try {
await cancelPersonalMessage(event);
} catch (e) {
log.error(e);
}
},
[cancelPersonalMessage],
);
const onCancel = useCallback(async () => {
try {
await dispatch(
rejectPendingApproval(
id,
serializeError(ethErrors.provider.userRejectedRequest()),
),
);
} catch (e) {
log.error(e);
}
}, []);
const handleCancelAll = () => {
const unapprovedTxCount = messagesCount;
@ -112,7 +115,7 @@ export default function SignatureRequestSIWE({
name: 'REJECT_TRANSACTIONS',
unapprovedTxCount,
onSubmit: async () => {
await dispatch(cancelMsgs(valuesFor(messagesList)));
await dispatch(rejectAllMessages(valuesFor(messagesList)));
dispatch(clearConfirmTransaction());
history.push(mostRecentOverviewPage);
},
@ -242,12 +245,4 @@ SignatureRequestSIWE.propTypes = {
* The display content of transaction data
*/
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 { memoize } from 'lodash';
import PropTypes from 'prop-types';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import LedgerInstructionField from '../ledger-instruction-field';
import {
sanitizeMessage,
@ -62,14 +63,7 @@ export default class SignatureRequest extends PureComponent {
* Check if the wallet is ledget wallet or not
*/
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.
*/
@ -92,10 +86,14 @@ export default class SignatureRequest extends PureComponent {
history: PropTypes.object,
mostRecentOverviewPage: PropTypes.string,
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)
showCustodianDeepLink: PropTypes.func,
isNotification: PropTypes.bool,
mmiOnSignCallback: PropTypes.func,
// Used to show a warning if the signing account is not the selected account
// Largely relevant for contract wallet custodians
selectedAccount: PropTypes.object,
@ -150,18 +148,18 @@ export default class SignatureRequest extends PureComponent {
handleCancelAll = () => {
const {
cancelAll,
clearConfirmTransaction,
history,
mostRecentOverviewPage,
showRejectTransactionsConfirmationModal,
unapprovedMessagesCount,
cancelAllApprovals,
} = this.props;
showRejectTransactionsConfirmationModal({
unapprovedTxCount: unapprovedMessagesCount,
onSubmit: async () => {
await cancelAll();
await cancelAllApprovals();
clearConfirmTransaction();
history.push(mostRecentOverviewPage);
},
@ -174,10 +172,9 @@ export default class SignatureRequest extends PureComponent {
txData: {
msgParams: { data, origin, version },
type,
id,
},
fromAccount: { address, balance, name },
cancel,
sign,
isLedgerWallet,
hardwareWalletRequiresConnection,
chainId,
@ -188,6 +185,9 @@ export default class SignatureRequest extends PureComponent {
currentCurrency,
conversionRate,
unapprovedMessagesCount,
resolvePendingApproval,
rejectPendingApproval,
completedTx,
} = this.props;
const { t, trackEvent } = this.context;
@ -221,8 +221,15 @@ export default class SignatureRequest extends PureComponent {
.toBase(10)
.toString();
const onSign = (event) => {
sign(event);
const onSign = async () => {
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({
category: MetaMetricsEventCategory.Transactions,
event: 'Confirm',
@ -235,8 +242,11 @@ export default class SignatureRequest extends PureComponent {
});
};
const onCancel = (event) => {
cancel(event);
const onCancel = async () => {
await rejectPendingApproval(
id,
serializeError(ethErrors.provider.userRejectedRequest()),
);
trackEvent({
category: MetaMetricsEventCategory.Transactions,
event: 'Cancel',

View File

@ -13,6 +13,7 @@ import {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getAccountType,
getSelectedAccount,
unapprovedTypedMessagesSelector,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
import {
@ -29,16 +30,20 @@ import {
setTypedMessageInProgress,
} from '../../../store/institutional/institution-background';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { checkForUnapprovedTypedMessages } from '../../../store/institutional/institution-actions';
///: END:ONLY_INCLUDE_IN
import {
MESSAGE_TYPE,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
ENVIRONMENT_TYPE_NOTIFICATION,
///: END:ONLY_INCLUDE_IN
} from '../../../../shared/constants/app';
import {
cancelMsgs,
showModal,
resolvePendingApproval,
rejectPendingApproval,
rejectAllMessages,
completedTx,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
goHome,
///: END:ONLY_INCLUDE_IN
@ -89,6 +94,7 @@ function mapStateToProps(state, ownProps) {
accountType: getAccountType(state),
isNotification: envType === ENVIRONMENT_TYPE_NOTIFICATION,
selectedAccount: getSelectedAccount(state),
unapprovedTypedMessages: unapprovedTypedMessagesSelector(state),
///: END:ONLY_INCLUDE_IN
};
}
@ -143,6 +149,10 @@ mapDispatchToProps = mmiMapDispatchToProps;
mapDispatchToProps = function (dispatch) {
return {
resolvePendingApproval: (id) => dispatch(resolvePendingApproval(id)),
completedTx: (id) => dispatch(completedTx(id)),
rejectPendingApproval: (id, error) =>
dispatch(rejectPendingApproval(id, error)),
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
showRejectTransactionsConfirmationModal: ({
onSubmit,
@ -157,8 +167,9 @@ mapDispatchToProps = function (dispatch) {
}),
);
},
cancelAll: (unconfirmedMessagesList) =>
dispatch(cancelMsgs(unconfirmedMessagesList)),
cancelAllApprovals: (unconfirmedMessagesList) => {
dispatch(rejectAllMessages(unconfirmedMessagesList));
},
};
};
@ -180,49 +191,33 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
accountType,
isNotification,
unapprovedTypedMessages,
///: END:ONLY_INCLUDE_IN
} = stateProps;
const {
signPersonalMessage,
signTypedMessage,
cancelPersonalMessage,
cancelTypedMessage,
signMessage,
cancelMessage,
txData,
} = ownProps;
const { cancelAll: dispatchCancelAll } = dispatchProps;
const { txData } = ownProps;
const {
cancelAll: dispatchCancelAll,
cancelAllApprovals: dispatchCancelAllApprovals,
} = dispatchProps;
const {
type,
msgParams: { from },
} = txData;
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)
const signFn = async (...opts) => {
const mmiOnSignCallback = async (_msgData) => {
if (accountType === 'custody') {
try {
let msgData = opts;
let id = opts.custodyId;
if (!opts.custodyId) {
msgData = await sign(opts);
let msgData = _msgData;
let id = _msgData.custodyId;
if (!_msgData.custodyId) {
msgData = checkForUnapprovedTypedMessages(
_msgData,
unapprovedTypedMessages,
);
id = msgData.custodyId;
}
dispatchProps.showCustodianDeepLink({
@ -235,7 +230,6 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
await dispatchProps.setMsgInProgress(msgData.metamaskId);
await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
await goHome();
return msgData;
} catch (err) {
await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
await dispatchProps.showTransactionsFailedModal({
@ -243,11 +237,8 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
closeNotification: true,
operationFailed: true,
});
return null;
}
}
return sign(opts);
};
///: END:ONLY_INCLUDE_IN
@ -256,14 +247,6 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
...dispatchProps,
fromAccount,
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,
hardwareWalletRequiresConnection,
chainId,
@ -276,6 +259,11 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
unapprovedMessagesCount,
mostRecentOverviewPage,
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 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 { renderWithProvider } from '../../../../test/lib/render-helpers';
import SignatureRequest from './signature-request.container';
@ -90,6 +90,7 @@ describe('Signature Request', () => {
clearConfirmTransaction: sinon.spy(),
cancelMessage: sinon.spy(),
cancel: sinon.stub().resolves(),
rejectPendingApproval: sinon.stub().resolves(),
showRejectTransactionsConfirmationModal: sinon.stub().resolves(),
cancelAll: sinon.stub().resolves(),
providerConfig: {
@ -97,6 +98,9 @@ describe('Signature Request', () => {
},
unapprovedMessagesCount: 2,
sign: sinon.stub().resolves(),
cancelAllApprovals: sinon.stub().resolves(),
resolvePendingApproval: sinon.stub().resolves(),
completedTx: sinon.stub().resolves(),
txData: {
msgParams: {
id: 1,
@ -162,22 +166,24 @@ describe('Signature Request', () => {
propsWithFiat.clearConfirmTransaction.resetHistory();
});
it('cancel', () => {
it('cancel', async () => {
const cancelButton = screen.getByTestId('page-container-footer-cancel');
fireEvent.click(cancelButton);
expect(propsWithFiat.cancel.calledOnce).toStrictEqual(true);
await act(() => {
fireEvent.click(cancelButton);
});
expect(propsWithFiat.rejectPendingApproval.calledOnce).toStrictEqual(
true,
);
});
it('sign', () => {
it('sign', async () => {
const signButton = screen.getByTestId('page-container-footer-next');
fireEvent.click(signButton);
expect(propsWithFiat.sign.calledOnce).toStrictEqual(true);
});
it('cancelAll', () => {
const cancelAll = screen.getByTestId('signature-request-reject-all');
fireEvent.click(cancelAll);
expect(propsWithFiat.cancelAll.calledOnce).toStrictEqual(false);
await act(() => {
fireEvent.click(signButton);
});
expect(propsWithFiat.resolvePendingApproval.calledOnce).toStrictEqual(
true,
);
});
it('have user warning', () => {

View File

@ -22,12 +22,6 @@ import { TransactionStatus } from '../../../shared/constants/transaction';
import { getSendTo } from '../../ducks/send';
import { getProviderConfig } from '../../ducks/metamask/metamask';
const SIGN_MESSAGE_TYPE = {
MESSAGE: 'message',
PERSONAL: 'personal',
TYPED: 'typed',
};
const signatureSelect = (txData) => {
const {
type,
@ -49,12 +43,6 @@ const signatureSelect = (txData) => {
return SignatureRequestOriginal;
};
const stopPropagation = (event) => {
if (event?.stopPropagation) {
event.stopPropagation();
}
};
const ConfirmTxScreen = ({ match }) => {
const dispatch = useDispatch();
const { navigateToMostRecentOverviewPage } = useRouting();
@ -182,34 +170,6 @@ const ConfirmTxScreen = ({ match }) => {
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);
return (
@ -219,12 +179,6 @@ const ConfirmTxScreen = ({ match }) => {
identities={identities}
currentCurrency={currentCurrency}
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)
selectedAccount={selectedAccount}
///: 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', () => {
const txParams = {
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('#setDesktopEnabled', () => {
it('calls background setDesktopEnabled method', async () => {

View File

@ -4,6 +4,7 @@ import { captureException } from '@sentry/browser';
import { capitalize, isEqual } from 'lodash';
import { ThunkAction } from 'redux-thunk';
import { Action, AnyAction } from 'redux';
import { ethErrors, serializeError } from 'eth-rpc-errors';
import { Hex, Json } from '@metamask/utils';
import {
AssetsContractController,
@ -15,14 +16,12 @@ import { PayloadAction } from '@reduxjs/toolkit';
import { GasFeeController } from '@metamask/gas-fee-controller';
import { PermissionsRequest } from '@metamask/permission-controller';
import { NonEmptyArray } from '@metamask/controller-utils';
import { ethErrors } from 'eth-rpc-errors';
import { getMethodDataAsync } from '../helpers/utils/transactions.util';
import switchDirection from '../../shared/lib/switch-direction';
import {
ENVIRONMENT_TYPE_NOTIFICATION,
ORIGIN_METAMASK,
POLLING_TOKEN_ENVIRONMENT_TYPES,
MESSAGE_TYPE,
} from '../../shared/constants/app';
import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util';
import {
@ -91,10 +90,7 @@ import { CustomGasSettings } from '../../app/scripts/controllers/transactions';
import { ThemeType } from '../../shared/constants/preferences';
import * as actionConstants from './actionConstants';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import {
checkForUnapprovedTypedMessages,
updateCustodyState,
} from './institutional/institution-actions';
import { updateCustodyState } from './institutional/institution-actions';
///: END:ONLY_INCLUDE_IN
import {
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(
decryptedMsgData: TemporaryMessageDataType['msgParams'],
): 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) {
return {
type: actionConstants.UPDATE_CUSTOM_NONCE,
@ -1330,156 +1218,6 @@ export async function disableDesktop() {
}
///: 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(
msgData: TemporaryMessageDataType,
): 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(
txMeta: TransactionMeta,
_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(
type: 'create' | 'import',
): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> {

View File

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

View File

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

View File

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

View File

@ -4333,9 +4333,9 @@ __metadata:
languageName: node
linkType: hard
"@metamask/message-manager@npm:^6.0.0":
version: 6.0.0
resolution: "@metamask/message-manager@npm:6.0.0"
"@metamask/message-manager@npm:^7.0.0":
version: 7.0.0
resolution: "@metamask/message-manager@npm:7.0.0"
dependencies:
"@metamask/base-controller": ^3.0.0
"@metamask/controller-utils": ^4.0.0
@ -4345,7 +4345,7 @@ __metadata:
ethereumjs-util: ^7.0.10
jsonschema: ^1.2.4
uuid: ^8.3.2
checksum: f1601145317739e06cb83aefb99cb12ecd717a164103e6094a753197dbf1b62e8eaa1e4aff8dd849d67d21358933cba7a6e1551eede0fcc32b7657496c4a2f2e
checksum: 1a7b785159d93153ef966a82bcdf9beea1a641cd663d2c1719cf6b671364f18b98c84795d23fa9872ec5e2bd9f08162c2c4d7369f46a81d12fd496b6433818a0
languageName: node
linkType: hard
@ -4683,39 +4683,22 @@ __metadata:
languageName: node
linkType: hard
"@metamask/signature-controller@npm:3.0.0":
version: 3.0.0
resolution: "@metamask/signature-controller@npm:3.0.0"
"@metamask/signature-controller@npm:^4.0.1":
version: 4.0.1
resolution: "@metamask/signature-controller@npm:4.0.1"
dependencies:
"@metamask/approval-controller": ^3.0.0
"@metamask/approval-controller": ^3.3.0
"@metamask/base-controller": ^3.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
eth-rpc-errors: ^4.0.2
ethereumjs-util: ^7.0.10
immer: ^9.0.6
lodash: ^4.17.21
peerDependencies:
"@metamask/approval-controller": ^3.0.0
checksum: 9afc2b8bc09d7cc6bab9d3e0bed1e70fd852cb78b0b07321afadac395cc0d7775697f99e1a846edd68afe8279929e3f763f09aa9b91eac682f0caed3e1c4fd5f
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
"@metamask/approval-controller": ^3.3.0
checksum: 8d510fe3761f2c0ac39bb6b9891497012e3608559480aee11f8f76556573521b01ecde334f1ffd71e8fc674243ae7fa111d0f2aaa36f4466d2e9466998af6618
languageName: node
linkType: hard
@ -24363,7 +24346,7 @@ __metadata:
"@metamask/jazzicon": ^2.0.0
"@metamask/key-tree": ^7.0.0
"@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/notification-controller": ^3.0.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/safe-event-emitter": ^2.0.0
"@metamask/scure-bip39": ^2.0.3
"@metamask/signature-controller": ^3.0.0
"@metamask/signature-controller": ^4.0.1
"@metamask/slip44": ^3.0.0
"@metamask/smart-transactions-controller": ^3.1.0
"@metamask/snaps-controllers": ^0.32.2