mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Refactor background Segment usage (#9509)
* Create wrapper function for segment events * Extract transaction controller metrics calls into own function Co-authored-by: Mark Stacey <markjstacey@gmail.com>
This commit is contained in:
parent
4411f0eb9c
commit
30d6ad83f7
@ -22,7 +22,6 @@ import cleanErrorStack from '../../lib/cleanErrorStack'
|
|||||||
import { hexToBn, bnToHex, BnMultiplyByFraction } from '../../lib/util'
|
import { hexToBn, bnToHex, BnMultiplyByFraction } from '../../lib/util'
|
||||||
import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/app/helpers/constants/error-keys'
|
import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/app/helpers/constants/error-keys'
|
||||||
import { getSwapsTokensReceivedFromTxMeta } from '../../../../ui/app/pages/swaps/swaps.util'
|
import { getSwapsTokensReceivedFromTxMeta } from '../../../../ui/app/pages/swaps/swaps.util'
|
||||||
import { segment, METAMETRICS_ANONYMOUS_ID } from '../../lib/segment'
|
|
||||||
import TransactionStateManager from './tx-state-manager'
|
import TransactionStateManager from './tx-state-manager'
|
||||||
import TxGasUtil from './tx-gas-utils'
|
import TxGasUtil from './tx-gas-utils'
|
||||||
import PendingTransactionTracker from './pending-tx-tracker'
|
import PendingTransactionTracker from './pending-tx-tracker'
|
||||||
@ -77,7 +76,8 @@ export default class TransactionController extends EventEmitter {
|
|||||||
this.blockTracker = opts.blockTracker
|
this.blockTracker = opts.blockTracker
|
||||||
this.signEthTx = opts.signTransaction
|
this.signEthTx = opts.signTransaction
|
||||||
this.inProcessOfSigning = new Set()
|
this.inProcessOfSigning = new Set()
|
||||||
this.version = opts.version
|
this._trackSegmentEvent = opts.trackSegmentEvent
|
||||||
|
this._getParticipateInMetrics = opts.getParticipateInMetrics
|
||||||
|
|
||||||
this.memStore = new ObservableStore({})
|
this.memStore = new ObservableStore({})
|
||||||
this.query = new EthQuery(this.provider)
|
this.query = new EthQuery(this.provider)
|
||||||
@ -569,7 +569,6 @@ export default class TransactionController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// It seems that sometimes the numerical values being returned from
|
// It seems that sometimes the numerical values being returned from
|
||||||
// this.query.getTransactionReceipt are BN instances and not strings.
|
// this.query.getTransactionReceipt are BN instances and not strings.
|
||||||
const gasUsed = typeof txReceipt.gasUsed === 'string'
|
const gasUsed = typeof txReceipt.gasUsed === 'string'
|
||||||
@ -586,64 +585,8 @@ export default class TransactionController extends EventEmitter {
|
|||||||
txMeta.postTxBalance = postTxBalance.toString(16)
|
txMeta.postTxBalance = postTxBalance.toString(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txMeta.swapMetaData) {
|
|
||||||
let { version } = this
|
|
||||||
if (process.env.METAMASK_ENVIRONMENT !== 'production') {
|
|
||||||
version = `${version}-${process.env.METAMASK_ENVIRONMENT}`
|
|
||||||
}
|
|
||||||
const segmentContext = {
|
|
||||||
app: {
|
|
||||||
version,
|
|
||||||
name: 'MetaMask Extension',
|
|
||||||
},
|
|
||||||
locale: this.preferencesStore.getState().currentLocale.replace('_', '-'),
|
|
||||||
page: {
|
|
||||||
path: '/background-process',
|
|
||||||
title: 'Background Process',
|
|
||||||
url: '/background-process',
|
|
||||||
},
|
|
||||||
userAgent: window.navigator.userAgent,
|
|
||||||
}
|
|
||||||
|
|
||||||
const metametricsId = this.preferencesStore.getState().metaMetricsId
|
|
||||||
if (metametricsId && txMeta.swapMetaData && txReceipt.status !== '0x0') {
|
|
||||||
const tokensReceived = getSwapsTokensReceivedFromTxMeta(
|
|
||||||
txMeta.destinationTokenSymbol,
|
|
||||||
txMeta,
|
|
||||||
txMeta.destinationTokenAddress,
|
|
||||||
txMeta.txParams.from,
|
|
||||||
txMeta.destinationTokenDecimals,
|
|
||||||
)
|
|
||||||
const quoteVsExecutionRatio = `${(new BigNumber(tokensReceived, 10))
|
|
||||||
.div(txMeta.swapMetaData?.['token_to_amount'], 10).times(100).round(2)}%`
|
|
||||||
|
|
||||||
segment.track({ event: 'Swap Completed', userId: metametricsId, context: segmentContext, category: 'swaps' })
|
|
||||||
segment.track({
|
|
||||||
event: 'Swap Completed',
|
|
||||||
properties: {
|
|
||||||
...txMeta.swapMetaData,
|
|
||||||
token_to_amount_received: tokensReceived,
|
|
||||||
quote_vs_executionRatio: quoteVsExecutionRatio,
|
|
||||||
},
|
|
||||||
context: segmentContext,
|
|
||||||
anonymousId: METAMETRICS_ANONYMOUS_ID,
|
|
||||||
excludeMetaMetricsId: true,
|
|
||||||
category: 'swaps',
|
|
||||||
})
|
|
||||||
} else if (metametricsId && txMeta.swapMetaData) {
|
|
||||||
segment.track({ event: 'Swap Failed', userId: metametricsId, context: segmentContext, category: 'swaps' })
|
|
||||||
segment.track({
|
|
||||||
event: 'Swap Failed',
|
|
||||||
properties: { ...txMeta.swapMetaData },
|
|
||||||
anonymousId: METAMETRICS_ANONYMOUS_ID,
|
|
||||||
excludeMetaMetricsId: true,
|
|
||||||
context: segmentContext,
|
|
||||||
category: 'swaps',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.txStateManager.updateTx(txMeta, 'transactions#confirmTransaction - add txReceipt')
|
this.txStateManager.updateTx(txMeta, 'transactions#confirmTransaction - add txReceipt')
|
||||||
|
this._trackSwapsMetrics(txMeta)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error(err)
|
log.error(err)
|
||||||
}
|
}
|
||||||
@ -879,4 +822,53 @@ export default class TransactionController extends EventEmitter {
|
|||||||
this.memStore.updateState({ unapprovedTxs, currentNetworkTxList })
|
this.memStore.updateState({ unapprovedTxs, currentNetworkTxList })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_trackSwapsMetrics (txMeta) {
|
||||||
|
if (this._getParticipateInMetrics() && txMeta.swapMetaData) {
|
||||||
|
if (txMeta.txReceipt.status === '0x0') {
|
||||||
|
this._trackSegmentEvent({
|
||||||
|
event: 'Swap Failed',
|
||||||
|
category: 'swaps',
|
||||||
|
excludeMetaMetricsId: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
this._trackSegmentEvent({
|
||||||
|
event: 'Swap Failed',
|
||||||
|
properties: { ...txMeta.swapMetaData },
|
||||||
|
category: 'swaps',
|
||||||
|
excludeMetaMetricsId: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const tokensReceived = getSwapsTokensReceivedFromTxMeta(
|
||||||
|
txMeta.destinationTokenSymbol,
|
||||||
|
txMeta,
|
||||||
|
txMeta.destinationTokenAddress,
|
||||||
|
txMeta.txParams.from,
|
||||||
|
txMeta.destinationTokenDecimals,
|
||||||
|
)
|
||||||
|
|
||||||
|
const quoteVsExecutionRatio = `${
|
||||||
|
(new BigNumber(tokensReceived, 10))
|
||||||
|
.div(txMeta.swapMetaData.token_to_amount, 10)
|
||||||
|
.times(100)
|
||||||
|
.round(2)
|
||||||
|
}%`
|
||||||
|
|
||||||
|
this._trackSegmentEvent({
|
||||||
|
event: 'Swap Completed',
|
||||||
|
category: 'swaps',
|
||||||
|
excludeMetaMetricsId: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
this._trackSegmentEvent({
|
||||||
|
event: 'Swap Completed',
|
||||||
|
properties: {
|
||||||
|
...txMeta.swapMetaData,
|
||||||
|
token_to_amount_received: tokensReceived,
|
||||||
|
quote_vs_executionRatio: quoteVsExecutionRatio,
|
||||||
|
},
|
||||||
|
excludeMetaMetricsId: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ const inDevelopment = process.env.METAMASK_DEBUG || process.env.IN_TEST
|
|||||||
|
|
||||||
const flushAt = inDevelopment ? 1 : undefined
|
const flushAt = inDevelopment ? 1 : undefined
|
||||||
|
|
||||||
|
const METAMETRICS_ANONYMOUS_ID = '0x0000000000000000'
|
||||||
|
|
||||||
const segmentNoop = {
|
const segmentNoop = {
|
||||||
track () {
|
track () {
|
||||||
// noop
|
// noop
|
||||||
@ -20,8 +22,83 @@ const segmentNoop = {
|
|||||||
// provided a SEGMENT_WRITE_KEY. This also holds true for test environments and
|
// provided a SEGMENT_WRITE_KEY. This also holds true for test environments and
|
||||||
// E2E, which is handled in the build process by never providing the SEGMENT_WRITE_KEY
|
// E2E, which is handled in the build process by never providing the SEGMENT_WRITE_KEY
|
||||||
// which process.env.IN_TEST is true
|
// which process.env.IN_TEST is true
|
||||||
export const segment = process.env.SEGMENT_WRITE_KEY
|
const segment = process.env.SEGMENT_WRITE_KEY
|
||||||
? new Analytics(process.env.SEGMENT_WRITE_KEY, { flushAt })
|
? new Analytics(process.env.SEGMENT_WRITE_KEY, { flushAt })
|
||||||
: segmentNoop
|
: segmentNoop
|
||||||
|
|
||||||
export const METAMETRICS_ANONYMOUS_ID = '0x0000000000000000'
|
/**
|
||||||
|
* Returns a function for tracking Segment events.
|
||||||
|
*
|
||||||
|
* @param {string} metamaskVersion - The current version of the MetaMask
|
||||||
|
* extension.
|
||||||
|
* @param {Function} getParticipateInMetrics - A function that returns
|
||||||
|
* whether the user participates in MetaMetrics.
|
||||||
|
* @param {Function} getMetricsState - A function for getting state relevant
|
||||||
|
* to MetaMetrics/Segment.
|
||||||
|
*/
|
||||||
|
export function getTrackSegmentEvent (
|
||||||
|
metamaskVersion,
|
||||||
|
getParticipateInMetrics,
|
||||||
|
getMetricsState,
|
||||||
|
) {
|
||||||
|
const version = process.env.METAMASK_ENVIRONMENT === 'production'
|
||||||
|
? metamaskVersion
|
||||||
|
: `${metamaskVersion}-${process.env.METAMASK_ENVIRONMENT}`
|
||||||
|
|
||||||
|
const segmentContext = {
|
||||||
|
app: {
|
||||||
|
name: 'MetaMask Extension',
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
page: {
|
||||||
|
path: '/background-process',
|
||||||
|
title: 'Background Process',
|
||||||
|
url: '/background-process',
|
||||||
|
},
|
||||||
|
userAgent: window.navigator.userAgent,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks a Segment event per the given arguments.
|
||||||
|
*
|
||||||
|
* @param {string} event - The event name.
|
||||||
|
* @param {string} category - The event category.
|
||||||
|
* @param {Object} [properties] - The event properties.
|
||||||
|
* @param {boolean} [excludeMetaMetricsId] - `true` if the user's MetaMetrics id should
|
||||||
|
* not be included, and `false` otherwise. Default: `true`
|
||||||
|
*/
|
||||||
|
return function trackSegmentEvent ({
|
||||||
|
event,
|
||||||
|
category,
|
||||||
|
properties = {},
|
||||||
|
excludeMetaMetricsId = true,
|
||||||
|
}) {
|
||||||
|
if (!event || !category) {
|
||||||
|
throw new Error('Must specify event and category.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getParticipateInMetrics()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { currentLocale, metaMetricsId } = getMetricsState()
|
||||||
|
|
||||||
|
const trackOptions = {
|
||||||
|
event,
|
||||||
|
category,
|
||||||
|
context: {
|
||||||
|
...segmentContext,
|
||||||
|
locale: currentLocale.replace('_', '-'),
|
||||||
|
},
|
||||||
|
properties,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludeMetaMetricsId) {
|
||||||
|
trackOptions.anonymousId = METAMETRICS_ANONYMOUS_ID
|
||||||
|
} else {
|
||||||
|
trackOptions.userId = metaMetricsId
|
||||||
|
}
|
||||||
|
|
||||||
|
segment.track(trackOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -55,6 +55,7 @@ import getRestrictedMethods from './controllers/permissions/restrictedMethods'
|
|||||||
import nodeify from './lib/nodeify'
|
import nodeify from './lib/nodeify'
|
||||||
import accountImporter from './account-import-strategies'
|
import accountImporter from './account-import-strategies'
|
||||||
import seedPhraseVerifier from './lib/seed-phrase-verifier'
|
import seedPhraseVerifier from './lib/seed-phrase-verifier'
|
||||||
|
import { getTrackSegmentEvent } from './lib/segment'
|
||||||
|
|
||||||
import backgroundMetaMetricsEvent from './lib/background-metametrics'
|
import backgroundMetaMetricsEvent from './lib/background-metametrics'
|
||||||
|
|
||||||
@ -114,6 +115,19 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
migrateAddressBookState: this.migrateAddressBookState.bind(this),
|
migrateAddressBookState: this.migrateAddressBookState.bind(this),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This depends on preferences controller state
|
||||||
|
this.trackSegmentEvent = getTrackSegmentEvent(
|
||||||
|
this.platform.getVersion(),
|
||||||
|
() => this.preferencesController.getParticipateInMetaMetrics(),
|
||||||
|
() => {
|
||||||
|
const {
|
||||||
|
currentLocale,
|
||||||
|
metaMetricsId,
|
||||||
|
} = this.preferencesController.getState()
|
||||||
|
return { currentLocale, metaMetricsId }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
this.appStateController = new AppStateController({
|
this.appStateController = new AppStateController({
|
||||||
addUnlockListener: this.on.bind(this, 'unlock'),
|
addUnlockListener: this.on.bind(this, 'unlock'),
|
||||||
isUnlocked: this.isUnlocked.bind(this),
|
isUnlocked: this.isUnlocked.bind(this),
|
||||||
@ -239,7 +253,8 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
|
signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
blockTracker: this.blockTracker,
|
blockTracker: this.blockTracker,
|
||||||
version: this.platform.getVersion(),
|
trackSegmentEvent: this.trackSegmentEvent,
|
||||||
|
getParticipateInMetrics: () => this.preferencesController.getParticipateInMetaMetrics(),
|
||||||
})
|
})
|
||||||
this.txController.on('newUnapprovedTx', () => opts.showUnapprovedTx())
|
this.txController.on('newUnapprovedTx', () => opts.showUnapprovedTx())
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ describe('Transaction Controller', function () {
|
|||||||
}),
|
}),
|
||||||
getPermittedAccounts: () => undefined,
|
getPermittedAccounts: () => undefined,
|
||||||
getCurrentChainId: () => currentChainId,
|
getCurrentChainId: () => currentChainId,
|
||||||
|
getParticipateInMetrics: () => false,
|
||||||
})
|
})
|
||||||
txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
|
txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
|
||||||
})
|
})
|
||||||
@ -558,6 +559,7 @@ describe('Transaction Controller', function () {
|
|||||||
ethTx.sign(_fromAccount.key)
|
ethTx.sign(_fromAccount.key)
|
||||||
resolve()
|
resolve()
|
||||||
}),
|
}),
|
||||||
|
getParticipateInMetrics: () => false,
|
||||||
})
|
})
|
||||||
const result = await _txController._determineTransactionCategory({
|
const result = await _txController._determineTransactionCategory({
|
||||||
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
||||||
@ -590,6 +592,7 @@ describe('Transaction Controller', function () {
|
|||||||
ethTx.sign(_fromAccount.key)
|
ethTx.sign(_fromAccount.key)
|
||||||
resolve()
|
resolve()
|
||||||
}),
|
}),
|
||||||
|
getParticipateInMetrics: () => false,
|
||||||
})
|
})
|
||||||
const result = await _txController._determineTransactionCategory({
|
const result = await _txController._determineTransactionCategory({
|
||||||
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user