From fed9c86abb979916a0bfbb137c41bf302c0db81a Mon Sep 17 00:00:00 2001 From: ryanml Date: Mon, 21 Jun 2021 12:02:51 -0700 Subject: [PATCH 01/12] Adding gasEstimateType to 'Changed Gas Button' metrics event (#11352) Adding 'Changed Gas Button' metrics event --- .../send-gas-row/send-gas-row.component.js | 25 ++++++++----------- .../send-gas-row.component.test.js | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/ui/pages/send/send-content/send-gas-row/send-gas-row.component.js b/ui/pages/send/send-content/send-gas-row/send-gas-row.component.js index 9cbe21629..0ee7a6030 100644 --- a/ui/pages/send/send-content/send-gas-row/send-gas-row.component.js +++ b/ui/pages/send/send-content/send-gas-row/send-gas-row.component.js @@ -32,11 +32,11 @@ export default class SendGasRow extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; renderAdvancedOptionsButton() { - const { metricsEvent } = this.context; + const { trackEvent } = this.context; const { showCustomizeGasModal, isMainnet, @@ -54,12 +54,9 @@ export default class SendGasRow extends Component {
{ - metricsEvent({ - eventOpts: { - category: 'Transactions', - action: 'Edit Screen', - name: 'Clicked "Advanced Options"', - }, + trackEvent({ + category: 'Transactions', + event: 'Clicked "Advanced Options"', }); showCustomizeGasModal(); }} @@ -105,7 +102,7 @@ export default class SendGasRow extends Component { isEthGasPrice, noGasPrice, } = this.props; - const { metricsEvent } = this.context; + const { trackEvent } = this.context; const gasPriceFetchFailure = isEthGasPrice || noGasPrice; const gasPriceButtonGroup = ( @@ -115,11 +112,11 @@ export default class SendGasRow extends Component { showCheck={false} {...gasPriceButtonGroupProps} handleGasPriceSelection={async (opts) => { - metricsEvent({ - eventOpts: { - category: 'Transactions', - action: 'Edit Screen', - name: 'Changed Gas Button', + trackEvent({ + category: 'Transactions', + event: 'User Clicked Gas Estimate Button', + properties: { + gasEstimateType: opts.gasEstimateType.toLowerCase(), }, }); await gasPriceButtonGroupProps.handleGasPriceSelection(opts); diff --git a/ui/pages/send/send-content/send-gas-row/send-gas-row.component.test.js b/ui/pages/send/send-content/send-gas-row/send-gas-row.component.test.js index b6382f5bf..7f4505558 100644 --- a/ui/pages/send/send-content/send-gas-row/send-gas-row.component.test.js +++ b/ui/pages/send/send-content/send-gas-row/send-gas-row.component.test.js @@ -32,7 +32,7 @@ describe('SendGasRow Component', () => { anotherGasPriceButtonGroupProp: 'bar', }} />, - { context: { t: (str) => `${str}_t`, metricsEvent: () => ({}) } }, + { context: { t: (str) => `${str}_t`, trackEvent: () => ({}) } }, ); wrapper.setProps({ isMainnet: true }); }); From 077ee16ec2018227875130ba43501a92b613db20 Mon Sep 17 00:00:00 2001 From: ryanml Date: Mon, 21 Jun 2021 12:02:43 -0700 Subject: [PATCH 02/12] Add 'Transaction Added' metric event to TransactionController (#11341) --- app/scripts/controllers/transactions/index.js | 24 +++++ .../controllers/transactions/index.test.js | 90 +++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index fa7c46a2b..a67a71e4a 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -151,8 +151,32 @@ export default class TransactionController extends EventEmitter { @emits ${txMeta.id}:unapproved */ addTransaction(txMeta) { + const { + type, + status, + chainId, + origin: referrer, + txParams: { gasPrice }, + metamaskNetworkId: network, + } = txMeta; + const source = referrer === 'metamask' ? 'user' : 'dapp'; + this.txStateManager.addTransaction(txMeta); this.emit(`${txMeta.id}:unapproved`, txMeta); + + this._trackMetaMetricsEvent({ + event: 'Transaction Added', + category: 'Transactions', + sensitiveProperties: { + type, + status, + gasPrice, + referrer, + source, + network, + chain_id: chainId, + }, + }); } /** diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index 3615c33d0..d5cfe8de0 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -56,6 +56,7 @@ describe('Transaction Controller', function () { getPermittedAccounts: () => undefined, getCurrentChainId: () => currentChainId, getParticipateInMetrics: () => false, + trackMetaMetricsEvent: () => undefined, }); txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop }); @@ -414,6 +415,19 @@ describe('Transaction Controller', function () { }); describe('#addTransaction', function () { + let trackMetaMetricsEventSpy; + + beforeEach(function () { + trackMetaMetricsEventSpy = sinon.spy( + txController, + '_trackMetaMetricsEvent', + ); + }); + + afterEach(function () { + trackMetaMetricsEventSpy.restore(); + }); + it('should emit updates', function (done) { const txMeta = { id: '1', @@ -451,6 +465,82 @@ describe('Transaction Controller', function () { .catch(done); txController.addTransaction(txMeta); }); + + it('should call _trackMetaMetricsEvent with the correct payload (one)', function () { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: 'sentEther', + origin: 'metamask', + chainId: currentChainId, + metamaskNetworkId: currentNetworkId, + }; + const expectedPayload = { + event: 'Transaction Added', + category: 'Transactions', + sensitiveProperties: { + chain_id: '0x2a', + gasPrice: '0x77359400', + network: '42', + referrer: 'metamask', + source: 'user', + status: 'unapproved', + type: 'sentEther', + }, + }; + + txController.addTransaction(txMeta); + assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.deepEqual( + trackMetaMetricsEventSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('should call _trackMetaMetricsEvent with the correct payload (two)', function () { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: 'sentEther', + origin: 'other', + chainId: '0x3', + metamaskNetworkId: '3', + }; + const expectedPayload = { + event: 'Transaction Added', + category: 'Transactions', + sensitiveProperties: { + chain_id: '0x3', + gasPrice: '0x77359400', + network: '3', + referrer: 'other', + source: 'dapp', + status: 'unapproved', + type: 'sentEther', + }, + }; + + txController.addTransaction(txMeta); + assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.deepEqual( + trackMetaMetricsEventSpy.getCall(0).args[0], + expectedPayload, + ); + }); }); describe('#approveTransaction', function () { From 9a6b619740fa57c6236b5dd7101055cf46ac2cce Mon Sep 17 00:00:00 2001 From: ryanml Date: Thu, 24 Jun 2021 12:00:54 -0700 Subject: [PATCH 03/12] Adding metric events for Approved, Rejected, and Submitted to the TxController (#11358) --- app/scripts/controllers/transactions/index.js | 153 ++++++++--- .../controllers/transactions/index.test.js | 254 +++++++++++++----- 2 files changed, 311 insertions(+), 96 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index a67a71e4a..49a895ad5 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -33,6 +33,14 @@ const hstInterface = new ethers.utils.Interface(abi); const MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory +export const TRANSACTION_EVENTS = { + ADDED: 'Transaction Added', + APPROVED: 'Transaction Approved', + FINALIZED: 'Transaction Finalized', + REJECTED: 'Transaction Rejected', + SUBMITTED: 'Transaction Submitted', +}; + /** Transaction Controller is an aggregate of sub-controllers and trackers composing them in a way to be exposed to the metamask controller @@ -151,32 +159,9 @@ export default class TransactionController extends EventEmitter { @emits ${txMeta.id}:unapproved */ addTransaction(txMeta) { - const { - type, - status, - chainId, - origin: referrer, - txParams: { gasPrice }, - metamaskNetworkId: network, - } = txMeta; - const source = referrer === 'metamask' ? 'user' : 'dapp'; - this.txStateManager.addTransaction(txMeta); this.emit(`${txMeta.id}:unapproved`, txMeta); - - this._trackMetaMetricsEvent({ - event: 'Transaction Added', - category: 'Transactions', - sensitiveProperties: { - type, - status, - gasPrice, - referrer, - source, - network, - chain_id: chainId, - }, - }); + this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.ADDED); } /** @@ -556,12 +541,13 @@ export default class TransactionController extends EventEmitter { // sign transaction const rawTx = await this.signTransaction(txId); await this.publishTransaction(txId, rawTx); + this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.APPROVED); // must set transaction to submitted/failed before releasing lock nonceLock.releaseLock(); } catch (err) { // this is try-catch wrapped so that we can guarantee that the nonceLock is released try { - this.txStateManager.setTxStatusFailed(txId, err); + this._failTransaction(txId, err); } catch (err2) { log.error(err2); } @@ -639,6 +625,11 @@ export default class TransactionController extends EventEmitter { this.setTxHash(txId, txHash); this.txStateManager.setTxStatusSubmitted(txId); + + const { gas } = txMeta.txParams; + this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.SUBMITTED, { + gas_limit: gas, + }); } /** @@ -671,6 +662,29 @@ export default class TransactionController extends EventEmitter { this.txStateManager.setTxStatusConfirmed(txId); this._markNonceDuplicatesDropped(txId); + const { submittedTime } = txMeta; + const { blockNumber } = txReceipt; + const metricsParams = { gas_used: gasUsed }; + const completionTime = await this._getTransactionCompletionTime( + blockNumber, + submittedTime, + ); + + if (completionTime) { + metricsParams.completion_time = completionTime; + } + + if (txReceipt.status === '0x0') { + metricsParams.status = 'failed on-chain'; + // metricsParams.error = TODO: figure out a way to get the on-chain failure reason + } + + this._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.FINALIZED, + metricsParams, + ); + this.txStateManager.updateTransaction( txMeta, 'transactions#confirmTransaction - add txReceipt', @@ -704,7 +718,9 @@ export default class TransactionController extends EventEmitter { @returns {Promise} */ async cancelTransaction(txId) { + const txMeta = this.txStateManager.getTransaction(txId); this.txStateManager.setTxStatusRejected(txId); + this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.REJECTED); } /** @@ -787,7 +803,7 @@ export default class TransactionController extends EventEmitter { txMeta, 'failed to estimate gas during boot cleanup.', ); - this.txStateManager.setTxStatusFailed(txMeta.id, error); + this._failTransaction(txMeta.id, error); }); }); @@ -801,7 +817,7 @@ export default class TransactionController extends EventEmitter { const txSignError = new Error( 'Transaction found as "approved" during boot - possibly stuck during signing', ); - this.txStateManager.setTxStatusFailed(txMeta.id, txSignError); + this._failTransaction(txMeta.id, txSignError); }); } @@ -821,17 +837,15 @@ export default class TransactionController extends EventEmitter { 'transactions/pending-tx-tracker#event: tx:warning', ); }); - this.pendingTxTracker.on( - 'tx:failed', - this.txStateManager.setTxStatusFailed.bind(this.txStateManager), - ); + this.pendingTxTracker.on('tx:failed', (txId, error) => { + this._failTransaction(txId, error); + }); this.pendingTxTracker.on('tx:confirmed', (txId, transactionReceipt) => this.confirmTransaction(txId, transactionReceipt), ); - this.pendingTxTracker.on( - 'tx:dropped', - this.txStateManager.setTxStatusDropped.bind(this.txStateManager), - ); + this.pendingTxTracker.on('tx:dropped', (txId) => { + this._dropTransaction(txId); + }); this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => { if (!txMeta.firstRetryBlockNumber) { txMeta.firstRetryBlockNumber = latestBlockNumber; @@ -941,7 +955,7 @@ export default class TransactionController extends EventEmitter { txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce', ); - this.txStateManager.setTxStatusDropped(otherTxMeta.id); + this._dropTransaction(otherTxMeta.id); }); } @@ -1034,4 +1048,71 @@ export default class TransactionController extends EventEmitter { } } } + + /** + * Extracts relevant properties from a transaction meta + * object and uses them to create and send metrics for various transaction + * events. + * @param {Object} txMeta - the txMeta object + * @param {string} event - the name of the transaction event + * @param {Object} extraParams - optional props and values to include in sensitiveProperties + */ + _trackTransactionMetricsEvent(txMeta, event, extraParams = {}) { + const { + type, + time, + status, + chainId, + origin: referrer, + txParams: { gasPrice }, + metamaskNetworkId: network, + } = txMeta; + const source = referrer === 'metamask' ? 'user' : 'dapp'; + + this._trackMetaMetricsEvent({ + event, + category: 'Transactions', + sensitiveProperties: { + type, + status, + referrer, + source, + network, + chain_id: chainId, + gas_price: gasPrice, + first_seen: time, + ...extraParams, + }, + }); + } + + async _getTransactionCompletionTime(blockNumber, submittedTime) { + const transactionBlock = await this.query.getBlockByNumber( + blockNumber.toString(16), + false, + ); + + if (!transactionBlock) { + return ''; + } + + return new BigNumber(transactionBlock.timestamp, 10) + .minus(submittedTime / 1000) + .round() + .toString(10); + } + + _failTransaction(txId, error) { + this.txStateManager.setTxStatusFailed(txId, error); + const txMeta = this.txStateManager.getTransaction(txId); + this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED, { + error: error.message, + }); + } + + _dropTransaction(txId) { + this.txStateManager.setTxStatusDropped(txId); + const txMeta = this.txStateManager.getTransaction(txId); + this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED); + } } diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index d5cfe8de0..ace6342a9 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -15,7 +15,7 @@ import { } from '../../../../shared/constants/transaction'; import { SECOND } from '../../../../shared/constants/time'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; -import TransactionController from '.'; +import TransactionController, { TRANSACTION_EVENTS } from '.'; const noop = () => true; const currentNetworkId = '42'; @@ -415,17 +415,17 @@ describe('Transaction Controller', function () { }); describe('#addTransaction', function () { - let trackMetaMetricsEventSpy; + let trackTransactionMetricsEventSpy; beforeEach(function () { - trackMetaMetricsEventSpy = sinon.spy( + trackTransactionMetricsEventSpy = sinon.spy( txController, - '_trackMetaMetricsEvent', + '_trackTransactionMetricsEvent', ); }); afterEach(function () { - trackMetaMetricsEventSpy.restore(); + trackTransactionMetricsEventSpy.restore(); }); it('should emit updates', function (done) { @@ -466,7 +466,7 @@ describe('Transaction Controller', function () { txController.addTransaction(txMeta); }); - it('should call _trackMetaMetricsEvent with the correct payload (one)', function () { + it('should call _trackTransactionMetricsEvent with the correct params', function () { const txMeta = { id: 1, status: TRANSACTION_STATUSES.UNAPPROVED, @@ -480,65 +480,20 @@ describe('Transaction Controller', function () { type: 'sentEther', origin: 'metamask', chainId: currentChainId, + time: 1624408066355, metamaskNetworkId: currentNetworkId, }; - const expectedPayload = { - event: 'Transaction Added', - category: 'Transactions', - sensitiveProperties: { - chain_id: '0x2a', - gasPrice: '0x77359400', - network: '42', - referrer: 'metamask', - source: 'user', - status: 'unapproved', - type: 'sentEther', - }, - }; txController.addTransaction(txMeta); - assert.equal(trackMetaMetricsEventSpy.callCount, 1); + + assert.equal(trackTransactionMetricsEventSpy.callCount, 1); assert.deepEqual( - trackMetaMetricsEventSpy.getCall(0).args[0], - expectedPayload, + trackTransactionMetricsEventSpy.getCall(0).args[0], + txMeta, ); - }); - - it('should call _trackMetaMetricsEvent with the correct payload (two)', function () { - const txMeta = { - id: 1, - status: TRANSACTION_STATUSES.UNAPPROVED, - txParams: { - from: fromAccount.address, - to: '0x1678a085c290ebd122dc42cba69373b5953b831d', - gasPrice: '0x77359400', - gas: '0x7b0d', - nonce: '0x4b', - }, - type: 'sentEther', - origin: 'other', - chainId: '0x3', - metamaskNetworkId: '3', - }; - const expectedPayload = { - event: 'Transaction Added', - category: 'Transactions', - sensitiveProperties: { - chain_id: '0x3', - gasPrice: '0x77359400', - network: '3', - referrer: 'other', - source: 'dapp', - status: 'unapproved', - type: 'sentEther', - }, - }; - - txController.addTransaction(txMeta); - assert.equal(trackMetaMetricsEventSpy.callCount, 1); - assert.deepEqual( - trackMetaMetricsEventSpy.getCall(0).args[0], - expectedPayload, + assert.equal( + trackTransactionMetricsEventSpy.getCall(0).args[1], + TRANSACTION_EVENTS.ADDED, ); }); }); @@ -813,7 +768,8 @@ describe('Transaction Controller', function () { }); describe('#publishTransaction', function () { - let hash, txMeta; + let hash, txMeta, trackTransactionMetricsEventSpy; + beforeEach(function () { hash = '0x2a5523c6fa98b47b7d9b6c8320179785150b42a16bcff36b398c5062b65657e8'; @@ -821,12 +777,21 @@ describe('Transaction Controller', function () { id: 1, status: TRANSACTION_STATUSES.UNAPPROVED, txParams: { + gas: '0x7b0d', to: VALID_ADDRESS, from: VALID_ADDRESS_TWO, }, metamaskNetworkId: currentNetworkId, }; providerResultStub.eth_sendRawTransaction = hash; + trackTransactionMetricsEventSpy = sinon.spy( + txController, + '_trackTransactionMetricsEvent', + ); + }); + + afterEach(function () { + trackTransactionMetricsEventSpy.restore(); }); it('should publish a tx, updates the rawTx when provided a one', async function () { @@ -854,6 +819,25 @@ describe('Transaction Controller', function () { ); assert.equal(publishedTx.status, TRANSACTION_STATUSES.SUBMITTED); }); + + it('should call _trackTransactionMetricsEvent with the correct params', async function () { + const rawTx = + '0x477b2e6553c917af0db0388ae3da62965ff1a184558f61b749d1266b2e6d024c'; + txController.txStateManager.addTransaction(txMeta); + await txController.publishTransaction(txMeta.id, rawTx); + assert.equal(trackTransactionMetricsEventSpy.callCount, 1); + assert.deepEqual( + trackTransactionMetricsEventSpy.getCall(0).args[0], + txMeta, + ); + assert.equal( + trackTransactionMetricsEventSpy.getCall(0).args[1], + TRANSACTION_EVENTS.SUBMITTED, + ); + assert.deepEqual(trackTransactionMetricsEventSpy.getCall(0).args[2], { + gas_limit: txMeta.txParams.gas, + }); + }); }); describe('#_markNonceDuplicatesDropped', function () { @@ -1195,4 +1179,154 @@ describe('Transaction Controller', function () { ); }); }); + + describe('#_trackTransactionMetricsEvent', function () { + let trackMetaMetricsEventSpy; + + beforeEach(function () { + trackMetaMetricsEventSpy = sinon.spy( + txController, + '_trackMetaMetricsEvent', + ); + }); + + afterEach(function () { + trackMetaMetricsEventSpy.restore(); + }); + + it('should call _trackMetaMetricsEvent with the correct payload (user source)', function () { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: 'sentEther', + origin: 'metamask', + chainId: currentChainId, + time: 1624408066355, + metamaskNetworkId: currentNetworkId, + }; + const expectedPayload = { + event: 'Transaction Added', + category: 'Transactions', + sensitiveProperties: { + chain_id: '0x2a', + gas_price: '0x77359400', + first_seen: 1624408066355, + network: '42', + referrer: 'metamask', + source: 'user', + status: 'unapproved', + type: 'sentEther', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.ADDED, + ); + assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.deepEqual( + trackMetaMetricsEventSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('should call _trackMetaMetricsEvent with the correct payload (dapp source)', function () { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: 'sentEther', + origin: 'other', + chainId: currentChainId, + time: 1624408066355, + metamaskNetworkId: currentNetworkId, + }; + const expectedPayload = { + event: 'Transaction Added', + category: 'Transactions', + sensitiveProperties: { + chain_id: '0x2a', + gas_price: '0x77359400', + first_seen: 1624408066355, + network: '42', + referrer: 'other', + source: 'dapp', + status: 'unapproved', + type: 'sentEther', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.ADDED, + ); + assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.deepEqual( + trackMetaMetricsEventSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('should call _trackMetaMetricsEvent with the correct payload (extra params)', function () { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: 'sentEther', + origin: 'other', + chainId: currentChainId, + time: 1624408066355, + metamaskNetworkId: currentNetworkId, + }; + const expectedPayload = { + event: 'Transaction Added', + category: 'Transactions', + sensitiveProperties: { + baz: 3.0, + foo: 'bar', + chain_id: '0x2a', + gas_price: '0x77359400', + first_seen: 1624408066355, + network: '42', + referrer: 'other', + source: 'dapp', + status: 'unapproved', + type: 'sentEther', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.ADDED, + { + baz: 3.0, + foo: 'bar', + }, + ); + assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.deepEqual( + trackMetaMetricsEventSpy.getCall(0).args[0], + expectedPayload, + ); + }); + }); }); From b1f469d1e29fe0963a54632b12047b00720bed6f Mon Sep 17 00:00:00 2001 From: ryanml Date: Thu, 24 Jun 2021 15:37:44 -0700 Subject: [PATCH 04/12] Removing obsolete client-side transaction metrics events (#11329) * Removing metametrics send count tracking * Removing client side Transaction Completed and Canceled events --- .storybook/test-data.js | 1 - app/scripts/controllers/metametrics.js | 38 +------- app/scripts/controllers/metametrics.test.js | 65 ------------- app/scripts/lib/setupSentry.js | 1 - app/scripts/metamask-controller.js | 13 --- app/scripts/migrations/062.js | 28 ++++++ app/scripts/migrations/062.test.js | 80 ++++++++++++++++ app/scripts/migrations/index.js | 1 + test/e2e/fixtures/address-entry/state.json | 1 - test/e2e/fixtures/connected-state/state.json | 1 - test/e2e/fixtures/custom-rpc/state.json | 1 - test/e2e/fixtures/custom-token/state.json | 1 - test/e2e/fixtures/import-ui/state.json | 3 +- test/e2e/fixtures/imported-account/state.json | 1 - test/e2e/fixtures/localization/state.json | 1 - test/e2e/fixtures/metrics-enabled/state.json | 1 - test/e2e/fixtures/send-edit/state.json | 1 - test/e2e/fixtures/threebox-enabled/state.json | 3 +- ui/ducks/metamask/metamask.js | 7 -- .../confirm-transaction-base.component.js | 93 ++++--------------- .../confirm-transaction-base.container.js | 4 - ui/store/actionConstants.js | 1 - ui/store/actions.js | 21 ----- 23 files changed, 131 insertions(+), 236 deletions(-) create mode 100644 app/scripts/migrations/062.js create mode 100644 app/scripts/migrations/062.test.js diff --git a/.storybook/test-data.js b/.storybook/test-data.js index 32c24690f..18f384647 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -140,7 +140,6 @@ const state = { } }, "participateInMetaMetrics": true, - "metaMetricsSendCount": 2, "nextNonce": 71, "connectedStatusPopoverHasBeenShown": true, "swapsWelcomeMessageHasBeenShown": true, diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index c55a97c85..1539cbcba 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -7,30 +7,6 @@ import { METAMETRICS_BACKGROUND_PAGE_OBJECT, } from '../../../shared/constants/metametrics'; -/** - * Used to determine whether or not to attach a user's metametrics id - * to events that include on-chain data. This helps to prevent identifying - * a user by being able to trace their activity on etherscan/block exploring - */ -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]); -} - /** * @typedef {import('../../../shared/constants/metametrics').MetaMetricsContext} MetaMetricsContext * @typedef {import('../../../shared/constants/metametrics').MetaMetricsEventPayload} MetaMetricsEventPayload @@ -48,9 +24,6 @@ export function sendCountIsTrackable(sendCount) { * @property {?boolean} participateInMetaMetrics - The user's preference for * participating in the MetaMetrics analytics program. This setting controls * whether or not events are tracked - * @property {number} metaMetricsSendCount - How many send transactions have - * been tracked through this controller. Used to prevent attaching sensitive - * data that can be traced through on chain data. */ export default class MetaMetricsController { @@ -89,7 +62,6 @@ export default class MetaMetricsController { this.store = new ObservableStore({ participateInMetaMetrics: null, metaMetricsId: null, - metaMetricsSendCount: 0, ...initState, }); @@ -138,10 +110,6 @@ export default class MetaMetricsController { return this.store.getState(); } - setMetaMetricsSendCount(val) { - this.store.updateState({ metaMetricsSendCount: val }); - } - /** * Build the context object to attach to page and track events. * @private @@ -231,11 +199,7 @@ export default class MetaMetricsController { // to be updated to work with the new tracking plan. I think we should use // a config setting for this instead of trying to match the event name const isSendFlow = Boolean(payload.event.match(/^send|^confirm/iu)); - if ( - isSendFlow && - this.state.metaMetricsSendCount && - !sendCountIsTrackable(this.state.metaMetricsSendCount + 1) - ) { + if (isSendFlow) { excludeMetaMetricsId = true; } // If we are tracking sensitive data we will always use the anonymousId diff --git a/app/scripts/controllers/metametrics.test.js b/app/scripts/controllers/metametrics.test.js index e30b6ad5e..bb0d15bed 100644 --- a/app/scripts/controllers/metametrics.test.js +++ b/app/scripts/controllers/metametrics.test.js @@ -84,7 +84,6 @@ function getMockPreferencesStore({ currentLocale = LOCALE } = {}) { function getMetaMetricsController({ participateInMetaMetrics = true, metaMetricsId = TEST_META_METRICS_ID, - metaMetricsSendCount = 0, preferencesStore = getMockPreferencesStore(), networkController = getMockNetworkController(), } = {}) { @@ -106,7 +105,6 @@ function getMetaMetricsController({ initState: { participateInMetaMetrics, metaMetricsId, - metaMetricsSendCount, }, }); } @@ -198,14 +196,6 @@ describe('MetaMetricsController', function () { }); }); - describe('setMetaMetricsSendCount', function () { - it('should update the send count in state', function () { - const metaMetricsController = getMetaMetricsController(); - metaMetricsController.setMetaMetricsSendCount(1); - assert.equal(metaMetricsController.state.metaMetricsSendCount, 1); - }); - }); - describe('trackEvent', function () { it('should not track an event if user is not participating in metametrics', function () { const mock = sinon.mock(segment); @@ -337,61 +327,6 @@ describe('MetaMetricsController', function () { mock.verify(); }); - it('should use anonymousId when metametrics send count is not trackable in send flow', function () { - const mock = sinon.mock(segment); - const metaMetricsController = getMetaMetricsController({ - metaMetricsSendCount: 1, - }); - mock - .expects('track') - .once() - .withArgs({ - event: 'Send Fake Event', - anonymousId: METAMETRICS_ANONYMOUS_ID, - context: DEFAULT_TEST_CONTEXT, - properties: { - test: 1, - ...DEFAULT_EVENT_PROPERTIES, - }, - }); - metaMetricsController.trackEvent({ - event: 'Send Fake Event', - category: 'Unit Test', - properties: { - test: 1, - }, - }); - mock.verify(); - }); - - it('should use user metametrics id when metametrics send count is trackable in send flow', function () { - const mock = sinon.mock(segment); - const metaMetricsController = getMetaMetricsController(); - mock - .expects('track') - .once() - .withArgs({ - event: 'Send Fake Event', - userId: TEST_META_METRICS_ID, - context: DEFAULT_TEST_CONTEXT, - properties: { - test: 1, - ...DEFAULT_EVENT_PROPERTIES, - }, - }); - metaMetricsController.trackEvent( - { - event: 'Send Fake Event', - category: 'Unit Test', - properties: { - test: 1, - }, - }, - { metaMetricsSendCount: 0 }, - ); - mock.verify(); - }); - it('should immediately flush queue if flushImmediately set to true', async function () { const metaMetricsController = getMetaMetricsController(); const flushStub = sinon.stub(segment, 'flush'); diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index 25bbd7a46..56625b220 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -36,7 +36,6 @@ export const SENTRY_STATE = { isInitialized: true, isUnlocked: true, metaMetricsId: true, - metaMetricsSendCount: true, nativeCurrency: true, network: true, nextNonce: true, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index e2546a91f..31186aaa3 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -676,7 +676,6 @@ export default class MetamaskController extends EventEmitter { setUsePhishDetect: this.setUsePhishDetect.bind(this), setIpfsGateway: this.setIpfsGateway.bind(this), setParticipateInMetaMetrics: this.setParticipateInMetaMetrics.bind(this), - setMetaMetricsSendCount: this.setMetaMetricsSendCount.bind(this), setFirstTimeFlowType: this.setFirstTimeFlowType.bind(this), setCurrentLocale: this.setCurrentLocale.bind(this), markPasswordForgotten: this.markPasswordForgotten.bind(this), @@ -2760,18 +2759,6 @@ export default class MetamaskController extends EventEmitter { } } - setMetaMetricsSendCount(val, cb) { - try { - this.metaMetricsController.setMetaMetricsSendCount(val); - cb(null); - return; - } catch (err) { - cb(err); - // eslint-disable-next-line no-useless-return - return; - } - } - /** * Sets the type of first time flow the user wishes to follow: create or import * @param {string} type - Indicates the type of first time flow the user wishes to follow diff --git a/app/scripts/migrations/062.js b/app/scripts/migrations/062.js new file mode 100644 index 000000000..54b52b967 --- /dev/null +++ b/app/scripts/migrations/062.js @@ -0,0 +1,28 @@ +import { cloneDeep } from 'lodash'; + +const version = 62; + +/** + * Removes metaMetricsSendCount from MetaMetrics controller + */ +export default { + version, + async migrate(originalVersionedData) { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + const state = versionedData.data; + const newState = transformState(state); + versionedData.data = newState; + return versionedData; + }, +}; + +function transformState(state) { + if (state.MetaMetricsController) { + const { metaMetricsSendCount } = state.MetaMetricsController; + if (metaMetricsSendCount !== undefined) { + delete state.MetaMetricsController.metaMetricsSendCount; + } + } + return state; +} diff --git a/app/scripts/migrations/062.test.js b/app/scripts/migrations/062.test.js new file mode 100644 index 000000000..44229ac1d --- /dev/null +++ b/app/scripts/migrations/062.test.js @@ -0,0 +1,80 @@ +import { strict as assert } from 'assert'; +import migration62 from './062'; + +describe('migration #62', function () { + it('should update the version metadata', async function () { + const oldStorage = { + meta: { + version: 61, + }, + data: {}, + }; + + const newStorage = await migration62.migrate(oldStorage); + assert.deepEqual(newStorage.meta, { + version: 62, + }); + }); + + it('should remove metaMetricsSendCount from MetaMetricsController', async function () { + const oldStorage = { + meta: {}, + data: { + MetaMetricsController: { + metaMetricsSendCount: 1, + bar: 'baz', + }, + foo: 'bar', + }, + }; + + const newStorage = await migration62.migrate(oldStorage); + assert.deepStrictEqual(newStorage.data, { + MetaMetricsController: { + bar: 'baz', + }, + foo: 'bar', + }); + }); + + it('should remove metaMetricsSendCount from MetaMetricsController (falsey but defined)', async function () { + const oldStorage = { + meta: {}, + data: { + MetaMetricsController: { + metaMetricsSendCount: 0, + bar: 'baz', + }, + foo: 'bar', + }, + }; + + const newStorage = await migration62.migrate(oldStorage); + assert.deepStrictEqual(newStorage.data, { + MetaMetricsController: { + bar: 'baz', + }, + foo: 'bar', + }); + }); + + it('should not modify MetaMetricsController when metaMetricsSendCount is undefined', async function () { + const oldStorage = { + meta: {}, + data: { + MetaMetricsController: { + bar: 'baz', + }, + foo: 'bar', + }, + }; + + const newStorage = await migration62.migrate(oldStorage); + assert.deepStrictEqual(newStorage.data, { + MetaMetricsController: { + bar: 'baz', + }, + foo: 'bar', + }); + }); +}); diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 175ee0044..389b4ef4a 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -66,6 +66,7 @@ const migrations = [ require('./059').default, require('./060').default, require('./061').default, + require('./062').default, ]; export default migrations; diff --git a/test/e2e/fixtures/address-entry/state.json b/test/e2e/fixtures/address-entry/state.json index 0bc9f3b9d..c17fd7e0c 100644 --- a/test/e2e/fixtures/address-entry/state.json +++ b/test/e2e/fixtures/address-entry/state.json @@ -128,7 +128,6 @@ "knownMethodData": {}, "lostIdentities": {}, "metaMetricsId": null, - "metaMetricsSendCount": 0, "participateInMetaMetrics": false, "preferences": { "useNativeCurrencyAsPrimaryCurrency": true diff --git a/test/e2e/fixtures/connected-state/state.json b/test/e2e/fixtures/connected-state/state.json index baebea6b7..5d3d2f68e 100644 --- a/test/e2e/fixtures/connected-state/state.json +++ b/test/e2e/fixtures/connected-state/state.json @@ -138,7 +138,6 @@ }, "completedOnboarding": true, "metaMetricsId": null, - "metaMetricsSendCount": 0, "ipfsGateway": "dweb.link", "selectedAddress": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1" }, diff --git a/test/e2e/fixtures/custom-rpc/state.json b/test/e2e/fixtures/custom-rpc/state.json index aa93a938c..54d6bb4a8 100644 --- a/test/e2e/fixtures/custom-rpc/state.json +++ b/test/e2e/fixtures/custom-rpc/state.json @@ -129,7 +129,6 @@ "knownMethodData": {}, "lostIdentities": {}, "metaMetricsId": null, - "metaMetricsSendCount": 0, "participateInMetaMetrics": false, "preferences": { "useNativeCurrencyAsPrimaryCurrency": true diff --git a/test/e2e/fixtures/custom-token/state.json b/test/e2e/fixtures/custom-token/state.json index 1d3a24437..bd85436ad 100644 --- a/test/e2e/fixtures/custom-token/state.json +++ b/test/e2e/fixtures/custom-token/state.json @@ -121,7 +121,6 @@ "knownMethodData": {}, "lostIdentities": {}, "metaMetricsId": null, - "metaMetricsSendCount": 0, "participateInMetaMetrics": false, "preferences": { "useNativeCurrencyAsPrimaryCurrency": true diff --git a/test/e2e/fixtures/import-ui/state.json b/test/e2e/fixtures/import-ui/state.json index 1e6572574..758a85d66 100644 --- a/test/e2e/fixtures/import-ui/state.json +++ b/test/e2e/fixtures/import-ui/state.json @@ -125,8 +125,7 @@ }, "MetaMetricsController": { "participateInMetaMetrics": false, - "metaMetricsId": null, - "metaMetricsSendCount": 1 + "metaMetricsId": null }, "PermissionsController": { "permissionsRequests": [], diff --git a/test/e2e/fixtures/imported-account/state.json b/test/e2e/fixtures/imported-account/state.json index 7c4a2108a..407bbbd83 100644 --- a/test/e2e/fixtures/imported-account/state.json +++ b/test/e2e/fixtures/imported-account/state.json @@ -114,7 +114,6 @@ "knownMethodData": {}, "lostIdentities": {}, "metaMetricsId": null, - "metaMetricsSendCount": 0, "participateInMetaMetrics": false, "preferences": { "useNativeCurrencyAsPrimaryCurrency": true diff --git a/test/e2e/fixtures/localization/state.json b/test/e2e/fixtures/localization/state.json index 009e8ac77..73a3b97b2 100644 --- a/test/e2e/fixtures/localization/state.json +++ b/test/e2e/fixtures/localization/state.json @@ -114,7 +114,6 @@ "knownMethodData": {}, "lostIdentities": {}, "metaMetricsId": null, - "metaMetricsSendCount": 0, "participateInMetaMetrics": false, "preferences": { "showFiatInTestnets": true, diff --git a/test/e2e/fixtures/metrics-enabled/state.json b/test/e2e/fixtures/metrics-enabled/state.json index 8e0f082d5..67cbf9977 100644 --- a/test/e2e/fixtures/metrics-enabled/state.json +++ b/test/e2e/fixtures/metrics-enabled/state.json @@ -138,7 +138,6 @@ }, "completedOnboarding": true, "metaMetricsId": "fake-metrics-id", - "metaMetricsSendCount": 0, "ipfsGateway": "dweb.link", "selectedAddress": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1" }, diff --git a/test/e2e/fixtures/send-edit/state.json b/test/e2e/fixtures/send-edit/state.json index a5f3a8bab..6c9658c28 100644 --- a/test/e2e/fixtures/send-edit/state.json +++ b/test/e2e/fixtures/send-edit/state.json @@ -115,7 +115,6 @@ "knownMethodData": {}, "lostIdentities": {}, "metaMetricsId": null, - "metaMetricsSendCount": 0, "participateInMetaMetrics": false, "preferences": { "useNativeCurrencyAsPrimaryCurrency": true diff --git a/test/e2e/fixtures/threebox-enabled/state.json b/test/e2e/fixtures/threebox-enabled/state.json index f182c5d47..abad9782b 100644 --- a/test/e2e/fixtures/threebox-enabled/state.json +++ b/test/e2e/fixtures/threebox-enabled/state.json @@ -120,8 +120,7 @@ }, "MetaMetricsController": { "metaMetricsId": null, - "participateInMetaMetrics": false, - "metaMetricsSendCount": 0 + "participateInMetaMetrics": false }, "ThreeBoxController": { "threeBoxSyncingAllowed": true, diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 21a0476ea..0047b6904 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -33,7 +33,6 @@ export default function reduceMetamask(state = {}, action) { completedOnboarding: false, knownMethodData: {}, participateInMetaMetrics: null, - metaMetricsSendCount: 0, nextNonce: null, conversionRate: null, nativeCurrency: 'ETH', @@ -126,12 +125,6 @@ export default function reduceMetamask(state = {}, action) { participateInMetaMetrics: action.value, }; - case actionConstants.SET_METAMETRICS_SEND_COUNT: - return { - ...metamaskState, - metaMetricsSendCount: action.value, - }; - case actionConstants.SET_USE_BLOCKIE: return { ...metamaskState, diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index 8a8e9e47e..99544f587 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -86,8 +86,6 @@ export default class ConfirmTransactionBase extends Component { hideSubtitle: PropTypes.bool, identiconAddress: PropTypes.string, onEdit: PropTypes.func, - setMetaMetricsSendCount: PropTypes.func, - metaMetricsSendCount: PropTypes.number, subtitleComponent: PropTypes.node, title: PropTypes.string, advancedInlineGasShown: PropTypes.bool, @@ -475,35 +473,16 @@ export default class ConfirmTransactionBase extends Component { } handleCancel() { - const { metricsEvent } = this.context; const { txData, cancelTransaction, history, mostRecentOverviewPage, clearConfirmTransaction, - actionKey, - txData: { origin }, - methodData = {}, updateCustomNonce, } = this.props; this._removeBeforeUnload(); - metricsEvent({ - eventOpts: { - category: 'Transactions', - action: 'Confirm Screen', - name: 'Cancel', - }, - customVariables: { - recipientKnown: null, - functionType: - actionKey || - getMethodName(methodData.name) || - TRANSACTION_TYPES.CONTRACT_INTERACTION, - origin, - }, - }); updateCustomNonce(''); cancelTransaction(txData).then(() => { clearConfirmTransaction(); @@ -512,18 +491,12 @@ export default class ConfirmTransactionBase extends Component { } handleSubmit() { - const { metricsEvent } = this.context; const { - txData: { origin }, sendTransaction, clearConfirmTransaction, txData, history, - actionKey, mostRecentOverviewPage, - metaMetricsSendCount = 0, - setMetaMetricsSendCount, - methodData = {}, updateCustomNonce, } = this.props; const { submitting } = this.state; @@ -539,44 +512,27 @@ export default class ConfirmTransactionBase extends Component { }, () => { this._removeBeforeUnload(); - metricsEvent({ - eventOpts: { - category: 'Transactions', - action: 'Confirm Screen', - name: 'Transaction Completed', - }, - customVariables: { - recipientKnown: null, - functionType: - actionKey || - getMethodName(methodData.name) || - TRANSACTION_TYPES.CONTRACT_INTERACTION, - origin, - }, - }); - setMetaMetricsSendCount(metaMetricsSendCount + 1).then(() => { - sendTransaction(txData) - .then(() => { - clearConfirmTransaction(); - this.setState( - { - submitting: false, - }, - () => { - history.push(mostRecentOverviewPage); - updateCustomNonce(''); - }, - ); - }) - .catch((error) => { - this.setState({ + sendTransaction(txData) + .then(() => { + clearConfirmTransaction(); + this.setState( + { submitting: false, - submitError: error.message, - }); - updateCustomNonce(''); + }, + () => { + history.push(mostRecentOverviewPage); + updateCustomNonce(''); + }, + ); + }) + .catch((error) => { + this.setState({ + submitting: false, + submitError: error.message, }); - }); + updateCustomNonce(''); + }); }, ); } @@ -643,18 +599,7 @@ export default class ConfirmTransactionBase extends Component { } _beforeUnload = () => { - const { txData: { origin, id } = {}, cancelTransaction } = this.props; - const { metricsEvent } = this.context; - metricsEvent({ - eventOpts: { - category: 'Transactions', - action: 'Confirm Screen', - name: 'Cancel Tx Via Notification Close', - }, - customVariables: { - origin, - }, - }); + const { txData: { id } = {}, cancelTransaction } = this.props; cancelTransaction({ id }); }; diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index 6af7e49a9..711b12157 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -10,7 +10,6 @@ import { cancelTxs, updateAndApproveTx, showModal, - setMetaMetricsSendCount, updateTransaction, getNextNonce, tryReverseResolveAddress, @@ -76,7 +75,6 @@ const mapStateToProps = (state, ownProps) => { assetImages, network, unapprovedTxs, - metaMetricsSendCount, nextNonce, provider: { chainId }, } = metamask; @@ -186,7 +184,6 @@ const mapStateToProps = (state, ownProps) => { insufficientBalance, hideSubtitle: !isMainnet && !showFiatInTestnets, hideFiatConversion: !isMainnet && !showFiatInTestnets, - metaMetricsSendCount, type, nextNonce, mostRecentOverviewPage: getMostRecentOverviewPage(state), @@ -234,7 +231,6 @@ export const mapDispatchToProps = (dispatch) => { cancelAllTransactions: (txList) => dispatch(cancelTxs(txList)), sendTransaction: (txData) => dispatch(updateAndApproveTx(customNonceMerge(txData))), - setMetaMetricsSendCount: (val) => dispatch(setMetaMetricsSendCount(val)), getNextNonce: () => dispatch(getNextNonce()), setDefaultHomeActiveTabName: (tabName) => dispatch(setDefaultHomeActiveTabName(tabName)), diff --git a/ui/store/actionConstants.js b/ui/store/actionConstants.js index d16caa61f..ed0bee9af 100644 --- a/ui/store/actionConstants.js +++ b/ui/store/actionConstants.js @@ -60,7 +60,6 @@ export const UPDATE_CUSTOM_NONCE = 'UPDATE_CUSTOM_NONCE'; export const SET_IPFS_GATEWAY = 'SET_IPFS_GATEWAY'; export const SET_PARTICIPATE_IN_METAMETRICS = 'SET_PARTICIPATE_IN_METAMETRICS'; -export const SET_METAMETRICS_SEND_COUNT = 'SET_METAMETRICS_SEND_COUNT'; // locale export const SET_CURRENT_LOCALE = 'SET_CURRENT_LOCALE'; diff --git a/ui/store/actions.js b/ui/store/actions.js index f2269cdad..8dc7e9ee9 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -1968,27 +1968,6 @@ export function setParticipateInMetaMetrics(val) { }; } -export function setMetaMetricsSendCount(val) { - return (dispatch) => { - log.debug(`background.setMetaMetricsSendCount`); - return new Promise((resolve, reject) => { - background.setMetaMetricsSendCount(val, (err) => { - if (err) { - dispatch(displayWarning(err.message)); - reject(err); - return; - } - - dispatch({ - type: actionConstants.SET_METAMETRICS_SEND_COUNT, - value: val, - }); - resolve(val); - }); - }); - }; -} - export function setUseBlockie(val) { return (dispatch) => { dispatch(showLoadingIndication()); From a1e141fbe143cf8534f8fb378c30d2c0c08ae632 Mon Sep 17 00:00:00 2001 From: ryanml Date: Mon, 28 Jun 2021 15:38:20 -0700 Subject: [PATCH 05/12] Updating address error (#11389) --- app/scripts/controllers/preferences.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 19aef453e..7d7669804 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -285,7 +285,7 @@ export default class PreferencesController { */ syncAddresses(addresses) { if (!Array.isArray(addresses) || addresses.length === 0) { - throw new Error('Expected non-empty array of addresses.'); + throw new Error('Expected non-empty array of addresses. Error #11201'); } const { identities, lostIdentities } = this.store.getState(); From 6280b849adefed19e15fe9e391239a516b257b56 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Mon, 28 Jun 2021 10:23:17 -0500 Subject: [PATCH 06/12] Add methods to easily detect transaction type based on gas fields (#11382) --- shared/modules/transaction.utils.js | 34 ++++++++++ shared/modules/transaction.utils.test.js | 83 ++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 shared/modules/transaction.utils.test.js diff --git a/shared/modules/transaction.utils.js b/shared/modules/transaction.utils.js index 9e89679f8..092f8e465 100644 --- a/shared/modules/transaction.utils.js +++ b/shared/modules/transaction.utils.js @@ -1,6 +1,40 @@ +import { isHexString } from 'ethereumjs-util'; + export function transactionMatchesNetwork(transaction, chainId, networkId) { if (typeof transaction.chainId !== 'undefined') { return transaction.chainId === chainId; } return transaction.metamaskNetworkId === networkId; } + +/** + * Determines if the maxFeePerGas and maxPriorityFeePerGas fields are supplied + * and valid inputs. This will return false for non hex string inputs. + * @param {import("../constants/transaction").TransactionMeta} transaction - + * the transaction to check + * @returns {boolean} true if transaction uses valid EIP1559 fields + */ +export function isEIP1559Transaction(transaction) { + return ( + isHexString(transaction.txParams.maxFeePerGas) && + isHexString(transaction.txParams.maxPriorityFeePerGas) + ); +} + +/** + * Determine if the maxFeePerGas and maxPriorityFeePerGas fields are not + * supplied and that the gasPrice field is valid if it is provided. This will + * return false if gasPrice is a non hex string. + * @param {import("../constants/transaction").TransactionMeta} transaction - + * the transaction to check + * @returns {boolean} true if transaction uses valid Legacy fields OR lacks + * EIP1559 fields + */ +export function isLegacyTransaction(transaction) { + return ( + typeof transaction.txParams.maxFeePerGas === 'undefined' && + typeof transaction.txParams.maxPriorityFeePerGas === 'undefined' && + (typeof transaction.txParams.gasPrice === 'undefined' || + isHexString(transaction.txParams.gasPrice)) + ); +} diff --git a/shared/modules/transaction.utils.test.js b/shared/modules/transaction.utils.test.js new file mode 100644 index 000000000..3a333caa7 --- /dev/null +++ b/shared/modules/transaction.utils.test.js @@ -0,0 +1,83 @@ +import { isEIP1559Transaction, isLegacyTransaction } from './transaction.utils'; + +describe('Transaction.utils', function () { + describe('isEIP1559Transaction', function () { + it('should return true if both maxFeePerGas and maxPriorityFeePerGas are hex strings', () => { + expect( + isEIP1559Transaction({ + txParams: { maxFeePerGas: '0x1', maxPriorityFeePerGas: '0x1' }, + }), + ).toBe(true); + }); + + it('should return false if either maxFeePerGas and maxPriorityFeePerGas are non-hex strings', () => { + expect( + isEIP1559Transaction({ + txParams: { maxFeePerGas: 0, maxPriorityFeePerGas: '0x1' }, + }), + ).toBe(false); + expect( + isEIP1559Transaction({ + txParams: { maxFeePerGas: '0x1', maxPriorityFeePerGas: 'fail' }, + }), + ).toBe(false); + }); + + it('should return false if either maxFeePerGas or maxPriorityFeePerGas are not supplied', () => { + expect( + isEIP1559Transaction({ + txParams: { maxPriorityFeePerGas: '0x1' }, + }), + ).toBe(false); + expect( + isEIP1559Transaction({ + txParams: { maxFeePerGas: '0x1' }, + }), + ).toBe(false); + }); + }); + + describe('isLegacyTransaction', function () { + it('should return true if no gas related fields are supplied', () => { + expect( + isLegacyTransaction({ + txParams: {}, + }), + ).toBe(true); + }); + + it('should return true if gasPrice is solely provided', () => { + expect( + isLegacyTransaction({ + txParams: { gasPrice: '0x1' }, + }), + ).toBe(true); + }); + + it('should return false if gasPrice is not a hex string', () => { + expect( + isLegacyTransaction({ + txParams: { gasPrice: 100 }, + }), + ).toBe(false); + }); + + it('should return false if either maxFeePerGas or maxPriorityFeePerGas are supplied', () => { + expect( + isLegacyTransaction({ + txParams: { + maxFeePerGas: '0x1', + }, + }), + ).toBe(false); + + expect( + isLegacyTransaction({ + txParams: { + maxPriorityFeePerGas: 'any data', + }, + }), + ).toBe(false); + }); + }); +}); From 4e0bfbc463b8c3cd1a7073605dfdb428ddbce663 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Tue, 29 Jun 2021 16:54:42 -0500 Subject: [PATCH 07/12] add eip-1559 fields to event schema (#11408) * add eip-1559 fields to event schema * add gas_limit to all --- app/scripts/controllers/transactions/index.js | 23 +++++-- .../controllers/transactions/index.test.js | 63 ++++++++++++++++++- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 49a895ad5..95d5e533d 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -24,6 +24,7 @@ import { } from '../../../../shared/constants/transaction'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; import { GAS_LIMITS } from '../../../../shared/constants/gas'; +import { isEIP1559Transaction } from '../../../../shared/modules/transaction.utils'; import TransactionStateManager from './tx-state-manager'; import TxGasUtil from './tx-gas-utils'; import PendingTransactionTracker from './pending-tx-tracker'; @@ -626,10 +627,7 @@ export default class TransactionController extends EventEmitter { this.txStateManager.setTxStatusSubmitted(txId); - const { gas } = txMeta.txParams; - this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.SUBMITTED, { - gas_limit: gas, - }); + this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.SUBMITTED); } /** @@ -1064,11 +1062,20 @@ export default class TransactionController extends EventEmitter { status, chainId, origin: referrer, - txParams: { gasPrice }, + txParams: { gasPrice, gas: gasLimit, maxFeePerGas, maxPriorityFeePerGas }, metamaskNetworkId: network, } = txMeta; const source = referrer === 'metamask' ? 'user' : 'dapp'; + const gasParams = {}; + + if (isEIP1559Transaction(txMeta)) { + gasParams.max_fee_per_gas = maxFeePerGas; + gasParams.max_priority_fee_per_gas = maxPriorityFeePerGas; + } else { + gasParams.gas_price = gasPrice; + } + this._trackMetaMetricsEvent({ event, category: 'Transactions', @@ -1079,8 +1086,12 @@ export default class TransactionController extends EventEmitter { source, network, chain_id: chainId, - gas_price: gasPrice, + transaction_envelope_type: isEIP1559Transaction(txMeta) + ? 'fee-market' + : 'legacy', first_seen: time, + gas_limit: gasLimit, + ...gasParams, ...extraParams, }, }); diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index ace6342a9..d016a80e2 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -478,6 +478,7 @@ describe('Transaction Controller', function () { nonce: '0x4b', }, type: 'sentEther', + transaction_envelope_type: 'legacy', origin: 'metamask', chainId: currentChainId, time: 1624408066355, @@ -834,9 +835,6 @@ describe('Transaction Controller', function () { trackTransactionMetricsEventSpy.getCall(0).args[1], TRANSACTION_EVENTS.SUBMITTED, ); - assert.deepEqual(trackTransactionMetricsEventSpy.getCall(0).args[2], { - gas_limit: txMeta.txParams.gas, - }); }); }); @@ -1217,7 +1215,9 @@ describe('Transaction Controller', function () { sensitiveProperties: { chain_id: '0x2a', gas_price: '0x77359400', + gas_limit: '0x7b0d', first_seen: 1624408066355, + transaction_envelope_type: 'legacy', network: '42', referrer: 'metamask', source: 'user', @@ -1260,7 +1260,9 @@ describe('Transaction Controller', function () { sensitiveProperties: { chain_id: '0x2a', gas_price: '0x77359400', + gas_limit: '0x7b0d', first_seen: 1624408066355, + transaction_envelope_type: 'legacy', network: '42', referrer: 'other', source: 'dapp', @@ -1305,7 +1307,62 @@ describe('Transaction Controller', function () { foo: 'bar', chain_id: '0x2a', gas_price: '0x77359400', + gas_limit: '0x7b0d', first_seen: 1624408066355, + transaction_envelope_type: 'legacy', + network: '42', + referrer: 'other', + source: 'dapp', + status: 'unapproved', + type: 'sentEther', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.ADDED, + { + baz: 3.0, + foo: 'bar', + }, + ); + assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.deepEqual( + trackMetaMetricsEventSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('should call _trackMetaMetricsEvent with the correct payload (EIP-1559)', function () { + const txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + maxFeePerGas: '0x77359400', + maxPriorityFeePerGas: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: 'sentEther', + origin: 'other', + chainId: currentChainId, + time: 1624408066355, + metamaskNetworkId: currentNetworkId, + }; + const expectedPayload = { + event: 'Transaction Added', + category: 'Transactions', + sensitiveProperties: { + baz: 3.0, + foo: 'bar', + chain_id: '0x2a', + max_fee_per_gas: '0x77359400', + max_priority_fee_per_gas: '0x77359400', + gas_limit: '0x7b0d', + first_seen: 1624408066355, + transaction_envelope_type: 'fee-market', network: '42', referrer: 'other', source: 'dapp', From 810978c1ff49cf5328e9c239553d0f551e915c01 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Thu, 24 Jun 2021 14:52:29 -0500 Subject: [PATCH 08/12] use jest for testing shared (#11334) --- .eslintrc.js | 8 ++++++-- jest.config.js | 8 ++++++-- package.json | 6 +++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index fa67adaaf..56c2d4b86 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -108,7 +108,11 @@ module.exports = { }, { files: ['**/*.test.js'], - excludedFiles: ['ui/**/*.test.js', 'ui/__mocks__/*.js'], + excludedFiles: [ + 'ui/**/*.test.js', + 'ui/__mocks__/*.js', + 'shared/**/*.test.js', + ], extends: ['@metamask/eslint-config-mocha'], rules: { 'mocha/no-setup-in-describe': 'off', @@ -125,7 +129,7 @@ module.exports = { }, }, { - files: ['ui/**/*.test.js', 'ui/__mocks__/*.js'], + files: ['ui/**/*.test.js', 'ui/__mocks__/*.js', 'shared/**/*.test.js'], extends: ['@metamask/eslint-config-jest'], rules: { 'jest/no-restricted-matchers': 'off', diff --git a/jest.config.js b/jest.config.js index 6fce2ce4d..f236fbb90 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,7 +2,11 @@ module.exports = { restoreMocks: true, coverageDirectory: 'jest-coverage/', collectCoverageFrom: ['/ui/**/swaps/**'], - coveragePathIgnorePatterns: ['.stories.js', '.snap'], + coveragePathIgnorePatterns: [ + '.stories.js', + '.snap', + '**/shared/**/?(*.)+(test).js', + ], coverageThreshold: { global: { branches: 32.75, @@ -13,5 +17,5 @@ module.exports = { }, setupFiles: ['./test/setup.js', './test/env.js'], setupFilesAfterEnv: ['./test/jest/setup.js'], - testMatch: ['**/ui/**/?(*.)+(test).js'], + testMatch: ['ui/**/?(*.)+(test).js', '**/shared/**/?(*.)+(test).js'], }; diff --git a/package.json b/package.json index aea30bbba..3123ae48a 100644 --- a/package.json +++ b/package.json @@ -23,13 +23,13 @@ "dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'", "forwarder": "node ./development/static-server.js ./node_modules/@metamask/forwarder/dist/ --port 9010", "dapp-forwarder": "concurrently -k -n forwarder,dapp -p '[{time}][{name}]' 'yarn forwarder' 'yarn dapp'", - "test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive './{app,shared}/**/*.test.js'", + "test:unit": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/**/*.test.js'", "test:unit:global": "mocha --exit --require test/env.js --require test/setup.js --recursive test/unit-global/*.test.js", "test:unit:jest": "jest", "test:unit:jest:watch": "jest --watch", "test:unit:jest:watch:silent": "jest --watch --silent", "test:unit:jest:ci": "jest --maxWorkers=2", - "test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/controllers/permissions/*.test.js' --recursive './{app,shared}/**/*.test.js'", + "test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/controllers/permissions/*.test.js' --recursive './app/**/*.test.js'", "test:unit:strict": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/scripts/controllers/permissions/*.test.js'", "test:unit:path": "mocha --exit --require test/env.js --require test/setup.js --recursive", "test:e2e:chrome": "SELENIUM_BROWSER=chrome test/e2e/run-all.sh", @@ -53,7 +53,7 @@ "verify-locales": "node ./development/verify-locale-strings.js", "verify-locales:fix": "node ./development/verify-locale-strings.js --fix", "mozilla-lint": "addons-linter dist/firefox", - "watch": "mocha --watch --require test/env.js --require test/setup.js --reporter min --recursive \"test/unit/**/*.js\" \"ui/**/*.test.js\" \"shared/**/*.test.js\"", + "watch": "mocha --watch --require test/env.js --require test/setup.js --reporter min --recursive \"test/unit/**/*.js\" \"ui/**/*.test.js\"", "devtools:react": "react-devtools", "devtools:redux": "remotedev --hostname=localhost --port=8000", "start:dev": "concurrently -k -n build,react,redux yarn:start yarn:devtools:react yarn:devtools:redux", From f03882a3ca037942ccb8f5f22a3b41f17e6138f8 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Fri, 25 Jun 2021 16:31:01 -0500 Subject: [PATCH 09/12] fix jest tests to run ui tests (#11390) --- jest.config.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/jest.config.js b/jest.config.js index f236fbb90..8e215b994 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,11 +2,7 @@ module.exports = { restoreMocks: true, coverageDirectory: 'jest-coverage/', collectCoverageFrom: ['/ui/**/swaps/**'], - coveragePathIgnorePatterns: [ - '.stories.js', - '.snap', - '**/shared/**/?(*.)+(test).js', - ], + coveragePathIgnorePatterns: ['.stories.js', '.snap'], coverageThreshold: { global: { branches: 32.75, @@ -17,5 +13,8 @@ module.exports = { }, setupFiles: ['./test/setup.js', './test/env.js'], setupFilesAfterEnv: ['./test/jest/setup.js'], - testMatch: ['ui/**/?(*.)+(test).js', '**/shared/**/?(*.)+(test).js'], + testMatch: [ + '/ui/**/?(*.)+(test).js', + '/shared/**/?(*.)+(test).js', + ], }; From 520fbcdd033efaedac39f809b88a81eb240bf40c Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Wed, 30 Jun 2021 15:56:34 -0230 Subject: [PATCH 10/12] Fix gas-modal-page-container.container check for custom gas price safety (#11426) * Fix gas-modal-page-container.container check for custom gas price safety * Ensure gas price has been fetch before checking for price safety on testnets --- .../gas-modal-page-container.container.js | 13 +++++++++---- ui/selectors/custom-gas.js | 13 +++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index 9e70e3284..fc94176a0 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -41,6 +41,7 @@ import { getAveragePriceEstimateInHexWEI, isCustomPriceExcessive, getIsGasEstimatesFetched, + getIsCustomNetworkGasPriceFetched, } from '../../../../selectors'; import { @@ -141,13 +142,17 @@ const mapStateToProps = (state, ownProps) => { conversionRate, }); const isGasEstimate = getIsGasEstimatesFetched(state); + const customNetworkEstimateWasFetched = getIsCustomNetworkGasPriceFetched( + state, + ); - let customPriceIsSafe; + let customPriceIsSafe = true; if ((isMainnet || process.env.IN_TEST) && isGasEstimate) { customPriceIsSafe = isCustomPriceSafe(state); - } else if (isTestnet) { - customPriceIsSafe = true; - } else { + } else if ( + !(isMainnet || process.env.IN_TEST || isTestnet) && + customNetworkEstimateWasFetched + ) { customPriceIsSafe = isCustomPriceSafeForCustomNetwork(state); } diff --git a/ui/selectors/custom-gas.js b/ui/selectors/custom-gas.js index 4f905a7fe..4a3cbdf1e 100644 --- a/ui/selectors/custom-gas.js +++ b/ui/selectors/custom-gas.js @@ -109,6 +109,10 @@ export function isCustomPriceSafeForCustomNetwork(state) { return true; } + if (!estimatedPrice) { + return false; + } + const customPriceSafe = conversionGreaterThan( { value: customGasPrice, @@ -391,6 +395,15 @@ export function getIsEthGasPriceFetched(state) { ); } +export function getIsCustomNetworkGasPriceFetched(state) { + const gasState = state.gas; + return Boolean( + gasState.estimateSource === GAS_SOURCE.ETHGASPRICE && + gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.READY && + !getIsMainnet(state), + ); +} + export function getNoGasPriceFetched(state) { const gasState = state.gas; return Boolean(gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.FAILED); From 4b7a1ec91f714c40ebae6869b3b53de2d59777bc Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Wed, 30 Jun 2021 19:29:07 +0000 Subject: [PATCH 11/12] Version v9.7.1 --- CHANGELOG.md | 16 +++++++++++++++- package.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce028e641..3e5647066 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [9.7.1] +### Uncategorized +- Fix gas-modal-page-container.container check for custom gas price safety ([#11426](https://github.com/MetaMask/metamask-extension/pull/11426)) +- fix jest tests to run ui tests ([#11390](https://github.com/MetaMask/metamask-extension/pull/11390)) +- use jest for testing shared ([#11334](https://github.com/MetaMask/metamask-extension/pull/11334)) +- add eip-1559 fields to event schema ([#11408](https://github.com/MetaMask/metamask-extension/pull/11408)) +- Add methods to easily detect transaction type based on gas fields ([#11382](https://github.com/MetaMask/metamask-extension/pull/11382)) +- Updating address error ([#11389](https://github.com/MetaMask/metamask-extension/pull/11389)) +- Removing obsolete client-side transaction metrics events ([#11329](https://github.com/MetaMask/metamask-extension/pull/11329)) +- Adding metric events for Approved, Rejected, and Submitted to the TxController ([#11358](https://github.com/MetaMask/metamask-extension/pull/11358)) +- Add 'Transaction Added' metric event to TransactionController ([#11341](https://github.com/MetaMask/metamask-extension/pull/11341)) +- Adding gasEstimateType to 'Changed Gas Button' metrics event ([#11352](https://github.com/MetaMask/metamask-extension/pull/11352)) + ## [9.7.0] ### Added - [#11021](https://github.com/MetaMask/metamask-extension/pull/11021): Add periodic reminder modal for backing up recovery phrase @@ -2317,7 +2330,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Uncategorized - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v9.7.0...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v9.7.1...HEAD +[9.7.1]: https://github.com/MetaMask/metamask-extension/compare/v9.7.0...v9.7.1 [9.7.0]: https://github.com/MetaMask/metamask-extension/compare/v9.6.1...v9.7.0 [9.6.1]: https://github.com/MetaMask/metamask-extension/compare/v9.6.0...v9.6.1 [9.6.0]: https://github.com/MetaMask/metamask-extension/compare/v9.5.9...v9.6.0 diff --git a/package.json b/package.json index 3123ae48a..06bed064d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "9.7.0", + "version": "9.7.1", "private": true, "repository": { "type": "git", From da259e11a53dfe0b72130ccdecb989aa3b4ecea0 Mon Sep 17 00:00:00 2001 From: ryanml Date: Wed, 30 Jun 2021 12:46:23 -0700 Subject: [PATCH 12/12] [skip e2e] Update changelog for v9.7.1 (#11431) --- CHANGELOG.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e5647066..88b6e58c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,17 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ## [9.7.1] -### Uncategorized -- Fix gas-modal-page-container.container check for custom gas price safety ([#11426](https://github.com/MetaMask/metamask-extension/pull/11426)) -- fix jest tests to run ui tests ([#11390](https://github.com/MetaMask/metamask-extension/pull/11390)) -- use jest for testing shared ([#11334](https://github.com/MetaMask/metamask-extension/pull/11334)) -- add eip-1559 fields to event schema ([#11408](https://github.com/MetaMask/metamask-extension/pull/11408)) -- Add methods to easily detect transaction type based on gas fields ([#11382](https://github.com/MetaMask/metamask-extension/pull/11382)) -- Updating address error ([#11389](https://github.com/MetaMask/metamask-extension/pull/11389)) -- Removing obsolete client-side transaction metrics events ([#11329](https://github.com/MetaMask/metamask-extension/pull/11329)) -- Adding metric events for Approved, Rejected, and Submitted to the TxController ([#11358](https://github.com/MetaMask/metamask-extension/pull/11358)) -- Add 'Transaction Added' metric event to TransactionController ([#11341](https://github.com/MetaMask/metamask-extension/pull/11341)) -- Adding gasEstimateType to 'Changed Gas Button' metrics event ([#11352](https://github.com/MetaMask/metamask-extension/pull/11352)) +### Fixed +- [#11426](https://github.com/MetaMask/metamask-extension/pull/11426): Fixed bug that broke transaction speed up and cancel, when attempting those actions immediately after opening MetaMask ## [9.7.0] ### Added