From 608879781ecc5d97b4aa3d696c1bc43eec945db4 Mon Sep 17 00:00:00 2001 From: Bilal <44588480+BZahory@users.noreply.github.com> Date: Thu, 22 Aug 2024 14:02:33 -0400 Subject: [PATCH] chore: add user IDs to send page analytics (#26600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** We currently anonymize all send page events. This PR introduces logic that allows for explicitly overriding this logic for vetted send page events, so that we can create funnels to track user dropoff. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/26600?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Log the payload in `_track()` 2. Complete a swap+send transaction, starting from the home page 3. Ensure send flow events are fired w/ user IDs ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- app/scripts/controllers/metametrics.js | 3 +- .../app/assets/nfts/nfts-items/nfts-items.js | 23 +++++++------- .../app/wallet-overview/coin-buttons.tsx | 21 +++++++------ .../asset-picker-modal/asset-picker-modal.tsx | 23 +++++++------- .../asset-picker/asset-picker.tsx | 17 ++++++----- .../pages/send/components/address-book.tsx | 17 ++++++----- .../send/components/quote-card/index.tsx | 17 ++++++----- .../pages/send/components/recipient-input.tsx | 17 ++++++----- .../pages/send/components/recipient.tsx | 17 ++++++----- .../pages/send/components/your-accounts.tsx | 17 ++++++----- ui/components/multichain/pages/send/send.js | 30 +++++++++++-------- ui/pages/asset/components/token-buttons.tsx | 21 +++++++------ 12 files changed, 130 insertions(+), 93 deletions(-) diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index 6c3f3392e929..8aafa0893d53 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -1016,7 +1016,8 @@ export default class MetaMetricsController { // to be updated to work with the new tracking plan. I think we should use // a config setting for this instead of trying to match the event name const isSendFlow = Boolean(payload.event.match(/^send|^confirm/iu)); - if (isSendFlow) { + // do not filter if excludeMetaMetricsId is explicitly set to false + if (options?.excludeMetaMetricsId !== false && isSendFlow) { excludeMetaMetricsId = true; } // If we are tracking sensitive data we will always use the anonymousId diff --git a/ui/components/app/assets/nfts/nfts-items/nfts-items.js b/ui/components/app/assets/nfts/nfts-items/nfts-items.js index 736f4acd6787..1eb46aa8ccc1 100644 --- a/ui/components/app/assets/nfts/nfts-items/nfts-items.js +++ b/ui/components/app/assets/nfts/nfts-items/nfts-items.js @@ -167,17 +167,20 @@ export default function NftsItems({ }; const onSendNft = async (nft) => { - trackEvent({ - event: MetaMetricsEventName.sendAssetSelected, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_destination_asset_picker_modal: false, - new_asset_symbol: nft.name, - new_asset_address: nft.address, - is_nft: true, + trackEvent( + { + event: MetaMetricsEventName.sendAssetSelected, + category: MetaMetricsEventCategory.Send, + properties: { + ...sendAnalytics, + is_destination_asset_picker_modal: false, + new_asset_symbol: nft.name, + new_asset_address: nft.address, + is_nft: true, + }, }, - }); + { excludeMetaMetricsId: false }, + ); await dispatch( updateSendAsset({ type: AssetType.NFT, diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx index 776631091334..1bd24c23ecdb 100644 --- a/ui/components/app/wallet-overview/coin-buttons.tsx +++ b/ui/components/app/wallet-overview/coin-buttons.tsx @@ -215,16 +215,19 @@ const CoinButtons = ({ ///: END:ONLY_INCLUDE_IF const handleSendOnClick = useCallback(async () => { - trackEvent({ - event: MetaMetricsEventName.NavSendButtonClicked, - category: MetaMetricsEventCategory.Navigation, - properties: { - token_symbol: 'ETH', - location: 'Home', - text: 'Send', - chain_id: chainId, + trackEvent( + { + event: MetaMetricsEventName.NavSendButtonClicked, + category: MetaMetricsEventCategory.Navigation, + properties: { + token_symbol: 'ETH', + location: 'Home', + text: 'Send', + chain_id: chainId, + }, }, - }); + { excludeMetaMetricsId: false }, + ); await dispatch(startNewDraftTransaction({ type: AssetType.native })); history.push(SEND_ROUTE); }, [chainId]); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index e51dd976fddc..0e4207896460 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -122,17 +122,20 @@ export function AssetPickerModal({ const handleAssetChange = useCallback( (token: Token) => { onAssetChange(token); - trackEvent({ - event: MetaMetricsEventName.sendAssetSelected, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_destination_asset_picker_modal: Boolean(isDest), - new_asset_symbol: token.symbol, - new_asset_address: token.address, - is_nft: false, + trackEvent( + { + event: MetaMetricsEventName.sendAssetSelected, + category: MetaMetricsEventCategory.Send, + properties: { + ...sendAnalytics, + is_destination_asset_picker_modal: Boolean(isDest), + new_asset_symbol: token.symbol, + new_asset_address: token.address, + is_nft: false, + }, }, - }); + { excludeMetaMetricsId: false }, + ); onClose(); }, [onAssetChange], diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index ea382450fbd1..e7b1b080770f 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -161,14 +161,17 @@ export function AssetPicker({ backgroundColor={BackgroundColor.transparent} onClick={() => { setShowAssetPickerModal(true); - trackEvent({ - event: MetaMetricsEventName.sendTokenModalOpened, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_destination_asset_picker_modal: Boolean(sendingAsset), + trackEvent( + { + event: MetaMetricsEventName.sendTokenModalOpened, + category: MetaMetricsEventCategory.Send, + properties: { + ...sendAnalytics, + is_destination_asset_picker_modal: Boolean(sendingAsset), + }, }, - }); + { excludeMetaMetricsId: false }, + ); }} endIconName={IconName.ArrowDown} endIconProps={{ diff --git a/ui/components/multichain/pages/send/components/address-book.tsx b/ui/components/multichain/pages/send/components/address-book.tsx index 84e29e0a1ed5..8100646486ca 100644 --- a/ui/components/multichain/pages/send/components/address-book.tsx +++ b/ui/components/multichain/pages/send/components/address-book.tsx @@ -99,14 +99,17 @@ export const SendPageAddressBook = () => { `sendFlow - User clicked recipient from ${type}. address: ${address}, nickname ${nickname}`, ), ); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'address book', - inputType: type, + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'address book', + inputType: type, + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch(updateRecipient({ address, nickname })); dispatch(updateRecipientUserInput(address)); }; diff --git a/ui/components/multichain/pages/send/components/quote-card/index.tsx b/ui/components/multichain/pages/send/components/quote-card/index.tsx index d3815fd3d64c..58f7b2e245d4 100644 --- a/ui/components/multichain/pages/send/components/quote-card/index.tsx +++ b/ui/components/multichain/pages/send/components/quote-card/index.tsx @@ -85,14 +85,17 @@ export function QuoteCard({ scrollRef }: QuoteCardProps) { } if (bestQuote) { - trackEvent({ - event: MetaMetricsEventName.sendSwapQuoteFetched, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, - is_first_fetch: isQuoteJustLoaded, + trackEvent( + { + event: MetaMetricsEventName.sendSwapQuoteFetched, + category: MetaMetricsEventCategory.Send, + properties: { + ...sendAnalytics, + is_first_fetch: isQuoteJustLoaded, + }, }, - }); + { excludeMetaMetricsId: false }, + ); setTimeLeft(REFRESH_INTERVAL); } else { setTimeLeft(undefined); diff --git a/ui/components/multichain/pages/send/components/recipient-input.tsx b/ui/components/multichain/pages/send/components/recipient-input.tsx index debcdbbdca36..4d7d8cee633b 100644 --- a/ui/components/multichain/pages/send/components/recipient-input.tsx +++ b/ui/components/multichain/pages/send/components/recipient-input.tsx @@ -46,14 +46,17 @@ export const SendPageRecipientInput = () => { addHistoryEntry(`sendFlow - Valid address typed ${address}`), ); await dispatch(updateRecipientUserInput(address)); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'send page recipient input', - inputType: 'user input', + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'send page recipient input', + inputType: 'user input', + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch(updateRecipient({ address, nickname: '' })); }} internalSearch={isUsingMyAccountsForRecipientSearch} diff --git a/ui/components/multichain/pages/send/components/recipient.tsx b/ui/components/multichain/pages/send/components/recipient.tsx index fc1a1f66cf2d..7458083b9d3f 100644 --- a/ui/components/multichain/pages/send/components/recipient.tsx +++ b/ui/components/multichain/pages/send/components/recipient.tsx @@ -67,14 +67,17 @@ export const SendPageRecipient = () => { `sendFlow - User clicked recipient from ${type}. address: ${address}, nickname ${nickname}`, ), ); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'send page recipient screen', - inputType: type, + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'send page recipient screen', + inputType: type, + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch(updateRecipient({ address, nickname })); dispatch(updateRecipientUserInput(address)); }; diff --git a/ui/components/multichain/pages/send/components/your-accounts.tsx b/ui/components/multichain/pages/send/components/your-accounts.tsx index 0b3ee36dc183..f53d6603cb78 100644 --- a/ui/components/multichain/pages/send/components/your-accounts.tsx +++ b/ui/components/multichain/pages/send/components/your-accounts.tsx @@ -63,14 +63,17 @@ export const SendPageYourAccounts = ({ `sendFlow - User clicked recipient from my accounts. address: ${account.address}, nickname ${account.name}`, ), ); - trackEvent({ - event: MetaMetricsEventName.sendRecipientSelected, - category: MetaMetricsEventCategory.Send, - properties: { - location: 'my accounts', - inputType: 'click', + trackEvent( + { + event: MetaMetricsEventName.sendRecipientSelected, + category: MetaMetricsEventCategory.Send, + properties: { + location: 'my accounts', + inputType: 'click', + }, }, - }); + { excludeMetaMetricsId: false }, + ); dispatch( updateRecipient({ address: account.address, diff --git a/ui/components/multichain/pages/send/send.js b/ui/components/multichain/pages/send/send.js index 7da8aed96751..831a7ae83d38 100644 --- a/ui/components/multichain/pages/send/send.js +++ b/ui/components/multichain/pages/send/send.js @@ -202,13 +202,16 @@ export const SendPage = () => { } dispatch(resetSendState()); - trackEvent({ - event: MetaMetricsEventName.sendFlowExited, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, + trackEvent( + { + event: MetaMetricsEventName.sendFlowExited, + category: MetaMetricsEventCategory.Send, + properties: { + ...sendAnalytics, + }, }, - }); + { excludeMetaMetricsId: false }, + ); const nextRoute = sendStage === SEND_STAGES.EDIT ? DEFAULT_ROUTE : mostRecentOverviewPage; @@ -217,13 +220,16 @@ export const SendPage = () => { useEffect(() => { if (swapQuotesError) { - trackEvent({ - event: MetaMetricsEventName.sendSwapQuoteError, - category: MetaMetricsEventCategory.Send, - properties: { - ...sendAnalytics, + trackEvent( + { + event: MetaMetricsEventName.sendSwapQuoteError, + category: MetaMetricsEventCategory.Send, + properties: { + ...sendAnalytics, + }, }, - }); + { excludeMetaMetricsId: false }, + ); } // sendAnalytics should not result in the event refiring // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/ui/pages/asset/components/token-buttons.tsx b/ui/pages/asset/components/token-buttons.tsx index 4642524fa679..a07cdaca2d48 100644 --- a/ui/pages/asset/components/token-buttons.tsx +++ b/ui/pages/asset/components/token-buttons.tsx @@ -183,16 +183,19 @@ const TokenButtons = ({ { - trackEvent({ - event: MetaMetricsEventName.NavSendButtonClicked, - category: MetaMetricsEventCategory.Navigation, - properties: { - token_symbol: token.symbol, - location: MetaMetricsSwapsEventSource.TokenView, - text: 'Send', - chain_id: chainId, + trackEvent( + { + event: MetaMetricsEventName.NavSendButtonClicked, + category: MetaMetricsEventCategory.Navigation, + properties: { + token_symbol: token.symbol, + location: MetaMetricsSwapsEventSource.TokenView, + text: 'Send', + chain_id: chainId, + }, }, - }); + { excludeMetaMetricsId: false }, + ); try { await dispatch( startNewDraftTransaction({