@exodus/balances 13.9.1 → 13.11.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.
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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
|
+
## [13.11.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/balances@13.10.0...@exodus/balances@13.11.0) (2025-07-09)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- feat: getBalance, createBalance selectors to default 0 NU (#13164)
|
|
11
|
+
|
|
12
|
+
## [13.10.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/balances@13.9.1...@exodus/balances@13.10.0) (2025-07-04)
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- feat: provide feeData to asset.api.getBalances (#13104)
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
- fix: provide feeData as undefined to getBalances when disabled (#13113)
|
|
21
|
+
|
|
6
22
|
## [13.9.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/balances@13.9.0...@exodus/balances@13.9.1) (2025-06-11)
|
|
7
23
|
|
|
8
24
|
### Performance
|
package/module/index.js
CHANGED
|
@@ -42,6 +42,7 @@ class Balances {
|
|
|
42
42
|
#balancesAtom
|
|
43
43
|
#accountStatesAtom
|
|
44
44
|
#txLogsAtom
|
|
45
|
+
#feeDataAtom
|
|
45
46
|
#enabledWalletAccountsAtom
|
|
46
47
|
#loaded = false
|
|
47
48
|
#subscriptions = []
|
|
@@ -55,6 +56,7 @@ class Balances {
|
|
|
55
56
|
balancesAtom,
|
|
56
57
|
txLogsAtom,
|
|
57
58
|
accountStatesAtom,
|
|
59
|
+
feeDataAtom,
|
|
58
60
|
}) {
|
|
59
61
|
this.#logger = logger
|
|
60
62
|
this.#assetsModule = assetsModule
|
|
@@ -64,6 +66,7 @@ class Balances {
|
|
|
64
66
|
this.#balancesAtom = balancesAtom
|
|
65
67
|
this.#accountStatesAtom = accountStatesAtom
|
|
66
68
|
this.#txLogsAtom = txLogsAtom
|
|
69
|
+
this.#feeDataAtom = feeDataAtom
|
|
67
70
|
this.#enabledWalletAccountsAtom = enabledWalletAccountsAtom
|
|
68
71
|
|
|
69
72
|
const shareQueue =
|
|
@@ -87,6 +90,7 @@ class Balances {
|
|
|
87
90
|
// these share a queue
|
|
88
91
|
this.#updateAssetSource = shareQueue(this.#updateAssetSource)
|
|
89
92
|
this.#handleTxLogUpdate = shareQueue(this.#handleTxLogUpdate)
|
|
93
|
+
this.#handleFeeDataUpdate = shareQueue(this.#handleFeeDataUpdate)
|
|
90
94
|
this.#recompute = shareQueue(this.#recompute)
|
|
91
95
|
this.#onAssetsChanged = shareQueue(this.#onAssetsChanged)
|
|
92
96
|
this.#getBalancesForAssetSource = swallowErrors(this.#getBalancesForAssetSource)
|
|
@@ -111,6 +115,16 @@ class Balances {
|
|
|
111
115
|
this.#handleTxLogUpdate({ walletAccount, assetName, txLog })
|
|
112
116
|
)
|
|
113
117
|
),
|
|
118
|
+
this.#feeDataAtom.observe((changes) => {
|
|
119
|
+
Object.entries(changes).forEach(([assetName, feeData]) => {
|
|
120
|
+
const asset = this.#getAsset(assetName).baseAsset
|
|
121
|
+
if (asset.api?.features?.balancesRequireFeeData)
|
|
122
|
+
this.#walletAccounts.forEach((walletAccount) => {
|
|
123
|
+
return this.#handleFeeDataUpdate({ walletAccount, assetName, feeData })
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
}),
|
|
127
|
+
|
|
114
128
|
getChanges(this.#availableAssetNamesAtom).observe(({ previous = [], current }) => {
|
|
115
129
|
this.#availableAssetNames = current
|
|
116
130
|
const added = difference(current, previous)
|
|
@@ -143,6 +157,15 @@ class Balances {
|
|
|
143
157
|
return this.#updateAssetSource(opts)
|
|
144
158
|
}
|
|
145
159
|
|
|
160
|
+
#handleFeeDataUpdate = (opts) => {
|
|
161
|
+
if (!this.#ready) {
|
|
162
|
+
this.#logger.log('ignore update from fee-data event.', { isReady: this.#ready })
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return this.#updateAssetSource(opts)
|
|
167
|
+
}
|
|
168
|
+
|
|
146
169
|
#pushIntoSharedQueue = makeConcurrent((fn) => fn(), { concurrency: 1 })
|
|
147
170
|
|
|
148
171
|
#reset = ({ walletAccounts = this.#walletAccounts } = {}) => {
|
|
@@ -162,7 +185,12 @@ class Balances {
|
|
|
162
185
|
// It adds both old and new balances to the response.
|
|
163
186
|
// Ideally, selectors should start using the new ones (total, spendable, etc)
|
|
164
187
|
|
|
165
|
-
#getBalancesFromBlockchainMetadata = ({
|
|
188
|
+
#getBalancesFromBlockchainMetadata = ({
|
|
189
|
+
assetNames,
|
|
190
|
+
accountState,
|
|
191
|
+
feeData,
|
|
192
|
+
txLogsByAssetName,
|
|
193
|
+
}) => {
|
|
166
194
|
// in the future accountState may have all the balances and we won't need to rely on txLog
|
|
167
195
|
// for now, accountState-based balance has both the baseAsset and token balances while txLog is per asset/token
|
|
168
196
|
const getAssetBalances = (assetName) => {
|
|
@@ -171,7 +199,7 @@ class Balances {
|
|
|
171
199
|
|
|
172
200
|
const zero = asset.currency.ZERO
|
|
173
201
|
|
|
174
|
-
const apiBalances = asset.api.getBalances({ asset, accountState, txLog })
|
|
202
|
+
const apiBalances = asset.api.getBalances({ asset, accountState, txLog, feeData })
|
|
175
203
|
const balances = processAssetBalances({
|
|
176
204
|
balanceFieldsConfig: this.#balanceFieldsConfig,
|
|
177
205
|
balances: apiBalances || Object.create(null),
|
|
@@ -259,24 +287,33 @@ class Balances {
|
|
|
259
287
|
#loadBlockchainMetadataForBalance = async ({
|
|
260
288
|
assetName,
|
|
261
289
|
walletAccount,
|
|
290
|
+
feeData: providedFeeData, // it's optional, undefined when balancesRequireFeeData is disabled
|
|
262
291
|
accountState: providedAccountState = null,
|
|
263
292
|
txLogsByAssetName: providedTxLogsByAssetName = {},
|
|
264
293
|
}) => {
|
|
265
294
|
const { value: txLogsByAssetSource } = await this.#txLogsAtom.get()
|
|
266
295
|
let accountState = providedAccountState
|
|
296
|
+
|
|
297
|
+
const baseAsset = this.#getAsset(assetName).baseAsset
|
|
298
|
+
|
|
267
299
|
if (!accountState) {
|
|
268
|
-
const
|
|
269
|
-
const assetNameToUse = asset.name
|
|
300
|
+
const assetNameToUse = baseAsset.name
|
|
270
301
|
const { value } = await this.#accountStatesAtom.get()
|
|
271
302
|
const cached = value[walletAccount]?.[assetNameToUse]
|
|
272
303
|
if (cached) {
|
|
273
304
|
accountState = cached
|
|
274
305
|
} else {
|
|
275
|
-
const AccountState =
|
|
306
|
+
const AccountState = baseAsset.api?.createAccountState?.()
|
|
276
307
|
accountState = AccountState?.create()
|
|
277
308
|
}
|
|
278
309
|
}
|
|
279
310
|
|
|
311
|
+
let feeData = providedFeeData
|
|
312
|
+
if (!feeData && baseAsset.api?.features?.balancesRequireFeeData) {
|
|
313
|
+
const fees = await this.#feeDataAtom.get()
|
|
314
|
+
feeData = fees[baseAsset.name]
|
|
315
|
+
}
|
|
316
|
+
|
|
280
317
|
const assetNames = this.#getRelatedAssetNames(assetName)
|
|
281
318
|
const txLogsByAssetName = zipObject(
|
|
282
319
|
assetNames,
|
|
@@ -289,6 +326,7 @@ class Balances {
|
|
|
289
326
|
)
|
|
290
327
|
|
|
291
328
|
return {
|
|
329
|
+
feeData,
|
|
292
330
|
accountState,
|
|
293
331
|
txLogsByAssetName,
|
|
294
332
|
}
|
|
@@ -299,6 +337,7 @@ class Balances {
|
|
|
299
337
|
walletAccount,
|
|
300
338
|
accountState: providedAccountState,
|
|
301
339
|
txLog: providedTxLog,
|
|
340
|
+
feeData: providedFeeData,
|
|
302
341
|
}) => {
|
|
303
342
|
if (!this.#isSupportedAsset(assetName)) return
|
|
304
343
|
|
|
@@ -307,20 +346,23 @@ class Balances {
|
|
|
307
346
|
return
|
|
308
347
|
}
|
|
309
348
|
|
|
310
|
-
const { accountState, txLogsByAssetName } =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
349
|
+
const { accountState, txLogsByAssetName, feeData } =
|
|
350
|
+
await this.#loadBlockchainMetadataForBalance({
|
|
351
|
+
assetName,
|
|
352
|
+
walletAccount,
|
|
353
|
+
feeData: providedFeeData,
|
|
354
|
+
accountState: providedAccountState,
|
|
355
|
+
txLogsByAssetName: {
|
|
356
|
+
[assetName]: providedTxLog,
|
|
357
|
+
},
|
|
358
|
+
})
|
|
318
359
|
|
|
319
360
|
const accountBalances = this.#balances[walletAccount]
|
|
320
361
|
const newBalances = this.#getBalancesFromBlockchainMetadata({
|
|
321
362
|
assetNames: this.#getRelatedAssetNames(assetName),
|
|
322
363
|
accountState,
|
|
323
364
|
txLogsByAssetName,
|
|
365
|
+
feeData,
|
|
324
366
|
})
|
|
325
367
|
|
|
326
368
|
const changes = Object.create(null)
|
|
@@ -383,6 +425,7 @@ const balancesDefinition = {
|
|
|
383
425
|
'balancesAtom',
|
|
384
426
|
'txLogsAtom',
|
|
385
427
|
'accountStatesAtom',
|
|
428
|
+
'feeDataAtom',
|
|
386
429
|
],
|
|
387
430
|
public: true,
|
|
388
431
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/balances",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.11.0",
|
|
4
4
|
"description": "Tracks crypto balances across enabled wallet accounts.",
|
|
5
5
|
"author": "Exodus Movement, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@exodus/assets": "^11.0.0",
|
|
55
55
|
"@exodus/assets-base": "^10.0.0",
|
|
56
|
-
"@exodus/assets-feature": "^8.1.
|
|
56
|
+
"@exodus/assets-feature": "^8.1.3",
|
|
57
57
|
"@exodus/available-assets": "^8.7.0",
|
|
58
58
|
"@exodus/bitcoin-meta": "^2.0.0",
|
|
59
|
-
"@exodus/blockchain-metadata": "^15.
|
|
59
|
+
"@exodus/blockchain-metadata": "^15.12.0",
|
|
60
60
|
"@exodus/ethereum-meta": "^2.0.0",
|
|
61
61
|
"@exodus/fee-data-monitors": "^5.0.4",
|
|
62
62
|
"@exodus/storage-memory": "^2.2.2",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"publishConfig": {
|
|
68
68
|
"access": "public"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "db4613f1399e3fa3c65135835bb0e84ba5feb520"
|
|
71
71
|
}
|
|
@@ -5,12 +5,14 @@ import { createSelector } from 'reselect'
|
|
|
5
5
|
const { memoize } = lodash // eslint-disable-line @exodus/basic-utils/prefer-basic-utils
|
|
6
6
|
|
|
7
7
|
const createBalanceSelectorFactory = ({ field, id }) => {
|
|
8
|
-
const selectorFactory = (selfSelector) =>
|
|
8
|
+
const selectorFactory = (assetsSelector, selfSelector) =>
|
|
9
9
|
memoize(
|
|
10
10
|
({ assetName, walletAccount }) =>
|
|
11
11
|
createSelector(
|
|
12
|
+
assetsSelector,
|
|
12
13
|
selfSelector,
|
|
13
|
-
(balances) =>
|
|
14
|
+
(assets, balances) =>
|
|
15
|
+
balances[walletAccount]?.data[assetName]?.[field] ?? assets[assetName]?.currency.ZERO
|
|
14
16
|
),
|
|
15
17
|
({ assetName, walletAccount, field }) => [assetName, walletAccount, field].join('-')
|
|
16
18
|
)
|
|
@@ -18,7 +20,7 @@ const createBalanceSelectorFactory = ({ field, id }) => {
|
|
|
18
20
|
return {
|
|
19
21
|
id,
|
|
20
22
|
selectorFactory,
|
|
21
|
-
dependencies: [{ selector: MY_STATE }],
|
|
23
|
+
dependencies: [{ module: 'assets', selector: 'all' }, { selector: MY_STATE }],
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -5,7 +5,7 @@ const { memoize } = lodash // eslint-disable-line @exodus/basic-utils/prefer-bas
|
|
|
5
5
|
|
|
6
6
|
const { balanceFields } = defaultConfig
|
|
7
7
|
|
|
8
|
-
const resultFunction = (getBalances) =>
|
|
8
|
+
const resultFunction = (assets, getBalances) =>
|
|
9
9
|
memoize(
|
|
10
10
|
({ assetName, walletAccount, field }) => {
|
|
11
11
|
if (!balanceFields.includes(field)) {
|
|
@@ -14,7 +14,7 @@ const resultFunction = (getBalances) =>
|
|
|
14
14
|
)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
return getBalances({ assetName, walletAccount })?.[field]
|
|
17
|
+
return getBalances({ assetName, walletAccount })?.[field] ?? assets[assetName]?.currency.ZERO
|
|
18
18
|
},
|
|
19
19
|
({ assetName, walletAccount, field }) => [assetName, walletAccount, field].join('-')
|
|
20
20
|
)
|
|
@@ -22,7 +22,10 @@ const resultFunction = (getBalances) =>
|
|
|
22
22
|
const getBalanceForFieldSelectorDefinition = {
|
|
23
23
|
id: 'getBalanceForField',
|
|
24
24
|
resultFunction,
|
|
25
|
-
dependencies: [
|
|
25
|
+
dependencies: [
|
|
26
|
+
{ module: 'assets', selector: 'all' },
|
|
27
|
+
{ module: 'balances', selector: 'getBalances' },
|
|
28
|
+
],
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export default getBalanceForFieldSelectorDefinition
|