@exodus/balances 13.16.0 → 13.17.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 (134) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +1 -1
  3. package/atoms/asset-names-with-balance.d.ts +1 -0
  4. package/atoms/asset-names-with-balance.js +1 -40
  5. package/atoms/asset-total-wallet-amounts.d.ts +1 -0
  6. package/atoms/asset-total-wallet-amounts.js +1 -47
  7. package/atoms/assets-with-balance-count.d.ts +1 -0
  8. package/atoms/assets-with-balance-count.js +1 -8
  9. package/atoms/balances.d.ts +1 -0
  10. package/atoms/balances.js +1 -6
  11. package/atoms/funded-wallet-accounts.d.ts +1 -0
  12. package/atoms/funded-wallet-accounts.js +1 -25
  13. package/atoms/has-balance.d.ts +1 -0
  14. package/atoms/has-balance.js +1 -31
  15. package/atoms/index.d.ts +8 -0
  16. package/atoms/index.js +8 -54
  17. package/default-config.d.ts +1 -0
  18. package/default-config.js +1 -26
  19. package/index.d.ts +2 -35
  20. package/index.js +1 -65
  21. package/lib/atoms/asset-names-with-balance.d.ts +5 -0
  22. package/lib/atoms/asset-names-with-balance.js +31 -0
  23. package/lib/atoms/asset-total-wallet-amounts.d.ts +5 -0
  24. package/lib/atoms/asset-total-wallet-amounts.js +39 -0
  25. package/lib/atoms/assets-with-balance-count.d.ts +5 -0
  26. package/lib/atoms/assets-with-balance-count.js +6 -0
  27. package/lib/atoms/balances.d.ts +3 -0
  28. package/lib/atoms/balances.js +3 -0
  29. package/lib/atoms/funded-wallet-accounts.d.ts +8 -0
  30. package/lib/atoms/funded-wallet-accounts.js +19 -0
  31. package/lib/atoms/has-balance.d.ts +7 -0
  32. package/lib/atoms/has-balance.js +21 -0
  33. package/lib/atoms/index.d.ts +54 -0
  34. package/lib/atoms/index.js +48 -0
  35. package/lib/default-config.d.ts +3 -0
  36. package/lib/default-config.js +24 -0
  37. package/lib/index.d.ts +187 -0
  38. package/lib/index.js +49 -0
  39. package/lib/module/balances-utils.d.ts +13 -0
  40. package/lib/module/balances-utils.js +70 -0
  41. package/lib/module/index.d.ts +11 -0
  42. package/lib/module/index.js +367 -0
  43. package/lib/module/mockable-balances.d.ts +16 -0
  44. package/lib/module/mockable-balances.js +21 -0
  45. package/lib/module/types.d.ts +80 -0
  46. package/lib/module/types.js +1 -0
  47. package/lib/plugins/analytics.d.ts +18 -0
  48. package/lib/plugins/analytics.js +30 -0
  49. package/lib/plugins/index.d.ts +35 -0
  50. package/lib/plugins/index.js +22 -0
  51. package/lib/plugins/lifecycle.d.ts +14 -0
  52. package/lib/plugins/lifecycle.js +32 -0
  53. package/lib/redux/id.d.ts +2 -0
  54. package/lib/redux/id.js +1 -0
  55. package/lib/redux/index.d.ts +588 -0
  56. package/lib/redux/index.js +53 -0
  57. package/lib/redux/initial-state.d.ts +3 -0
  58. package/lib/redux/initial-state.js +6 -0
  59. package/lib/redux/multi-account-helper.d.ts +463 -0
  60. package/lib/redux/multi-account-helper.js +23 -0
  61. package/lib/redux/selectors/by-asset.d.ts +12 -0
  62. package/lib/redux/selectors/by-asset.js +19 -0
  63. package/lib/redux/selectors/create-balance-selectors.d.ts +70 -0
  64. package/lib/redux/selectors/create-balance-selectors.js +32 -0
  65. package/lib/redux/selectors/create-balances.d.ts +11 -0
  66. package/lib/redux/selectors/create-balances.js +13 -0
  67. package/lib/redux/selectors/create-fuel-threshold.d.ts +19 -0
  68. package/lib/redux/selectors/create-fuel-threshold.js +22 -0
  69. package/lib/redux/selectors/create-requires-fuel-threshold.d.ts +19 -0
  70. package/lib/redux/selectors/create-requires-fuel-threshold.js +29 -0
  71. package/lib/redux/selectors/create-unconfirmed-balance.d.ts +15 -0
  72. package/lib/redux/selectors/create-unconfirmed-balance.js +14 -0
  73. package/lib/redux/selectors/get-balance-for-field.d.ts +14 -0
  74. package/lib/redux/selectors/get-balance-for-field.js +19 -0
  75. package/lib/redux/selectors/get-balance-selectors.d.ts +68 -0
  76. package/lib/redux/selectors/get-balance-selectors.js +32 -0
  77. package/lib/redux/selectors/get-balances.d.ts +10 -0
  78. package/lib/redux/selectors/get-balances.js +13 -0
  79. package/lib/redux/selectors/get-unconfirmed-balance.d.ts +19 -0
  80. package/lib/redux/selectors/get-unconfirmed-balance.js +27 -0
  81. package/lib/redux/selectors/index.d.ts +339 -0
  82. package/lib/redux/selectors/index.js +22 -0
  83. package/lib/redux/types.d.ts +45 -0
  84. package/lib/redux/types.js +1 -0
  85. package/lib/report/index.d.ts +34 -0
  86. package/lib/report/index.js +51 -0
  87. package/lib/types.d.ts +24 -0
  88. package/lib/types.js +1 -0
  89. package/module/balances-utils.d.ts +1 -0
  90. package/module/balances-utils.js +1 -86
  91. package/module/index.d.ts +2 -0
  92. package/module/index.js +2 -490
  93. package/module/mockable-balances.d.ts +1 -0
  94. package/module/mockable-balances.js +1 -27
  95. package/package.json +36 -29
  96. package/plugins/analytics.d.ts +1 -0
  97. package/plugins/analytics.js +1 -48
  98. package/plugins/index.d.ts +4 -0
  99. package/plugins/index.js +4 -24
  100. package/plugins/lifecycle.d.ts +1 -0
  101. package/plugins/lifecycle.js +1 -40
  102. package/redux/id.d.ts +1 -0
  103. package/redux/id.js +1 -1
  104. package/redux/index.d.ts +2 -0
  105. package/redux/index.js +1 -61
  106. package/redux/initial-state.d.ts +1 -0
  107. package/redux/initial-state.js +1 -8
  108. package/redux/multi-account-helper.d.ts +1 -0
  109. package/redux/multi-account-helper.js +1 -14
  110. package/redux/selectors/by-asset.d.ts +1 -0
  111. package/redux/selectors/by-asset.js +1 -25
  112. package/redux/selectors/create-balance-selectors.d.ts +1 -0
  113. package/redux/selectors/create-balance-selectors.js +1 -48
  114. package/redux/selectors/create-balances.d.ts +1 -0
  115. package/redux/selectors/create-balances.js +1 -26
  116. package/redux/selectors/create-fuel-threshold.d.ts +1 -0
  117. package/redux/selectors/create-fuel-threshold.js +1 -32
  118. package/redux/selectors/create-requires-fuel-threshold.d.ts +1 -0
  119. package/redux/selectors/create-requires-fuel-threshold.js +1 -59
  120. package/redux/selectors/create-unconfirmed-balance.d.ts +1 -0
  121. package/redux/selectors/create-unconfirmed-balance.js +1 -28
  122. package/redux/selectors/get-balance-for-field.d.ts +1 -0
  123. package/redux/selectors/get-balance-for-field.js +1 -34
  124. package/redux/selectors/get-balance-selectors.d.ts +1 -0
  125. package/redux/selectors/get-balance-selectors.js +1 -42
  126. package/redux/selectors/get-balances.d.ts +1 -0
  127. package/redux/selectors/get-balances.js +1 -24
  128. package/redux/selectors/get-unconfirmed-balance.d.ts +2 -0
  129. package/redux/selectors/get-unconfirmed-balance.js +2 -43
  130. package/redux/selectors/index.d.ts +1 -0
  131. package/redux/selectors/index.js +1 -23
  132. package/report/index.d.ts +1 -0
  133. package/report/index.js +1 -61
  134. package/tsconfig.json +0 -4
