@metamask-previews/transaction-controller 8.0.1-preview.d32a7cc

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,113 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [8.0.1]
10
+ ### Changed
11
+ - Replace `eth-query` ^2.1.2 with `@metamask/eth-query` ^3.0.1 ([#1546](https://github.com/MetaMask/core/pull/1546))
12
+
13
+ ## [8.0.0]
14
+ ### Changed
15
+ - **BREAKING**: Change `babel-runtime` from a `dependency` to a `peerDependency` ([#1504](https://github.com/MetaMask/core/pull/1504))
16
+ - Update `@metamask/utils` to `^6.2.0` ([#1514](https://github.com/MetaMask/core/pull/1514))
17
+
18
+ ## [7.1.0]
19
+ ### Added
20
+ - Expose `HARDFORK` constant ([#1423](https://github.com/MetaMask/core/pull/1423))
21
+ - Add support for transactions on Linea networks ([#1423](https://github.com/MetaMask/core/pull/1423))
22
+
23
+ ## [7.0.0]
24
+ ### Changed
25
+ - **BREAKING**: Change the approveTransaction and cancelTransaction methods to private ([#1435](https://github.com/MetaMask/core/pull/1435))
26
+ - Consumers should migrate from use of these methods to use of `processApproval`.
27
+ - Update the TransactionController to await the approval request promise before automatically performing the relevant logic, either signing and submitting the transaction, or cancelling it ([#1435](https://github.com/MetaMask/core/pull/1435))
28
+
29
+ ## [6.1.0]
30
+ ### Changed
31
+ - Relax types of `provider` and `blockTracker` options ([#1443](https://github.com/MetaMask/core/pull/1443))
32
+ - The types used to require proxy versions of Provider and BlockTracker. Now they just require the non-proxy versions, which are a strict subset of the proxied versions.
33
+
34
+ ## [6.0.0]
35
+ ### Added
36
+ - Update transaction controller to automatically initiate, finalize, and cancel approval requests as transactions move through states ([#1241](https://github.com/MetaMask/core/pull/1241))
37
+ - The `ApprovalController:addRequest` action will be called when a new transaction is initiated
38
+ - The `ApprovalController:rejectRequest` action will be called if a transaction fails
39
+ - The `ApprovalController:acceptRequest` action will be called when a transaction is approved
40
+
41
+ ### Changed
42
+ - **BREAKING:** Bump to Node 16 ([#1262](https://github.com/MetaMask/core/pull/1262))
43
+ - **BREAKING:** Update `@metamask/network-controller` dependency and peer dependency ([#1367](https://github.com/MetaMask/core/pull/1367))
44
+ - This affects the `getNetworkState` and `onNetworkStateChange` constructor parameters
45
+ - **BREAKING:** Change format of chain ID in state to `Hex` ([#1367](https://github.com/MetaMask/core/pull/1367))
46
+ - The `chainId` property of the `Transaction` type has been changed from `number` to `Hex`
47
+ - The `chainId` property of the `TransactionMeta` type has been changed from a decimal `string` to `Hex`, and the `transaction` property has been updated along with the `Transaction` type (as described above).
48
+ - The state property `transactions` is an array of `TransactionMeta` objects, so it has changed according to the description above.
49
+ - This requires a state migration: each entry should have the `chainId` property converted from a decimal `string` to `Hex`, and the `transaction.chainId` property changed from `number` to `Hex`.
50
+ - The `addTransaction` and `estimateGas` methods now expect the first parameter (`transaction`) to use type `Hex` for the `chainId` property.
51
+ - The `updateTransaction` method now expects the `transactionMeta` parameter to use type `Hex` for the `chainId` property (and for the nested `transaction.chainId` property)
52
+ - **BREAKING:** Add `messenger` as required constructor parameter ([#1241](https://github.com/MetaMask/core/pull/1241))
53
+ - **BREAKING:** Add `@metamask/approval-controller` as a dependency and peer dependency ([#1241](https://github.com/MetaMask/core/pull/1241), [#1393](https://github.com/MetaMask/core/pull/1393))
54
+ - Add `@metamask/utils` dependency ([#1367](https://github.com/MetaMask/core/pull/1367))
55
+
56
+ ### Fixed
57
+ - Fix inaccurate hard-coded `chainId` on incoming token transactions ([#1366](https://github.com/MetaMask/core/pull/1366))
58
+
59
+ ## [5.0.0]
60
+ ### Changed
61
+ - **BREAKING**: peerDeps: @metamask/network-controller@6.0.0->8.0.0 ([#1196](https://github.com/MetaMask/core/pull/1196))
62
+ - deps: eth-rpc-errors@4.0.0->4.0.2 ([#1215](https://github.com/MetaMask/core/pull/1215))
63
+ - Add nonce tracker to transactions controller ([#1147](https://github.com/MetaMask/core/pull/1147))
64
+ - Previously this controller would get the next nonce by calling `eth_getTransactionCount` with a block reference of `pending`. The next nonce would then be returned from our middleware (within `web3-provider-engine`).
65
+ - Instead we're now using the nonce tracker to get the next nonce, dropping our reliance on this `eth_getTransactionCount` middleware. This will let us drop that middleware in a future update without impacting the transaction controller.
66
+ - This should result in no functional changes, except that the nonce middleware is no longer required.
67
+
68
+ ## [4.0.1]
69
+ ### Changed
70
+ - Use `NetworkType` enum for chain configuration ([#1132](https://github.com/MetaMask/core/pull/1132))
71
+
72
+ ## [4.0.0]
73
+ ### Removed
74
+ - **BREAKING:** Remove `isomorphic-fetch` ([#1106](https://github.com/MetaMask/controllers/pull/1106))
75
+ - Consumers must now import `isomorphic-fetch` or another polyfill themselves if they are running in an environment without `fetch`
76
+
77
+ ## [3.0.0]
78
+ ### Added
79
+ - Add Etherscan API support for Sepolia and Goerli ([#1041](https://github.com/MetaMask/controllers/pull/1041))
80
+ - Export `isEIP1559Transaction` function from package ([#1058](https://github.com/MetaMask/controllers/pull/1058))
81
+
82
+ ### Changed
83
+ - **BREAKING**: Drop Etherscan API support for Ropsten, Rinkeby, and Kovan ([#1041](https://github.com/MetaMask/controllers/pull/1041))
84
+ - Rename this repository to `core` ([#1031](https://github.com/MetaMask/controllers/pull/1031))
85
+ - Update `@metamask/controller-utils` package ([#1041](https://github.com/MetaMask/controllers/pull/1041))
86
+
87
+ ## [2.0.0]
88
+ ### Changed
89
+ - **BREAKING:** Update `getNetworkState` constructor option to take an object with `providerConfig` property rather than `providerConfig` ([#995](https://github.com/MetaMask/core/pull/995))
90
+ - Relax dependency on `@metamask/base-controller`, `@metamask/controller-utils`, and `@metamask/network-controller` (use `^` instead of `~`) ([#998](https://github.com/MetaMask/core/pull/998))
91
+
92
+ ## [1.0.0]
93
+ ### Added
94
+ - Initial release
95
+ - As a result of converting our shared controllers repo into a monorepo ([#831](https://github.com/MetaMask/core/pull/831)), we've created this package from select parts of [`@metamask/controllers` v33.0.0](https://github.com/MetaMask/core/tree/v33.0.0), namely:
96
+ - Everything in `src/transaction`
97
+ - Transaction-related functions from `src/util.ts` and accompanying tests
98
+
99
+ All changes listed after this point were applied to this package following the monorepo conversion.
100
+
101
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@8.0.1...HEAD
102
+ [8.0.1]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@8.0.0...@metamask/transaction-controller@8.0.1
103
+ [8.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@7.1.0...@metamask/transaction-controller@8.0.0
104
+ [7.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@7.0.0...@metamask/transaction-controller@7.1.0
105
+ [7.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@6.1.0...@metamask/transaction-controller@7.0.0
106
+ [6.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@6.0.0...@metamask/transaction-controller@6.1.0
107
+ [6.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@5.0.0...@metamask/transaction-controller@6.0.0
108
+ [5.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@4.0.1...@metamask/transaction-controller@5.0.0
109
+ [4.0.1]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@4.0.0...@metamask/transaction-controller@4.0.1
110
+ [4.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@3.0.0...@metamask/transaction-controller@4.0.0
111
+ [3.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@2.0.0...@metamask/transaction-controller@3.0.0
112
+ [2.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@1.0.0...@metamask/transaction-controller@2.0.0
113
+ [1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/transaction-controller@1.0.0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 MetaMask
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # `@metamask/transaction-controller`
2
+
3
+ Stores transactions alongside their periodically updated statuses and manages interactions such as approval and cancellation.
4
+
5
+ ## Installation
6
+
7
+ `yarn add @metamask/transaction-controller`
8
+
9
+ or
10
+
11
+ `npm install @metamask/transaction-controller`
12
+
13
+ ## Contributing
14
+
15
+ This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
@@ -0,0 +1,15 @@
1
+ import type { Hex } from '@metamask/utils';
2
+ import type { RemoteTransactionSource, RemoteTransactionSourceRequest, TransactionMeta } from './types';
3
+ /**
4
+ * A RemoteTransactionSource that fetches transaction data from Etherscan.
5
+ */
6
+ export declare class EtherscanRemoteTransactionSource implements RemoteTransactionSource {
7
+ #private;
8
+ constructor({ apiKey, includeTokenTransfers, }?: {
9
+ apiKey?: string;
10
+ includeTokenTransfers?: boolean;
11
+ });
12
+ isSupportedNetwork(chainId: Hex, _networkId: string): boolean;
13
+ fetchTransactions(request: RemoteTransactionSourceRequest): Promise<TransactionMeta[]>;
14
+ }
15
+ //# sourceMappingURL=EtherscanRemoteTransactionSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EtherscanRemoteTransactionSource.d.ts","sourceRoot":"","sources":["../src/EtherscanRemoteTransactionSource.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAgB3C,OAAO,KAAK,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,eAAe,EAChB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,qBAAa,gCACX,YAAW,uBAAuB;;gBAMtB,EACV,MAAM,EACN,qBAAqB,GACtB,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAO;IAK5D,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAIvD,iBAAiB,CACrB,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,eAAe,EAAE,CAAC;CAgH9B"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
12
+ if (kind === "m") throw new TypeError("Private method is not writable");
13
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
14
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
15
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
16
+ };
17
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
20
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
+ };
22
+ var _EtherscanRemoteTransactionSource_instances, _EtherscanRemoteTransactionSource_apiKey, _EtherscanRemoteTransactionSource_includeTokenTransfers, _EtherscanRemoteTransactionSource_normalizeTransaction, _EtherscanRemoteTransactionSource_normalizeTokenTransaction, _EtherscanRemoteTransactionSource_normalizeTransactionBase;
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.EtherscanRemoteTransactionSource = void 0;
25
+ const controller_utils_1 = require("@metamask/controller-utils");
26
+ const ethereumjs_util_1 = require("ethereumjs-util");
27
+ const uuid_1 = require("uuid");
28
+ const constants_1 = require("./constants");
29
+ const etherscan_1 = require("./etherscan");
30
+ const types_1 = require("./types");
31
+ /**
32
+ * A RemoteTransactionSource that fetches transaction data from Etherscan.
33
+ */
34
+ class EtherscanRemoteTransactionSource {
35
+ constructor({ apiKey, includeTokenTransfers, } = {}) {
36
+ _EtherscanRemoteTransactionSource_instances.add(this);
37
+ _EtherscanRemoteTransactionSource_apiKey.set(this, void 0);
38
+ _EtherscanRemoteTransactionSource_includeTokenTransfers.set(this, void 0);
39
+ __classPrivateFieldSet(this, _EtherscanRemoteTransactionSource_apiKey, apiKey, "f");
40
+ __classPrivateFieldSet(this, _EtherscanRemoteTransactionSource_includeTokenTransfers, includeTokenTransfers !== null && includeTokenTransfers !== void 0 ? includeTokenTransfers : true, "f");
41
+ }
42
+ isSupportedNetwork(chainId, _networkId) {
43
+ return Object.keys(constants_1.ETHERSCAN_SUPPORTED_NETWORKS).includes(chainId);
44
+ }
45
+ fetchTransactions(request) {
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const etherscanRequest = Object.assign(Object.assign({}, request), { apiKey: __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_apiKey, "f"), chainId: request.currentChainId });
48
+ const transactionPromise = (0, etherscan_1.fetchEtherscanTransactions)(etherscanRequest);
49
+ const tokenTransactionPromise = __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_includeTokenTransfers, "f")
50
+ ? (0, etherscan_1.fetchEtherscanTokenTransactions)(etherscanRequest)
51
+ : Promise.resolve({
52
+ result: [],
53
+ });
54
+ const [etherscanTransactions, etherscanTokenTransactions] = yield Promise.all([transactionPromise, tokenTransactionPromise]);
55
+ const transactions = etherscanTransactions.result.map((tx) => __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_instances, "m", _EtherscanRemoteTransactionSource_normalizeTransaction).call(this, tx, request.currentNetworkId, request.currentChainId));
56
+ const tokenTransactions = etherscanTokenTransactions.result.map((tx) => __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_instances, "m", _EtherscanRemoteTransactionSource_normalizeTokenTransaction).call(this, tx, request.currentNetworkId, request.currentChainId));
57
+ return [...transactions, ...tokenTransactions];
58
+ });
59
+ }
60
+ }
61
+ exports.EtherscanRemoteTransactionSource = EtherscanRemoteTransactionSource;
62
+ _EtherscanRemoteTransactionSource_apiKey = new WeakMap(), _EtherscanRemoteTransactionSource_includeTokenTransfers = new WeakMap(), _EtherscanRemoteTransactionSource_instances = new WeakSet(), _EtherscanRemoteTransactionSource_normalizeTransaction = function _EtherscanRemoteTransactionSource_normalizeTransaction(txMeta, currentNetworkId, currentChainId) {
63
+ const base = __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_instances, "m", _EtherscanRemoteTransactionSource_normalizeTransactionBase).call(this, txMeta, currentNetworkId, currentChainId);
64
+ return Object.assign(Object.assign(Object.assign({}, base), { transaction: Object.assign(Object.assign({}, base.transaction), { data: txMeta.input }) }), (txMeta.isError === '0'
65
+ ? { status: types_1.TransactionStatus.confirmed }
66
+ : {
67
+ error: new Error('Transaction failed'),
68
+ status: types_1.TransactionStatus.failed,
69
+ }));
70
+ }, _EtherscanRemoteTransactionSource_normalizeTokenTransaction = function _EtherscanRemoteTransactionSource_normalizeTokenTransaction(txMeta, currentNetworkId, currentChainId) {
71
+ const base = __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_instances, "m", _EtherscanRemoteTransactionSource_normalizeTransactionBase).call(this, txMeta, currentNetworkId, currentChainId);
72
+ return Object.assign(Object.assign({}, base), { isTransfer: true, transferInformation: {
73
+ contractAddress: txMeta.contractAddress,
74
+ decimals: Number(txMeta.tokenDecimal),
75
+ symbol: txMeta.tokenSymbol,
76
+ } });
77
+ }, _EtherscanRemoteTransactionSource_normalizeTransactionBase = function _EtherscanRemoteTransactionSource_normalizeTransactionBase(txMeta, currentNetworkId, currentChainId) {
78
+ const time = parseInt(txMeta.timeStamp, 10) * 1000;
79
+ return {
80
+ blockNumber: txMeta.blockNumber,
81
+ chainId: currentChainId,
82
+ id: (0, uuid_1.v1)({ msecs: time }),
83
+ networkID: currentNetworkId,
84
+ status: types_1.TransactionStatus.confirmed,
85
+ time,
86
+ transaction: {
87
+ from: txMeta.from,
88
+ gas: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gas)),
89
+ gasPrice: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gasPrice)),
90
+ gasUsed: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.gasUsed)),
91
+ nonce: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.nonce)),
92
+ to: txMeta.to,
93
+ value: (0, controller_utils_1.BNToHex)(new ethereumjs_util_1.BN(txMeta.value)),
94
+ },
95
+ transactionHash: txMeta.hash,
96
+ verifiedOnBlockchain: false,
97
+ };
98
+ };
99
+ //# sourceMappingURL=EtherscanRemoteTransactionSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EtherscanRemoteTransactionSource.js","sourceRoot":"","sources":["../src/EtherscanRemoteTransactionSource.ts"],"names":[],"mappings":"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","sourcesContent":["import { BNToHex } from '@metamask/controller-utils';\nimport type { Hex } from '@metamask/utils';\nimport { BN } from 'ethereumjs-util';\nimport { v1 as random } from 'uuid';\n\nimport { ETHERSCAN_SUPPORTED_NETWORKS } from './constants';\nimport type {\n EtherscanTokenTransactionMeta,\n EtherscanTransactionMeta,\n EtherscanTransactionMetaBase,\n EtherscanTransactionRequest,\n EtherscanTransactionResponse,\n} from './etherscan';\nimport {\n fetchEtherscanTokenTransactions,\n fetchEtherscanTransactions,\n} from './etherscan';\nimport type {\n RemoteTransactionSource,\n RemoteTransactionSourceRequest,\n TransactionMeta,\n} from './types';\nimport { TransactionStatus } from './types';\n\n/**\n * A RemoteTransactionSource that fetches transaction data from Etherscan.\n */\nexport class EtherscanRemoteTransactionSource\n implements RemoteTransactionSource\n{\n #apiKey?: string;\n\n #includeTokenTransfers: boolean;\n\n constructor({\n apiKey,\n includeTokenTransfers,\n }: { apiKey?: string; includeTokenTransfers?: boolean } = {}) {\n this.#apiKey = apiKey;\n this.#includeTokenTransfers = includeTokenTransfers ?? true;\n }\n\n isSupportedNetwork(chainId: Hex, _networkId: string): boolean {\n return Object.keys(ETHERSCAN_SUPPORTED_NETWORKS).includes(chainId);\n }\n\n async fetchTransactions(\n request: RemoteTransactionSourceRequest,\n ): Promise<TransactionMeta[]> {\n const etherscanRequest: EtherscanTransactionRequest = {\n ...request,\n apiKey: this.#apiKey,\n chainId: request.currentChainId,\n };\n\n const transactionPromise = fetchEtherscanTransactions(etherscanRequest);\n\n const tokenTransactionPromise = this.#includeTokenTransfers\n ? fetchEtherscanTokenTransactions(etherscanRequest)\n : Promise.resolve({\n result: [] as EtherscanTokenTransactionMeta[],\n } as EtherscanTransactionResponse<EtherscanTokenTransactionMeta>);\n\n const [etherscanTransactions, etherscanTokenTransactions] =\n await Promise.all([transactionPromise, tokenTransactionPromise]);\n\n const transactions = etherscanTransactions.result.map((tx) =>\n this.#normalizeTransaction(\n tx,\n request.currentNetworkId,\n request.currentChainId,\n ),\n );\n\n const tokenTransactions = etherscanTokenTransactions.result.map((tx) =>\n this.#normalizeTokenTransaction(\n tx,\n request.currentNetworkId,\n request.currentChainId,\n ),\n );\n\n return [...transactions, ...tokenTransactions];\n }\n\n #normalizeTransaction(\n txMeta: EtherscanTransactionMeta,\n currentNetworkId: string,\n currentChainId: Hex,\n ): TransactionMeta {\n const base = this.#normalizeTransactionBase(\n txMeta,\n currentNetworkId,\n currentChainId,\n );\n\n return {\n ...base,\n transaction: {\n ...base.transaction,\n data: txMeta.input,\n },\n ...(txMeta.isError === '0'\n ? { status: TransactionStatus.confirmed }\n : {\n error: new Error('Transaction failed'),\n status: TransactionStatus.failed,\n }),\n };\n }\n\n #normalizeTokenTransaction(\n txMeta: EtherscanTokenTransactionMeta,\n currentNetworkId: string,\n currentChainId: Hex,\n ): TransactionMeta {\n const base = this.#normalizeTransactionBase(\n txMeta,\n currentNetworkId,\n currentChainId,\n );\n\n return {\n ...base,\n isTransfer: true,\n transferInformation: {\n contractAddress: txMeta.contractAddress,\n decimals: Number(txMeta.tokenDecimal),\n symbol: txMeta.tokenSymbol,\n },\n };\n }\n\n #normalizeTransactionBase(\n txMeta: EtherscanTransactionMetaBase,\n currentNetworkId: string,\n currentChainId: Hex,\n ): TransactionMeta {\n const time = parseInt(txMeta.timeStamp, 10) * 1000;\n\n return {\n blockNumber: txMeta.blockNumber,\n chainId: currentChainId,\n id: random({ msecs: time }),\n networkID: currentNetworkId,\n status: TransactionStatus.confirmed,\n time,\n transaction: {\n from: txMeta.from,\n gas: BNToHex(new BN(txMeta.gas)),\n gasPrice: BNToHex(new BN(txMeta.gasPrice)),\n gasUsed: BNToHex(new BN(txMeta.gasUsed)),\n nonce: BNToHex(new BN(txMeta.nonce)),\n to: txMeta.to,\n value: BNToHex(new BN(txMeta.value)),\n },\n transactionHash: txMeta.hash,\n verifiedOnBlockchain: false,\n };\n }\n}\n"]}
@@ -0,0 +1,24 @@
1
+ /// <reference types="node" />
2
+ import type { BlockTracker, NetworkState } from '@metamask/network-controller';
3
+ import type { Hex } from '@metamask/utils';
4
+ import EventEmitter from 'events';
5
+ import type { RemoteTransactionSource, TransactionMeta } from './types';
6
+ export declare class IncomingTransactionHelper {
7
+ #private;
8
+ hub: EventEmitter;
9
+ constructor({ blockTracker, getCurrentAccount, getLocalTransactions, getNetworkState, isEnabled, lastFetchedBlockNumbers, remoteTransactionSource, transactionLimit, updateTransactions, }: {
10
+ blockTracker: BlockTracker;
11
+ getCurrentAccount: () => string;
12
+ getNetworkState: () => NetworkState;
13
+ getLocalTransactions?: () => TransactionMeta[];
14
+ isEnabled?: () => boolean;
15
+ lastFetchedBlockNumbers?: Record<string, number>;
16
+ remoteTransactionSource: RemoteTransactionSource;
17
+ transactionLimit?: number;
18
+ updateTransactions?: boolean;
19
+ });
20
+ start(): void;
21
+ stop(): void;
22
+ update(latestBlockNumberHex?: Hex): Promise<void>;
23
+ }
24
+ //# sourceMappingURL=IncomingTransactionHelper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IncomingTransactionHelper.d.ts","sourceRoot":"","sources":["../src/IncomingTransactionHelper.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC/E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAOxE,qBAAa,yBAAyB;;IACpC,GAAG,EAAE,YAAY,CAAC;gBA0BN,EACV,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,SAAS,EACT,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,GACnB,EAAE;QACD,YAAY,EAAE,YAAY,CAAC;QAC3B,iBAAiB,EAAE,MAAM,MAAM,CAAC;QAChC,eAAe,EAAE,MAAM,YAAY,CAAC;QACpC,oBAAoB,CAAC,EAAE,MAAM,eAAe,EAAE,CAAC;QAC/C,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;QAC1B,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjD,uBAAuB,EAAE,uBAAuB,CAAC;QACjD,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;KAC9B;IAyBD,KAAK;IAaL,IAAI;IAKE,MAAM,CAAC,oBAAoB,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAmLxD"}
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
12
+ if (kind === "m") throw new TypeError("Private method is not writable");
13
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
14
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
15
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
16
+ };
17
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
20
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ var _IncomingTransactionHelper_instances, _IncomingTransactionHelper_blockTracker, _IncomingTransactionHelper_getCurrentAccount, _IncomingTransactionHelper_getLocalTransactions, _IncomingTransactionHelper_getNetworkState, _IncomingTransactionHelper_isEnabled, _IncomingTransactionHelper_isRunning, _IncomingTransactionHelper_lastFetchedBlockNumbers, _IncomingTransactionHelper_mutex, _IncomingTransactionHelper_onLatestBlock, _IncomingTransactionHelper_remoteTransactionSource, _IncomingTransactionHelper_transactionLimit, _IncomingTransactionHelper_updateTransactions, _IncomingTransactionHelper_sortTransactionsByTime, _IncomingTransactionHelper_getNewTransactions, _IncomingTransactionHelper_getUpdatedTransactions, _IncomingTransactionHelper_isTransactionOutdated, _IncomingTransactionHelper_getFromBlock, _IncomingTransactionHelper_updateLastFetchedBlockNumber, _IncomingTransactionHelper_getBlockNumberKey, _IncomingTransactionHelper_canStart, _IncomingTransactionHelper_getCurrentChainId, _IncomingTransactionHelper_getCurrentNetworkId;
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.IncomingTransactionHelper = void 0;
28
+ const async_mutex_1 = require("async-mutex");
29
+ const events_1 = __importDefault(require("events"));
30
+ const UPDATE_CHECKS = [
31
+ (txMeta) => txMeta.status,
32
+ (txMeta) => txMeta.transaction.gasUsed,
33
+ ];
34
+ class IncomingTransactionHelper {
35
+ constructor({ blockTracker, getCurrentAccount, getLocalTransactions, getNetworkState, isEnabled, lastFetchedBlockNumbers, remoteTransactionSource, transactionLimit, updateTransactions, }) {
36
+ _IncomingTransactionHelper_instances.add(this);
37
+ _IncomingTransactionHelper_blockTracker.set(this, void 0);
38
+ _IncomingTransactionHelper_getCurrentAccount.set(this, void 0);
39
+ _IncomingTransactionHelper_getLocalTransactions.set(this, void 0);
40
+ _IncomingTransactionHelper_getNetworkState.set(this, void 0);
41
+ _IncomingTransactionHelper_isEnabled.set(this, void 0);
42
+ _IncomingTransactionHelper_isRunning.set(this, void 0);
43
+ _IncomingTransactionHelper_lastFetchedBlockNumbers.set(this, void 0);
44
+ _IncomingTransactionHelper_mutex.set(this, new async_mutex_1.Mutex());
45
+ _IncomingTransactionHelper_onLatestBlock.set(this, void 0);
46
+ _IncomingTransactionHelper_remoteTransactionSource.set(this, void 0);
47
+ _IncomingTransactionHelper_transactionLimit.set(this, void 0);
48
+ _IncomingTransactionHelper_updateTransactions.set(this, void 0);
49
+ this.hub = new events_1.default();
50
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_blockTracker, blockTracker, "f");
51
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_getCurrentAccount, getCurrentAccount, "f");
52
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_getLocalTransactions, getLocalTransactions || (() => []), "f");
53
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_getNetworkState, getNetworkState, "f");
54
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_isEnabled, isEnabled !== null && isEnabled !== void 0 ? isEnabled : (() => true), "f");
55
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_isRunning, false, "f");
56
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_lastFetchedBlockNumbers, lastFetchedBlockNumbers !== null && lastFetchedBlockNumbers !== void 0 ? lastFetchedBlockNumbers : {}, "f");
57
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_remoteTransactionSource, remoteTransactionSource, "f");
58
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_transactionLimit, transactionLimit, "f");
59
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_updateTransactions, updateTransactions !== null && updateTransactions !== void 0 ? updateTransactions : false, "f");
60
+ // Using a property instead of a method to provide a listener reference
61
+ // with the correct scope that we can remove later if stopped.
62
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_onLatestBlock, (blockNumberHex) => __awaiter(this, void 0, void 0, function* () {
63
+ try {
64
+ yield this.update(blockNumberHex);
65
+ }
66
+ catch (error) {
67
+ console.error('Error while checking incoming transactions', error);
68
+ }
69
+ }), "f");
70
+ }
71
+ start() {
72
+ if (__classPrivateFieldGet(this, _IncomingTransactionHelper_isRunning, "f")) {
73
+ return;
74
+ }
75
+ if (!__classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_canStart).call(this)) {
76
+ return;
77
+ }
78
+ __classPrivateFieldGet(this, _IncomingTransactionHelper_blockTracker, "f").addListener('latest', __classPrivateFieldGet(this, _IncomingTransactionHelper_onLatestBlock, "f"));
79
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_isRunning, true, "f");
80
+ }
81
+ stop() {
82
+ __classPrivateFieldGet(this, _IncomingTransactionHelper_blockTracker, "f").removeListener('latest', __classPrivateFieldGet(this, _IncomingTransactionHelper_onLatestBlock, "f"));
83
+ __classPrivateFieldSet(this, _IncomingTransactionHelper_isRunning, false, "f");
84
+ }
85
+ update(latestBlockNumberHex) {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ const releaseLock = yield __classPrivateFieldGet(this, _IncomingTransactionHelper_mutex, "f").acquire();
88
+ try {
89
+ if (!__classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_canStart).call(this)) {
90
+ return;
91
+ }
92
+ const latestBlockNumber = parseInt(latestBlockNumberHex || (yield __classPrivateFieldGet(this, _IncomingTransactionHelper_blockTracker, "f").getLatestBlock()), 16);
93
+ const fromBlock = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getFromBlock).call(this, latestBlockNumber);
94
+ const address = __classPrivateFieldGet(this, _IncomingTransactionHelper_getCurrentAccount, "f").call(this);
95
+ const currentChainId = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getCurrentChainId).call(this);
96
+ const currentNetworkId = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getCurrentNetworkId).call(this);
97
+ let remoteTransactions = [];
98
+ try {
99
+ remoteTransactions =
100
+ yield __classPrivateFieldGet(this, _IncomingTransactionHelper_remoteTransactionSource, "f").fetchTransactions({
101
+ address,
102
+ currentChainId,
103
+ currentNetworkId,
104
+ fromBlock,
105
+ limit: __classPrivateFieldGet(this, _IncomingTransactionHelper_transactionLimit, "f"),
106
+ });
107
+ }
108
+ catch (error) {
109
+ return;
110
+ }
111
+ if (!__classPrivateFieldGet(this, _IncomingTransactionHelper_updateTransactions, "f")) {
112
+ remoteTransactions = remoteTransactions.filter((tx) => { var _a; return ((_a = tx.transaction.to) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === address.toLowerCase(); });
113
+ }
114
+ const localTransactions = !__classPrivateFieldGet(this, _IncomingTransactionHelper_updateTransactions, "f")
115
+ ? []
116
+ : __classPrivateFieldGet(this, _IncomingTransactionHelper_getLocalTransactions, "f").call(this);
117
+ const newTransactions = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getNewTransactions).call(this, remoteTransactions, localTransactions);
118
+ const updatedTransactions = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getUpdatedTransactions).call(this, remoteTransactions, localTransactions);
119
+ if (newTransactions.length > 0 || updatedTransactions.length > 0) {
120
+ __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_sortTransactionsByTime).call(this, newTransactions);
121
+ __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_sortTransactionsByTime).call(this, updatedTransactions);
122
+ this.hub.emit('transactions', {
123
+ added: newTransactions,
124
+ updated: updatedTransactions,
125
+ });
126
+ }
127
+ __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_updateLastFetchedBlockNumber).call(this, remoteTransactions);
128
+ }
129
+ finally {
130
+ releaseLock();
131
+ }
132
+ });
133
+ }
134
+ }
135
+ exports.IncomingTransactionHelper = IncomingTransactionHelper;
136
+ _IncomingTransactionHelper_blockTracker = new WeakMap(), _IncomingTransactionHelper_getCurrentAccount = new WeakMap(), _IncomingTransactionHelper_getLocalTransactions = new WeakMap(), _IncomingTransactionHelper_getNetworkState = new WeakMap(), _IncomingTransactionHelper_isEnabled = new WeakMap(), _IncomingTransactionHelper_isRunning = new WeakMap(), _IncomingTransactionHelper_lastFetchedBlockNumbers = new WeakMap(), _IncomingTransactionHelper_mutex = new WeakMap(), _IncomingTransactionHelper_onLatestBlock = new WeakMap(), _IncomingTransactionHelper_remoteTransactionSource = new WeakMap(), _IncomingTransactionHelper_transactionLimit = new WeakMap(), _IncomingTransactionHelper_updateTransactions = new WeakMap(), _IncomingTransactionHelper_instances = new WeakSet(), _IncomingTransactionHelper_sortTransactionsByTime = function _IncomingTransactionHelper_sortTransactionsByTime(transactions) {
137
+ transactions.sort((a, b) => (a.time < b.time ? -1 : 1));
138
+ }, _IncomingTransactionHelper_getNewTransactions = function _IncomingTransactionHelper_getNewTransactions(remoteTxs, localTxs) {
139
+ return remoteTxs.filter((tx) => !localTxs.some(({ transactionHash }) => transactionHash === tx.transactionHash));
140
+ }, _IncomingTransactionHelper_getUpdatedTransactions = function _IncomingTransactionHelper_getUpdatedTransactions(remoteTxs, localTxs) {
141
+ return remoteTxs.filter((remoteTx) => localTxs.some((localTx) => remoteTx.transactionHash === localTx.transactionHash &&
142
+ __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_isTransactionOutdated).call(this, remoteTx, localTx)));
143
+ }, _IncomingTransactionHelper_isTransactionOutdated = function _IncomingTransactionHelper_isTransactionOutdated(remoteTx, localTx) {
144
+ return UPDATE_CHECKS.some((getValue) => getValue(remoteTx) !== getValue(localTx));
145
+ }, _IncomingTransactionHelper_getFromBlock = function _IncomingTransactionHelper_getFromBlock(latestBlockNumber) {
146
+ const lastFetchedKey = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getBlockNumberKey).call(this);
147
+ const lastFetchedBlockNumber = __classPrivateFieldGet(this, _IncomingTransactionHelper_lastFetchedBlockNumbers, "f")[lastFetchedKey];
148
+ if (lastFetchedBlockNumber) {
149
+ return lastFetchedBlockNumber + 1;
150
+ }
151
+ // Avoid using latest block as remote transaction source
152
+ // may not have indexed it yet
153
+ return Math.max(latestBlockNumber - 10, 0);
154
+ }, _IncomingTransactionHelper_updateLastFetchedBlockNumber = function _IncomingTransactionHelper_updateLastFetchedBlockNumber(remoteTxs) {
155
+ let lastFetchedBlockNumber = -1;
156
+ for (const tx of remoteTxs) {
157
+ const currentBlockNumberValue = tx.blockNumber
158
+ ? parseInt(tx.blockNumber, 10)
159
+ : -1;
160
+ lastFetchedBlockNumber = Math.max(lastFetchedBlockNumber, currentBlockNumberValue);
161
+ }
162
+ if (lastFetchedBlockNumber === -1) {
163
+ return;
164
+ }
165
+ const lastFetchedKey = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getBlockNumberKey).call(this);
166
+ const previousValue = __classPrivateFieldGet(this, _IncomingTransactionHelper_lastFetchedBlockNumbers, "f")[lastFetchedKey];
167
+ if (previousValue === lastFetchedBlockNumber) {
168
+ return;
169
+ }
170
+ __classPrivateFieldGet(this, _IncomingTransactionHelper_lastFetchedBlockNumbers, "f")[lastFetchedKey] = lastFetchedBlockNumber;
171
+ this.hub.emit('updatedLastFetchedBlockNumbers', {
172
+ lastFetchedBlockNumbers: __classPrivateFieldGet(this, _IncomingTransactionHelper_lastFetchedBlockNumbers, "f"),
173
+ blockNumber: lastFetchedBlockNumber,
174
+ });
175
+ }, _IncomingTransactionHelper_getBlockNumberKey = function _IncomingTransactionHelper_getBlockNumberKey() {
176
+ return `${__classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getCurrentChainId).call(this)}#${__classPrivateFieldGet(this, _IncomingTransactionHelper_getCurrentAccount, "f").call(this).toLowerCase()}`;
177
+ }, _IncomingTransactionHelper_canStart = function _IncomingTransactionHelper_canStart() {
178
+ const isEnabled = __classPrivateFieldGet(this, _IncomingTransactionHelper_isEnabled, "f").call(this);
179
+ const currentChainId = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getCurrentChainId).call(this);
180
+ const currentNetworkId = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getCurrentNetworkId).call(this);
181
+ const isSupportedNetwork = __classPrivateFieldGet(this, _IncomingTransactionHelper_remoteTransactionSource, "f").isSupportedNetwork(currentChainId, currentNetworkId);
182
+ return isEnabled && isSupportedNetwork;
183
+ }, _IncomingTransactionHelper_getCurrentChainId = function _IncomingTransactionHelper_getCurrentChainId() {
184
+ return __classPrivateFieldGet(this, _IncomingTransactionHelper_getNetworkState, "f").call(this).providerConfig.chainId;
185
+ }, _IncomingTransactionHelper_getCurrentNetworkId = function _IncomingTransactionHelper_getCurrentNetworkId() {
186
+ return __classPrivateFieldGet(this, _IncomingTransactionHelper_getNetworkState, "f").call(this).networkId;
187
+ };
188
+ //# sourceMappingURL=IncomingTransactionHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IncomingTransactionHelper.js","sourceRoot":"","sources":["../src/IncomingTransactionHelper.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BlockTracker, NetworkState } from '@metamask/network-controller';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport EventEmitter from 'events';\n\nimport type { RemoteTransactionSource, TransactionMeta } from './types';\n\nconst UPDATE_CHECKS: ((txMeta: TransactionMeta) => any)[] = [\n (txMeta) => txMeta.status,\n (txMeta) => txMeta.transaction.gasUsed,\n];\n\nexport class IncomingTransactionHelper {\n hub: EventEmitter;\n\n #blockTracker: BlockTracker;\n\n #getCurrentAccount: () => string;\n\n #getLocalTransactions: () => TransactionMeta[];\n\n #getNetworkState: () => NetworkState;\n\n #isEnabled: () => boolean;\n\n #isRunning: boolean;\n\n #lastFetchedBlockNumbers: Record<string, number>;\n\n #mutex = new Mutex();\n\n #onLatestBlock: (blockNumberHex: Hex) => Promise<void>;\n\n #remoteTransactionSource: RemoteTransactionSource;\n\n #transactionLimit?: number;\n\n #updateTransactions: boolean;\n\n constructor({\n blockTracker,\n getCurrentAccount,\n getLocalTransactions,\n getNetworkState,\n isEnabled,\n lastFetchedBlockNumbers,\n remoteTransactionSource,\n transactionLimit,\n updateTransactions,\n }: {\n blockTracker: BlockTracker;\n getCurrentAccount: () => string;\n getNetworkState: () => NetworkState;\n getLocalTransactions?: () => TransactionMeta[];\n isEnabled?: () => boolean;\n lastFetchedBlockNumbers?: Record<string, number>;\n remoteTransactionSource: RemoteTransactionSource;\n transactionLimit?: number;\n updateTransactions?: boolean;\n }) {\n this.hub = new EventEmitter();\n\n this.#blockTracker = blockTracker;\n this.#getCurrentAccount = getCurrentAccount;\n this.#getLocalTransactions = getLocalTransactions || (() => []);\n this.#getNetworkState = getNetworkState;\n this.#isEnabled = isEnabled ?? (() => true);\n this.#isRunning = false;\n this.#lastFetchedBlockNumbers = lastFetchedBlockNumbers ?? {};\n this.#remoteTransactionSource = remoteTransactionSource;\n this.#transactionLimit = transactionLimit;\n this.#updateTransactions = updateTransactions ?? false;\n\n // Using a property instead of a method to provide a listener reference\n // with the correct scope that we can remove later if stopped.\n this.#onLatestBlock = async (blockNumberHex: Hex) => {\n try {\n await this.update(blockNumberHex);\n } catch (error) {\n console.error('Error while checking incoming transactions', error);\n }\n };\n }\n\n start() {\n if (this.#isRunning) {\n return;\n }\n\n if (!this.#canStart()) {\n return;\n }\n\n this.#blockTracker.addListener('latest', this.#onLatestBlock);\n this.#isRunning = true;\n }\n\n stop() {\n this.#blockTracker.removeListener('latest', this.#onLatestBlock);\n this.#isRunning = false;\n }\n\n async update(latestBlockNumberHex?: Hex): Promise<void> {\n const releaseLock = await this.#mutex.acquire();\n\n try {\n if (!this.#canStart()) {\n return;\n }\n\n const latestBlockNumber = parseInt(\n latestBlockNumberHex || (await this.#blockTracker.getLatestBlock()),\n 16,\n );\n\n const fromBlock = this.#getFromBlock(latestBlockNumber);\n const address = this.#getCurrentAccount();\n const currentChainId = this.#getCurrentChainId();\n const currentNetworkId = this.#getCurrentNetworkId();\n\n let remoteTransactions = [];\n\n try {\n remoteTransactions =\n await this.#remoteTransactionSource.fetchTransactions({\n address,\n currentChainId,\n currentNetworkId,\n fromBlock,\n limit: this.#transactionLimit,\n });\n } catch (error: any) {\n return;\n }\n\n if (!this.#updateTransactions) {\n remoteTransactions = remoteTransactions.filter(\n (tx) => tx.transaction.to?.toLowerCase() === address.toLowerCase(),\n );\n }\n\n const localTransactions = !this.#updateTransactions\n ? []\n : this.#getLocalTransactions();\n\n const newTransactions = this.#getNewTransactions(\n remoteTransactions,\n localTransactions,\n );\n\n const updatedTransactions = this.#getUpdatedTransactions(\n remoteTransactions,\n localTransactions,\n );\n\n if (newTransactions.length > 0 || updatedTransactions.length > 0) {\n this.#sortTransactionsByTime(newTransactions);\n this.#sortTransactionsByTime(updatedTransactions);\n\n this.hub.emit('transactions', {\n added: newTransactions,\n updated: updatedTransactions,\n });\n }\n\n this.#updateLastFetchedBlockNumber(remoteTransactions);\n } finally {\n releaseLock();\n }\n }\n\n #sortTransactionsByTime(transactions: TransactionMeta[]) {\n transactions.sort((a, b) => (a.time < b.time ? -1 : 1));\n }\n\n #getNewTransactions(\n remoteTxs: TransactionMeta[],\n localTxs: TransactionMeta[],\n ): TransactionMeta[] {\n return remoteTxs.filter(\n (tx) =>\n !localTxs.some(\n ({ transactionHash }) => transactionHash === tx.transactionHash,\n ),\n );\n }\n\n #getUpdatedTransactions(\n remoteTxs: TransactionMeta[],\n localTxs: TransactionMeta[],\n ): TransactionMeta[] {\n return remoteTxs.filter((remoteTx) =>\n localTxs.some(\n (localTx) =>\n remoteTx.transactionHash === localTx.transactionHash &&\n this.#isTransactionOutdated(remoteTx, localTx),\n ),\n );\n }\n\n #isTransactionOutdated(\n remoteTx: TransactionMeta,\n localTx: TransactionMeta,\n ): boolean {\n return UPDATE_CHECKS.some(\n (getValue) => getValue(remoteTx) !== getValue(localTx),\n );\n }\n\n #getFromBlock(latestBlockNumber: number): number {\n const lastFetchedKey = this.#getBlockNumberKey();\n\n const lastFetchedBlockNumber =\n this.#lastFetchedBlockNumbers[lastFetchedKey];\n\n if (lastFetchedBlockNumber) {\n return lastFetchedBlockNumber + 1;\n }\n\n // Avoid using latest block as remote transaction source\n // may not have indexed it yet\n return Math.max(latestBlockNumber - 10, 0);\n }\n\n #updateLastFetchedBlockNumber(remoteTxs: TransactionMeta[]) {\n let lastFetchedBlockNumber = -1;\n\n for (const tx of remoteTxs) {\n const currentBlockNumberValue = tx.blockNumber\n ? parseInt(tx.blockNumber, 10)\n : -1;\n\n lastFetchedBlockNumber = Math.max(\n lastFetchedBlockNumber,\n currentBlockNumberValue,\n );\n }\n\n if (lastFetchedBlockNumber === -1) {\n return;\n }\n\n const lastFetchedKey = this.#getBlockNumberKey();\n const previousValue = this.#lastFetchedBlockNumbers[lastFetchedKey];\n\n if (previousValue === lastFetchedBlockNumber) {\n return;\n }\n\n this.#lastFetchedBlockNumbers[lastFetchedKey] = lastFetchedBlockNumber;\n\n this.hub.emit('updatedLastFetchedBlockNumbers', {\n lastFetchedBlockNumbers: this.#lastFetchedBlockNumbers,\n blockNumber: lastFetchedBlockNumber,\n });\n }\n\n #getBlockNumberKey(): string {\n return `${this.#getCurrentChainId()}#${this.#getCurrentAccount().toLowerCase()}`;\n }\n\n #canStart(): boolean {\n const isEnabled = this.#isEnabled();\n const currentChainId = this.#getCurrentChainId();\n const currentNetworkId = this.#getCurrentNetworkId();\n\n const isSupportedNetwork = this.#remoteTransactionSource.isSupportedNetwork(\n currentChainId,\n currentNetworkId,\n );\n\n return isEnabled && isSupportedNetwork;\n }\n\n #getCurrentChainId(): Hex {\n return this.#getNetworkState().providerConfig.chainId;\n }\n\n #getCurrentNetworkId(): string {\n return this.#getNetworkState().networkId as string;\n }\n}\n"]}