@metamask/transaction-controller 8.0.0 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,46 +12,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.TransactionController = exports.SPEED_UP_RATE = exports.CANCEL_RATE = exports.WalletDevice = exports.TransactionStatus = exports.HARDFORK = void 0;
15
+ exports.TransactionController = exports.SPEED_UP_RATE = exports.CANCEL_RATE = exports.HARDFORK = void 0;
16
16
  const common_1 = require("@ethereumjs/common");
17
17
  const tx_1 = require("@ethereumjs/tx");
18
18
  const base_controller_1 = require("@metamask/base-controller");
19
19
  const controller_utils_1 = require("@metamask/controller-utils");
20
+ const eth_query_1 = __importDefault(require("@metamask/eth-query"));
20
21
  const async_mutex_1 = require("async-mutex");
21
22
  const eth_method_registry_1 = __importDefault(require("eth-method-registry"));
22
- const eth_query_1 = __importDefault(require("eth-query"));
23
23
  const eth_rpc_errors_1 = require("eth-rpc-errors");
24
24
  const ethereumjs_util_1 = require("ethereumjs-util");
25
25
  const events_1 = require("events");
26
26
  const nonce_tracker_1 = __importDefault(require("nonce-tracker"));
27
27
  const uuid_1 = require("uuid");
28
+ const EtherscanRemoteTransactionSource_1 = require("./EtherscanRemoteTransactionSource");
29
+ const IncomingTransactionHelper_1 = require("./IncomingTransactionHelper");
30
+ const types_1 = require("./types");
28
31
  const utils_1 = require("./utils");
29
32
  exports.HARDFORK = common_1.Hardfork.London;
30
- /**
31
- * The status of the transaction. Each status represents the state of the transaction internally
32
- * in the wallet. Some of these correspond with the state of the transaction on the network, but
33
- * some are wallet-specific.
34
- */
35
- var TransactionStatus;
36
- (function (TransactionStatus) {
37
- TransactionStatus["approved"] = "approved";
38
- TransactionStatus["cancelled"] = "cancelled";
39
- TransactionStatus["confirmed"] = "confirmed";
40
- TransactionStatus["failed"] = "failed";
41
- TransactionStatus["rejected"] = "rejected";
42
- TransactionStatus["signed"] = "signed";
43
- TransactionStatus["submitted"] = "submitted";
44
- TransactionStatus["unapproved"] = "unapproved";
45
- })(TransactionStatus = exports.TransactionStatus || (exports.TransactionStatus = {}));
46
- /**
47
- * Options for wallet device.
48
- */
49
- var WalletDevice;
50
- (function (WalletDevice) {
51
- WalletDevice["MM_MOBILE"] = "metamask_mobile";
52
- WalletDevice["MM_EXTENSION"] = "metamask_extension";
53
- WalletDevice["OTHER"] = "other_device";
54
- })(WalletDevice = exports.WalletDevice || (exports.WalletDevice = {}));
55
33
  /**
56
34
  * Multiplier used to determine a transaction's increased gas fee during cancellation
57
35
  */