package/module/index.js CHANGED
@@ -1,490 +1,2 @@
1
- import { safeString } from '@exodus/safe-string'
2
- import { TxSet } from '@exodus/models'
3
- import { difference as getChanges } from '@exodus/atoms'
4
- import { difference, pick, set, mapValues } from '@exodus/basic-utils'
5
- import makeConcurrent from 'make-concurrent'
6
- import { processAssetBalances, validateBalances } from './balances-utils.js'
7
- import defaultConfig from '../default-config.js'
8
-
9
- const zipObject = (keys, values) => {
10
- const result = Object.create(null)
11
- for (const [i, key] of keys.entries()) {
12
- result[key] = values[i]
13
- }
14
-
15
- return result
16
- }
17
-
18
- export const cloneBalances = (balances) => {
19
- const result = Object.create(null)
20
- for (const walletAccount of Object.keys(balances)) {
21
- const accountBalances = balances[walletAccount]
22
- const clonedAccount = Object.create(null)
23
- for (const assetName of Object.keys(accountBalances)) {
24
- clonedAccount[assetName] = { ...accountBalances[assetName] }
25
- }
26
-
27
- result[walletAccount] = clonedAccount
28
- }
29
-
30
- return result
31
- }
32
-
33
- const zipNonNull = (keys, values) => {
34
- const result = Object.create(null)
35
- for (const [i, key] of keys.entries()) {
36
- if (values[i] != null) result[key] = values[i]
37
- }
38
-
39
- return result
40
- }
41
-
42
- class Balances {
43
- #logger
44
- #assetsModule
45
- #availableAssetNamesAtom
46
- #availableAssetNames = []
47
- #balanceFieldsConfig
48
- #balanceFields
49
- #walletAccounts = [] // string[]
50
- #ready = false
51
-
52
- #balances = Object.create(null)
53
- #balancesAtom
54
- #blockchainMetadataAtom
55
- #feeDataAtom
56
- #enabledWalletAccountsAtom
57
- #loaded = false
58
- #subscriptions = []
59
- #errorTracking
60
- constructor({
61
- assetsModule,
62
- availableAssetNamesAtom,
63
- enabledWalletAccountsAtom,
64
- config,
65
- logger,
66
- balancesAtom,
67
- blockchainMetadataAtom,
68
- feeDataAtom,
69
- errorTracking,
70
- }) {
71
- this.#logger = logger
72
- this.#assetsModule = assetsModule
73
- this.#availableAssetNamesAtom = availableAssetNamesAtom
74
- this.#balanceFieldsConfig = config?.balanceFieldsConfig || defaultConfig.balanceFieldsConfig
75
- this.#balanceFields = config?.balanceFields || defaultConfig.balanceFields
76
- this.#balancesAtom = balancesAtom
77
- this.#blockchainMetadataAtom = blockchainMetadataAtom
78
- this.#feeDataAtom = feeDataAtom
79
- this.#enabledWalletAccountsAtom = enabledWalletAccountsAtom
80
- this.#errorTracking = errorTracking
81
-
82
- const shareQueue =
83
- (fn) =>
84
- (...args) => {
85
- if (!this.#ready) return
86
-
87
- this.#pushIntoSharedQueue(() => fn.apply(this, args))
88
- }
89
-
90
- const swallowErrors =
91
- (fn, method) =>
92
- async (...args) => {
93
- try {
94
- return await fn(...args)
95
- } catch (error) {
96
- this.#errorTracking.track({ error: new Error(method, { cause: error }) })
97
- }
98
- }
99
-
100
- // these share a queue
101
- this.#updateAssetSource = shareQueue(this.#updateAssetSource)
102
- this.#handleBlockchainMetadataUpdate = shareQueue(this.#handleBlockchainMetadataUpdate)
103
- this.#handleFeeDataUpdate = shareQueue(this.#handleFeeDataUpdate)
104
- this.#recompute = shareQueue(this.#recompute)
105
- this.#onAssetsChanged = shareQueue(this.#onAssetsChanged)
106
- this.#getBalancesForAssetSource = swallowErrors(
107
- this.#getBalancesForAssetSource,
108
- safeString`getBalancesForAssetSource`
109
- )
110
- this.#setWalletAccounts = swallowErrors(this.#setWalletAccounts, safeString`setWalletAccounts`)
111
- }
112
-
113
- load = async () => {
114
- if (this.#loaded) return
115
- this.#loaded = true
116
-
117
- this.#subscriptions.push(
118
- this.#enabledWalletAccountsAtom.observe((payload) => {
119
- this.#setWalletAccounts(Object.keys(payload))
120
- }),
121
- this.#blockchainMetadataAtom.observe(({ accountStateChanges, txLogChanges }) => {
122
- this.#flattenToBlockchainMetadataObjects({ accountStateChanges, txLogChanges }).forEach(
123
- ({ walletAccount, assetName, accountState, txLogsByAssetName }) =>
124
- this.#handleBlockchainMetadataUpdate({
125
- walletAccount,
126
- assetName,
127
- accountState,
128
- txLogsByAssetName,
129
- })
130
- )
131
- }),
132
- this.#feeDataAtom.observe((changes) => {
133
- Object.entries(changes).forEach(([assetName, feeData]) => {
134
- const asset = this.#getAsset(assetName).baseAsset
135
- if (asset.api?.features?.balancesRequireFeeData) {
136
- this.#walletAccounts.forEach((walletAccount) => {
137
- return this.#handleFeeDataUpdate({ walletAccount, assetName, feeData })
138
- })
139
- }
140
- })
141
- }),
142
-
143
- getChanges(this.#availableAssetNamesAtom).observe(({ previous = [], current }) => {
144
- this.#availableAssetNames = current
145
- const added = difference(current, previous)
146
- const removed = difference(previous, current)
147
- if (added.length > 0 || removed.length > 0) this.#onAssetsChanged({ added, removed })
148
- })
149
- )
150
-
151
- this.#availableAssetNames = await this.#availableAssetNamesAtom.get()
152
- }
153
-
154
- #getAsset = (assetName) => this.#assetsModule.getAsset(assetName)
155
-
156
- #getAssets = () => this.#assetsModule.getAssets()
157
-
158
- #flattenToBlockchainMetadataObjects = ({
159
- accountStateChanges = Object.create(null),
160
- txLogChanges = Object.create(null),
161
- }) => {
162
- const byBaseAssetName = mapValues(accountStateChanges, (accountStates) =>
163
- mapValues(accountStates, (accountState) => ({ accountState }))
164
- )
165
-
166
- Object.entries(txLogChanges).forEach(([walletAccount, txLogs]) => {
167
- Object.entries(txLogs).map(([assetName, txLog]) => {
168
- const { baseAsset } = this.#getAsset(assetName)
169
- set(byBaseAssetName, [walletAccount, baseAsset.name, 'txLogsByAssetName', assetName], txLog)
170
- })
171
- })
172
-
173
- return Object.entries(byBaseAssetName).flatMap(([walletAccount, baseAssets]) =>
174
- Object.entries(baseAssets).map(([baseAssetName, { accountState, txLogsByAssetName }]) => ({
175
- walletAccount,
176
- assetName: baseAssetName,
177
- ...(accountState && { accountState }),
178
- ...(txLogsByAssetName && { txLogsByAssetName }),
179
- }))
180
- )
181
- }
182
-
183
- #handleBlockchainMetadataUpdate = (opts) => {
184
- if (!this.#ready) {
185
- this.#logger.log('ignore update from blockchain-metadata event.', { isReady: this.#ready })
186
- return
187
- }
188
-
189
- return this.#updateAssetSource(opts)
190
- }
191
-
192
- #handleFeeDataUpdate = (opts) => {
193
- if (!this.#ready) {
194
- this.#logger.log('ignore update from fee-data event.', { isReady: this.#ready })
195
- return
196
- }
197
-
198
- return this.#updateAssetSource(opts)
199
- }
200
-
201
- #pushIntoSharedQueue = makeConcurrent((fn) => fn(), { concurrency: 1 })
202
-
203
- #reset = ({ walletAccounts = this.#walletAccounts } = {}) => {
204
- walletAccounts.forEach((walletAccount) => {
205
- this.#balances[walletAccount] = Object.create(null)
206
- })
207
- }
208
-
209
- #getBalancesForAssetSource = async ({ walletAccount, assetName }) => {
210
- const metadata = await this.#loadBlockchainMetadataForBalance({ walletAccount, assetName })
211
- return this.#getBalancesFromBlockchainMetadata({
212
- assetNames: this.#getRelatedAssetNames(assetName),
213
- ...metadata,
214
- })
215
- }
216
-
217
- // It adds both old and new balances to the response.
218
- // Ideally, selectors should start using the new ones (total, spendable, etc)
219
-
220
- #getBalancesFromBlockchainMetadata = ({
221
- assetNames,
222
- accountState,
223
- feeData,
224
- txLogsByAssetName,
225
- }) => {
226
- // in the future accountState may have all the balances and we won't need to rely on txLog
227
- // for now, accountState-based balance has both the baseAsset and token balances while txLog is per asset/token
228
- const getAssetBalances = (assetName) => {
229
- const asset = this.#getAsset(assetName)
230
- const txLog = txLogsByAssetName[assetName]
231
-
232
- const zero = asset.currency.ZERO
233
-
234
- const apiBalances = asset.api.getBalances({ asset, accountState, txLog, feeData })
235
- const balances = processAssetBalances({
236
- balanceFieldsConfig: this.#balanceFieldsConfig,
237
- balances: apiBalances || Object.create(null),
238
- zero,
239
- })
240
- return validateBalances({
241
- balances: pick(balances, this.#balanceFields),
242
- asset,
243
- zero,
244
- logger: this.#logger,
245
- emitter: this,
246
- })
247
- }
248
-
249
- const balances = assetNames.map(getAssetBalances)
250
- return zipNonNull(assetNames, balances)
251
- }
252
-
253
- #getBalancesForWalletAccount = async ({ walletAccount }) => {
254
- const assetNames = this.#getSupportedAssetNames()
255
- const results = await Promise.allSettled(
256
- assetNames.map((assetName) => this.#getBalancesForAssetSource({ assetName, walletAccount }))
257
- )
258
-
259
- const balances = results.map(({ value }) => value).filter(Boolean)
260
- return Object.assign(Object.create(null), ...balances)
261
- }
262
-
263
- #load = async ({ walletAccount }) => {
264
- this.#logger.log(`loading walletAccount ${walletAccount}`)
265
- this.#balances[walletAccount] = await this.#getBalancesForWalletAccount({ walletAccount })
266
- }
267
-
268
- #setWalletAccounts = async (walletAccounts) => {
269
- const added = difference(walletAccounts, this.#walletAccounts)
270
- const removed = difference(this.#walletAccounts, walletAccounts)
271
- if (added.length === 0 && removed.length === 0) return
272
-
273
- this.#walletAccounts = walletAccounts
274
- this.#ready = true
275
- if (removed.length > 0) {
276
- this.#logger.log('recomputing after removing walletAccounts', removed)
277
- removed.forEach((walletAccount) => {
278
- delete this.#balances[walletAccount]
279
- })
280
- }
281
-
282
- if (added.length > 0) {
283
- this.#logger.log('recomputing after adding walletAccounts', added)
284
- await this.#recompute({ walletAccounts: added })
285
- } else {
286
- this.#flush()
287
- }
288
- }
289
-
290
- #onAssetsChanged = async ({ added, removed }) => {
291
- if (removed.length > 0) {
292
- for (const walletAccount of this.#walletAccounts) {
293
- for (const assetName of removed) {
294
- delete this.#balances[walletAccount]?.[assetName]
295
- }
296
- }
297
- }
298
-
299
- const newSupportedAssets = added.filter(this.#isSupportedAsset)
300
- const missingAssets = newSupportedAssets.filter(
301
- (name) =>
302
- !this.#walletAccounts.every((walletAccount) => !!this.#balances?.[walletAccount]?.[name])
303
- )
304
-
305
- if (missingAssets.length === 0) {
306
- if (removed.length > 0) this.#flush()
307
- return
308
- }
309
-
310
- this.#logger.log('computing balances for newly available assets', missingAssets)
311
- await Promise.all(
312
- this.#walletAccounts.map(async (walletAccount) => {
313
- const balances = await Promise.all(
314
- missingAssets.map((assetName) =>
315
- this.#getBalancesForAssetSource({ assetName, walletAccount })
316
- )
317
- )
318
- if (this.#balances[walletAccount]) {
319
- Object.assign(this.#balances[walletAccount], ...balances)
320
- }
321
- })
322
- )
323
-
324
- this.#flush()
325
- }
326
-
327
- #isSupportedAsset = (assetName) => {
328
- if (!this.#availableAssetNames.includes(assetName)) return false
329
- const asset = this.#getAsset(assetName)
330
- return asset && !asset.isCombined
331
- }
332
-
333
- #getSupportedAssetNames = () => Object.keys(this.#getAssets()).filter(this.#isSupportedAsset)
334
-
335
- #getRelatedAssetNames = (assetName) => {
336
- const asset = this.#assetsModule.getAsset(assetName)
337
- if (asset.baseAsset.name === assetName) {
338
- return [assetName, ...this.#assetsModule.getTokenNames(assetName)].filter(
339
- this.#isSupportedAsset
340
- )
341
- }
342
-
343
- return [assetName]
344
- }
345
-
346
- #loadBlockchainMetadataForBalance = async ({
347
- assetName,
348
- walletAccount,
349
- feeData: providedFeeData, // it's optional, undefined when balancesRequireFeeData is disabled
350
- accountState: providedAccountState = null,
351
- txLogsByAssetName: providedTxLogsByAssetName = {},
352
- }) => {
353
- const {
354
- value: { txs: txLogsByAssetSource, accountStates },
355
- } = await this.#blockchainMetadataAtom.get()
356
- let accountState = providedAccountState
357
-
358
- const baseAsset = this.#getAsset(assetName).baseAsset
359
-
360
- if (!accountState) {
361
- const cached = accountStates[walletAccount]?.[baseAsset.name]
362
- if (cached) {
363
- accountState = cached
364
- } else {
365
- const AccountState = baseAsset.api?.createAccountState?.()
366
- accountState = AccountState?.create()
367
- }
368
- }
369
-
370
- let feeData = providedFeeData
371
- if (!feeData && baseAsset.api?.features?.balancesRequireFeeData) {
372
- const fees = await this.#feeDataAtom.get()
373
- feeData = fees[baseAsset.name]
374
- }
375
-
376
- const assetNames = this.#getRelatedAssetNames(assetName)
377
- const txLogsByAssetName = zipObject(
378
- assetNames,
379
- assetNames.map(
380
- (assetName) =>
381
- providedTxLogsByAssetName[assetName] ||
382
- txLogsByAssetSource[walletAccount]?.[assetName] ||
383
- TxSet.EMPTY
384
- )
385
- )
386
-
387
- return {
388
- feeData,
389
- accountState,
390
- txLogsByAssetName,
391
- }
392
- }
393
-
394
- #updateAssetSource = async ({
395
- assetName,
396
- walletAccount,
397
- accountState: providedAccountState,
398
- txLogsByAssetName: providedTxLogsByAssetName,
399
- feeData: providedFeeData,
400
- }) => {
401
- if (!this.#isSupportedAsset(assetName)) return
402
-
403
- if (!this.#walletAccounts.includes(walletAccount)) {
404
- this.#logger.warn(`unexpected update for disabled walletAccount ${walletAccount}`)
405
- return
406
- }
407
-
408
- const { accountState, txLogsByAssetName, feeData } =
409
- await this.#loadBlockchainMetadataForBalance({
410
- assetName,
411
- walletAccount,
412
- feeData: providedFeeData,
413
- accountState: providedAccountState,
414
- txLogsByAssetName: providedTxLogsByAssetName,
415
- })
416
-
417
- const accountBalances = this.#balances[walletAccount]
418
- const newBalances = this.#getBalancesFromBlockchainMetadata({
419
- assetNames: this.#getRelatedAssetNames(assetName),
420
- accountState,
421
- txLogsByAssetName,
422
- feeData,
423
- })
424
-
425
- const changes = Object.create(null)
426
- for (const subAssetName in newBalances) {
427
- const { currency } = this.#getAsset(subAssetName)
428
- const newAssetBalances = newBalances?.[subAssetName]
429
- // e.g. totalBalance or spendableBalance
430
- for (const field of this.#balanceFields) {
431
- const oldBalance = accountBalances?.[subAssetName]?.[field]
432
- const newBalance = newAssetBalances?.[field]
433
- if (!newBalance) continue
434
- if ((oldBalance || currency.ZERO).equals(newBalance)) continue
435
-
436
- this.#logger.log(`updating assetSource ${walletAccount}:${assetName}`)
437
- set(changes, [walletAccount, subAssetName, field], {
438
- from: oldBalance ?? currency.ZERO,
439
- to: newBalance,
440
- })
441
-
442
- set(accountBalances, [subAssetName, field], newBalance)
443
- }
444
- }
445
-
446
- if (Object.keys(changes).length > 0) this.#flush({ changes })
447
- }
448
-
449
- #recompute = async ({ walletAccounts = this.#walletAccounts } = {}) => {
450
- this.#reset({ walletAccounts })
451
- await Promise.all(walletAccounts.map((walletAccount) => this.#load({ walletAccount })))
452
- this.#flush()
453
- }
454
-
455
- #flush = ({ changes } = {}) => {
456
- const balances = cloneBalances(this.#balances)
457
-
458
- if (Object.keys(balances).length === 0) return // wait till `load` finished
459
-
460
- this.#logger.log('balances updated')
461
- this.#balancesAtom.set({ balances, changes })
462
- }
463
-
464
- stop = () => {
465
- this.#subscriptions.forEach((unsubscribe) => unsubscribe())
466
- this.#subscriptions = []
467
- }
468
- }
469
-
470
- const createBalances = (opts) => new Balances(opts)
471
-
472
- const balancesDefinition = {
473
- id: 'balances',
474
- type: 'module',
475
- factory: createBalances,
476
- dependencies: [
477
- 'assetsModule',
478
- 'availableAssetNamesAtom',
479
- 'enabledWalletAccountsAtom',
480
- 'config',
481
- 'logger',
482
- 'balancesAtom',
483
- 'blockchainMetadataAtom',
484
- 'feeDataAtom',
485
- 'errorTracking',
486
- ],
487
- public: true,
488
- }
489
-
490
- export default balancesDefinition
1
+ export { default } from '../lib/module/index.js'
2
+ export { cloneBalances } from '../lib/module/index.js'
@@ -0,0 +1 @@
1
+ export { default } from '../lib/module/mockable-balances.js'
@@ -1,27 +1 @@
1
- class Balances {
2
- #balancesAtom
3
- #configBalances
4
-
5
- constructor({ config, balancesAtom }) {
6
- this.#balancesAtom = balancesAtom
7
- this.#configBalances = config.importReport
8
- }
9
-
10
- load = async () => {
11
- this.#balancesAtom.set({ balances: this.#configBalances })
12
- }
13
-
14
- stop = () => {}
15
- }
16
-
17
- const createBalances = (opts) => new Balances(opts)
18
-
19
- const mockableBalancesDefinition = {
20
- id: 'balances',
21
- type: 'module',
22
- factory: createBalances,
23
- dependencies: ['config', 'balancesAtom'],
24
- public: true,
25
- }
26
-
27
- export default mockableBalancesDefinition
1
+ export { default } from '../lib/module/mockable-balances.js'
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "@exodus/balances",
3
- "version": "13.16.0",
3
+ "version": "13.17.0",
4
4
  "description": "Tracks crypto balances across enabled wallet accounts.",
