@talismn/balances-react 0.0.0-pr584-20230301012058 → 0.0.0-pr587-20230301175306

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,12 +1,13 @@
1
- import { useContext, createContext, useState, useEffect, useRef, useMemo, useCallback } from 'react';
1
+ import { useContext, createContext, useState, useEffect, useMemo, useRef, 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';
8
9
  import anylogger from 'anylogger';
9
- import { Observable, defer, shareReplay } from 'rxjs';
10
+ import { Subject, Observable, defer, shareReplay } from 'rxjs';
10
11
  import { ChainConnector } from '@talismn/chain-connector';
11
12
  import { ChainConnectorEvm } from '@talismn/chain-connector-evm';
12
13
 
@@ -44,13 +45,15 @@ const useBalanceModulesProvider = ({
44
45
  const [BalanceModulesProvider, useBalanceModules] = provideContext(useBalanceModulesProvider);
45
46
 
46
47
  function useChaindataProvider(options = {}) {
47
- const [chaindata, setChaindata] = useState();
48
+ const [onfinalityApiKey, setOnfinalityApiKey] = useState(options.onfinalityApiKey);
49
+
50
+ // make sure we recreate provider only when the onfinalityApiKey changes
48
51
  useEffect(() => {
49
- setChaindata(new ChaindataProviderExtension({
50
- onfinalityApiKey: options.onfinalityApiKey
51
- }));
52
- }, [options.onfinalityApiKey]);
53
- return chaindata;
52
+ if (options.onfinalityApiKey !== onfinalityApiKey) setOnfinalityApiKey(options.onfinalityApiKey);
53
+ }, [options.onfinalityApiKey, onfinalityApiKey]);
54
+ return useMemo(() => new ChaindataProviderExtension({
55
+ onfinalityApiKey
56
+ }), [onfinalityApiKey]);
54
57
  }
55
58
  const [ChaindataProvider, useChaindata] = provideContext(useChaindataProvider);
56
59
 
@@ -126,9 +129,7 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, tokenRates, al
126
129
  balances
127
130
  };
128
131
  };
129
- const useDbCacheProvider = ({
130
- useTestnets = false
131
- }) => {
132
+ const useDbCacheProvider = () => {
132
133
  const chaindataProvider = useChaindata();
133
134
  const chainList = useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.chains(), [chaindataProvider]);
134
135
  const evmNetworkList = useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.evmNetworks(), [chaindataProvider]);
@@ -140,7 +141,7 @@ const useDbCacheProvider = ({
140
141
  // debounce every 500ms to prevent hammering UI with updates
141
142
  useDebounce(() => {
142
143
  setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates, rawBalances));
143
- }, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates, useTestnets]);
144
+ }, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates]);
144
145
  const refInitialized = useRef(false);
145
146
 
146
147
  // force an update as soon as all datasources are fetched, so UI can display data ASAP
@@ -149,14 +150,14 @@ const useDbCacheProvider = ({
149
150
  setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates, rawBalances));
150
151
  refInitialized.current = true;
151
152
  }
152
- }, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates, useTestnets]);
153
+ }, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates]);
153
154
  return dbData;
154
155
  };
155
156
  const [DbCacheProvider, useDbCache] = provideContext(useDbCacheProvider);
156
157
 
157
158
  var packageJson = {
158
159
  name: "@talismn/balances-react",
159
- version: "0.0.0-pr584-20230301012058",
160
+ version: "0.0.0-pr587-20230301175306",
160
161
  author: "Talisman",
161
162
  homepage: "https://talisman.xyz",
162
163
  license: "UNLICENSED",
@@ -189,6 +190,7 @@ var packageJson = {
189
190
  "@talismn/chaindata-provider-extension": "workspace:^",
190
191
  "@talismn/token-rates": "workspace:^",
191
192
  anylogger: "^1.0.11",
193
+ "blueimp-md5": "2.19.0",
192
194
  dexie: "^3.2.3",
193
195
  "dexie-react-hooks": "^1.1.1",
194
196
  "react-use": "^17.4.0",
@@ -219,6 +221,95 @@ var packageJson = {
219
221
 
220
222
  var log = anylogger(packageJson.name);
221
223
 
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
+
222
313
  /**
223
314
  * Creates a subscription function that can be used to subscribe to a multicast observable created from an upstream source.
224
315
  *
@@ -260,193 +351,191 @@ const createMulticastSubscription = upstream => {
260
351
  return subscribe;
261
352
  };
262
353
 
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);
354
+ const useWithTestnetsProvider = ({
355
+ withTestnets
356
+ }) => {
357
+ return {
358
+ withTestnets
359
+ };
360
+ };
361
+ const [WithTestnetsProvider, useWithTestnets] = provideContext(useWithTestnetsProvider);
291
362
 
292
363
  /**
293
364
  * This hook is responsible for fetching the data used for balances and inserting it into the db.
294
365
  */
295
366
  const useDbCacheSubscription = subscribeTo => {
296
- const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances] = useSubscriptions();
297
- useEffect(() => {
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(() => {
298
371
  switch (subscribeTo) {
299
372
  case "chains":
300
- return subscribeHydrateChains();
373
+ return subscribeChainDataHydrate(provider, "chains");
301
374
  case "evmNetworks":
302
- return subscribeHydrateEvmNetworks();
375
+ return subscribeChainDataHydrate(provider, "evmNetworks");
303
376
  case "tokens":
304
- return subscribeHydrateTokens();
305
- case "tokenRates":
306
- return subscribeTokenRates();
307
- case "balances":
308
- return subscribeBalances();
377
+ return subscribeChainDataHydrate(provider, "tokens");
309
378
  }
310
- }, [subscribeTo, subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances]);
379
+ }, [provider, subscribeTo]);
380
+ useSharedSubscription(subscribeTo, subscribe);
311
381
  };
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;
341
- }
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;
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);
384
404
  }
