@exodus/ethereum-api 8.59.1 → 8.59.2

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 CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [8.59.2](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.59.1...@exodus/ethereum-api@8.59.2) (2025-11-19)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+
12
+ * fix: correct absolute balance logic for deeply nested pending transactions (#6966)
13
+
14
+ * fix: drop pending receive amounts from absolute spendable balances (#6958)
15
+
16
+
17
+
6
18
  ## [8.59.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.59.0...@exodus/ethereum-api@8.59.1) (2025-11-10)
7
19
 
8
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "8.59.1",
3
+ "version": "8.59.2",
4
4
  "description": "Transaction monitors, fee monitors, RPC with the blockchain node, and other networking code for Ethereum and EVM-based blockchains",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -67,5 +67,5 @@
67
67
  "type": "git",
68
68
  "url": "git+https://github.com/ExodusMovement/assets.git"
69
69
  },
70
- "gitHead": "ec8e542519bed6a27d746a9cb8c305d45d3a5a3c"
70
+ "gitHead": "d1505076f4e06aad8f7962d9fad12354c020a30c"
71
71
  }
@@ -27,6 +27,15 @@ export const getAbsoluteBalance = ({ asset, txLog }) => {
27
27
  break
28
28
  }
29
29
 
30
+ // NOTE: Avoids processing any unconfirmed balances, since
31
+ // `getAbsoluteBalance` only processes a subset of
32
+ // transactions up until the first `balanceChange` event
33
+ // is encountered, but `getBalancesFactory` takes all
34
+ // all pending amounts into consideration (i.e. those
35
+ // at a greater depth than the most recent `balanceChange`
36
+ // event).
37
+ if (!tx.failed && tx.pending) continue
38
+
30
39
  if (!(tx.dropped || tx.data.replacedBy)) {
31
40
  if (!tx.error) {
32
41
  balance = balance.add(tx.coinAmount)
@@ -128,21 +137,29 @@ export const getBalancesFactory = ({ monitorType, useAbsoluteBalance, rpcBalance
128
137
  const unstaking = canClaimUndelegatedBalance ? asset.currency.ZERO : unclaimedUndelegatedBalance
129
138
  const unstaked = canClaimUndelegatedBalance ? unclaimedUndelegatedBalance : asset.currency.ZERO
130
139
 
131
- let total
132
140
  let spendable
133
141
 
134
142
  // Balance from accountState is considered total b/c is fetched from rpc
135
143
  if (rpcBalanceAssetNames.includes(asset.name) || monitorType === 'no-history') {
136
- total = getBalanceFromAccountState({ asset, accountState }).sub(unconfirmedSent)
137
- spendable = total.sub(staked).sub(staking).sub(unstaking).sub(unstaked)
144
+ // NOTE: Balances from a `no-history` monitor work like calls
145
+ // to `balanceOf`/`eth_getBalance` at the latest block tag,
146
+ // therefore there's no need to subtract `unconfirmedReceived`.
147
+ //
148
+ // Likewise, they do not compensate for pending sends, so we
149
+ // must manually subtract the `unconfirmedSent` to produce a
150
+ // pessimistic reading that's safe for consumers.
151
+ spendable = getBalanceFromAccountState({ asset, accountState }).sub(unconfirmedSent)
138
152
  } else {
139
- // Balance from txLog does not include staking rewards
140
- // spendable and total are calculated differently based on staking txs
141
153
  const absoluteBalance = useAbsoluteBalance && getAbsoluteBalance({ asset, txLog })
142
154
 
143
155
  if (absoluteBalance) {
144
- spendable = absoluteBalance
156
+ // NOTE: The returned `absoluteBalance` returns only confirmed
157
+ // values similar to the `'no-history'` case, so we need
158
+ // to reduce this amount by the `unconfirmedSent`.
159
+ spendable = absoluteBalance.sub(unconfirmedSent)
145
160
  } else {
161
+ // Balance from txLog does not include staking rewards
162
+ // spendable and total are calculated differently based on staking txs
146
163
  spendable = getSpendable({
147
164
  asset,
148
165
  balance: getBalanceFromTxLog({ txLog, asset }),
@@ -150,10 +167,9 @@ export const getBalancesFactory = ({ monitorType, useAbsoluteBalance, rpcBalance
150
167
  unconfirmedReceived,
151
168
  })
152
169
  }
153
-
154
- total = spendable.add(staked).add(staking).add(unstaking).add(unstaked)
155
170
  }
156
171
 
172
+ const total = spendable.add(staked).add(staking).add(unstaking).add(unstaked)
157
173
  const stakeable = spendable
158
174
 
159
175
  return {