5
5
  "author": "Exodus Movement, Inc.",
6
6
  "license": "MIT",
7
7
  "scripts": {
8
- "test": "run -T exodus-test --jest",
9
- "lint": "run -T eslint -c ../../eslint.config.mjs",
10
- "lint:fix": "yarn lint --fix",
11
- "format": "run -T prettier --write \"**/*.{json,md,yaml}\" --log-level warn",
12
- "format:check": "run -T prettier --check \"**/*.{json,md,yaml}\" --log-level warn"
8
+ "test": "pnpm run build && pnpm exec exodus-test --jest",
9
+ "typecheck": "pnpm run build && pnpm exec tsc -p tsconfig.json --noEmit",
10
+ "lint": "pnpm exec eslint -c ../../eslint.config.mjs",
11
+ "lint:fix": "pnpm run lint --fix",
12
+ "format": "pnpm exec prettier --write \"**/*.{json,md,yaml}\" --log-level warn",
13
+ "format:check": "pnpm exec prettier --check \"**/*.{json,md,yaml}\" --log-level warn",
14
+ "build": "pnpm exec tsc -p tsconfig.build.json",
15
+ "prepublishOnly": "pnpm -w run build --scope @exodus/balances"
13
16
  },
14
17
  "type": "module",
15
18
  "main": "index.js",