@@ -83,34 +61,6 @@ class TransactionController extends base_controller_1.BaseController {
83
61
  constructor({ getNetworkState, onNetworkStateChange, provider, blockTracker, messenger, }, config, state) {
84
62
  super(config, state);
85
63
  this.mutex = new async_mutex_1.Mutex();
86
- this.normalizeTokenTx = (txMeta, currentNetworkID, currentChainId) => {
87
- const time = parseInt(txMeta.timeStamp, 10) * 1000;
88
- const { to, from, gas, gasPrice, gasUsed, hash, contractAddress, tokenDecimal, tokenSymbol, value, } = txMeta;
89
- return {
90
- id: (0, uuid_1.v1)({ msecs: time }),
91
- isTransfer: true,
92
- networkID: currentNetworkID,
93
- chainId: currentChainId,
94
- status: TransactionStatus.confirmed,
95
- time,
96
- transaction: {
97
- chainId: currentChainId,
98
- from,
99
- gas,
100
- gasPrice,
101
- gasUsed,
102
- to,
103
- value,
104
- },
105
- transactionHash: hash,
106
- transferInformation: {
107
- contractAddress,
108
- decimals: Number(tokenDecimal),
109
- symbol: tokenSymbol,
110
- },
111
- verifiedOnBlockchain: false,
112
- };
113
- };
114
64
  /**
115
65
  * EventEmitter instance used to listen to specific transactional events
116
66
  */
@@ -136,8 +86,14 @@ class TransactionController extends base_controller_1.BaseController {
136
86
  this.nonceTracker = new nonce_tracker_1.default({
137
87
  provider,
138
88
  blockTracker,
139
- getPendingTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, TransactionStatus.submitted, this.state.transactions),
140
- getConfirmedTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, TransactionStatus.confirmed, this.state.transactions),
89
+ getPendingTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, types_1.TransactionStatus.submitted, this.state.transactions),
90
+ getConfirmedTransactions: (address) => (0, utils_1.getAndFormatTransactionsForNonceTracker)(address, types_1.TransactionStatus.confirmed, this.state.transactions),
91
+ });
92
+ this.incomingTransactionHelper = new IncomingTransactionHelper_1.IncomingTransactionHelper({
93
+ getNetworkState,
94
+ getEthQuery: () => this.ethQuery,
95
+ transactionLimit: this.config.txHistoryLimit,
96
+ remoteTransactionSource: new EtherscanRemoteTransactionSource_1.EtherscanRemoteTransactionSource(),
141
97
  });
142
98
  onNetworkStateChange(() => {
143
99
  this.ethQuery = new eth_query_1.default(this.provider);
@@ -146,7 +102,7 @@ class TransactionController extends base_controller_1.BaseController {
146
102
  this.poll();
147
103
  }
148
104
  failTransaction(transactionMeta, error) {
149
- const newTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { error, status: TransactionStatus.failed });
105
+ const newTransactionMeta = Object.assign(Object.assign({}, transactionMeta), { error, status: types_1.TransactionStatus.failed });
150
106
  this.updateTransaction(newTransactionMeta);
151
107
  this.hub.emit(`${transactionMeta.id}:finished`, newTransactionMeta);
152
108
  }
@@ -157,43 +113,6 @@ class TransactionController extends base_controller_1.BaseController {
157
113
  return { registryMethod, parsedRegistryMethod };
158
114
  });
159
115
  }
160
- /**
161
- * Normalizes the transaction information from etherscan
162
- * to be compatible with the TransactionMeta interface.
163
- *
164
- * @param txMeta - The transaction.
165
- * @param currentNetworkID - The current network ID.
166
- * @param currentChainId - The current chain ID.
167
- * @returns The normalized transaction.
168
- */
169
- normalizeTx(txMeta, currentNetworkID, currentChainId) {
170
- const time = parseInt(txMeta.timeStamp, 10) * 1000;
171
- const normalizedTransactionBase = {
172
- blockNumber: txMeta.blockNumber,
173
- id: (0, uuid_1.v1)({ msecs: time }),
174
- networkID: currentNetworkID,
175
- chainId: currentChainId,
176
- time,
177
- transaction: {
178
- data: txMeta.input,
179
- from: txMeta.from,
180
- gas: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gas)),
181
- gasPrice: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gasPrice)),
182
- gasUsed: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gasUsed)),
183
- nonce: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.nonce)),
184
- to: txMeta.to,
185
- value: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.value)),
186
- },
187
- transactionHash: txMeta.hash,
188
- verifiedOnBlockchain: false,
189
- };
190
- /* istanbul ignore else */
191
- if (txMeta.isError === '0') {
192
- return Object.assign(Object.assign({}, normalizedTransactionBase), { status: TransactionStatus.confirmed });
193
- }
194
- /* istanbul ignore next */
195
- return Object.assign(Object.assign({}, normalizedTransactionBase), { error: new Error('Transaction failed'), status: TransactionStatus.failed });
196
- }
197
116
  /**
198
117
  * Starts a new polling interval.
199
118
  *
@@ -256,7 +175,7 @@ class TransactionController extends base_controller_1.BaseController {
256
175
  networkID: networkId !== null && networkId !== void 0 ? networkId : undefined,
257
176
  chainId: providerConfig.chainId,
258
177
  origin,
259
- status: TransactionStatus.unapproved,
178
+ status: types_1.TransactionStatus.unapproved,
260
179
  time: Date.now(),
261
180
  transaction,
262
181
  deviceConfirmedOn,
@@ -373,7 +292,7 @@ class TransactionController extends base_controller_1.BaseController {
373
292
  const signedTx = yield this.sign(unsignedEthTx, transactionMeta.transaction.from);
374
293
  const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
375
294
  yield (0, controller_utils_1.query)(this.ethQuery, 'sendRawTransaction', [rawTransaction]);
376
- transactionMeta.status = TransactionStatus.cancelled;
295
+ transactionMeta.status = types_1.TransactionStatus.cancelled;
377
296
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
378
297
  });
379
298
  }
@@ -585,57 +504,19 @@ class TransactionController extends base_controller_1.BaseController {
585
504
  */
