@talismn/balances-react 0.0.0-pr587-20230302015709 → 0.0.0-pr589-20230302073822

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.
@@ -1,13 +1,12 @@
1
- import { useContext, createContext, useState, useEffect, useMemo, useRef, useCallback } from 'react';
1
+ import { useContext, createContext, useState, useEffect, useRef, useMemo, useCallback } from 'react';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { db as db$1, balances, Balances } from '@talismn/balances';
4
4
  import { db, fetchTokenRates } from '@talismn/token-rates';
5
5
  import { useLiveQuery } from 'dexie-react-hooks';
6
6
  import { useDebounce } from 'react-use';
7
7
  import { ChaindataProviderExtension } from '@talismn/chaindata-provider-extension';
8
- import md5 from 'blueimp-md5';
9
8
  import anylogger from 'anylogger';
10
- import { Subject, Observable, defer, shareReplay } from 'rxjs';
9
+ import { Observable, defer, shareReplay } from 'rxjs';
11
10
  import { ChainConnector } from '@talismn/chain-connector';
12
11
  import { ChainConnectorEvm } from '@talismn/chain-connector-evm';
13
12
 
@@ -45,15 +44,13 @@ const useBalanceModulesProvider = ({
45
44
  const [BalanceModulesProvider, useBalanceModules] = provideContext(useBalanceModulesProvider);
46
45
 
47
46
  function useChaindataProvider(options = {}) {
48
- const [onfinalityApiKey, setOnfinalityApiKey] = useState(options.onfinalityApiKey);
49
-
50
- // make sure we recreate provider only when the onfinalityApiKey changes
47
+ const [chaindata, setChaindata] = useState();
51
48
  useEffect(() => {
52
- if (options.onfinalityApiKey !== onfinalityApiKey) setOnfinalityApiKey(options.onfinalityApiKey);
53
- }, [options.onfinalityApiKey, onfinalityApiKey]);
54
- return useMemo(() => new ChaindataProviderExtension({
55
- onfinalityApiKey
56
- }), [onfinalityApiKey]);
49
+ setChaindata(new ChaindataProviderExtension({
50
+ onfinalityApiKey: options.onfinalityApiKey
51
+ }));
52
+ }, [options.onfinalityApiKey]);
53
+ return chaindata;
57
54
  }
58
55
  const [ChaindataProvider, useChaindata] = provideContext(useChaindataProvider);
59
56
 
@@ -129,7 +126,9 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, tokenRates, al
129
126
  balances
130
127
  };
131
128
  };
132
- const useDbCacheProvider = () => {
129
+ const useDbCacheProvider = ({
130
+ useTestnets = false
131
+ }) => {
133
132
  const chaindataProvider = useChaindata();
134
133
  const chainList = useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.chains(), [chaindataProvider]);
135
134
  const evmNetworkList = useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.evmNetworks(), [chaindataProvider]);
@@ -141,7 +140,7 @@ const useDbCacheProvider = () => {
141
140
  // debounce every 500ms to prevent hammering UI with updates
142
141
  useDebounce(() => {
143
142
  setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates, rawBalances));
144
- }, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates]);
143
+ }, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates, useTestnets]);
145
144
  const refInitialized = useRef(false);
146
145
 
147
146
  // force an update as soon as all datasources are fetched, so UI can display data ASAP
@@ -150,14 +149,14 @@ const useDbCacheProvider = () => {
150
149
  setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates, rawBalances));
151
150
  refInitialized.current = true;
152
151
  }
153
- }, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates]);
152
+ }, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates, useTestnets]);
154
153
  return dbData;
155
154
  };
156
155
  const [DbCacheProvider, useDbCache] = provideContext(useDbCacheProvider);
157
156
 