@@ -19,35 +22,38 @@
19
22
  "atoms",
20
23
  "default-config.js",
21
24
  "index.d.ts",
22
- "tsconfig.json",
23
25
  "report",
24
26
  "plugins",
25
27
  "redux",
26
28
  "README.md",
27
29
  "CHANGELOG.md",
28
- "!**/__tests__/**"
30
+ "!**/__tests__/**",
31
+ "lib",
32
+ "default-config.d.ts"
29
33
  ],
30
34
  "repository": {
31
35
  "type": "git",
32
- "url": "git+https://github.com/ExodusMovement/exodus-hydra.git"
36
+ "url": "git+https://github.com/ExodusMovement/exodus.git"
33
37
  },
34
- "homepage": "https://github.com/ExodusMovement/exodus-hydra/tree/master/features/balances",
38
+ "homepage": "https://github.com/ExodusMovement/exodus/tree/master/libs/features/balances",
35
39
  "bugs": {
36
- "url": "https://github.com/ExodusMovement/exodus-hydra/issues?q=is%3Aissue+is%3Aopen+label%3Abalances"
40
+ "url": "https://github.com/ExodusMovement/exodus/issues?q=is%3Aissue+is%3Aopen+label%3Abalances"
37
41
  },
38
42
  "dependencies": {
39
43
  "@exodus/asset-types": "^0.6.0",
40
- "@exodus/atoms": "^10.0.0",
41
- "@exodus/basic-utils": "^5.0.0",
42
- "@exodus/core-selectors": "^4.0.0",
43
- "@exodus/currency": "^6.0.0",
44
- "@exodus/errors": "^3.0.0",
45
- "@exodus/models": "^13.0.0",
46
- "@exodus/multi-account-redux": "^3.0.0",
47
- "@exodus/redux-dependency-injection": "^4.0.3",
48
- "@exodus/safe-string": "^1.1.0",
49
- "@exodus/typeforce": "^1.18.0",
50
- "@exodus/zod": "^3.24.2",
44
+ "@exodus/atoms": "^10.3.5",
45
+ "@exodus/basic-utils": "^5.2.1",
46
+ "@exodus/core-selectors": "^4.0.4",
47
+ "@exodus/currency": "^6.4.2",
48
+ "@exodus/errors": "^3.8.2",
49
+ "@exodus/models": "^13.3.3",
50
+ "@exodus/multi-account-redux": "^3.0.2",
51
+ "@exodus/redux-dependency-injection": "^5.0.0",
52
+ "@exodus/safe-string": "^1.5.1",
53
+ "@exodus/storage-interface": "^1.0.2",
54
+ "@exodus/typeforce": "^1.19.5",
55
+ "@exodus/zod": "^3.24.4",
56
+ "@types/lodash": "^4.17.0",
51
57
  "lodash": "^4.17.21",
52
58
  "make-concurrent": "^4.0.0",
53
59
  "reselect": "^3.0.1"
@@ -58,14 +64,15 @@
58
64
  "devDependencies": {
59
65
  "@exodus/assets": "^11.0.0",
60
66
  "@exodus/assets-base": "^12.0.0",
61
- "@exodus/assets-feature": "^9.2.2",
62
- "@exodus/available-assets": "^8.11.0",
67
+ "@exodus/assets-feature": "^9.7.1",
68
+ "@exodus/available-assets": "^8.11.3",
63
69
  "@exodus/bitcoin-meta": "^2.0.0",
64
- "@exodus/blockchain-metadata": "^17.1.1",
70
+ "@exodus/blockchain-metadata": "^17.2.2",
71
+ "@exodus/dependency-types": "^2.1.1",
65
72
  "@exodus/ethereum-meta": "^2.0.0",
66
- "@exodus/fee-data-monitors": "^5.0.5",
67
- "@exodus/storage-memory": "^2.4.0",
68
- "@exodus/wallet-accounts": "^20.4.4",
73
+ "@exodus/fee-data-monitors": "^5.0.6",
74
+ "@exodus/storage-memory": "^2.4.1",
75
+ "@exodus/wallet-accounts": "^20.4.12",
69
76
  "events": "^3.3.0",
70
77
  "redux": "^4.2.1"
71
78
  },
@@ -73,5 +80,5 @@
73
80
  "access": "public",
74
81
  "provenance": false
75
82
  },
