1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Make metrics related actions idempotent (#15737)

This commit is contained in:
Jyoti Puri 2022-09-16 22:34:14 +05:30 committed by GitHub
parent f465089c2a
commit c2b7690119
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 170 additions and 54 deletions

View File

@ -188,6 +188,15 @@ export default class MetaMetricsController {
}`, }`,
); );
} }
const existingFragment = this.getExistingEventFragment(
options.actionId,
options.uniqueIdentifier,
);
if (existingFragment) {
return existingFragment;
}
const { fragments } = this.store.getState(); const { fragments } = this.store.getState();
const id = options.uniqueIdentifier ?? uuidv4(); const id = options.uniqueIdentifier ?? uuidv4();
@ -236,6 +245,26 @@ export default class MetaMetricsController {
return fragment; return fragment;
} }
/**
* Returns the fragment stored in memory with provided id or undefined if it
* does not exist.
*
* @param {string} actionId - actionId passed from UI
* @param {string} uniqueIdentifier - uniqueIdentifier of the event
* @returns {[MetaMetricsEventFragment]}
*/
getExistingEventFragment(actionId, uniqueIdentifier) {
const { fragments } = this.store.getState();
const existingFragment = Object.values(fragments).find(
(fragment) =>
fragment.actionId === actionId &&
fragment.uniqueIdentifier === uniqueIdentifier,
);
return existingFragment;
}
/** /**
* Updates an event fragment in state * Updates an event fragment in state
* *

View File

@ -301,7 +301,11 @@ export default class TransactionController extends EventEmitter {
addTransaction(txMeta) { addTransaction(txMeta) {
this.txStateManager.addTransaction(txMeta); this.txStateManager.addTransaction(txMeta);
this.emit(`${txMeta.id}:unapproved`, txMeta); this.emit(`${txMeta.id}:unapproved`, txMeta);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.ADDED); this._trackTransactionMetricsEvent(
txMeta,
TRANSACTION_EVENTS.ADDED,
txMeta.actionId,
);
} }
/** /**
@ -1216,7 +1220,7 @@ export default class TransactionController extends EventEmitter {
} }
this.addTransaction(newTxMeta); this.addTransaction(newTxMeta);
await this.approveTransaction(newTxMeta.id); await this.approveTransaction(newTxMeta.id, actionId);
return newTxMeta; return newTxMeta;
} }
@ -1274,7 +1278,7 @@ export default class TransactionController extends EventEmitter {
} }
this.addTransaction(newTxMeta); this.addTransaction(newTxMeta);
await this.approveTransaction(newTxMeta.id); await this.approveTransaction(newTxMeta.id, actionId);
return newTxMeta; return newTxMeta;
} }
@ -1294,13 +1298,14 @@ export default class TransactionController extends EventEmitter {
* updates and approves the transaction * updates and approves the transaction
* *
* @param {object} txMeta * @param {object} txMeta
* @param {string} actionId
*/ */
async updateAndApproveTransaction(txMeta) { async updateAndApproveTransaction(txMeta, actionId) {
this.txStateManager.updateTransaction( this.txStateManager.updateTransaction(
txMeta, txMeta,
'confTx: user approved transaction', 'confTx: user approved transaction',
); );
await this.approveTransaction(txMeta.id); await this.approveTransaction(txMeta.id, actionId);
} }
/** /**
@ -1311,8 +1316,9 @@ export default class TransactionController extends EventEmitter {
* if any of these steps fails the tx status will be set to failed * if any of these steps fails the tx status will be set to failed
* *
* @param {number} txId - the tx's Id * @param {number} txId - the tx's Id
* @param {string} actionId - actionId passed from UI
*/ */
async approveTransaction(txId) { async approveTransaction(txId, actionId) {
// TODO: Move this safety out of this function. // TODO: Move this safety out of this function.
// Since this transaction is async, // Since this transaction is async,
// we need to keep track of what is currently being signed, // we need to keep track of what is currently being signed,
@ -1354,14 +1360,18 @@ export default class TransactionController extends EventEmitter {
); );
// sign transaction // sign transaction
const rawTx = await this.signTransaction(txId); const rawTx = await this.signTransaction(txId);
await this.publishTransaction(txId, rawTx); await this.publishTransaction(txId, rawTx, actionId);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.APPROVED); this._trackTransactionMetricsEvent(
txMeta,
TRANSACTION_EVENTS.APPROVED,
actionId,
);
// must set transaction to submitted/failed before releasing lock // must set transaction to submitted/failed before releasing lock
nonceLock.releaseLock(); nonceLock.releaseLock();
} catch (err) { } catch (err) {
// this is try-catch wrapped so that we can guarantee that the nonceLock is released // this is try-catch wrapped so that we can guarantee that the nonceLock is released
try { try {
this._failTransaction(txId, err); this._failTransaction(txId, err, actionId);
} catch (err2) { } catch (err2) {
log.error(err2); log.error(err2);
} }
@ -1490,8 +1500,9 @@ export default class TransactionController extends EventEmitter {
* @param {number} txId - the tx's Id * @param {number} txId - the tx's Id
* @param {string} rawTx - the hex string of the serialized signed transaction * @param {string} rawTx - the hex string of the serialized signed transaction
* @returns {Promise<void>} * @returns {Promise<void>}
* @param {number} actionId - actionId passed from UI
*/ */
async publishTransaction(txId, rawTx) { async publishTransaction(txId, rawTx, actionId) {
const txMeta = this.txStateManager.getTransaction(txId); const txMeta = this.txStateManager.getTransaction(txId);
txMeta.rawTx = rawTx; txMeta.rawTx = rawTx;
if (txMeta.type === TRANSACTION_TYPES.SWAP) { if (txMeta.type === TRANSACTION_TYPES.SWAP) {
@ -1517,7 +1528,11 @@ export default class TransactionController extends EventEmitter {
this.txStateManager.setTxStatusSubmitted(txId); this.txStateManager.setTxStatusSubmitted(txId);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.SUBMITTED); this._trackTransactionMetricsEvent(
txMeta,
TRANSACTION_EVENTS.SUBMITTED,
actionId,
);
} }
async updatePostTxBalance({ txMeta, txId, numberOfAttempts = 6 }) { async updatePostTxBalance({ txMeta, txId, numberOfAttempts = 6 }) {
@ -1606,6 +1621,7 @@ export default class TransactionController extends EventEmitter {
this._trackTransactionMetricsEvent( this._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.FINALIZED, TRANSACTION_EVENTS.FINALIZED,
undefined,
metricsParams, metricsParams,
); );
@ -1666,6 +1682,7 @@ export default class TransactionController extends EventEmitter {
this._trackTransactionMetricsEvent( this._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.FINALIZED, TRANSACTION_EVENTS.FINALIZED,
undefined,
metricsParams, metricsParams,
); );
@ -1689,12 +1706,17 @@ export default class TransactionController extends EventEmitter {
* Convenience method for the ui thats sets the transaction to rejected * Convenience method for the ui thats sets the transaction to rejected
* *
* @param {number} txId - the tx's Id * @param {number} txId - the tx's Id
* @param {string} actionId - actionId passed from UI
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async cancelTransaction(txId) { async cancelTransaction(txId, actionId) {
const txMeta = this.txStateManager.getTransaction(txId); const txMeta = this.txStateManager.getTransaction(txId);
this.txStateManager.setTxStatusRejected(txId); this.txStateManager.setTxStatusRejected(txId);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.REJECTED); this._trackTransactionMetricsEvent(
txMeta,
TRANSACTION_EVENTS.REJECTED,
actionId,
);
} }
/** /**
@ -1717,8 +1739,9 @@ export default class TransactionController extends EventEmitter {
* @param {number} transactionId - The transaction id to create the event * @param {number} transactionId - The transaction id to create the event
* fragment for * fragment for
* @param {valueOf<TRANSACTION_EVENTS>} event - event type to create * @param {valueOf<TRANSACTION_EVENTS>} event - event type to create
* @param {string} actionId - actionId passed from UI
*/ */
async createTransactionEventFragment(transactionId, event) { async createTransactionEventFragment(transactionId, event, actionId) {
const txMeta = this.txStateManager.getTransaction(transactionId); const txMeta = this.txStateManager.getTransaction(transactionId);
const { properties, sensitiveProperties } = const { properties, sensitiveProperties } =
await this._buildEventFragmentProperties(txMeta); await this._buildEventFragmentProperties(txMeta);
@ -1727,6 +1750,7 @@ export default class TransactionController extends EventEmitter {
event, event,
properties, properties,
sensitiveProperties, sensitiveProperties,
actionId,
); );
} }
@ -2329,6 +2353,7 @@ export default class TransactionController extends EventEmitter {
* triggered fragment creation * triggered fragment creation
* @param {object} properties - properties to include in the fragment * @param {object} properties - properties to include in the fragment
* @param {object} [sensitiveProperties] - sensitive properties to include in * @param {object} [sensitiveProperties] - sensitive properties to include in
* @param {object} [actionId] - actionId passed from UI
* the fragment * the fragment
*/ */
_createTransactionEventFragment( _createTransactionEventFragment(
@ -2336,6 +2361,7 @@ export default class TransactionController extends EventEmitter {
event, event,
properties, properties,
sensitiveProperties, sensitiveProperties,
actionId,
) { ) {
const isSubmitted = [ const isSubmitted = [
TRANSACTION_EVENTS.FINALIZED, TRANSACTION_EVENTS.FINALIZED,
@ -2370,6 +2396,7 @@ export default class TransactionController extends EventEmitter {
sensitiveProperties, sensitiveProperties,
persist: true, persist: true,
uniqueIdentifier, uniqueIdentifier,
actionId,
}); });
break; break;
// If for some reason an approval or rejection occurs without the added // If for some reason an approval or rejection occurs without the added
@ -2390,6 +2417,7 @@ export default class TransactionController extends EventEmitter {
sensitiveProperties, sensitiveProperties,
persist: true, persist: true,
uniqueIdentifier, uniqueIdentifier,
actionId,
}); });
break; break;
// When a transaction is submitted it will always result in updating // When a transaction is submitted it will always result in updating
@ -2411,6 +2439,7 @@ export default class TransactionController extends EventEmitter {
sensitiveProperties, sensitiveProperties,
persist: true, persist: true,
uniqueIdentifier, uniqueIdentifier,
actionId,
}); });
break; break;
// If for some reason a transaction is finalized without the submitted // If for some reason a transaction is finalized without the submitted
@ -2429,6 +2458,7 @@ export default class TransactionController extends EventEmitter {
sensitiveProperties, sensitiveProperties,
persist: true, persist: true,
uniqueIdentifier, uniqueIdentifier,
actionId,
}); });
break; break;
default: default:
@ -2443,9 +2473,15 @@ export default class TransactionController extends EventEmitter {
* *
* @param {object} txMeta - the txMeta object * @param {object} txMeta - the txMeta object
* @param {TransactionMetaMetricsEventString} event - the name of the transaction event * @param {TransactionMetaMetricsEventString} event - the name of the transaction event
* @param {string} actionId - actionId passed from UI
* @param {object} extraParams - optional props and values to include in sensitiveProperties * @param {object} extraParams - optional props and values to include in sensitiveProperties
*/ */
async _trackTransactionMetricsEvent(txMeta, event, extraParams = {}) { async _trackTransactionMetricsEvent(
txMeta,
event,
actionId,
extraParams = {},
) {
if (!txMeta) { if (!txMeta) {
return; return;
} }
@ -2459,6 +2495,7 @@ export default class TransactionController extends EventEmitter {
event, event,
properties, properties,
sensitiveProperties, sensitiveProperties,
actionId,
); );
let id; let id;
@ -2508,19 +2545,29 @@ export default class TransactionController extends EventEmitter {
return gasValuesInGwei; return gasValuesInGwei;
} }
_failTransaction(txId, error) { _failTransaction(txId, error, actionId) {
this.txStateManager.setTxStatusFailed(txId, error); this.txStateManager.setTxStatusFailed(txId, error);
const txMeta = this.txStateManager.getTransaction(txId); const txMeta = this.txStateManager.getTransaction(txId);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED, { this._trackTransactionMetricsEvent(
error: error.message, txMeta,
}); TRANSACTION_EVENTS.FINALIZED,
actionId,
{
error: error.message,
},
);
} }
_dropTransaction(txId) { _dropTransaction(txId) {
this.txStateManager.setTxStatusDropped(txId); this.txStateManager.setTxStatusDropped(txId);
const txMeta = this.txStateManager.getTransaction(txId); const txMeta = this.txStateManager.getTransaction(txId);
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.FINALIZED, { this._trackTransactionMetricsEvent(
dropped: true, txMeta,
}); TRANSACTION_EVENTS.FINALIZED,
undefined,
{
dropped: true,
},
);
} }
} }

