@exodus/ethereum-api 9.0.0-alpha.2 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +2121 -0
  2. package/LICENSE +7 -0
  3. package/README.md +124 -0
  4. package/package.json +34 -33
  5. package/src/address-has-history.js +6 -2
  6. package/src/allowance/constants.js +4 -5
  7. package/src/allowance/index.js +159 -40
  8. package/src/check-tx/create-assess-transaction.js +23 -0
  9. package/src/check-tx/create-check-tx.js +104 -0
  10. package/src/check-tx/index.js +1 -0
  11. package/src/create-asset-plugin-factory.js +115 -0
  12. package/src/create-asset-utils.js +520 -0
  13. package/src/create-asset.js +272 -97
  14. package/src/create-token-factory.js +47 -17
  15. package/src/custom-fees.js +57 -0
  16. package/src/eip7702-whitelist.js +62 -0
  17. package/src/ens/index.js +12 -17
  18. package/src/error-wrapper.js +383 -0
  19. package/src/eth-like-util.js +172 -33
  20. package/src/exodus-eth-server/api-coin-nodes.js +27 -196
  21. package/src/exodus-eth-server/api.js +23 -14
  22. package/src/exodus-eth-server/clarity-v2.js +255 -0
  23. package/src/exodus-eth-server/clarity.js +67 -265
  24. package/src/exodus-eth-server/errors.js +40 -0
  25. package/src/exodus-eth-server/eth-like-server.js +385 -0
  26. package/src/exodus-eth-server/fetch-json.js +48 -0
  27. package/src/exodus-eth-server/index.js +77 -0
  28. package/src/exodus-eth-server/utils.js +31 -0
  29. package/src/exodus-eth-server/ws-gateway.js +306 -0
  30. package/src/exodus-eth-server/ws.js +1 -2
  31. package/src/fee-data/EthLikeFeeData.js +81 -0
  32. package/src/fee-data/index.js +54 -0
  33. package/src/fee-utils.js +229 -0
  34. package/src/gas-estimation.js +183 -66
  35. package/src/get-balance-for-address.js +1 -1
  36. package/src/get-balances.js +285 -27
  37. package/src/get-fee.js +92 -56
  38. package/src/get-historical-balance.js +58 -0
  39. package/src/hooks/index.js +1 -1
  40. package/src/hooks/monitor.js +182 -43
  41. package/src/hooks/staking-freshness.js +58 -0
  42. package/src/index.js +118 -12
  43. package/src/move-funds.js +153 -0
  44. package/src/multicall3/index.js +212 -0
  45. package/src/multisig-address.js +146 -0
  46. package/src/nft-utils.js +60 -0
  47. package/src/number-utils.js +34 -2
  48. package/src/optimism-gas/index.js +13 -5
  49. package/src/send-validations.js +58 -0
  50. package/src/server-based-fee-monitor.js +12 -19
  51. package/src/simulate-tx/fetch-tx-preview.js +0 -2
  52. package/src/simulate-tx/index.js +2 -2
  53. package/src/simulate-tx/simulate-eth-tx.js +24 -11
  54. package/src/simulation/common.js +34 -0
  55. package/src/simulation/create-simulate-message.js +49 -0
  56. package/src/simulation/create-simulate-transactions.js +108 -0
  57. package/src/simulation/estimate-fee.js +14 -0
  58. package/src/simulation/estimate-simple-transfer.js +15 -0
  59. package/src/simulation/get-message-type.js +18 -0
  60. package/src/simulation/simulate-message-api.js +68 -0
  61. package/src/simulation/simulate-transactions-api.js +266 -0
  62. package/src/simulation/simulate-transactions.js +16 -0
  63. package/src/simulation/transactions.js +52 -0
  64. package/src/simulation/try-estimating-changes-locally.js +26 -0
  65. package/src/staking/api/index.js +43 -0
  66. package/src/staking/cached-read.js +33 -0
  67. package/src/staking/ethereum/api.js +166 -34
  68. package/src/staking/ethereum/deps.js +37 -0
  69. package/src/staking/ethereum/everstake.js +113 -0
  70. package/src/staking/ethereum/index.js +4 -3
  71. package/src/staking/ethereum/service.js +682 -181
  72. package/src/staking/ethereum/staking-utils.js +275 -4
  73. package/src/staking/fantom-staking.js +9 -9
  74. package/src/staking/index.js +4 -4
  75. package/src/staking/matic/addresses.js +2 -0
  76. package/src/staking/matic/api.js +69 -49
  77. package/src/staking/matic/index.js +3 -3
  78. package/src/staking/matic/matic-staking-utils.js +243 -5
  79. package/src/staking/matic/matic-staking.md +53 -0
  80. package/src/staking/staking-provider-client.js +1 -1
  81. package/src/staking/utils/index.js +39 -0
  82. package/src/tx-create.js +666 -0
  83. package/src/tx-log/clarity-monitor.js +273 -103
  84. package/src/tx-log/clarity-truncated-history-monitor.js +109 -0
  85. package/src/tx-log/clarity-truncated-history-monitor.md +219 -0
  86. package/src/tx-log/clarity-utils/absolute.js +96 -0
  87. package/src/tx-log/clarity-utils/get-log-items-from-server-tx.js +94 -31
  88. package/src/tx-log/clarity-utils/get-names-of-tokens-transferred-by-server-tx.js +3 -2
  89. package/src/tx-log/clarity-utils/index.js +9 -3
  90. package/src/tx-log/clarity-utils/is-spam-pending-tx.js +13 -0
  91. package/src/tx-log/clarity-utils/normalize-transactions-response.js +65 -0
  92. package/src/tx-log/ethereum-monitor.js +93 -47
  93. package/src/tx-log/ethereum-no-history-monitor.js +52 -41
  94. package/src/tx-log/get-optimistic-txlog-effects.js +379 -0
  95. package/src/tx-log/index.js +5 -3
  96. package/src/tx-log/monitor-utils/check-pending-transactions.js +14 -5
  97. package/src/tx-log/monitor-utils/exclude-unchanged-token-balances.js +1 -1
  98. package/src/tx-log/monitor-utils/extract-balance-from-clarity-account-info.js +108 -0
  99. package/src/tx-log/monitor-utils/get-balance-updates.js +44 -0
  100. package/src/tx-log/monitor-utils/get-batched-rpc-balances.js +47 -0
  101. package/src/tx-log/monitor-utils/get-current-blacklist-status.js +38 -0
  102. package/src/tx-log/monitor-utils/get-current-eip7702-delegation.js +79 -0
  103. package/src/tx-log/monitor-utils/get-derive-transactions-to-check.js +10 -5
  104. package/src/tx-log/monitor-utils/get-fee-amount.js +1 -1
  105. package/src/tx-log/monitor-utils/get-history-from-server.js +1 -1
  106. package/src/tx-log/monitor-utils/get-log-items-from-server-tx.js +75 -33
  107. package/src/tx-log/monitor-utils/get-names-of-tokens-transferred-by-server-tx.js +3 -2
  108. package/src/tx-log/monitor-utils/get-raw-transfer-amount.js +29 -0
  109. package/src/tx-log/monitor-utils/get-transfers-by-token-name.js +3 -2
  110. package/src/tx-log/monitor-utils/get-value-of-transfers.js +10 -12
  111. package/src/tx-log/monitor-utils/index.js +12 -7
  112. package/src/tx-log/monitor-utils/is-unverifiable-pending-tx-expired.js +19 -0
  113. package/src/tx-log/monitor-utils/verify-pending-tx-status-rpc.js +90 -0
  114. package/src/tx-log-staking-processor/asset-staking-tx-data.js +82 -24
  115. package/src/tx-log-staking-processor/index.js +28 -14
  116. package/src/tx-log-staking-processor/utils.js +21 -20
  117. package/src/tx-send/broadcast-error-handler.js +63 -0
  118. package/src/tx-send/index.js +1 -2
  119. package/src/tx-send/nonce-utils.js +97 -0
  120. package/src/tx-send/tx-send.js +94 -288
  121. package/src/tx-sign/index.js +17 -0
  122. package/src/tx-type/index.js +280 -0
  123. package/src/watch-tx.js +36 -0
  124. package/src/web3/createSimulateMessage.js +3 -2
  125. package/src/web3/createSimulateTransactions.js +11 -0
  126. package/src/web3/index.js +3 -1
  127. package/src/etherscan/account.js +0 -49
  128. package/src/etherscan/index.js +0 -28
  129. package/src/etherscan/logs.js +0 -16
  130. package/src/etherscan/proxy.js +0 -47
  131. package/src/etherscan/request.js +0 -26
  132. package/src/etherscan/ws.js +0 -88
  133. package/src/exodus-eth-server/server-factory.js +0 -28
  134. package/src/exodus-eth-server/server-map.js +0 -21
  135. package/src/get-fee-async.js +0 -97
  136. package/src/staking/matic/service.js +0 -437
  137. package/src/staking-api.js +0 -5
  138. package/src/tx-log/clarity-utils/get-derive-data-needed-for-tick.js +0 -12
  139. package/src/tx-log-staking-processor/get-asset-tx-amount.js +0 -26
  140. package/src/tx-send/get-fee-info.js +0 -41
  141. package/src/websocket/index.android.js +0 -2
  142. package/src/websocket/index.ios.js +0 -2
  143. package/src/websocket/index.js +0 -21
