diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index df8a1d895..9330d0a95 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2142,6 +2142,9 @@ "metrics": { "message": "Metrics" }, + "mismatchAccount": { + "message": "Your selected account ($1) is different than the account trying to sign ($2)" + }, "mismatchedChainLinkText": { "message": "verify the network details", "description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key." diff --git a/ui/components/app/signature-request-original/signature-request-original.component.js b/ui/components/app/signature-request-original/signature-request-original.component.js index 9d46e92d1..d6b432035 100644 --- a/ui/components/app/signature-request-original/signature-request-original.component.js +++ b/ui/components/app/signature-request-original/signature-request-original.component.js @@ -5,7 +5,13 @@ import { ObjectInspector } from 'react-inspector'; import LedgerInstructionField from '../ledger-instruction-field'; import { MESSAGE_TYPE } from '../../../../shared/constants/app'; -import { getURLHostName, sanitizeString } from '../../../helpers/utils/util'; +import { + getURLHostName, + sanitizeString, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + shortenAddress, + ///: END:ONLY_INCLUDE_IN +} from '../../../helpers/utils/util'; import { stripHexPrefix } from '../../../../shared/modules/hexstring-utils'; import Button from '../../ui/button'; import SiteOrigin from '../../ui/site-origin'; @@ -17,6 +23,13 @@ import { FONT_WEIGHT, TEXT_ALIGN, TextColor, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + IconColor, + DISPLAY, + BLOCK_SIZES, + TextVariant, + BackgroundColor, + ///: END:ONLY_INCLUDE_IN } from '../../../helpers/constants/design-system'; import { NETWORK_TYPES } from '../../../../shared/constants/network'; import { Numeric } from '../../../../shared/modules/Numeric'; @@ -26,6 +39,11 @@ import SecurityProviderBannerMessage from '../security-provider-banner-message/s import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants'; import { formatCurrency } from '../../../helpers/utils/confirm-tx.util'; import { getValueFromWeiHex } from '../../../../shared/modules/conversion.utils'; + +///: BEGIN:ONLY_INCLUDE_IN(mmi) +import { Icon, IconName, Text } from '../../component-library'; +import Box from '../../ui/box/box'; +///: END:ONLY_INCLUDE_IN import SignatureRequestOriginalWarning from './signature-request-original-warning'; export default class SignatureRequestOriginal extends Component { @@ -55,6 +73,9 @@ export default class SignatureRequestOriginal extends Component { showRejectTransactionsConfirmationModal: PropTypes.func.isRequired, cancelAll: PropTypes.func.isRequired, provider: PropTypes.object, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + selectedAccount: PropTypes.object, + ///: END:ONLY_INCLUDE_IN }; state = { @@ -92,6 +113,16 @@ export default class SignatureRequestOriginal extends Component { } }; + renderAccountInfo = () => { + return ( +
+ {this.renderAccount()} + {this.renderRequestIcon()} + {this.renderBalance()} +
+ ); + }; + renderTypedData = (data) => { const { t } = this.context; const { domain, message } = JSON.parse(data); @@ -152,6 +183,39 @@ export default class SignatureRequestOriginal extends Component { securityProviderResponse={txData.securityProviderResponse} /> ) : null} + + { + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + this.props.selectedAccount.address === + this.props.fromAccount.address ? null : ( + + + + {this.context.t('mismatchAccount', [ + shortenAddress(this.props.selectedAccount.address), + shortenAddress(this.props.fromAccount.address), + ])} + + + ) + ///: END:ONLY_INCLUDE_IN + } +
{ diff --git a/ui/components/app/signature-request/signature-request.component.js b/ui/components/app/signature-request/signature-request.component.js index 92fb56a06..68390deb3 100644 --- a/ui/components/app/signature-request/signature-request.component.js +++ b/ui/components/app/signature-request/signature-request.component.js @@ -2,7 +2,13 @@ import React, { PureComponent } from 'react'; import { memoize } from 'lodash'; import PropTypes from 'prop-types'; import LedgerInstructionField from '../ledger-instruction-field'; -import { sanitizeMessage, getURLHostName } from '../../../helpers/utils/util'; +import { + sanitizeMessage, + getURLHostName, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + shortenAddress, + ///: END:ONLY_INCLUDE_IN +} from '../../../helpers/utils/util'; import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics'; import SiteOrigin from '../../ui/site-origin'; import Button from '../../ui/button'; @@ -13,6 +19,13 @@ import { FONT_WEIGHT, TEXT_ALIGN, TextColor, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + IconColor, + DISPLAY, + BLOCK_SIZES, + TextVariant, + BackgroundColor, + ///: END:ONLY_INCLUDE_IN } from '../../../helpers/constants/design-system'; import NetworkAccountBalanceHeader from '../network-account-balance-header'; import { NETWORK_TYPES } from '../../../../shared/constants/network'; @@ -23,6 +36,11 @@ import SecurityProviderBannerMessage from '../security-provider-banner-message/s import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants'; import { formatCurrency } from '../../../helpers/utils/confirm-tx.util'; import { getValueFromWeiHex } from '../../../../shared/modules/conversion.utils'; +///: BEGIN:ONLY_INCLUDE_IN(mmi) +import { Icon, IconName, Text } from '../../component-library'; +import Box from '../../ui/box/box'; +///: END:ONLY_INCLUDE_IN + import Footer from './signature-request-footer'; import Message from './signature-request-message'; @@ -75,6 +93,11 @@ export default class SignatureRequest extends PureComponent { mostRecentOverviewPage: PropTypes.string, showRejectTransactionsConfirmationModal: PropTypes.func.isRequired, cancelAll: PropTypes.func.isRequired, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + // Used to show a warning if the signing account is not the selected account + // Largely relevant for contract wallet custodians + selectedAccount: PropTypes.object, + ///: END:ONLY_INCLUDE_IN }; static contextTypes = { @@ -255,6 +278,38 @@ export default class SignatureRequest extends PureComponent { securityProviderResponse={txData.securityProviderResponse} /> ) : null} + + { + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + this.props.selectedAccount.address === address ? null : ( + + + + {this.context.t('mismatchAccount', [ + shortenAddress(this.props.selectedAccount.address), + shortenAddress(address), + ])} + + + ) + ///: END:ONLY_INCLUDE_IN + } +
{ @@ -324,5 +327,38 @@ describe('Signature Request Component', () => { ).toBeNull(); expect(queryByText('This is based on information from')).toBeNull(); }); + + it('should render a warning when the selected account is not the one being used to sign', () => { + const msgParams = { + data: JSON.stringify(messageData), + version: 'V4', + origin: 'test', + }; + + const { container } = renderWithProvider( + , + store, + ); + + expect( + container.querySelector('.request-signature__mismatch-info'), + ).toBeInTheDocument(); + }); }); }); diff --git a/ui/components/app/signature-request/signature-request.container.js b/ui/components/app/signature-request/signature-request.container.js index 7ce1c76da..5aae9f3ca 100644 --- a/ui/components/app/signature-request/signature-request.container.js +++ b/ui/components/app/signature-request/signature-request.container.js @@ -10,6 +10,9 @@ import { getCurrentCurrency, getPreferences, conversionRateSelector, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + getSelectedAccount, + ///: END:ONLY_INCLUDE_IN } from '../../../selectors'; import { isAddressLedger, @@ -55,6 +58,9 @@ function mapStateToProps(state, ownProps) { subjectMetadata: getSubjectMetadata(state), // not forwarded to component allAccounts: accountsWithSendEtherInfoSelector(state), + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + selectedAccount: getSelectedAccount(state), + ///: END:ONLY_INCLUDE_IN }; } diff --git a/ui/components/app/signature-request/signature-request.container.test.js b/ui/components/app/signature-request/signature-request.container.test.js index 13579552c..54a60c889 100644 --- a/ui/components/app/signature-request/signature-request.container.test.js +++ b/ui/components/app/signature-request/signature-request.container.test.js @@ -111,6 +111,9 @@ describe('Signature Request', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: null, + selectedAccount: { + address: '0x123456789abcdef', + }, }; const propsWithFiat = { diff --git a/ui/components/app/signature-request/signature-request.stories.js b/ui/components/app/signature-request/signature-request.stories.js index 5050ac9ab..cfd118c5b 100644 --- a/ui/components/app/signature-request/signature-request.stories.js +++ b/ui/components/app/signature-request/signature-request.stories.js @@ -3,7 +3,9 @@ import testData from '../../../../.storybook/test-data'; import README from './README.mdx'; import SignatureRequest from './signature-request.component'; -const [MOCK_PRIMARY_IDENTITY] = Object.values(testData.metamask.identities); +const [MOCK_PRIMARY_IDENTITY, MOCK_SECONDARY_IDENTITY] = Object.values( + testData.metamask.identities, +); export default { title: 'Components/App/SignatureRequest', @@ -76,4 +78,16 @@ DefaultStory.args = { }, fromAccount: MOCK_PRIMARY_IDENTITY, provider: { name: 'Goerli ETH' }, + selectedAccount: MOCK_PRIMARY_IDENTITY, +}; + +export const AccountMismatchStory = (args) => { + return ; +}; + +AccountMismatchStory.storyName = 'AccountMismatch'; + +AccountMismatchStory.args = { + ...DefaultStory.args, + selectedAccount: MOCK_SECONDARY_IDENTITY, }; diff --git a/ui/pages/confirm-signature-request/index.js b/ui/pages/confirm-signature-request/index.js index 51d977c00..d93af8e94 100644 --- a/ui/pages/confirm-signature-request/index.js +++ b/ui/pages/confirm-signature-request/index.js @@ -10,7 +10,12 @@ import SignatureRequestSIWE from '../../components/app/signature-request-siwe'; import SignatureRequestOriginal from '../../components/app/signature-request-original'; import Loading from '../../components/ui/loading-screen'; import { useRouting } from '../../hooks/useRouting'; -import { getTotalUnapprovedSignatureRequestCount } from '../../selectors'; +import { + getTotalUnapprovedSignatureRequestCount, + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + getSelectedAccount, + ///: END:ONLY_INCLUDE_IN +} from '../../selectors'; import { MESSAGE_TYPE } from '../../../shared/constants/app'; import { TransactionStatus } from '../../../shared/constants/transaction'; import { getSendTo } from '../../ducks/send'; @@ -68,6 +73,11 @@ const ConfirmTxScreen = ({ match }) => { provider: { chainId }, } = useSelector((state) => state.metamask); const { txId: index } = useSelector((state) => state.appState); + + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + const selectedAccount = useSelector(getSelectedAccount); + ///: END:ONLY_INCLUDE_IN + const [prevValue, setPrevValues] = useState(); useEffect(() => { @@ -212,6 +222,9 @@ const ConfirmTxScreen = ({ match }) => { cancelMessage={cancelMessage(SIGN_MESSAGE_TYPE.MESSAGE)} cancelPersonalMessage={cancelMessage(SIGN_MESSAGE_TYPE.PERSONAL)} cancelTypedMessage={cancelMessage(SIGN_MESSAGE_TYPE.TYPED)} + ///: BEGIN:ONLY_INCLUDE_IN(mmi) + selectedAccount={selectedAccount} + ///: END:ONLY_INCLUDE_IN /> ); };