@metamask/transaction-controller 8.0.0 → 9.0.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 +44 -1
- package/dist/EtherscanRemoteTransactionSource.d.ts +15 -0
- package/dist/EtherscanRemoteTransactionSource.d.ts.map +1 -0
- package/dist/EtherscanRemoteTransactionSource.js +99 -0
- package/dist/EtherscanRemoteTransactionSource.js.map +1 -0
- package/dist/IncomingTransactionHelper.d.ts +24 -0
- package/dist/IncomingTransactionHelper.d.ts.map +1 -0
- package/dist/IncomingTransactionHelper.js +188 -0
- package/dist/IncomingTransactionHelper.js.map +1 -0
- package/dist/TransactionController.d.ts +58 -232
- package/dist/TransactionController.d.ts.map +1 -1
- package/dist/TransactionController.js +198 -311
- package/dist/TransactionController.js.map +1 -1
- package/dist/constants.d.ts +119 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +124 -0
- package/dist/constants.js.map +1 -0
- package/dist/etherscan.d.ts +65 -0
- package/dist/etherscan.d.ts.map +1 -0
- package/dist/etherscan.js +116 -0
- package/dist/etherscan.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +265 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +29 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +13 -23
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +20 -77
- package/dist/utils.js.map +1 -1
- package/package.json +5 -6
- package/dist/mocks/txsMock.d.ts +0 -64
- package/dist/mocks/txsMock.d.ts.map +0 -1
- package/dist/mocks/txsMock.js +0 -516
- package/dist/mocks/txsMock.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [9.0.0]
|
|
10
|
+
### Added
|
|
11
|
+
- Add `baseFeePerGas` to transaction metadata ([#1590](https://github.com/MetaMask/core/pull/1590))
|
|
12
|
+
- Add `txReceipt` to transaction metadata ([#1592](https://github.com/MetaMask/core/pull/1592))
|
|
13
|
+
- Add `initApprovals` method to generate approval requests from unapproved transactions ([#1575](https://github.com/MetaMask/core/pull/1575))
|
|
14
|
+
- Add `dappSuggestedGasFees` to transaction metadata ([#1617](https://github.com/MetaMask/core/pull/1617))
|
|
15
|
+
- Add optional `incomingTransactions` constructor arguments ([#1579](https://github.com/MetaMask/core/pull/1579))
|
|
16
|
+
- `apiKey`
|
|
17
|
+
- `includeTokenTransfers`
|
|
18
|
+
- `isEnabled`
|
|
19
|
+
- `updateTransactions`
|
|
20
|
+
- Add incoming transaction methods ([#1579](https://github.com/MetaMask/core/pull/1579))
|
|
21
|
+
- `startIncomingTransactionPolling`
|
|
22
|
+
- `stopIncomingTransactionPolling`
|
|
23
|
+
- `updateIncomingTransactions`
|
|
24
|
+
- Add `requireApproval` option to `addTransaction` method options ([#1580](https://github.com/MetaMask/core/pull/1580))
|
|
25
|
+
- Add `address` argument to `wipeTransactions` method ([#1573](https://github.com/MetaMask/core/pull/1573))
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- **BREAKING**: Add required `getSelectedAddress` callback argument to constructor ([#1579](https://github.com/MetaMask/core/pull/1579))
|
|
29
|
+
- **BREAKING**: Add `isSupportedNetwork` method to `RemoteTransactionSource` interface ([#1579](https://github.com/MetaMask/core/pull/1579))
|
|
30
|
+
- **BREAKING**: Move all but first argument to options bag in `addTransaction` method ([#1576](https://github.com/MetaMask/core/pull/1576))
|
|
31
|
+
- **BREAKING**: Update properties of `RemoteTransactionSourceRequest` type ([#1579](https://github.com/MetaMask/core/pull/1579))
|
|
32
|
+
- The `fromBlock` property has changed from `string` to `number`
|
|
33
|
+
- The `networkType` property has been removed
|
|
34
|
+
- This type is intended mainly for internal use, so it's likely this change doesn't affect most projects
|
|
35
|
+
|
|
36
|
+
### Removed
|
|
37
|
+
- **BREAKING**: Remove `fetchAll` method ([#1579](https://github.com/MetaMask/core/pull/1579))
|
|
38
|
+
- This method was used to fetch transaction history from Etherscan
|
|
39
|
+
- This is now handled automatically by the controller on each new block, if polling is enabled
|
|
40
|
+
- Polling can be enabled or disabled by calling `startIncomingTransactionPolling` or `stopIncomingTransactionPolling` respectively
|
|
41
|
+
- An immediate update can be requested by calling `updateIncomingTransactions`
|
|
42
|
+
- The new constructor parameter `incomingTransactions.isEnabled` acts as an override to disable this functionality based on a client preference for example
|
|
43
|
+
- **BREAKING**: Remove `prepareUnsignedEthTx` and `getCommonConfiguration` methods ([#1581](https://github.com/MetaMask/core/pull/1581))
|
|
44
|
+
- These methods were intended mainly for internal use, so it's likely this change doesn't affect most projects
|
|
45
|
+
|
|
46
|
+
## [8.0.1]
|
|
47
|
+
### Changed
|
|
48
|
+
- Replace `eth-query` ^2.1.2 with `@metamask/eth-query` ^3.0.1 ([#1546](https://github.com/MetaMask/core/pull/1546))
|
|
49
|
+
|
|
9
50
|
## [8.0.0]
|
|
10
51
|
### Changed
|
|
11
52
|
- **BREAKING**: Change `babel-runtime` from a `dependency` to a `peerDependency` ([#1504](https://github.com/MetaMask/core/pull/1504))
|
|
@@ -94,7 +135,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
94
135
|
|
|
95
136
|
All changes listed after this point were applied to this package following the monorepo conversion.
|
|
96
137
|
|
|
97
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@
|
|
138
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@9.0.0...HEAD
|
|
139
|
+
[9.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@8.0.1...@metamask/transaction-controller@9.0.0
|
|
140
|
+
[8.0.1]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@8.0.0...@metamask/transaction-controller@8.0.1
|
|
98
141
|
[8.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@7.1.0...@metamask/transaction-controller@8.0.0
|
|
99
142
|
[7.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@7.0.0...@metamask/transaction-controller@7.1.0
|
|
100
143
|
[7.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@6.1.0...@metamask/transaction-controller@7.0.0
|
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,6CAAoC;AACpC,oDAAkC;AAIlC,MAAM,aAAa,GAAyC;IAC1D,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM;IACzB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO;CACvC,CAAC;AAEF,MAAa,yBAAyB;IA2BpC,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,SAAS,EACT,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,GAWnB;;QA5CD,0DAA4B;QAE5B,+DAAiC;QAEjC,kEAA+C;QAE/C,6DAAqC;QAErC,uDAA0B;QAE1B,uDAAoB;QAEpB,qEAAiD;QAEjD,2CAAS,IAAI,mBAAK,EAAE,EAAC;QAErB,2DAAuD;QAEvD,qEAAkD;QAElD,8DAA2B;QAE3B,gEAA6B;QAuB3B,IAAI,CAAC,GAAG,GAAG,IAAI,gBAAY,EAAE,CAAC;QAE9B,uBAAA,IAAI,2CAAiB,YAAY,MAAA,CAAC;QAClC,uBAAA,IAAI,gDAAsB,iBAAiB,MAAA,CAAC;QAC5C,uBAAA,IAAI,mDAAyB,oBAAoB,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,MAAA,CAAC;QAChE,uBAAA,IAAI,8CAAoB,eAAe,MAAA,CAAC;QACxC,uBAAA,IAAI,wCAAc,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAA,CAAC;QAC5C,uBAAA,IAAI,wCAAc,KAAK,MAAA,CAAC;QACxB,uBAAA,IAAI,sDAA4B,uBAAuB,aAAvB,uBAAuB,cAAvB,uBAAuB,GAAI,EAAE,MAAA,CAAC;QAC9D,uBAAA,IAAI,sDAA4B,uBAAuB,MAAA,CAAC;QACxD,uBAAA,IAAI,+CAAqB,gBAAgB,MAAA,CAAC;QAC1C,uBAAA,IAAI,iDAAuB,kBAAkB,aAAlB,kBAAkB,cAAlB,kBAAkB,GAAI,KAAK,MAAA,CAAC;QAEvD,uEAAuE;QACvE,8DAA8D;QAC9D,uBAAA,IAAI,4CAAkB,CAAO,cAAmB,EAAE,EAAE;YAClD,IAAI;gBACF,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;aACnC;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,KAAK,CAAC,4CAA4C,EAAE,KAAK,CAAC,CAAC;aACpE;QACH,CAAC,CAAA,MAAA,CAAC;IACJ,CAAC;IAED,KAAK;QACH,IAAI,uBAAA,IAAI,4CAAW,EAAE;YACnB,OAAO;SACR;QAED,IAAI,CAAC,uBAAA,IAAI,iFAAU,MAAd,IAAI,CAAY,EAAE;YACrB,OAAO;SACR;QAED,uBAAA,IAAI,+CAAc,CAAC,WAAW,CAAC,QAAQ,EAAE,uBAAA,IAAI,gDAAe,CAAC,CAAC;QAC9D,uBAAA,IAAI,wCAAc,IAAI,MAAA,CAAC;IACzB,CAAC;IAED,IAAI;QACF,uBAAA,IAAI,+CAAc,CAAC,cAAc,CAAC,QAAQ,EAAE,uBAAA,IAAI,gDAAe,CAAC,CAAC;QACjE,uBAAA,IAAI,wCAAc,KAAK,MAAA,CAAC;IAC1B,CAAC;IAEK,MAAM,CAAC,oBAA0B;;YACrC,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,wCAAO,CAAC,OAAO,EAAE,CAAC;YAEhD,IAAI;gBACF,IAAI,CAAC,uBAAA,IAAI,iFAAU,MAAd,IAAI,CAAY,EAAE;oBACrB,OAAO;iBACR;gBAED,MAAM,iBAAiB,GAAG,QAAQ,CAChC,oBAAoB,IAAI,CAAC,MAAM,uBAAA,IAAI,+CAAc,CAAC,cAAc,EAAE,CAAC,EACnE,EAAE,CACH,CAAC;gBAEF,MAAM,SAAS,GAAG,uBAAA,IAAI,qFAAc,MAAlB,IAAI,EAAe,iBAAiB,CAAC,CAAC;gBACxD,MAAM,OAAO,GAAG,uBAAA,IAAI,oDAAmB,MAAvB,IAAI,CAAqB,CAAC;gBAC1C,MAAM,cAAc,GAAG,uBAAA,IAAI,0FAAmB,MAAvB,IAAI,CAAqB,CAAC;gBACjD,MAAM,gBAAgB,GAAG,uBAAA,IAAI,4FAAqB,MAAzB,IAAI,CAAuB,CAAC;gBAErD,IAAI,kBAAkB,GAAG,EAAE,CAAC;gBAE5B,IAAI;oBACF,kBAAkB;wBAChB,MAAM,uBAAA,IAAI,0DAAyB,CAAC,iBAAiB,CAAC;4BACpD,OAAO;4BACP,cAAc;4BACd,gBAAgB;4BAChB,SAAS;4BACT,KAAK,EAAE,uBAAA,IAAI,mDAAkB;yBAC9B,CAAC,CAAC;iBACN;gBAAC,OAAO,KAAU,EAAE;oBACnB,OAAO;iBACR;gBAED,IAAI,CAAC,uBAAA,IAAI,qDAAoB,EAAE;oBAC7B,kBAAkB,GAAG,kBAAkB,CAAC,MAAM,CAC5C,CAAC,EAAE,EAAE,EAAE,WAAC,OAAA,CAAA,MAAA,EAAE,CAAC,WAAW,CAAC,EAAE,0CAAE,WAAW,EAAE,MAAK,OAAO,CAAC,WAAW,EAAE,CAAA,EAAA,CACnE,CAAC;iBACH;gBAED,MAAM,iBAAiB,GAAG,CAAC,uBAAA,IAAI,qDAAoB;oBACjD,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC,uBAAA,IAAI,uDAAsB,MAA1B,IAAI,CAAwB,CAAC;gBAEjC,MAAM,eAAe,GAAG,uBAAA,IAAI,2FAAoB,MAAxB,IAAI,EAC1B,kBAAkB,EAClB,iBAAiB,CAClB,CAAC;gBAEF,MAAM,mBAAmB,GAAG,uBAAA,IAAI,+FAAwB,MAA5B,IAAI,EAC9B,kBAAkB,EAClB,iBAAiB,CAClB,CAAC;gBAEF,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE;oBAChE,uBAAA,IAAI,+FAAwB,MAA5B,IAAI,EAAyB,eAAe,CAAC,CAAC;oBAC9C,uBAAA,IAAI,+FAAwB,MAA5B,IAAI,EAAyB,mBAAmB,CAAC,CAAC;oBAElD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE;wBAC5B,KAAK,EAAE,eAAe;wBACtB,OAAO,EAAE,mBAAmB;qBAC7B,CAAC,CAAC;iBACJ;gBAED,uBAAA,IAAI,qGAA8B,MAAlC,IAAI,EAA+B,kBAAkB,CAAC,CAAC;aACxD;oBAAS;gBACR,WAAW,EAAE,CAAC;aACf;QACH,CAAC;KAAA;CAgHF;AA7QD,8DA6QC;q3BA9GyB,YAA+B;IACrD,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC,yGAGC,SAA4B,EAC5B,QAA2B;IAE3B,OAAO,SAAS,CAAC,MAAM,CACrB,CAAC,EAAE,EAAE,EAAE,CACL,CAAC,QAAQ,CAAC,IAAI,CACZ,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,eAAe,KAAK,EAAE,CAAC,eAAe,CAChE,CACJ,CAAC;AACJ,CAAC,iHAGC,SAA4B,EAC5B,QAA2B;IAE3B,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CACnC,QAAQ,CAAC,IAAI,CACX,CAAC,OAAO,EAAE,EAAE,CACV,QAAQ,CAAC,eAAe,KAAK,OAAO,CAAC,eAAe;QACpD,uBAAA,IAAI,8FAAuB,MAA3B,IAAI,EAAwB,QAAQ,EAAE,OAAO,CAAC,CACjD,CACF,CAAC;AACJ,CAAC,+GAGC,QAAyB,EACzB,OAAwB;IAExB,OAAO,aAAa,CAAC,IAAI,CACvB,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,OAAO,CAAC,CACvD,CAAC;AACJ,CAAC,6FAEa,iBAAyB;IACrC,MAAM,cAAc,GAAG,uBAAA,IAAI,0FAAmB,MAAvB,IAAI,CAAqB,CAAC;IAEjD,MAAM,sBAAsB,GAC1B,uBAAA,IAAI,0DAAyB,CAAC,cAAc,CAAC,CAAC;IAEhD,IAAI,sBAAsB,EAAE;QAC1B,OAAO,sBAAsB,GAAG,CAAC,CAAC;KACnC;IAED,wDAAwD;IACxD,8BAA8B;IAC9B,OAAO,IAAI,CAAC,GAAG,CAAC,iBAAiB,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC,6HAE6B,SAA4B;IACxD,IAAI,sBAAsB,GAAG,CAAC,CAAC,CAAC;IAEhC,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE;QAC1B,MAAM,uBAAuB,GAAG,EAAE,CAAC,WAAW;YAC5C,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;YAC9B,CAAC,CAAC,CAAC,CAAC,CAAC;QAEP,sBAAsB,GAAG,IAAI,CAAC,GAAG,CAC/B,sBAAsB,EACtB,uBAAuB,CACxB,CAAC;KACH;IAED,IAAI,sBAAsB,KAAK,CAAC,CAAC,EAAE;QACjC,OAAO;KACR;IAED,MAAM,cAAc,GAAG,uBAAA,IAAI,0FAAmB,MAAvB,IAAI,CAAqB,CAAC;IACjD,MAAM,aAAa,GAAG,uBAAA,IAAI,0DAAyB,CAAC,cAAc,CAAC,CAAC;IAEpE,IAAI,aAAa,KAAK,sBAAsB,EAAE;QAC5C,OAAO;KACR;IAED,uBAAA,IAAI,0DAAyB,CAAC,cAAc,CAAC,GAAG,sBAAsB,CAAC;IAEvE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,gCAAgC,EAAE;QAC9C,uBAAuB,EAAE,uBAAA,IAAI,0DAAyB;QACtD,WAAW,EAAE,sBAAsB;KACpC,CAAC,CAAC;AACL,CAAC;IAGC,OAAO,GAAG,uBAAA,IAAI,0FAAmB,MAAvB,IAAI,CAAqB,IAAI,uBAAA,IAAI,oDAAmB,MAAvB,IAAI,CAAqB,CAAC,WAAW,EAAE,EAAE,CAAC;AACnF,CAAC;IAGC,MAAM,SAAS,GAAG,uBAAA,IAAI,4CAAW,MAAf,IAAI,CAAa,CAAC;IACpC,MAAM,cAAc,GAAG,uBAAA,IAAI,0FAAmB,MAAvB,IAAI,CAAqB,CAAC;IACjD,MAAM,gBAAgB,GAAG,uBAAA,IAAI,4FAAqB,MAAzB,IAAI,CAAuB,CAAC;IAErD,MAAM,kBAAkB,GAAG,uBAAA,IAAI,0DAAyB,CAAC,kBAAkB,CACzE,cAAc,EACd,gBAAgB,CACjB,CAAC;IAEF,OAAO,SAAS,IAAI,kBAAkB,CAAC;AACzC,CAAC;IAGC,OAAO,uBAAA,IAAI,kDAAiB,MAArB,IAAI,CAAmB,CAAC,cAAc,CAAC,OAAO,CAAC;AACxD,CAAC;IAGC,OAAO,uBAAA,IAAI,kDAAiB,MAArB,IAAI,CAAmB,CAAC,SAAmB,CAAC;AACrD,CAAC","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"]}
|