@@ -1,31 +1,33 @@
1
- import { isRpcBalanceAsset, getAssetAddresses } from '@exodus/ethereum-lib'
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
- excludeUnchangedTokenBalances,
11
- } from './monitor-utils'
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
- import { BaseMonitor } from '@exodus/asset-lib'
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 { pendingTransactionsToCheck, pendingTransactionsGroupedByAddressAndNonce } =
75
- checkPendingTransactions(params)
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
- params.logItemsByAsset[assetName].push({
101
- ...tokenTxSet.get(tx.txId),
102
- ...(isTxConfirmed ? { confirmations: 1 } : { dropped: true, error: 'Dropped' }),
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
- if (params.refresh) {
121
- await updateTx(tx, assetName, { remove: true })
122
- } else {
123
- // FIXME: Fetching unconfirmed txs from node helps to avoid
124
- // flagging on-chain confirmed txs as dropped in the wallet.
125
- // Once the real bug is found, remove this logic
126
- const response = await this.server.getTransactionByHash(tx.txId)
127
- const isTxConfirmed = response?.result?.blockNumber !== null
128
- this.logger.warn(`tx ${tx.txId} confirmed: ${isTxConfirmed}`)
129
- await updateTx(tx, assetName, { isTxConfirmed })
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._getUnknownTokenAddresses({
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({ gasPrice, baseFeePerGas }) {
245
+ async updateGasPrice(fetchedGasPrices) {
202
246
  try {
203
- const feeConfig = {
204
- gasPrice: `${parseInt(gasPrice, 16)} wei`,
205
- }
206
- if (baseFeePerGas) {
207
- feeConfig.baseFeePerGas = `${parseInt(baseFeePerGas, 16)} wei`
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 (isRpcBalanceAsset(asset)) {
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) => isRpcBalanceAsset(token) && token.contract.address)
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
- _getUnknownTokenAddresses({ transactions, tokensByAddress }) {
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
- excludeUnchangedTokenBalances,
7
- } from './monitor-utils'
8
-
9
- import { isEmpty, unionBy, zipObject } from 'lodash'
15
+ } from './monitor-utils/index.js'
10
16
 
11
- import { BaseMonitor } from '@exodus/asset-lib'
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
- const batch = Object.create(null)
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
- if (now - tx.date.getTime() > UNCONFIRMED_TX_LIMIT && txFromNode?.blockHash == null) {
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 (txFromNode?.blockHash !== null) {
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 this.updateAccountState({ newData: { ...accountState }, walletAccount })
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 })