@exodus/balances 13.13.1 → 13.14.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,16 @@
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.14.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/balances@13.13.1...@exodus/balances@13.14.0) (2025-11-27)
7
+
8
+ ### Features
9
+
10
+ - feat: bump atoms for importers of observer factory (#14057)
11
+
12
+ - feat: introduce blockChainMetadataAtom (#14470)
13
+
14
+ - feat: update all consumers to use safe strings for error namespaces (#14429)
15
+
6
16
  ## [13.13.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/balances@13.13.0...@exodus/balances@13.13.1) (2025-08-22)
7
17
 
8
18
  **Note:** Version bump only for package @exodus/balances
@@ -1,4 +1,5 @@
1
1
  import { compute, createStorageAtomFactory } from '@exodus/atoms'
2
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
2
3
  import lodash from 'lodash'
3
4
 
4
5
  const { flatMap, map } = lodash
package/index.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  import typeforce from '@exodus/typeforce'
16
16
  import defaultConfig from './default-config.js'
17
17
  import mockableBalancesDefinition from './module/mockable-balances.js'
18
+ import { safeString } from '@exodus/safe-string'
18
19
 
19
20
  const balances = (walletConfig) => {
20
21
  const config = {
@@ -34,7 +35,7 @@ const balances = (walletConfig) => {
34
35
  )
35
36
 
36
37
  return {
37
- id: 'balances',
38
+ id: safeString`balances`,
38
39
  definitions: [
39
40
  {
40
41
  definition: walletConfig?.importReport ? mockableBalancesDefinition : balancesDefinition,
package/module/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { safeString } from '@exodus/safe-string'
2
2
  import { isNumberUnit } from '@exodus/currency'
3
3
  import { TxSet } from '@exodus/models'
4
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
4
5
  import lodash from 'lodash'
5
6
  import { difference as getChanges } from '@exodus/atoms'
6
- import { difference, flattenToPaths, pick, set } from '@exodus/basic-utils'
7
+ import { difference, pick, set, mapValues } from '@exodus/basic-utils'
7
8
  import makeConcurrent from 'make-concurrent'
8
9
  import { processAssetBalances, validateBalances } from './balances-utils.js'
9
10
  import defaultConfig from '../default-config.js'
@@ -41,8 +42,7 @@ class Balances {
41
42
 
42
43
  #balances = Object.create(null)
43
44
  #balancesAtom
44
- #accountStatesAtom
45
- #txLogsAtom
45
+ #blockchainMetadataAtom
46
46
  #feeDataAtom
47
47
  #enabledWalletAccountsAtom
48
48
  #loaded = false
@@ -55,8 +55,7 @@ class Balances {
55
55
  config,
56
56
  logger,
57
57
  balancesAtom,
58
- txLogsAtom,
59
- accountStatesAtom,
58
+ blockchainMetadataAtom,
60
59
  feeDataAtom,
61
60
  errorTracking,
62
61
  }) {
@@ -66,8 +65,7 @@ class Balances {
66
65
  this.#balanceFieldsConfig = config?.balanceFieldsConfig || defaultConfig.balanceFieldsConfig
67
66
  this.#balanceFields = config?.balanceFields || defaultConfig.balanceFields
68
67
  this.#balancesAtom = balancesAtom
69
- this.#accountStatesAtom = accountStatesAtom
70
- this.#txLogsAtom = txLogsAtom
68
+ this.#blockchainMetadataAtom = blockchainMetadataAtom
71
69
  this.#feeDataAtom = feeDataAtom
72
70
  this.#enabledWalletAccountsAtom = enabledWalletAccountsAtom
73
71
  this.#errorTracking = errorTracking
@@ -92,7 +90,7 @@ class Balances {
92
90
 
93
91
  // these share a queue
94
92
  this.#updateAssetSource = shareQueue(this.#updateAssetSource)
95
- this.#handleTxLogUpdate = shareQueue(this.#handleTxLogUpdate)
93
+ this.#handleBlockchainMetadataUpdate = shareQueue(this.#handleBlockchainMetadataUpdate)
96
94
  this.#handleFeeDataUpdate = shareQueue(this.#handleFeeDataUpdate)
97
95
  this.#recompute = shareQueue(this.#recompute)
98
96
  this.#onAssetsChanged = shareQueue(this.#onAssetsChanged)
@@ -111,16 +109,17 @@ class Balances {
111
109
  this.#enabledWalletAccountsAtom.observe((payload) => {
112
110
  this.#setWalletAccounts(Object.keys(payload))
113
111
  }),
114
- this.#accountStatesAtom.observe(({ changes }) =>
115
- flattenToPaths(changes).forEach(([walletAccount, assetName, accountState]) =>
116
- this.#handleAccountStateUpdate({ walletAccount, assetName, accountState })
117
- )
118
- ),
119
- this.#txLogsAtom.observe(({ changes }) =>
120
- flattenToPaths(changes).forEach(([walletAccount, assetName, txLog]) =>
121
- this.#handleTxLogUpdate({ walletAccount, assetName, txLog })
112
+ this.#blockchainMetadataAtom.observe(({ accountStateChanges, txLogChanges }) => {
113
+ this.#flattenToBlockchainMetadataObjects({ accountStateChanges, txLogChanges }).forEach(
114
+ ({ walletAccount, assetName, accountState, txLogsByAssetName }) =>
115
+ this.#handleBlockchainMetadataUpdate({
116
+ walletAccount,
117
+ assetName,
118
+ accountState,
119
+ txLogsByAssetName,
120
+ })
122
121
  )
123
- ),
122
+ }),
124
123
  this.#feeDataAtom.observe((changes) => {
125
124
  Object.entries(changes).forEach(([assetName, feeData]) => {
126
125
  const asset = this.#getAsset(assetName).baseAsset
@@ -146,18 +145,34 @@ class Balances {
146
145
 
147
146
  #getAssets = () => this.#assetsModule.getAssets()
148
147
 
149
- #handleAccountStateUpdate = async (opts) => {
150
- if (!this.#ready) {
151
- this.#logger.log('ignore update from account-state event.', { isReady: this.#ready })
152
- return
153
- }
148
+ #flattenToBlockchainMetadataObjects = ({
149
+ accountStateChanges = Object.create(null),
150
+ txLogChanges = Object.create(null),
151
+ }) => {
152
+ const byBaseAssetName = mapValues(accountStateChanges, (accountStates) =>
153
+ mapValues(accountStates, (accountState) => ({ accountState }))
154
+ )
154
155
 
155
- return this.#updateAssetSource(opts)
156
+ Object.entries(txLogChanges).forEach(([walletAccount, txLogs]) => {
157
+ Object.entries(txLogs).map(([assetName, txLog]) => {
158
+ const { baseAsset } = this.#getAsset(assetName)
159
+ set(byBaseAssetName, [walletAccount, baseAsset.name, 'txLogsByAssetName', assetName], txLog)
160
+ })
161
+ })
162
+
163
+ return Object.entries(byBaseAssetName).flatMap(([walletAccount, baseAssets]) =>
164
+ Object.entries(baseAssets).map(([baseAssetName, { accountState, txLogsByAssetName }]) => ({
165
+ walletAccount,
166
+ assetName: baseAssetName,
167
+ ...(accountState && { accountState }),
168
+ ...(txLogsByAssetName && { txLogsByAssetName }),
169
+ }))
170
+ )
156
171
  }
157
172
 
158
- #handleTxLogUpdate = (opts) => {
173
+ #handleBlockchainMetadataUpdate = (opts) => {
159
174
  if (!this.#ready) {
160
- this.#logger.log('ignore update from tx-log event.', { isReady: this.#ready })
175
+ this.#logger.log('ignore update from blockchain-metadata event.', { isReady: this.#ready })
161
176
  return
162
177
  }
163
178
 
@@ -298,15 +313,15 @@ class Balances {
298
313
  accountState: providedAccountState = null,
299
314
  txLogsByAssetName: providedTxLogsByAssetName = {},
300
315
  }) => {
301
- const { value: txLogsByAssetSource } = await this.#txLogsAtom.get()
316
+ const {
317
+ value: { txs: txLogsByAssetSource, accountStates },
318
+ } = await this.#blockchainMetadataAtom.get()
302
319
  let accountState = providedAccountState
303
320
 
304
321
  const baseAsset = this.#getAsset(assetName).baseAsset
305
322
 
306
323
  if (!accountState) {
307
- const assetNameToUse = baseAsset.name
308
- const { value } = await this.#accountStatesAtom.get()
309
- const cached = value[walletAccount]?.[assetNameToUse]
324
+ const cached = accountStates[walletAccount]?.[baseAsset.name]
310
325
  if (cached) {
311
326
  accountState = cached
312
327
  } else {
@@ -343,7 +358,7 @@ class Balances {
343
358
  assetName,
344
359
  walletAccount,
345
360
  accountState: providedAccountState,
346
- txLog: providedTxLog,
361
+ txLogsByAssetName: providedTxLogsByAssetName,
347
362
  feeData: providedFeeData,
348
363
  }) => {
349
364
  if (!this.#isSupportedAsset(assetName)) return
@@ -359,9 +374,7 @@ class Balances {
359
374
  walletAccount,
360
375
  feeData: providedFeeData,
361
376
  accountState: providedAccountState,
362
- txLogsByAssetName: {
363
- [assetName]: providedTxLog,
364
- },
377
+ txLogsByAssetName: providedTxLogsByAssetName,
365
378
  })
366
379
 
367
380
  const accountBalances = this.#balances[walletAccount]
@@ -430,8 +443,7 @@ const balancesDefinition = {
430
443
  'config',
431
444
  'logger',
432
445
  'balancesAtom',
433
- 'txLogsAtom',
434
- 'accountStatesAtom',
446
+ 'blockchainMetadataAtom',
435
447
  'feeDataAtom',
436
448
  'errorTracking',
437
449
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/balances",
3
- "version": "13.13.1",
3
+ "version": "13.14.0",
4
4
  "description": "Tracks crypto balances across enabled wallet accounts.",
5
5
  "author": "Exodus Movement, Inc.",
6
6
  "license": "MIT",
@@ -33,7 +33,7 @@
33
33
  "url": "https://github.com/ExodusMovement/exodus-hydra/issues?q=is%3Aissue+is%3Aopen+label%3Abalances"
34
34
  },
35
35
  "dependencies": {
36
- "@exodus/atoms": "^9.0.0",
36
+ "@exodus/atoms": "^10.0.0",
37
37
  "@exodus/basic-utils": "^3.0.1",
38
38
  "@exodus/core-selectors": "^4.0.0",
39
39
  "@exodus/currency": "^5.0.2",
@@ -49,24 +49,25 @@
49
49
  "reselect": "^3.0.1"
50
50
  },
51
51
  "peerDependencies": {
52
- "@exodus/blockchain-metadata": "^15.4.0"
52
+ "@exodus/blockchain-metadata": "^17.0.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@exodus/assets": "^11.0.0",
56
56
  "@exodus/assets-base": "^10.0.0",
57
- "@exodus/assets-feature": "^8.4.0",
57
+ "@exodus/assets-feature": "^8.7.1",
58
58
  "@exodus/available-assets": "^8.8.0",
59
59
  "@exodus/bitcoin-meta": "^2.0.0",
60
- "@exodus/blockchain-metadata": "^15.14.0",
60
+ "@exodus/blockchain-metadata": "^17.1.0",
61
61
  "@exodus/ethereum-meta": "^2.0.0",
62
62
  "@exodus/fee-data-monitors": "^5.0.4",
63
63
  "@exodus/storage-memory": "^2.3.0",
64
- "@exodus/wallet-accounts": "^18.0.0",
64
+ "@exodus/wallet-accounts": "^20.2.1",
65
65
  "events": "^3.3.0",
66
66
  "redux": "^4.2.1"
67
67
  },
68
68
  "publishConfig": {
69
- "access": "public"
69
+ "access": "public",
70
+ "provenance": false
70
71
  },
71
- "gitHead": "089fd1aa6a27487f0dd0de41b5235676545a9bdc"
72
+ "gitHead": "1535d3792c06b4c96e94e0787f75d40fbb5cca1f"
72
73
  }
package/redux/index.js CHANGED
@@ -2,6 +2,7 @@ import id from './id.js'
2
2
  import initialState from './initial-state.js'
3
3
  import selectorDefinitions from './selectors/index.js'
4
4
  import { mapValues } from '@exodus/basic-utils'
5
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
5
6
  import lodash from 'lodash'
6
7
  import helper from './multi-account-helper.js'
7
8
  import { setAccounts } from '@exodus/multi-account-redux/src/common.js'
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
  import { MY_STATE } from '@exodus/redux-dependency-injection'
3
4
  import { createSelector } from 'reselect'
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
  import { createSelector } from 'reselect'
3
4
  import { MY_STATE } from '@exodus/redux-dependency-injection'
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
  import { createSelector } from 'reselect'
3
4
  import { isNumberUnit } from '@exodus/currency'
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
  import { createSelector } from 'reselect'
3
4
  import { getUnconfirmedBalance } from './get-unconfirmed-balance.js'
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
  import defaultConfig from '../../default-config.js'
3
4
 
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
 
3
4
  const { memoize } = lodash // eslint-disable-line @exodus/basic-utils/prefer-basic-utils
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
  import { createSelector } from 'reselect'
3
4
  import { MY_STATE } from '@exodus/redux-dependency-injection'
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line no-restricted-imports -- TODO: Fix this the next time the file is edited.
1
2
  import lodash from 'lodash'
2
3
  import { createSelector } from 'reselect'
3
4