@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.
- package/dist/ethereumControllers.cjs.js +508 -433
- package/dist/ethereumControllers.cjs.js.map +1 -1
- package/dist/ethereumControllers.esm.js +1219 -1061
- package/dist/ethereumControllers.esm.js.map +1 -1
- package/dist/ethereumControllers.umd.min.js +1 -1
- package/dist/ethereumControllers.umd.min.js.map +1 -1
- package/dist/types/Preferences/PreferencesController.d.ts +2 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/utils/helpers.d.ts +2 -1
- package/dist/types/utils/interfaces.d.ts +34 -0
- package/package.json +2 -2
- package/src/Preferences/PreferencesController.ts +16 -3
- package/src/index.ts +1 -0
- package/src/utils/constants.ts +3 -3
- package/src/utils/helpers.ts +59 -4
- package/src/utils/interfaces.ts +35 -0
|
@@ -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,
|
|
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,
|
|
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,
|
|
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.
|
|
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: "
|
|
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.
|
|
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
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
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
|
-
*
|
|
1045
|
+
* normalizes txParams
|
|
1066
1046
|
*/
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
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
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|
-
|
|
1111
|
-
|
|
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
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
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
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
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
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
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
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
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
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
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
|
-
|
|
1216
|
-
|
|
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
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1332
|
-
|
|
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
|
-
*
|
|
1163
|
+
* Validates the given tx parameters
|
|
1164
|
+
* @throws if the tx params contains invalid fields
|
|
1366
1165
|
*/
|
|
1367
|
-
|
|
1368
|
-
let
|
|
1369
|
-
|
|
1370
|
-
|
|
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 (
|
|
1405
|
-
|
|
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
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
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
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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
|
-
|
|
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
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
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: [
|
|
5021
|
+
params: [address, block.idempotencyKey]
|
|
4773
5022
|
});
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
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
|
-
|
|
4789
|
-
const
|
|
4790
|
-
const
|
|
4791
|
-
return
|
|
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
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
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
|
-
|
|
4808
|
-
const
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
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
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
5061
|
+
name: "local",
|
|
5062
|
+
nonce: highest,
|
|
5063
|
+
details: {
|
|
5064
|
+
startPoint,
|
|
5065
|
+
highest
|
|
5066
|
+
}
|
|
4831
5067
|
};
|
|
4832
5068
|
}
|
|
5069
|
+
}
|
|
4833
5070
|
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
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
|
-
|
|
5100
|
+
checks the network for signed txs and releases the nonce global lock if it is
|
|
4854
5101
|
*/
|
|
4855
|
-
async
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
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
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
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
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
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
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
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
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
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
|
-
|
|
5060
|
-
|
|
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
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
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
|
-
|
|
5068
|
-
|
|
5069
|
-
//
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
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
|
-
|
|
5107
|
-
default:
|
|
5108
|
-
ensureFieldIsString(txParams, key);
|
|
5310
|
+
};
|
|
5109
5311
|
}
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
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
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
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
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
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
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
const
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
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
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
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
|