586
505
  fetchAll(address, opt) {
587
506
  return __awaiter(this, void 0, void 0, function* () {
588
- const { providerConfig, networkId: currentNetworkID } = this.getNetworkState();
589
- const { chainId: currentChainId, type: networkType } = providerConfig;
590
- const { transactions } = this.state;
591
- const supportedNetworkIds = ['1', '5', '11155111'];
592
- /* istanbul ignore next */
593
- if (currentNetworkID === null ||
594
- !supportedNetworkIds.includes(currentNetworkID)) {
595
- return undefined;
596
- }
597
- const [etherscanTxResponse, etherscanTokenResponse] = yield (0, utils_1.handleTransactionFetch)(networkType, address, this.config.txHistoryLimit, opt);
598
- const normalizedTxs = etherscanTxResponse.result.map((tx) => this.normalizeTx(tx, currentNetworkID, currentChainId));
599
- const normalizedTokenTxs = etherscanTokenResponse.result.map((tx) => this.normalizeTokenTx(tx, currentNetworkID, currentChainId));
600
- const [updateRequired, allTxs] = this.etherscanTransactionStateReconciler([...normalizedTxs, ...normalizedTokenTxs], transactions);
601
- allTxs.sort((a, b) => (a.time < b.time ? -1 : 1));
602
- let latestIncomingTxBlockNumber;
603
- allTxs.forEach((tx) => __awaiter(this, void 0, void 0, function* () {
604
- /* istanbul ignore next */
605
- if (
606
- // Using fallback to networkID only when there is no chainId present. Should be removed when networkID is completely removed.
607
- (tx.chainId === currentChainId ||
608
- (!tx.chainId && tx.networkID === currentNetworkID)) &&
609
- tx.transaction.to &&
610
- tx.transaction.to.toLowerCase() === address.toLowerCase()) {
611
- if (tx.blockNumber &&
612
- (!latestIncomingTxBlockNumber ||
613
- parseInt(latestIncomingTxBlockNumber, 10) <
614
- parseInt(tx.blockNumber, 10))) {
615
- latestIncomingTxBlockNumber = tx.blockNumber;
616
- }
617
- }
618
- /* istanbul ignore else */
619
- if (tx.toSmartContract === undefined) {
620
- // If not `to` is a contract deploy, if not `data` is send eth
621
- if (tx.transaction.to &&
622
- (!tx.transaction.data || tx.transaction.data !== '0x')) {
623
- const code = yield (0, controller_utils_1.query)(this.ethQuery, 'getCode', [
624
- tx.transaction.to,
625
- ]);
626
- tx.toSmartContract = (0, controller_utils_1.isSmartContractCode)(code);
627
- }
628
- else {
629
- tx.toSmartContract = false;
630
- }
631
- }
632
- }));
633
- // Update state only if new transactions were fetched or
634
- // the status or gas data of a transaction has changed
507
+ const { transactions: localTransactions } = this.state;
508
+ const { updateRequired, transactions, latestBlockNumber } = yield this.incomingTransactionHelper.reconcile({
509
+ address,
510
+ localTransactions,
511
+ fromBlock: opt === null || opt === void 0 ? void 0 : opt.fromBlock,
512
+ apiKey: opt === null || opt === void 0 ? void 0 : opt.etherscanApiKey,
513
+ });
635
514
  if (updateRequired) {
636
- this.update({ transactions: this.trimTransactionsForState(allTxs) });
515
+ this.update({
516
+ transactions: this.trimTransactionsForState(transactions),
517
+ });
637
518
  }
638
- return latestIncomingTxBlockNumber;
519
+ return latestBlockNumber;
639
520
  });
640
521
  }
