1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-25 12:52:33 +02:00
metamask-extension/ui/app/helpers/utils/metametrics.util.js
Mark Stacey 8713927e5e
Remove url parameter from metricsEvent (#9157)
* Remove `url` parameter from `metricsEvent`

The `url` parameter was used to override the `currentPath`, but it
never worked correctly. It was supposed to be used for setting the
`url` query parameter that was sent to Matomo, but `currentPath` was
always used even if it `url` was set and `currentPath` was empty.

Instead, `currentPath` is now always used. There was never a need to
provide an "override" for `currentPath` when it can be set directly.
The metrics provider does set `currentPath` automatically by default,
but this can be overwritten already by passing a second parameter to
`metricsEvent`.

There were two places this `url` parameter was being used: background
events, and path changes. Background events were submitted with no
`currentPath`, so because of the bug with the `url` parameter, the
metrics utility would crash upon each event. So those were never
actually sent. This commit will fix that crash.

The `currentPath` parameter was supplied as an empty string for the
path change events, so those never crashed. They just had the `url`
query string parameter set incorrectly (to an empty string). It should
now be correctly populated, which should mean we'll be capturing all
path changes now. Previously we were only capturing path changes to
pages that happened to include an event, because of this blank `url`
problem.

* Use `url` query parameter as fallback for generating `pv_id`

The `pv_id` parameter currently isn't generated correctly on Firefox,
as the generation assumes that the current URL starts with
`chrome-extension://`. The `url` query parameter is still unique for
each path, so it's probably good enough for generating an id for each
page.

This is just a temporary fix; it will be removed in a future PR, where
Firefox will be properly supported.
2020-08-07 14:57:27 -03:00

228 lines
9.2 KiB
JavaScript

/* eslint camelcase: 0 */
import ethUtil from 'ethereumjs-util'
const inDevelopment = process.env.METAMASK_DEBUG || process.env.IN_TEST
let projectId = process.env.METAMETRICS_PROJECT_ID
if (!projectId) {
projectId = inDevelopment ? 1 : 2
}
const METAMETRICS_BASE_URL = 'https://chromeextensionmm.innocraft.cloud/piwik.php'
const METAMETRICS_REQUIRED_PARAMS = `?idsite=${projectId}&rec=1&apiv=1`
const METAMETRICS_BASE_FULL = METAMETRICS_BASE_URL + METAMETRICS_REQUIRED_PARAMS
export const METAMETRICS_TRACKING_URL = inDevelopment
? 'http://www.metamask.io/metametrics'
: 'http://www.metamask.io/metametrics-prod'
/** ***************Custom variables*************** **/
// Custom variable declarations
const METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE = 'gasLimitChange'
const METAMETRICS_CUSTOM_GAS_PRICE_CHANGE = 'gasPriceChange'
const METAMETRICS_CUSTOM_FUNCTION_TYPE = 'functionType'
const METAMETRICS_CUSTOM_RECIPIENT_KNOWN = 'recipientKnown'
const METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN = 'origin'
const METAMETRICS_CUSTOM_FROM_NETWORK = 'fromNetwork'
const METAMETRICS_CUSTOM_TO_NETWORK = 'toNetwork'
const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField'
const METAMETRICS_CUSTOM_ERROR_MESSAGE = 'errorMessage'
const METAMETRICS_CUSTOM_RPC_NETWORK_ID = 'networkId'
const METAMETRICS_CUSTOM_RPC_CHAIN_ID = 'chainId'
const METAMETRICS_CUSTOM_GAS_CHANGED = 'gasChanged'
const METAMETRICS_CUSTOM_ASSET_SELECTED = 'assetSelected'
const customVariableNameIdMap = {
[METAMETRICS_CUSTOM_FUNCTION_TYPE]: 1,
[METAMETRICS_CUSTOM_RECIPIENT_KNOWN]: 2,
[METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN]: 3,
[METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4,
[METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5,
[METAMETRICS_CUSTOM_FROM_NETWORK]: 1,
[METAMETRICS_CUSTOM_TO_NETWORK]: 2,
[METAMETRICS_CUSTOM_RPC_NETWORK_ID]: 1,
[METAMETRICS_CUSTOM_RPC_CHAIN_ID]: 2,
[METAMETRICS_CUSTOM_ERROR_FIELD]: 3,
[METAMETRICS_CUSTOM_ERROR_MESSAGE]: 4,
[METAMETRICS_CUSTOM_GAS_CHANGED]: 1,
[METAMETRICS_CUSTOM_ASSET_SELECTED]: 2,
}
/** ********************************************************** **/
const METAMETRICS_CUSTOM_NETWORK = 'network'
const METAMETRICS_CUSTOM_ENVIRONMENT_TYPE = 'environmentType'
const METAMETRICS_CUSTOM_ACTIVE_CURRENCY = 'activeCurrency'
const METAMETRICS_CUSTOM_ACCOUNT_TYPE = 'accountType'
const METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS = 'numberOfAccounts'
const METAMETRICS_CUSTOM_NUMBER_OF_TOKENS = 'numberOfTokens'
const METAMETRICS_CUSTOM_VERSION = 'version'
const customDimensionsNameIdMap = {
[METAMETRICS_CUSTOM_NETWORK]: 5,
[METAMETRICS_CUSTOM_ENVIRONMENT_TYPE]: 6,
[METAMETRICS_CUSTOM_ACTIVE_CURRENCY]: 7,
[METAMETRICS_CUSTOM_ACCOUNT_TYPE]: 8,
[METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS]: 9,
[METAMETRICS_CUSTOM_NUMBER_OF_TOKENS]: 10,
[METAMETRICS_CUSTOM_VERSION]: 11,
}
function composeUrlRefParamAddition (previousPath, confirmTransactionOrigin) {
const externalOrigin = confirmTransactionOrigin && confirmTransactionOrigin !== 'metamask'
return `&urlref=${externalOrigin ? 'EXTERNAL' : encodeURIComponent(previousPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}`
}
// composes query params of the form &dimension[0-999]=[value]
function composeCustomDimensionParamAddition (customDimensions) {
const customDimensionParamStrings = Object.keys(customDimensions).reduce((acc, name) => {
return [...acc, `dimension${customDimensionsNameIdMap[name]}=${customDimensions[name]}`]
}, [])
return `&${customDimensionParamStrings.join('&')}`
}
// composes query params in form: &cvar={[id]:[[name],[value]]}
// Example: &cvar={"1":["OS","iphone 5.0"],"2":["Matomo Mobile Version","1.6.2"],"3":["Locale","en::en"],"4":["Num Accounts","2"]}
function composeCustomVarParamAddition (customVariables) {
const customVariableIdValuePairs = Object.keys(customVariables).reduce((acc, name) => {
return {
[customVariableNameIdMap[name]]: [name, customVariables[name]],
...acc,
}
}, {})
return `&cvar=${encodeURIComponent(JSON.stringify(customVariableIdValuePairs))}`
}
function composeParamAddition (paramValue, paramName) {
return paramValue !== 0 && !paramValue
? ''
: `&${paramName}=${paramValue}`
}
/**
* @name composeUrl
* @param {Object} config - configuration object for composing the metametrics url
* @property {object} config.eventOpts Object containing event category, action and name descriptors
* @property {object} config.customVariables Object containing custom properties with values relevant to a specific event
* @property {object} config.pageOpts Objects containing information about a page/route the event is dispatched from
* @property {number} config.network The selected network of the user when the event occurs
* @property {string} config.environmentType The "environment" the user is using the app from: 'popup', 'notification' or 'fullscreen'
* @property {string} config.activeCurrency The current the user has select as their primary currency at the time of the event
* @property {string} config.accountType The account type being used at the time of the event: 'hardware', 'imported' or 'default'
* @property {number} config.numberOfTokens The number of tokens that the user has added at the time of the event
* @property {number} config.numberOfAccounts The number of accounts the user has added at the time of the event
* @property {string} config.previousPath The location path the user was on prior to the path they are on at the time of the event
* @property {string} config.currentPath The location path the user is on at the time of the event
* @property {string} config.metaMetricsId A random id assigned to a user at the time of opting in to metametrics. A hexadecimal number
* @property {string} config.confirmTransactionOrigin The origin on a transaction
* @property {boolean} config.excludeMetaMetricsId Whether or not the tracked event data should be associated with a metametrics id
* @property {boolean} config.isNewVisit Whether or not the event should be tracked as a new visit/user sessions
* @returns {string} - Returns a url to be passed to fetch to make the appropriate request to matomo.
* Example: https://chromeextensionmm.innocraft.cloud/piwik.php?idsite=1&rec=1&apiv=1&e_c=Navigation&e_a=Home&e_n=Clicked%20Send:%20Eth&urlref=http%3A%2F%2Fwww.metamask.io%2Fmetametrics%2Fhome.html%23send&dimension5=3&dimension6=fullscreen&dimension7=ETH&dimension8=default&dimension9=0&dimension10=3&url=http%3A%2F%2Fwww.metamask.io%2Fmetametrics%2Fhome.html%23&_id=49c10aff19795e9a&rand=7906028754863992&pv_id=53acad&uid=49c1
*/
function composeUrl (config) {
const {
eventOpts = {},
customVariables = '',
pageOpts = '',
network,
environmentType,
activeCurrency,
accountType,
numberOfTokens,
numberOfAccounts,
version,
previousPath = '',
currentPath,
metaMetricsId,
confirmTransactionOrigin,
excludeMetaMetricsId,
isNewVisit,
} = config
const base = METAMETRICS_BASE_FULL
const e_c = composeParamAddition(eventOpts.category, 'e_c')
const e_a = composeParamAddition(eventOpts.action, 'e_a')
const e_n = composeParamAddition(eventOpts.name, 'e_n')
const new_visit = isNewVisit ? `&new_visit=1` : ''
const cvar = (customVariables && composeCustomVarParamAddition(customVariables)) || ''
const action_name = ''
const urlref = previousPath && composeUrlRefParamAddition(previousPath, confirmTransactionOrigin)
const dimensions = !pageOpts.hideDimensions ? composeCustomDimensionParamAddition({
network,
environmentType,
activeCurrency,
accountType,
version,
numberOfTokens: (customVariables && customVariables.numberOfTokens) || numberOfTokens,
numberOfAccounts: (customVariables && customVariables.numberOfAccounts) || numberOfAccounts,
}) : ''
const url = currentPath ? `&url=${encodeURIComponent(currentPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}` : ''
const _id = metaMetricsId && !excludeMetaMetricsId ? `&_id=${metaMetricsId.slice(2, 18)}` : ''
const rand = `&rand=${String(Math.random()).slice(2)}`
const pv_id = currentPath ? `&pv_id=${ethUtil.bufferToHex(ethUtil.sha3(currentPath.match(/chrome-extension:\/\/\w+\/(.+)/)?.[0] || url)).slice(2, 8)}` : ''
const uid = metaMetricsId && !excludeMetaMetricsId
? `&uid=${metaMetricsId.slice(2, 18)}`
: excludeMetaMetricsId
? '&uid=0000000000000000'
: ''
return [ base, e_c, e_a, e_n, cvar, action_name, urlref, dimensions, url, _id, rand, pv_id, uid, new_visit ].join('')
}
export function sendMetaMetricsEvent (config) {
return window.fetch(composeUrl(config), {
'headers': {},
'method': 'GET',
})
}
export function verifyUserPermission (config, props) {
const {
eventOpts = {},
} = config
const { userPermissionPreferences } = props
const {
allowAll,
allowNone,
allowSendMetrics,
} = userPermissionPreferences
if (allowNone) {
return false
} else if (allowAll) {
return true
} else if (allowSendMetrics && eventOpts.name === 'send') {
return true
} else {
return false
}
}
const trackableSendCounts = {
1: true,
10: true,
30: true,
50: true,
100: true,
250: true,
500: true,
1000: true,
2500: true,
5000: true,
10000: true,
25000: true,
}
export function sendCountIsTrackable (sendCount) {
return Boolean(trackableSendCounts[sendCount])
}