Skip to content

Commit

Permalink
Event tracking for Token Detection V2 (MetaMask#14441)
Browse files Browse the repository at this point in the history
  • Loading branch information
NiranjanaBinoy authored May 11, 2022
1 parent 4b2cd0e commit 6c757ab
Show file tree
Hide file tree
Showing 17 changed files with 231 additions and 38 deletions.
20 changes: 20 additions & 0 deletions app/scripts/controllers/detect-tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { MINUTE } from '../../../shared/constants/time';
import { MAINNET_CHAIN_ID } from '../../../shared/constants/network';
import { isTokenDetectionEnabledForNetwork } from '../../../shared/modules/network.utils';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import { TOKEN_STANDARDS } from '../../../ui/helpers/constants/common';
import { ASSET_TYPES } from '../../../shared/constants/transaction';
import { EVENT, EVENT_NAMES } from '../../../shared/constants/metametrics';

// By default, poll every 3 minutes
const DEFAULT_INTERVAL = MINUTE * 3;
Expand All @@ -26,6 +29,7 @@ export default class DetectTokensController {
* @param config.tokenList
* @param config.tokensController
* @param config.assetsContractController
* @param config.trackMetaMetricsEvent
*/
constructor({
interval = DEFAULT_INTERVAL,
Expand All @@ -35,6 +39,7 @@ export default class DetectTokensController {
tokenList,
tokensController,
assetsContractController = null,
trackMetaMetricsEvent,
} = {}) {
this.assetsContractController = assetsContractController;
this.tokensController = tokensController;
Expand All @@ -51,6 +56,7 @@ export default class DetectTokensController {
this.detectedTokens = process.env.TOKEN_DETECTION_V2
? this.tokensController?.state.detectedTokens
: [];
this._trackMetaMetricsEvent = trackMetaMetricsEvent;

preferences?.store.subscribe(({ selectedAddress, useTokenDetection }) => {
if (
Expand Down Expand Up @@ -162,6 +168,7 @@ export default class DetectTokensController {

let tokensWithBalance = [];
if (process.env.TOKEN_DETECTION_V2) {
const eventTokensDetails = [];
if (result) {
const nonZeroTokenAddresses = Object.keys(result);
for (const nonZeroTokenAddress of nonZeroTokenAddresses) {
Expand All @@ -172,6 +179,9 @@ export default class DetectTokensController {
iconUrl,
aggregators,
} = tokenList[nonZeroTokenAddress];

eventTokensDetails.push(`${symbol} - ${address}`);

tokensWithBalance.push({
address,
symbol,
Expand All @@ -180,7 +190,17 @@ export default class DetectTokensController {
aggregators,
});
}

if (tokensWithBalance.length > 0) {
this._trackMetaMetricsEvent({
event: EVENT_NAMES.TOKEN_DETECTED,
category: EVENT.CATEGORIES.WALLET,
properties: {
tokens: eventTokensDetails,
token_standard: TOKEN_STANDARDS.ERC20,
asset_type: ASSET_TYPES.TOKEN,
},
});
await this.tokensController.addDetectedTokens(tokensWithBalance);
}
}
Expand Down
1 change: 1 addition & 0 deletions app/scripts/controllers/metametrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ export default class MetaMetricsController {
[TRAITS.OPENSEA_API_ENABLED]: metamaskState.openSeaEnabled,
[TRAITS.THREE_BOX_ENABLED]: metamaskState.threeBoxSyncingAllowed,
[TRAITS.THEME]: metamaskState.theme || 'default',
[TRAITS.TOKEN_DETECTION_ENABLED]: metamaskState.useTokenDetection,
};

if (!this.previousTraits) {
Expand Down
6 changes: 6 additions & 0 deletions app/scripts/controllers/metametrics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ describe('MetaMetricsController', function () {
threeBoxSyncingAllowed: false,
useCollectibleDetection: false,
theme: 'default',
useTokenDetection: true,
});

assert.deepEqual(traits, {
Expand All @@ -696,6 +697,7 @@ describe('MetaMetricsController', function () {
[TRAITS.OPENSEA_API_ENABLED]: true,
[TRAITS.THREE_BOX_ENABLED]: false,
[TRAITS.THEME]: 'default',
[TRAITS.TOKEN_DETECTION_ENABLED]: true,
});
});

Expand All @@ -717,6 +719,7 @@ describe('MetaMetricsController', function () {
threeBoxSyncingAllowed: false,
useCollectibleDetection: false,
theme: 'default',
useTokenDetection: true,
});

const updatedTraits = metaMetricsController._buildUserTraitsObject({
Expand All @@ -737,6 +740,7 @@ describe('MetaMetricsController', function () {
threeBoxSyncingAllowed: false,
useCollectibleDetection: false,
theme: 'default',
useTokenDetection: true,
});

assert.deepEqual(updatedTraits, {
Expand Down Expand Up @@ -765,6 +769,7 @@ describe('MetaMetricsController', function () {
threeBoxSyncingAllowed: false,
useCollectibleDetection: true,
theme: 'default',
useTokenDetection: true,
});

const updatedTraits = metaMetricsController._buildUserTraitsObject({
Expand All @@ -783,6 +788,7 @@ describe('MetaMetricsController', function () {
threeBoxSyncingAllowed: false,
useCollectibleDetection: true,
theme: 'default',
useTokenDetection: true,
});

assert.equal(updatedTraits, null);
Expand Down
14 changes: 7 additions & 7 deletions app/scripts/controllers/transactions/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1470,7 +1470,7 @@ describe('Transaction Controller', function () {
gas_edit_type: 'none',
network: '42',
referrer: ORIGIN_METAMASK,
source: 'user',
source: EVENT.SOURCE.TRANSACTION.USER,
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
Expand Down Expand Up @@ -1549,7 +1549,7 @@ describe('Transaction Controller', function () {
gas_edit_type: 'none',
network: '42',
referrer: ORIGIN_METAMASK,
source: 'user',
source: EVENT.SOURCE.TRANSACTION.USER,
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
Expand Down Expand Up @@ -1638,7 +1638,7 @@ describe('Transaction Controller', function () {
gas_edit_type: 'none',
network: '42',
referrer: 'other',
source: 'dapp',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
Expand Down Expand Up @@ -1719,7 +1719,7 @@ describe('Transaction Controller', function () {
gas_edit_type: 'none',
network: '42',
referrer: 'other',
source: 'dapp',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
Expand Down Expand Up @@ -1800,7 +1800,7 @@ describe('Transaction Controller', function () {
gas_edit_type: 'none',
network: '42',
referrer: 'other',
source: 'dapp',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
Expand Down Expand Up @@ -1859,7 +1859,7 @@ describe('Transaction Controller', function () {
properties: {
network: '42',
referrer: 'other',
source: 'dapp',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
chain_id: '0x2a',
eip_1559_version: '0',
Expand Down Expand Up @@ -1936,7 +1936,7 @@ describe('Transaction Controller', function () {
gas_edit_type: 'none',
network: '42',
referrer: 'other',
source: 'dapp',
source: EVENT.SOURCE.TRANSACTION.DAPP,
type: TRANSACTION_TYPES.SIMPLE_SEND,
account_type: 'MetaMask',
asset_type: ASSET_TYPES.NATIVE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ async function addEthereumChainHandler(
network: firstValidRPCUrl,
symbol: ticker,
block_explorer_url: firstValidBlockExplorerUrl,
source: 'dapp',
source: EVENT.SOURCE.TRANSACTION.DAPP,
},
});

Expand Down
3 changes: 3 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,9 @@ export default class MetamaskController extends EventEmitter {
network: this.networkController,
keyringMemStore: this.keyringController.memStore,
tokenList: this.tokenListController,
trackMetaMetricsEvent: this.metaMetricsController.trackEvent.bind(
this.metaMetricsController,
),
}))
: (this.detectTokensController = new DetectTokensController({
preferences: this.preferencesController,
Expand Down
32 changes: 31 additions & 1 deletion shared/constants/metametrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@
* @property {'three_box_enabled'} THREE_BOX_ENABLED - when 3box feature is
* toggled we identify the 3box_enabled trait
* @property {'theme'} THEME - when the user's theme changes we identify the theme trait
* @property {'token_detection_enabled'} TOKEN_DETECTION_ENABLED - when token detection feature is toggled we
* identify the token_detection_enabled trait
*/

/**
Expand All @@ -197,6 +199,7 @@ export const TRAITS = {
OPENSEA_API_ENABLED: 'opensea_api_enabled',
THREE_BOX_ENABLED: 'three_box_enabled',
THEME: 'theme',
TOKEN_DETECTION_ENABLED: 'token_detection_enabled',
};

/**
Expand All @@ -222,6 +225,7 @@ export const TRAITS = {
* @property {boolean} [three_box_enabled] - does the user have 3box sync
* enabled?
* @property {string} [theme] - which theme the user has selected
* @property {boolean} [token_detection_enabled] - does the user have token detection is enabled?
*/

// Mixpanel converts the zero address value to a truly anonymous event, which
Expand Down Expand Up @@ -265,10 +269,15 @@ export const REJECT_NOTFICIATION_CLOSE_SIG =
*/

export const EVENT_NAMES = {
SIGNATURE_REQUESTED: 'Signature Requested',
ENCRYPTION_PUBLIC_KEY_REQUESTED: 'Encryption Public Key Requested',
DECRYPTION_REQUESTED: 'Decryption Requested',
PERMISSIONS_REQUESTED: 'Permissions Requested',
SIGNATURE_REQUESTED: 'Signature Requested',
TOKEN_ADDED: 'Token Added',
TOKEN_DETECTED: 'Token Detected',
TOKEN_HIDDEN: 'Token Hidden',
TOKEN_IMPORT_CANCELED: 'Token Import Canceled',
TOKEN_IMPORT_CLICKED: 'Token Import Clicked',
};

export const EVENT = {
Expand All @@ -288,4 +297,25 @@ export const EVENT = {
TRANSACTIONS: 'Transactions',
WALLET: 'Wallet',
},
SOURCE: {
SWAPS: {
MAIN_VIEW: 'Main View',
TOKEN_VIEW: 'Token View',
},
TRANSACTION: {
USER: 'user',
DAPP: 'dapp',
},
TOKEN: {
CUSTOM: 'custom',
DETECTED: 'detected',
DAPP: 'dapp',
LIST: 'list',
},
},
LOCATION: {
TOKEN_DETECTION: 'token_detection',
TOKEN_MENU: 'token_menu',
TOKEN_DETAILS: 'token_details',
},
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useContext } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import classNames from 'classnames';
Expand All @@ -7,11 +7,32 @@ import Box from '../../../ui/box/box';
import Button from '../../../ui/button';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import { getDetectedTokensInCurrentNetwork } from '../../../../selectors';
import { MetaMetricsContext } from '../../../../contexts/metametrics';
import {
EVENT,
EVENT_NAMES,
} from '../../../../../shared/constants/metametrics';

const DetectedTokensLink = ({ className = '', setShowDetectedTokens }) => {
const t = useI18nContext();
const trackEvent = useContext(MetaMetricsContext);

const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork);
const detectedTokensDetails = detectedTokens.map(
({ address, symbol }) => `${symbol} - ${address}`,
);

const onClick = () => {
setShowDetectedTokens(true);
trackEvent({
event: EVENT_NAMES.TOKEN_IMPORT_CLICKED,
category: EVENT.CATEGORIES.WALLET,
properties: {
source: EVENT.SOURCE.TOKEN.DETECTED,
tokens: detectedTokensDetails,
},
});
};
return (
<Box
className={classNames('detected-tokens-link', className)}
Expand All @@ -20,7 +41,7 @@ const DetectedTokensLink = ({ className = '', setShowDetectedTokens }) => {
<Button
type="link"
className="detected-tokens-link__link"
onClick={() => setShowDetectedTokens(true)}
onClick={onClick}
>
{t('numberOfNewTokensDetected', [detectedTokens.length])}
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { Provider } from 'react-redux';

import testData from '../../../../../.storybook/test-data';
import configureStore from '../../../../store/store';
import DetectedTokensLink from './detected-tokens-link';

const store = configureStore(testData);

export default {
title: 'Components/App/AssetList/DetectedTokensLink',
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
id: __filename,
argTypes: {
setShowDetectedTokens: { control: 'func' },
},
args: {
setShowDetectedTokens: 'setShowDetectedTokensSpy',
},
};

const Template = (args) => {
return <DetectedTokensLink {...args} />;
};

export const DefaultStory = Template.bind({});

DefaultStory.storyName = 'Default';
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import {
renderWithProvider,
screen,
fireEvent,
} from '../../../../../test/jest';
import configureStore from '../../../../store/store';
import testData from '../../../../../.storybook/test-data';

import DetectedTokensLink from './detected-tokens-link';

describe('DetectedTokensLink', () => {
let setShowDetectedTokensSpy;
const args = {};

beforeEach(() => {
setShowDetectedTokensSpy = jest.fn();
args.setShowDetectedTokens = setShowDetectedTokensSpy;
});

it('should render number of tokens detected link', () => {
const store = configureStore(testData);
renderWithProvider(<DetectedTokensLink {...args} />, store);

expect(
screen.getByText('3 new tokens found in this account'),
).toBeInTheDocument();

fireEvent.click(screen.getByText('3 new tokens found in this account'));
expect(setShowDetectedTokensSpy).toHaveBeenCalled();
});
});
Loading

0 comments on commit 6c757ab

Please sign in to comment.