@exodus/ethereum-api 8.76.6 → 8.77.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 (38) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/package.json +2 -3
  3. package/src/create-asset-plugin-factory.js +1 -0
  4. package/src/create-asset-utils.js +39 -35
  5. package/src/create-asset.js +32 -14
  6. package/src/create-token-factory.js +3 -0
  7. package/src/exodus-eth-server/api-coin-nodes.js +11 -84
  8. package/src/exodus-eth-server/clarity-v2.js +30 -51
  9. package/src/exodus-eth-server/clarity.js +2 -115
  10. package/src/exodus-eth-server/errors.js +5 -1
  11. package/src/exodus-eth-server/eth-like-server-base.js +123 -0
  12. package/src/exodus-eth-server/fetch-json.js +48 -0
  13. package/src/gas-estimation.js +19 -4
  14. package/src/get-balances.js +14 -0
  15. package/src/get-historical-balance.js +58 -0
  16. package/src/index.js +2 -0
  17. package/src/multicall3/index.js +169 -0
  18. package/src/simulation/common.js +34 -0
  19. package/src/simulation/create-simulate-message.js +49 -0
  20. package/src/simulation/create-simulate-transactions.js +106 -0
  21. package/src/simulation/estimate-fee.js +14 -0
  22. package/src/simulation/estimate-simple-transfer.js +15 -0
  23. package/src/simulation/get-message-type.js +18 -0
  24. package/src/simulation/simulate-message-api.js +68 -0
  25. package/src/simulation/simulate-transactions-api.js +265 -0
  26. package/src/simulation/simulate-transactions.js +16 -0
  27. package/src/simulation/transactions.js +52 -0
  28. package/src/simulation/try-estimating-changes-locally.js +26 -0
  29. package/src/staking/ethereum/staking-utils.js +3 -1
  30. package/src/staking/matic/matic-staking-utils.js +3 -1
  31. package/src/tx-log/clarity-truncated-history-monitor.js +34 -0
  32. package/src/tx-log/clarity-utils/absolute.js +5 -1
  33. package/src/tx-log/ethereum-no-history-monitor.js +2 -23
  34. package/src/tx-log/monitor-utils/get-batched-rpc-balances.js +28 -0
  35. package/src/tx-send/broadcast-error-handler.js +7 -2
  36. package/src/tx-send/tx-send.js +1 -0
  37. package/src/web3/createSimulateMessage.js +2 -1
  38. package/src/web3/createSimulateTransactions.js +3 -9
