@toruslabs/ethereum-controllers 5.3.3 → 5.3.5

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.
@@ -1,18 +1,18 @@
1
1
  import _objectSpread from '@babel/runtime/helpers/objectSpread2';
2
2
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
3
- import { CHAIN_NAMESPACES, BaseController, formatSmallNumbers, addressSlicer, ACTIVITY_ACTION_RECEIVE, ACTIVITY_ACTION_SEND, significantDigits, TransactionStatus, BaseBlockTracker, timeout, BaseCurrencyController, BaseKeyringController, randomId, PROVIDER_JRPC_METHODS, createFetchMiddleware, createSwappableProxy, createEventEmitterProxy, BasePreferencesController, TX_EVENTS, TRANSACTION_TYPES, BaseTransactionStateManager, transactionMatchesNetwork as transactionMatchesNetwork$1 } from '@toruslabs/base-controllers';
3
+ import { CHAIN_NAMESPACES, BaseController, randomId, TransactionStatus, TRANSACTION_TYPES, formatSmallNumbers, addressSlicer, ACTIVITY_ACTION_RECEIVE, ACTIVITY_ACTION_SEND, significantDigits, BaseBlockTracker, timeout, BaseCurrencyController, BaseKeyringController, PROVIDER_JRPC_METHODS, createFetchMiddleware, createSwappableProxy, createEventEmitterProxy, BasePreferencesController, TX_EVENTS, BaseTransactionStateManager, transactionMatchesNetwork as transactionMatchesNetwork$1 } from '@toruslabs/base-controllers';
4
4
  import { Mutex } from 'async-mutex';
5
- import { BrowserProvider, toQuantity, Contract, SigningKey, Wallet, isHexString as isHexString$1, JsonRpcProvider, Interface, keccak256 } from 'ethers';
5
+ import { BrowserProvider, toQuantity, Contract, Interface, formatEther, SigningKey, Wallet, isHexString as isHexString$1, JsonRpcProvider, keccak256 } from 'ethers';
6
6
  import log from 'loglevel';
7
- import { addHexPrefix, isValidAddress, toChecksumAddress, stripHexPrefix, isHexString, ecsign, bigIntToBytes, bytesToHex } from '@ethereumjs/util';
7
+ import { isHexString, addHexPrefix, isValidAddress, toChecksumAddress, stripHexPrefix, ecsign, bigIntToBytes, bytesToHex } from '@ethereumjs/util';
8
8
  import BigNumber from 'bignumber.js';
9
+ import { rpcErrors, providerErrors } from '@metamask/rpc-errors';
9
10
  import { get, post, remove, patch } from '@toruslabs/http-helpers';
10
11
  import { cloneDeep, merge, omitBy, mapValues, keyBy, sortBy, pickBy } from 'lodash';
11
- import '@babel/runtime/helpers/objectDestructuringEmpty';
12
+ import _objectDestructuringEmpty from '@babel/runtime/helpers/objectDestructuringEmpty';
12
13
  import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
13
14
  import BN, { BN as BN$1 } from 'bn.js';
14
15
  import { concatSig, personalSign, signTypedData, getEncryptionPublicKey, decrypt, typedSignatureHash, TYPED_MESSAGE_SCHEMA, SignTypedDataVersion } from '@metamask/eth-sig-util';
15
- import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
16
16
  import { validate } from 'jsonschema';
17
17
  import { createAsyncMiddleware, mergeMiddleware, createScaffoldMiddleware, providerFromMiddleware, JRPCEngine, providerFromEngine, SafeEventEmitter } from '@toruslabs/openlogin-jrpc';
18
18
  import { Hardfork, Common } from '@ethereumjs/common';
