import React, { useContext, useState, useEffect } from 'react'; import { useDispatch, useSelector, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) shallowEqual, ///: END:ONLY_INCLUDE_IN } from 'react-redux'; import PropTypes from 'prop-types'; import { memoize } from 'lodash'; import { ethErrors, serializeError } from 'eth-rpc-errors'; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) import { showCustodianDeepLink } from '@metamask-institutional/extension'; ///: END:ONLY_INCLUDE_IN import { ///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask) resolvePendingApproval, completedTx, ///: END:ONLY_INCLUDE_IN rejectPendingApproval, } from '../../../store/actions'; import { doesAddressRequireLedgerHidConnection, getSubjectMetadata, getTotalUnapprovedMessagesCount, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) accountsWithSendEtherInfoSelector, getSelectedAccount, getAccountType, ///: END:ONLY_INCLUDE_IN } from '../../../selectors'; import { getProviderConfig, isAddressLedger, } from '../../../ducks/metamask/metamask'; import { sanitizeMessage, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) getAccountByAddress, shortenAddress, ///: END:ONLY_INCLUDE_IN } from '../../../helpers/utils/util'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { useRejectTransactionModal } from '../../../hooks/useRejectTransactionModal'; import { ConfirmPageContainerNavigation } from '../confirm-page-container'; import SignatureRequestHeader from '../signature-request-header/signature-request-header'; import SecurityProviderBannerMessage from '../security-provider-banner-message'; import LedgerInstructionField from '../ledger-instruction-field'; import ContractDetailsModal from '../modals/contract-details-modal'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { MetaMetricsEventCategory, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) MetaMetricsEventName, ///: END:ONLY_INCLUDE_IN } from '../../../../shared/constants/metametrics'; import { SECURITY_PROVIDER_MESSAGE_SEVERITY } from '../../../../shared/constants/security-provider'; import { TextAlign, TextColor, TextVariant, Size, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) IconColor, BackgroundColor, Display, BlockSize, ///: END:ONLY_INCLUDE_IN } from '../../../helpers/constants/design-system'; import { BUTTON_VARIANT, Button, ButtonLink, TagUrl, Text, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) Icon, IconName, ///: END:ONLY_INCLUDE_IN } from '../../component-library'; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) // eslint-disable-next-line import/order import Box from '../../ui/box/box'; import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../shared/constants/app'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { mmiActionsFactory } from '../../../store/institutional/institution-background'; import { showCustodyConfirmLink } from '../../../store/institutional/institution-actions'; import { useMMICustodySignMessage } from '../../../hooks/useMMICustodySignMessage'; ///: END:ONLY_INCLUDE_IN ///: BEGIN:ONLY_INCLUDE_IN(blockaid) import BlockaidBannerAlert from '../security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert'; ///: END:ONLY_INCLUDE_IN import Message from './signature-request-message'; import Footer from './signature-request-footer'; const SignatureRequest = ({ txData }) => { const trackEvent = useContext(MetaMetricsContext); const dispatch = useDispatch(); const t = useI18nContext(); const [hasScrolledMessage, setHasScrolledMessage] = useState(false); const [showContractDetails, setShowContractDetails] = useState(false); const [messageRootRef, setMessageRootRef] = useState(null); const [messageIsScrollable, setMessageIsScrollable] = useState(false); const { id, type, msgParams: { from, data, origin, version }, } = txData; // not forwarded to component const hardwareWalletRequiresConnection = useSelector((state) => doesAddressRequireLedgerHidConnection(state, from), ); const { chainId, rpcPrefs } = useSelector(getProviderConfig); const unapprovedMessagesCount = useSelector(getTotalUnapprovedMessagesCount); const subjectMetadata = useSelector(getSubjectMetadata); const isLedgerWallet = useSelector((state) => isAddressLedger(state, from)); const { handleCancelAll } = useRejectTransactionModal(); ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) // Used to show a warning if the signing account is not the selected account // Largely relevant for contract wallet custodians const selectedAccount = useSelector(getSelectedAccount); const mmiActions = mmiActionsFactory(); const accountType = useSelector(getAccountType); const isNotification = getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION; const allAccounts = useSelector( accountsWithSendEtherInfoSelector, shallowEqual, ); const { address } = getAccountByAddress(allAccounts, from) || {}; const { custodySignFn } = useMMICustodySignMessage(); ///: END:ONLY_INCLUDE_IN useEffect(() => { setMessageIsScrollable( messageRootRef?.scrollHeight > messageRootRef?.clientHeight, ); }, [messageRootRef]); const targetSubjectMetadata = subjectMetadata?.[origin] || null; const parseMessage = memoize((dataToParse) => { const { message, domain = {}, primaryType, types, } = JSON.parse(dataToParse); const sanitizedMessage = sanitizeMessage(message, primaryType, types); return { sanitizedMessage, domain, primaryType }; }); const onSign = async () => { ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) if (accountType === 'custody') { await custodySignFn(txData); return; } ///: END:ONLY_INCLUDE_IN ///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask) await dispatch(resolvePendingApproval(id)); completedTx(id); ///: END:ONLY_INCLUDE_IN trackEvent({ category: MetaMetricsEventCategory.Transactions, event: 'Confirm', properties: { action: 'Sign Request', legacy_event: true, type, version, }, }); }; const onCancel = async () => { await dispatch( rejectPendingApproval( id, serializeError(ethErrors.provider.userRejectedRequest()), ), ); trackEvent({ category: MetaMetricsEventCategory.Transactions, event: 'Cancel', properties: { action: 'Sign Request', legacy_event: true, type, version, }, }); }; const { sanitizedMessage, domain: { verifyingContract }, primaryType, } = parseMessage(data); ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) useEffect(() => { if (txData.custodyId) { showCustodianDeepLink({ dispatch, mmiActions, txId: undefined, custodyId: txData.custodyId, fromAddress: address, isSignature: true, closeNotification: isNotification, onDeepLinkFetched: () => undefined, onDeepLinkShown: () => { trackEvent({ category: MetaMetricsEventCategory.MMI, event: MetaMetricsEventName.SignatureDeeplinkDisplayed, }); }, showCustodyConfirmLink, }); } }, [ dispatch, mmiActions, txData.custodyId, address, isNotification, trackEvent, ]); ///: END:ONLY_INCLUDE_IN return (