641
522
  processApproval(transactionMeta) {
@@ -664,14 +545,14 @@ class TransactionController extends base_controller_1.BaseController {
664
545
  }
665
546
  const finalMeta = this.getTransaction(transactionId);
666
547
  switch (finalMeta === null || finalMeta === void 0 ? void 0 : finalMeta.status) {
667
- case TransactionStatus.failed:
548
+ case types_1.TransactionStatus.failed:
668
549
  resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(finalMeta.error);
669
550
  throw eth_rpc_errors_1.ethErrors.rpc.internal(finalMeta.error.message);
670
- case TransactionStatus.cancelled:
551
+ case types_1.TransactionStatus.cancelled:
671
552
  const cancelError = eth_rpc_errors_1.ethErrors.rpc.internal('User cancelled the transaction');
672
553
  resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.error(cancelError);
673
554
  throw cancelError;
674
- case TransactionStatus.submitted:
555
+ case types_1.TransactionStatus.submitted:
675
556
  resultCallbacks === null || resultCallbacks === void 0 ? void 0 : resultCallbacks.success();
676
557
  return finalMeta.transactionHash;
677
558
  default:
@@ -710,7 +591,7 @@ class TransactionController extends base_controller_1.BaseController {
710
591
  this.failTransaction(transactionMeta, new Error('No chainId defined.'));
711
592
  return;
712
593
  }
713
- const { approved: status } = TransactionStatus;
594
+ const { approved: status } = types_1.TransactionStatus;
714
595
  let nonceToUse = nonce;
715
596
  // if a nonce already exists on the transactionMeta it means this is a speedup or cancel transaction
716
597
  // so we want to reuse that nonce and hope that it beats the previous attempt to chain. Otherwise use a new locked nonce
@@ -733,7 +614,7 @@ class TransactionController extends base_controller_1.BaseController {
733
614
  }
734
615
  const unsignedEthTx = this.prepareUnsignedEthTx(txParams);
735
616
  const signedTx = yield this.sign(unsignedEthTx, from);
736
- transactionMeta.status = TransactionStatus.signed;
617
+ transactionMeta.status = types_1.TransactionStatus.signed;
737
618
  this.updateTransaction(transactionMeta);
738
619
  const rawTransaction = (0, ethereumjs_util_1.bufferToHex)(signedTx.serialize());
739
620
  transactionMeta.rawTransaction = rawTransaction;
@@ -742,7 +623,7 @@ class TransactionController extends base_controller_1.BaseController {
742
623
  rawTransaction,
743
624
  ]);
744
625
  transactionMeta.transactionHash = transactionHash;
745
- transactionMeta.status = TransactionStatus.submitted;
626
+ transactionMeta.status = types_1.TransactionStatus.submitted;
746
627
  this.updateTransaction(transactionMeta);
747
628
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
748
629
  }
