@talismn/balances-react 0.0.0-pr2075-20250708160640 → 0.0.0-pr2075-20250709134044

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.
@@ -2,29 +2,28 @@
2
2
 
3
3
  var jotai = require('jotai');
4
4
  var react = require('react');
5
- var balances = require('@talismn/balances');
6
5
  var tokenRates = require('@talismn/token-rates');
7
6
  var jsxRuntime = require('react/jsx-runtime');
8
7
  var chaindataProvider = require('@talismn/chaindata-provider');
8
+ var balances = require('@talismn/balances');
9
9
  var util = require('@talismn/util');
10
10
  var jotaiEffect = require('jotai-effect');
11
11
  var utils = require('jotai/utils');
12
12
  var lodash = require('lodash');
13
13
  var rxjs = require('rxjs');
14
- var anylogger = require('anylogger');
15
14
  var chainConnector = require('@talismn/chain-connector');
16
15
  var chainConnectorEvm = require('@talismn/chain-connector-evm');
17
16
  var connectionMeta = require('@talismn/connection-meta');
18
17
  var dexie = require('dexie');
19
18
  var isEqual = require('lodash/isEqual');
20
19
  var utilCrypto = require('@polkadot/util-crypto');
20
+ var anylogger = require('anylogger');
21
21
 
22
22
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
23
23
 
24
- var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
25
24
  var isEqual__default = /*#__PURE__*/_interopDefault(isEqual);
25
+ var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
26
26
 
27
- const balanceModuleCreatorsAtom = jotai.atom(balances.defaultBalanceModules);
28
27
  const innerCoinsApiConfigAtom = jotai.atom(tokenRates.DEFAULT_COINSAPI_CONFIG);
