@metamask/transaction-controller 8.0.1 → 9.1.0

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 CHANGED
@@ -6,6 +6,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [9.1.0]
10
+ ### Added
11
+ - Add `blockTimestamp` to `TransactionMetaBase` type ([#1616](https://github.com/MetaMask/core/pull/1616))
12
+ - Update `queryTransactionStatuses` to populate `blockTimestamp` on each transaction when it is verified ([#1616](https://github.com/MetaMask/core/pull/1616))
13
+
14
+ ### Changed
15
+ - Bump dependency and peer dependency on `@metamask/approval-controller` to ^3.5.1
16
+ - Bump dependency on `@metamask/base-controller` to ^3.2.1
17
+ - Bump dependency on `@metamask/controller-utils` to ^4.3.2
18
+ - Bump dependency and peer dependency on `@metamask/network-controller` to ^12.1.2
19
+
20
+ ## [9.0.0]
21
+ ### Added
22
+ - Add `baseFeePerGas` to transaction metadata ([#1590](https://github.com/MetaMask/core/pull/1590))
23
+ - Add `txReceipt` to transaction metadata ([#1592](https://github.com/MetaMask/core/pull/1592))
24
+ - Add `initApprovals` method to generate approval requests from unapproved transactions ([#1575](https://github.com/MetaMask/core/pull/1575))
25
+ - Add `dappSuggestedGasFees` to transaction metadata ([#1617](https://github.com/MetaMask/core/pull/1617))
26
+ - Add optional `incomingTransactions` constructor arguments ([#1579](https://github.com/MetaMask/core/pull/1579))
27
+ - `apiKey`
28
+ - `includeTokenTransfers`
29
+ - `isEnabled`
30
+ - `updateTransactions`
31
+ - Add incoming transaction methods ([#1579](https://github.com/MetaMask/core/pull/1579))
32
+ - `startIncomingTransactionPolling`
33
+ - `stopIncomingTransactionPolling`
34
+ - `updateIncomingTransactions`
35
+ - Add `requireApproval` option to `addTransaction` method options ([#1580](https://github.com/MetaMask/core/pull/1580))
36
+ - Add `address` argument to `wipeTransactions` method ([#1573](https://github.com/MetaMask/core/pull/1573))
37
+
38
+ ### Changed
39
+ - **BREAKING**: Add required `getSelectedAddress` callback argument to constructor ([#1579](https://github.com/MetaMask/core/pull/1579))
40
+ - **BREAKING**: Add `isSupportedNetwork` method to `RemoteTransactionSource` interface ([#1579](https://github.com/MetaMask/core/pull/1579))
41
+ - **BREAKING**: Move all but first argument to options bag in `addTransaction` method ([#1576](https://github.com/MetaMask/core/pull/1576))
42
+ - **BREAKING**: Update properties of `RemoteTransactionSourceRequest` type ([#1579](https://github.com/MetaMask/core/pull/1579))
43
+ - The `fromBlock` property has changed from `string` to `number`
44
+ - The `networkType` property has been removed
45
+ - This type is intended mainly for internal use, so it's likely this change doesn't affect most projects
46
+
47
+ ### Removed
48
+ - **BREAKING**: Remove `fetchAll` method ([#1579](https://github.com/MetaMask/core/pull/1579))
49
+ - This method was used to fetch transaction history from Etherscan
50
+ - This is now handled automatically by the controller on each new block, if polling is enabled
51
+ - Polling can be enabled or disabled by calling `startIncomingTransactionPolling` or `stopIncomingTransactionPolling` respectively
52
+ - An immediate update can be requested by calling `updateIncomingTransactions`
53
+ - The new constructor parameter `incomingTransactions.isEnabled` acts as an override to disable this functionality based on a client preference for example
54
+ - **BREAKING**: Remove `prepareUnsignedEthTx` and `getCommonConfiguration` methods ([#1581](https://github.com/MetaMask/core/pull/1581))
55
+ - These methods were intended mainly for internal use, so it's likely this change doesn't affect most projects
56
+
9
57
  ## [8.0.1]
10
58
  ### Changed
11
59
  - Replace `eth-query` ^2.1.2 with `@metamask/eth-query` ^3.0.1 ([#1546](https://github.com/MetaMask/core/pull/1546))
@@ -98,7 +146,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
98
146
 
99
147
  All changes listed after this point were applied to this package following the monorepo conversion.
100
148
 
101
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@8.0.1...HEAD
149
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@9.1.0...HEAD
150
+ [9.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@9.0.0...@metamask/transaction-controller@9.1.0
151
+ [9.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@8.0.1...@metamask/transaction-controller@9.0.0
102
152
  [8.0.1]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@8.0.0...@metamask/transaction-controller@8.0.1
103
153
  [8.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@7.1.0...@metamask/transaction-controller@8.0.0
104
154
  [7.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@7.0.0...@metamask/transaction-controller@7.1.0
@@ -1,15 +1,15 @@
1
+ import type { Hex } from '@metamask/utils';
1
2
  import type { RemoteTransactionSource, RemoteTransactionSourceRequest, TransactionMeta } from './types';
2
3
  /**
3
4
  * A RemoteTransactionSource that fetches transaction data from Etherscan.
4
5
  */
5
6
  export declare class EtherscanRemoteTransactionSource implements RemoteTransactionSource {
6
7
  #private;
7
- /**
8
- * Retrieve transaction data from Etherscan.
9
- *
10
- * @param request - The configuration required to fetch Etherscan transaction data.
11
- * @returns An array of transaction metadata.
12
- */
8
+ constructor({ apiKey, includeTokenTransfers, }?: {
9
+ apiKey?: string;
10
+ includeTokenTransfers?: boolean;
11
+ });
12
+ isSupportedNetwork(chainId: Hex, _networkId: string): boolean;
13
13
  fetchTransactions(request: RemoteTransactionSourceRequest): Promise<TransactionMeta[]>;
14
14
  }
15
15
  //# sourceMappingURL=EtherscanRemoteTransactionSource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EtherscanRemoteTransactionSource.d.ts","sourceRoot":"","sources":["../src/EtherscanRemoteTransactionSource.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,eAAe,EAChB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,qBAAa,gCACX,YAAW,uBAAuB;;IAElC;;;;;OAKG;IACG,iBAAiB,CACrB,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,eAAe,EAAE,CAAC;CAqG9B"}
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"}
@@ -8,38 +8,50 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
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
+ };
11
17
  var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
12
18
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
13
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");
14
20
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
21
  };
16
- var _EtherscanRemoteTransactionSource_instances, _EtherscanRemoteTransactionSource_normalizeTransaction, _EtherscanRemoteTransactionSource_normalizeTokenTransaction, _EtherscanRemoteTransactionSource_normalizeTransactionBase;
22
+ var _EtherscanRemoteTransactionSource_instances, _EtherscanRemoteTransactionSource_apiKey, _EtherscanRemoteTransactionSource_includeTokenTransfers, _EtherscanRemoteTransactionSource_normalizeTransaction, _EtherscanRemoteTransactionSource_normalizeTokenTransaction, _EtherscanRemoteTransactionSource_normalizeTransactionBase;
17
23
  Object.defineProperty(exports, "__esModule", { value: true });
18
24
  exports.EtherscanRemoteTransactionSource = void 0;
19
25
  const controller_utils_1 = require("@metamask/controller-utils");
20
26
  const ethereumjs_util_1 = require("ethereumjs-util");
21
27
  const uuid_1 = require("uuid");
28
+ const constants_1 = require("./constants");
22
29
  const etherscan_1 = require("./etherscan");
23
30
  const types_1 = require("./types");
24
31
  /**
25
32
  * A RemoteTransactionSource that fetches transaction data from Etherscan.
26
33
  */
27
34
  class EtherscanRemoteTransactionSource {
28
- constructor() {
35
+ constructor({ apiKey, includeTokenTransfers, } = {}) {
29
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);
30
44
  }
31
- /**
32
- * Retrieve transaction data from Etherscan.
33
- *
34
- * @param request - The configuration required to fetch Etherscan transaction data.
35
- * @returns An array of transaction metadata.
36
- */
37
45
  fetchTransactions(request) {
38
46
  return __awaiter(this, void 0, void 0, function* () {
39
- const [etherscanTransactions, etherscanTokenTransactions] = yield Promise.all([
40
- (0, etherscan_1.fetchEtherscanTransactions)(request),
41
- (0, etherscan_1.fetchEtherscanTokenTransactions)(request),
42
- ]);
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]);
43
55
  const transactions = etherscanTransactions.result.map((tx) => __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_instances, "m", _EtherscanRemoteTransactionSource_normalizeTransaction).call(this, tx, request.currentNetworkId, request.currentChainId));
44
56
  const tokenTransactions = etherscanTokenTransactions.result.map((tx) => __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_instances, "m", _EtherscanRemoteTransactionSource_normalizeTokenTransaction).call(this, tx, request.currentNetworkId, request.currentChainId));
45
57
  return [...transactions, ...tokenTransactions];
@@ -47,7 +59,7 @@ class EtherscanRemoteTransactionSource {
47
59
  }
48
60
  }
49
61
  exports.EtherscanRemoteTransactionSource = EtherscanRemoteTransactionSource;
50
- _EtherscanRemoteTransactionSource_instances = new WeakSet(), _EtherscanRemoteTransactionSource_normalizeTransaction = function _EtherscanRemoteTransactionSource_normalizeTransaction(txMeta, currentNetworkId, currentChainId) {
62
+ _EtherscanRemoteTransactionSource_apiKey = new WeakMap(), _EtherscanRemoteTransactionSource_includeTokenTransfers = new WeakMap(), _EtherscanRemoteTransactionSource_instances = new WeakSet(), _EtherscanRemoteTransactionSource_normalizeTransaction = function _EtherscanRemoteTransactionSource_normalizeTransaction(txMeta, currentNetworkId, currentChainId) {
51
63
  const base = __classPrivateFieldGet(this, _EtherscanRemoteTransactionSource_instances, "m", _EtherscanRemoteTransactionSource_normalizeTransactionBase).call(this, txMeta, currentNetworkId, currentChainId);
52
64
  return Object.assign(Object.assign(Object.assign({}, base), { transaction: Object.assign(Object.assign({}, base.transaction), { data: txMeta.input }) }), (txMeta.isError === '0'
53
65
  ? { status: types_1.TransactionStatus.confirmed }
@@ -1 +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 type {\n EtherscanTokenTransactionMeta,\n EtherscanTransactionMeta,\n EtherscanTransactionMetaBase,\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 /**\n * Retrieve transaction data from Etherscan.\n *\n * @param request - The configuration required to fetch Etherscan transaction data.\n * @returns An array of transaction metadata.\n */\n async fetchTransactions(\n request: RemoteTransactionSourceRequest,\n ): Promise<TransactionMeta[]> {\n const [etherscanTransactions, etherscanTokenTransactions] =\n await Promise.all([\n fetchEtherscanTransactions(request),\n fetchEtherscanTokenTransactions(request),\n ]);\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"]}
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"]}
@@ -1,23 +1,24 @@
1
- import type EthQuery from '@metamask/eth-query';
2
- import type { NetworkState } from '@metamask/network-controller';
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';
3
5
  import type { RemoteTransactionSource, TransactionMeta } from './types';
4
6
  export declare class IncomingTransactionHelper {
5
7
  #private;
6
- constructor({ getNetworkState, getEthQuery, transactionLimit, remoteTransactionSource, }: {
8
+ hub: EventEmitter;
9
+ constructor({ blockTracker, getCurrentAccount, getLocalTransactions, getNetworkState, isEnabled, lastFetchedBlockNumbers, remoteTransactionSource, transactionLimit, updateTransactions, }: {
10
+ blockTracker: BlockTracker;
11
+ getCurrentAccount: () => string;
7
12
  getNetworkState: () => NetworkState;
8
- getEthQuery: () => EthQuery;
9
- transactionLimit: number;
13
+ getLocalTransactions?: () => TransactionMeta[];
14
+ isEnabled?: () => boolean;
15
+ lastFetchedBlockNumbers?: Record<string, number>;
10
16
  remoteTransactionSource: RemoteTransactionSource;
17
+ transactionLimit?: number;
18
+ updateTransactions?: boolean;
11
19
  });
12
- reconcile({ address, localTransactions, fromBlock, apiKey, }: {
13
- address: string;
14
- localTransactions: TransactionMeta[];
15
- fromBlock?: string;
16
- apiKey?: string;
17
- }): Promise<{
18
- updateRequired: boolean;
19
- transactions: TransactionMeta[];
20
- latestBlockNumber?: string;
21
- }>;
20
+ start(): void;
21
+ stop(): void;
22
+ update(latestBlockNumberHex?: Hex): Promise<void>;
22
23
  }
23
24
  //# sourceMappingURL=IncomingTransactionHelper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"IncomingTransactionHelper.d.ts","sourceRoot":"","sources":["../src/IncomingTransactionHelper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAGjE,OAAO,KAAK,EACV,uBAAuB,EAEvB,eAAe,EAEhB,MAAM,SAAS,CAAC;AAQjB,qBAAa,yBAAyB;;gBASxB,EACV,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,uBAAuB,GACxB,EAAE;QACD,eAAe,EAAE,MAAM,YAAY,CAAC;QACpC,WAAW,EAAE,MAAM,QAAQ,CAAC;QAC5B,gBAAgB,EAAE,MAAM,CAAC;QACzB,uBAAuB,EAAE,uBAAuB,CAAC;KAClD;IAOK,SAAS,CAAC,EACd,OAAO,EACP,iBAAiB,EACjB,SAAS,EACT,MAAM,GACP,EAAE;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,iBAAiB,EAAE,eAAe,EAAE,CAAC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC;QACV,cAAc,EAAE,OAAO,CAAC;QACxB,YAAY,EAAE,eAAe,EAAE,CAAC;QAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;CAmMH"}
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"}
@@ -19,126 +19,170 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
19
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
20
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
21
  };
22
- var _IncomingTransactionHelper_instances, _IncomingTransactionHelper_getNetworkState, _IncomingTransactionHelper_getEthQuery, _IncomingTransactionHelper_transactionLimit, _IncomingTransactionHelper_remoteTransactionSource, _IncomingTransactionHelper_updateSmartContractProperty, _IncomingTransactionHelper_getLatestBlockNumber, _IncomingTransactionHelper_isToSmartContract, _IncomingTransactionHelper_sortTransactionsByTime, _IncomingTransactionHelper_reconcileTransactions, _IncomingTransactionHelper_getNewTransactions, _IncomingTransactionHelper_getUpdatedTransactions, _IncomingTransactionHelper_isTransactionOutdated, _IncomingTransactionHelper_isStatusOutdated, _IncomingTransactionHelper_isGasDataOutdated;
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;
23
26
  Object.defineProperty(exports, "__esModule", { value: true });
24
27
  exports.IncomingTransactionHelper = void 0;
25
- const controller_utils_1 = require("@metamask/controller-utils");
26
- const SUPPORTED_NETWORK_IDS = [
27
- '1',
28
- '5',
29
- '11155111', // Sepolia
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,
30
33
  ];
31
34
  class IncomingTransactionHelper {
32
- constructor({ getNetworkState, getEthQuery, transactionLimit, remoteTransactionSource, }) {
35
+ constructor({ blockTracker, getCurrentAccount, getLocalTransactions, getNetworkState, isEnabled, lastFetchedBlockNumbers, remoteTransactionSource, transactionLimit, updateTransactions, }) {
33
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);
34
40
  _IncomingTransactionHelper_getNetworkState.set(this, void 0);
35
- _IncomingTransactionHelper_getEthQuery.set(this, void 0);
36
- _IncomingTransactionHelper_transactionLimit.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);
37
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");
38
53
  __classPrivateFieldSet(this, _IncomingTransactionHelper_getNetworkState, getNetworkState, "f");
39
- __classPrivateFieldSet(this, _IncomingTransactionHelper_getEthQuery, getEthQuery, "f");
40
- __classPrivateFieldSet(this, _IncomingTransactionHelper_transactionLimit, transactionLimit, "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");
41
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");
42
80
  }
43
- reconcile({ address, localTransactions, fromBlock, apiKey, }) {
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) {
44
86
  return __awaiter(this, void 0, void 0, function* () {
45
- const { providerConfig, networkId: currentNetworkId } = __classPrivateFieldGet(this, _IncomingTransactionHelper_getNetworkState, "f").call(this);
46
- const { chainId: currentChainId, type: networkType } = providerConfig;
47
- if (currentNetworkId === null ||
48
- !SUPPORTED_NETWORK_IDS.includes(currentNetworkId)) {
49
- return { updateRequired: false, transactions: [] };
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();
50
131
  }
51
- const remoteTransactions = yield __classPrivateFieldGet(this, _IncomingTransactionHelper_remoteTransactionSource, "f").fetchTransactions({
52
- address,
53
- networkType,
54
- limit: __classPrivateFieldGet(this, _IncomingTransactionHelper_transactionLimit, "f"),
55
- currentChainId,
56
- currentNetworkId,
57
- fromBlock,
58
- apiKey,
59
- });
60
- const [updateRequired, transactions] = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_reconcileTransactions).call(this, localTransactions, remoteTransactions);
61
- __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_sortTransactionsByTime).call(this, transactions);
62
- const latestBlockNumber = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getLatestBlockNumber).call(this, transactions, address, currentChainId, currentNetworkId);
63
- yield __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_updateSmartContractProperty).call(this, transactions);
64
- return { updateRequired, transactions, latestBlockNumber };
65
132
  });
66
133
  }
67
134
  }
68
135
  exports.IncomingTransactionHelper = IncomingTransactionHelper;
69
- _IncomingTransactionHelper_getNetworkState = new WeakMap(), _IncomingTransactionHelper_getEthQuery = new WeakMap(), _IncomingTransactionHelper_transactionLimit = new WeakMap(), _IncomingTransactionHelper_remoteTransactionSource = new WeakMap(), _IncomingTransactionHelper_instances = new WeakSet(), _IncomingTransactionHelper_updateSmartContractProperty = function _IncomingTransactionHelper_updateSmartContractProperty(transactions) {
70
- return __awaiter(this, void 0, void 0, function* () {
71
- yield Promise.all(transactions.map((tx) => __awaiter(this, void 0, void 0, function* () {
72
- var _a;
73
- (_a = tx.toSmartContract) !== null && _a !== void 0 ? _a : (tx.toSmartContract = yield __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_isToSmartContract).call(this, tx.transaction));
74
- })));
75
- });
76
- }, _IncomingTransactionHelper_getLatestBlockNumber = function _IncomingTransactionHelper_getLatestBlockNumber(transactions, address, currentChainId, currentNetworkId) {
77
- var _a;
78
- let latestBlockNumber;
79
- for (const tx of transactions) {
80
- const onCurrentChain = tx.chainId === currentChainId ||
81
- (!tx.chainId && tx.networkID === currentNetworkId);
82
- const toCurrentAccount = ((_a = tx.transaction.to) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === address.toLowerCase();
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) {
83
157
  const currentBlockNumberValue = tx.blockNumber
84
158
  ? parseInt(tx.blockNumber, 10)
85
159
  : -1;
86
- const latestBlockNumberValue = latestBlockNumber
87
- ? parseInt(latestBlockNumber, 10)
88
- : -1;
89
- if (onCurrentChain &&
90
- toCurrentAccount &&
91
- latestBlockNumberValue < currentBlockNumberValue) {
92
- latestBlockNumber = tx.blockNumber;
93
- }
160
+ lastFetchedBlockNumber = Math.max(lastFetchedBlockNumber, currentBlockNumberValue);
94
161
  }
95
- return latestBlockNumber;
96
- }, _IncomingTransactionHelper_isToSmartContract = function _IncomingTransactionHelper_isToSmartContract(transaction) {
97
- return __awaiter(this, void 0, void 0, function* () {
98
- // Contract Deploy
99
- if (!transaction.to) {
100
- return false;
101
- }
102
- // Send
103
- if (transaction.data === '0x') {
104
- return false;
105
- }
106
- const ethQuery = __classPrivateFieldGet(this, _IncomingTransactionHelper_getEthQuery, "f").call(this);
107
- const code = yield (0, controller_utils_1.query)(ethQuery, 'getCode', [transaction.to]);
108
- return (0, controller_utils_1.isSmartContractCode)(code);
109
- });
110
- }, _IncomingTransactionHelper_sortTransactionsByTime = function _IncomingTransactionHelper_sortTransactionsByTime(transactions) {
111
- transactions.sort((a, b) => (a.time < b.time ? -1 : 1));
112
- }, _IncomingTransactionHelper_reconcileTransactions = function _IncomingTransactionHelper_reconcileTransactions(localTxs, remoteTxs) {
113
- const updatedTxs = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getUpdatedTransactions).call(this, remoteTxs, localTxs);
114
- const newTxs = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_getNewTransactions).call(this, remoteTxs, localTxs);
115
- const updatedLocalTxs = localTxs.map((tx) => {
116
- const txIdx = updatedTxs.findIndex(({ transactionHash }) => transactionHash === tx.transactionHash);
117
- return txIdx === -1 ? tx : updatedTxs[txIdx];
118
- });
119
- const updateRequired = newTxs.length > 0 || updatedTxs.length > 0;
120
- const transactions = [...newTxs, ...updatedLocalTxs];
121
- return [updateRequired, transactions];
122
- }, _IncomingTransactionHelper_getNewTransactions = function _IncomingTransactionHelper_getNewTransactions(remoteTxs, localTxs) {
123
- return remoteTxs.filter((tx) => {
124
- const alreadyInTransactions = localTxs.find(({ transactionHash }) => transactionHash === tx.transactionHash);
125
- return !alreadyInTransactions;
126
- });
127
- }, _IncomingTransactionHelper_getUpdatedTransactions = function _IncomingTransactionHelper_getUpdatedTransactions(remoteTxs, localTxs) {
128
- return remoteTxs.filter((remoteTx) => {
129
- const isTxOutdated = localTxs.find((localTx) => {
130
- return (remoteTx.transactionHash === localTx.transactionHash &&
131
- __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_isTransactionOutdated).call(this, remoteTx, localTx));
132
- });
133
- return isTxOutdated;
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,
134
174
  });
135
- }, _IncomingTransactionHelper_isTransactionOutdated = function _IncomingTransactionHelper_isTransactionOutdated(remoteTx, localTx) {
136
- const statusOutdated = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_isStatusOutdated).call(this, remoteTx.transactionHash, localTx.transactionHash, remoteTx.status, localTx.status);
137
- const gasDataOutdated = __classPrivateFieldGet(this, _IncomingTransactionHelper_instances, "m", _IncomingTransactionHelper_isGasDataOutdated).call(this, remoteTx.transaction.gasUsed, localTx.transaction.gasUsed);
138
- return statusOutdated || gasDataOutdated;
139
- }, _IncomingTransactionHelper_isStatusOutdated = function _IncomingTransactionHelper_isStatusOutdated(remoteTxHash, localTxHash, remoteTxStatus, localTxStatus) {
140
- return remoteTxHash === localTxHash && remoteTxStatus !== localTxStatus;
141
- }, _IncomingTransactionHelper_isGasDataOutdated = function _IncomingTransactionHelper_isGasDataOutdated(remoteGasUsed, localGasUsed) {
142
- return remoteGasUsed !== localGasUsed;
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;
143
187
  };
144
188
  //# sourceMappingURL=IncomingTransactionHelper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"IncomingTransactionHelper.js","sourceRoot":"","sources":["../src/IncomingTransactionHelper.ts"],"names":[],"mappings":"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","sourcesContent":["import { isSmartContractCode, query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport type { NetworkState } from '@metamask/network-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type {\n RemoteTransactionSource,\n Transaction,\n TransactionMeta,\n TransactionStatus,\n} from './types';\n\nconst SUPPORTED_NETWORK_IDS = [\n '1', // Mainnet\n '5', // Goerli\n '11155111', // Sepolia\n];\n\nexport class IncomingTransactionHelper {\n #getNetworkState: () => NetworkState;\n\n #getEthQuery: () => EthQuery;\n\n #transactionLimit: number;\n\n #remoteTransactionSource: RemoteTransactionSource;\n\n constructor({\n getNetworkState,\n getEthQuery,\n transactionLimit,\n remoteTransactionSource,\n }: {\n getNetworkState: () => NetworkState;\n getEthQuery: () => EthQuery;\n transactionLimit: number;\n remoteTransactionSource: RemoteTransactionSource;\n }) {\n this.#getNetworkState = getNetworkState;\n this.#getEthQuery = getEthQuery;\n this.#transactionLimit = transactionLimit;\n this.#remoteTransactionSource = remoteTransactionSource;\n }\n\n async reconcile({\n address,\n localTransactions,\n fromBlock,\n apiKey,\n }: {\n address: string;\n localTransactions: TransactionMeta[];\n fromBlock?: string;\n apiKey?: string;\n }): Promise<{\n updateRequired: boolean;\n transactions: TransactionMeta[];\n latestBlockNumber?: string;\n }> {\n const { providerConfig, networkId: currentNetworkId } =\n this.#getNetworkState();\n const { chainId: currentChainId, type: networkType } = providerConfig;\n\n if (\n currentNetworkId === null ||\n !SUPPORTED_NETWORK_IDS.includes(currentNetworkId)\n ) {\n return { updateRequired: false, transactions: [] };\n }\n\n const remoteTransactions =\n await this.#remoteTransactionSource.fetchTransactions({\n address,\n networkType,\n limit: this.#transactionLimit,\n currentChainId,\n currentNetworkId,\n fromBlock,\n apiKey,\n });\n\n const [updateRequired, transactions] = this.#reconcileTransactions(\n localTransactions,\n remoteTransactions,\n );\n\n this.#sortTransactionsByTime(transactions);\n\n const latestBlockNumber = this.#getLatestBlockNumber(\n transactions,\n address,\n currentChainId,\n currentNetworkId,\n );\n\n await this.#updateSmartContractProperty(transactions);\n\n return { updateRequired, transactions, latestBlockNumber };\n }\n\n async #updateSmartContractProperty(transactions: TransactionMeta[]) {\n await Promise.all(\n transactions.map(async (tx) => {\n tx.toSmartContract ??= await this.#isToSmartContract(tx.transaction);\n }),\n );\n }\n\n #getLatestBlockNumber(\n transactions: TransactionMeta[],\n address: string,\n currentChainId: Hex,\n currentNetworkId: string,\n ): string | undefined {\n let latestBlockNumber: string | undefined;\n\n for (const tx of transactions) {\n const onCurrentChain =\n tx.chainId === currentChainId ||\n (!tx.chainId && tx.networkID === currentNetworkId);\n\n const toCurrentAccount =\n tx.transaction.to?.toLowerCase() === address.toLowerCase();\n\n const currentBlockNumberValue = tx.blockNumber\n ? parseInt(tx.blockNumber, 10)\n : -1;\n\n const latestBlockNumberValue = latestBlockNumber\n ? parseInt(latestBlockNumber, 10)\n : -1;\n\n if (\n onCurrentChain &&\n toCurrentAccount &&\n latestBlockNumberValue < currentBlockNumberValue\n ) {\n latestBlockNumber = tx.blockNumber;\n }\n }\n\n return latestBlockNumber;\n }\n\n async #isToSmartContract(transaction: Transaction): Promise<boolean> {\n // Contract Deploy\n if (!transaction.to) {\n return false;\n }\n\n // Send\n if (transaction.data === '0x') {\n return false;\n }\n\n const ethQuery = this.#getEthQuery();\n const code = await query(ethQuery, 'getCode', [transaction.to]);\n\n return isSmartContractCode(code);\n }\n\n #sortTransactionsByTime(transactions: TransactionMeta[]) {\n transactions.sort((a, b) => (a.time < b.time ? -1 : 1));\n }\n\n #reconcileTransactions(\n localTxs: TransactionMeta[],\n remoteTxs: TransactionMeta[],\n ): [boolean, TransactionMeta[]] {\n const updatedTxs: TransactionMeta[] = this.#getUpdatedTransactions(\n remoteTxs,\n localTxs,\n );\n\n const newTxs: TransactionMeta[] = this.#getNewTransactions(\n remoteTxs,\n localTxs,\n );\n\n const updatedLocalTxs = localTxs.map((tx: TransactionMeta) => {\n const txIdx = updatedTxs.findIndex(\n ({ transactionHash }) => transactionHash === tx.transactionHash,\n );\n return txIdx === -1 ? tx : updatedTxs[txIdx];\n });\n\n const updateRequired = newTxs.length > 0 || updatedTxs.length > 0;\n const transactions = [...newTxs, ...updatedLocalTxs];\n\n return [updateRequired, transactions];\n }\n\n #getNewTransactions(\n remoteTxs: TransactionMeta[],\n localTxs: TransactionMeta[],\n ): TransactionMeta[] {\n return remoteTxs.filter((tx) => {\n const alreadyInTransactions = localTxs.find(\n ({ transactionHash }) => transactionHash === tx.transactionHash,\n );\n return !alreadyInTransactions;\n });\n }\n\n #getUpdatedTransactions(\n remoteTxs: TransactionMeta[],\n localTxs: TransactionMeta[],\n ): TransactionMeta[] {\n return remoteTxs.filter((remoteTx) => {\n const isTxOutdated = localTxs.find((localTx) => {\n return (\n remoteTx.transactionHash === localTx.transactionHash &&\n this.#isTransactionOutdated(remoteTx, localTx)\n );\n });\n return isTxOutdated;\n });\n }\n\n #isTransactionOutdated(\n remoteTx: TransactionMeta,\n localTx: TransactionMeta,\n ): boolean {\n const statusOutdated = this.#isStatusOutdated(\n remoteTx.transactionHash,\n localTx.transactionHash,\n remoteTx.status,\n localTx.status,\n );\n\n const gasDataOutdated = this.#isGasDataOutdated(\n remoteTx.transaction.gasUsed,\n localTx.transaction.gasUsed,\n );\n\n return statusOutdated || gasDataOutdated;\n }\n\n #isStatusOutdated(\n remoteTxHash: string | undefined,\n localTxHash: string | undefined,\n remoteTxStatus: TransactionStatus,\n localTxStatus: TransactionStatus,\n ): boolean {\n return remoteTxHash === localTxHash && remoteTxStatus !== localTxStatus;\n }\n\n #isGasDataOutdated(\n remoteGasUsed: string | undefined,\n localGasUsed: string | undefined,\n ): boolean {\n return remoteGasUsed !== localGasUsed;\n }\n}\n"]}
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"]}