@@ -769,7 +650,7 @@ class TransactionController extends base_controller_1.BaseController {
769
650
  if (!transactionMeta) {
770
651
  return;
771
652
  }
772
- transactionMeta.status = TransactionStatus.rejected;
653
+ transactionMeta.status = types_1.TransactionStatus.rejected;
773
654
  this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
774
655
  const transactions = this.state.transactions.filter(({ id }) => id !== transactionID);
775
656
  this.update({ transactions: this.trimTransactionsForState(transactions) });
@@ -815,10 +696,10 @@ class TransactionController extends base_controller_1.BaseController {
815
696
  * @returns Whether the transaction is in a final state.
816
697
  */
817
698
  isFinalState(status) {
818
- return (status === TransactionStatus.rejected ||
819
- status === TransactionStatus.confirmed ||
820
- status === TransactionStatus.failed ||
821
- status === TransactionStatus.cancelled);
699
+ return (status === types_1.TransactionStatus.rejected ||
700
+ status === types_1.TransactionStatus.confirmed ||
701
+ status === types_1.TransactionStatus.failed ||
702
+ status === types_1.TransactionStatus.cancelled);
822
703
  }
823
704
  /**
824
705
  * Whether the transaction has at least completed all local processing.
@@ -828,11 +709,11 @@ class TransactionController extends base_controller_1.BaseController {
828
709
  */
829
710
  isLocalFinalState(status) {
830
711
  return [
831
- TransactionStatus.cancelled,
832
- TransactionStatus.confirmed,
833
- TransactionStatus.failed,
834
- TransactionStatus.rejected,
835
- TransactionStatus.submitted,
712
+ types_1.TransactionStatus.cancelled,
713
+ types_1.TransactionStatus.confirmed,
714
+ types_1.TransactionStatus.failed,
715
+ types_1.TransactionStatus.rejected,
716
+ types_1.TransactionStatus.submitted,
836
717
  ].includes(status);
837
718
  }
838
719
  /**
@@ -845,7 +726,7 @@ class TransactionController extends base_controller_1.BaseController {
845
726
  return __awaiter(this, void 0, void 0, function* () {
846
727
  const { status, transactionHash } = meta;
847
728
  switch (status) {
848
- case TransactionStatus.confirmed:
729
+ case types_1.TransactionStatus.confirmed:
849
730
  const txReceipt = yield (0, controller_utils_1.query)(this.ethQuery, 'getTransactionReceipt', [
850
731
  transactionHash,
851
732
  ]);
@@ -862,7 +743,7 @@ class TransactionController extends base_controller_1.BaseController {
862
743
  return [meta, false];
863
744
  }
864
745
  return [meta, true];
865
- case TransactionStatus.submitted:
746
+ case types_1.TransactionStatus.submitted:
866
747
  const txObj = yield (0, controller_utils_1.query)(this.ethQuery, 'getTransactionByHash', [
867
748
  transactionHash,
868
749
  ]);
@@ -877,7 +758,7 @@ class TransactionController extends base_controller_1.BaseController {
877
758
  }
878
759
  /* istanbul ignore next */
879
760
  if (txObj === null || txObj === void 0 ? void 0 : txObj.blockNumber) {
880
- meta.status = TransactionStatus.confirmed;
761
+ meta.status = types_1.TransactionStatus.confirmed;
881
762
  this.hub.emit(`${meta.id}:confirmed`, meta);
882
763
  return [meta, true];
883
764
  }
@@ -908,89 +789,6 @@ class TransactionController extends base_controller_1.BaseController {
908
789
  return Number(txReceipt.status) === 0;
909
790
  });
910
791
  }