158
157
  var packageJson = {
159
158
  name: "@talismn/balances-react",
160
- version: "0.0.0-pr587-20230302015709",
159
+ version: "0.0.0-pr589-20230302073822",
161
160
  author: "Talisman",
162
161
  homepage: "https://talisman.xyz",
163
162
  license: "UNLICENSED",
@@ -190,7 +189,6 @@ var packageJson = {
190
189
  "@talismn/chaindata-provider-extension": "workspace:^",
191
190
  "@talismn/token-rates": "workspace:^",
192
191
  anylogger: "^1.0.11",
193
- "blueimp-md5": "2.19.0",
194
192
  dexie: "^3.2.3",
195
193
  "dexie-react-hooks": "^1.1.1",
196
194
  "react-use": "^17.4.0",
@@ -221,95 +219,6 @@ var packageJson = {
221
219
 
222
220
  var log = anylogger(packageJson.name);
223
221
 
224
- // global data store containing all subscriptions
225
- const subscriptions = {};
226
-
227
- /**
228
- * This hook ensures a subscription is created only once, and unsubscribe automatically as soon as there is no consumer to the hook
229
- * @param key key that is unique to the subscription's parameters
230
- * @param subscribe // subscribe function that will be shared by all consumers of the key
231
- */
232
- const useSharedSubscription = (key, subscribe) => {
233
- // create the rxJS subject if it doesn't exist
234
- if (!subscriptions[key]) subscriptions[key] = {
235
- subject: new Subject()
236
- };
237
- useEffect(() => {
238
- // subscribe to subject.
239
- // it won't change but we need to count subscribers, to unsubscribe main subscription when no more observers
240
- const s = subscriptions[key].subject.subscribe();
241
- return () => {
242
- // unsubscribe from our local observable updates to prevent memory leaks
243
- s.unsubscribe();
244
- const {
245
- subject,
246
- unsubscribe
247
- } = subscriptions[key];
248
- if (!subject.observed && unsubscribe) {
249
- log.debug(`[useSharedSubscription] unsubscribing ${key}`);
250
-
251
- // unsubscribe from backend updates to prevent unnecessary network connections
252
- unsubscribe();
253
- delete subscriptions[key].unsubscribe;
254
- }
255
- };
256
- }, [key]);
257
-
258
- // Initialize subscription
259
- useEffect(() => {
260
- const {
261
- unsubscribe
262
- } = subscriptions[key];
263
- // launch the subscription if it's a new key
264
- if (!unsubscribe) {
265
- const cb = subscribe();
266
- log.debug(`[useSharedSubscription] subscribing ${key}`);
267
- if (cb) subscriptions[key].unsubscribe = cb;
268
- // this error should only happen when developping a new hook, let it bubble up
269
- else throw new Error(`${key} subscribe did not return an unsubscribe callback`);
270
- }
271
- }, [key, subscribe]);
272
- };
273
-
274
- function useChainConnectorsProvider(options) {
275
- const [onfinalityApiKey, setOnfinalityApiKey] = useState(options.onfinalityApiKey);
276
-
277
- // make sure we recreate provider only when the onfinalityApiKey changes
278
- useEffect(() => {
279
- if (options.onfinalityApiKey !== onfinalityApiKey) setOnfinalityApiKey(options.onfinalityApiKey);
280
- }, [options.onfinalityApiKey, onfinalityApiKey]);
281
-
282
- // chaindata dependency
283
- const chaindata = useChaindata();
284
-
285
- // substrate connector
286
- const substrate = useMemo(() => new ChainConnector(chaindata), [chaindata]);
287
-
288
- // evm connector
289
- const evm = useMemo(() => new ChainConnectorEvm(chaindata, {
290
- onfinalityApiKey
291
- }), [chaindata, onfinalityApiKey]);
292
- return useMemo(() => ({
293
- substrate,
294
- evm
295
- }), [substrate, evm]);
296
- }
297
- const [ChainConnectorsProvider, useChainConnectors] = provideContext(useChainConnectorsProvider);
298
-
299
- function useTokens(withTestnets) {
300
- // keep db data up to date
301
- useDbCacheSubscription("tokens");
302
- const {
303
- tokensWithTestnetsMap,
304
- tokensWithoutTestnetsMap
305
- } = useDbCache();
306
- return withTestnets ? tokensWithTestnetsMap : tokensWithoutTestnetsMap;
307
- }
308
- function useToken(tokenId, withTestnets) {
309
- const tokens = useTokens(withTestnets);
310
- return tokenId ? tokens[tokenId] : undefined;
311
- }
312
-
313
222
  /**
314
223
  * Creates a subscription function that can be used to subscribe to a multicast observable created from an upstream source.
315
224
  *
@@ -351,193 +260,193 @@ const createMulticastSubscription = upstream => {
351
260
  return subscribe;
352
261
  };
353
262
 
354
- const useWithTestnetsProvider = ({
355
- withTestnets
356
- }) => {
357
- return {
358
- withTestnets
359
- };
360
- };
361
- const [WithTestnetsProvider, useWithTestnets] = provideContext(useWithTestnetsProvider);
263
+ function useChainConnectorsProvider(options) {
264
+ // chaindata dependency
265
+ const chaindata = useChaindata();
266
+
267
+ // substrate connector
268
+ const [substrate, setSubstrate] = useState();
269
+ useEffect(() => {
270
+ if (!chaindata) return;
271
+ setSubstrate(new ChainConnector(chaindata));
272
+ }, [chaindata]);
273
+
274
+ // evm connector
275
+ const [evm, setEvm] = useState();
276
+ useEffect(() => {
277
+ if (!chaindata) return;
278
+ setEvm(new ChainConnectorEvm(chaindata, {
279
+ onfinalityApiKey: options.onfinalityApiKey
280
+ }));
281
+ }, [chaindata, options.onfinalityApiKey]);
282
+ return useMemo(() => ({
283
+ substrate,
284
+ evm
285
+ }), [substrate, evm]);
286
+ }
287
+ const [ChainConnectorsProvider, useChainConnectors] = provideContext(useChainConnectorsProvider);
288
+
289
+ const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeTokenRates(), useSubscribeBalances()];
290
+ const [SubscriptionsProvider, useSubscriptions] = provideContext(useSubscriptionsProvider);
362
291
 
363
292
  /**
364
293
  * This hook is responsible for fetching the data used for balances and inserting it into the db.
365
294
  */
366
295
  const useDbCacheSubscription = subscribeTo => {
367
- const provider = useChaindata();
368
-
369
- // can't handle balances & tokenRates here as they have other dependencies, it would trigger to many subscriptions
370
- const subscribe = useCallback(() => {
296
+ const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances] = useSubscriptions();
297
+ useEffect(() => {
371
298
  switch (subscribeTo) {
372
299
  case "chains":
373
- return subscribeChainDataHydrate(provider, "chains");
300
+ return subscribeHydrateChains();
374
301
  case "evmNetworks":
375
- return subscribeChainDataHydrate(provider, "evmNetworks");
302
+ return subscribeHydrateEvmNetworks();
376
303
  case "tokens":
377
- return subscribeChainDataHydrate(provider, "tokens");
304
+ return subscribeHydrateTokens();
305
+ case "tokenRates":
306
+ return subscribeTokenRates();
307
+ case "balances":
308
+ return subscribeBalances();
378
309
  }
379
- }, [provider, subscribeTo]);
380
- useSharedSubscription(subscribeTo, subscribe);
310
+ }, [subscribeTo, subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances]);
381
311
  };
382
-
383
- /**
384
- * This hook is responsible for fetching the data used for token rates and inserting it into the db.
385
- */
386
- function useDbCacheTokenRatesSubscription() {
387
- const {
388
- withTestnets
389
- } = useWithTestnets();
390
- const tokens = useTokens(withTestnets);
391
- const subscriptionKey = useMemo(
392
- // not super sexy but we need key to change based on this stuff
393
- () => {
394
- const key = Object.values(tokens ?? {}).map(({
395
- id
396
- }) => id).sort().join();
397
- return `tokenRates-${md5(key)}`;
398
- }, [tokens]);
399
- const subscription = useCallback(() => {
400
- if (!Object.values(tokens ?? {}).length) return () => {};
401
- return subscribeTokenRates(tokens);
402
- }, [tokens]);
403
- useSharedSubscription(subscriptionKey, subscription);
312
+ function useSubscribeChaindataHydrate(type) {
313
+ const chaindata =
314
+ // cheeky hack to give us access to the hydrate methods
315
+ useChaindata();
316
+ const createSubscription = useCallback(() => {
317
+ if (!chaindata) return;
318
+ let active = true;
319
+ const interval = 300_000; // 300_000ms = 300s = 5 minutes
320
+
321
+ const hydrate = async () => {
322
+ if (!active) return;
323
+ try {
324
+ if (type === "chains") await chaindata.hydrateChains();
325
+ if (type === "evmNetworks") await chaindata.hydrateEvmNetworks();
326
+ if (type === "tokens") await chaindata.hydrateTokens();
327
+ setTimeout(hydrate, interval);
328
+ } catch (error) {
329
+ const retryTimeout = 5_000; // 5_000ms = 5 seconds
330
+ log.error(`Failed to fetch chaindata, retrying in ${Math.round(retryTimeout / 1000)} seconds`, error);
331
+ setTimeout(hydrate, retryTimeout);
332
+ }
333
+ };
334
+ hydrate();
335
+ return () => {
336
+ active = false;
337
+ };
338
+ }, [chaindata, type]);
339
+ const subscribe = useMulticastSubscription(createSubscription);
340
+ return subscribe;
404
341
  }
405
-
406
- /**
407
- * This hook is responsible for fetching the data used for balances and inserting it into the db.
408
- */
409
- function useDbCacheBalancesSubscription() {
410
- const {
411
- withTestnets
412
- } = useWithTestnets();
342
+ function useSubscribeTokenRates() {
343
+ const chaindataProvider = useChaindata();
344
+ const tokens = useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.tokens(), [chaindataProvider]);
345
+ const generationRef = useRef(0);
346
+ const createSubscription = useCallback(() => {
347
+ if (!chaindataProvider) return;
348
+ if (!tokens) return;
349
+ if (Object.keys(tokens).length < 1) return;
350
+
351
+ // when we make a new request, we want to ignore any old requests which haven't yet completed
352
+ // otherwise we risk replacing the most recent data with older data
353
+ const generation = (generationRef.current + 1) % Number.MAX_SAFE_INTEGER;
354
+ generationRef.current = generation;
355
+ let active = true;
356
+ const REFRESH_INTERVAL = 300_000; // 300_000ms = 5 minutes
357
+ const RETRY_INTERVAL = 5_000; // 5_000ms = 5 seconds
358
+
359
+ const hydrate = async () => {
360
+ if (!active) return;
361
+ if (generationRef.current !== generation) return;
362
+ try {
363
+ const tokenRates = await fetchTokenRates(tokens);
364
+ if (!active) return;
365
+ if (generationRef.current !== generation) return;
366
+ const putTokenRates = Object.entries(tokenRates).map(([tokenId, rates]) => ({
367
+ tokenId,
368
+ rates
369
+ }));
370
+ db.transaction("rw", db.tokenRates, async () => await db.tokenRates.bulkPut(putTokenRates));
371
+ setTimeout(hydrate, REFRESH_INTERVAL);
372
+ } catch (error) {
373
+ log.error(`Failed to fetch tokenRates, retrying in ${Math.round(RETRY_INTERVAL / 1000)} seconds`, error);
374
+ setTimeout(hydrate, RETRY_INTERVAL);
375
+ }
376
+ };
377
+ hydrate();
378
+ return () => {
379
+ active = false;
380
+ };
381
+ }, [chaindataProvider, tokens]);
382
+ const subscribe = useMulticastSubscription(createSubscription);
383
+ return subscribe;
384
+ }
385
+ function useSubscribeBalances() {
413
386
  const balanceModules = useBalanceModules();
414
387
  const chaindataProvider = useChaindata();
415
388
  const chainConnectors = useChainConnectors();
416
389
  const [allAddresses] = useAllAddresses();
417
- const tokens = useTokens(withTestnets);
418
- const subscriptionKey = useMemo(
419
- // not super sexy but we need key to change based on this stuff
420
- () => {
421
- const key = allAddresses.sort().join().concat(...Object.values(tokens ?? {}).map(({
422
- id
423
- }) => id).sort()).concat(`evm:${!!chainConnectors.evm}`, `sub:${!!chainConnectors.substrate}`, ...balanceModules.map(m => m.type).sort(), `cd:${!!chaindataProvider}`);
424
- return `balances-${md5(key)}`;
425
- }, [allAddresses, balanceModules, chainConnectors, chaindataProvider, tokens]);
426
- const subscription = useCallback(() => {
427
- if (!Object.values(tokens ?? {}).length || !allAddresses.length) return () => {};
428
- return subscribeBalances(tokens ?? {}, allAddresses, chainConnectors, chaindataProvider, balanceModules);
429
- }, [allAddresses, balanceModules, chainConnectors, chaindataProvider, tokens]);
430
- useSharedSubscription(subscriptionKey, subscription);
431
- }
432
- const subscribeChainDataHydrate = (provider, type) => {
433
- const chaindata = provider;
434
- const delay = 300_000; // 300_000ms = 300s = 5 minutes
435
-
436
- let timeout = null;
437
- const hydrate = async () => {
438
- try {
439
- if (type === "chains") await chaindata.hydrateChains();
440
- if (type === "evmNetworks") await chaindata.hydrateEvmNetworks();
441
- if (type === "tokens") await chaindata.hydrateTokens();
442
- timeout = setTimeout(hydrate, delay);
443
- } catch (error) {
444
- const retryTimeout = 5_000; // 5_000ms = 5 seconds
445
- log.error(`Failed to fetch chaindata, retrying in ${Math.round(retryTimeout / 1000)} seconds`, error);
446
- timeout = setTimeout(hydrate, retryTimeout);
447
- }
448
- };
449
-
450
- // launch the loop
451
- hydrate();
452
- return () => {
453
- if (timeout) clearTimeout(timeout);
454
- };
455
- };
456
- const subscribeTokenRates = tokens => {
457
- const REFRESH_INTERVAL = 300_000; // 6 minutes
458
- const RETRY_INTERVAL = 5_000; // 5 sec
459
-
460
- let timeout = null;
461
- const refreshTokenRates = async () => {
462
- try {
463
- if (timeout) clearTimeout(timeout);
464
- const tokenRates = await fetchTokenRates(tokens);
465
- const putTokenRates = Object.entries(tokenRates).map(([tokenId, rates]) => ({
466
- tokenId,
467
- rates
468
- }));
469
- db.transaction("rw", db.tokenRates, async () => await db.tokenRates.bulkPut(putTokenRates));
470
- timeout = setTimeout(() => {
471
- refreshTokenRates();
472
- }, REFRESH_INTERVAL);
473
- } catch (error) {
474
- log.error(`Failed to fetch tokenRates, retrying in ${Math.round(RETRY_INTERVAL / 1000)} seconds`, error);
475
- setTimeout(async () => {
476
- refreshTokenRates();
477
- }, RETRY_INTERVAL);
478
- }
479
- };
480
-
481
- // launch the loop
482
- refreshTokenRates();
483
- return () => {
484
- if (timeout) clearTimeout(timeout);
485
- };
486
- };
487
- const subscribeBalances = (tokens, addresses, chainConnectors, provider, balanceModules) => {
488
- const tokenIds = Object.values(tokens).map(({
390
+ const tokens = useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.tokens(), [chaindataProvider]);
391
+ const tokenIds = useMemo(() => Object.values(tokens ?? {}).map(({
489
392
  id
490
- }) => id);
491
- const addressesByToken = Object.fromEntries(tokenIds.map(tokenId => [tokenId, addresses]));
492
- const updateDb = balances => {
493
- const putBalances = Object.entries(balances.toJSON()).map(([id, balance]) => ({
494
- id,
495
- ...balance
496
- }));
497
- db$1.transaction("rw", db$1.balances, async () => await db$1.balances.bulkPut(putBalances));
498
- };
499
- let unsubscribed = false;
500
-
501
- // eslint-disable-next-line no-console
502
- log.log("subscribing to balance changes for %d tokens and %d addresses", tokenIds.length, addresses.length);
503
- const unsubs = balanceModules.map(async balanceModule => {
504
- // filter out tokens to only include those which this module knows how to fetch balances for
505
- const moduleTokenIds = Object.values(tokens ?? {}).filter(({
506
- type
507
- }) => type === balanceModule.type).map(({
508
- id
509
- }) => id);
510
- const addressesByModuleToken = Object.fromEntries(Object.entries(addressesByToken).filter(([tokenId]) => moduleTokenIds.includes(tokenId)));
511
- const unsub = balances(balanceModule, chainConnectors, provider, addressesByModuleToken, (error, balances) => {
512
- // log errors
513
- if (error) return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
514
- // ignore empty balance responses
515
- if (!balances) return;
516
- // ignore balances from old subscriptions which are still in the process of unsubscribing
517
- if (unsubscribed) return;
518
- updateDb(balances);
519
- });
520
- return () => {
521
- // wait 2 seconds before actually unsubscribing, allowing for websocket to be reused
522
- unsub.then(unsubscribe => {
523
- setTimeout(unsubscribe, 2_000);
393
+ }) => id), [tokens]);
394
+ const addressesByToken = useMemo(() => Object.fromEntries(tokenIds.map(tokenId => [tokenId, allAddresses])), [allAddresses, tokenIds]);
395
+ const generationRef = useRef(0);
396
+ const createSubscription = useCallback(() => {
397
+ if (!chainConnectors.substrate) return;
398
+ if (!chainConnectors.evm) return;
399
+ if (!chaindataProvider) return;
400
+ const generation = (generationRef.current + 1) % Number.MAX_SAFE_INTEGER;
401
+ generationRef.current = generation;
402
+ const unsubs = balanceModules.map(balanceModule => {
403
+ // filter out tokens to only include those which this module knows how to fetch balances for
404
+ const moduleTokenIds = Object.values(tokens ?? {}).filter(({
405
+ type
406
+ }) => type === balanceModule.type).map(({
407
+ id
408
+ }) => id);
409
+ const addressesByModuleToken = Object.fromEntries(Object.entries(addressesByToken).filter(([tokenId]) => moduleTokenIds.includes(tokenId)));
410
+ const subscribe = createMulticastSubscription(next => {
411
+ const unsub = balances(balanceModule, chainConnectors, chaindataProvider, addressesByModuleToken, (error, balances) => {
412
+ // log errors
413
+ if (error) return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
414
+ // ignore empty balance responses
415
+ if (!balances) return;
416
+ // ignore balances from old subscriptions which are still in the process of unsubscribing
417
+ if (generationRef.current !== generation) return;
418
+ next(balances);
419
+ });
420
+ return () => {
421
+ // unsubscribe from upstream
422
+ unsub.then(unsubscribe => unsubscribe());
423
+
424
+ // set this subscription's balances in the store to status: cache
425
+ db$1.transaction("rw", db$1.balances, async () => await db$1.balances.filter(balance => {
426
+ if (balance.source !== balanceModule.type) return false;
427
+ if (!Object.keys(addressesByModuleToken).includes(balance.tokenId)) return false;
428
+ if (!addressesByModuleToken[balance.tokenId].includes(balance.address)) return false;
429
+ return true;
430
+ }).modify({
431
+ status: "cache"
432
+ }));
433
+ };
524
434
  });
525
- db$1.transaction("rw", db$1.balances, async () => await db$1.balances.filter(balance => {
526
- if (balance.source !== balanceModule.type) return false;
527
- if (!Object.keys(addressesByModuleToken).includes(balance.tokenId)) return false;
528
- if (!addressesByModuleToken[balance.tokenId].includes(balance.address)) return false;
529
- return true;
530
- }).modify({
531
- status: "cache"
532
- }));
533
- };
534
- });
535
- const unsubscribeAll = () => {
536
- unsubscribed = true;
537
- unsubs.forEach(unsub => unsub.then(unsubscribe => unsubscribe()));
538
- };
539
- return unsubscribeAll;
540
- };
435
+ const unsubscribe = subscribe(balances => {
436
+ const putBalances = Object.entries(balances.toJSON()).map(([id, balance]) => ({
437
+ id,
438
+ ...balance
439
+ }));
440
+ db$1.transaction("rw", db$1.balances, async () => await db$1.balances.bulkPut(putBalances));
441
+ });
442
+ return unsubscribe;
443
+ });
444
+ const unsubscribeAll = () => unsubs.forEach(unsub => unsub());
445
+ return unsubscribeAll;
446
+ }, [addressesByToken, balanceModules, chainConnectors, chaindataProvider, tokens]);
447
+ const subscribe = useMulticastSubscription(createSubscription);
448
+ return subscribe;
449
+ }
541
450
 
542
451
  function useChains(withTestnets) {
543
452
  // keep db data up to date
@@ -569,7 +478,7 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
569
478
 
570
479
  function useTokenRates() {
571
480
  // keep db data up to date
572
- useDbCacheTokenRatesSubscription();
481
+ useDbCacheSubscription("tokenRates");
573
482
  const {
574
483
  tokenRatesMap
575
484
  } = useDbCache();
@@ -580,10 +489,21 @@ function useTokenRate(tokenId) {
580
489
  return tokenId ? tokenRates[tokenId] : undefined;
581
490
  }
582
491
 
583
- const useBalancesHydrate = () => {
492
+ function useTokens(withTestnets) {
493
+ // keep db data up to date
494
+ useDbCacheSubscription("tokens");
584
495
  const {
585
- withTestnets
586
- } = useWithTestnets();
496
+ tokensWithTestnetsMap,
497
+ tokensWithoutTestnetsMap
498
+ } = useDbCache();
499
+ return withTestnets ? tokensWithTestnetsMap : tokensWithoutTestnetsMap;
500
+ }
501
+ function useToken(tokenId, withTestnets) {
502
+ const tokens = useTokens(withTestnets);
503
+ return tokenId ? tokens[tokenId] : undefined;
504
+ }
505
+
506
+ const useBalancesHydrate = withTestnets => {
587
507
  const chains = useChains(withTestnets);
588
508
  const evmNetworks = useEvmNetworks(withTestnets);
589
509
  const tokens = useTokens(withTestnets);
@@ -598,7 +518,7 @@ const useBalancesHydrate = () => {
598
518
 
599
519
  function useBalances(addressesByToken) {
600
520
  // keep db data up to date
601
- useDbCacheBalancesSubscription();
521
+ useDbCacheSubscription("balances");
602
522
  const balanceModules = useBalanceModules();
603
523
  const {
604
524
  balances
@@ -629,18 +549,18 @@ function useBalances(addressesByToken) {
629
549
  const BalancesProvider = ({
630
550
  balanceModules,
631
551
  onfinalityApiKey,
632
- withTestnets,
552
+ useTestnets,
633
553
  children
634
- }) => /*#__PURE__*/jsx(WithTestnetsProvider, {
635
- withTestnets: withTestnets,
636
- children: /*#__PURE__*/jsx(ChaindataProvider, {
554
+ }) => /*#__PURE__*/jsx(ChaindataProvider, {
555
+ onfinalityApiKey: onfinalityApiKey,
556
+ children: /*#__PURE__*/jsx(ChainConnectorsProvider, {
637
557
  onfinalityApiKey: onfinalityApiKey,
638
- children: /*#__PURE__*/jsx(ChainConnectorsProvider, {
639
- onfinalityApiKey: onfinalityApiKey,
640
- children: /*#__PURE__*/jsx(AllAddressesProvider, {
641
- children: /*#__PURE__*/jsx(BalanceModulesProvider, {
642
- balanceModules: balanceModules,
643
- children: /*#__PURE__*/jsx(DbCacheProvider, {
558
+ children: /*#__PURE__*/jsx(AllAddressesProvider, {
559
+ children: /*#__PURE__*/jsx(BalanceModulesProvider, {
560
+ balanceModules: balanceModules,
561
+ children: /*#__PURE__*/jsx(DbCacheProvider, {
562
+ useTestnets: useTestnets,
563
+ children: /*#__PURE__*/jsx(SubscriptionsProvider, {
644
564
  children: children
645
565
  })
646
566
  })
@@ -649,4 +569,4 @@ const BalancesProvider = ({
649
569
  })
650
570
  });
651
571
 
652
- export { AllAddressesProvider, BalanceModulesProvider, BalancesProvider, ChainConnectorsProvider, ChaindataProvider, DbCacheProvider, WithTestnetsProvider, createMulticastSubscription, provideContext, useAllAddresses, useBalanceModules, useBalances, useBalancesHydrate, useChain, useChainConnectors, useChaindata, useChains, useDbCache, useDbCacheBalancesSubscription, useDbCacheSubscription, useDbCacheTokenRatesSubscription, useEvmNetwork, useEvmNetworks, useMulticastSubscription, useToken, useTokenRate, useTokenRates, useTokens, useWithTestnets };
572
+ export { AllAddressesProvider, BalanceModulesProvider, BalancesProvider, ChainConnectorsProvider, ChaindataProvider, DbCacheProvider, SubscriptionsProvider, createMulticastSubscription, provideContext, useAllAddresses, useBalanceModules, useBalances, useBalancesHydrate, useChain, useChainConnectors, useChaindata, useChains, useDbCache, useDbCacheSubscription, useEvmNetwork, useEvmNetworks, useMulticastSubscription, useSubscriptions, useToken, useTokenRate, useTokenRates, useTokens };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/balances-react",
3
- "version": "0.0.0-pr587-20230302015709",
3
+ "version": "0.0.0-pr589-20230302073822",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "UNLICENSED",
@@ -26,14 +26,13 @@
26
26
  "clean": "rm -rf dist && rm -rf .turbo rm -rf node_modules"
27
27
  },
28
28
  "dependencies": {
29
- "@talismn/balances": "^0.0.0-pr587-20230302015709",
29
+ "@talismn/balances": "^0.0.0-pr589-20230302073822",
30
30
  "@talismn/chain-connector": "^0.4.2",
31
31
  "@talismn/chain-connector-evm": "^0.4.2",
32
32
  "@talismn/chaindata-provider": "^0.4.2",
33
33
  "@talismn/chaindata-provider-extension": "^0.4.2",
34
- "@talismn/token-rates": "^0.0.0-pr587-20230302015709",
34
+ "@talismn/token-rates": "^0.0.0-pr589-20230302073822",
35
35
  "anylogger": "^1.0.11",
36
- "blueimp-md5": "2.19.0",
37
36
  "dexie": "^3.2.3",
38
37
  "dexie-react-hooks": "^1.1.1",
39
38
  "react-use": "^17.4.0",
@@ -1,8 +0,0 @@
1
- /// <reference types="react" />
2
- export declare const WithTestnetsProvider: import("react").FC<{
3
- withTestnets?: boolean | undefined;
4
- } & {
5
- children?: import("react").ReactNode;
6
- }>, useWithTestnets: () => {
7
- withTestnets: boolean | undefined;
8
- };
@@ -1,9 +0,0 @@
1
- type UnsubscribeFn = () => void;
2
- type InitSubscriptionCallback = () => UnsubscribeFn;
3
- /**
4
- * This hook ensures a subscription is created only once, and unsubscribe automatically as soon as there is no consumer to the hook
5
- * @param key key that is unique to the subscription's parameters
6
- * @param subscribe // subscribe function that will be shared by all consumers of the key
7
- */
8
- export declare const useSharedSubscription: (key: string, subscribe: InitSubscriptionCallback) => void;
9
- export {};