track rpc method usage (#14269)

feature/default_network_editable
Brad Decker 3 years ago committed by GitHub
parent 2d08fe35e5
commit d6df3700f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 86
      app/scripts/lib/createRPCMethodTrackingMiddleware.js
  2. 12
      app/scripts/metamask-controller.js
  3. 7
      shared/constants/metametrics.js

@ -0,0 +1,86 @@
import { EVENT_NAMES } from '../../../shared/constants/metametrics';
import { SECOND } from '../../../shared/constants/time';
const USER_PROMPTED_EVENT_NAME_MAP = {
eth_signTypedData_v4: EVENT_NAMES.SIGNATURE_REQUESTED,
eth_signTypedData_v3: EVENT_NAMES.SIGNATURE_REQUESTED,
eth_signTypedData: EVENT_NAMES.SIGNATURE_REQUESTED,
eth_personal_sign: EVENT_NAMES.SIGNATURE_REQUESTED,
eth_sign: EVENT_NAMES.SIGNATURE_REQUESTED,
eth_getEncryptionPublicKey: EVENT_NAMES.ENCRYPTION_PUBLIC_KEY_REQUESTED,
eth_decrypt: EVENT_NAMES.DECRYPTION_REQUESTED,
wallet_requestPermissions: EVENT_NAMES.PERMISSIONS_REQUESTED,
eth_requestAccounts: EVENT_NAMES.PERMISSIONS_REQUESTED,
};
const samplingTimeouts = {};
/**
* Returns a middleware that tracks inpage_provider usage using sampling for
* each type of event except those that require user interaction, such as
* signature requests
*
* @param {object} opts - options for the rpc method tracking middleware
* @param {Function} opts.trackEvent - trackEvent method from MetaMetricsController
* @param {Function} opts.getMetricsState - get the state of MetaMetricsController
* @returns {Function}
*/
export default function createRPCMethodTrackingMiddleware({
trackEvent,
getMetricsState,
}) {
return function rpcMethodTrackingMiddleware(
/** @type {any} */ req,
/** @type {any} */ res,
/** @type {Function} */ next,
) {
const startTime = Date.now();
const { origin } = req;
next((callback) => {
const endTime = Date.now();
if (!getMetricsState().participateInMetaMetrics) {
return callback();
}
if (USER_PROMPTED_EVENT_NAME_MAP[req.method]) {
const userRejected = res.error?.code === 4001;
trackEvent({
event: USER_PROMPTED_EVENT_NAME_MAP[req.method],
category: 'inpage_provider',
referrer: {
url: origin,
},
properties: {
method: req.method,
status: userRejected ? 'rejected' : 'approved',
error_code: res.error?.code,
error_message: res.error?.message,
has_result: typeof res.result !== 'undefined',
duration: endTime - startTime,
},
});
} else if (typeof samplingTimeouts[req.method] === 'undefined') {
trackEvent({
event: 'Provider Method Called',
category: 'inpage_provider',
referrer: {
url: origin,
},
properties: {
method: req.method,
error_code: res.error?.code,
error_message: res.error?.message,
has_result: typeof res.result !== 'undefined',
duration: endTime - startTime,
},
});
// Only record one call to this method every ten seconds to avoid
// overloading network requests.
samplingTimeouts[req.method] = setTimeout(() => {
delete samplingTimeouts[req.method];
}, SECOND * 10);
}
return callback();
});
};
}

@ -137,6 +137,7 @@ import {
buildSnapRestrictedMethodSpecifications, buildSnapRestrictedMethodSpecifications,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
} from './controllers/permissions'; } from './controllers/permissions';
import createRPCMethodTrackingMiddleware from './lib/createRPCMethodTrackingMiddleware';
///: BEGIN:ONLY_INCLUDE_IN(flask) ///: BEGIN:ONLY_INCLUDE_IN(flask)
import { getPlatform } from './lib/util'; import { getPlatform } from './lib/util';
@ -3330,6 +3331,17 @@ export default class MetamaskController extends EventEmitter {
engine.push(createLoggerMiddleware({ origin })); engine.push(createLoggerMiddleware({ origin }));
engine.push(this.permissionLogController.createMiddleware()); engine.push(this.permissionLogController.createMiddleware());
engine.push(
createRPCMethodTrackingMiddleware({
trackEvent: this.metaMetricsController.trackEvent.bind(
this.metaMetricsController,
),
getMetricsState: this.metaMetricsController.store.getState.bind(
this.metaMetricsController.store,
),
}),
);
// onboarding // onboarding
if (subjectType === SUBJECT_TYPES.WEBSITE) { if (subjectType === SUBJECT_TYPES.WEBSITE) {
engine.push( engine.push(

@ -213,3 +213,10 @@ export const METAMETRICS_BACKGROUND_PAGE_OBJECT = {
export const REJECT_NOTFICIATION_CLOSE = 'Cancel Via Notification Close'; export const REJECT_NOTFICIATION_CLOSE = 'Cancel Via Notification Close';
export const REJECT_NOTFICIATION_CLOSE_SIG = export const REJECT_NOTFICIATION_CLOSE_SIG =
'Cancel Sig Request Via Notification Close'; 'Cancel Sig Request Via Notification Close';
export const EVENT_NAMES = {
SIGNATURE_REQUESTED: 'Signature Requested',
ENCRYPTION_PUBLIC_KEY_REQUESTED: 'Encryption Public Key Requested',
DECRYPTION_REQUESTED: 'Decryption Requested',
PERMISSIONS_REQUESTED: 'Permissions Requested',
};

Loading…
Cancel
Save