911
- /**
912
- * Method to verify the state of transactions using Etherscan as a source of truth.
913
- *
914
- * @param remoteTxs - Transactions to reconcile that are from a remote source.
915
- * @param localTxs - Transactions to reconcile that are local.
916
- * @returns A tuple containing a boolean indicating whether or not an update was required, and the updated transaction.
917
- */
918
- etherscanTransactionStateReconciler(remoteTxs, localTxs) {
919
- const updatedTxs = this.getUpdatedTransactions(remoteTxs, localTxs);
920
- const newTxs = this.getNewTransactions(remoteTxs, localTxs);
921
- const updatedLocalTxs = localTxs.map((tx) => {
922
- const txIdx = updatedTxs.findIndex(({ transactionHash }) => transactionHash === tx.transactionHash);
923
- return txIdx === -1 ? tx : updatedTxs[txIdx];
924
- });
925
- const updateRequired = newTxs.length > 0 || updatedLocalTxs.length > 0;
926
- return [updateRequired, [...newTxs, ...updatedLocalTxs]];
927
- }
928
- /**
929
- * Get all transactions that are in the remote transactions array
930
- * but not in the local transactions array.
931
- *
932
- * @param remoteTxs - Array of transactions from remote source.
933
- * @param localTxs - Array of transactions stored locally.
934
- * @returns The new transactions.
935
- */
936
- getNewTransactions(remoteTxs, localTxs) {
937
- return remoteTxs.filter((tx) => {
938
- const alreadyInTransactions = localTxs.find(({ transactionHash }) => transactionHash === tx.transactionHash);
939
- return !alreadyInTransactions;
940
- });
941
- }
942
- /**
943
- * Get all the transactions that are locally outdated with respect
944
- * to a remote source (etherscan or blockchain). The returned array
945
- * contains the transactions with the updated data.
946
- *
947
- * @param remoteTxs - Array of transactions from remote source.
948
- * @param localTxs - Array of transactions stored locally.
949
- * @returns The updated transactions.
950
- */
951
- getUpdatedTransactions(remoteTxs, localTxs) {
952
- return remoteTxs.filter((remoteTx) => {
953
- const isTxOutdated = localTxs.find((localTx) => {
954
- return (remoteTx.transactionHash === localTx.transactionHash &&
955
- this.isTransactionOutdated(remoteTx, localTx));
956
- });
957
- return isTxOutdated;
958
- });
959
- }
960
- /**
961
- * Verifies if a local transaction is outdated with respect to the remote transaction.
962
- *
963
- * @param remoteTx - The remote transaction from Etherscan.
964
- * @param localTx - The local transaction.
965
- * @returns Whether the transaction is outdated.
966
- */
967
- isTransactionOutdated(remoteTx, localTx) {
968
- const statusOutdated = this.isStatusOutdated(remoteTx.transactionHash, localTx.transactionHash, remoteTx.status, localTx.status);
969
- const gasDataOutdated = this.isGasDataOutdated(remoteTx.transaction.gasUsed, localTx.transaction.gasUsed);
970
- return statusOutdated || gasDataOutdated;
971
- }
972
- /**
973
- * Verifies if the status of a local transaction is outdated with respect to the remote transaction.
974
- *
975
- * @param remoteTxHash - Remote transaction hash.
976
- * @param localTxHash - Local transaction hash.
977
- * @param remoteTxStatus - Remote transaction status.
978
- * @param localTxStatus - Local transaction status.
979
- * @returns Whether the status is outdated.
980
- */
981
- isStatusOutdated(remoteTxHash, localTxHash, remoteTxStatus, localTxStatus) {
982
- return remoteTxHash === localTxHash && remoteTxStatus !== localTxStatus;
983
- }
984
- /**
985
- * Verifies if the gas data of a local transaction is outdated with respect to the remote transaction.
986
- *
987
- * @param remoteGasUsed - Remote gas used in the transaction.
988
- * @param localGasUsed - Local gas used in the transaction.
989
- * @returns Whether the gas data is outdated.
990
- */
991
- isGasDataOutdated(remoteGasUsed, localGasUsed) {
992
- return remoteGasUsed !== localGasUsed;
993
- }
994
792
  requestApproval(txMeta) {
995
793
  return __awaiter(this, void 0, void 0, function* () {
996
794
  const id = this.getApprovalId(txMeta);