Add web3 usage metrics, prepare for web3 removal (#9144)

* add web3 usage metrics

* move web3 metrics method to new middleware

* rename some methods, files, and exports
feature/default_network_editable
Erik Marks 4 years ago committed by Mark Stacey
parent f39498703e
commit 500dbf76cf
  1. 4
      app/scripts/controllers/permissions/index.js
  2. 2
      app/scripts/controllers/permissions/permissionsMethodMiddleware.js
  3. 27
      app/scripts/inpage.js
  4. 4
      app/scripts/lib/background-metametrics.js
  5. 32
      app/scripts/lib/createMethodMiddleware.js
  6. 47
      app/scripts/lib/setupWeb3.js
  7. 40
      app/scripts/metamask-controller.js
  8. 4
      ui/app/helpers/utils/metametrics.util.js

@ -7,7 +7,7 @@ import { CapabilitiesController as RpcCap } from 'rpc-cap'
import { ethErrors } from 'eth-json-rpc-errors' import { ethErrors } from 'eth-json-rpc-errors'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import createMethodMiddleware from './methodMiddleware' import createPermissionsMethodMiddleware from './permissionsMethodMiddleware'
import PermissionsLogController from './permissionsLog' import PermissionsLogController from './permissionsLog'
// Methods that do not require any permissions to use: // Methods that do not require any permissions to use:
@ -90,7 +90,7 @@ export class PermissionsController {
engine.push(this.permissionsLog.createMiddleware()) engine.push(this.permissionsLog.createMiddleware())
engine.push(createMethodMiddleware({ engine.push(createPermissionsMethodMiddleware({
addDomainMetadata: this.addDomainMetadata.bind(this), addDomainMetadata: this.addDomainMetadata.bind(this),
getAccounts: this.getAccounts.bind(this, origin), getAccounts: this.getAccounts.bind(this, origin),
getUnlockPromise: () => this._getUnlockPromise(true), getUnlockPromise: () => this._getUnlockPromise(true),

@ -4,7 +4,7 @@ import { ethErrors } from 'eth-json-rpc-errors'
/** /**
* Create middleware for handling certain methods and preprocessing permissions requests. * Create middleware for handling certain methods and preprocessing permissions requests.
*/ */
export default function createMethodMiddleware ({ export default function createPermissionsMethodMiddleware ({
addDomainMetadata, addDomainMetadata,
getAccounts, getAccounts,
getUnlockPromise, getUnlockPromise,

@ -1,5 +1,3 @@
/*global Web3*/
// need to make sure we aren't affected by overlapping namespaces // need to make sure we aren't affected by overlapping namespaces
// and that we dont affect the app with our namespace // and that we dont affect the app with our namespace
// mostly a fix for web3's BigNumber if AMD's "define" is defined... // mostly a fix for web3's BigNumber if AMD's "define" is defined...
@ -37,9 +35,7 @@ import LocalMessageDuplexStream from 'post-message-stream'
import { initProvider } from '@metamask/inpage-provider' import { initProvider } from '@metamask/inpage-provider'
// TODO:deprecate:2020 // TODO:deprecate:2020
import 'web3/dist/web3.min.js' import setupWeb3 from './lib/setupWeb3.js'
import setupDappAutoReload from './lib/auto-reload.js'
restoreContextAfterImports() restoreContextAfterImports()
@ -59,11 +55,9 @@ initProvider({
connectionStream: metamaskStream, connectionStream: metamaskStream,
}) })
//
// TODO:deprecate:2020 // TODO:deprecate:2020
// // Setup web3
// setup web3
if (typeof window.web3 !== 'undefined') { if (typeof window.web3 !== 'undefined') {
throw new Error(`MetaMask detected another web3. throw new Error(`MetaMask detected another web3.
@ -73,18 +67,5 @@ if (typeof window.web3 !== 'undefined') {
and try again.`) and try again.`)
} }
const web3 = new Web3(window.ethereum) // proxy web3, assign to window, and set up site auto reload
web3.setProvider = function () { setupWeb3(log)
log.debug('MetaMask - overrode web3.setProvider')
}
log.debug('MetaMask - injected web3')
Object.defineProperty(window.ethereum, '_web3Ref', {
enumerable: false,
writable: true,
configurable: true,
value: web3.eth,
})
// setup dapp auto reload AND proxy web3
setupDappAutoReload(web3, window.ethereum._publicConfigStore)

@ -1,13 +1,13 @@
import { getBackgroundMetaMetricState } from '../../../ui/app/selectors' import { getBackgroundMetaMetricState } from '../../../ui/app/selectors'
import { sendMetaMetricsEvent } from '../../../ui/app/helpers/utils/metametrics.util' import { sendMetaMetricsEvent } from '../../../ui/app/helpers/utils/metametrics.util'
export default function backEndMetaMetricsEvent (metaMaskState, eventData) { export default function backgroundMetaMetricsEvent (metaMaskState, eventData) {
const stateEventData = getBackgroundMetaMetricState({ metamask: metaMaskState }) const stateEventData = getBackgroundMetaMetricState({ metamask: metaMaskState })
if (stateEventData.participateInMetaMetrics) { if (stateEventData.participateInMetaMetrics) {
sendMetaMetricsEvent({ sendMetaMetricsEvent({
...stateEventData, ...stateEventData,
...eventData, ...eventData,
category: 'Background',
currentPath: '/background', currentPath: '/background',
}) })
} }

@ -0,0 +1,32 @@
/**
* Returns a middleware that implements the following RPC methods:
* - metamask_logInjectedWeb3Usage
*
* @param {Object} opts - The middleware options
* @param {string} opts.origin - The origin for the middleware stack
* @param {Function} opts.sendMetrics - A function for sending a metrics event
* @returns {(req: any, res: any, next: Function, end: Function) => void}
*/
export default function createMethodMiddleware ({ origin, sendMetrics }) {
return function methodMiddleware (req, res, next, end) {
switch (req.method) {
case 'metamask_logInjectedWeb3Usage':
const { action, name } = req.params[0]
sendMetrics({
action,
name,
customVariables: { origin },
})
res.result = true
break
default:
return next()
}
return end()
}
}

@ -1,26 +1,67 @@
/*global Web3*/
// TODO:deprecate:2020 // TODO:deprecate:2020
// Delete this file
export default function setupDappAutoReload (web3, observable) { import 'web3/dist/web3.min.js'
const shouldLogUsage = !([
'docs.metamask.io',
'metamask.github.io',
'metamask.io',
].includes(window.location.hostname))
export default function setupWeb3 (log) {
// export web3 as a global, checking for usage // export web3 as a global, checking for usage
let reloadInProgress = false let reloadInProgress = false
let lastTimeUsed let lastTimeUsed
let lastSeenNetwork let lastSeenNetwork
let hasBeenWarned = false let hasBeenWarned = false
const web3 = new Web3(window.ethereum)
web3.setProvider = function () {
log.debug('MetaMask - overrode web3.setProvider')
}
log.debug('MetaMask - injected web3')
Object.defineProperty(window.ethereum, '_web3Ref', {
enumerable: false,
writable: true,
configurable: true,
value: web3.eth,
})
const web3Proxy = new Proxy(web3, { const web3Proxy = new Proxy(web3, {
get: (_web3, key) => { get: (_web3, key) => {
// get the time of use // get the time of use
lastTimeUsed = Date.now() lastTimeUsed = Date.now()
// show warning once on web3 access // show warning once on web3 access
if (!hasBeenWarned && key !== 'currentProvider') { if (!hasBeenWarned) {
console.warn(`MetaMask: We will stop injecting web3 in Q4 2020.\nPlease see this article for more information: https://medium.com/metamask/no-longer-injecting-web3-js-4a899ad6e59e`) console.warn(`MetaMask: We will stop injecting web3 in Q4 2020.\nPlease see this article for more information: https://medium.com/metamask/no-longer-injecting-web3-js-4a899ad6e59e`)
hasBeenWarned = true hasBeenWarned = true
} }
if (shouldLogUsage) {
window.ethereum.request({
method: 'metamask_logInjectedWeb3Usage',
params: [{ action: 'window.web3 get', name: key }],
})
}
// return value normally // return value normally
return _web3[key] return _web3[key]
}, },
set: (_web3, key, value) => { set: (_web3, key, value) => {
if (shouldLogUsage) {
window.ethereum.request({
method: 'metamask_logInjectedWeb3Usage',
params: [{ action: 'window.web3 set', name: key }],
})
}
// set value normally // set value normally
_web3[key] = value _web3[key] = value
}, },
@ -33,7 +74,7 @@ export default function setupDappAutoReload (web3, observable) {
value: web3Proxy, value: web3Proxy,
}) })
observable.subscribe(function (state) { window.ethereum._publicConfigStore.subscribe((state) => {
// if the auto refresh on network change is false do not // if the auto refresh on network change is false do not
// do anything // do anything
if (!window.ethereum.autoRefreshOnNetworkChange) { if (!window.ethereum.autoRefreshOnNetworkChange) {

@ -19,6 +19,7 @@ import createEngineStream from 'json-rpc-middleware-stream/engineStream'
import createFilterMiddleware from 'eth-json-rpc-filters' import createFilterMiddleware from 'eth-json-rpc-filters'
import createSubscriptionManager from 'eth-json-rpc-filters/subscriptionManager' import createSubscriptionManager from 'eth-json-rpc-filters/subscriptionManager'
import createLoggerMiddleware from './lib/createLoggerMiddleware' import createLoggerMiddleware from './lib/createLoggerMiddleware'
import createMethodMiddleware from './lib/createMethodMiddleware'
import createOriginMiddleware from './lib/createOriginMiddleware' import createOriginMiddleware from './lib/createOriginMiddleware'
import createTabIdMiddleware from './lib/createTabIdMiddleware' import createTabIdMiddleware from './lib/createTabIdMiddleware'
import createOnboardingMiddleware from './lib/createOnboardingMiddleware' import createOnboardingMiddleware from './lib/createOnboardingMiddleware'
@ -66,7 +67,7 @@ import {
PhishingController, PhishingController,
} from '@metamask/controllers' } from '@metamask/controllers'
import backEndMetaMetricsEvent from './lib/backend-metametrics' import backgroundMetaMetricsEvent from './lib/background-metametrics'
export default class MetamaskController extends EventEmitter { export default class MetamaskController extends EventEmitter {
@ -249,18 +250,11 @@ export default class MetamaskController extends EventEmitter {
this.platform.showTransactionNotification(txMeta) this.platform.showTransactionNotification(txMeta)
const { txReceipt } = txMeta const { txReceipt } = txMeta
const participateInMetaMetrics = this.preferencesController.getParticipateInMetaMetrics() if (txReceipt && txReceipt.status === '0x0') {
if (txReceipt && txReceipt.status === '0x0' && participateInMetaMetrics) { this.sendBackgroundMetaMetrics({
const metamaskState = await this.getState() action: 'Transactions',
backEndMetaMetricsEvent(metamaskState, { name: 'On Chain Failure',
customVariables: { customVariables: { errorMessage: txMeta.simulationFails?.reason },
errorMessage: txMeta.simulationFails?.reason,
},
eventOpts: {
category: 'Background',
action: 'Transactions',
name: 'On Chain Failure',
},
}) })
} }
} }
@ -1637,6 +1631,10 @@ export default class MetamaskController extends EventEmitter {
location, location,
registerOnboarding: this.onboardingController.registerOnboarding, registerOnboarding: this.onboardingController.registerOnboarding,
})) }))
engine.push(createMethodMiddleware({
origin,
sendMetrics: this.sendBackgroundMetaMetrics.bind(this),
}))
// filter and subscription polyfills // filter and subscription polyfills
engine.push(filterMiddleware) engine.push(filterMiddleware)
engine.push(subscriptionManager.middleware) engine.push(subscriptionManager.middleware)
@ -1837,6 +1835,22 @@ export default class MetamaskController extends EventEmitter {
return nonceLock.nextNonce return nonceLock.nextNonce
} }
async sendBackgroundMetaMetrics ({ action, name, customVariables } = {}) {
if (!action || !name) {
throw new Error('Must provide action and name.')
}
const metamaskState = await this.getState()
backgroundMetaMetricsEvent(metamaskState, {
customVariables,
eventOpts: {
action,
name,
},
})
}
//============================================================================= //=============================================================================
// CONFIG // CONFIG
//============================================================================= //=============================================================================

@ -23,7 +23,7 @@ const METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE = 'gasLimitChange'
const METAMETRICS_CUSTOM_GAS_PRICE_CHANGE = 'gasPriceChange' const METAMETRICS_CUSTOM_GAS_PRICE_CHANGE = 'gasPriceChange'
const METAMETRICS_CUSTOM_FUNCTION_TYPE = 'functionType' const METAMETRICS_CUSTOM_FUNCTION_TYPE = 'functionType'
const METAMETRICS_CUSTOM_RECIPIENT_KNOWN = 'recipientKnown' const METAMETRICS_CUSTOM_RECIPIENT_KNOWN = 'recipientKnown'
const METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN = 'origin' const METAMETRICS_REQUEST_ORIGIN = 'origin'
const METAMETRICS_CUSTOM_FROM_NETWORK = 'fromNetwork' const METAMETRICS_CUSTOM_FROM_NETWORK = 'fromNetwork'
const METAMETRICS_CUSTOM_TO_NETWORK = 'toNetwork' const METAMETRICS_CUSTOM_TO_NETWORK = 'toNetwork'
const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField' const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField'
@ -36,7 +36,7 @@ const METAMETRICS_CUSTOM_ASSET_SELECTED = 'assetSelected'
const customVariableNameIdMap = { const customVariableNameIdMap = {
[METAMETRICS_CUSTOM_FUNCTION_TYPE]: 1, [METAMETRICS_CUSTOM_FUNCTION_TYPE]: 1,
[METAMETRICS_CUSTOM_RECIPIENT_KNOWN]: 2, [METAMETRICS_CUSTOM_RECIPIENT_KNOWN]: 2,
[METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN]: 3, [METAMETRICS_REQUEST_ORIGIN]: 3,
[METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4, [METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4,
[METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5, [METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5,

Loading…
Cancel
Save