View File

@ -36,7 +36,7 @@ const currentChainId = '0x2a';
const providerConfig = { const providerConfig = {
type: 'kovan', type: 'kovan',
}; };
const actionId = 'DUMMY_ACTION_ID';
const VALID_ADDRESS = '0x0000000000000000000000000000000000000000'; const VALID_ADDRESS = '0x0000000000000000000000000000000000000000';
const VALID_ADDRESS_TWO = '0x0000000000000000000000000000000000000001'; const VALID_ADDRESS_TWO = '0x0000000000000000000000000000000000000001';
@ -551,8 +551,7 @@ describe('Transaction Controller', function () {
await txController.createCancelTransaction( await txController.createCancelTransaction(
txMeta.id, txMeta.id,
{}, {},
undefined, { actionId: 12345 },
12345,
); );
const transactionCount1 = const transactionCount1 =
txController.txStateManager.getTransactions().length; txController.txStateManager.getTransactions().length;
@ -1666,6 +1665,7 @@ describe('Transaction Controller', function () {
describe('On transaction created by the user', function () { describe('On transaction created by the user', function () {
let txMeta; let txMeta;
before(function () { before(function () {
txMeta = { txMeta = {
id: 1, id: 1,
@ -1691,6 +1691,7 @@ describe('Transaction Controller', function () {
it('should create an event fragment when transaction added', async function () { it('should create an event fragment when transaction added', async function () {
const expectedPayload = { const expectedPayload = {
actionId,
initialEvent: 'Transaction Added', initialEvent: 'Transaction Added',
successEvent: 'Transaction Approved', successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected', failureEvent: 'Transaction Rejected',
@ -1728,6 +1729,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.ADDED, TRANSACTION_EVENTS.ADDED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 1); assert.equal(createEventFragmentSpy.callCount, 1);
assert.equal(finalizeEventFragmentSpy.callCount, 0); assert.equal(finalizeEventFragmentSpy.callCount, 0);
@ -1742,6 +1744,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.REJECTED, TRANSACTION_EVENTS.REJECTED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 0); assert.equal(createEventFragmentSpy.callCount, 0);
assert.equal(finalizeEventFragmentSpy.callCount, 1); assert.equal(finalizeEventFragmentSpy.callCount, 1);
@ -1759,6 +1762,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.APPROVED, TRANSACTION_EVENTS.APPROVED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 0); assert.equal(createEventFragmentSpy.callCount, 0);
assert.equal(finalizeEventFragmentSpy.callCount, 1); assert.equal(finalizeEventFragmentSpy.callCount, 1);
@ -1774,6 +1778,7 @@ describe('Transaction Controller', function () {
it('should create an event fragment when transaction is submitted', async function () { it('should create an event fragment when transaction is submitted', async function () {
const expectedPayload = { const expectedPayload = {
actionId,
initialEvent: 'Transaction Submitted', initialEvent: 'Transaction Submitted',
successEvent: 'Transaction Finalized', successEvent: 'Transaction Finalized',
uniqueIdentifier: 'transaction-submitted-1', uniqueIdentifier: 'transaction-submitted-1',
@ -1810,6 +1815,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.SUBMITTED, TRANSACTION_EVENTS.SUBMITTED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 1); assert.equal(createEventFragmentSpy.callCount, 1);
assert.equal(finalizeEventFragmentSpy.callCount, 0); assert.equal(finalizeEventFragmentSpy.callCount, 0);
@ -1824,6 +1830,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.FINALIZED, TRANSACTION_EVENTS.FINALIZED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 0); assert.equal(createEventFragmentSpy.callCount, 0);
assert.equal(finalizeEventFragmentSpy.callCount, 1); assert.equal(finalizeEventFragmentSpy.callCount, 1);
@ -1865,6 +1872,7 @@ describe('Transaction Controller', function () {
it('should create an event fragment when transaction added', async function () { it('should create an event fragment when transaction added', async function () {
const expectedPayload = { const expectedPayload = {
actionId,
initialEvent: 'Transaction Added', initialEvent: 'Transaction Added',
successEvent: 'Transaction Approved', successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected', failureEvent: 'Transaction Rejected',
@ -1902,6 +1910,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.ADDED, TRANSACTION_EVENTS.ADDED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 1); assert.equal(createEventFragmentSpy.callCount, 1);
assert.equal(finalizeEventFragmentSpy.callCount, 0); assert.equal(finalizeEventFragmentSpy.callCount, 0);
@ -1917,6 +1926,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.REJECTED, TRANSACTION_EVENTS.REJECTED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 0); assert.equal(createEventFragmentSpy.callCount, 0);
assert.equal(finalizeEventFragmentSpy.callCount, 1); assert.equal(finalizeEventFragmentSpy.callCount, 1);
@ -1935,6 +1945,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.APPROVED, TRANSACTION_EVENTS.APPROVED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 0); assert.equal(createEventFragmentSpy.callCount, 0);
assert.equal(finalizeEventFragmentSpy.callCount, 1); assert.equal(finalizeEventFragmentSpy.callCount, 1);
@ -1950,6 +1961,7 @@ describe('Transaction Controller', function () {
it('should create an event fragment when transaction is submitted', async function () { it('should create an event fragment when transaction is submitted', async function () {
const expectedPayload = { const expectedPayload = {
actionId,
initialEvent: 'Transaction Submitted', initialEvent: 'Transaction Submitted',
successEvent: 'Transaction Finalized', successEvent: 'Transaction Finalized',
uniqueIdentifier: 'transaction-submitted-1', uniqueIdentifier: 'transaction-submitted-1',
@ -1986,6 +1998,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.SUBMITTED, TRANSACTION_EVENTS.SUBMITTED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 1); assert.equal(createEventFragmentSpy.callCount, 1);
assert.equal(finalizeEventFragmentSpy.callCount, 0); assert.equal(finalizeEventFragmentSpy.callCount, 0);
@ -2001,6 +2014,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.FINALIZED, TRANSACTION_EVENTS.FINALIZED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 0); assert.equal(createEventFragmentSpy.callCount, 0);
assert.equal(finalizeEventFragmentSpy.callCount, 1); assert.equal(finalizeEventFragmentSpy.callCount, 1);
@ -2034,6 +2048,7 @@ describe('Transaction Controller', function () {
}; };
const expectedPayload = { const expectedPayload = {
actionId,
successEvent: 'Transaction Approved', successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected', failureEvent: 'Transaction Rejected',
uniqueIdentifier: 'transaction-added-1', uniqueIdentifier: 'transaction-added-1',
@ -2067,6 +2082,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.APPROVED, TRANSACTION_EVENTS.APPROVED,
actionId,
); );
assert.equal(createEventFragmentSpy.callCount, 1); assert.equal(createEventFragmentSpy.callCount, 1);
assert.deepEqual( assert.deepEqual(
@ -2099,6 +2115,7 @@ describe('Transaction Controller', function () {
metamaskNetworkId: currentNetworkId, metamaskNetworkId: currentNetworkId,
}; };
const expectedPayload = { const expectedPayload = {
actionId,
initialEvent: 'Transaction Added', initialEvent: 'Transaction Added',
successEvent: 'Transaction Approved', successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected', failureEvent: 'Transaction Rejected',
@ -2136,6 +2153,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.ADDED, TRANSACTION_EVENTS.ADDED,
actionId,
{ {
baz: 3.0, baz: 3.0,
foo: 'bar', foo: 'bar',
@ -2175,6 +2193,7 @@ describe('Transaction Controller', function () {
}, },
}; };
const expectedPayload = { const expectedPayload = {
actionId,
initialEvent: 'Transaction Added', initialEvent: 'Transaction Added',
successEvent: 'Transaction Approved', successEvent: 'Transaction Approved',
failureEvent: 'Transaction Rejected', failureEvent: 'Transaction Rejected',
@ -2218,6 +2237,7 @@ describe('Transaction Controller', function () {
await txController._trackTransactionMetricsEvent( await txController._trackTransactionMetricsEvent(
txMeta, txMeta,
TRANSACTION_EVENTS.ADDED, TRANSACTION_EVENTS.ADDED,
actionId,
{ {
baz: 3.0, baz: 3.0,
foo: 'bar', foo: 'bar',

View File

@ -29,6 +29,8 @@ let promisifiedBackground = null;
const actionRetryQueue = []; const actionRetryQueue = [];
export const generateActionId = () => Date.now() + Math.random();
function failQueue() { function failQueue() {
actionRetryQueue.forEach(({ reject }) => actionRetryQueue.forEach(({ reject }) =>
reject( reject(
@ -81,7 +83,7 @@ const executeActionOrAddToRetryQueue = (item) => {
export function submitRequestToBackground( export function submitRequestToBackground(
method, method,
args = [], args = [],
actionId = Date.now() + Math.random(), // current date is not guaranteed to be unique actionId = generateActionId(), // current date is not guaranteed to be unique
) { ) {
if (isManifestV3) { if (isManifestV3) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -112,7 +114,7 @@ export const callBackgroundMethod = (
method, method,
args = [], args = [],
callback, callback,
actionId = Date.now() + Math.random(), // current date is not guaranteed to be unique actionId = generateActionId(), // current date is not guaranteed to be unique
) => { ) => {
if (isManifestV3) { if (isManifestV3) {
const resolve = (value) => callback(null, value); const resolve = (value) => callback(null, value);

View File

@ -48,6 +48,7 @@ import { NOTIFICATIONS_EXPIRATION_DELAY } from '../helpers/constants/notificatio
import { setNewCustomNetworkAdded } from '../ducks/app/app'; import { setNewCustomNetworkAdded } from '../ducks/app/app';
import * as actionConstants from './actionConstants'; import * as actionConstants from './actionConstants';
import { import {
generateActionId,
callBackgroundMethod, callBackgroundMethod,
submitRequestToBackground, submitRequestToBackground,
} from './action-queue'; } from './action-queue';
@ -894,7 +895,7 @@ export function addUnapprovedTransactionAndRouteToConfirmationPage(
sendFlowHistory, sendFlowHistory,
) { ) {
return async (dispatch) => { return async (dispatch) => {
const actionId = Date.now() + Math.random(); const actionId = generateActionId();
try { try {
log.debug('background.addUnapprovedTransaction'); log.debug('background.addUnapprovedTransaction');
const txMeta = await submitRequestToBackground( const txMeta = await submitRequestToBackground(
@ -927,7 +928,7 @@ export function addUnapprovedTransactionAndRouteToConfirmationPage(
*/ */
export async function addUnapprovedTransaction(txParams, type) { export async function addUnapprovedTransaction(txParams, type) {
log.debug('background.addUnapprovedTransaction'); log.debug('background.addUnapprovedTransaction');
const actionId = Date.now() + Math.random(); const actionId = generateActionId();
const txMeta = await submitRequestToBackground( const txMeta = await submitRequestToBackground(
'addUnapprovedTransaction', 'addUnapprovedTransaction',
[txParams, ORIGIN_METAMASK, type, undefined, actionId], [txParams, ORIGIN_METAMASK, type, undefined, actionId],
@ -940,20 +941,25 @@ export function updateAndApproveTx(txData, dontShowLoadingIndicator) {
return (dispatch) => { return (dispatch) => {
!dontShowLoadingIndicator && dispatch(showLoadingIndication()); !dontShowLoadingIndicator && dispatch(showLoadingIndication());
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
callBackgroundMethod('updateAndApproveTransaction', [txData], (err) => { const actionId = generateActionId();
dispatch(updateTransactionParams(txData.id, txData.txParams)); callBackgroundMethod(
dispatch(resetSendState()); 'updateAndApproveTransaction',
[txData, actionId],
(err) => {
dispatch(updateTransactionParams(txData.id, txData.txParams));
dispatch(resetSendState());
if (err) { if (err) {
dispatch(txError(err)); dispatch(txError(err));
dispatch(goHome()); dispatch(goHome());
log.error(err.message); log.error(err.message);
reject(err); reject(err);
return; return;
} }
resolve(txData); resolve(txData);
}); },
);
}) })
.then(() => updateMetamaskStateFromBackground()) .then(() => updateMetamaskStateFromBackground())
.then((newState) => dispatch(updateMetamaskState(newState))) .then((newState) => dispatch(updateMetamaskState(newState)))
@ -1287,14 +1293,19 @@ export function cancelTx(txData, _showLoadingIndication = true) {
return (dispatch) => { return (dispatch) => {
_showLoadingIndication && dispatch(showLoadingIndication()); _showLoadingIndication && dispatch(showLoadingIndication());
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
callBackgroundMethod('cancelTransaction', [txData.id], (error) => { const actionId = generateActionId();
if (error) { callBackgroundMethod(
reject(error); 'cancelTransaction',
return; [txData.id, actionId],
} (error) => {
if (error) {
reject(error);
return;
}
resolve(); resolve();
}); },
);
}) })
.then(() => updateMetamaskStateFromBackground()) .then(() => updateMetamaskStateFromBackground())
.then((newState) => dispatch(updateMetamaskState(newState))) .then((newState) => dispatch(updateMetamaskState(newState)))
@ -1328,7 +1339,8 @@ export function cancelTxs(txDataList) {
const cancellations = txIds.map( const cancellations = txIds.map(
(id) => (id) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
callBackgroundMethod('cancelTransaction', [id], (err) => { const actionId = generateActionId();
callBackgroundMethod('cancelTransaction', [id, actionId], (err) => {
if (err) { if (err) {
reject(err); reject(err);
return; return;
@ -1979,7 +1991,7 @@ export function createCancelTransaction(txId, customGasSettings, options) {
let newTxId; let newTxId;
return (dispatch) => { return (dispatch) => {
const actionId = Date.now() + Math.random(); const actionId = generateActionId();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
callBackgroundMethod( callBackgroundMethod(
'createCancelTransaction', 'createCancelTransaction',
@ -2009,7 +2021,7 @@ export function createSpeedUpTransaction(txId, customGasSettings, options) {
let newTx; let newTx;
return (dispatch) => { return (dispatch) => {
const actionId = Date.now() + Math.random(); const actionId = generateActionId();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
callBackgroundMethod( callBackgroundMethod(
'createSpeedUpTransaction', 'createSpeedUpTransaction',
@ -2038,9 +2050,10 @@ export function createRetryTransaction(txId, customGasSettings) {
return (dispatch) => { return (dispatch) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const actionId = generateActionId();
callBackgroundMethod( callBackgroundMethod(
'createSpeedUpTransaction', 'createSpeedUpTransaction',
[txId, customGasSettings], [txId, customGasSettings, { actionId }],
(err, newState) => { (err, newState) => {
if (err) { if (err) {
dispatch(displayWarning(err.message)); dispatch(displayWarning(err.message));
@ -3605,13 +3618,18 @@ export function trackMetaMetricsEvent(payload, options) {
} }
export function createEventFragment(options) { export function createEventFragment(options) {
return submitRequestToBackground('createEventFragment', [options]); const actionId = generateActionId();
return submitRequestToBackground('createEventFragment', [
{ ...options, actionId },
]);
} }
export function createTransactionEventFragment(transactionId, event) { export function createTransactionEventFragment(transactionId, event) {
const actionId = generateActionId();
return submitRequestToBackground('createTransactionEventFragment', [ return submitRequestToBackground('createTransactionEventFragment', [
transactionId, transactionId,
event, event,
actionId,
]); ]);
} }

View File

@ -1664,7 +1664,7 @@ describe('Actions', () => {
const store = mockStore(); const store = mockStore();
background.getApi.returns({ background.getApi.returns({
cancelTransaction: sinon.stub().callsFake((_, cb) => { cancelTransaction: sinon.stub().callsFake((_1, _2, cb) => {
cb(); cb();
}), }),
getState: sinon.stub().callsFake((cb) => getState: sinon.stub().callsFake((cb) =>