29
28
  const coinsApiConfigAtom = jotai.atom(get => get(innerCoinsApiConfigAtom), (_get, set, options) => set(innerCoinsApiConfigAtom, {
30
29
  apiUrl: options.apiUrl ?? tokenRates.DEFAULT_COINSAPI_CONFIG.apiUrl
@@ -36,55 +35,6 @@ const enabledTokensAtom = jotai.atom(undefined);
36
35
  /** Sets the list of addresses for which token balances will be fetched by the balances subscription */
37
36
  const allAddressesAtom = jotai.atom([]);
38
37
 
39
- var packageJson = {
40
- name: "@talismn/balances-react"};
41
-
42
- var log = anylogger__default.default(packageJson.name);
43
-
44
- /**
45
- // Persistence backend for indexedDB
46
- // Add a new backend by implementing the BalancesPersistBackend interface
47
- // configureStore can be called with a different indexedDB table
48
- */
49
- const {
50
- persistData,
51
- retrieveData
52
- } = balances.configureStore();
53
-
54
- /**
55
- // Persistence backend for localStorage
56
- */
57
- const localStoragePersist = async balances$1 => {
58
- const storedBalances = balances$1.map(b => {
59
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
60
- const {
61
- status,
62
- ...rest
63
- } = b;
64
- return rest;
65
- });
66
- const deflated = balances.compress(storedBalances);
67
- localStorage.setItem("talismanBalances", deflated.toString());
68
- };
69
- const localStorageRetrieve = async () => {
70
- const deflated = localStorage.getItem("talismanBalances");
71
- if (deflated) {
72
- // deflated will be a long string of numbers separated by commas
73
- const deflatedArray = deflated.split(",").map(n => parseInt(n, 10));
74
- const deflatedBytes = new Uint8Array(deflatedArray.length);
75
- deflatedArray.forEach((n, i) => deflatedBytes[i] = n);
76
- return balances.decompress(deflatedBytes).map(b => ({
77
- ...b,
78
- status: "cache"
79
- }));
80
- }
81
- return [];
82
- };
83
- const localStorageBalancesPersistBackend = {
84
- persist: localStoragePersist,
85
- retrieve: localStorageRetrieve
86
- };
87
-
88
38
  const chaindataProviderAtom = jotai.atom(() => {
89
39
  return new chaindataProvider.ChaindataProvider({});
90
40
  });
@@ -99,17 +49,9 @@ const chainConnectorsAtom = jotai.atom(get => {
99
49
  };
100
50
  });
101
51
 
102
- const balanceModulesAtom = jotai.atom(get => {
103
- const balanceModuleCreators = get(balanceModuleCreatorsAtom);
104
- const chainConnectors = get(chainConnectorsAtom);
105
- const chaindataProvider = get(chaindataProviderAtom);
106
- if (!chainConnectors.substrate) return [];
107
- if (!chainConnectors.evm) return [];
108
- if (!chaindataProvider) return [];
109
- return balanceModuleCreators.map(mod => mod({
110
- chainConnectors,
111
- chaindataProvider
112
- }));
52
+ const balancesProviderAtom = jotai.atom(get => {
53
+ return new balances.BalancesProvider(get(chaindataProviderAtom), get(chainConnectorsAtom)) // TODO pass storage
54
+ ;
113
55
  });
114
56
 
115
57
  /**
@@ -173,6 +115,11 @@ const chaindataAtom = utils.atomWithObservable(get => {
173
115
 
174
116
  const cryptoWaitReadyAtom = jotai.atom(async () => await utilCrypto.cryptoWaitReady());
175
117
 
118
+ var packageJson = {
119
+ name: "@talismn/balances-react"};
120
+
121
+ var log = anylogger__default.default(packageJson.name);
122
+
176
123
  const tokenRatesAtom = jotai.atom(async get => {
177
124
  // runs a timer to keep tokenRates up to date
178
125
  get(tokenRatesFetcherAtomEffect);
@@ -246,14 +193,20 @@ const allBalancesAtom = jotai.atom(async get => {
246
193
  });
247
194
  const balancesObservable = new rxjs.BehaviorSubject({});
248
195
  const balancesObservableAtom = utils.atomWithObservable(() => balancesObservable);
249
- const balancesPersistBackendAtom = jotai.atom(localStorageBalancesPersistBackend);
250
- const hydrateBalancesObservableAtom = jotai.atom(async get => {
251
- const persistBackend = get(balancesPersistBackendAtom);
252
- const balances$1 = await persistBackend.retrieve();
253
- balancesObservable.next(Object.fromEntries(balances$1.map(b => [balances.getBalanceId(b), {
254
- ...b,
255
- status: "cache"
256
- }])));
196
+
197
+ // export const balancesPersistBackendAtom = atom<BalancesPersistBackend>(
198
+ // localStorageBalancesPersistBackend,
199
+ // )
200
+
201
+ const hydrateBalancesObservableAtom = jotai.atom(async () => {
202
+ // const persistBackend = get(balancesPersistBackendAtom)
203
+ // const balances = await persistBackend.retrieve()
204
+
205
+ balancesObservable.next({}
206
+ // Object.fromEntries(
207
+ // balances.map((b) => [getBalanceId(b), { ...b, status: "cache" } as BalanceJson]),
208
+ // ),
209
+ );
257
210
  });
258
211
  const balancesHydrateDataAtom = jotai.atom(async get => {
259
212
  const [{
@@ -272,16 +225,21 @@ const balancesSubscriptionAtomEffect = jotaiEffect.atomEffect(get => {
272
225
 
273
226
  // we have to specify these synchronously, otherwise jotai won't know
274
227
  // that it needs to restart our subscriptions when they change
275
- const atomDependencies = Promise.all([get(cryptoWaitReadyAtom), get(balanceModulesAtom), get(allAddressesAtom), get(chainsAtom), get(chainsByIdAtom), get(evmNetworksAtom), get(evmNetworksByIdAtom), get(tokensAtom), get(tokensByIdAtom), get(miniMetadatasAtom), get(enabledChainsAtom), get(enabledTokensAtom), get(hydrateBalancesObservableAtom)]);
276
- const persistBackend = get(balancesPersistBackendAtom);
228
+ const atomDependencies = Promise.all([get(cryptoWaitReadyAtom),
229
+ // get(balanceModulesAtom), // TODO yeet
230
+ get(balancesProviderAtom), get(allAddressesAtom), get(chainsAtom), get(chainsByIdAtom), get(evmNetworksAtom), get(evmNetworksByIdAtom), get(tokensAtom), get(tokensByIdAtom), get(miniMetadatasAtom), get(enabledChainsAtom), get(enabledTokensAtom), get(hydrateBalancesObservableAtom)]);
231
+
232
+ // const persistBackend = get(balancesPersistBackendAtom)
233
+
277
234
  const unsubsPromise = (async () => {
278
- const [_cryptoReady, balanceModules, allAddresses, chains, chainsById, evmNetworks, evmNetworksById, tokens, tokensById, _miniMetadatas, enabledChainsConfig, enabledTokensConfig] = await atomDependencies;
235
+ const [_cryptoReady, balancesProvider, allAddresses, chains, chainsById, evmNetworks, evmNetworksById, tokens, tokensById, _miniMetadatas, enabledChainsConfig, enabledTokensConfig] = await atomDependencies;
279
236
  if (abort.signal.aborted) return;
280
237
 
281
238
  // persist data every thirty seconds
282
- balancesObservable.pipe(rxjs.debounceTime(10000)).subscribe(balancesUpdate => {
283
- persistBackend.persist(Object.values(balancesUpdate));
284
- });
239
+ // balancesObservable.pipe(debounceTime(10000)).subscribe((balancesUpdate) => {
240
+ // persistBackend.persist(Object.values(balancesUpdate))
241
+ // })
242
+
285
243
  const updateBalances = async balancesUpdates => {
286
244
  if (abort.signal.aborted) return;
287
245
  const updatesWithIds = new balances.Balances(balancesUpdates);
@@ -335,6 +293,7 @@ const balancesSubscriptionAtomEffect = jotaiEffect.atomEffect(get => {
335
293
  return util.isEthereumAddress(address) ? token.networkId || chainsById[token.networkId ?? ""]?.account === "secp256k1" : token.networkId && chainsById[token.networkId ?? ""]?.account !== "secp256k1";
336
294
  });
337
295
  });
296
+ const addressesByTokenId = lodash.fromPairs(enabledTokenIds.map(tokenId => [tokenId, allAddresses]));
338
297
 
339
298
  // Delete invalid cached balances
340
299
  const chainIds = new Set(chains.map(chain => chain.id));
@@ -350,7 +309,7 @@ const balancesSubscriptionAtomEffect = jotaiEffect.atomEffect(get => {
350
309
  if (!enabledTokenIds.includes(balance.tokenId)) return true;
351
310
 
352
311
  // delete cached balance when module doesn't exist
353
- if (!balanceModules.find(module => module.type === balance.source)) return true;
312
+ // if (!balanceModules.find((module) => module.type === balance.source)) return true
354
313
 
355
314
  // delete cached balance for accounts on incompatible chains
356
315
  // (substrate accounts shouldn't have evm balances)
@@ -387,62 +346,96 @@ const balancesSubscriptionAtomEffect = jotaiEffect.atomEffect(get => {
387
346
  if (v.length) updateBalances(v);
388
347
  });
389
348
  }, 30_000);
390
- return balanceModules.map(balanceModule => {
391
- const unsub = balances.balances(balanceModule, addressesByTokenByModule[balanceModule.type] ?? {}, (error, balances) => {
392
- // log errors
393
- if (error) {
394
- if (error?.type === "STALE_RPC_ERROR" || error?.type === "WEBSOCKET_ALLOCATION_EXHAUSTED_ERROR") {
395
- const addressesByModuleToken = addressesByTokenByModule[balanceModule.type] ?? {};
396
- const staleObservable = balancesObservable.pipe(rxjs.map(val => Object.values(val).filter(balance => {
397
- const {
398
- tokenId,
399
- address,
400
- source
401
- } = balance;
402
- const chainComparison = error.chainId ? "chainId" in balance && error.chainId === balance.chainId : error.evmNetworkId ? "evmNetworkId" in balance && error.evmNetworkId === balance.evmNetworkId : true;
403
- return chainComparison && addressesByModuleToken[tokenId]?.includes(address) && source === balanceModule.type;
404
- }).map(balance => ({
405
- ...balance,
406
- status: "stale"
407
- }))));
408
- rxjs.firstValueFrom(staleObservable).then(v => {
409
- if (v.length) updateBalances(v);
410
- });
411
- }
412
- return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
413
- }
414
-
415
- // ignore empty balance responses
416
- if (!balances) return;
417
- // ignore balances from old subscriptions which are still in the process of unsubscribing
418
- if (abort.signal.aborted) return;
419
-
420
- // good balances
421
- if (balances) {
422
- if ("status" in balances) {
423
- // For modules using the new SubscriptionResultWithStatus pattern
424
- //TODO fix initialisin
425
- // if (result.status === "initialising") this.#initialising.add(balanceModule.type)
426
- // else this.#initialising.delete(balanceModule.type)
427
- updateBalances(balances.data);
428
- } else {
429
- // add good ones to initialisedBalances
430
- updateBalances(Object.values(balances.toJSON()));
431
- }
432
- }
433
- });
434
- return () => unsub.then(unsubscribe => unsubscribe());
349
+ const sub = balancesProvider.getBalances$(addressesByTokenId).subscribe(balances => {
350
+ updateBalances(balances.balances);
435
351
  });
352
+ return () => {
353
+ sub.unsubscribe();
354
+ };
355
+
356
+ // return balanceModules.map((balanceModule) => {
357
+ // const unsub = balancesFn(
358
+ // balanceModule,
359
+ // addressesByTokenByModule[balanceModule.type] ?? {},
360
+ // (error, balances) => {
361
+ // // log errors
362
+ // if (error) {
363
+ // if (
364
+ // error?.type === "STALE_RPC_ERROR" ||
365
+ // error?.type === "WEBSOCKET_ALLOCATION_EXHAUSTED_ERROR"
366
+ // ) {
367
+ // const addressesByModuleToken = addressesByTokenByModule[balanceModule.type] ?? {}
368
+
369
+ // const staleObservable = balancesObservable.pipe(
370
+ // map((val) =>
371
+ // Object.values(val)
372
+ // .filter((balance) => {
373
+ // const { tokenId, address, source } = balance
374
+ // const chainComparison = error.chainId
375
+ // ? "chainId" in balance && error.chainId === balance.chainId
376
+ // : error.evmNetworkId
377
+ // ? "evmNetworkId" in balance && error.evmNetworkId === balance.evmNetworkId
378
+ // : true
379
+ // return (
380
+ // chainComparison &&
381
+ // addressesByModuleToken[tokenId]?.includes(address) &&
382
+ // source === balanceModule.type
383
+ // )
384
+ // })
385
+ // .map((balance) => ({ ...balance, status: "stale" }) as BalanceJson),
386
+ // ),
387
+ // )
388
+
389
+ // firstValueFrom(staleObservable).then((v) => {
390
+ // if (v.length) updateBalances(v)
391
+ // })
392
+ // }
393
+
394
+ // return log.error(`Failed to fetch ${balanceModule.type} balances`, error)
395
+ // }
396
+
397
+ // // ignore empty balance responses
398
+ // if (!balances) return
399
+ // // ignore balances from old subscriptions which are still in the process of unsubscribing
400
+ // if (abort.signal.aborted) return
401
+
402
+ // // good balances
403
+ // if (balances) {
404
+ // if ("status" in balances) {
405
+ // // For modules using the new SubscriptionResultWithStatus pattern
406
+ // //TODO fix initialisin
407
+ // // if (result.status === "initialising") this.#initialising.add(balanceModule.type)
408
+ // // else this.#initialising.delete(balanceModule.type)
409
+ // updateBalances(balances.data)
410
+ // } else {
411
+ // // add good ones to initialisedBalances
412
+ // updateBalances(Object.values(balances.toJSON()))
413
+ // }
414
+ // }
415
+ // },
416
+ // )
417
+
418
+ // return () => unsub.then((unsubscribe) => unsubscribe())
419
+ // })
436
420
  })();
437
421
 
438
422
  // close the existing subscriptions when our effect unmounts
439
423
  // (wait 2 seconds before actually unsubscribing, to allow the websocket to be reused in that time)
440
- const unsubscribe = () => unsubsPromise.then(unsubs => {
441
- persistBackend.persist(Object.values(balancesObservable.value));
442
- unsubs?.forEach(unsub => unsub());
443
- });
444
- abort.signal.addEventListener("abort", () => setTimeout(unsubscribe, 2_000));
445
- return () => abort.abort("Unsubscribed");
424
+ // const unsubscribe = () =>
425
+ // unsubsPromise.then((unsubs) => {
426
+ // persistBackend.persist(Object.values(balancesObservable.value))
427
+ // unsubs?.forEach((unsub) => unsub())
428
+ // })
429
+ // abort.signal.addEventListener("abort", () => setTimeout(unsubscribe, 2_000))
430
+
431
+ // return () => abort.abort("Unsubscribed")
432
+
433
+ return () => {
434
+ unsubsPromise.then(unsub => {
435
+ unsub?.();
436
+ //console.log("unsubscribed")
437
+ });
438
+ };
446
439
  });
447
440
 
448
441
  const useSetBalancesAddresses = addresses => {
@@ -459,9 +452,7 @@ const useSetBalancesAddresses = addresses => {
459
452
  * @returns a Balances object containing the current balances state.
460
453
  */
461
454
 
462
- const useBalances = persistBackend => {
463
- const [, setPersistBackend] = jotai.useAtom(balancesPersistBackendAtom);
464
- if (persistBackend) setPersistBackend(persistBackend);
455
+ const useBalances = () => {
465
456
  return jotai.useAtomValue(allBalancesAtom);
466
457
  };
467
458
 
@@ -512,17 +503,12 @@ const useTokenRates = () => jotai.useAtomValue(tokenRatesAtom);
512
503
  const useTokenRate = tokenId => useTokenRates()[tokenId ?? ""] ?? undefined;
513
504
 
514
505
  const BalancesProvider = ({
515
- balanceModules,
516
506
  coinsApiUrl,
517
507
  withTestnets,
518
508
  enabledChains,
519
509
  enabledTokens,
520
510
  children
521
511
  }) => {
522
- const setBalanceModules = jotai.useSetAtom(balanceModuleCreatorsAtom);
523
- react.useEffect(() => {
524
- if (balanceModules !== undefined) setBalanceModules(balanceModules);
525
- }, [balanceModules, setBalanceModules]);
526
512
  const setCoinsApiConfig = jotai.useSetAtom(coinsApiConfigAtom);
527
513
  react.useEffect(() => {
528
514
  setCoinsApiConfig({
@@ -573,9 +559,6 @@ Object.defineProperty(exports, "subTokensTokenId", {
573
559
  exports.BalancesProvider = BalancesProvider;
574
560
  exports.allAddressesAtom = allAddressesAtom;
575
561
  exports.allBalancesAtom = allBalancesAtom;
576
- exports.balanceModuleCreatorsAtom = balanceModuleCreatorsAtom;
577
- exports.balanceModulesAtom = balanceModulesAtom;
578
- exports.balancesPersistBackendAtom = balancesPersistBackendAtom;
579
562
  exports.chainConnectorsAtom = chainConnectorsAtom;
580
563
  exports.chaindataAtom = chaindataAtom;
581
564
  exports.chaindataProviderAtom = chaindataProviderAtom;