@@ -75,6 +75,40 @@ export class ClarityTruncatedHistoryMonitor extends ClarityMonitor {
75
75
  let clarityCursor
76
76
 
77
77
  try {
78
+ // When refreshing, we need to clear out old asset
79
+ // balances in `accountState` since these can surface
80
+ // values populated from alternative monitors i.e.
81
+ // `no-history`, which can be written at arbitrary
82
+ // points in time.
83
+ //
84
+ // Since the `truncated-history-monitor` does not
85
+ // guarantee an `accountState` write for an asset,
86
+ // (these are only provided when the history is
87
+ // truncated), we must ensure older checkpoints cannot
88
+ // be inadvertently relied upon.
89
+ //
90
+ if (refresh) {
91
+ const defaultAccountState = this.asset.api.createAccountState().create()
92
+
93
+ const currentAccountState = derivedData.currentAccountState ?? defaultAccountState
94
+
95
+ const resetTokenBalances = {
96
+ balance: defaultAccountState.balance,
97
+ tokenBalances: defaultAccountState.tokenBalances,
98
+ }
99
+
100
+ // Optimistically update the `derivedData` for downstream.
101
+ derivedData.currentAccountState = currentAccountState.merge(resetTokenBalances)
102
+
103
+ this.aci.updateAccountStateBatch({
104
+ assetName,
105
+ walletAccount,
106
+ accountState: currentAccountState,
107
+ newData: resetTokenBalances,
108
+ batch,
109
+ })
110
+ }
111
+
78
112
  const response = await this.getHistoryFromServer({ walletAccount, derivedData, refresh })
79
113
 
80
114
  ;({ allTxs } = await normalizeTransactionsResponse({
@@ -50,12 +50,15 @@ const getLatestCanonicalAbsoluteTx = ({
50
50
  searchDepthMs = DEFAULT_CANONICAL_ABSOLUTE_TX_SEARCH_DEPTH,
51
51
  reversedTxLog,
52
52
  fieldName,
53
+ deadline = Infinity,
53
54
  }) => {
54
55
  assert(reversedTxLog, 'expected reversedTxLog')
55
56
 
56
57
  let latest = null
57
58
 
58
59
  for (const tx of reversedTxLog) {
60
+ if (+tx.date > deadline) continue
61
+
59
62
  if (latest) {
60
63
  const diff = +latest.date - +tx.date
61
64
 
@@ -77,10 +80,11 @@ const getLatestCanonicalAbsoluteTx = ({
77
80
  return latest
78
81
  }
79
82
 
80
- export const getLatestCanonicalAbsoluteBalanceTx = ({ searchDepthMs, reversedTxLog }) =>
83
+ export const getLatestCanonicalAbsoluteBalanceTx = ({ searchDepthMs, reversedTxLog, deadline }) =>
81
84
  getLatestCanonicalAbsoluteTx({
82
85
  searchDepthMs,
83
86
  reversedTxLog,
87
+ deadline,
84
88
  fieldName: ABSOLUTE_FIELD_NAME_BALANCE_CHANGE,
85
89
  })
86
90
 
@@ -3,7 +3,7 @@ import { SynchronizedTime } from '@exodus/basic-utils'
3
3
  import { Tx } from '@exodus/models'
4
4
  import lodash from 'lodash'
5
5
 
6
- import { fromHexToString } from '../number-utils.js'
6
+ import { getBatchedRpcBalances } from './monitor-utils/get-batched-rpc-balances.js'
7
7
  import { UNCONFIRMED_TX_LIMIT } from './monitor-utils/get-derive-transactions-to-check.js'
8
8
  import {
9
9
  excludeUnchangedTokenBalances,
@@ -41,28 +41,7 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
41
41
  }
42
42
 
43
43
  async getBalances({ tokens, ourWalletAddress }) {
44
- const batch = Object.create(null)
45
- const request = this.server.getBalanceRequest(ourWalletAddress)
46
- batch[this.asset.name] = request
47
- for (const token of tokens) {
48
- const request = this.server.balanceOfRequest(ourWalletAddress, token.contract.address)
49
- batch[token.name] = request
50
- }
51
-
52
- const pairs = Object.entries(batch)
53
- if (pairs.length === 0) {
54
- return {}
55
- }
56
-
57
- const requests = pairs.map((pair) => pair[1])
58
- const responses = await this.server.sendBatchRequest(requests)
59
- const entries = pairs.map((pair, idx) => {
60
- const balanceHex = responses[idx]
61
- const name = pair[0]
62
- const balance = fromHexToString(balanceHex)
63
- return [name, balance]
64
- })
65
- return Object.fromEntries(entries)
44
+ return getBatchedRpcBalances({ baseAsset: this.asset, ourWalletAddress, tokens })
66
45
  }
67
46
 
68
47
  async getNewAccountState({ tokens, currentTokenBalances, ourWalletAddress }) {
@@ -0,0 +1,28 @@
1
+ import assert from 'minimalistic-assert'
2
+
3
+ import { fromHexToString } from '../../number-utils.js'
4
+
5
+ export const getBatchedRpcBalances = async ({ baseAsset, tokens, ourWalletAddress }) => {
6
+ assert(baseAsset, 'expected baseAsset')
7
+ assert(Array.isArray(tokens), 'expected array tokens')
8
+ assert(ourWalletAddress, 'expected ourWalletAddress')
9
+
10
+ const [balance, ...tokenBalances] = await tokens
11
+ .reduce(
12
+ (acc, { contract: { address: tokenAddress } }) =>
13
+ acc.balanceOfRequest(ourWalletAddress, tokenAddress),
14
+ baseAsset.createRpcRequestAccumulator().getBalanceRequest(ourWalletAddress)
15
+ )
16
+ .flush(baseAsset.server)
17
+
18
+ return {
19
+ [baseAsset.name]: fromHexToString(balance),
20
+ ...Object.fromEntries(
21
+ tokens.map(({ name, contract: { address: tokenAddress } }, i) => {
22
+ const tokenBalance = tokenBalances[i]?.confirmed?.[tokenAddress]
23
+ assert(tokenBalance != null, `missing token balance for ${name} (${tokenAddress})`)
24
+ return [name, tokenBalance]
25
+ })
26
+ ),
27
+ }
28
+ }
@@ -11,10 +11,15 @@ import { transactionExists } from '../eth-like-util.js'
11
11
  * @param {string} options.txId - The transaction ID.
12
12
  * @param {boolean} options.isHardware - Whether this is a hardware wallet.
13
13
  * @param {string} options.hint - Hint for the error.
14
+ * @param {boolean} options.isBumpTx - Whether the occur occured whilst bumping a Tx.
15
+ * @param {boolean} options.isRetryNonceTx - If the error occurred during a nonce bump.
14
16
  * @returns {Promise<{ shouldRetry: boolean }>} - Returns if nonce too low and can retry.
15
17
  * @throws {EthLikeError} - Throws for all other error cases.
16
18
  */
17
- export const handleBroadcastError = async (err, { asset, txId, isHardware, hint, isBumpTx }) => {
19
+ export const handleBroadcastError = async (
20
+ err,
21
+ { asset, txId, isHardware, hint, isBumpTx, isRetryNonceTx }
22
+ ) => {
18
23
  const message = err.message
19
24
 
20
25
  const errorInfo = getEvmErrorReason(message) || EVM_ERROR_REASONS.broadcastTxFailed
@@ -46,7 +51,7 @@ export const handleBroadcastError = async (err, { asset, txId, isHardware, hint,
46
51
  // NOTE: Don't auto-retry nonce repair for bump/replacement txs.
47
52
  // A replacement must keep the *same nonce* as the tx it's replacing.
48
53
  // If we "fix" a bump tx by advancing the nonce, we create a brand-new tx instead of replacing the pending one.
49
- if (isNonceTooLow && !isHardware && !isBumpTx) return { shouldRetry: true }
54
+ if (isNonceTooLow && !isHardware && !isBumpTx && !isRetryNonceTx) return { shouldRetry: true }
50
55
 
51
56
  throw new EthLikeError({
52
57
  message: err.message,
@@ -100,6 +100,7 @@ const txSendFactory = ({ assetClientInterface, createTx }) => {
100
100
  isHardware: false,
101
101
  hint: safeString`retry:broadcastTx`,
102
102
  isBumpTx: false, // retry path is only for non-bump anyway
103
+ isRetryNonceTx: true,
103
104
  })
104
105
  }
105
106
  }
@@ -1,6 +1,7 @@
1
- import { createSimulateMessage as createSimulateEVMMessage } from '@exodus/web3-ethereum-utils'
2
1
  import assert from 'minimalistic-assert'
3
2
 
3
+ import { createSimulateMessage as createSimulateEVMMessage } from '../simulation/create-simulate-message.js'
4
+
4
5
  export const createSimulateMessage = ({ asset }) => {
5
6
  assert(asset, '"asset" should be passed.')
6
7
 
@@ -1,17 +1,11 @@
1
- import { createSimulateTransactions as createSimulateEVMTransactions } from '@exodus/web3-ethereum-utils'
2
1
  import assert from 'minimalistic-assert'
3
2
 
3
+ import { simulateTransactions as simulateEthereumTransactions } from '../simulation/simulate-transactions.js'
4
+
4
5
  export const createSimulateTransactions = ({ asset }) => {
5
6
  assert(asset, '"asset" should be passed.')
6
7
 
7
- const simulateEVMTransactions = createSimulateEVMTransactions({
8
- apiEndpoint: 'https://simulation.a.exodus.io/simulate',
9
- headers: {
10
- 'X-Api-Version': '2023-06-05',
11
- },
12
- })
13
-
14
8
  return function simulateTransaction({ transactions, ...restParameters }) {
15
- return simulateEVMTransactions({ asset, transactions, ...restParameters })
9
+ return simulateEthereumTransactions({ asset, transactions, ...restParameters })
16
10
  }
17
11
  }