@exodus/ethereum-api 9.0.0-alpha.3 → 9.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2139 -0
- package/LICENSE +7 -0
- package/README.md +124 -0
- package/package.json +34 -33
- package/src/address-has-history.js +6 -2
- package/src/allowance/constants.js +4 -5
- package/src/allowance/index.js +159 -40
- package/src/check-tx/create-assess-transaction.js +23 -0
- package/src/check-tx/create-check-tx.js +104 -0
- package/src/check-tx/index.js +1 -0
- package/src/create-asset-plugin-factory.js +115 -0
- package/src/create-asset-utils.js +520 -0
- package/src/create-asset.js +273 -97
- package/src/create-token-factory.js +47 -17
- package/src/custom-fees.js +57 -0
- package/src/eip7702-whitelist.js +62 -0
- package/src/ens/index.js +12 -17
- package/src/error-wrapper.js +383 -0
- package/src/eth-like-util.js +172 -33
- package/src/exodus-eth-server/api-coin-nodes.js +27 -196
- package/src/exodus-eth-server/api.js +23 -14
- package/src/exodus-eth-server/clarity-v2.js +255 -0
- package/src/exodus-eth-server/clarity.js +67 -265
- package/src/exodus-eth-server/errors.js +40 -0
- package/src/exodus-eth-server/eth-like-server.js +385 -0
- package/src/exodus-eth-server/fetch-json.js +48 -0
- package/src/exodus-eth-server/index.js +77 -0
- package/src/exodus-eth-server/utils.js +31 -0
- package/src/exodus-eth-server/ws-gateway.js +306 -0
- package/src/exodus-eth-server/ws.js +1 -2
- package/src/fee-data/EthLikeFeeData.js +81 -0
- package/src/fee-data/index.js +54 -0
- package/src/fee-utils.js +229 -0
- package/src/gas-estimation.js +183 -66
- package/src/get-balance-for-address.js +1 -1
- package/src/get-balances.js +285 -27
- package/src/get-fee.js +92 -56
- package/src/get-historical-balance.js +58 -0
- package/src/hooks/index.js +1 -1
- package/src/hooks/monitor.js +182 -43
- package/src/hooks/staking-freshness.js +58 -0
- package/src/index.js +118 -12
- package/src/move-funds.js +153 -0
- package/src/multicall3/index.js +212 -0
- package/src/multisig-address.js +146 -0
- package/src/nft-utils.js +60 -0
- package/src/number-utils.js +34 -2
- package/src/optimism-gas/index.js +13 -5
- package/src/send-validations.js +58 -0
- package/src/server-based-fee-monitor.js +12 -19
- package/src/simulate-tx/fetch-tx-preview.js +0 -2
- package/src/simulate-tx/index.js +2 -2
- package/src/simulate-tx/simulate-eth-tx.js +24 -11
- package/src/simulation/common.js +34 -0
- package/src/simulation/create-simulate-message.js +49 -0
- package/src/simulation/create-simulate-transactions.js +108 -0
- package/src/simulation/estimate-fee.js +14 -0
- package/src/simulation/estimate-simple-transfer.js +15 -0
- package/src/simulation/get-message-type.js +18 -0
- package/src/simulation/simulate-message-api.js +68 -0
- package/src/simulation/simulate-transactions-api.js +266 -0
- package/src/simulation/simulate-transactions.js +16 -0
- package/src/simulation/transactions.js +52 -0
- package/src/simulation/try-estimating-changes-locally.js +26 -0
- package/src/staking/api/index.js +43 -0
- package/src/staking/cached-read.js +33 -0
- package/src/staking/ethereum/api.js +166 -34
- package/src/staking/ethereum/deps.js +37 -0
- package/src/staking/ethereum/everstake.js +113 -0
- package/src/staking/ethereum/index.js +4 -3
- package/src/staking/ethereum/service.js +682 -181
- package/src/staking/ethereum/staking-utils.js +275 -4
- package/src/staking/fantom-staking.js +9 -9
- package/src/staking/index.js +4 -4
- package/src/staking/matic/addresses.js +2 -0
- package/src/staking/matic/api.js +69 -49
- package/src/staking/matic/index.js +3 -3
- package/src/staking/matic/matic-staking-utils.js +243 -5
- package/src/staking/matic/matic-staking.md +53 -0
- package/src/staking/staking-provider-client.js +1 -1
- package/src/staking/utils/index.js +39 -0
- package/src/tx-create.js +666 -0
- package/src/tx-log/clarity-monitor.js +276 -103
- package/src/tx-log/clarity-truncated-history-monitor.js +130 -0
- package/src/tx-log/clarity-truncated-history-monitor.md +219 -0
- package/src/tx-log/clarity-utils/absolute.js +96 -0
- package/src/tx-log/clarity-utils/get-log-items-from-server-tx.js +94 -31
- package/src/tx-log/clarity-utils/get-names-of-tokens-transferred-by-server-tx.js +3 -2
- package/src/tx-log/clarity-utils/index.js +9 -3
- package/src/tx-log/clarity-utils/is-spam-pending-tx.js +13 -0
- package/src/tx-log/clarity-utils/normalize-transactions-response.js +65 -0
- package/src/tx-log/ethereum-monitor.js +93 -47
- package/src/tx-log/ethereum-no-history-monitor.js +52 -41
- package/src/tx-log/get-optimistic-txlog-effects.js +379 -0
- package/src/tx-log/index.js +5 -3
- package/src/tx-log/monitor-utils/check-pending-transactions.js +14 -5
- package/src/tx-log/monitor-utils/exclude-unchanged-token-balances.js +1 -1
- package/src/tx-log/monitor-utils/extract-balance-from-clarity-account-info.js +108 -0
- package/src/tx-log/monitor-utils/get-balance-updates.js +44 -0
- package/src/tx-log/monitor-utils/get-batched-rpc-balances.js +47 -0
- package/src/tx-log/monitor-utils/get-current-blacklist-status.js +38 -0
- package/src/tx-log/monitor-utils/get-current-eip7702-delegation.js +79 -0
- package/src/tx-log/monitor-utils/get-derive-transactions-to-check.js +10 -5
- package/src/tx-log/monitor-utils/get-fee-amount.js +1 -1
- package/src/tx-log/monitor-utils/get-history-from-server.js +1 -1
- package/src/tx-log/monitor-utils/get-log-items-from-server-tx.js +75 -33
- package/src/tx-log/monitor-utils/get-names-of-tokens-transferred-by-server-tx.js +3 -2
- package/src/tx-log/monitor-utils/get-raw-transfer-amount.js +29 -0
- package/src/tx-log/monitor-utils/get-transfers-by-token-name.js +3 -2
- package/src/tx-log/monitor-utils/get-value-of-transfers.js +10 -12
- package/src/tx-log/monitor-utils/index.js +12 -7
- package/src/tx-log/monitor-utils/is-unverifiable-pending-tx-expired.js +19 -0
- package/src/tx-log/monitor-utils/verify-pending-tx-status-rpc.js +90 -0
- package/src/tx-log-staking-processor/asset-staking-tx-data.js +82 -24
- package/src/tx-log-staking-processor/index.js +28 -14
- package/src/tx-log-staking-processor/utils.js +21 -20
- package/src/tx-send/broadcast-error-handler.js +63 -0
- package/src/tx-send/index.js +1 -2
- package/src/tx-send/nonce-utils.js +97 -0
- package/src/tx-send/tx-send.js +94 -288
- package/src/tx-sign/index.js +17 -0
- package/src/tx-type/index.js +280 -0
- package/src/watch-tx.js +36 -0
- package/src/web3/createSimulateMessage.js +3 -2
- package/src/web3/createSimulateTransactions.js +11 -0
- package/src/web3/index.js +3 -1
- package/src/etherscan/account.js +0 -49
- package/src/etherscan/index.js +0 -28
- package/src/etherscan/logs.js +0 -16
- package/src/etherscan/proxy.js +0 -47
- package/src/etherscan/request.js +0 -26
- package/src/etherscan/ws.js +0 -88
- package/src/exodus-eth-server/server-factory.js +0 -28
- package/src/exodus-eth-server/server-map.js +0 -21
- package/src/get-fee-async.js +0 -97
- package/src/staking/matic/service.js +0 -437
- package/src/staking-api.js +0 -5
- package/src/tx-log/clarity-utils/get-derive-data-needed-for-tick.js +0 -12
- package/src/tx-log-staking-processor/get-asset-tx-amount.js +0 -26
- package/src/tx-send/get-fee-info.js +0 -41
- package/src/websocket/index.android.js +0 -2
- package/src/websocket/index.ios.js +0 -2
- package/src/websocket/index.js +0 -21
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseMonitor } from '@exodus/asset-lib'
|
|
2
|
+
import { getAssetAddresses } from '@exodus/ethereum-lib'
|
|
3
|
+
import lodash from 'lodash'
|
|
2
4
|
|
|
5
|
+
import { executeEthLikeFeeMonitorUpdate } from '../fee-utils.js'
|
|
6
|
+
import { fromHexToString } from '../number-utils.js'
|
|
3
7
|
import {
|
|
4
8
|
checkPendingTransactions,
|
|
9
|
+
excludeUnchangedTokenBalances,
|
|
5
10
|
getAllLogItemsByAsset,
|
|
6
|
-
getLogItemsFromServerTx,
|
|
7
11
|
getDeriveDataNeededForTick,
|
|
8
12
|
getDeriveTransactionsToCheck,
|
|
9
13
|
getHistoryFromServer,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
getLogItemsFromServerTx,
|
|
15
|
+
isUnverifiablePendingTxExpired,
|
|
16
|
+
verifyRpcPendingTxStatusBatch,
|
|
17
|
+
} from './monitor-utils/index.js'
|
|
13
18
|
import {
|
|
14
19
|
enableWSUpdates,
|
|
15
20
|
subscribeToGasPriceNotifications,
|
|
16
21
|
subscribeToWSNotifications,
|
|
17
|
-
} from './ws-updates'
|
|
18
|
-
|
|
19
|
-
import { isEmpty } from 'lodash'
|
|
22
|
+
} from './ws-updates.js'
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
import { fromHexToString } from '../number-utils'
|
|
24
|
+
const { isEmpty } = lodash
|
|
23
25
|
|
|
24
26
|
// The base ethereum monitor class handles listening for, fetching,
|
|
25
27
|
// formatting, and populating-to-state all ETH/ETC/ERC20 transactions.
|
|
26
28
|
|
|
27
29
|
export class EthereumMonitor extends BaseMonitor {
|
|
28
|
-
constructor({ server, config, ...args }) {
|
|
30
|
+
constructor({ server, config, rpcBalanceAssetNames, webSocketEnabled = true, ...args }) {
|
|
29
31
|
super(args)
|
|
30
32
|
this.server = server
|
|
31
33
|
this.config = { GAS_PRICE_FROM_WEBSOCKET: true, ...config }
|
|
@@ -37,6 +39,8 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
37
39
|
this.addHook('before-start', (...args) => this.beforeStart(...args))
|
|
38
40
|
this.addHook('after-stop', (...args) => this.afterStop(...args))
|
|
39
41
|
this.subscribedToGasPriceMap = new Map()
|
|
42
|
+
this._webSocketEnabled = webSocketEnabled
|
|
43
|
+
this.rpcBalanceAssetNames = rpcBalanceAssetNames
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
setServer(config = {}) {
|
|
@@ -71,16 +75,21 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
71
75
|
|
|
72
76
|
// eslint-disable-next-line no-undef
|
|
73
77
|
async checkPendingTransactions(params) {
|
|
74
|
-
const {
|
|
75
|
-
|
|
78
|
+
const {
|
|
79
|
+
pendingTransactionsToCheck,
|
|
80
|
+
pendingTransactionsGroupedByAddressAndNonce,
|
|
81
|
+
recentPendingTransactions,
|
|
82
|
+
} = checkPendingTransactions(params)
|
|
76
83
|
const txsToRemove = []
|
|
77
84
|
const {
|
|
78
85
|
assetSource: { walletAccount },
|
|
79
86
|
} = params
|
|
80
87
|
|
|
81
|
-
const updateTx = (tx, asset, { isTxConfirmed, remove }) => {
|
|
88
|
+
const updateTx = (tx, asset, { isTxConfirmed, remove, preserve }) => {
|
|
82
89
|
if (remove) {
|
|
83
90
|
txsToRemove.push({ tx, assetSource: { asset, walletAccount } })
|
|
91
|
+
} else if (preserve) {
|
|
92
|
+
params.logItemsByAsset[asset].push(tx)
|
|
84
93
|
} else {
|
|
85
94
|
params.logItemsByAsset[asset].push({
|
|
86
95
|
...tx,
|
|
@@ -97,10 +106,15 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
97
106
|
assetSource: { asset: assetName, walletAccount },
|
|
98
107
|
})
|
|
99
108
|
} else if (tokenTxSet && tokenTxSet.has(tx.txId)) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
// Optional: the stored tx may name a token since removed from the registry
|
|
110
|
+
params.logItemsByAsset[assetName]?.push(
|
|
111
|
+
preserve
|
|
112
|
+
? tokenTxSet.get(tx.txId)
|
|
113
|
+
: {
|
|
114
|
+
...tokenTxSet.get(tx.txId),
|
|
115
|
+
...(isTxConfirmed ? { confirmations: 1 } : { dropped: true, error: 'Dropped' }),
|
|
116
|
+
}
|
|
117
|
+
)
|
|
104
118
|
}
|
|
105
119
|
})
|
|
106
120
|
|
|
@@ -113,20 +127,50 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
113
127
|
if (replaced) {
|
|
114
128
|
await updateTx(tx, assetName, { remove: true })
|
|
115
129
|
delete pendingTransactionsToCheck[tx.txId]
|
|
130
|
+
delete recentPendingTransactions[tx.txId]
|
|
116
131
|
}
|
|
117
132
|
}
|
|
118
133
|
|
|
134
|
+
// Batch verify all pending txs with a single RPC call
|
|
135
|
+
const txIds = Object.keys(pendingTransactionsToCheck)
|
|
136
|
+
const statuses = await verifyRpcPendingTxStatusBatch({
|
|
137
|
+
server: this.server,
|
|
138
|
+
logger: this.logger,
|
|
139
|
+
txIds,
|
|
140
|
+
})
|
|
141
|
+
|
|
119
142
|
for (const { tx, assetName } of Object.values(pendingTransactionsToCheck)) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
const txStatus = statuses[tx.txId]
|
|
144
|
+
if (txStatus?.status === 'confirmed') {
|
|
145
|
+
await updateTx(tx, assetName, { isTxConfirmed: true })
|
|
146
|
+
} else if (txStatus?.status === 'dropped') {
|
|
147
|
+
if (params.refresh) {
|
|
148
|
+
await updateTx(tx, assetName, { remove: true })
|
|
149
|
+
} else {
|
|
150
|
+
await updateTx(tx, assetName, { isTxConfirmed: false })
|
|
151
|
+
}
|
|
152
|
+
} else if (params.refresh) {
|
|
153
|
+
// No txStatus at all means the whole verification batch failed - evidence
|
|
154
|
+
// about nothing, so preserve unconditionally; the age ceiling only applies
|
|
155
|
+
// to a per-entry 'unknown' verdict (evidence about that one hash).
|
|
156
|
+
if (!txStatus || txStatus.status === 'pending' || !isUnverifiablePendingTxExpired(tx)) {
|
|
157
|
+
// Still in the mempool (or not verifiable yet) but missing from the history
|
|
158
|
+
// server response: re-add it so the refresh overwrite cannot wipe it and
|
|
159
|
+
// break tx-log-based nonce resolution.
|
|
160
|
+
await updateTx(tx, assetName, { preserve: true })
|
|
161
|
+
} else {
|
|
162
|
+
// Unverifiable past the age ceiling: purge so a hash the node keeps
|
|
163
|
+
// erroring on cannot pin nonce derivation forever.
|
|
164
|
+
await updateTx(tx, assetName, { remove: true })
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// non-refresh: 'pending' or unknown - tx still in mempool, do nothing
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (params.refresh) {
|
|
171
|
+
// Too recent for a reliable on-chain verdict - always survive the refresh overwrite
|
|
172
|
+
for (const { tx, assetName } of Object.values(recentPendingTransactions)) {
|
|
173
|
+
await updateTx(tx, assetName, { preserve: true })
|
|
130
174
|
}
|
|
131
175
|
}
|
|
132
176
|
|
|
@@ -154,7 +198,7 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
154
198
|
server: this.server,
|
|
155
199
|
ourWalletAddress: derivedData.ourWalletAddress,
|
|
156
200
|
minimumConfirmations: derivedData.minimumConfirmations,
|
|
157
|
-
index: refresh ? 0 : derivedIndex ?? 0,
|
|
201
|
+
index: refresh ? 0 : (derivedIndex ?? 0),
|
|
158
202
|
})
|
|
159
203
|
const hasNewIndex = !derivedIndex || index > derivedIndex
|
|
160
204
|
|
|
@@ -188,7 +232,7 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
188
232
|
await this.updateTxLogByAsset({ logItemsByAsset, walletAccount, refresh })
|
|
189
233
|
|
|
190
234
|
if (refresh || hasNewIndex) {
|
|
191
|
-
const unknownTokenAddresses = this.
|
|
235
|
+
const unknownTokenAddresses = this.getUnknownTokenAddresses({
|
|
192
236
|
transactions: allTransactionsFromServer,
|
|
193
237
|
tokensByAddress,
|
|
194
238
|
})
|
|
@@ -198,19 +242,13 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
198
242
|
}
|
|
199
243
|
}
|
|
200
244
|
|
|
201
|
-
async updateGasPrice(
|
|
245
|
+
async updateGasPrice(fetchedGasPrices) {
|
|
202
246
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
this.logger.debug(
|
|
211
|
-
`Update ${this.asset.name} gas price: ${feeConfig.gasPrice}, baseFeePerGas: ${feeConfig.baseFeePerGas}`
|
|
212
|
-
)
|
|
213
|
-
await this.aci.updateFeeConfig({ assetName: this.asset.name, feeConfig })
|
|
247
|
+
await executeEthLikeFeeMonitorUpdate({
|
|
248
|
+
assetClientInterface: this.aci,
|
|
249
|
+
feeAsset: this.asset,
|
|
250
|
+
fetchedGasPrices,
|
|
251
|
+
})
|
|
214
252
|
} catch (e) {
|
|
215
253
|
this.logger.warn('error updating gasPrice', e)
|
|
216
254
|
}
|
|
@@ -218,9 +256,9 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
218
256
|
|
|
219
257
|
async getNewAccountState({ tokens, currentTokenBalances, ourWalletAddress }) {
|
|
220
258
|
const asset = this.asset
|
|
221
|
-
const newAccountState =
|
|
259
|
+
const newAccountState = Object.create(null)
|
|
222
260
|
const server = this.server
|
|
223
|
-
if (
|
|
261
|
+
if (this.rpcBalanceAssetNames.includes(asset.name)) {
|
|
224
262
|
const result = await server.getBalanceProxied(ourWalletAddress)
|
|
225
263
|
const balance = fromHexToString(result)
|
|
226
264
|
newAccountState.balance = asset.currency.baseUnit(balance)
|
|
@@ -228,7 +266,7 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
228
266
|
|
|
229
267
|
const tokenBalancePairs = await Promise.all(
|
|
230
268
|
tokens
|
|
231
|
-
.filter((token) =>
|
|
269
|
+
.filter((token) => this.rpcBalanceAssetNames.includes(token.name) && token.contract.address)
|
|
232
270
|
.map(async (token) => {
|
|
233
271
|
const { confirmed } = await server.balanceOf(ourWalletAddress, token.contract.address)
|
|
234
272
|
const value = token.currency.baseUnit(confirmed[token.contract.address] || 0)
|
|
@@ -243,7 +281,7 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
243
281
|
|
|
244
282
|
async getReceiveAddressesByWalletAccount() {
|
|
245
283
|
const walletAccounts = await this.aci.getWalletAccounts({ assetName: this.asset.name })
|
|
246
|
-
const addressesByAccount =
|
|
284
|
+
const addressesByAccount = Object.create(null)
|
|
247
285
|
for (const walletAccount of walletAccounts) {
|
|
248
286
|
addressesByAccount[walletAccount] = await this.aci.getReceiveAddresses({
|
|
249
287
|
assetName: this.asset.name,
|
|
@@ -256,6 +294,8 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
256
294
|
}
|
|
257
295
|
|
|
258
296
|
async _subscribeToWSNotifications() {
|
|
297
|
+
if (this._webSocketEnabled === false) return
|
|
298
|
+
|
|
259
299
|
const addressesByWalletAccount = await this.getReceiveAddressesByWalletAccount()
|
|
260
300
|
subscribeToWSNotifications({
|
|
261
301
|
addressesByWalletAccount,
|
|
@@ -264,7 +304,7 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
264
304
|
})
|
|
265
305
|
}
|
|
266
306
|
|
|
267
|
-
|
|
307
|
+
getUnknownTokenAddresses({ transactions, tokensByAddress }) {
|
|
268
308
|
const set = transactions.reduce((acc, txn) => {
|
|
269
309
|
const transfers = txn.erc20 || []
|
|
270
310
|
transfers.forEach((transfer) => {
|
|
@@ -279,6 +319,12 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
279
319
|
}
|
|
280
320
|
|
|
281
321
|
async beforeStart() {
|
|
322
|
+
await this._enableWSUpdates()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async _enableWSUpdates() {
|
|
326
|
+
if (this._webSocketEnabled === false) return
|
|
327
|
+
|
|
282
328
|
const addressesByWalletAccount = await this.getReceiveAddressesByWalletAccount()
|
|
283
329
|
enableWSUpdates({
|
|
284
330
|
interval: this.interval,
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
+
import { BaseMonitor } from '@exodus/asset-lib'
|
|
2
|
+
import { SynchronizedTime } from '@exodus/basic-utils'
|
|
1
3
|
import { Tx } from '@exodus/models'
|
|
4
|
+
import lodash from 'lodash'
|
|
2
5
|
|
|
6
|
+
import { applyRemoteConfigEip7702Whitelist, createEip7702Whitelist } from '../eip7702-whitelist.js'
|
|
7
|
+
import { getBatchedRpcBalances } from './monitor-utils/get-batched-rpc-balances.js'
|
|
8
|
+
import { UNCONFIRMED_TX_LIMIT } from './monitor-utils/get-derive-transactions-to-check.js'
|
|
3
9
|
import {
|
|
10
|
+
excludeUnchangedTokenBalances,
|
|
11
|
+
getCurrentBlackListStatus,
|
|
12
|
+
getCurrentEIP7702Delegation,
|
|
4
13
|
getDeriveDataNeededForTick,
|
|
5
14
|
getDeriveTransactionsToCheck,
|
|
6
|
-
|
|
7
|
-
} from './monitor-utils'
|
|
8
|
-
|
|
9
|
-
import { isEmpty, unionBy, zipObject } from 'lodash'
|
|
15
|
+
} from './monitor-utils/index.js'
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import { UNCONFIRMED_TX_LIMIT } from './monitor-utils/get-derive-transactions-to-check'
|
|
14
|
-
import { fromHexToString } from '../number-utils'
|
|
15
|
-
import { SynchronizedTime } from '@exodus/basic-utils'
|
|
17
|
+
const { isEmpty, unionBy, zipObject } = lodash
|
|
16
18
|
|
|
17
19
|
// The base ethereum monitor no history class handles listening for assets with no history
|
|
18
20
|
|
|
19
21
|
export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
20
|
-
constructor({ server, config, ...args }) {
|
|
22
|
+
constructor({ server, config, eip7702Supported, eip7702Whitelist, getBlackListStatus, ...args }) {
|
|
21
23
|
super(args)
|
|
22
24
|
this.server = server
|
|
23
25
|
this.config = { ...config }
|
|
26
|
+
this.eip7702Whitelist = eip7702Whitelist ?? createEip7702Whitelist(eip7702Supported)
|
|
27
|
+
this.getBlackListStatus = getBlackListStatus
|
|
24
28
|
this.deriveDataNeededForTick = getDeriveDataNeededForTick(this.aci)
|
|
25
29
|
this.deriveTransactionsToCheck = getDeriveTransactionsToCheck({
|
|
26
30
|
getTxLog: (...args) => this.aci.getTxLog(...args),
|
|
@@ -28,6 +32,12 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
setServer(config) {
|
|
35
|
+
applyRemoteConfigEip7702Whitelist({
|
|
36
|
+
eip7702Whitelist: this.eip7702Whitelist,
|
|
37
|
+
remoteConfig: config,
|
|
38
|
+
logger: this.logger,
|
|
39
|
+
})
|
|
40
|
+
|
|
31
41
|
const uri = config?.server || this.server.defaultUri
|
|
32
42
|
|
|
33
43
|
if (uri === this.server.uri) {
|
|
@@ -38,33 +48,12 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
async getBalances({ tokens, ourWalletAddress }) {
|
|
41
|
-
|
|
42
|
-
const request = this.server.getBalanceRequest(ourWalletAddress)
|
|
43
|
-
batch[this.asset.name] = request
|
|
44
|
-
for (const token of tokens) {
|
|
45
|
-
const request = this.server.balanceOfRequest(ourWalletAddress, token.contract.address)
|
|
46
|
-
batch[token.name] = request
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const pairs = Object.entries(batch)
|
|
50
|
-
if (pairs.length === 0) {
|
|
51
|
-
return {}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const requests = pairs.map((pair) => pair[1])
|
|
55
|
-
const responses = await this.server.sendBatchRequest(requests)
|
|
56
|
-
const entries = pairs.map((pair, idx) => {
|
|
57
|
-
const balanceHex = responses[idx]
|
|
58
|
-
const name = pair[0]
|
|
59
|
-
const balance = fromHexToString(balanceHex)
|
|
60
|
-
return [name, balance]
|
|
61
|
-
})
|
|
62
|
-
return Object.fromEntries(entries)
|
|
51
|
+
return getBatchedRpcBalances({ baseAsset: this.asset, ourWalletAddress, tokens })
|
|
63
52
|
}
|
|
64
53
|
|
|
65
54
|
async getNewAccountState({ tokens, currentTokenBalances, ourWalletAddress }) {
|
|
66
55
|
const asset = this.asset
|
|
67
|
-
const newAccountState =
|
|
56
|
+
const newAccountState = Object.create(null)
|
|
68
57
|
const balances = await this.getBalances({ tokens, ourWalletAddress })
|
|
69
58
|
const balance = balances[asset.name]
|
|
70
59
|
newAccountState.balance = asset.currency.baseUnit(balance)
|
|
@@ -72,6 +61,7 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
|
72
61
|
const tokenBalanceEntries = tokenBalancePairs
|
|
73
62
|
.map((pair) => {
|
|
74
63
|
const token = tokens.find((token) => token.name === pair[0])
|
|
64
|
+
if (!token) return null
|
|
75
65
|
const value = token.currency.baseUnit(pair[1] || 0)
|
|
76
66
|
return [token.name, value]
|
|
77
67
|
})
|
|
@@ -129,20 +119,17 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
|
129
119
|
const txsToRemove = []
|
|
130
120
|
const now = SynchronizedTime.now()
|
|
131
121
|
|
|
132
|
-
if (isEmpty(pendingTransactions))
|
|
133
|
-
return {
|
|
134
|
-
txsToUpdate,
|
|
135
|
-
txsToRemove,
|
|
136
|
-
}
|
|
122
|
+
if (isEmpty(pendingTransactions)) return { txsToUpdate, txsToRemove }
|
|
137
123
|
|
|
138
124
|
for (const { tx, assetName } of pendingTransactions) {
|
|
139
125
|
const txFromNode = pendingTxsFromNode[tx.txId]
|
|
140
|
-
|
|
126
|
+
const isConfirmed = Boolean(txFromNode?.blockHash)
|
|
127
|
+
if (now - tx.date.getTime() > UNCONFIRMED_TX_LIMIT && !txFromNode) {
|
|
141
128
|
txsToRemove.push({
|
|
142
129
|
tx,
|
|
143
130
|
assetSource: { asset: assetName, walletAccount },
|
|
144
131
|
})
|
|
145
|
-
} else if (
|
|
132
|
+
} else if (isConfirmed) {
|
|
146
133
|
txsToUpdate.push({
|
|
147
134
|
tx: { ...tx, confirmations: 1 },
|
|
148
135
|
assetSource: { asset: assetName, walletAccount },
|
|
@@ -157,6 +144,7 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
|
157
144
|
}
|
|
158
145
|
|
|
159
146
|
async tick({ refresh, walletAccount }) {
|
|
147
|
+
const shouldCheckBlacklist = this.tickCount[walletAccount] === 0
|
|
160
148
|
const assets = await this.aci.getAssetsForNetwork({ baseAssetName: this.asset.name })
|
|
161
149
|
const tokens = Object.values(assets).filter((asset) => asset.baseAsset.name !== asset.name)
|
|
162
150
|
|
|
@@ -189,7 +177,30 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
|
189
177
|
ourWalletAddress,
|
|
190
178
|
})
|
|
191
179
|
|
|
192
|
-
await
|
|
180
|
+
const eip7702Delegation = await getCurrentEIP7702Delegation({
|
|
181
|
+
server: this.server,
|
|
182
|
+
address: ourWalletAddress,
|
|
183
|
+
eip7702Supported: this.eip7702Whitelist.get(),
|
|
184
|
+
currentDelegation: currentAccountState?.eip7702Delegation,
|
|
185
|
+
logger: this.logger,
|
|
186
|
+
})
|
|
187
|
+
const isBlacklisted = shouldCheckBlacklist
|
|
188
|
+
? await getCurrentBlackListStatus({
|
|
189
|
+
getBlackListStatus: this.getBlackListStatus,
|
|
190
|
+
address: ourWalletAddress,
|
|
191
|
+
currentIsBlacklisted: currentAccountState?.isBlacklisted,
|
|
192
|
+
logger: this.logger,
|
|
193
|
+
})
|
|
194
|
+
: undefined
|
|
195
|
+
|
|
196
|
+
// All updates must go through newData (accountState param is only used for mem merging)
|
|
197
|
+
const newData = {
|
|
198
|
+
...accountState,
|
|
199
|
+
...(isBlacklisted !== undefined && { isBlacklisted }),
|
|
200
|
+
...(eip7702Delegation !== undefined && { eip7702Delegation }),
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
await this.updateAccountState({ accountState, newData, walletAccount })
|
|
193
204
|
|
|
194
205
|
await this.removeFromTxLog(txsToRemove)
|
|
195
206
|
await this.updateTxLogByAsset({ logItemsByAsset, walletAccount, refresh })
|