Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add web3 usage metrics, prepare for web3 removal #9144

Merged
merged 3 commits into from
Aug 7, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/scripts/controllers/permissions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CapabilitiesController as RpcCap } from 'rpc-cap'
import { ethErrors } from 'eth-json-rpc-errors'
import { cloneDeep } from 'lodash'

import createMethodMiddleware from './methodMiddleware'
import createPermissionsMethodMiddleware from './permissionsMethodMiddleware'
import PermissionsLogController from './permissionsLog'

// Methods that do not require any permissions to use:
Expand Down Expand Up @@ -90,7 +90,7 @@ export class PermissionsController {

engine.push(this.permissionsLog.createMiddleware())

engine.push(createMethodMiddleware({
engine.push(createPermissionsMethodMiddleware({
addDomainMetadata: this.addDomainMetadata.bind(this),
getAccounts: this.getAccounts.bind(this, origin),
getUnlockPromise: () => this._getUnlockPromise(true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ethErrors } from 'eth-json-rpc-errors'
/**
* Create middleware for handling certain methods and preprocessing permissions requests.
*/
export default function createMethodMiddleware ({
export default function createPermissionsMethodMiddleware ({
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file and all references to its export were renamed to disambiguate it from the new top-level methodMiddleware.

addDomainMetadata,
getAccounts,
getUnlockPromise,
Expand Down
27 changes: 4 additions & 23 deletions app/scripts/inpage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/*global Web3*/

// need to make sure we aren't affected by overlapping namespaces
// and that we dont affect the app with our namespace
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
Expand Down Expand Up @@ -37,9 +35,7 @@ import LocalMessageDuplexStream from 'post-message-stream'
import { initProvider } from '@metamask/inpage-provider'

// TODO:deprecate:2020
import 'web3/dist/web3.min.js'

import setupDappAutoReload from './lib/auto-reload.js'
import setupWeb3 from './lib/setupWeb3.js'

restoreContextAfterImports()

Expand All @@ -59,11 +55,9 @@ initProvider({
connectionStream: metamaskStream,
})

//
// TODO:deprecate:2020
//
// Setup web3

// setup web3

if (typeof window.web3 !== 'undefined') {
throw new Error(`MetaMask detected another web3.
Expand All @@ -73,18 +67,5 @@ if (typeof window.web3 !== 'undefined') {
and try again.`)
}

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,
})

// setup dapp auto reload AND proxy web3
setupDappAutoReload(web3, window.ethereum._publicConfigStore)
// proxy web3, assign to window, and set up site auto reload
setupWeb3(log)
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getBackgroundMetaMetricState } from '../../../ui/app/selectors'
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 })

if (stateEventData.participateInMetaMetrics) {
sendMetaMetricsEvent({
...stateEventData,
...eventData,
category: 'Background',
currentPath: '/background',
})
}
Expand Down
32 changes: 32 additions & 0 deletions app/scripts/lib/createMethodMiddleware.js
Original file line number Diff line number Diff line change
@@ -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()
}
}
47 changes: 44 additions & 3 deletions app/scripts/lib/auto-reload.js → app/scripts/lib/setupWeb3.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,67 @@
/*global Web3*/

// TODO:deprecate:2020
// Delete this file

export default function setupDappAutoReload (web3, observable) {
import 'web3/dist/web3.min.js'

const shouldLogUsage = !([
'docs.metamask.io',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting - are these sites using web3?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, the test dapp definitely is. For the others, it might be useful to know if they're using the injected web3 instance though. That would be something we should fix.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I swear to verify this manually 😄

'metamask.github.io',
'metamask.io',
].includes(window.location.hostname))

export default function setupWeb3 (log) {
// export web3 as a global, checking for usage
let reloadInProgress = false
let lastTimeUsed
let lastSeenNetwork
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, {
get: (_web3, key) => {

// get the time of use
lastTimeUsed = Date.now()

// 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`)
hasBeenWarned = true
}

if (shouldLogUsage) {
window.ethereum.request({
method: 'metamask_logInjectedWeb3Usage',
params: [{ action: 'window.web3 get', name: key }],
})
}

// return value normally
return _web3[key]
},
set: (_web3, key, value) => {

if (shouldLogUsage) {
window.ethereum.request({
method: 'metamask_logInjectedWeb3Usage',
params: [{ action: 'window.web3 set', name: key }],
})
}

// set value normally
_web3[key] = value
},
Expand All @@ -33,7 +74,7 @@ export default function setupDappAutoReload (web3, observable) {
value: web3Proxy,
})

observable.subscribe(function (state) {
window.ethereum._publicConfigStore.subscribe((state) => {
// if the auto refresh on network change is false do not
// do anything
if (!window.ethereum.autoRefreshOnNetworkChange) {
Expand Down
42 changes: 29 additions & 13 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import createEngineStream from 'json-rpc-middleware-stream/engineStream'
import createFilterMiddleware from 'eth-json-rpc-filters'
import createSubscriptionManager from 'eth-json-rpc-filters/subscriptionManager'
import createLoggerMiddleware from './lib/createLoggerMiddleware'
import createMethodMiddleware from './lib/createMethodMiddleware'
import createOriginMiddleware from './lib/createOriginMiddleware'
import createTabIdMiddleware from './lib/createTabIdMiddleware'
import createOnboardingMiddleware from './lib/createOnboardingMiddleware'
Expand Down Expand Up @@ -66,7 +67,7 @@ import {
PhishingController,
} from '@metamask/controllers'

import backEndMetaMetricsEvent from './lib/backend-metametrics'
import backEndMetaMetricsEvent from './lib/background-metametrics'
Gudahtt marked this conversation as resolved.
Show resolved Hide resolved

export default class MetamaskController extends EventEmitter {

Expand Down Expand Up @@ -249,18 +250,11 @@ export default class MetamaskController extends EventEmitter {
this.platform.showTransactionNotification(txMeta)

const { txReceipt } = txMeta
const participateInMetaMetrics = this.preferencesController.getParticipateInMetaMetrics()
if (txReceipt && txReceipt.status === '0x0' && participateInMetaMetrics) {
const metamaskState = await this.getState()
backEndMetaMetricsEvent(metamaskState, {
customVariables: {
errorMessage: txMeta.simulationFails?.reason,
},
eventOpts: {
category: 'Background',
action: 'Transactions',
name: 'On Chain Failure',
},
if (txReceipt && txReceipt.status === '0x0') {
this.sendBackgroundMetaMetrics({
action: 'Transactions',
name: 'On Chain Failure',
customVariables: { errorMessage: txMeta.simulationFails?.reason },
})
}
}
Expand Down Expand Up @@ -1637,6 +1631,10 @@ export default class MetamaskController extends EventEmitter {
location,
registerOnboarding: this.onboardingController.registerOnboarding,
}))
engine.push(createMethodMiddleware({
origin,
sendMetrics: this.sendBackgroundMetaMetrics.bind(this),
}))
// filter and subscription polyfills
engine.push(filterMiddleware)
engine.push(subscriptionManager.middleware)
Expand Down Expand Up @@ -1837,6 +1835,24 @@ export default class MetamaskController extends EventEmitter {
return nonceLock.nextNonce
}

async sendBackgroundMetaMetrics ({ action, name, customVariables } = {}) {

if (!action || !name) {
throw new Error('Must provide action and name.')
}

if (this.preferencesController.getParticipateInMetaMetrics()) {
rekmarks marked this conversation as resolved.
Show resolved Hide resolved
const metamaskState = await this.getState()
backEndMetaMetricsEvent(metamaskState, {
customVariables,
eventOpts: {
action,
name,
},
})
}
}

//=============================================================================
// CONFIG
//=============================================================================
Expand Down
6 changes: 3 additions & 3 deletions ui/app/helpers/utils/metametrics.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ 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_REQUEST_ORIGIN = 'origin'
const METAMETRICS_CUSTOM_FROM_NETWORK = 'fromNetwork'
const METAMETRICS_CUSTOM_TO_NETWORK = 'toNetwork'
const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField'
Expand All @@ -36,7 +36,7 @@ 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_REQUEST_ORIGIN]: 3,
[METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4,
[METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5,

Expand Down Expand Up @@ -137,7 +137,7 @@ function composeUrl (config) {
numberOfAccounts,
version,
previousPath = '',
currentPath,
currentPath = '',
rekmarks marked this conversation as resolved.
Show resolved Hide resolved
metaMetricsId,
confirmTransactionOrigin,
excludeMetaMetricsId,
Expand Down