385
- function useSubscribeBalances() {
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();
386
413
  const balanceModules = useBalanceModules();
387
414
  const chaindataProvider = useChaindata();
388
415
  const chainConnectors = useChainConnectors();
389
416
  const [allAddresses] = useAllAddresses();
390
- const tokens = useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.tokens(), [chaindataProvider]);
391
- const tokenIds = useMemo(() => Object.values(tokens ?? {}).map(({
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(({
392
489
  id
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
- };
434
- });
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;
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);
443
519
  });
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
- }
520
+ return () => {
521
+ // TODO should we add a timeout before unsubscribe to prevent closing sockets to quickly ?
522
+ unsub.then(unsubscribe => unsubscribe());
523
+ db$1.transaction("rw", db$1.balances, async () => await db$1.balances.filter(balance => {
524
+ if (balance.source !== balanceModule.type) return false;
525
+ if (!Object.keys(addressesByModuleToken).includes(balance.tokenId)) return false;
526
+ if (!addressesByModuleToken[balance.tokenId].includes(balance.address)) return false;
527
+ return true;
528
+ }).modify({
529
+ status: "cache"
530
+ }));
531
+ };
532
+ });
533
+ const unsubscribeAll = () => {
534
+ unsubscribed = true;
535
+ unsubs.forEach(unsub => unsub.then(unsubscribe => unsubscribe()));
536
+ };
537
+ return unsubscribeAll;
538
+ };
450
539
 
451
540
  function useChains(withTestnets) {
452
541
  // keep db data up to date
@@ -478,7 +567,7 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
478
567
 
479
568
  function useTokenRates() {
480
569
  // keep db data up to date
481
- useDbCacheSubscription("tokenRates");
570
+ useDbCacheTokenRatesSubscription();
482
571
  const {
483
572
  tokenRatesMap
484
573
  } = useDbCache();
@@ -489,21 +578,10 @@ function useTokenRate(tokenId) {
489
578
  return tokenId ? tokenRates[tokenId] : undefined;
490
579
  }
491
580
 
492
- function useTokens(withTestnets) {
493
- // keep db data up to date
494
- useDbCacheSubscription("tokens");
581
+ const useBalancesHydrate = () => {
495
582
  const {
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 => {
583
+ withTestnets
584
+ } = useWithTestnets();
507
585
  const chains = useChains(withTestnets);
508
586
  const evmNetworks = useEvmNetworks(withTestnets);
509
587
  const tokens = useTokens(withTestnets);
@@ -518,7 +596,7 @@ const useBalancesHydrate = withTestnets => {
518
596
 
519
597
  function useBalances(addressesByToken) {
520
598
  // keep db data up to date
521
- useDbCacheSubscription("balances");
599
+ useDbCacheBalancesSubscription();
522
600
  const balanceModules = useBalanceModules();
523
601
  const {
524
602
  balances
@@ -549,18 +627,18 @@ function useBalances(addressesByToken) {
549
627
  const BalancesProvider = ({
550
628
  balanceModules,
551
629
  onfinalityApiKey,
552
- useTestnets,
630
+ withTestnets,
553
631
  children
554
- }) => /*#__PURE__*/jsx(ChaindataProvider, {
555
- onfinalityApiKey: onfinalityApiKey,
556
- children: /*#__PURE__*/jsx(ChainConnectorsProvider, {
632
+ }) => /*#__PURE__*/jsx(WithTestnetsProvider, {
633
+ withTestnets: withTestnets,
634
+ children: /*#__PURE__*/jsx(ChaindataProvider, {
557
635
  onfinalityApiKey: onfinalityApiKey,
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, {
636
+ children: /*#__PURE__*/jsx(ChainConnectorsProvider, {
637
+ onfinalityApiKey: onfinalityApiKey,
638
+ children: /*#__PURE__*/jsx(AllAddressesProvider, {
639
+ children: /*#__PURE__*/jsx(BalanceModulesProvider, {
640
+ balanceModules: balanceModules,
641
+ children: /*#__PURE__*/jsx(DbCacheProvider, {
564
642
  children: children
565
643
  })
566
644
  })
@@ -569,4 +647,4 @@ const BalancesProvider = ({
569
647
  })
570
648
  });
571
649
 
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 };
650
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/balances-react",
3
- "version": "0.0.0-pr584-20230301012058",
3
+ "version": "0.0.0-pr587-20230301175306",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "UNLICENSED",
@@ -26,13 +26,14 @@
26
26
  "clean": "rm -rf dist && rm -rf .turbo rm -rf node_modules"
27
27
  },
28
28
  "dependencies": {
29
- "@talismn/balances": "^0.0.0-pr584-20230301012058",
29
+ "@talismn/balances": "^0.0.0-pr587-20230301175306",
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-pr584-20230301012058",
34
+ "@talismn/token-rates": "^0.0.0-pr587-20230301175306",
35
35
  "anylogger": "^1.0.11",
36
+ "blueimp-md5": "2.19.0",
36
37
  "dexie": "^3.2.3",
37
38
  "dexie-react-hooks": "^1.1.1",
38
39
  "react-use": "^17.4.0",