1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-12 04:37:13 +01:00
metamask-extension/app/scripts/controllers/transactions/tx-state-manager.js

537 lines
16 KiB
JavaScript
Raw Normal View History

import EventEmitter from 'safe-event-emitter';
import { ObservableStore } from '@metamask/obs-store';
import log from 'loglevel';
import createId from '../../lib/random-id';
import { TRANSACTION_STATUSES } from '../../../../shared/constants/transaction';
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
import { transactionMatchesNetwork } from '../../../../shared/modules/transaction.utils';
2020-11-03 00:41:28 +01:00
import {
generateHistoryEntry,
replayHistory,
snapshotFromTxMeta,
} from './lib/tx-state-history-helpers';
import { getFinalStates, normalizeTxParams } from './lib/util';
2018-04-19 20:29:26 +02:00
/**
* TransactionStatuses reimported from the shared transaction constants file
* @typedef {import('../../../../shared/constants/transaction').TransactionStatuses} TransactionStatuses
*/
/**
* TransactionStateManager is responsible for the state of a transaction and
* storing the transaction. It also has some convenience methods for finding
* subsets of transactions.
* @param {Object} opts
* @param {Object} [opts.initState={ transactions: [] }] - initial transactions list with the key transaction {Array}
* @param {number} [opts.txHistoryLimit] - limit for how many finished
* transactions can hang around in state
* @param {Function} opts.getNetwork - return network number
* @class
*/
export default class TransactionStateManager extends EventEmitter {
constructor({ initState, txHistoryLimit, getNetwork, getCurrentChainId }) {
super();
2017-09-12 18:59:59 +02:00
this.store = new ObservableStore({ transactions: [], ...initState });
this.txHistoryLimit = txHistoryLimit;
this.getNetwork = getNetwork;
this.getCurrentChainId = getCurrentChainId;
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* @param {Object} opts - the object to use when overwriting defaults
* @returns {txMeta} the default txMeta object
*/
2020-11-03 00:41:28 +01:00
generateTxMeta(opts) {
const netId = this.getNetwork();
const chainId = this.getCurrentChainId();
if (netId === 'loading') {
throw new Error('MetaMask is having trouble connecting to the network');
}
return {
id: createId(),
2020-11-03 00:41:28 +01:00
time: new Date().getTime(),
status: TRANSACTION_STATUSES.UNAPPROVED,
metamaskNetworkId: netId,
chainId,
2020-11-03 00:41:28 +01:00
loadingDefaults: true,
...opts,
};
}
2018-04-19 20:29:26 +02:00
/**
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
* Returns the full tx list for the current network
*
* The list is iterated backwards as new transactions are pushed onto it.
*
* @param {number} [limit] - a limit for the number of transactions to return
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
* @returns {Object[]} The {@code txMeta}s, filtered to the current network
*/
2020-11-03 00:41:28 +01:00
getTxList(limit) {
const network = this.getNetwork();
const chainId = this.getCurrentChainId();
const fullTxList = this.getFullTxList();
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
const nonces = new Set();
const txs = [];
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
for (let i = fullTxList.length - 1; i > -1; i--) {
const txMeta = fullTxList[i];
if (transactionMatchesNetwork(txMeta, chainId, network) === false) {
continue;
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
}
if (limit !== undefined) {
const { nonce } = txMeta.txParams;
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
if (!nonces.has(nonce)) {
if (nonces.size < limit) {
nonces.add(nonce);
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
} else {
continue;
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
}
}
}
txs.unshift(txMeta);
Limit number of transactions passed outside of TransactionController (#9010) Refs #8572 Refs #8991 This change limits the number of transactions (`txMeta`s) that are passed outside of the `TransactionController`, resulting in shorter serialization and deserialization times when state is moved between the background and UI contexts. `TransactionController#_updateMemstore` --------------------------------------- The `currentNetworkTxList` state of the `TransactionController` is used externally (i.e. outside of the controller) as the canonical source for the full transaction history. Prior to this change, the method would iterate the full transaction history and possibly return all of it. This change limits it to `MAX_MEMSTORE_TX_LIST_SIZE` to make sure that: 1. Calls to `_updateMemstore` are fast(er) 2. Passing `currentNetworkTxList` around is fast(er) (Shown in #8377, `_updateMemstore`, is called _frequently_ when a transaction is pending.) The list is iterated backwards because it is possible that new transactions are at the end of the list. [1] Results ------- In profiles before this change, with ~3k transactions locally, `PortDuplexStream._onMessage` took up to ~4.5s to complete when the set of transactions is included. [2] In profiles after this change, `PortDuplexStream._onMessage` took ~90ms to complete. [3] Before vs. after profile screenshots: ![Profile 1][2] ![Profile 2][3] [1]:https://github.com/MetaMask/metamask-extension/blob/5a3ae85b728096cb45c8cc6822249eed5555ee25/app/scripts/controllers/transactions/tx-state-manager.js#L172-L174 [2]:https://user-images.githubusercontent.com/1623628/87613203-36f51d80-c6e7-11ea-89bc-11a1cc2f3b1e.png [3]:https://user-images.githubusercontent.com/1623628/87613215-3bb9d180-c6e7-11ea-8d85-aff3acbd0374.png [8337]:https://github.com/MetaMask/metamask-extension/issues/8377 [8572]:https://github.com/MetaMask/metamask-extension/issues/8572 [8991]:https://github.com/MetaMask/metamask-extension/issues/8991
2020-07-16 22:22:41 +02:00
}
return txs;
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* @returns {Array} of all the txMetas in store
*/
2020-11-03 00:41:28 +01:00
getFullTxList() {
return this.store.getState().transactions;
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* @returns {Array} the tx list with unapproved status
*/
2020-11-03 00:41:28 +01:00
getUnapprovedTxList() {
const txList = this.getTxsByMetaData(
'status',
TRANSACTION_STATUSES.UNAPPROVED,
);
2017-08-18 21:23:35 +02:00
return txList.reduce((result, tx) => {
result[tx.id] = tx;
return result;
}, {});
2017-08-18 21:23:35 +02:00
}
/**
* @param {string} [address] - hex prefixed address to sort the txMetas for [optional]
* @returns {Array} the tx list with approved status if no address is provide
* returns all txMetas with approved statuses for the current network
*/
2020-11-03 00:41:28 +01:00
getApprovedTransactions(address) {
const opts = { status: TRANSACTION_STATUSES.APPROVED };
if (address) {
opts.from = address;
}
return this.getFilteredTxList(opts);
}
2018-04-19 20:29:26 +02:00
/**
* @param {string} [address] - hex prefixed address to sort the txMetas for [optional]
* @returns {Array} the tx list submitted status if no address is provide
* returns all txMetas with submitted statuses for the current network
*/
2020-11-03 00:41:28 +01:00
getPendingTransactions(address) {
const opts = { status: TRANSACTION_STATUSES.SUBMITTED };
if (address) {
opts.from = address;
}
return this.getFilteredTxList(opts);
}
2018-04-19 20:29:26 +02:00
/**
@param {string} [address] - hex prefixed address to sort the txMetas for [optional]
@returns {Array} the tx list whose status is confirmed if no address is provide
2018-04-25 20:13:51 +02:00
returns all txMetas who's status is confirmed for the current network
2018-04-19 20:29:26 +02:00
*/
2020-11-03 00:41:28 +01:00
getConfirmedTransactions(address) {
const opts = { status: TRANSACTION_STATUSES.CONFIRMED };
if (address) {
opts.from = address;
}
return this.getFilteredTxList(opts);
}
2018-04-19 20:29:26 +02:00
/**
* Adds the txMeta to the list of transactions in the store.
* if the list is over txHistoryLimit it will remove a transaction that
* is in its final state.
* it will also add the key `history` to the txMeta with the snap shot of
* the original object
* @param {Object} txMeta
* @returns {Object} the txMeta
*/
2020-11-03 00:41:28 +01:00
addTx(txMeta) {
// normalize and validate txParams if present
if (txMeta.txParams) {
txMeta.txParams = this.normalizeAndValidateTxParams(txMeta.txParams);
}
this.once(`${txMeta.id}:signed`, () => {
this.removeAllListeners(`${txMeta.id}:rejected`);
});
this.once(`${txMeta.id}:rejected`, () => {
this.removeAllListeners(`${txMeta.id}:signed`);
});
// initialize history
txMeta.history = [];
// capture initial snapshot of txMeta for history
const snapshot = snapshotFromTxMeta(txMeta);
txMeta.history.push(snapshot);
2017-08-18 21:23:35 +02:00
const transactions = this.getFullTxList();
const txCount = transactions.length;
const { txHistoryLimit } = this;
2017-08-11 23:19:35 +02:00
// checks if the length of the tx history is
// longer then desired persistence limit
// and then if it is removes only confirmed
// or rejected tx's.
// not tx's that are pending or unapproved
if (txCount > txHistoryLimit - 1) {
2018-04-10 23:53:40 +02:00
const index = transactions.findIndex((metaTx) => {
return getFinalStates().includes(metaTx.status);
});
if (index !== -1) {
transactions.splice(index, 1);
}
2017-08-11 23:19:35 +02:00
}
2020-11-03 00:41:28 +01:00
const newTxIndex = transactions.findIndex(
(currentTxMeta) => currentTxMeta.time > txMeta.time,
);
newTxIndex === -1
? transactions.push(txMeta)
: transactions.splice(newTxIndex, 0, txMeta);
this._saveTxList(transactions);
return txMeta;
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* @param {number} txId
* @returns {Object} the txMeta who matches the given id if none found
* for the network returns undefined
*/
2020-11-03 00:41:28 +01:00
getTx(txId) {
const txMeta = this.getTxsByMetaData('id', txId)[0];
return txMeta;
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* updates the txMeta in the list and adds a history entry
* @param {Object} txMeta - the txMeta to update
* @param {string} [note] - a note about the update for history
*/
2020-11-03 00:41:28 +01:00
updateTx(txMeta, note) {
// normalize and validate txParams if present
if (txMeta.txParams) {
txMeta.txParams = this.normalizeAndValidateTxParams(txMeta.txParams);
}
2017-08-11 23:19:35 +02:00
// create txMeta snapshot for history
const currentState = snapshotFromTxMeta(txMeta);
// recover previous tx state obj
const previousState = replayHistory(txMeta.history);
// generate history entry and add to history
const entry = generateHistoryEntry(previousState, currentState, note);
if (entry.length) {
txMeta.history.push(entry);
}
// commit txMeta to state
const txId = txMeta.id;
const txList = this.getFullTxList();
const index = txList.findIndex((txData) => txData.id === txId);
txList[index] = txMeta;
this._saveTxList(txList);
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* merges txParams obj onto txMeta.txParams use extend to ensure
* that all fields are filled
* @param {number} txId - the id of the txMeta
* @param {Object} txParams - the updated txParams
*/
2020-11-03 00:41:28 +01:00
updateTxParams(txId, txParams) {
const txMeta = this.getTx(txId);
txMeta.txParams = { ...txMeta.txParams, ...txParams };
this.updateTx(txMeta, `txStateManager#updateTxParams`);
2017-08-11 23:19:35 +02:00
}
/**
* normalize and validate txParams members
* @param {Object} txParams - txParams
*/
2020-11-03 00:41:28 +01:00
normalizeAndValidateTxParams(txParams) {
if (typeof txParams.data === 'undefined') {
delete txParams.data;
}
// eslint-disable-next-line no-param-reassign
txParams = normalizeTxParams(txParams, false);
this.validateTxParams(txParams);
return txParams;
}
2018-04-19 20:29:26 +02:00
/**
* validates txParams members by type
* @param {Object} txParams - txParams to validate
*/
2020-11-03 00:41:28 +01:00
validateTxParams(txParams) {
Object.keys(txParams).forEach((key) => {
const value = txParams[key];
// validate types
switch (key) {
case 'chainId':
if (typeof value !== 'number' && typeof value !== 'string') {
2020-11-03 00:41:28 +01:00
throw new Error(
`${key} in txParams is not a Number or hex string. got: (${value})`,
);
}
break;
default:
if (typeof value !== 'string') {
2020-11-03 00:41:28 +01:00
throw new Error(
`${key} in txParams is not a string. got: (${value})`,
);
}
break;
}
});
}
/**
@param {Object} opts - an object of fields to search for eg:<br>
2018-04-19 20:29:26 +02:00
let <code>thingsToLookFor = {<br>
to: '0x0..',<br>
from: '0x0..',<br>
status: 'signed', \\ (status) => status !== 'rejected' give me all txs who's status is not rejected<br>
2018-04-19 20:29:26 +02:00
err: undefined,<br>
}<br></code>
optionally the values of the keys can be functions for situations like where
you want all but one status.
@param {Array} [initialList=this.getTxList()]
@returns {Array} array of txMeta with all
2017-08-11 23:19:35 +02:00
options matching
2018-04-19 20:29:26 +02:00
*/
/*
2017-08-11 23:19:35 +02:00
****************HINT****************
| `err: undefined` is like looking |
| for a tx with no err |
| so you can also search txs that |
| dont have something as well by |
| setting the value as undefined |
************************************
this is for things like filtering a the tx list
for only tx's from 1 account
or for filtering for all txs from one account
2017-08-11 23:19:35 +02:00
and that have been 'confirmed'
*/
2020-11-03 00:41:28 +01:00
getFilteredTxList(opts, initialList) {
let filteredTxList = initialList;
2017-08-11 23:19:35 +02:00
Object.keys(opts).forEach((key) => {
filteredTxList = this.getTxsByMetaData(key, opts[key], filteredTxList);
});
return filteredTxList;
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* @param {string} key - the key to check
* @param {any} value - the value your looking for can also be a function that returns a bool
* @param {Array} [txList=this.getTxList()] - the list to search. default is the txList
* from txStateManager#getTxList
* @returns {Array} a list of txMetas who matches the search params
*/
2020-11-03 00:41:28 +01:00
getTxsByMetaData(key, value, txList = this.getTxList()) {
const filter = typeof value === 'function' ? value : (v) => v === value;
2017-08-11 23:19:35 +02:00
return txList.filter((txMeta) => {
if (key in txMeta.txParams) {
return filter(txMeta.txParams[key]);
2017-08-11 23:19:35 +02:00
}
return filter(txMeta[key]);
});
2017-08-11 23:19:35 +02:00
}
// get::set status
2018-04-19 20:29:26 +02:00
/**
* @param {number} txId - the txMeta Id
* @returns {string} the status of the tx.
*/
2020-11-03 00:41:28 +01:00
getTxStatus(txId) {
const txMeta = this.getTx(txId);
return txMeta.status;
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Update the status of the tx to 'rejected'.
* @param {number} txId - the txMeta Id
*/
2020-11-03 00:41:28 +01:00
setTxStatusRejected(txId) {
this._setTxStatus(txId, 'rejected');
this._removeTx(txId);
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Update the status of the tx to 'unapproved'.
* @param {number} txId - the txMeta Id
*/
2020-11-03 00:41:28 +01:00
setTxStatusUnapproved(txId) {
this._setTxStatus(txId, TRANSACTION_STATUSES.UNAPPROVED);
2017-12-07 04:20:11 +01:00
}
2018-04-19 20:29:26 +02:00
/**
* Update the status of the tx to 'approved'.
* @param {number} txId - the txMeta Id
*/
2020-11-03 00:41:28 +01:00
setTxStatusApproved(txId) {
this._setTxStatus(txId, TRANSACTION_STATUSES.APPROVED);
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Update the status of the tx to 'signed'.
* @param {number} txId - the txMeta Id
*/
2020-11-03 00:41:28 +01:00
setTxStatusSigned(txId) {
this._setTxStatus(txId, TRANSACTION_STATUSES.SIGNED);
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Update the status of the tx to 'submitted' and add a time stamp
* for when it was called
* @param {number} txId - the txMeta Id
*/
2020-11-03 00:41:28 +01:00
setTxStatusSubmitted(txId) {
const txMeta = this.getTx(txId);
txMeta.submittedTime = new Date().getTime();
this.updateTx(txMeta, 'txStateManager - add submitted time stamp');
this._setTxStatus(txId, TRANSACTION_STATUSES.SUBMITTED);
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Update the status of the tx to 'confirmed'.
* @param {number} txId - the txMeta Id
*/
2020-11-03 00:41:28 +01:00
setTxStatusConfirmed(txId) {
this._setTxStatus(txId, TRANSACTION_STATUSES.CONFIRMED);
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Update the status of the tx to 'dropped'.
* @param {number} txId - the txMeta Id
*/
2020-11-03 00:41:28 +01:00
setTxStatusDropped(txId) {
this._setTxStatus(txId, TRANSACTION_STATUSES.DROPPED);
}
2018-04-19 20:29:26 +02:00
/**
* Updates the status of the tx to 'failed' and put the error on the txMeta
* @param {number} txId - the txMeta Id
* @param {erroObject} err - error object
*/
2020-11-03 00:41:28 +01:00
setTxStatusFailed(txId, err) {
const error = err || new Error('Internal metamask failure');
const txMeta = this.getTx(txId);
txMeta.err = {
message: error.toString(),
rpc: error.value,
stack: error.stack,
};
this.updateTx(txMeta, 'transactions:tx-state-manager#fail - add error');
this._setTxStatus(txId, TRANSACTION_STATUSES.FAILED);
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Removes transaction from the given address for the current network
* from the txList
* @param {string} address - hex string of the from address on the txParams
* to remove
*/
2020-11-03 00:41:28 +01:00
wipeTransactions(address) {
// network only tx
const txs = this.getFullTxList();
const network = this.getNetwork();
const chainId = this.getCurrentChainId();
2018-01-31 19:29:02 +01:00
// Filter out the ones from the current account and network
2020-11-03 00:41:28 +01:00
const otherAccountTxs = txs.filter(
(txMeta) =>
!(
txMeta.txParams.from === address &&
transactionMatchesNetwork(txMeta, chainId, network)
2020-11-03 00:41:28 +01:00
),
);
// Update state
this._saveTxList(otherAccountTxs);
2018-01-31 09:33:15 +01:00
}
//
// PRIVATE METHODS
//
2017-08-11 23:19:35 +02:00
2018-04-19 20:29:26 +02:00
/**
* @param {number} txId - the txMeta Id
* @param {TransactionStatuses[keyof TransactionStatuses]} status - the status to set on the txMeta
* @emits tx:status-update - passes txId and status
* @emits ${txMeta.id}:finished - if it is a finished state. Passes the txMeta
* @emits 'updateBadge'
*/
2020-11-03 00:41:28 +01:00
_setTxStatus(txId, status) {
const txMeta = this.getTx(txId);
if (!txMeta) {
return;
}
txMeta.status = status;
try {
this.updateTx(txMeta, `txStateManager: setting status to ${status}`);
this.emit(`${txMeta.id}:${status}`, txId);
this.emit(`tx:status-update`, txId, status);
if (
[
TRANSACTION_STATUSES.SUBMITTED,
TRANSACTION_STATUSES.REJECTED,
TRANSACTION_STATUSES.FAILED,
].includes(status)
) {
this.emit(`${txMeta.id}:finished`, txMeta);
}
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
} catch (error) {
log.error(error);
}
2017-08-11 23:19:35 +02:00
}
2018-04-19 20:29:26 +02:00
/**
* Saves the new/updated txList. Intended only for internal use
* @param {Array} transactions - the list of transactions to save
*/
2020-11-03 00:41:28 +01:00
_saveTxList(transactions) {
this.store.updateState({ transactions });
2017-08-11 23:19:35 +02:00
}
2020-11-03 00:41:28 +01:00
_removeTx(txId) {
const transactionList = this.getFullTxList();
this._saveTxList(transactionList.filter((txMeta) => txMeta.id !== txId));
}
/**
* Filters out the unapproved transactions
*/
2020-11-03 00:41:28 +01:00
clearUnapprovedTxs() {
const transactions = this.getFullTxList();
2020-11-03 00:41:28 +01:00
const nonUnapprovedTxs = transactions.filter(
(tx) => tx.status !== TRANSACTION_STATUSES.UNAPPROVED,
);
this._saveTxList(nonUnapprovedTxs);
}
}