76
- "gitHead": "135eb6bf020031cc2fb02da2e088767f3614350d"
83
+ "gitHead": "8501081a08c1a353b35a1564bc28a1200087b647"
77
84
  }
@@ -0,0 +1 @@
1
+ export { default } from '../lib/plugins/analytics.js'
@@ -1,48 +1 @@
1
- const createBalancesAnalyticsPlugin = ({
2
- analytics,
3
- hasBalanceAtom,
4
- assetsWithBalanceCountAtom,
5
- assetsTotalWalletAmountsAtom,
6
- config: { assetsToTrackForBalances },
7
- }) => {
8
- let subscriptions = []
9
-
10
- const getAssetAmountFromNumberUnit = (unit) => {
11
- if (!unit) return 0
12
- return unit.toDefaultNumber()
13
- }
14
-
15
- const onStart = () => {
16
- analytics.requireDefaultEventProperties(['hasBalance', 'numberOfAssets'])
17
-
18
- const unsubscribeBalanceAtom = hasBalanceAtom.observe((hasBalance) =>
19
- analytics.setDefaultEventProperties({ hasBalance })
20
- )
21
-
22
- const unsubscribeBalanceCountAtom = assetsWithBalanceCountAtom.observe((numberOfAssets) =>
23
- analytics.setDefaultEventProperties({ numberOfAssets })
24
- )
25
-
26
- const unsubscribeWalletAmountsAtom = assetsTotalWalletAmountsAtom.observe(async (balances) => {
27
- const assetBalances = assetsToTrackForBalances.reduce((acc, obj) => {
28
- acc[obj.analyticsName] = getAssetAmountFromNumberUnit(balances.get(obj.assetName))
29
- return acc
30
- }, Object.create(null))
31
- analytics.setDefaultEventProperties(assetBalances)
32
- })
33
-
34
- subscriptions = [
35
- unsubscribeBalanceAtom,
36
- unsubscribeBalanceCountAtom,
37
- unsubscribeWalletAmountsAtom,
38
- ]
39
- }
40
-
41
- const onStop = () => {
42
- subscriptions.forEach((unsubscribe) => unsubscribe())
43
- }
44
-
45
- return { onStart, onStop }
46
- }
47
-
48
- export default createBalancesAnalyticsPlugin
1
+ export { default } from '../lib/plugins/analytics.js'
@@ -0,0 +1,4 @@
1
+ export {
2
+ balancesLifecyclePluginDefinition,
3
+ balancesAnalyticsPluginDefinition,
4
+ } from '../lib/plugins/index.js'