diff --git a/test/jest/rendering.js b/test/jest/rendering.js index 2c46c306ad2a..091c2a629e2b 100644 --- a/test/jest/rendering.js +++ b/test/jest/rendering.js @@ -7,6 +7,7 @@ import PropTypes from 'prop-types'; import { I18nContext, LegacyI18nProvider } from '../../ui/contexts/i18n'; import { getMessage } from '../../ui/helpers/utils/i18n-helper'; import * as en from '../../app/_locales/en/messages.json'; +import { LegacyMetaMetricsProvider } from '../../ui/contexts/metametrics'; export const I18nProvider = (props) => { const { currentLocale, current, en: eng } = props; @@ -38,7 +39,9 @@ export function renderWithProvider(component, store, initialEntries) { const WithoutStore = () => ( - {children} + + {children} + ); 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 073f6d5f61c1..176bd5c3bce5 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 @@ -44,6 +44,11 @@ import { ///: END:ONLY_INCLUDE_IN } from '../../component-library'; ///: BEGIN:ONLY_INCLUDE_IN(blockaid) +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../shared/constants/metametrics'; +import { getBlockaidMetricsParams } from '../../../helpers/utils/metrics'; import BlockaidBannerAlert from '../security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert'; ///: END:ONLY_INCLUDE_IN import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-page-container-navigation'; @@ -58,6 +63,7 @@ import SignatureRequestOriginalWarning from './signature-request-original-warnin export default class SignatureRequestOriginal extends Component { static contextTypes = { t: PropTypes.func.isRequired, + trackEvent: PropTypes.func, }; static propTypes = { @@ -90,6 +96,26 @@ export default class SignatureRequestOriginal extends Component { showSignatureRequestWarning: false, }; + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + componentDidMount() { + const { txData } = this.props; + if (txData.securityAlertResponse) { + const blockaidMetricsParams = getBlockaidMetricsParams( + txData.securityAlertResponse, + ); + + this.context.trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.SignatureRequested, + properties: { + action: 'Sign Request', + ...blockaidMetricsParams, + }, + }); + } + } + ///: END:ONLY_INCLUDE_IN + msgHexToText = (hex) => { try { const stripped = stripHexPrefix(hex); diff --git a/ui/components/app/signature-request-siwe/signature-request-siwe.js b/ui/components/app/signature-request-siwe/signature-request-siwe.js index 602aa2fe7141..7d85ea1cf159 100644 --- a/ui/components/app/signature-request-siwe/signature-request-siwe.js +++ b/ui/components/app/signature-request-siwe/signature-request-siwe.js @@ -1,4 +1,11 @@ -import React, { useCallback, useContext, useState } from 'react'; +import React, { + useCallback, + useContext, + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + useEffect, + ///: END:ONLY_INCLUDE_IN + useState, +} from 'react'; import PropTypes from 'prop-types'; import { useSelector, useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; @@ -40,6 +47,12 @@ import ConfirmPageContainerNavigation from '../confirm-page-container/confirm-pa import { getMostRecentOverviewPage } from '../../../ducks/history/history'; ///: BEGIN:ONLY_INCLUDE_IN(blockaid) import BlockaidBannerAlert from '../security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert'; +import { getBlockaidMetricsParams } from '../../../helpers/utils/metrics'; +import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../shared/constants/metametrics'; ///: END:ONLY_INCLUDE_IN import LedgerInstructionField from '../ledger-instruction-field'; @@ -56,6 +69,28 @@ export default function SignatureRequestSIWE({ txData }) { const messagesCount = useSelector(getTotalUnapprovedMessagesCount); const messagesList = useSelector(unconfirmedMessagesHashSelector); const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage); + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + const trackEvent = useContext(MetaMetricsContext); + ///: END:ONLY_INCLUDE_IN + + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + useEffect(() => { + if (txData.securityAlertResponse) { + const blockaidMetricsParams = getBlockaidMetricsParams( + txData.securityAlertResponse, + ); + + trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.SignatureRequested, + properties: { + action: 'Sign Request', + ...blockaidMetricsParams, + }, + }); + } + }, [txData?.securityAlertResponse]); + ///: END:ONLY_INCLUDE_IN const { msgParams: { diff --git a/ui/components/app/signature-request/signature-request.js b/ui/components/app/signature-request/signature-request.js index e5de3f67cca0..f29601010ae3 100644 --- a/ui/components/app/signature-request/signature-request.js +++ b/ui/components/app/signature-request/signature-request.js @@ -96,6 +96,7 @@ import { showCustodyConfirmLink } from '../../../store/institutional/institution import { useMMICustodySignMessage } from '../../../hooks/useMMICustodySignMessage'; ///: END:ONLY_INCLUDE_IN ///: BEGIN:ONLY_INCLUDE_IN(blockaid) +import { getBlockaidMetricsParams } from '../../../helpers/utils/metrics'; import BlockaidBannerAlert from '../security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert'; ///: END:ONLY_INCLUDE_IN @@ -255,6 +256,27 @@ const SignatureRequest = ({ txData }) => { ]); ///: END:ONLY_INCLUDE_IN + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + useEffect(() => { + if (txData.securityAlertResponse) { + const blockaidMetricsParams = getBlockaidMetricsParams( + txData.securityAlertResponse, + ); + + trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.SignatureRequested, + properties: { + action: 'Sign Request', + type, + version, + ...blockaidMetricsParams, + }, + }); + } + }, [txData?.securityAlertResponse]); + ///: END:ONLY_INCLUDE_IN + return (
diff --git a/ui/components/app/transaction-alerts/transaction-alerts.js b/ui/components/app/transaction-alerts/transaction-alerts.js index f2ef032eab69..00f0c6ab58ac 100644 --- a/ui/components/app/transaction-alerts/transaction-alerts.js +++ b/ui/components/app/transaction-alerts/transaction-alerts.js @@ -2,6 +2,7 @@ import React, { ///: BEGIN:ONLY_INCLUDE_IN(blockaid) useCallback, useContext, + useEffect, ///: END:ONLY_INCLUDE_IN } from 'react'; import PropTypes from 'prop-types'; @@ -27,12 +28,13 @@ import { getNativeCurrency } from '../../../ducks/metamask/metamask'; import { TransactionType } from '../../../../shared/constants/transaction'; import { parseStandardTokenTransactionData } from '../../../../shared/modules/transaction.utils'; import { getTokenValueParam } from '../../../../shared/lib/metamask-controller-utils'; +///: BEGIN:ONLY_INCLUDE_IN(blockaid) import { - ///: BEGIN:ONLY_INCLUDE_IN(blockaid) MetaMetricsEventCategory, MetaMetricsEventName, - ///: END:ONLY_INCLUDE_IN } from '../../../../shared/constants/metametrics'; +import { getBlockaidMetricsParams } from '../../../helpers/utils/metrics'; +///: END:ONLY_INCLUDE_IN const TransactionAlerts = ({ userAcknowledgedGasMissing, @@ -72,6 +74,25 @@ const TransactionAlerts = ({ const trackEvent = useContext(MetaMetricsContext); ///: END:ONLY_INCLUDE_IN + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + useEffect(() => { + if (txData.securityAlertResponse) { + const blockaidMetricsParams = getBlockaidMetricsParams( + txData.securityAlertResponse, + ); + + trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.SignatureRequested, + properties: { + action: 'Sign Request', + ...blockaidMetricsParams, + }, + }); + } + }, [txData?.securityAlertResponse]); + ///: END:ONLY_INCLUDE_IN + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) const onClickSupportLink = useCallback(() => { trackEvent({ diff --git a/ui/helpers/utils/metric.test.js b/ui/helpers/utils/metric.test.js index b3a4981d5a4e..5dbfb8ced832 100644 --- a/ui/helpers/utils/metric.test.js +++ b/ui/helpers/utils/metric.test.js @@ -1,4 +1,8 @@ -import { getMethodName } from './metrics'; +import { + BlockaidReason, + BlockaidResultType, +} from '../../../shared/constants/security-provider'; +import { getBlockaidMetricsParams, getMethodName } from './metrics'; describe('getMethodName', () => { it('should get correct method names', () => { @@ -11,3 +15,63 @@ describe('getMethodName', () => { ); }); }); + +describe('getBlockaidMetricsParams', () => { + it('should return empty object when securityAlertResponse is not defined', () => { + const result = getBlockaidMetricsParams(undefined); + expect(result).toStrictEqual({}); + }); + + it('should return additionalParams object when securityAlertResponse defined', () => { + const securityAlertResponse = { + result_type: BlockaidResultType.Malicious, + reason: BlockaidReason.notApplicable, + providerRequestsCount: { + eth_call: 5, + eth_getCode: 3, + }, + features: [], + }; + + const result = getBlockaidMetricsParams(securityAlertResponse); + expect(result).toStrictEqual({ + ui_customizations: ['flagged_as_malicious'], + security_alert_response: BlockaidResultType.Malicious, + security_alert_reason: BlockaidReason.notApplicable, + ppom_eth_call_count: 5, + ppom_eth_getCode_count: 3, + }); + }); + + it('should not return eth call counts if providerRequestsCount is empty', () => { + const securityAlertResponse = { + result_type: BlockaidResultType.Malicious, + reason: BlockaidReason.notApplicable, + features: [], + providerRequestsCount: {}, + }; + + const result = getBlockaidMetricsParams(securityAlertResponse); + expect(result).toStrictEqual({ + ui_customizations: ['flagged_as_malicious'], + security_alert_response: BlockaidResultType.Malicious, + security_alert_reason: BlockaidReason.notApplicable, + }); + }); + + it('should not return eth call counts if providerRequestsCount is undefined', () => { + const securityAlertResponse = { + result_type: BlockaidResultType.Malicious, + reason: BlockaidReason.notApplicable, + features: [], + providerRequestsCount: undefined, + }; + + const result = getBlockaidMetricsParams(securityAlertResponse); + expect(result).toStrictEqual({ + ui_customizations: ['flagged_as_malicious'], + security_alert_response: BlockaidResultType.Malicious, + security_alert_reason: BlockaidReason.notApplicable, + }); + }); +}); diff --git a/ui/helpers/utils/metrics.js b/ui/helpers/utils/metrics.js index 56cf958730e0..796377bcdee6 100644 --- a/ui/helpers/utils/metrics.js +++ b/ui/helpers/utils/metrics.js @@ -1,3 +1,10 @@ +///: BEGIN:ONLY_INCLUDE_IN(blockaid) +import { + BlockaidReason, + BlockaidResultType, +} from '../../../shared/constants/security-provider'; +///: END:ONLY_INCLUDE_IN + export function getMethodName(camelCase) { if (!camelCase || typeof camelCase !== 'string') { return ''; @@ -16,3 +23,40 @@ export function formatAccountType(accountType) { return accountType; } + +///: BEGIN:ONLY_INCLUDE_IN(blockaid) +export const getBlockaidMetricsParams = (securityAlertResponse = null) => { + const additionalParams = {}; + + if (securityAlertResponse) { + const { + result_type: resultType, + reason, + providerRequestsCount, + } = securityAlertResponse; + + if (resultType === BlockaidResultType.Malicious) { + additionalParams.ui_customizations = ['flagged_as_malicious']; + } + + if (resultType !== BlockaidResultType.Benign) { + additionalParams.security_alert_reason = BlockaidReason.notApplicable; + + if (reason) { + additionalParams.security_alert_response = resultType; + additionalParams.security_alert_reason = reason; + } + } + + // add counts of each RPC call + if (providerRequestsCount) { + Object.keys(providerRequestsCount).forEach((key) => { + const metricKey = `ppom_${key}_count`; + additionalParams[metricKey] = providerRequestsCount[key]; + }); + } + } + + return additionalParams; +}; +///: END:ONLY_INCLUDE_IN diff --git a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js index 4ce205308f32..5dd90c7e9814 100644 --- a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js +++ b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js @@ -24,6 +24,11 @@ import { ConfirmPageContainerWarning } from '../../../components/app/confirm-pag import LedgerInstructionField from '../../../components/app/ledger-instruction-field'; ///: BEGIN:ONLY_INCLUDE_IN(blockaid) import BlockaidBannerAlert from '../../../components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert'; +import { getBlockaidMetricsParams } from '../../../helpers/utils/metrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../shared/constants/metametrics'; ///: END:ONLY_INCLUDE_IN import { isSuspiciousResponse } from '../../../../shared/modules/security-provider.utils'; @@ -46,6 +51,9 @@ import { COPY_OPTIONS } from '../../../../shared/constants/copy'; export default class ConfirmApproveContent extends Component { static contextTypes = { t: PropTypes.func, + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + trackEvent: PropTypes.func, + ///: END:ONLY_INCLUDE_IN }; static propTypes = { @@ -95,6 +103,26 @@ export default class ConfirmApproveContent extends Component { setShowContractDetails: false, }; + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + componentDidMount() { + const { txData } = this.props; + if (txData.securityAlertResponse) { + const blockaidMetricsParams = getBlockaidMetricsParams( + txData.securityAlertResponse, + ); + + this.context.trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.SignatureRequested, + properties: { + action: 'Sign Request', + ...blockaidMetricsParams, + }, + }); + } + } + ///: END:ONLY_INCLUDE_IN + renderApproveContentCard({ showHeader = true, symbol, diff --git a/ui/pages/token-allowance/token-allowance.js b/ui/pages/token-allowance/token-allowance.js index 2bae6f067205..78ac171323db 100644 --- a/ui/pages/token-allowance/token-allowance.js +++ b/ui/pages/token-allowance/token-allowance.js @@ -63,6 +63,12 @@ import { import { isSuspiciousResponse } from '../../../shared/modules/security-provider.utils'; ///: BEGIN:ONLY_INCLUDE_IN(blockaid) import BlockaidBannerAlert from '../../components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert'; +import { getBlockaidMetricsParams } from '../../helpers/utils/metrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../shared/constants/metametrics'; +import { MetaMetricsContext } from '../../contexts/metametrics'; ///: END:ONLY_INCLUDE_IN import { ConfirmPageContainerNavigation } from '../../components/app/confirm-page-container'; import { useSimulationFailureWarning } from '../../hooks/useSimulationFailureWarning'; @@ -137,6 +143,29 @@ export default function TokenAllowance({ const nextNonce = useSelector(getNextSuggestedNonce); const customNonceValue = useSelector(getCustomNonceValue); + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + const trackEvent = useContext(MetaMetricsContext); + ///: END:ONLY_INCLUDE_IN + + ///: BEGIN:ONLY_INCLUDE_IN(blockaid) + useEffect(() => { + if (txData.securityAlertResponse) { + const blockaidMetricsParams = getBlockaidMetricsParams( + txData.securityAlertResponse, + ); + + trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.SignatureRequested, + properties: { + action: 'Sign Request', + ...blockaidMetricsParams, + }, + }); + } + }, [txData?.securityAlertResponse]); + ///: END:ONLY_INCLUDE_IN + /** * We set the customSpendingCap to the dappProposedTokenAmount, if provided, rather than setting customTokenAmount * because customTokenAmount is reserved for custom user input. This is only set once when the component is mounted.