@talismn/balances-react 0.0.0-pr587-20230301175306 → 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.
- package/CHANGELOG.md +3 -7
- package/dist/declarations/src/hooks/index.d.ts +2 -3
- package/dist/declarations/src/hooks/useBalancesHydrate.d.ts +1 -1
- package/dist/declarations/src/hooks/useChainConnectors.d.ts +2 -2
- package/dist/declarations/src/hooks/useChaindata.d.ts +2 -2
- package/dist/declarations/src/hooks/useDbCache.d.ts +4 -1
- package/dist/declarations/src/hooks/useDbCacheSubscription.d.ts +5 -9
- package/dist/talismn-balances-react.cjs.dev.js +209 -290
- package/dist/talismn-balances-react.cjs.prod.js +209 -290
- package/dist/talismn-balances-react.esm.js +210 -288
- package/package.json +3 -4
- package/dist/declarations/src/hooks/useWithTestnets.d.ts +0 -8
- package/dist/declarations/src/util/useSharedSubscription.d.ts +0 -9
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { useContext, createContext, useState, useEffect,
|
|
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 {
|
|
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 [
|
|
49
|
-
|
|
50
|
-
// make sure we recreate provider only when the onfinalityApiKey changes
|
|
47
|
+
const [chaindata, setChaindata] = useState();
|
|
51
48
|
useEffect(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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-
|
|
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,191 +260,193 @@ const createMulticastSubscription = upstream => {
|
|
|
351
260
|
return subscribe;
|
|
352
261
|
};
|
|
353
262
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
|
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
|
|
300
|
+
return subscribeHydrateChains();
|
|
374
301
|
case "evmNetworks":
|
|
375
|
-
return
|
|
302
|
+
return subscribeHydrateEvmNetworks();
|
|
376
303
|
case "tokens":
|
|
377
|
-
return
|
|
304
|
+
return subscribeHydrateTokens();
|
|
305
|
+
case "tokenRates":
|
|
306
|
+
return subscribeTokenRates();
|
|
307
|
+
case "balances":
|
|
308
|
+
return subscribeBalances();
|
|
378
309
|
}
|
|
379
|
-
}, [
|
|
380
|
-
useSharedSubscription(subscribeTo, subscribe);
|
|
310
|
+
}, [subscribeTo, subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances]);
|
|
381
311
|
};
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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 =
|
|
418
|
-
const
|
|
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,
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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;
|
|
519
443
|
});
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
};
|
|
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
|
+
}
|
|
539
450
|
|
|
540
451
|
function useChains(withTestnets) {
|
|
541
452
|
// keep db data up to date
|
|
@@ -567,7 +478,7 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
|
|
|
567
478
|
|
|
568
479
|
function useTokenRates() {
|
|
569
480
|
// keep db data up to date
|
|
570
|
-
|
|
481
|
+
useDbCacheSubscription("tokenRates");
|
|
571
482
|
const {
|
|
572
483
|
tokenRatesMap
|
|
573
484
|
} = useDbCache();
|
|
@@ -578,10 +489,21 @@ function useTokenRate(tokenId) {
|
|
|
578
489
|
return tokenId ? tokenRates[tokenId] : undefined;
|
|
579
490
|
}
|
|
580
491
|
|
|
581
|
-
|
|
492
|
+
function useTokens(withTestnets) {
|
|
493
|
+
// keep db data up to date
|
|
494
|
+
useDbCacheSubscription("tokens");
|
|
582
495
|
const {
|
|
583
|
-
|
|
584
|
-
|
|
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 => {
|
|
585
507
|
const chains = useChains(withTestnets);
|
|
586
508
|
const evmNetworks = useEvmNetworks(withTestnets);
|
|
587
509
|
const tokens = useTokens(withTestnets);
|
|
@@ -596,7 +518,7 @@ const useBalancesHydrate = () => {
|
|
|
596
518
|
|
|
597
519
|
function useBalances(addressesByToken) {
|
|
598
520
|
// keep db data up to date
|
|
599
|
-
|
|
521
|
+
useDbCacheSubscription("balances");
|
|
600
522
|
const balanceModules = useBalanceModules();
|
|
601
523
|
const {
|
|
602
524
|
balances
|
|
@@ -627,18 +549,18 @@ function useBalances(addressesByToken) {
|
|
|
627
549
|
const BalancesProvider = ({
|
|
628
550
|
balanceModules,
|
|
629
551
|
onfinalityApiKey,
|
|
630
|
-
|
|
552
|
+
useTestnets,
|
|
631
553
|
children
|
|
632
|
-
}) => /*#__PURE__*/jsx(
|
|
633
|
-
|
|
634
|
-
children: /*#__PURE__*/jsx(
|
|
554
|
+
}) => /*#__PURE__*/jsx(ChaindataProvider, {
|
|
555
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
556
|
+
children: /*#__PURE__*/jsx(ChainConnectorsProvider, {
|
|
635
557
|
onfinalityApiKey: onfinalityApiKey,
|
|
636
|
-
children: /*#__PURE__*/jsx(
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
children: /*#__PURE__*/jsx(
|
|
640
|
-
|
|
641
|
-
children: /*#__PURE__*/jsx(
|
|
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, {
|
|
642
564
|
children: children
|
|
643
565
|
})
|
|
644
566
|
})
|
|
@@ -647,4 +569,4 @@ const BalancesProvider = ({
|
|
|
647
569
|
})
|
|
648
570
|
});
|
|
649
571
|
|
|
650
|
-
export { AllAddressesProvider, BalanceModulesProvider, BalancesProvider, ChainConnectorsProvider, ChaindataProvider, DbCacheProvider,
|
|
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-
|
|
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-
|
|
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-
|
|
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,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 {};
|