@talismn/balances-react 0.0.0-pr593-20230303151944 → 0.0.0-pr593-20230306050934
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 +7 -3
- package/dist/declarations/src/hooks/index.d.ts +3 -2
- 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 +1 -4
- package/dist/declarations/src/hooks/useDbCacheSubscription.d.ts +9 -5
- package/dist/declarations/src/hooks/useWithTestnets.d.ts +8 -0
- package/dist/declarations/src/util/useSharedSubscription.d.ts +9 -0
- package/dist/talismn-balances-react.cjs.dev.js +292 -209
- package/dist/talismn-balances-react.cjs.prod.js +292 -209
- package/dist/talismn-balances-react.esm.js +290 -210
- package/package.json +4 -3
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { useContext, createContext, useState, useEffect,
|
|
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 [
|
|
48
|
+
const [onfinalityApiKey, setOnfinalityApiKey] = useState(options.onfinalityApiKey);
|
|
49
|
+
|
|
50
|
+
// make sure we recreate provider only when the onfinalityApiKey changes
|
|
48
51
|
useEffect(() => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
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
|
|
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-pr593-
|
|
160
|
+
version: "0.0.0-pr593-20230306050934",
|
|
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,193 @@ const createMulticastSubscription = upstream => {
|
|
|
260
351
|
return subscribe;
|
|
261
352
|
};
|
|
262
353
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
297
|
-
|
|
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
|
|
373
|
+
return subscribeChainDataHydrate(provider, "chains");
|
|
301
374
|
case "evmNetworks":
|
|
302
|
-
return
|
|
375
|
+
return subscribeChainDataHydrate(provider, "evmNetworks");
|
|
303
376
|
case "tokens":
|
|
304
|
-
return
|
|
305
|
-
case "tokenRates":
|
|
306
|
-
return subscribeTokenRates();
|
|
307
|
-
case "balances":
|
|
308
|
-
return subscribeBalances();
|
|
377
|
+
return subscribeChainDataHydrate(provider, "tokens");
|
|
309
378
|
}
|
|
310
|
-
}, [
|
|
379
|
+
}, [provider, subscribeTo]);
|
|
380
|
+
useSharedSubscription(subscribeTo, subscribe);
|
|
311
381
|
};
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
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 =
|
|
391
|
-
const
|
|
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)
|
|
394
|
-
const addressesByToken =
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
520
|
+
return () => {
|
|
521
|
+
// wait 2 seconds before actually unsubscribing, allowing for websocket to be reused
|
|
522
|
+
unsub.then(unsubscribe => {
|
|
523
|
+
setTimeout(unsubscribe, 2_000);
|
|
524
|
+
});
|
|
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
|
+
};
|
|
450
541
|
|
|
451
542
|
function useChains(withTestnets) {
|
|
452
543
|
// keep db data up to date
|
|
@@ -478,7 +569,7 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
|
|
|
478
569
|
|
|
479
570
|
function useTokenRates() {
|
|
480
571
|
// keep db data up to date
|
|
481
|
-
|
|
572
|
+
useDbCacheTokenRatesSubscription();
|
|
482
573
|
const {
|
|
483
574
|
tokenRatesMap
|
|
484
575
|
} = useDbCache();
|
|
@@ -489,21 +580,10 @@ function useTokenRate(tokenId) {
|
|
|
489
580
|
return tokenId ? tokenRates[tokenId] : undefined;
|
|
490
581
|
}
|
|
491
582
|
|
|
492
|
-
|
|
493
|
-
// keep db data up to date
|
|
494
|
-
useDbCacheSubscription("tokens");
|
|
583
|
+
const useBalancesHydrate = () => {
|
|
495
584
|
const {
|
|
496
|
-
|
|
497
|
-
|
|
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
|
+
withTestnets
|
|
586
|
+
} = useWithTestnets();
|
|
507
587
|
const chains = useChains(withTestnets);
|
|
508
588
|
const evmNetworks = useEvmNetworks(withTestnets);
|
|
509
589
|
const tokens = useTokens(withTestnets);
|
|
@@ -518,7 +598,7 @@ const useBalancesHydrate = withTestnets => {
|
|
|
518
598
|
|
|
519
599
|
function useBalances(addressesByToken) {
|
|
520
600
|
// keep db data up to date
|
|
521
|
-
|
|
601
|
+
useDbCacheBalancesSubscription();
|
|
522
602
|
const balanceModules = useBalanceModules();
|
|
523
603
|
const {
|
|
524
604
|
balances
|
|
@@ -549,18 +629,18 @@ function useBalances(addressesByToken) {
|
|
|
549
629
|
const BalancesProvider = ({
|
|
550
630
|
balanceModules,
|
|
551
631
|
onfinalityApiKey,
|
|
552
|
-
|
|
632
|
+
withTestnets,
|
|
553
633
|
children
|
|
554
|
-
}) => /*#__PURE__*/jsx(
|
|
555
|
-
|
|
556
|
-
children: /*#__PURE__*/jsx(
|
|
634
|
+
}) => /*#__PURE__*/jsx(WithTestnetsProvider, {
|
|
635
|
+
withTestnets: withTestnets,
|
|
636
|
+
children: /*#__PURE__*/jsx(ChaindataProvider, {
|
|
557
637
|
onfinalityApiKey: onfinalityApiKey,
|
|
558
|
-
children: /*#__PURE__*/jsx(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
children: /*#__PURE__*/jsx(
|
|
562
|
-
|
|
563
|
-
children: /*#__PURE__*/jsx(
|
|
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, {
|
|
564
644
|
children: children
|
|
565
645
|
})
|
|
566
646
|
})
|
|
@@ -569,4 +649,4 @@ const BalancesProvider = ({
|
|
|
569
649
|
})
|
|
570
650
|
});
|
|
571
651
|
|
|
572
|
-
export { AllAddressesProvider, BalanceModulesProvider, BalancesProvider, ChainConnectorsProvider, ChaindataProvider, DbCacheProvider,
|
|
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@talismn/balances-react",
|
|
3
|
-
"version": "0.0.0-pr593-
|
|
3
|
+
"version": "0.0.0-pr593-20230306050934",
|
|
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-pr593-
|
|
29
|
+
"@talismn/balances": "^0.0.0-pr593-20230306050934",
|
|
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-pr593-
|
|
34
|
+
"@talismn/token-rates": "^0.0.0-pr593-20230306050934",
|
|
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",
|