@@ -578,7 +578,7 @@ const SUPPORTED_NETWORKS = {
578
578
  blockExplorerUrl: "https://bscscan.com",
579
579
  chainId: BSC_MAINNET_CHAIN_ID,
580
580
  displayName: "Binance Smart Chain (BSC)",
581
- logo: "bnb_logo.png",
581
+ logo: "bnb_logo.svg",
582
582
  rpcTarget: `https://bsc-dataseed.binance.org`,
583
583
  ticker: "BNB",
584
584
  tickerName: "Binance Coin"
@@ -633,7 +633,7 @@ const SUPPORTED_NETWORKS = {
633
633
  blockExplorerUrl: "https://gnosis.blockscout.com",
634
634
  chainId: XDAI_CHAIN_ID,
635
635
  displayName: "xDai",
636
- logo: "xdai.svg",
636
+ logo: "xDai.svg",
637
637
  rpcTarget: `https://rpc.gnosischain.com`,
638
638
  ticker: "DAI",
639
639
  tickerName: "xDai Token"
@@ -679,7 +679,7 @@ const SUPPORTED_NETWORKS = {
679
679
  blockExplorerUrl: "https://testnet.bscscan.com",
680
680
  chainId: BSC_TESTNET_CHAIN_ID,
681
681
  displayName: "Binance Smart Chain Testnet",
682
- logo: "bnb_logo.png",
682
+ logo: "bnb_logo.svg",
683
683
  rpcTarget: `https://data-seed-prebsc-1-s1.binance.org:8545`,
684
684
  ticker: "BNB",
685
685
  tickerName: "Binance Coin",
@@ -1015,425 +1015,903 @@ class AccountTrackerController extends BaseController {
1015
1015
  }
1016
1016
  }
1017
1017
 
1018
- function getEtherScanHashLink(txHash, chainId) {
1019
- if (!SUPPORTED_NETWORKS[chainId]) return "";
1020
- return `${SUPPORTED_NETWORKS[chainId].blockExplorerUrl}/tx/${txHash}`;
1021
- }
1022
- const formatPastTx = (x, lowerCaseSelectedAddress) => {
1023
- var _x$to;
1024
- let totalAmountString = "";
1025
- if (x.type === CONTRACT_TYPE_ERC721 || x.type === CONTRACT_TYPE_ERC1155) totalAmountString = x.symbol;else if (x.type === CONTRACT_TYPE_ERC20) totalAmountString = formatSmallNumbers(Number.parseFloat(x.total_amount), x.symbol, true);else totalAmountString = formatSmallNumbers(Number.parseFloat(x.total_amount), x.type_name, true);
1026
- const currencyAmountString = x.type === CONTRACT_TYPE_ERC721 || x.type === CONTRACT_TYPE_ERC1155 ? "" : formatSmallNumbers(Number.parseFloat(x.currency_amount), x.selected_currency, true);
1027
- const finalObject = {
1028
- id: x.created_at.toString(),
1029
- date: new Date(x.created_at).toString(),
1030
- from: x.from,
1031
- from_aa_address: x.from_aa_address,
1032
- slicedFrom: typeof x.from === "string" ? addressSlicer(x.from) : "",
1033
- to: x.to,
1034
- slicedTo: typeof x.to === "string" ? addressSlicer(x.to) : "",
1035
- action: lowerCaseSelectedAddress === ((_x$to = x.to) === null || _x$to === void 0 ? void 0 : _x$to.toLowerCase()) || "" ? ACTIVITY_ACTION_RECEIVE : ACTIVITY_ACTION_SEND,
1036
- totalAmount: x.total_amount,
1037
- totalAmountString,
1038
- currencyAmount: x.currency_amount,
1039
- currencyAmountString,
1040
- amount: `${totalAmountString} / ${currencyAmountString}`,
1041
- status: x.status,
1042
- etherscanLink: getEtherScanHashLink(x.transaction_hash, x.chain_id || MAINNET_CHAIN_ID),
1043
- chainId: x.chain_id,
1044
- ethRate: Number.parseFloat(x === null || x === void 0 ? void 0 : x.total_amount) && Number.parseFloat(x === null || x === void 0 ? void 0 : x.currency_amount) ? `1 ${x.symbol} = ${significantDigits(Number.parseFloat(x.currency_amount) / Number.parseFloat(x.total_amount))}` : "",
1045
- currencyUsed: x.selected_currency,
1046
- type: x.type,
1047
- type_name: x.type_name,
1048
- type_image_link: x.type_image_link,
1049
- transaction_hash: x.transaction_hash,
1050
- transaction_category: x.transaction_category,
1051
- // TODO: // figure out how to handle these values.
1052
- // isEtherscan: x.isEtherscan,
1053
- // input: x.input || "",
1054
- // token_id: x.token_id || "",
1055
- contract_address: x.contract_address || "",
1056
- nonce: x.nonce || "",
1057
- is_cancel: !!x.is_cancel || false,
1058
- gas: x.gas || "",
1059
- gasPrice: x.gasPrice || ""
1060
- };
1061
- return finalObject;
1018
+ const erc20Interface = new Interface(erc20Abi);
1019
+ const erc721Interface = new Interface(erc721Abi);
1020
+ const erc1155Interface = new Interface(erc1155Abi);
1021
+
1022
+ // functions that handle normalizing of that key in txParams
1023
+
1024
+ const normalizers = {
1025
+ from: function (from) {
1026
+ let LowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
1027
+ return LowerCase ? addHexPrefix(from).toLowerCase() : addHexPrefix(from);
1028
+ },
1029
+ to: function (to) {
1030
+ let LowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
1031
+ return LowerCase ? addHexPrefix(to).toLowerCase() : addHexPrefix(to);
1032
+ },
1033
+ nonce: nonce => addHexPrefix(nonce),
1034
+ customNonceValue: nonce => addHexPrefix(nonce),
1035
+ value: value => addHexPrefix(value),
1036
+ data: data => addHexPrefix(data),
1037
+ gas: gas => addHexPrefix(gas),
1038
+ gasPrice: gasPrice => addHexPrefix(gasPrice),
1039
+ type: addHexPrefix,
1040
+ maxFeePerGas: addHexPrefix,
1041
+ maxPriorityFeePerGas: addHexPrefix
1062
1042
  };
1063
1043
 
1064
1044
  /**
1065
- * Ref - https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt
1045
+ * normalizes txParams
1066
1046
  */
1067
- const getEthTxStatus = async (hash, provider) => {
1068
- try {
1069
- const result = await provider.request({
1070
- method: METHOD_TYPES.ETH_GET_TRANSACTION_RECEIPT,
1071
- params: [hash]
1072
- });
1073
- if (result === null) return TransactionStatus.submitted;
1074
- if (result && result.status === "0x1") return TransactionStatus.confirmed;
1075
- if (result && result.status === "0x0") return TransactionStatus.rejected;
1076
- return undefined;
1077
- } catch (err) {
1078
- log.warn("unable to fetch transaction status", err);
1079
- return undefined;
1080
- }
1081
- };
1082
- function formatDate(inputDate) {
1083
- const monthList = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1084
- const date = new Date(inputDate);
1085
- const day = date.getDate();
1086
- const month = monthList[date.getMonth()];
1087
- const year = date.getFullYear();
1088
- return `${day} ${month} ${year}`;
1089
- }
1090
- function formatTime(time) {
1091
- return new Date(time).toTimeString().slice(0, 8);
1092
- }
1093
- const idleTimeTracker = (activityThresholdTime => {
1094
- let isIdle = false;
1095
- let idleTimeout = null;
1096
- const resetTimer = () => {
1097
- if (idleTimeout) {
1098
- window.clearTimeout(idleTimeout);
1099
- }
1100
- isIdle = false;
1101
- idleTimeout = window.setTimeout(() => {
1102
- isIdle = true;
1103
- }, activityThresholdTime * 1000);
1047
+ function normalizeTxParameters(txParameters) {
1048
+ let lowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
1049
+ // apply only keys in the normalizers
1050
+ const normalizedTxParameters = {
1051
+ id: txParameters.id || randomId(),
1052
+ from: txParameters.from
1104
1053
  };
1105
- if (typeof window !== "undefined" && typeof document !== "undefined") {
1106
- window.addEventListener("load", resetTimer);
1107
- document.addEventListener("mousemove", resetTimer);
1108
- document.addEventListener("keydown", resetTimer);
1054
+ for (const key in normalizers) {
1055
+ const currentKey = key;
1056
+ if (txParameters[currentKey])
1057
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1058
+ normalizedTxParameters[currentKey] = normalizers[currentKey](txParameters[currentKey], lowerCase);
1109
1059
  }
1110
- function checkIfIdle() {
1111
- return isIdle;
1060
+ return normalizedTxParameters;
1061
+ }
1062
+ function transactionMatchesNetwork(transaction, chainId) {
1063
+ if (typeof transaction.chainId !== "undefined") {
1064
+ return transaction.chainId === chainId;
1112
1065
  }
1113
- return {
1114
- checkIfIdle
1115
- };
1116
- })(60 * 3);
1117
- function isAddressByChainId(address, _chainId) {
1118
- // TOOD: add rsk network checks.
1119
- return isValidAddress(address);
1066
+ return false;
1120
1067
  }
1121
- function toChecksumAddressByChainId(address, chainId) {
1122
- // TOOD: add rsk network checks.
1123
- if (!isAddressByChainId(address)) return address;
1124
- return toChecksumAddress(address);
1068
+
1069
+ /**
1070
+ * Determines if the maxFeePerGas and maxPriorityFeePerGas fields are supplied
1071
+ * and valid inputs. This will return false for non hex string inputs.
1072
+ * the transaction to check
1073
+ * @returns true if transaction uses valid EIP1559 fields
1074
+ */
1075
+ function isEIP1559Transaction(transaction) {
1076
+ var _transaction$transact, _transaction$transact2;
1077
+ return isHexString(addHexPrefix(transaction === null || transaction === void 0 || (_transaction$transact = transaction.transaction) === null || _transaction$transact === void 0 ? void 0 : _transaction$transact.maxFeePerGas)) && isHexString(addHexPrefix(transaction === null || transaction === void 0 || (_transaction$transact2 = transaction.transaction) === null || _transaction$transact2 === void 0 ? void 0 : _transaction$transact2.maxPriorityFeePerGas));
1125
1078
  }
1126
- const GAS_LIMITS = {
1127
- // maximum gasLimit of a simple send
1128
- SIMPLE: addHexPrefix(21000 .toString(16)),
1129
- // a base estimate for token transfers.
1130
- BASE_TOKEN_ESTIMATE: addHexPrefix(100000 .toString(16))
1131
- };
1132
- function bnLessThan(a, b) {
1133
- if (a === null || a === undefined || b === null || b === undefined) {
1134
- return null;
1135
- }
1136
- return new BigNumber(a, 10).lt(b, 10);
1079
+
1080
+ /**
1081
+ * Determine if the maxFeePerGas and maxPriorityFeePerGas fields are not
1082
+ * supplied and that the gasPrice field is valid if it is provided. This will
1083
+ * return false if gasPrice is a non hex string.
1084
+ * transaction -
1085
+ * the transaction to check
1086
+ * @returns true if transaction uses valid Legacy fields OR lacks
1087
+ * EIP1559 fields
1088
+ */
1089
+ function isLegacyTransaction(transaction) {
1090
+ return typeof transaction.transaction.maxFeePerGas === "undefined" && typeof transaction.transaction.maxPriorityFeePerGas === "undefined" && (typeof transaction.transaction.gasPrice === "undefined" || isHexString(addHexPrefix(transaction.transaction.gasPrice)));
1137
1091
  }
1138
- const getIpfsEndpoint = path => `https://infura-ipfs.io/${path}`;
1139
- function sanitizeNftMetdataUrl(url) {
1140
- let finalUri = url;
1141
- if (url !== null && url !== void 0 && url.startsWith("ipfs")) {
1142
- const ipfsPath = url.split("ipfs://")[1];
1143
- finalUri = getIpfsEndpoint(ipfsPath);
1092
+
1093
+ /**
1094
+ * Given two fields, ensure that the second field is not included in txParams,
1095
+ * and if it is throw an invalidParams error.
1096
+ */
1097
+ function ensureMutuallyExclusiveFieldsNotProvided(txParams, fieldBeingValidated, mutuallyExclusiveField) {
1098
+ if (typeof txParams[mutuallyExclusiveField] !== "undefined") {
1099
+ throw rpcErrors.invalidParams(`Invalid transaction params: specified ${fieldBeingValidated} but also included ${mutuallyExclusiveField}, these cannot be mixed`);
1144
1100
  }
1145
- return finalUri;
1146
1101
  }
1147
- function getChainType(chainId) {
1148
- if (chainId === MAINNET_CHAIN_ID) {
1149
- return "mainnet";
1150
- } else if (TEST_CHAINS.includes(chainId)) {
1151
- return "testnet";
1102
+
1103
+ /**
1104
+ * Ensures that the provided value for field is a string, throws an
1105
+ * invalidParams error if field is not a string.
1106
+ */
1107
+ function ensureFieldIsString(txParams, field) {
1108
+ if (typeof txParams[field] !== "string") {
1109
+ throw rpcErrors.invalidParams(`Invalid transaction params: ${field} is not a string. got: (${txParams[field]})`);
1152
1110
  }
1153
- return "custom";
1154
1111
  }
1155
1112
 
1156
- const DEFAULT_POLLING_INTERVAL = 20;
1157
- const DEFAULT_RETRY_TIMEOUT = 2;
1158
- const SEC = 1000;
1159
- class PollingBlockTracker extends BaseBlockTracker {
1160
- constructor(_ref) {
1161
- let {
1162
- config,
1163
- state = {}
1164
- } = _ref;
1165
- if (!config.provider) {
1166
- throw new Error("PollingBlockTracker - no provider specified.");
1167
- }
1168
- super({
1169
- config,
1170
- state
1171
- });
1172
- const pollingInterval = config.pollingInterval || DEFAULT_POLLING_INTERVAL;
1173
- const retryTimeout = config.retryTimeout || DEFAULT_RETRY_TIMEOUT;
1174
-
1175
- // merge default + provided config.
1176
- this.defaultConfig = {
1177
- provider: config.provider,
1178
- pollingInterval: pollingInterval * SEC,
1179
- retryTimeout: retryTimeout * SEC,
1180
- setSkipCacheFlag: config.setSkipCacheFlag || false
1181
- };
1182
- this.initialize();
1183
- }
1184
- async checkForLatestBlock() {
1185
- await this._updateLatestBlock();
1186
- return this.getLatestBlock();
1113
+ /**
1114
+ * Ensures that the provided txParams has the proper 'type' specified for the
1115
+ * given field, if it is provided. If types do not match throws an
1116
+ * invalidParams error.
1117
+ */
1118
+ function ensureProperTransactionEnvelopeTypeProvided(txParams, field) {
1119
+ switch (field) {
1120
+ case "maxFeePerGas":
1121
+ case "maxPriorityFeePerGas":
1122
+ if (txParams.type && txParams.type !== TRANSACTION_ENVELOPE_TYPES.FEE_MARKET) {
1123
+ throw rpcErrors.invalidParams(`Invalid transaction envelope type: specified type "${txParams.type}" but ` + `including maxFeePerGas and maxPriorityFeePerGas requires type: "${TRANSACTION_ENVELOPE_TYPES.FEE_MARKET}"`);
1124
+ }
1125
+ break;
1126
+ case "gasPrice":
1127
+ default:
1128
+ if (txParams.type && txParams.type === TRANSACTION_ENVELOPE_TYPES.FEE_MARKET) {
1129
+ throw rpcErrors.invalidParams(`Invalid transaction envelope type: specified type "${txParams.type}" but ` + "included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas");
1130
+ }
1187
1131
  }
1132
+ }
1188
1133
 
1189
- // overrides the BaseBlockTracker._start method.
1190
- _start() {
1191
- this._synchronize().catch(err => this.emit("error", err));
1192
- }
1193
- async _synchronize() {
1194
- while (this.state._isRunning) {
1195
- if (idleTimeTracker.checkIfIdle()) return;
1196
- try {
1197
- await this._updateLatestBlock();
1198
- await timeout(this.config.pollingInterval);
1199
- } catch (err) {
1200
- const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`);
1201
- try {
1202
- this.emit("error", newErr);
1203
- } catch (emitErr) {
1204
- log.error(newErr);
1205
- }
1206
- await timeout(this.config.retryTimeout);
1207
- }
1208
- }
1209
- }
1210
- async _updateLatestBlock() {
1211
- // fetch + set latest block
1212
- const latestBlock = await this._fetchLatestBlock();
1213
- this._newPotentialLatest(latestBlock);
1134
+ /**
1135
+ * validates the from field in txParams
1136
+ */
1137
+ function validateFrom(txParams) {
1138
+ if (!(typeof txParams.from === "string")) {
1139
+ throw rpcErrors.invalidParams(`Invalid "from" address "${txParams.from}": not a string.`);
1214
1140
  }
1215
- async _fetchLatestBlock() {
1216
- try {
1217
- const block = await this.config.provider.request({
1218
- method: "eth_getBlockByNumber",
1219
- params: ["latest", false]
1220
- });
1221
- return {
1222
- blockHash: block.hash,
1223
- idempotencyKey: block.number,
1224
- timestamp: block.timestamp,
1225
- baseFeePerGas: block.baseFeePerGas,
1226
- gasLimit: block.gasLimit
1227
- };
1228
- } catch (error) {
1229
- log.error("Polling Block Tracker: ", error);
1230
- throw new Error(`PollingBlockTracker - encountered error fetching block:\n${error.message}`);
1231
- }
1141
+ if (!isValidAddress(txParams.from)) {
1142
+ throw rpcErrors.invalidParams('Invalid "from" address.');
1232
1143
  }
1233
1144
  }
1234
1145
 
1235
- class CurrencyController extends BaseCurrencyController {
1236
- constructor(_ref) {
1237
- let {
1238
- config,
1239
- state,
1240
- onNetworkChanged
1241
- } = _ref;
1242
- super({
1243
- config,
1244
- state
1245
- });
1246
- _defineProperty(this, "conversionInterval", void 0);
1247
- this.defaultState = _objectSpread(_objectSpread({}, this.defaultState), {}, {
1248
- commonDenomination: "USD",
1249
- commonDenominatorPrice: 0
1250
- });
1251
- this.initialize();
1252
- onNetworkChanged(networkState => {
1253
- // to be called as (listener) => this.networkController.on('networkDidChange', listener);
1254
- if (networkState.providerConfig.ticker.toUpperCase() !== this.state.nativeCurrency.toUpperCase()) {
1255
- this.setNativeCurrency(networkState.providerConfig.ticker);
1256
- this.updateConversionRate();
1257
- }
1258
- });
1259
- }
1260
- setCommonDenomination(commonDenomination) {
1261
- this.update({
1262
- commonDenomination
1263
- });
1264
- }
1265
- getCommonDenomination() {
1266
- return this.state.commonDenomination;
1267
- }
1268
- setCommonDenominatorPrice(commonDenominatorPrice) {
1269
- this.update({
1270
- commonDenominatorPrice
1271
- });
1272
- }
1273
- getCommonDenominatorPrice() {
1274
- return this.state.commonDenominatorPrice;
1275
- }
1276
-
1277
- /**
1278
- * Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
1279
- * stored at the controller's conversionInterval property. If it is called and such an id already exists, the
1280
- * previous interval is clear and a new one is created.
1281
- */
1282
- scheduleConversionInterval() {
1283
- if (this.conversionInterval) {
1284
- window.clearInterval(this.conversionInterval);
1285
- }
1286
- this.conversionInterval = window.setInterval(() => {
1287
- if (!idleTimeTracker.checkIfIdle()) {
1288
- this.updateConversionRate();
1289
- }
1290
- }, this.config.pollInterval);
1291
- }
1292
-
1293
- /**
1294
- * Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is
1295
- * fetched from an external API
1296
- */
1297
- async updateConversionRate() {
1298
- const currentCurrency = this.getCurrentCurrency();
1299
- const nativeCurrency = this.getNativeCurrency();
1300
- const commonDenomination = this.getCommonDenomination();
1301
- const conversionRate = await this.retrieveConversionRate(nativeCurrency, currentCurrency, commonDenomination);
1302
- const currentCurrencyRate = Number.parseFloat(conversionRate[currentCurrency.toUpperCase()]);
1303
- const commonDenominationRate = Number.parseFloat(conversionRate[commonDenomination.toUpperCase()]);
1304
- // set conversion rate
1305
- if (currentCurrencyRate || commonDenominationRate) {
1306
- // ETC
1307
- this.setConversionRate(currentCurrencyRate);
1308
- this.setConversionDate(Math.floor(Date.now() / 1000).toString());
1309
- if (currentCurrency.toUpperCase() === commonDenomination.toUpperCase()) {
1310
- this.setCommonDenominatorPrice(currentCurrencyRate);
1311
- } else {
1312
- this.setCommonDenominatorPrice(commonDenominationRate);
1313
- }
1146
+ /**
1147
+ * validates the to field in txParams
1148
+ */
1149
+ function validateRecipient(txParameters) {
1150
+ if (txParameters.to === "0x" || txParameters.to === null) {
1151
+ if (txParameters.data) {
1152
+ delete txParameters.to;
1314
1153
  } else {
1315
- this.setConversionRate(0);
1316
- this.setConversionDate("N/A");
1317
- }
1318
- }
1319
- async retrieveConversionRate(fromCurrency, toCurrency, commonDenomination) {
1320
- try {
1321
- // query cryptocompare
1322
- let apiUrl = `${this.config.api}/currency?fsym=${fromCurrency.toUpperCase()}&tsyms=${toCurrency.toUpperCase()}`;
1323
- if (commonDenomination && commonDenomination.toUpperCase() !== toCurrency.toUpperCase()) {
1324
- apiUrl += `,${commonDenomination.toUpperCase()}`;
1325
- }
1326
- const parsedResponse = await get(apiUrl);
1327
- return parsedResponse;
1328
- } catch (error) {
1329
- log.error(error, `CurrencyController - updateCommonDenominatorPrice: Failed to query rate for currency: ${fromCurrency}/ ${toCurrency}`);
1154
+ throw rpcErrors.invalidParams('Invalid "to" address.');
1330
1155
  }
1331
- return {
1332
- [toCurrency.toUpperCase()]: "0",
1333
- [commonDenomination.toUpperCase()]: "0"
1334
- };
1156
+ } else if (txParameters.to !== undefined && !isValidAddress(txParameters.to)) {
1157
+ throw rpcErrors.invalidParams('Invalid "to" address.');
1335
1158
  }
1159
+ return txParameters;
1336
1160
  }
1337
1161
 
1338
- // Big Number Constants
1339
- const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber("1000000000000000000");
1340
- const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber("1000000000");
1341
- const BIG_NUMBER_ETH_MULTIPLIER = new BigNumber("1");
1342
- // Setter Maps
1343
- const toBigNumber = {
1344
- hex: n => new BigNumber(stripHexPrefix(n), 16),
1345
- dec: n => new BigNumber(String(n), 10),
1346
- BN: n => new BigNumber(n.toString(16), 16)
1347
- };
1348
- const toNormalizedDenomination = {
1349
- WEI: bigNumber => bigNumber.div(BIG_NUMBER_WEI_MULTIPLIER),
1350
- GWEI: bigNumber => bigNumber.div(BIG_NUMBER_GWEI_MULTIPLIER),
1351
- ETH: bigNumber => bigNumber.div(BIG_NUMBER_ETH_MULTIPLIER)
1352
- };
1353
- const toSpecifiedDenomination = {
1354
- WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).dp(0, BigNumber.ROUND_HALF_UP),
1355
- GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).dp(9, BigNumber.ROUND_HALF_UP),
1356
- ETH: bigNumber => bigNumber.times(BIG_NUMBER_ETH_MULTIPLIER).dp(9, BigNumber.ROUND_HALF_UP)
1357
- };
1358
- const baseChange = {
1359
- hex: n => n.toString(16),
1360
- dec: n => new BigNumber(n).toString(10),
1361
- BN: n => new BN(n.toString(16))
1362
- };
1363
-
1364
1162
  /**
1365
- * Utility method to convert a value between denominations, formats and currencies.
1163
+ * Validates the given tx parameters
1164
+ * @throws if the tx params contains invalid fields
1366
1165
  */
1367
- const converter = _ref => {
1368
- let {
1369
- value,
1370
- fromNumericBase,
1371
- fromDenomination,
1372
- fromCurrency,
1373
- toNumericBase,
1374
- toDenomination,
1375
- toCurrency,
1376
- numberOfDecimals,
1377
- conversionRate,
1378
- invertConversionRate,
1379
- roundDown
1380
- } = _ref;
1381
- let convertedValue = fromNumericBase ? toBigNumber[fromNumericBase](value) : value;
1382
- if (fromDenomination) {
1383
- convertedValue = toNormalizedDenomination[fromDenomination](convertedValue);
1384
- }
1385
- if (fromCurrency !== toCurrency) {
1386
- if (conversionRate === null || conversionRate === undefined) {
1387
- throw new Error(`Converting from ${fromCurrency} to ${toCurrency} requires a conversionRate, but one was not provided`);
1388
- }
1389
- let rate = toBigNumber.dec(conversionRate);
1390
- if (invertConversionRate) {
1391
- rate = new BigNumber(1).div(conversionRate);
1392
- }
1393
- convertedValue = convertedValue.times(rate);
1394
- }
1395
- if (toDenomination) {
1396
- convertedValue = toSpecifiedDenomination[toDenomination](convertedValue);
1397
- }
1398
- if (numberOfDecimals) {
1399
- convertedValue = convertedValue.dp(numberOfDecimals, BigNumber.ROUND_HALF_DOWN);
1400
- }
1401
- if (roundDown) {
1402
- convertedValue = convertedValue.dp(roundDown, BigNumber.ROUND_DOWN);
1166
+ function validateTxParameters(txParams) {
1167
+ let eip1559Compatibility = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
1168
+ if (!txParams || typeof txParams !== "object" || Array.isArray(txParams)) {
1169
+ throw rpcErrors.invalidParams("Invalid transaction params: must be an object.");
1403
1170
  }
1404
- if (toNumericBase) {
1405
- convertedValue = baseChange[toNumericBase](convertedValue);
1171
+ if (!txParams.to && !txParams.data) {
1172
+ throw rpcErrors.invalidParams('Invalid transaction params: must specify "data" for contract deployments, or "to" (and optionally "data") for all other types of transactions.');
1406
1173
  }
1407
- return convertedValue;
1408
- };
1409
- const conversionUtil = (value, _ref2) => {
1410
- let {
1411
- fromCurrency = null,
1412
- toCurrency = fromCurrency,
1413
- fromNumericBase,
1414
- toNumericBase,
1415
- fromDenomination,
1416
- toDenomination,
1417
- numberOfDecimals,
1418
- conversionRate,
1419
- invertConversionRate
1420
- } = _ref2;
1421
- if (fromCurrency !== toCurrency && !conversionRate) {
1422
- return 0;
1174
+ if (isEIP1559Transaction({
1175
+ transaction: txParams
1176
+ }) && !eip1559Compatibility) {
1177
+ throw rpcErrors.invalidParams("Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559");
1423
1178
  }
1424
- return converter({
1425
- fromCurrency,
1426
- toCurrency,
1427
- fromNumericBase,
1428
- toNumericBase,
1429
- fromDenomination,
1430
- toDenomination,
1431
- numberOfDecimals,
1432
- conversionRate,
1433
- invertConversionRate,
1179
+ Object.entries(txParams).forEach(_ref => {
1180
+ let [key, value] = _ref;
1181
+ // validate types
1182
+ switch (key) {
1183
+ case "from":
1184
+ validateFrom(txParams);
1185
+ break;
1186
+ case "to":
1187
+ validateRecipient(txParams);
1188
+ break;
1189
+ case "gasPrice":
1190
+ ensureProperTransactionEnvelopeTypeProvided(txParams, "gasPrice");
1191
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "gasPrice", "maxFeePerGas");
1192
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "gasPrice", "maxPriorityFeePerGas");
1193
+ ensureFieldIsString(txParams, "gasPrice");
1194
+ break;
1195
+ case "maxFeePerGas":
1196
+ ensureProperTransactionEnvelopeTypeProvided(txParams, "maxFeePerGas");
1197
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "maxFeePerGas", "gasPrice");
1198
+ ensureFieldIsString(txParams, "maxFeePerGas");
1199
+ break;
1200
+ case "maxPriorityFeePerGas":
1201
+ ensureProperTransactionEnvelopeTypeProvided(txParams, "maxPriorityFeePerGas");
1202
+ ensureMutuallyExclusiveFieldsNotProvided(txParams, "maxPriorityFeePerGas", "gasPrice");
1203
+ ensureFieldIsString(txParams, "maxPriorityFeePerGas");
1204
+ break;
1205
+ case "value":
1206
+ ensureFieldIsString(txParams, "value");
1207
+ if (value.toString().includes("-")) {
1208
+ throw rpcErrors.invalidParams(`Invalid transaction value "${value}": not a positive number.`);
1209
+ }
1210
+ if (value.toString().includes(".")) {
1211
+ throw rpcErrors.invalidParams(`Invalid transaction value of "${value}": number must be in wei.`);
1212
+ }
1213
+ break;
1214
+ case "chainId":
1215
+ if (typeof value !== "number" && typeof value !== "string") {
1216
+ throw rpcErrors.invalidParams(`Invalid transaction params: ${key} is not a Number or hex string. got: (${value})`);
1217
+ }
1218
+ break;
1219
+ default:
1220
+ ensureFieldIsString(txParams, key);
1221
+ }
1222
+ });
1223
+ }
1224
+ function normalizeAndValidateTxParams(txParams) {
1225
+ let lowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
1226
+ const normalizedTxParams = normalizeTxParameters(txParams, lowerCase);
1227
+ validateTxParameters(normalizedTxParams);
1228
+ return normalizedTxParams;
1229
+ }
1230
+
1231
+ /**
1232
+ * @returns an array of states that can be considered final
1233
+ */
1234
+ function getFinalStates() {
1235
+ return [TransactionStatus.rejected,
1236
+ // the user has responded no!
1237
+ TransactionStatus.confirmed,
1238
+ // the tx has been included in a block.
1239
+ TransactionStatus.failed,
1240
+ // the tx failed for some reason, included on tx data.
1241
+ TransactionStatus.dropped // the tx nonce was already used
1242
+ ];
1243
+ }
1244
+ function parseStandardTokenTransactionData(data) {
1245
+ try {
1246
+ const txDesc = erc20Interface.parseTransaction({
1247
+ data
1248
+ });
1249
+ if (txDesc) return {
1250
+ name: txDesc.name,
1251
+ methodParams: txDesc.args.toArray(),
1252
+ type: CONTRACT_TYPE_ERC20
1253
+ };
1254
+ } catch {
1255
+ // ignore and next try to parse with erc721 ABI
1256
+ }
1257
+ try {
1258
+ const txDesc = erc721Interface.parseTransaction({
1259
+ data
1260
+ });
1261
+ if (txDesc) return {
1262
+ name: txDesc.name,
1263
+ methodParams: txDesc.args.toArray(),
1264
+ type: CONTRACT_TYPE_ERC721
1265
+ };
1266
+ } catch {
1267
+ // ignore and next try to parse with erc1155 ABI
1268
+ }
1269
+ try {
1270
+ const txDesc = erc1155Interface.parseTransaction({
1271
+ data
1272
+ });
1273
+ if (txDesc) return {
1274
+ name: txDesc.name,
1275
+ methodParams: txDesc.args.toArray(),
1276
+ type: CONTRACT_TYPE_ERC1155
1277
+ };
1278
+ } catch {
1279
+ // ignore and return undefined
1280
+ }
1281
+ return undefined;
1282
+ }
1283
+ const readAddressAsContract = async (provider, address) => {
1284
+ let contractCode;
1285
+ try {
1286
+ contractCode = await provider.request({
1287
+ method: METHOD_TYPES.ETH_GET_CODE,
1288
+ params: [address, "latest"]
1289
+ });
1290
+ } catch (e) {
1291
+ contractCode = null;
1292
+ }
1293
+ const isContractAddress = contractCode ? contractCode !== "0x" && contractCode !== "0x0" : false;
1294
+ return {
1295
+ contractCode,
1296
+ isContractAddress
1297
+ };
1298
+ };
1299
+ async function determineTransactionType(txParams, provider) {
1300
+ const {
1301
+ data,
1302
+ to
1303
+ } = txParams;
1304
+ let name = "";
1305
+ let methodParams = [];
1306
+ let type = "";
1307
+ try {
1308
+ ({
1309
+ name,
1310
+ methodParams,
1311
+ type
1312
+ } = data && parseStandardTokenTransactionData(data) || {});
1313
+ } catch (error) {
1314
+ log.debug("Failed to parse transaction data", error);
1315
+ }
1316
+ let result;
1317
+ let contractCode = "";
1318
+ if (data && !to) {
1319
+ result = TRANSACTION_TYPES.DEPLOY_CONTRACT;
1320
+ } else {
1321
+ const {
1322
+ contractCode: resultCode,
1323
+ isContractAddress
1324
+ } = await readAddressAsContract(provider, to);
1325
+ contractCode = resultCode;
1326
+ if (isContractAddress) {
1327
+ const valueExists = txParams.value && Number(txParams.value) !== 0;
1328
+ const tokenMethodName = [TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, TRANSACTION_TYPES.COLLECTIBLE_METHOD_SAFE_TRANSFER_FROM, TRANSACTION_TYPES.SET_APPROVAL_FOR_ALL].find(x => {
1329
+ var _name;
1330
+ return x.toLowerCase() === ((_name = name) === null || _name === void 0 ? void 0 : _name.toLowerCase());
1331
+ });
1332
+ result = data && tokenMethodName && !valueExists ? tokenMethodName : TRANSACTION_TYPES.CONTRACT_INTERACTION;
1333
+ } else {
1334
+ result = TRANSACTION_TYPES.SENT_ETHER;
1335
+ }
1336
+ }
1337
+ return {
1338
+ type: type || CONTRACT_TYPE_ETH,
1339
+ category: result,
1340
+ methodParams,
1341
+ getCodeResponse: contractCode
1342
+ };
1343
+ }
1344
+
1345
+ function getEtherScanHashLink(txHash, chainId) {
1346
+ if (!SUPPORTED_NETWORKS[chainId]) return "";
1347
+ return `${SUPPORTED_NETWORKS[chainId].blockExplorerUrl}/tx/${txHash}`;
1348
+ }
1349
+ const formatPastTx = (x, lowerCaseSelectedAddress) => {
1350
+ var _x$to;
1351
+ let totalAmountString = "";
1352
+ if (x.type === CONTRACT_TYPE_ERC721 || x.type === CONTRACT_TYPE_ERC1155) totalAmountString = x.symbol;else if (x.type === CONTRACT_TYPE_ERC20) totalAmountString = formatSmallNumbers(Number.parseFloat(x.total_amount), x.symbol, true);else totalAmountString = formatSmallNumbers(Number.parseFloat(x.total_amount), x.type_name, true);
1353
+ const currencyAmountString = x.type === CONTRACT_TYPE_ERC721 || x.type === CONTRACT_TYPE_ERC1155 ? "" : formatSmallNumbers(Number.parseFloat(x.currency_amount), x.selected_currency, true);
1354
+ const finalObject = {
1355
+ id: x.created_at.toString(),
1356
+ date: new Date(x.created_at).toString(),
1357
+ from: x.from,
1358
+ from_aa_address: x.from_aa_address,
1359
+ slicedFrom: typeof x.from === "string" ? addressSlicer(x.from) : "",
1360
+ to: x.to,
1361
+ slicedTo: typeof x.to === "string" ? addressSlicer(x.to) : "",
1362
+ action: lowerCaseSelectedAddress === ((_x$to = x.to) === null || _x$to === void 0 ? void 0 : _x$to.toLowerCase()) || "" ? ACTIVITY_ACTION_RECEIVE : ACTIVITY_ACTION_SEND,
1363
+ totalAmount: x.total_amount,
1364
+ totalAmountString,
1365
+ currencyAmount: x.currency_amount,
1366
+ currencyAmountString,
1367
+ amount: `${totalAmountString} / ${currencyAmountString}`,
1368
+ status: x.status,
1369
+ etherscanLink: getEtherScanHashLink(x.transaction_hash, x.chain_id || MAINNET_CHAIN_ID),
1370
+ chainId: x.chain_id,
1371
+ ethRate: Number.parseFloat(x === null || x === void 0 ? void 0 : x.total_amount) && Number.parseFloat(x === null || x === void 0 ? void 0 : x.currency_amount) ? `1 ${x.symbol} = ${significantDigits(Number.parseFloat(x.currency_amount) / Number.parseFloat(x.total_amount))}` : "",
1372
+ currencyUsed: x.selected_currency,
1373
+ type: x.type,
1374
+ type_name: x.type_name,
1375
+ type_image_link: x.type_image_link,
1376
+ transaction_hash: x.transaction_hash,
1377
+ transaction_category: x.transaction_category,
1378
+ // TODO: // figure out how to handle these values.
1379
+ isEtherscan: x.isEtherscan,
1380
+ input: x.input || "",
1381
+ token_id: x.token_id || "",
1382
+ contract_address: x.contract_address || "",
1383
+ nonce: x.nonce || "",
1384
+ is_cancel: !!x.is_cancel || false,
1385
+ gas: x.gas || "",
1386
+ gasPrice: x.gasPrice || ""
1387
+ };
1388
+ return finalObject;
1389
+ };
1390
+
1391
+ /**
1392
+ * Ref - https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt
1393
+ */
1394
+ const getEthTxStatus = async (hash, provider) => {
1395
+ try {
1396
+ const result = await provider.request({
1397
+ method: METHOD_TYPES.ETH_GET_TRANSACTION_RECEIPT,
1398
+ params: [hash]
1399
+ });
1400
+ if (result === null) return TransactionStatus.submitted;
1401
+ if (result && result.status === "0x1") return TransactionStatus.confirmed;
1402
+ if (result && result.status === "0x0") return TransactionStatus.rejected;
1403
+ return undefined;
1404
+ } catch (err) {
1405
+ log.warn("unable to fetch transaction status", err);
1406
+ return undefined;
1407
+ }
1408
+ };
1409
+ function formatDate(inputDate) {
1410
+ const monthList = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1411
+ const date = new Date(inputDate);
1412
+ const day = date.getDate();
1413
+ const month = monthList[date.getMonth()];
1414
+ const year = date.getFullYear();
1415
+ return `${day} ${month} ${year}`;
1416
+ }
1417
+ function formatTime(time) {
1418
+ return new Date(time).toTimeString().slice(0, 8);
1419
+ }
1420
+ const idleTimeTracker = (activityThresholdTime => {
1421
+ let isIdle = false;
1422
+ let idleTimeout = null;
1423
+ const resetTimer = () => {
1424
+ if (idleTimeout) {
1425
+ window.clearTimeout(idleTimeout);
1426
+ }
1427
+ isIdle = false;
1428
+ idleTimeout = window.setTimeout(() => {
1429
+ isIdle = true;
1430
+ }, activityThresholdTime * 1000);
1431
+ };
1432
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
1433
+ window.addEventListener("load", resetTimer);
1434
+ document.addEventListener("mousemove", resetTimer);
1435
+ document.addEventListener("keydown", resetTimer);
1436
+ }
1437
+ function checkIfIdle() {
1438
+ return isIdle;
1439
+ }
1440
+ return {
1441
+ checkIfIdle
1442
+ };
1443
+ })(60 * 3);
1444
+ function isAddressByChainId(address, _chainId) {
1445
+ // TOOD: add rsk network checks.
1446
+ return isValidAddress(address);
1447
+ }
1448
+ function toChecksumAddressByChainId(address, chainId) {
1449
+ // TOOD: add rsk network checks.
1450
+ if (!isAddressByChainId(address)) return address;
1451
+ return toChecksumAddress(address);
1452
+ }
1453
+ const GAS_LIMITS = {
1454
+ // maximum gasLimit of a simple send
1455
+ SIMPLE: addHexPrefix(21000 .toString(16)),
1456
+ // a base estimate for token transfers.
1457
+ BASE_TOKEN_ESTIMATE: addHexPrefix(100000 .toString(16))
1458
+ };
1459
+ function bnLessThan(a, b) {
1460
+ if (a === null || a === undefined || b === null || b === undefined) {
1461
+ return null;
1462
+ }
1463
+ return new BigNumber(a, 10).lt(b, 10);
1464
+ }
1465
+ const getIpfsEndpoint = path => `https://infura-ipfs.io/${path}`;
1466
+ function sanitizeNftMetdataUrl(url) {
1467
+ let finalUri = url;
1468
+ if (url !== null && url !== void 0 && url.startsWith("ipfs")) {
1469
+ const ipfsPath = url.split("ipfs://")[1];
1470
+ finalUri = getIpfsEndpoint(ipfsPath);
1471
+ }
1472
+ return finalUri;
1473
+ }
1474
+ function getChainType(chainId) {
1475
+ if (chainId === MAINNET_CHAIN_ID) {
1476
+ return "mainnet";
1477
+ } else if (TEST_CHAINS.includes(chainId)) {
1478
+ return "testnet";
1479
+ }
1480
+ return "custom";
1481
+ }
1482
+ const addEtherscanTransactions = async (txn, lowerCaseSelectedAddress, provider, chainId) => {
1483
+ const transactionPromises = await Promise.all(txn.map(async tx => {
1484
+ var _SUPPORTED_NETWORKS$c, _SUPPORTED_NETWORKS$c2;
1485
+ const {
1486
+ category,
1487
+ type
1488
+ } = await determineTransactionType(_objectSpread(_objectSpread({}, tx), {}, {
1489
+ data: tx.input
1490
+ }), provider);
1491
+ tx.transaction_category = tx.transaction_category || category;
1492
+ tx.type_image_link = ((_SUPPORTED_NETWORKS$c = SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c === void 0 ? void 0 : _SUPPORTED_NETWORKS$c.logo) || "";
1493
+ tx.type_name = (_SUPPORTED_NETWORKS$c2 = SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c2 === void 0 ? void 0 : _SUPPORTED_NETWORKS$c2.ticker;
1494
+ tx.type = type;
1495
+ return tx;
1496
+ }));
1497
+ const finalTxs = transactionPromises.reduce((accumulator, x) => {
1498
+ var _SUPPORTED_NETWORKS$c3, _SUPPORTED_NETWORKS$c4;
1499
+ const totalAmount = x.value ? formatEther(x.value) : "";
1500
+ const etherscanTransaction = {
1501
+ etherscanLink: getEtherScanHashLink(x.hash, chainId),
1502
+ type: x.type || ((_SUPPORTED_NETWORKS$c3 = SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c3 === void 0 ? void 0 : _SUPPORTED_NETWORKS$c3.ticker) || CONTRACT_TYPE_ETH,
1503
+ type_image_link: x.type_image_link || "n/a",
1504
+ type_name: x.type_name || "n/a",
1505
+ symbol: (_SUPPORTED_NETWORKS$c4 = SUPPORTED_NETWORKS[chainId]) === null || _SUPPORTED_NETWORKS$c4 === void 0 ? void 0 : _SUPPORTED_NETWORKS$c4.ticker,
1506
+ token_id: x.tokenID || "",
1507
+ total_amount: totalAmount,
1508
+ created_at: new Date(Number(x.timeStamp) * 1000),
1509
+ from: x.from,
1510
+ to: x.to,
1511
+ transaction_hash: x.hash,
1512
+ status: x.txreceipt_status && x.txreceipt_status === "0" ? TransactionStatus.failed : TransactionStatus.approved,
1513
+ isEtherscan: true,
1514
+ input: x.input,
1515
+ contract_address: x.contractAddress,
1516
+ transaction_category: x.transaction_category,
1517
+ gas: x.gas,
1518
+ gasPrice: x.gasPrice,
1519
+ chain_id: chainId,
1520
+ currency_amount: "",
1521
+ nonce: x.nonce,
1522
+ from_aa_address: "",
1523
+ is_cancel: false,
1524
+ selected_currency: ""
1525
+ };
1526
+ accumulator.push(formatPastTx(etherscanTransaction, lowerCaseSelectedAddress));
1527
+ return accumulator;
1528
+ }, []);
1529
+ return finalTxs;
1530
+ };
1531
+
1532
+ const DEFAULT_POLLING_INTERVAL = 20;
1533
+ const DEFAULT_RETRY_TIMEOUT = 2;
1534
+ const SEC = 1000;
1535
+ class PollingBlockTracker extends BaseBlockTracker {
1536
+ constructor(_ref) {
1537
+ let {
1538
+ config,
1539
+ state = {}
1540
+ } = _ref;
1541
+ if (!config.provider) {
1542
+ throw new Error("PollingBlockTracker - no provider specified.");
1543
+ }
1544
+ super({
1545
+ config,
1546
+ state
1547
+ });
1548
+ const pollingInterval = config.pollingInterval || DEFAULT_POLLING_INTERVAL;
1549
+ const retryTimeout = config.retryTimeout || DEFAULT_RETRY_TIMEOUT;
1550
+
1551
+ // merge default + provided config.
1552
+ this.defaultConfig = {
1553
+ provider: config.provider,
1554
+ pollingInterval: pollingInterval * SEC,
1555
+ retryTimeout: retryTimeout * SEC,
1556
+ setSkipCacheFlag: config.setSkipCacheFlag || false
1557
+ };
1558
+ this.initialize();
1559
+ }
1560
+ async checkForLatestBlock() {
1561
+ await this._updateLatestBlock();
1562
+ return this.getLatestBlock();
1563
+ }
1564
+
1565
+ // overrides the BaseBlockTracker._start method.
1566
+ _start() {
1567
+ this._synchronize().catch(err => this.emit("error", err));
1568
+ }
1569
+ async _synchronize() {
1570
+ while (this.state._isRunning) {
1571
+ if (idleTimeTracker.checkIfIdle()) return;
1572
+ try {
1573
+ await this._updateLatestBlock();
1574
+ await timeout(this.config.pollingInterval);
1575
+ } catch (err) {
1576
+ const newErr = new Error(`PollingBlockTracker - encountered an error while attempting to update latest block:\n${err.stack}`);
1577
+ try {
1578
+ this.emit("error", newErr);
1579
+ } catch (emitErr) {
1580
+ log.error(newErr);
1581
+ }
1582
+ await timeout(this.config.retryTimeout);
1583
+ }
1584
+ }
1585
+ }
1586
+ async _updateLatestBlock() {
1587
+ // fetch + set latest block
1588
+ const latestBlock = await this._fetchLatestBlock();
1589
+ this._newPotentialLatest(latestBlock);
1590
+ }
1591
+ async _fetchLatestBlock() {
1592
+ try {
1593
+ const block = await this.config.provider.request({
1594
+ method: "eth_getBlockByNumber",
1595
+ params: ["latest", false]
1596
+ });
1597
+ return {
1598
+ blockHash: block.hash,
1599
+ idempotencyKey: block.number,
1600
+ timestamp: block.timestamp,
1601
+ baseFeePerGas: block.baseFeePerGas,
1602
+ gasLimit: block.gasLimit
1603
+ };
1604
+ } catch (error) {
1605
+ log.error("Polling Block Tracker: ", error);
1606
+ throw new Error(`PollingBlockTracker - encountered error fetching block:\n${error.message}`);
1607
+ }
1608
+ }
1609
+ }
1610
+
1611
+ class CurrencyController extends BaseCurrencyController {
1612
+ constructor(_ref) {
1613
+ let {
1614
+ config,
1615
+ state,
1616
+ onNetworkChanged
1617
+ } = _ref;
1618
+ super({
1619
+ config,
1620
+ state
1621
+ });
1622
+ _defineProperty(this, "conversionInterval", void 0);
1623
+ this.defaultState = _objectSpread(_objectSpread({}, this.defaultState), {}, {
1624
+ commonDenomination: "USD",
1625
+ commonDenominatorPrice: 0
1626
+ });
1627
+ this.initialize();
1628
+ onNetworkChanged(networkState => {
1629
+ // to be called as (listener) => this.networkController.on('networkDidChange', listener);
1630
+ if (networkState.providerConfig.ticker.toUpperCase() !== this.state.nativeCurrency.toUpperCase()) {
1631
+ this.setNativeCurrency(networkState.providerConfig.ticker);
1632
+ this.updateConversionRate();
1633
+ }
1634
+ });
1635
+ }
1636
+ setCommonDenomination(commonDenomination) {
1637
+ this.update({
1638
+ commonDenomination
1639
+ });
1640
+ }
1641
+ getCommonDenomination() {
1642
+ return this.state.commonDenomination;
1643
+ }
1644
+ setCommonDenominatorPrice(commonDenominatorPrice) {
1645
+ this.update({
1646
+ commonDenominatorPrice
1647
+ });
1648
+ }
1649
+ getCommonDenominatorPrice() {
1650
+ return this.state.commonDenominatorPrice;
1651
+ }
1652
+
1653
+ /**
1654
+ * Creates a new poll, using setInterval, to periodically call updateConversionRate. The id of the interval is
1655
+ * stored at the controller's conversionInterval property. If it is called and such an id already exists, the
1656
+ * previous interval is clear and a new one is created.
1657
+ */
1658
+ scheduleConversionInterval() {
1659
+ if (this.conversionInterval) {
1660
+ window.clearInterval(this.conversionInterval);
1661
+ }
1662
+ this.conversionInterval = window.setInterval(() => {
1663
+ if (!idleTimeTracker.checkIfIdle()) {
1664
+ this.updateConversionRate();
1665
+ }
1666
+ }, this.config.pollInterval);
1667
+ }
1668
+
1669
+ /**
1670
+ * Updates the conversionRate and conversionDate properties associated with the currentCurrency. Updated info is
1671
+ * fetched from an external API
1672
+ */
1673
+ async updateConversionRate() {
1674
+ const currentCurrency = this.getCurrentCurrency();
1675
+ const nativeCurrency = this.getNativeCurrency();
1676
+ const commonDenomination = this.getCommonDenomination();
1677
+ const conversionRate = await this.retrieveConversionRate(nativeCurrency, currentCurrency, commonDenomination);
1678
+ const currentCurrencyRate = Number.parseFloat(conversionRate[currentCurrency.toUpperCase()]);
1679
+ const commonDenominationRate = Number.parseFloat(conversionRate[commonDenomination.toUpperCase()]);
1680
+ // set conversion rate
1681
+ if (currentCurrencyRate || commonDenominationRate) {
1682
+ // ETC
1683
+ this.setConversionRate(currentCurrencyRate);
1684
+ this.setConversionDate(Math.floor(Date.now() / 1000).toString());
1685
+ if (currentCurrency.toUpperCase() === commonDenomination.toUpperCase()) {
1686
+ this.setCommonDenominatorPrice(currentCurrencyRate);
1687
+ } else {
1688
+ this.setCommonDenominatorPrice(commonDenominationRate);
1689
+ }
1690
+ } else {
1691
+ this.setConversionRate(0);
1692
+ this.setConversionDate("N/A");
1693
+ }
1694
+ }
1695
+ async retrieveConversionRate(fromCurrency, toCurrency, commonDenomination) {
1696
+ try {
1697
+ // query cryptocompare
1698
+ let apiUrl = `${this.config.api}/currency?fsym=${fromCurrency.toUpperCase()}&tsyms=${toCurrency.toUpperCase()}`;
1699
+ if (commonDenomination && commonDenomination.toUpperCase() !== toCurrency.toUpperCase()) {
1700
+ apiUrl += `,${commonDenomination.toUpperCase()}`;
1701
+ }
1702
+ const parsedResponse = await get(apiUrl);
1703
+ return parsedResponse;
1704
+ } catch (error) {
1705
+ log.error(error, `CurrencyController - updateCommonDenominatorPrice: Failed to query rate for currency: ${fromCurrency}/ ${toCurrency}`);
1706
+ }
1707
+ return {
1708
+ [toCurrency.toUpperCase()]: "0",
1709
+ [commonDenomination.toUpperCase()]: "0"
1710
+ };
1711
+ }
1712
+ }
1713
+
1714
+ const _excluded$1 = ["aBase", "bBase"],
1715
+ _excluded2 = ["aBase", "bBase"],
1716
+ _excluded3 = ["multiplicandBase", "multiplierBase"];
1717
+
1718
+ // Big Number Constants
1719
+ const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber("1000000000000000000");
1720
+ const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber("1000000000");
1721
+ const BIG_NUMBER_ETH_MULTIPLIER = new BigNumber("1");
1722
+ // Setter Maps
1723
+ const toBigNumber = {
1724
+ hex: n => new BigNumber(stripHexPrefix(n), 16),
1725
+ dec: n => new BigNumber(String(n), 10),
1726
+ BN: n => new BigNumber(n.toString(16), 16)
1727
+ };
1728
+ const toNormalizedDenomination = {
1729
+ WEI: bigNumber => bigNumber.div(BIG_NUMBER_WEI_MULTIPLIER),
1730
+ GWEI: bigNumber => bigNumber.div(BIG_NUMBER_GWEI_MULTIPLIER),
1731
+ ETH: bigNumber => bigNumber.div(BIG_NUMBER_ETH_MULTIPLIER)
1732
+ };
1733
+ const toSpecifiedDenomination = {
1734
+ WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).dp(0, BigNumber.ROUND_HALF_UP),
1735
+ GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).dp(9, BigNumber.ROUND_HALF_UP),
1736
+ ETH: bigNumber => bigNumber.times(BIG_NUMBER_ETH_MULTIPLIER).dp(9, BigNumber.ROUND_HALF_UP)
1737
+ };
1738
+ const baseChange = {
1739
+ hex: n => n.toString(16),
1740
+ dec: n => new BigNumber(n).toString(10),
1741
+ BN: n => new BN(n.toString(16))
1742
+ };
1743
+
1744
+ // Utility function for checking base types
1745
+ const isValidBase = base => Number.isInteger(base) && base > 1;
1746
+
1747
+ /**
1748
+ * Utility method to convert a value between denominations, formats and currencies.
1749
+ */
1750
+ const converter = _ref => {
1751
+ let {
1752
+ value,
1753
+ fromNumericBase,
1754
+ fromDenomination,
1755
+ fromCurrency,
1756
+ toNumericBase,
1757
+ toDenomination,
1758
+ toCurrency,
1759
+ numberOfDecimals,
1760
+ conversionRate,
1761
+ invertConversionRate,
1762
+ roundDown
1763
+ } = _ref;
1764
+ let convertedValue = fromNumericBase ? toBigNumber[fromNumericBase](value) : value;
1765
+ if (fromDenomination) {
1766
+ convertedValue = toNormalizedDenomination[fromDenomination](convertedValue);
1767
+ }
1768
+ if (fromCurrency !== toCurrency) {
1769
+ if (conversionRate === null || conversionRate === undefined) {
1770
+ throw new Error(`Converting from ${fromCurrency} to ${toCurrency} requires a conversionRate, but one was not provided`);
1771
+ }
1772
+ let rate = toBigNumber.dec(conversionRate);
1773
+ if (invertConversionRate) {
1774
+ rate = new BigNumber(1).div(conversionRate);
1775
+ }
1776
+ convertedValue = convertedValue.times(rate);
1777
+ }
1778
+ if (toDenomination) {
1779
+ convertedValue = toSpecifiedDenomination[toDenomination](convertedValue);
1780
+ }
1781
+ if (numberOfDecimals) {
1782
+ convertedValue = convertedValue.dp(numberOfDecimals, BigNumber.ROUND_HALF_DOWN);
1783
+ }
1784
+ if (roundDown) {
1785
+ convertedValue = convertedValue.dp(roundDown, BigNumber.ROUND_DOWN);
1786
+ }
1787
+ if (toNumericBase) {
1788
+ convertedValue = baseChange[toNumericBase](convertedValue);
1789
+ }
1790
+ return convertedValue;
1791
+ };
1792
+ const conversionUtil = (value, _ref2) => {
1793
+ let {
1794
+ fromCurrency = null,
1795
+ toCurrency = fromCurrency,
1796
+ fromNumericBase,
1797
+ toNumericBase,
1798
+ fromDenomination,
1799
+ toDenomination,
1800
+ numberOfDecimals,
1801
+ conversionRate,
1802
+ invertConversionRate
1803
+ } = _ref2;
1804
+ if (fromCurrency !== toCurrency && !conversionRate) {
1805
+ return 0;
1806
+ }
1807
+ return converter({
1808
+ fromCurrency,
1809
+ toCurrency,
1810
+ fromNumericBase,
1811
+ toNumericBase,
1812
+ fromDenomination,
1813
+ toDenomination,
1814
+ numberOfDecimals,
1815
+ conversionRate,
1816
+ invertConversionRate,
1434
1817
  value
1435
1818
  });
1436
1819
  };
1820
+ const getBigNumber = (value, base) => {
1821
+ if (!isValidBase(base)) {
1822
+ throw new Error("Must specificy valid base");
1823
+ }
1824
+
1825
+ // We don't include 'number' here, because BigNumber will throw if passed
1826
+ // a number primitive it considers unsafe.
1827
+ if (typeof value === "string" || value instanceof BigNumber) {
1828
+ return new BigNumber(value, base);
1829
+ }
1830
+ return new BigNumber(String(value), base);
1831
+ };
1832
+ const addCurrencies = function (a, b) {
1833
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1834
+ const {
1835
+ aBase,
1836
+ bBase
1837
+ } = options,
1838
+ conversionOptions = _objectWithoutProperties(options, _excluded$1);
1839
+ if (!isValidBase(aBase) || !isValidBase(bBase)) {
1840
+ throw new Error("Must specify valid aBase and bBase");
1841
+ }
1842
+ const value = getBigNumber(a, aBase).plus(getBigNumber(b, bBase));
1843
+ return converter(_objectSpread({
1844
+ value
1845
+ }, conversionOptions));
1846
+ };
1847
+ const subtractCurrencies = function (a, b) {
1848
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1849
+ const {
1850
+ aBase,
1851
+ bBase
1852
+ } = options,
1853
+ conversionOptions = _objectWithoutProperties(options, _excluded2);
1854
+ if (!isValidBase(aBase) || !isValidBase(bBase)) {
1855
+ throw new Error("Must specify valid aBase and bBase");
1856
+ }
1857
+ const value = getBigNumber(a, aBase).minus(getBigNumber(b, bBase));
1858
+ return converter(_objectSpread({
1859
+ value
1860
+ }, conversionOptions));
1861
+ };
1862
+ const multiplyCurrencies = function (a, b) {
1863
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1864
+ const {
1865
+ multiplicandBase,
1866
+ multiplierBase
1867
+ } = options,
1868
+ conversionOptions = _objectWithoutProperties(options, _excluded3);
1869
+ if (!isValidBase(multiplicandBase) || !isValidBase(multiplierBase)) {
1870
+ throw new Error("Must specify valid multiplicandBase and multiplierBase");
1871
+ }
1872
+ const value = getBigNumber(a, multiplicandBase).times(getBigNumber(b, multiplierBase));
1873
+ return converter(_objectSpread({
1874
+ value
1875
+ }, conversionOptions));
1876
+ };
1877
+ const conversionGreaterThan = (_ref3, _ref4) => {
1878
+ let secondProps = Object.assign({}, (_objectDestructuringEmpty(_ref4), _ref4));
1879
+ let firstProps = Object.assign({}, (_objectDestructuringEmpty(_ref3), _ref3));
1880
+ const firstValue = converter(_objectSpread({}, firstProps));
1881
+ const secondValue = converter(_objectSpread({}, secondProps));
1882
+ return firstValue.gt(secondValue);
1883
+ };
1884
+ const conversionLessThan = (_ref5, _ref6) => {
1885
+ let secondProps = Object.assign({}, (_objectDestructuringEmpty(_ref6), _ref6));
1886
+ let firstProps = Object.assign({}, (_objectDestructuringEmpty(_ref5), _ref5));
1887
+ const firstValue = converter(_objectSpread({}, firstProps));
1888
+ const secondValue = converter(_objectSpread({}, secondProps));
1889
+ return firstValue.lt(secondValue);
1890
+ };
1891
+ const conversionMax = (_ref7, _ref8) => {
1892
+ let secondProps = Object.assign({}, (_objectDestructuringEmpty(_ref8), _ref8));
1893
+ let firstProps = Object.assign({}, (_objectDestructuringEmpty(_ref7), _ref7));
1894
+ const firstIsGreater = conversionGreaterThan(_objectSpread({}, firstProps), _objectSpread({}, secondProps));
1895
+ return firstIsGreater ? firstProps.value : secondProps.value;
1896
+ };
1897
+ const conversionGTE = (_ref9, _ref10) => {
1898
+ let secondProps = Object.assign({}, (_objectDestructuringEmpty(_ref10), _ref10));
1899
+ let firstProps = Object.assign({}, (_objectDestructuringEmpty(_ref9), _ref9));
1900
+ const firstValue = converter(_objectSpread({}, firstProps));
1901
+ const secondValue = converter(_objectSpread({}, secondProps));
1902
+ return firstValue.isGreaterThanOrEqualTo(secondValue);
1903
+ };
1904
+ const conversionLTE = (_ref11, _ref12) => {
1905
+ let secondProps = Object.assign({}, (_objectDestructuringEmpty(_ref12), _ref12));
1906
+ let firstProps = Object.assign({}, (_objectDestructuringEmpty(_ref11), _ref11));
1907
+ const firstValue = converter(_objectSpread({}, firstProps));
1908
+ const secondValue = converter(_objectSpread({}, secondProps));
1909
+ return firstValue.isLessThanOrEqualTo(secondValue);
1910
+ };
1911
+ const toNegative = function (n) {
1912
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1913
+ return multiplyCurrencies(n, -1, options);
1914
+ };
1437
1915
  const decGWEIToHexWEI = decGWEI => {
1438
1916
  return conversionUtil(decGWEI, {
1439
1917
  fromNumericBase: "dec",
@@ -3619,7 +4097,8 @@ class PreferencesController extends BasePreferencesController {
3619
4097
  defaultPreferences: {
3620
4098
  formattedPastTransactions: [],
3621
4099
  fetchedPastTx: [],
3622
- paymentTx: []
4100
+ paymentTx: [],
4101
+ etherscanTransactions: []
3623
4102
  },
3624
4103
  signAuthMessage
3625
4104
  });
@@ -3800,10 +4279,15 @@ class PreferencesController extends BasePreferencesController {
3800
4279
  chainId
3801
4280
  } = this.getProviderConfig();
3802
4281
  if (ETHERSCAN_SUPPORTED_CHAINS.includes(chainId)) {
3803
- return this.fetchEtherscanTx({
4282
+ const etherscanTxn = await this.fetchEtherscanTx({
3804
4283
  selectedAddress,
3805
4284
  chainId: this.getProviderConfig().chainId
3806
4285
  });
4286
+ const finalEthScanTxn = await addEtherscanTransactions(etherscanTxn, selectedAddress, this.provider, chainId);
4287
+ this.updateState({
4288
+ etherscanTransactions: finalEthScanTxn
4289
+ });
4290
+ return etherscanTxn;
3807
4291
  }
3808
4292
  }
3809
4293
  }
@@ -3812,6 +4296,7 @@ class PreferencesController extends BasePreferencesController {
3812
4296
  const url = new URL(`${this.config.api}/etherscan`);
3813
4297
  url.searchParams.append("chainId", parameters.chainId);
3814
4298
  const response = await get(url.href, this.headers(parameters.selectedAddress));
4299
+ log.info("Etherscan Response", response);
3815
4300
  return response.success ? response.data : [];
3816
4301
  } catch (error) {
3817
4302
  log.error("unable to fetch etherscan tx", error);
@@ -4522,712 +5007,385 @@ class NonceTracker {
4522
5007
  let mutex = this.lockMap[lockId];
4523
5008
  if (!mutex) {
4524
5009
  mutex = new Mutex();
4525
- this.lockMap[lockId] = mutex;
4526
- }
4527
- return mutex;
4528
- }
4529
- async _getNetworkNextNonce(address) {
4530
- // calculate next nonce
4531
- // we need to make sure our base count
4532
- // and pending count are from the same block
4533
- const block = await this.blockTracker.getLatestBlock();
4534
- const baseCountStr = await this.provider.request({
4535
- method: METHOD_TYPES.ETH_GET_TRANSACTION_COUNT,
4536
- params: [address, block.idempotencyKey]
4537
- });
4538
- const baseCount = Number.parseInt(baseCountStr, 16);
4539
- const nonceDetails = {
4540
- block,
4541
- baseCount
4542
- };
4543
- return {
4544
- name: "network",
4545
- nonce: baseCount,
4546
- details: nonceDetails
4547
- };
4548
- }
4549
- _getHighestLocallyConfirmed(address) {
4550
- const confirmedTransactions = this.getConfirmedTransactions(address);
4551
- const highest = this._getHighestNonce(confirmedTransactions);
4552
- return Number.isInteger(highest) ? highest + 1 : 0;
4553
- }
4554
- _getHighestNonce(txList) {
4555
- const nonces = txList.map(txMeta => {
4556
- const {
4557
- nonce
4558
- } = txMeta.transaction;
4559
- return Number.parseInt(nonce, 16);
4560
- });
4561
- const highestNonce = Math.max.apply(null, nonces);
4562
- return highestNonce;
4563
- }
4564
- _getHighestContinuousFrom(txList, startPoint) {
4565
- const nonces = new Set(txList.map(txMeta => {
4566
- const {
4567
- nonce
4568
- } = txMeta.transaction;
4569
- return Number.parseInt(nonce, 16);
4570
- }));
4571
- let highest = startPoint;
4572
- while (nonces.has(highest)) {
4573
- highest += 1;
4574
- }
4575
- return {
4576
- name: "local",
4577
- nonce: highest,
4578
- details: {
4579
- startPoint,
4580
- highest
4581
- }
4582
- };
4583
- }
4584
- }
4585
-
4586
- class PendingTransactionTracker extends SafeEventEmitter {
4587
- constructor(_ref) {
4588
- let {
4589
- provider,
4590
- nonceTracker,
4591
- approveTransaction,
4592
- publishTransaction,
4593
- getPendingTransactions,
4594
- getConfirmedTransactions
4595
- } = _ref;
4596
- super();
4597
- _defineProperty(this, "DROPPED_BUFFER_COUNT", 3);
4598
- _defineProperty(this, "nonceTracker", void 0);
4599
- _defineProperty(this, "provider", void 0);
4600
- _defineProperty(this, "approveTransaction", void 0);
4601
- _defineProperty(this, "droppedBlocksBufferByHash", void 0);
4602
- _defineProperty(this, "getConfirmedTransactions", void 0);
4603
- _defineProperty(this, "getPendingTransactions", void 0);
4604
- _defineProperty(this, "publishTransaction", void 0);
4605
- this.provider = provider;
4606
- this.nonceTracker = nonceTracker;
4607
- this.approveTransaction = approveTransaction;
4608
- this.publishTransaction = publishTransaction;
4609
- this.getPendingTransactions = getPendingTransactions;
4610
- this.getConfirmedTransactions = getConfirmedTransactions;
4611
- this.droppedBlocksBufferByHash = new Map();
4612
- }
4613
-
4614
- /**
4615
- checks the network for signed txs and releases the nonce global lock if it is
4616
- */
4617
- async updatePendingTxs() {
4618
- // in order to keep the nonceTracker accurate we block it while updating pending transactions
4619
- const nonceGlobalLock = await this.nonceTracker.getGlobalLock();
4620
- try {
4621
- const pendingTxs = this.getPendingTransactions();
4622
- await Promise.all(pendingTxs.map(txMeta => this._checkPendingTx(txMeta)));
4623
- } catch (error) {
4624
- log.error("PendingTransactionTracker - Error updating pending transactions");
4625
- log.error(error);
4626
- }
4627
- nonceGlobalLock.releaseLock();
4628
- }
4629
- async resubmitPendingTxs(block) {
4630
- const pending = this.getPendingTransactions();
4631
- // only try resubmitting if their are transactions to resubmit
4632
- if (pending.length === 0) return;
4633
- // Keep this as a for loop because we want to wait for each item to be submitted
4634
- for (const txMeta of pending) {
4635
- try {
4636
- await this._resubmitTx(txMeta, block.idempotencyKey);
4637
- } catch (error) {
4638
- var _value;
4639
- /*
4640
- Dont marked as failed if the error is a "known" transaction warning
4641
- "there is already a transaction with the same sender-nonce
4642
- but higher/same gas price"
4643
- Also don't mark as failed if it has ever been broadcast successfully.
4644
- A successful broadcast means it may still be mined.
4645
- */
4646
- const errorMessage = ((_value = error.value) === null || _value === void 0 || (_value = _value.message) === null || _value === void 0 ? void 0 : _value.toLowerCase()) || error.message.toLowerCase();
4647
- const isKnownTx =
4648
- // geth
4649
- errorMessage.includes("replacement transaction underpriced") || errorMessage.includes("known transaction") ||
4650
- // parity
4651
- errorMessage.includes("gas price too low to replace") || errorMessage.includes("transaction with the same hash was already imported") ||
4652
- // other
4653
- errorMessage.includes("gateway timeout") || errorMessage.includes("nonce too low");
4654
- // ignore resubmit warnings, return early
4655
- if (isKnownTx) return;
4656
- // encountered real error - transition to error state
4657
- txMeta.warning = {
4658
- error: errorMessage,
4659
- message: "There was an error when resubmitting this transaction."
4660
- };
4661
- this.emit(TX_EVENTS.TX_WARNING, {
4662
- txMeta,
4663
- error,
4664
- txId: txMeta.id
4665
- });
4666
- }
4667
- }
4668
- }
4669
- async _resubmitTx(txMeta, latestBlockNumber) {
4670
- if (!txMeta.firstRetryBlockNumber) {
4671
- this.emit(TX_EVENTS.TX_BLOCK_UPDATE, {
4672
- txMeta,
4673
- latestBlockNumber,
4674
- txId: txMeta.id
4675
- });
4676
- }
4677
- const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber;
4678
- const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16);
4679
- const retryCount = txMeta.retryCount || 0;
4680
-
4681
- // Exponential backoff to limit retries at publishing (capped at last 15 mins)
4682
- if (txBlockDistance <= Math.min(50, 2 ** retryCount)) return undefined;
4683
-
4684
- // Only auto-submit already-signed txs:
4685
- if (!("rawTx" in txMeta)) return this.approveTransaction(txMeta.id);
4686
- const {
4687
- rawTx
4688
- } = txMeta;
4689
- const txHash = await this.publishTransaction(rawTx);
4690
-
4691
- // Increment successful tries:
4692
- this.emit(TX_EVENTS.TX_RETRY, {
4693
- txMeta,
4694
- txId: txMeta.id
4695
- });
4696
- return txHash;
4697
- }
4698
- async _checkPendingTx(foundTx) {
4699
- const txMeta = foundTx;
4700
- const txHash = txMeta.transactionHash;
4701
- const txId = txMeta.id;
4702
-
4703
- // Only check submitted txs
4704
- if (txMeta.status !== TransactionStatus.submitted) return;
4705
-
4706
- // extra check in case there was an uncaught error during the
4707
- // signature and submission process
4708
- if (!txHash) {
4709
- const noTxHashError = new Error("We had an error while submitting this transaction, please try again.");
4710
- noTxHashError.name = "NoTxHashError";
4711
- this.emit(TX_EVENTS.TX_FAILED, {
4712
- txId,
4713
- error: noTxHashError
4714
- });
4715
- return;
4716
- }
4717
-
4718
- // If another tx with the same nonce is mined, set as failed.
4719
- if (this._checkIfNonceIsTaken(txMeta)) {
4720
- this.emit(TX_EVENTS.TX_DROPPED, {
4721
- txId
4722
- });
4723
- return;
4724
- }
4725
- try {
4726
- const transactionReceipt = await this.provider.request({
4727
- method: METHOD_TYPES.ETH_GET_TRANSACTION_RECEIPT,
4728
- params: [txHash]
4729
- });
4730
- if (transactionReceipt !== null && transactionReceipt !== void 0 && transactionReceipt.blockNumber) {
4731
- const {
4732
- baseFeePerGas,
4733
- timestamp
4734
- } = await this.provider.request({
4735
- method: METHOD_TYPES.ETH_GET_BLOCK_BY_HASH,
4736
- params: [transactionReceipt.blockHash, false]
4737
- });
4738
- this.emit(TX_EVENTS.TX_CONFIRMED, {
4739
- txId,
4740
- txReceipt: transactionReceipt,
4741
- baseFeePerGas,
4742
- blockTimestamp: timestamp
4743
- });
4744
- return;
4745
- }
4746
- } catch (error) {
4747
- log.error("error while loading tx", error);
4748
- txMeta.warning = {
4749
- error: error.message,
4750
- message: "There was a problem loading this transaction."
4751
- };
4752
- this.emit(TX_EVENTS.TX_WARNING, {
4753
- txMeta
4754
- });
4755
- }
4756
- if (await this._checkIfTxWasDropped(txMeta)) {
4757
- this.emit(TX_EVENTS.TX_DROPPED, {
4758
- txId
4759
- });
5010
+ this.lockMap[lockId] = mutex;
4760
5011
  }
5012
+ return mutex;
4761
5013
  }
4762
- async _checkIfTxWasDropped(txMeta) {
4763
- const {
4764
- transactionHash: txHash,
4765
- transaction: {
4766
- nonce,
4767
- from
4768
- }
4769
- } = txMeta;
4770
- const networkNextNonce = await this.provider.request({
5014
+ async _getNetworkNextNonce(address) {
5015
+ // calculate next nonce
5016
+ // we need to make sure our base count
5017
+ // and pending count are from the same block
5018
+ const block = await this.blockTracker.getLatestBlock();
5019
+ const baseCountStr = await this.provider.request({
4771
5020
  method: METHOD_TYPES.ETH_GET_TRANSACTION_COUNT,
4772
- params: [from, "latest"]
5021
+ params: [address, block.idempotencyKey]
4773
5022
  });
4774
- if (Number.parseInt(nonce, 16) >= Number.parseInt(networkNextNonce, 16)) {
4775
- return false;
4776
- }
4777
- if (!this.droppedBlocksBufferByHash.has(txHash)) {
4778
- this.droppedBlocksBufferByHash.set(txHash, 0);
4779
- }
4780
- const currentBlockBuffer = this.droppedBlocksBufferByHash.get(txHash);
4781
- if (currentBlockBuffer < this.DROPPED_BUFFER_COUNT) {
4782
- this.droppedBlocksBufferByHash.set(txHash, currentBlockBuffer + 1);
4783
- return false;
4784
- }
4785
- this.droppedBlocksBufferByHash.delete(txHash);
4786
- return true;
5023
+ const baseCount = Number.parseInt(baseCountStr, 16);
5024
+ const nonceDetails = {
5025
+ block,
5026
+ baseCount
5027
+ };
5028
+ return {
5029
+ name: "network",
5030
+ nonce: baseCount,
5031
+ details: nonceDetails
5032
+ };
4787
5033
  }
4788
- _checkIfNonceIsTaken(txMeta) {
4789
- const address = txMeta.transaction.from;
4790
- const completed = this.getConfirmedTransactions(address);
4791
- return completed.some(otherMeta => {
4792
- if (otherMeta.id === txMeta.id) {
4793
- return false;
4794
- }
4795
- return otherMeta.transaction.nonce === txMeta.transaction.nonce;
4796
- });
5034
+ _getHighestLocallyConfirmed(address) {
5035
+ const confirmedTransactions = this.getConfirmedTransactions(address);
5036
+ const highest = this._getHighestNonce(confirmedTransactions);
5037
+ return Number.isInteger(highest) ? highest + 1 : 0;
4797
5038
  }
4798
- }
4799
-
4800
- class TransactionGasUtil {
4801
- constructor(provider, blockTracker) {
4802
- _defineProperty(this, "provider", void 0);
4803
- _defineProperty(this, "blockTracker", void 0);
4804
- this.provider = provider;
4805
- this.blockTracker = blockTracker;
5039
+ _getHighestNonce(txList) {
5040
+ const nonces = txList.map(txMeta => {
5041
+ const {
5042
+ nonce
5043
+ } = txMeta.transaction;
5044
+ return Number.parseInt(nonce, 16);
5045
+ });
5046
+ const highestNonce = Math.max.apply(null, nonces);
5047
+ return highestNonce;
4806
5048
  }
4807
- async analyzeGasUsage(txMeta) {
4808
- const block = await this.blockTracker.getLatestBlock();
4809
- // fallback to block gasLimit
4810
- const blockGasLimitBN = new BN$1(stripHexPrefix(block.gasLimit), 16);
4811
- const saferGasLimitBN = blockGasLimitBN.mul(new BN$1(19)).div(new BN$1(20));
4812
- let estimatedGasHex = addHexPrefix(saferGasLimitBN.toString("hex"));
4813
- let simulationFails;
4814
- try {
4815
- estimatedGasHex = await this.estimateTxGas(txMeta);
4816
- } catch (error) {
4817
- log.warn(error);
4818
- simulationFails = {
4819
- reason: error.message,
4820
- errorKey: error.errorKey,
4821
- debug: {
4822
- blockNumber: block.idempotencyKey,
4823
- blockGasLimit: block.gasLimit
4824
- }
4825
- };
5049
+ _getHighestContinuousFrom(txList, startPoint) {
5050
+ const nonces = new Set(txList.map(txMeta => {
5051
+ const {
5052
+ nonce
5053
+ } = txMeta.transaction;
5054
+ return Number.parseInt(nonce, 16);
5055
+ }));
5056
+ let highest = startPoint;
5057
+ while (nonces.has(highest)) {
5058
+ highest += 1;
4826
5059
  }
4827
5060
  return {
4828
- blockGasLimit: block.gasLimit,
4829
- estimatedGasHex,
4830
- simulationFails
5061
+ name: "local",
5062
+ nonce: highest,
5063
+ details: {
5064
+ startPoint,
5065
+ highest
5066
+ }
4831
5067
  };
4832
5068
  }
5069
+ }
4833
5070
 
4834
- /**
4835
- Adds a gas buffer with out exceeding the block gas limit
4836
- */
4837
- addGasBuffer(initialGasLimitHex, blockGasLimitHex) {
4838
- let multiplier = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1.5;
4839
- const initialGasLimitBn = new BN$1(stripHexPrefix(initialGasLimitHex), 16);
4840
- const blockGasLimitBn = new BN$1(stripHexPrefix(blockGasLimitHex), 16);
4841
- const upperGasLimitBn = blockGasLimitBn.muln(0.9);
4842
- const bufferedGasLimitBn = initialGasLimitBn.muln(multiplier);
4843
-
4844
- // if initialGasLimit is above blockGasLimit, dont modify it
4845
- if (initialGasLimitBn.gt(upperGasLimitBn)) return addHexPrefix(initialGasLimitBn.toString("hex"));
4846
- // if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
4847
- if (bufferedGasLimitBn.lt(upperGasLimitBn)) return addHexPrefix(bufferedGasLimitBn.toString("hex"));
4848
- // otherwise use blockGasLimit
4849
- return addHexPrefix(upperGasLimitBn.toString("hex"));
5071
+ class PendingTransactionTracker extends SafeEventEmitter {
5072
+ constructor(_ref) {
5073
+ let {
5074
+ provider,
5075
+ nonceTracker,
5076
+ approveTransaction,
5077
+ publishTransaction,
5078
+ getPendingTransactions,
5079
+ getConfirmedTransactions
5080
+ } = _ref;
5081
+ super();
5082
+ _defineProperty(this, "DROPPED_BUFFER_COUNT", 3);
5083
+ _defineProperty(this, "nonceTracker", void 0);
5084
+ _defineProperty(this, "provider", void 0);
5085
+ _defineProperty(this, "approveTransaction", void 0);
5086
+ _defineProperty(this, "droppedBlocksBufferByHash", void 0);
5087
+ _defineProperty(this, "getConfirmedTransactions", void 0);
5088
+ _defineProperty(this, "getPendingTransactions", void 0);
5089
+ _defineProperty(this, "publishTransaction", void 0);
5090
+ this.provider = provider;
5091
+ this.nonceTracker = nonceTracker;
5092
+ this.approveTransaction = approveTransaction;
5093
+ this.publishTransaction = publishTransaction;
5094
+ this.getPendingTransactions = getPendingTransactions;
5095
+ this.getConfirmedTransactions = getConfirmedTransactions;
5096
+ this.droppedBlocksBufferByHash = new Map();
4850
5097
  }
4851
5098
 
4852
5099
  /**
4853
- Estimates the tx's gas usage
5100
+ checks the network for signed txs and releases the nonce global lock if it is
4854
5101
  */
4855
- async estimateTxGas(txMeta) {
4856
- const txParams = cloneDeep(txMeta.transaction);
4857
-
4858
- // `eth_estimateGas` can fail if the user has insufficient balance for the
4859
- // value being sent, or for the gas cost. We don't want to check their
4860
- // balance here, we just want the gas estimate. The gas price is removed
4861
- // to skip those balance checks. We check balance elsewhere. We also delete
4862
- // maxFeePerGas and maxPriorityFeePerGas to support EIP-1559 txs.
4863
- delete txParams.gasPrice;
4864
- delete txParams.maxFeePerGas;
4865
- delete txParams.maxPriorityFeePerGas;
4866
- return this.provider.request({
4867
- method: "eth_estimateGas",
4868
- params: [txParams]
4869
- });
4870
- }
4871
- }
4872
-
4873
- /**
4874
- Generates an array of history objects sense the previous state.
4875
- The object has the keys
4876
- op (the operation performed),
4877
- path (the key and if a nested object then each key will be seperated with a `/`)
4878
- value
4879
- with the first entry having the note and a timestamp when the change took place
4880
- */
4881
- function generateHistoryEntry(previousState, newState, note) {
4882
- const entry = jsonDiffer.compare(previousState, newState);
4883
- // Add a note to the first op, since it breaks if we append it to the entry
4884
- if (entry[0]) {
4885
- if (note) {
4886
- entry[0].note = note;
5102
+ async updatePendingTxs() {
5103
+ // in order to keep the nonceTracker accurate we block it while updating pending transactions
5104
+ const nonceGlobalLock = await this.nonceTracker.getGlobalLock();
5105
+ try {
5106
+ const pendingTxs = this.getPendingTransactions();
5107
+ await Promise.all(pendingTxs.map(txMeta => this._checkPendingTx(txMeta)));
5108
+ } catch (error) {
5109
+ log.error("PendingTransactionTracker - Error updating pending transactions");
5110
+ log.error(error);
4887
5111
  }
4888
- entry[0].timestamp = Date.now();
4889
- }
4890
- return entry;
4891
- }
4892
-
4893
- /**
4894
- Recovers previous txMeta state obj
4895
- */
4896
- function replayHistory(_shortHistory) {
4897
- const shortHistory = cloneDeep(_shortHistory);
4898
- return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument);
4899
- }
4900
- function snapshotFromTxMeta(txMeta) {
4901
- const shallow = _objectSpread({}, txMeta);
4902
- delete shallow.history;
4903
- return cloneDeep(shallow);
4904
- }
4905
-
4906
- const erc20Interface = new Interface(erc20Abi);
4907
- const erc721Interface = new Interface(erc721Abi);
4908
- const erc1155Interface = new Interface(erc1155Abi);
4909
-
4910
- // functions that handle normalizing of that key in txParams
4911
-
4912
- const normalizers = {
4913
- from: function (from) {
4914
- let LowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
4915
- return LowerCase ? addHexPrefix(from).toLowerCase() : addHexPrefix(from);
4916
- },
4917
- to: function (to) {
4918
- let LowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
4919
- return LowerCase ? addHexPrefix(to).toLowerCase() : addHexPrefix(to);
4920
- },
4921
- nonce: nonce => addHexPrefix(nonce),
4922
- customNonceValue: nonce => addHexPrefix(nonce),
4923
- value: value => addHexPrefix(value),
4924
- data: data => addHexPrefix(data),
4925
- gas: gas => addHexPrefix(gas),
4926
- gasPrice: gasPrice => addHexPrefix(gasPrice),
4927
- type: addHexPrefix,
4928
- maxFeePerGas: addHexPrefix,
4929
- maxPriorityFeePerGas: addHexPrefix
4930
- };
4931
-
4932
- /**
4933
- * normalizes txParams
4934
- */
4935
- function normalizeTxParameters(txParameters) {
4936
- let lowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
4937
- // apply only keys in the normalizers
4938
- const normalizedTxParameters = {
4939
- id: txParameters.id || randomId(),
4940
- from: txParameters.from
4941
- };
4942
- for (const key in normalizers) {
4943
- const currentKey = key;
4944
- if (txParameters[currentKey])
4945
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
4946
- normalizedTxParameters[currentKey] = normalizers[currentKey](txParameters[currentKey], lowerCase);
5112
+ nonceGlobalLock.releaseLock();
4947
5113
  }
4948
- return normalizedTxParameters;
4949
- }
4950
- function transactionMatchesNetwork(transaction, chainId) {
4951
- if (typeof transaction.chainId !== "undefined") {
4952
- return transaction.chainId === chainId;
5114
+ async resubmitPendingTxs(block) {
5115
+ const pending = this.getPendingTransactions();
5116
+ // only try resubmitting if their are transactions to resubmit
5117
+ if (pending.length === 0) return;
5118
+ // Keep this as a for loop because we want to wait for each item to be submitted
5119
+ for (const txMeta of pending) {
5120
+ try {
5121
+ await this._resubmitTx(txMeta, block.idempotencyKey);
5122
+ } catch (error) {
5123
+ var _value;
5124
+ /*
5125
+ Dont marked as failed if the error is a "known" transaction warning
5126
+ "there is already a transaction with the same sender-nonce
5127
+ but higher/same gas price"
5128
+ Also don't mark as failed if it has ever been broadcast successfully.
5129
+ A successful broadcast means it may still be mined.
5130
+ */
5131
+ const errorMessage = ((_value = error.value) === null || _value === void 0 || (_value = _value.message) === null || _value === void 0 ? void 0 : _value.toLowerCase()) || error.message.toLowerCase();
5132
+ const isKnownTx =
5133
+ // geth
5134
+ errorMessage.includes("replacement transaction underpriced") || errorMessage.includes("known transaction") ||
5135
+ // parity
5136
+ errorMessage.includes("gas price too low to replace") || errorMessage.includes("transaction with the same hash was already imported") ||
5137
+ // other
5138
+ errorMessage.includes("gateway timeout") || errorMessage.includes("nonce too low");
5139
+ // ignore resubmit warnings, return early
5140
+ if (isKnownTx) return;
5141
+ // encountered real error - transition to error state
5142
+ txMeta.warning = {
5143
+ error: errorMessage,
5144
+ message: "There was an error when resubmitting this transaction."
5145
+ };
5146
+ this.emit(TX_EVENTS.TX_WARNING, {
5147
+ txMeta,
5148
+ error,
5149
+ txId: txMeta.id
5150
+ });
5151
+ }
5152
+ }
4953
5153
  }
4954
- return false;
4955
- }
4956
-
4957
- /**
4958
- * Determines if the maxFeePerGas and maxPriorityFeePerGas fields are supplied
4959
- * and valid inputs. This will return false for non hex string inputs.
4960
- * the transaction to check
4961
- * @returns true if transaction uses valid EIP1559 fields
4962
- */
4963
- function isEIP1559Transaction(transaction) {
4964
- var _transaction$transact, _transaction$transact2;
4965
- return isHexString(addHexPrefix(transaction === null || transaction === void 0 || (_transaction$transact = transaction.transaction) === null || _transaction$transact === void 0 ? void 0 : _transaction$transact.maxFeePerGas)) && isHexString(addHexPrefix(transaction === null || transaction === void 0 || (_transaction$transact2 = transaction.transaction) === null || _transaction$transact2 === void 0 ? void 0 : _transaction$transact2.maxPriorityFeePerGas));
4966
- }
5154
+ async _resubmitTx(txMeta, latestBlockNumber) {
5155
+ if (!txMeta.firstRetryBlockNumber) {
5156
+ this.emit(TX_EVENTS.TX_BLOCK_UPDATE, {
5157
+ txMeta,
5158
+ latestBlockNumber,
5159
+ txId: txMeta.id
5160
+ });
5161
+ }
5162
+ const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber;
5163
+ const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16);
5164
+ const retryCount = txMeta.retryCount || 0;
4967
5165
 
4968
- /**
4969
- * Determine if the maxFeePerGas and maxPriorityFeePerGas fields are not
4970
- * supplied and that the gasPrice field is valid if it is provided. This will
4971
- * return false if gasPrice is a non hex string.
4972
- * transaction -
4973
- * the transaction to check
4974
- * @returns true if transaction uses valid Legacy fields OR lacks
4975
- * EIP1559 fields
4976
- */
4977
- function isLegacyTransaction(transaction) {
4978
- return typeof transaction.transaction.maxFeePerGas === "undefined" && typeof transaction.transaction.maxPriorityFeePerGas === "undefined" && (typeof transaction.transaction.gasPrice === "undefined" || isHexString(addHexPrefix(transaction.transaction.gasPrice)));
4979
- }
5166
+ // Exponential backoff to limit retries at publishing (capped at last 15 mins)
5167
+ if (txBlockDistance <= Math.min(50, 2 ** retryCount)) return undefined;
4980
5168
 
4981
- /**
4982
- * Given two fields, ensure that the second field is not included in txParams,
4983
- * and if it is throw an invalidParams error.
4984
- */
4985
- function ensureMutuallyExclusiveFieldsNotProvided(txParams, fieldBeingValidated, mutuallyExclusiveField) {
4986
- if (typeof txParams[mutuallyExclusiveField] !== "undefined") {
4987
- throw rpcErrors.invalidParams(`Invalid transaction params: specified ${fieldBeingValidated} but also included ${mutuallyExclusiveField}, these cannot be mixed`);
4988
- }
4989
- }
5169
+ // Only auto-submit already-signed txs:
5170
+ if (!("rawTx" in txMeta)) return this.approveTransaction(txMeta.id);
5171
+ const {
5172
+ rawTx
5173
+ } = txMeta;
5174
+ const txHash = await this.publishTransaction(rawTx);
4990
5175
 
4991
- /**
4992
- * Ensures that the provided value for field is a string, throws an
4993
- * invalidParams error if field is not a string.
4994
- */
4995
- function ensureFieldIsString(txParams, field) {
4996
- if (typeof txParams[field] !== "string") {
4997
- throw rpcErrors.invalidParams(`Invalid transaction params: ${field} is not a string. got: (${txParams[field]})`);
5176
+ // Increment successful tries:
5177
+ this.emit(TX_EVENTS.TX_RETRY, {
5178
+ txMeta,
5179
+ txId: txMeta.id
5180
+ });
5181
+ return txHash;
4998
5182
  }
4999
- }
5183
+ async _checkPendingTx(foundTx) {
5184
+ const txMeta = foundTx;
5185
+ const txHash = txMeta.transactionHash;
5186
+ const txId = txMeta.id;
5000
5187
 
5001
- /**
5002
- * Ensures that the provided txParams has the proper 'type' specified for the
5003
- * given field, if it is provided. If types do not match throws an
5004
- * invalidParams error.
5005
- */
5006
- function ensureProperTransactionEnvelopeTypeProvided(txParams, field) {
5007
- switch (field) {
5008
- case "maxFeePerGas":
5009
- case "maxPriorityFeePerGas":
5010
- if (txParams.type && txParams.type !== TRANSACTION_ENVELOPE_TYPES.FEE_MARKET) {
5011
- throw rpcErrors.invalidParams(`Invalid transaction envelope type: specified type "${txParams.type}" but ` + `including maxFeePerGas and maxPriorityFeePerGas requires type: "${TRANSACTION_ENVELOPE_TYPES.FEE_MARKET}"`);
5012
- }
5013
- break;
5014
- case "gasPrice":
5015
- default:
5016
- if (txParams.type && txParams.type === TRANSACTION_ENVELOPE_TYPES.FEE_MARKET) {
5017
- throw rpcErrors.invalidParams(`Invalid transaction envelope type: specified type "${txParams.type}" but ` + "included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas");
5018
- }
5019
- }
5020
- }
5188
+ // Only check submitted txs
5189
+ if (txMeta.status !== TransactionStatus.submitted) return;
5021
5190
 
5022
- /**
5023
- * validates the from field in txParams
5024
- */
5025
- function validateFrom(txParams) {
5026
- if (!(typeof txParams.from === "string")) {
5027
- throw rpcErrors.invalidParams(`Invalid "from" address "${txParams.from}": not a string.`);
5028
- }
5029
- if (!isValidAddress(txParams.from)) {
5030
- throw rpcErrors.invalidParams('Invalid "from" address.');
5031
- }
5032
- }
5191
+ // extra check in case there was an uncaught error during the
5192
+ // signature and submission process
5193
+ if (!txHash) {
5194
+ const noTxHashError = new Error("We had an error while submitting this transaction, please try again.");
5195
+ noTxHashError.name = "NoTxHashError";
5196
+ this.emit(TX_EVENTS.TX_FAILED, {
5197
+ txId,
5198
+ error: noTxHashError
5199
+ });
5200
+ return;
5201
+ }
5033
5202
 
5034
- /**
5035
- * validates the to field in txParams
5036
- */
5037
- function validateRecipient(txParameters) {
5038
- if (txParameters.to === "0x" || txParameters.to === null) {
5039
- if (txParameters.data) {
5040
- delete txParameters.to;
5041
- } else {
5042
- throw rpcErrors.invalidParams('Invalid "to" address.');
5203
+ // If another tx with the same nonce is mined, set as failed.
5204
+ if (this._checkIfNonceIsTaken(txMeta)) {
5205
+ this.emit(TX_EVENTS.TX_DROPPED, {
5206
+ txId
5207
+ });
5208
+ return;
5209
+ }
5210
+ try {
5211
+ const transactionReceipt = await this.provider.request({
5212
+ method: METHOD_TYPES.ETH_GET_TRANSACTION_RECEIPT,
5213
+ params: [txHash]
5214
+ });
5215
+ if (transactionReceipt !== null && transactionReceipt !== void 0 && transactionReceipt.blockNumber) {
5216
+ const {
5217
+ baseFeePerGas,
5218
+ timestamp
5219
+ } = await this.provider.request({
5220
+ method: METHOD_TYPES.ETH_GET_BLOCK_BY_HASH,
5221
+ params: [transactionReceipt.blockHash, false]
5222
+ });
5223
+ this.emit(TX_EVENTS.TX_CONFIRMED, {
5224
+ txId,
5225
+ txReceipt: transactionReceipt,
5226
+ baseFeePerGas,
5227
+ blockTimestamp: timestamp
5228
+ });
5229
+ return;
5230
+ }
5231
+ } catch (error) {
5232
+ log.error("error while loading tx", error);
5233
+ txMeta.warning = {
5234
+ error: error.message,
5235
+ message: "There was a problem loading this transaction."
5236
+ };
5237
+ this.emit(TX_EVENTS.TX_WARNING, {
5238
+ txMeta
5239
+ });
5240
+ }
5241
+ if (await this._checkIfTxWasDropped(txMeta)) {
5242
+ this.emit(TX_EVENTS.TX_DROPPED, {
5243
+ txId
5244
+ });
5043
5245
  }
5044
- } else if (txParameters.to !== undefined && !isValidAddress(txParameters.to)) {
5045
- throw rpcErrors.invalidParams('Invalid "to" address.');
5046
5246
  }
5047
- return txParameters;
5048
- }
5049
-
5050
- /**
5051
- * Validates the given tx parameters
5052
- * @throws if the tx params contains invalid fields
5053
- */
5054
- function validateTxParameters(txParams) {
5055
- let eip1559Compatibility = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
5056
- if (!txParams || typeof txParams !== "object" || Array.isArray(txParams)) {
5057
- throw rpcErrors.invalidParams("Invalid transaction params: must be an object.");
5247
+ async _checkIfTxWasDropped(txMeta) {
5248
+ const {
5249
+ transactionHash: txHash,
5250
+ transaction: {
5251
+ nonce,
5252
+ from
5253
+ }
5254
+ } = txMeta;
5255
+ const networkNextNonce = await this.provider.request({
5256
+ method: METHOD_TYPES.ETH_GET_TRANSACTION_COUNT,
5257
+ params: [from, "latest"]
5258
+ });
5259
+ if (Number.parseInt(nonce, 16) >= Number.parseInt(networkNextNonce, 16)) {
5260
+ return false;
5261
+ }
5262
+ if (!this.droppedBlocksBufferByHash.has(txHash)) {
5263
+ this.droppedBlocksBufferByHash.set(txHash, 0);
5264
+ }
5265
+ const currentBlockBuffer = this.droppedBlocksBufferByHash.get(txHash);
5266
+ if (currentBlockBuffer < this.DROPPED_BUFFER_COUNT) {
5267
+ this.droppedBlocksBufferByHash.set(txHash, currentBlockBuffer + 1);
5268
+ return false;
5269
+ }
5270
+ this.droppedBlocksBufferByHash.delete(txHash);
5271
+ return true;
5058
5272
  }
5059
- if (!txParams.to && !txParams.data) {
5060
- throw rpcErrors.invalidParams('Invalid transaction params: must specify "data" for contract deployments, or "to" (and optionally "data") for all other types of transactions.');
5273
+ _checkIfNonceIsTaken(txMeta) {
5274
+ const address = txMeta.transaction.from;
5275
+ const completed = this.getConfirmedTransactions(address);
5276
+ return completed.some(otherMeta => {
5277
+ if (otherMeta.id === txMeta.id) {
5278
+ return false;
5279
+ }
5280
+ return otherMeta.transaction.nonce === txMeta.transaction.nonce;
5281
+ });
5061
5282
  }
5062
- if (isEIP1559Transaction({
5063
- transaction: txParams
5064
- }) && !eip1559Compatibility) {
5065
- throw rpcErrors.invalidParams("Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559");
5283
+ }
5284
+
5285
+ class TransactionGasUtil {
5286
+ constructor(provider, blockTracker) {
5287
+ _defineProperty(this, "provider", void 0);
5288
+ _defineProperty(this, "blockTracker", void 0);
5289
+ this.provider = provider;
5290
+ this.blockTracker = blockTracker;
5066
5291
  }
5067
- Object.entries(txParams).forEach(_ref => {
5068
- let [key, value] = _ref;
5069
- // validate types
5070
- switch (key) {
5071
- case "from":
5072
- validateFrom(txParams);
5073
- break;
5074
- case "to":
5075
- validateRecipient(txParams);
5076
- break;
5077
- case "gasPrice":
5078
- ensureProperTransactionEnvelopeTypeProvided(txParams, "gasPrice");
5079
- ensureMutuallyExclusiveFieldsNotProvided(txParams, "gasPrice", "maxFeePerGas");
5080
- ensureMutuallyExclusiveFieldsNotProvided(txParams, "gasPrice", "maxPriorityFeePerGas");
5081
- ensureFieldIsString(txParams, "gasPrice");
5082
- break;
5083
- case "maxFeePerGas":
5084
- ensureProperTransactionEnvelopeTypeProvided(txParams, "maxFeePerGas");
5085
- ensureMutuallyExclusiveFieldsNotProvided(txParams, "maxFeePerGas", "gasPrice");
5086
- ensureFieldIsString(txParams, "maxFeePerGas");
5087
- break;
5088
- case "maxPriorityFeePerGas":
5089
- ensureProperTransactionEnvelopeTypeProvided(txParams, "maxPriorityFeePerGas");
5090
- ensureMutuallyExclusiveFieldsNotProvided(txParams, "maxPriorityFeePerGas", "gasPrice");
5091
- ensureFieldIsString(txParams, "maxPriorityFeePerGas");
5092
- break;
5093
- case "value":
5094
- ensureFieldIsString(txParams, "value");
5095
- if (value.toString().includes("-")) {
5096
- throw rpcErrors.invalidParams(`Invalid transaction value "${value}": not a positive number.`);
5097
- }
5098
- if (value.toString().includes(".")) {
5099
- throw rpcErrors.invalidParams(`Invalid transaction value of "${value}": number must be in wei.`);
5100
- }
5101
- break;
5102
- case "chainId":
5103
- if (typeof value !== "number" && typeof value !== "string") {
5104
- throw rpcErrors.invalidParams(`Invalid transaction params: ${key} is not a Number or hex string. got: (${value})`);
5292
+ async analyzeGasUsage(txMeta) {
5293
+ const block = await this.blockTracker.getLatestBlock();
5294
+ // fallback to block gasLimit
5295
+ const blockGasLimitBN = new BN$1(stripHexPrefix(block.gasLimit), 16);
5296
+ const saferGasLimitBN = blockGasLimitBN.mul(new BN$1(19)).div(new BN$1(20));
5297
+ let estimatedGasHex = addHexPrefix(saferGasLimitBN.toString("hex"));
5298
+ let simulationFails;
5299
+ try {
5300
+ estimatedGasHex = await this.estimateTxGas(txMeta);
5301
+ } catch (error) {
5302
+ log.warn(error);
5303
+ simulationFails = {
5304
+ reason: error.message,
5305
+ errorKey: error.errorKey,
5306
+ debug: {
5307
+ blockNumber: block.idempotencyKey,
5308
+ blockGasLimit: block.gasLimit
5105
5309
  }
5106
- break;
5107
- default:
5108
- ensureFieldIsString(txParams, key);
5310
+ };
5109
5311
  }
5110
- });
5111
- }
5112
- function normalizeAndValidateTxParams(txParams) {
5113
- let lowerCase = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
5114
- const normalizedTxParams = normalizeTxParameters(txParams, lowerCase);
5115
- validateTxParameters(normalizedTxParams);
5116
- return normalizedTxParams;
5117
- }
5118
-
5119
- /**
5120
- * @returns an array of states that can be considered final
5121
- */
5122
- function getFinalStates() {
5123
- return [TransactionStatus.rejected,
5124
- // the user has responded no!
5125
- TransactionStatus.confirmed,
5126
- // the tx has been included in a block.
5127
- TransactionStatus.failed,
5128
- // the tx failed for some reason, included on tx data.
5129
- TransactionStatus.dropped // the tx nonce was already used
5130
- ];
5131
- }
5132
- function parseStandardTokenTransactionData(data) {
5133
- try {
5134
- const txDesc = erc20Interface.parseTransaction({
5135
- data
5136
- });
5137
- if (txDesc) return {
5138
- name: txDesc.name,
5139
- methodParams: txDesc.args.toArray(),
5140
- type: CONTRACT_TYPE_ERC20
5312
+ return {
5313
+ blockGasLimit: block.gasLimit,
5314
+ estimatedGasHex,
5315
+ simulationFails
5141
5316
  };
5142
- } catch {
5143
- // ignore and next try to parse with erc721 ABI
5144
5317
  }
5145
- try {
5146
- const txDesc = erc721Interface.parseTransaction({
5147
- data
5148
- });
5149
- if (txDesc) return {
5150
- name: txDesc.name,
5151
- methodParams: txDesc.args.toArray(),
5152
- type: CONTRACT_TYPE_ERC721
5153
- };
5154
- } catch {
5155
- // ignore and next try to parse with erc1155 ABI
5318
+
5319
+ /**
5320
+ Adds a gas buffer with out exceeding the block gas limit
5321
+ */
5322
+ addGasBuffer(initialGasLimitHex, blockGasLimitHex) {
5323
+ let multiplier = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1.5;
5324
+ const initialGasLimitBn = new BN$1(stripHexPrefix(initialGasLimitHex), 16);
5325
+ const blockGasLimitBn = new BN$1(stripHexPrefix(blockGasLimitHex), 16);
5326
+ const upperGasLimitBn = blockGasLimitBn.muln(0.9);
5327
+ const bufferedGasLimitBn = initialGasLimitBn.muln(multiplier);
5328
+
5329
+ // if initialGasLimit is above blockGasLimit, dont modify it
5330
+ if (initialGasLimitBn.gt(upperGasLimitBn)) return addHexPrefix(initialGasLimitBn.toString("hex"));
5331
+ // if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
5332
+ if (bufferedGasLimitBn.lt(upperGasLimitBn)) return addHexPrefix(bufferedGasLimitBn.toString("hex"));
5333
+ // otherwise use blockGasLimit
5334
+ return addHexPrefix(upperGasLimitBn.toString("hex"));
5156
5335
  }
5157
- try {
5158
- const txDesc = erc1155Interface.parseTransaction({
5159
- data
5336
+
5337
+ /**
5338
+ Estimates the tx's gas usage
5339
+ */
5340
+ async estimateTxGas(txMeta) {
5341
+ const txParams = cloneDeep(txMeta.transaction);
5342
+
5343
+ // `eth_estimateGas` can fail if the user has insufficient balance for the
5344
+ // value being sent, or for the gas cost. We don't want to check their
5345
+ // balance here, we just want the gas estimate. The gas price is removed
5346
+ // to skip those balance checks. We check balance elsewhere. We also delete
5347
+ // maxFeePerGas and maxPriorityFeePerGas to support EIP-1559 txs.
5348
+ delete txParams.gasPrice;
5349
+ delete txParams.maxFeePerGas;
5350
+ delete txParams.maxPriorityFeePerGas;
5351
+ return this.provider.request({
5352
+ method: "eth_estimateGas",
5353
+ params: [txParams]
5160
5354
  });
5161
- if (txDesc) return {
5162
- name: txDesc.name,
5163
- methodParams: txDesc.args.toArray(),
5164
- type: CONTRACT_TYPE_ERC1155
5165
- };
5166
- } catch {
5167
- // ignore and return undefined
5168
5355
  }
5169
- return undefined;
5170
5356
  }
5171
- const readAddressAsContract = async (provider, address) => {
5172
- let contractCode;
5173
- try {
5174
- contractCode = await provider.request({
5175
- method: METHOD_TYPES.ETH_GET_CODE,
5176
- params: [address, "latest"]
5177
- });
5178
- } catch (e) {
5179
- contractCode = null;
5180
- }
5181
- const isContractAddress = contractCode ? contractCode !== "0x" && contractCode !== "0x0" : false;
5182
- return {
5183
- contractCode,
5184
- isContractAddress
5185
- };
5186
- };
5187
- async function determineTransactionType(txParams, provider) {
5188
- const {
5189
- data,
5190
- to
5191
- } = txParams;
5192
- let name = "";
5193
- let methodParams = [];
5194
- let type = "";
5195
- try {
5196
- ({
5197
- name,
5198
- methodParams,
5199
- type
5200
- } = data && parseStandardTokenTransactionData(data) || {});
5201
- } catch (error) {
5202
- log.debug("Failed to parse transaction data", error);
5203
- }
5204
- let result;
5205
- let contractCode = "";
5206
- if (data && !to) {
5207
- result = TRANSACTION_TYPES.DEPLOY_CONTRACT;
5208
- } else {
5209
- const {
5210
- contractCode: resultCode,
5211
- isContractAddress
5212
- } = await readAddressAsContract(provider, to);
5213
- contractCode = resultCode;
5214
- if (isContractAddress) {
5215
- const valueExists = txParams.value && Number(txParams.value) !== 0;
5216
- const tokenMethodName = [TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, TRANSACTION_TYPES.COLLECTIBLE_METHOD_SAFE_TRANSFER_FROM, TRANSACTION_TYPES.SET_APPROVAL_FOR_ALL].find(x => {
5217
- var _name;
5218
- return x.toLowerCase() === ((_name = name) === null || _name === void 0 ? void 0 : _name.toLowerCase());
5219
- });
5220
- result = data && tokenMethodName && !valueExists ? tokenMethodName : TRANSACTION_TYPES.CONTRACT_INTERACTION;
5221
- } else {
5222
- result = TRANSACTION_TYPES.SENT_ETHER;
5357
+
5358
+ /**
5359
+ Generates an array of history objects sense the previous state.
5360
+ The object has the keys
5361
+ op (the operation performed),
5362
+ path (the key and if a nested object then each key will be seperated with a `/`)
5363
+ value
5364
+ with the first entry having the note and a timestamp when the change took place
5365
+ */
5366
+ function generateHistoryEntry(previousState, newState, note) {
5367
+ const entry = jsonDiffer.compare(previousState, newState);
5368
+ // Add a note to the first op, since it breaks if we append it to the entry
5369
+ if (entry[0]) {
5370
+ if (note) {
5371
+ entry[0].note = note;
5223
5372
  }
5373
+ entry[0].timestamp = Date.now();
5224
5374
  }
5225
- return {
5226
- type: type || CONTRACT_TYPE_ETH,
5227
- category: result,
5228
- methodParams,
5229
- getCodeResponse: contractCode
5230
- };
5375
+ return entry;
5376
+ }
5377
+
5378
+ /**
5379
+ Recovers previous txMeta state obj
5380
+ */
5381
+ function replayHistory(_shortHistory) {
5382
+ const shortHistory = cloneDeep(_shortHistory);
5383
+ return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument);
5384
+ }
5385
+ function snapshotFromTxMeta(txMeta) {
5386
+ const shallow = _objectSpread({}, txMeta);
5387
+ delete shallow.history;
5388
+ return cloneDeep(shallow);
5231
5389
  }
5232
5390
 
5233
5391
  class TransactionStateManager extends BaseTransactionStateManager {
@@ -6008,5 +6166,5 @@ class TransactionController extends TransactionStateManager {
6008
6166
  }
6009
6167
  }
6010
6168
 
6011
- export { ARBITRUM_MAINNET_CHAIN_ID, ARBITRUM_TESTNET_CHAIN_ID, AVALANCHE_MAINNET_CHAIN_ID, AVALANCHE_TESTNET_CHAIN_ID, AccountTrackerController, AddChainController, BASE_CHAIN_ID, BASE_TESTNET_CHAIN_ID, BSC_MAINNET_CHAIN_ID, BSC_TESTNET_CHAIN_ID, CELO_MAINNET_CHAIN_ID, CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP, COINGECKO_PLATFORMS_CHAIN_CODE_MAP, COINGECKO_SUPPORTED_CURRENCIES, CONTRACT_TYPE_ERC1155, CONTRACT_TYPE_ERC20, CONTRACT_TYPE_ERC721, CONTRACT_TYPE_ETH, CurrencyController, DEFAULT_CURRENCY, DecryptMessageController, ERC1155_INTERFACE_ID, ERC721_ENUMERABLE_INTERFACE_ID, ERC721_INTERFACE_ID, ERC721_METADATA_INTERFACE_ID, ETHERSCAN_SUPPORTED_CHAINS, EncryptionPublicKeyController, GAS_ESTIMATE_TYPES, GAS_LIMITS, GasFeeController, KeyringController, LOCALHOST, MAINNET_CHAIN_ID, MESSAGE_EVENTS, METHOD_TYPES, MessageController, MessageStatus, NetworkController, NftHandler, NftsController, NonceTracker, OLD_ERC721_LIST, OPTIMISM_MAINNET_CHAIN_ID, OPTIMISM_TESTNET_CHAIN_ID, POLYGON_CHAIN_ID, POLYGON_MUMBAI_CHAIN_ID, PendingTransactionTracker, PersonalMessageController, PollingBlockTracker, PreferencesController, SEPOLIA_CHAIN_ID, SIMPLEHASH_SUPPORTED_CHAINS, SUPPORTED_NETWORKS, SwitchChainController, TEST_CHAINS, TRANSACTION_ENVELOPE_TYPES, TokenHandler, TokenRatesController, TokensController, TransactionController, TransactionGasUtil, TransactionStateManager, TypedMessageController, XDAI_CHAIN_ID, bnLessThan, createChainIdMiddleware, createEthereumMiddleware, createGetAccountsMiddleware, createJsonRpcClient, createPendingNonceMiddleware, createPendingTxMiddleware, createProcessAddEthereumChain, createProcessDecryptMessageMiddleware, createProcessEncryptionPublicKeyMiddleware, createProcessEthSignMessage, createProcessPersonalMessage, createProcessSwitchEthereumChain, createProcessTransactionMiddleware, createProcessTypedMessage, createProcessTypedMessageV3, createProcessTypedMessageV4, createProviderConfigMiddleware, createRequestAccountsMiddleware, determineTransactionType, ensureFieldIsString, ensureMutuallyExclusiveFieldsNotProvided, erc1155Abi, erc20Abi, erc721Abi, formatDate, formatPastTx, formatTime, formatTxMetaForRpcResult, generateHistoryEntry, getChainType, getEthTxStatus, getEtherScanHashLink, getFinalStates, getIpfsEndpoint, idleTimeTracker, isAddressByChainId, isEIP1559Transaction, isLegacyTransaction, normalizeAndValidateTxParams, normalizeMessageData, normalizeTxParameters, parseDecryptMessageData, parseStandardTokenTransactionData, readAddressAsContract, replayHistory, sanitizeNftMetdataUrl, singleBalanceCheckerAbi, snapshotFromTxMeta, toChecksumAddressByChainId, transactionMatchesNetwork, validateAddChainData, validateAddress, validateDecryptedMessageData, validateEncryptionPublicKeyMessageData, validateFrom, validateRecipient, validateSignMessageData, validateSwitchChainData, validateTxParameters, validateTypedSignMessageDataV1, validateTypedSignMessageDataV3V4 };
6169
+ export { ARBITRUM_MAINNET_CHAIN_ID, ARBITRUM_TESTNET_CHAIN_ID, AVALANCHE_MAINNET_CHAIN_ID, AVALANCHE_TESTNET_CHAIN_ID, AccountTrackerController, AddChainController, BASE_CHAIN_ID, BASE_TESTNET_CHAIN_ID, BSC_MAINNET_CHAIN_ID, BSC_TESTNET_CHAIN_ID, CELO_MAINNET_CHAIN_ID, CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP, COINGECKO_PLATFORMS_CHAIN_CODE_MAP, COINGECKO_SUPPORTED_CURRENCIES, CONTRACT_TYPE_ERC1155, CONTRACT_TYPE_ERC20, CONTRACT_TYPE_ERC721, CONTRACT_TYPE_ETH, CurrencyController, DEFAULT_CURRENCY, DecryptMessageController, ERC1155_INTERFACE_ID, ERC721_ENUMERABLE_INTERFACE_ID, ERC721_INTERFACE_ID, ERC721_METADATA_INTERFACE_ID, ETHERSCAN_SUPPORTED_CHAINS, EncryptionPublicKeyController, GAS_ESTIMATE_TYPES, GAS_LIMITS, GasFeeController, KeyringController, LOCALHOST, MAINNET_CHAIN_ID, MESSAGE_EVENTS, METHOD_TYPES, MessageController, MessageStatus, NetworkController, NftHandler, NftsController, NonceTracker, OLD_ERC721_LIST, OPTIMISM_MAINNET_CHAIN_ID, OPTIMISM_TESTNET_CHAIN_ID, POLYGON_CHAIN_ID, POLYGON_MUMBAI_CHAIN_ID, PendingTransactionTracker, PersonalMessageController, PollingBlockTracker, PreferencesController, SEPOLIA_CHAIN_ID, SIMPLEHASH_SUPPORTED_CHAINS, SUPPORTED_NETWORKS, SwitchChainController, TEST_CHAINS, TRANSACTION_ENVELOPE_TYPES, TokenHandler, TokenRatesController, TokensController, TransactionController, TransactionGasUtil, TransactionStateManager, TypedMessageController, XDAI_CHAIN_ID, addCurrencies, addEtherscanTransactions, bnLessThan, conversionGTE, conversionGreaterThan, conversionLTE, conversionLessThan, conversionMax, conversionUtil, createChainIdMiddleware, createEthereumMiddleware, createGetAccountsMiddleware, createJsonRpcClient, createPendingNonceMiddleware, createPendingTxMiddleware, createProcessAddEthereumChain, createProcessDecryptMessageMiddleware, createProcessEncryptionPublicKeyMiddleware, createProcessEthSignMessage, createProcessPersonalMessage, createProcessSwitchEthereumChain, createProcessTransactionMiddleware, createProcessTypedMessage, createProcessTypedMessageV3, createProcessTypedMessageV4, createProviderConfigMiddleware, createRequestAccountsMiddleware, decGWEIToHexWEI, determineTransactionType, ensureFieldIsString, ensureMutuallyExclusiveFieldsNotProvided, erc1155Abi, erc20Abi, erc721Abi, formatDate, formatPastTx, formatTime, formatTxMetaForRpcResult, generateHistoryEntry, getBigNumber, getChainType, getEthTxStatus, getEtherScanHashLink, getFinalStates, getIpfsEndpoint, hexWEIToDecGWEI, idleTimeTracker, isAddressByChainId, isEIP1559Transaction, isLegacyTransaction, multiplyCurrencies, normalizeAndValidateTxParams, normalizeMessageData, normalizeTxParameters, parseDecryptMessageData, parseStandardTokenTransactionData, readAddressAsContract, replayHistory, sanitizeNftMetdataUrl, singleBalanceCheckerAbi, snapshotFromTxMeta, subtractCurrencies, toChecksumAddressByChainId, toNegative, transactionMatchesNetwork, validateAddChainData, validateAddress, validateDecryptedMessageData, validateEncryptionPublicKeyMessageData, validateFrom, validateRecipient, validateSignMessageData, validateSwitchChainData, validateTxParameters, validateTypedSignMessageDataV1, validateTypedSignMessageDataV3V4 };
6012
6170
  //# sourceMappingURL=ethereumControllers.esm.js.map