@talismn/balances-react 0.0.0-pr586-20230301091501 → 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.
- 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 +290 -209
- package/dist/talismn-balances-react.cjs.prod.js +290 -209
- package/dist/talismn-balances-react.esm.js +288 -210
- package/package.json +4 -3
|
@@ -9,6 +9,7 @@ var tokenRates = require('@talismn/token-rates');
|
|
|
9
9
|
var dexieReactHooks = require('dexie-react-hooks');
|
|
10
10
|
var reactUse = require('react-use');
|
|
11
11
|
var chaindataProviderExtension = require('@talismn/chaindata-provider-extension');
|
|
12
|
+
var md5 = require('blueimp-md5');
|
|
12
13
|
var anylogger = require('anylogger');
|
|
13
14
|
var rxjs = require('rxjs');
|
|
14
15
|
var chainConnector = require('@talismn/chain-connector');
|
|
@@ -16,6 +17,7 @@ var chainConnectorEvm = require('@talismn/chain-connector-evm');
|
|
|
16
17
|
|
|
17
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
18
19
|
|
|
20
|
+
var md5__default = /*#__PURE__*/_interopDefault(md5);
|
|
19
21
|
var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
|
|
20
22
|
|
|
21
23
|
const provideContext = useProviderContext => {
|
|
@@ -52,13 +54,15 @@ const useBalanceModulesProvider = ({
|
|
|
52
54
|
const [BalanceModulesProvider, useBalanceModules] = provideContext(useBalanceModulesProvider);
|
|
53
55
|
|
|
54
56
|
function useChaindataProvider(options = {}) {
|
|
55
|
-
const [
|
|
57
|
+
const [onfinalityApiKey, setOnfinalityApiKey] = react.useState(options.onfinalityApiKey);
|
|
58
|
+
|
|
59
|
+
// make sure we recreate provider only when the onfinalityApiKey changes
|
|
56
60
|
react.useEffect(() => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
if (options.onfinalityApiKey !== onfinalityApiKey) setOnfinalityApiKey(options.onfinalityApiKey);
|
|
62
|
+
}, [options.onfinalityApiKey, onfinalityApiKey]);
|
|
63
|
+
return react.useMemo(() => new chaindataProviderExtension.ChaindataProviderExtension({
|
|
64
|
+
onfinalityApiKey
|
|
65
|
+
}), [onfinalityApiKey]);
|
|
62
66
|
}
|
|
63
67
|
const [ChaindataProvider, useChaindata] = provideContext(useChaindataProvider);
|
|
64
68
|
|
|
@@ -134,9 +138,7 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, tokenRates, al
|
|
|
134
138
|
balances
|
|
135
139
|
};
|
|
136
140
|
};
|
|
137
|
-
const useDbCacheProvider = ({
|
|
138
|
-
useTestnets = false
|
|
139
|
-
}) => {
|
|
141
|
+
const useDbCacheProvider = () => {
|
|
140
142
|
const chaindataProvider = useChaindata();
|
|
141
143
|
const chainList = dexieReactHooks.useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.chains(), [chaindataProvider]);
|
|
142
144
|
const evmNetworkList = dexieReactHooks.useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.evmNetworks(), [chaindataProvider]);
|
|
@@ -148,7 +150,7 @@ const useDbCacheProvider = ({
|
|
|
148
150
|
// debounce every 500ms to prevent hammering UI with updates
|
|
149
151
|
reactUse.useDebounce(() => {
|
|
150
152
|
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates$1, rawBalances));
|
|
151
|
-
}, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates$1
|
|
153
|
+
}, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates$1]);
|
|
152
154
|
const refInitialized = react.useRef(false);
|
|
153
155
|
|
|
154
156
|
// force an update as soon as all datasources are fetched, so UI can display data ASAP
|
|
@@ -157,14 +159,14 @@ const useDbCacheProvider = ({
|
|
|
157
159
|
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates$1, rawBalances));
|
|
158
160
|
refInitialized.current = true;
|
|
159
161
|
}
|
|
160
|
-
}, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates$1
|
|
162
|
+
}, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates$1]);
|
|
161
163
|
return dbData;
|
|
162
164
|
};
|
|
163
165
|
const [DbCacheProvider, useDbCache] = provideContext(useDbCacheProvider);
|
|
164
166
|
|
|
165
167
|
var packageJson = {
|
|
166
168
|
name: "@talismn/balances-react",
|
|
167
|
-
version: "0.0.0-
|
|
169
|
+
version: "0.0.0-pr587-20230301175306",
|
|
168
170
|
author: "Talisman",
|
|
169
171
|
homepage: "https://talisman.xyz",
|
|
170
172
|
license: "UNLICENSED",
|
|
@@ -197,6 +199,7 @@ var packageJson = {
|
|
|
197
199
|
"@talismn/chaindata-provider-extension": "workspace:^",
|
|
198
200
|
"@talismn/token-rates": "workspace:^",
|
|
199
201
|
anylogger: "^1.0.11",
|
|
202
|
+
"blueimp-md5": "2.19.0",
|
|
200
203
|
dexie: "^3.2.3",
|
|
201
204
|
"dexie-react-hooks": "^1.1.1",
|
|
202
205
|
"react-use": "^17.4.0",
|
|
@@ -227,6 +230,95 @@ var packageJson = {
|
|
|
227
230
|
|
|
228
231
|
var log = anylogger__default["default"](packageJson.name);
|
|
229
232
|
|
|
233
|
+
// global data store containing all subscriptions
|
|
234
|
+
const subscriptions = {};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* This hook ensures a subscription is created only once, and unsubscribe automatically as soon as there is no consumer to the hook
|
|
238
|
+
* @param key key that is unique to the subscription's parameters
|
|
239
|
+
* @param subscribe // subscribe function that will be shared by all consumers of the key
|
|
240
|
+
*/
|
|
241
|
+
const useSharedSubscription = (key, subscribe) => {
|
|
242
|
+
// create the rxJS subject if it doesn't exist
|
|
243
|
+
if (!subscriptions[key]) subscriptions[key] = {
|
|
244
|
+
subject: new rxjs.Subject()
|
|
245
|
+
};
|
|
246
|
+
react.useEffect(() => {
|
|
247
|
+
// subscribe to subject.
|
|
248
|
+
// it won't change but we need to count subscribers, to unsubscribe main subscription when no more observers
|
|
249
|
+
const s = subscriptions[key].subject.subscribe();
|
|
250
|
+
return () => {
|
|
251
|
+
// unsubscribe from our local observable updates to prevent memory leaks
|
|
252
|
+
s.unsubscribe();
|
|
253
|
+
const {
|
|
254
|
+
subject,
|
|
255
|
+
unsubscribe
|
|
256
|
+
} = subscriptions[key];
|
|
257
|
+
if (!subject.observed && unsubscribe) {
|
|
258
|
+
log.debug(`[useSharedSubscription] unsubscribing ${key}`);
|
|
259
|
+
|
|
260
|
+
// unsubscribe from backend updates to prevent unnecessary network connections
|
|
261
|
+
unsubscribe();
|
|
262
|
+
delete subscriptions[key].unsubscribe;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}, [key]);
|
|
266
|
+
|
|
267
|
+
// Initialize subscription
|
|
268
|
+
react.useEffect(() => {
|
|
269
|
+
const {
|
|
270
|
+
unsubscribe
|
|
271
|
+
} = subscriptions[key];
|
|
272
|
+
// launch the subscription if it's a new key
|
|
273
|
+
if (!unsubscribe) {
|
|
274
|
+
const cb = subscribe();
|
|
275
|
+
log.debug(`[useSharedSubscription] subscribing ${key}`);
|
|
276
|
+
if (cb) subscriptions[key].unsubscribe = cb;
|
|
277
|
+
// this error should only happen when developping a new hook, let it bubble up
|
|
278
|
+
else throw new Error(`${key} subscribe did not return an unsubscribe callback`);
|
|
279
|
+
}
|
|
280
|
+
}, [key, subscribe]);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
function useChainConnectorsProvider(options) {
|
|
284
|
+
const [onfinalityApiKey, setOnfinalityApiKey] = react.useState(options.onfinalityApiKey);
|
|
285
|
+
|
|
286
|
+
// make sure we recreate provider only when the onfinalityApiKey changes
|
|
287
|
+
react.useEffect(() => {
|
|
288
|
+
if (options.onfinalityApiKey !== onfinalityApiKey) setOnfinalityApiKey(options.onfinalityApiKey);
|
|
289
|
+
}, [options.onfinalityApiKey, onfinalityApiKey]);
|
|
290
|
+
|
|
291
|
+
// chaindata dependency
|
|
292
|
+
const chaindata = useChaindata();
|
|
293
|
+
|
|
294
|
+
// substrate connector
|
|
295
|
+
const substrate = react.useMemo(() => new chainConnector.ChainConnector(chaindata), [chaindata]);
|
|
296
|
+
|
|
297
|
+
// evm connector
|
|
298
|
+
const evm = react.useMemo(() => new chainConnectorEvm.ChainConnectorEvm(chaindata, {
|
|
299
|
+
onfinalityApiKey
|
|
300
|
+
}), [chaindata, onfinalityApiKey]);
|
|
301
|
+
return react.useMemo(() => ({
|
|
302
|
+
substrate,
|
|
303
|
+
evm
|
|
304
|
+
}), [substrate, evm]);
|
|
305
|
+
}
|
|
306
|
+
const [ChainConnectorsProvider, useChainConnectors] = provideContext(useChainConnectorsProvider);
|
|
307
|
+
|
|
308
|
+
function useTokens(withTestnets) {
|
|
309
|
+
// keep db data up to date
|
|
310
|
+
useDbCacheSubscription("tokens");
|
|
311
|
+
const {
|
|
312
|
+
tokensWithTestnetsMap,
|
|
313
|
+
tokensWithoutTestnetsMap
|
|
314
|
+
} = useDbCache();
|
|
315
|
+
return withTestnets ? tokensWithTestnetsMap : tokensWithoutTestnetsMap;
|
|
316
|
+
}
|
|
317
|
+
function useToken(tokenId, withTestnets) {
|
|
318
|
+
const tokens = useTokens(withTestnets);
|
|
319
|
+
return tokenId ? tokens[tokenId] : undefined;
|
|
320
|
+
}
|
|
321
|
+
|
|
230
322
|
/**
|
|
231
323
|
* Creates a subscription function that can be used to subscribe to a multicast observable created from an upstream source.
|
|
232
324
|
*
|
|
@@ -268,193 +360,191 @@ const createMulticastSubscription = upstream => {
|
|
|
268
360
|
return subscribe;
|
|
269
361
|
};
|
|
270
362
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
setSubstrate(new chainConnector.ChainConnector(chaindata));
|
|
280
|
-
}, [chaindata]);
|
|
281
|
-
|
|
282
|
-
// evm connector
|
|
283
|
-
const [evm, setEvm] = react.useState();
|
|
284
|
-
react.useEffect(() => {
|
|
285
|
-
if (!chaindata) return;
|
|
286
|
-
setEvm(new chainConnectorEvm.ChainConnectorEvm(chaindata, {
|
|
287
|
-
onfinalityApiKey: options.onfinalityApiKey
|
|
288
|
-
}));
|
|
289
|
-
}, [chaindata, options.onfinalityApiKey]);
|
|
290
|
-
return react.useMemo(() => ({
|
|
291
|
-
substrate,
|
|
292
|
-
evm
|
|
293
|
-
}), [substrate, evm]);
|
|
294
|
-
}
|
|
295
|
-
const [ChainConnectorsProvider, useChainConnectors] = provideContext(useChainConnectorsProvider);
|
|
296
|
-
|
|
297
|
-
const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeTokenRates(), useSubscribeBalances()];
|
|
298
|
-
const [SubscriptionsProvider, useSubscriptions] = provideContext(useSubscriptionsProvider);
|
|
363
|
+
const useWithTestnetsProvider = ({
|
|
364
|
+
withTestnets
|
|
365
|
+
}) => {
|
|
366
|
+
return {
|
|
367
|
+
withTestnets
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
const [WithTestnetsProvider, useWithTestnets] = provideContext(useWithTestnetsProvider);
|
|
299
371
|
|
|
300
372
|
/**
|
|
301
373
|
* This hook is responsible for fetching the data used for balances and inserting it into the db.
|
|
302
374
|
*/
|
|
303
375
|
const useDbCacheSubscription = subscribeTo => {
|
|
304
|
-
const
|
|
305
|
-
|
|
376
|
+
const provider = useChaindata();
|
|
377
|
+
|
|
378
|
+
// can't handle balances & tokenRates here as they have other dependencies, it would trigger to many subscriptions
|
|
379
|
+
const subscribe = react.useCallback(() => {
|
|
306
380
|
switch (subscribeTo) {
|
|
307
381
|
case "chains":
|
|
308
|
-
return
|
|
382
|
+
return subscribeChainDataHydrate(provider, "chains");
|
|
309
383
|
case "evmNetworks":
|
|
310
|
-
return
|
|
384
|
+
return subscribeChainDataHydrate(provider, "evmNetworks");
|
|
311
385
|
case "tokens":
|
|
312
|
-
return
|
|
313
|
-
case "tokenRates":
|
|
314
|
-
return subscribeTokenRates();
|
|
315
|
-
case "balances":
|
|
316
|
-
return subscribeBalances();
|
|
386
|
+
return subscribeChainDataHydrate(provider, "tokens");
|
|
317
387
|
}
|
|
318
|
-
}, [
|
|
388
|
+
}, [provider, subscribeTo]);
|
|
389
|
+
useSharedSubscription(subscribeTo, subscribe);
|
|
319
390
|
};
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
hydrate();
|
|
343
|
-
return () => {
|
|
344
|
-
active = false;
|
|
345
|
-
};
|
|
346
|
-
}, [chaindata, type]);
|
|
347
|
-
const subscribe = useMulticastSubscription(createSubscription);
|
|
348
|
-
return subscribe;
|
|
349
|
-
}
|
|
350
|
-
function useSubscribeTokenRates() {
|
|
351
|
-
const chaindataProvider = useChaindata();
|
|
352
|
-
const tokens = dexieReactHooks.useLiveQuery(() => chaindataProvider === null || chaindataProvider === void 0 ? void 0 : chaindataProvider.tokens(), [chaindataProvider]);
|
|
353
|
-
const generationRef = react.useRef(0);
|
|
354
|
-
const createSubscription = react.useCallback(() => {
|
|
355
|
-
if (!chaindataProvider) return;
|
|
356
|
-
if (!tokens) return;
|
|
357
|
-
if (Object.keys(tokens).length < 1) return;
|
|
358
|
-
|
|
359
|
-
// when we make a new request, we want to ignore any old requests which haven't yet completed
|
|
360
|
-
// otherwise we risk replacing the most recent data with older data
|
|
361
|
-
const generation = (generationRef.current + 1) % Number.MAX_SAFE_INTEGER;
|
|
362
|
-
generationRef.current = generation;
|
|
363
|
-
let active = true;
|
|
364
|
-
const REFRESH_INTERVAL = 300_000; // 300_000ms = 5 minutes
|
|
365
|
-
const RETRY_INTERVAL = 5_000; // 5_000ms = 5 seconds
|
|
366
|
-
|
|
367
|
-
const hydrate = async () => {
|
|
368
|
-
if (!active) return;
|
|
369
|
-
if (generationRef.current !== generation) return;
|
|
370
|
-
try {
|
|
371
|
-
const tokenRates$1 = await tokenRates.fetchTokenRates(tokens);
|
|
372
|
-
if (!active) return;
|
|
373
|
-
if (generationRef.current !== generation) return;
|
|
374
|
-
const putTokenRates = Object.entries(tokenRates$1).map(([tokenId, rates]) => ({
|
|
375
|
-
tokenId,
|
|
376
|
-
rates
|
|
377
|
-
}));
|
|
378
|
-
tokenRates.db.transaction("rw", tokenRates.db.tokenRates, async () => await tokenRates.db.tokenRates.bulkPut(putTokenRates));
|
|
379
|
-
setTimeout(hydrate, REFRESH_INTERVAL);
|
|
380
|
-
} catch (error) {
|
|
381
|
-
log.error(`Failed to fetch tokenRates, retrying in ${Math.round(RETRY_INTERVAL / 1000)} seconds`, error);
|
|
382
|
-
setTimeout(hydrate, RETRY_INTERVAL);
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
hydrate();
|
|
386
|
-
return () => {
|
|
387
|
-
active = false;
|
|
388
|
-
};
|
|
389
|
-
}, [chaindataProvider, tokens]);
|
|
390
|
-
const subscribe = useMulticastSubscription(createSubscription);
|
|
391
|
-
return subscribe;
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* This hook is responsible for fetching the data used for token rates and inserting it into the db.
|
|
394
|
+
*/
|
|
395
|
+
function useDbCacheTokenRatesSubscription() {
|
|
396
|
+
const {
|
|
397
|
+
withTestnets
|
|
398
|
+
} = useWithTestnets();
|
|
399
|
+
const tokens = useTokens(withTestnets);
|
|
400
|
+
const subscriptionKey = react.useMemo(
|
|
401
|
+
// not super sexy but we need key to change based on this stuff
|
|
402
|
+
() => {
|
|
403
|
+
const key = Object.values(tokens ?? {}).map(({
|
|
404
|
+
id
|
|
405
|
+
}) => id).sort().join();
|
|
406
|
+
return `tokenRates-${md5__default["default"](key)}`;
|
|
407
|
+
}, [tokens]);
|
|
408
|
+
const subscription = react.useCallback(() => {
|
|
409
|
+
if (!Object.values(tokens ?? {}).length) return () => {};
|
|
410
|
+
return subscribeTokenRates(tokens);
|
|
411
|
+
}, [tokens]);
|
|
412
|
+
useSharedSubscription(subscriptionKey, subscription);
|
|
392
413
|
}
|
|
393
|
-
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* This hook is responsible for fetching the data used for balances and inserting it into the db.
|
|
417
|
+
*/
|
|
418
|
+
function useDbCacheBalancesSubscription() {
|
|
419
|
+
const {
|
|
420
|
+
withTestnets
|
|
421
|
+
} = useWithTestnets();
|
|
394
422
|
const balanceModules = useBalanceModules();
|
|
395
423
|
const chaindataProvider = useChaindata();
|
|
396
424
|
const chainConnectors = useChainConnectors();
|
|
397
425
|
const [allAddresses] = useAllAddresses();
|
|
398
|
-
const tokens =
|
|
399
|
-
const
|
|
426
|
+
const tokens = useTokens(withTestnets);
|
|
427
|
+
const subscriptionKey = react.useMemo(
|
|
428
|
+
// not super sexy but we need key to change based on this stuff
|
|
429
|
+
() => {
|
|
430
|
+
const key = allAddresses.sort().join().concat(...Object.values(tokens ?? {}).map(({
|
|
431
|
+
id
|
|
432
|
+
}) => id).sort()).concat(`evm:${!!chainConnectors.evm}`, `sub:${!!chainConnectors.substrate}`, ...balanceModules.map(m => m.type).sort(), `cd:${!!chaindataProvider}`);
|
|
433
|
+
return `balances-${md5__default["default"](key)}`;
|
|
434
|
+
}, [allAddresses, balanceModules, chainConnectors, chaindataProvider, tokens]);
|
|
435
|
+
const subscription = react.useCallback(() => {
|
|
436
|
+
if (!Object.values(tokens ?? {}).length || !allAddresses.length) return () => {};
|
|
437
|
+
return subscribeBalances(tokens ?? {}, allAddresses, chainConnectors, chaindataProvider, balanceModules);
|
|
438
|
+
}, [allAddresses, balanceModules, chainConnectors, chaindataProvider, tokens]);
|
|
439
|
+
useSharedSubscription(subscriptionKey, subscription);
|
|
440
|
+
}
|
|
441
|
+
const subscribeChainDataHydrate = (provider, type) => {
|
|
442
|
+
const chaindata = provider;
|
|
443
|
+
const delay = 300_000; // 300_000ms = 300s = 5 minutes
|
|
444
|
+
|
|
445
|
+
let timeout = null;
|
|
446
|
+
const hydrate = async () => {
|
|
447
|
+
try {
|
|
448
|
+
if (type === "chains") await chaindata.hydrateChains();
|
|
449
|
+
if (type === "evmNetworks") await chaindata.hydrateEvmNetworks();
|
|
450
|
+
if (type === "tokens") await chaindata.hydrateTokens();
|
|
451
|
+
timeout = setTimeout(hydrate, delay);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
const retryTimeout = 5_000; // 5_000ms = 5 seconds
|
|
454
|
+
log.error(`Failed to fetch chaindata, retrying in ${Math.round(retryTimeout / 1000)} seconds`, error);
|
|
455
|
+
timeout = setTimeout(hydrate, retryTimeout);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// launch the loop
|
|
460
|
+
hydrate();
|
|
461
|
+
return () => {
|
|
462
|
+
if (timeout) clearTimeout(timeout);
|
|
463
|
+
};
|
|
464
|
+
};
|
|
465
|
+
const subscribeTokenRates = tokens => {
|
|
466
|
+
const REFRESH_INTERVAL = 300_000; // 6 minutes
|
|
467
|
+
const RETRY_INTERVAL = 5_000; // 5 sec
|
|
468
|
+
|
|
469
|
+
let timeout = null;
|
|
470
|
+
const refreshTokenRates = async () => {
|
|
471
|
+
try {
|
|
472
|
+
if (timeout) clearTimeout(timeout);
|
|
473
|
+
const tokenRates$1 = await tokenRates.fetchTokenRates(tokens);
|
|
474
|
+
const putTokenRates = Object.entries(tokenRates$1).map(([tokenId, rates]) => ({
|
|
475
|
+
tokenId,
|
|
476
|
+
rates
|
|
477
|
+
}));
|
|
478
|
+
tokenRates.db.transaction("rw", tokenRates.db.tokenRates, async () => await tokenRates.db.tokenRates.bulkPut(putTokenRates));
|
|
479
|
+
timeout = setTimeout(() => {
|
|
480
|
+
refreshTokenRates();
|
|
481
|
+
}, REFRESH_INTERVAL);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
log.error(`Failed to fetch tokenRates, retrying in ${Math.round(RETRY_INTERVAL / 1000)} seconds`, error);
|
|
484
|
+
setTimeout(async () => {
|
|
485
|
+
refreshTokenRates();
|
|
486
|
+
}, RETRY_INTERVAL);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// launch the loop
|
|
491
|
+
refreshTokenRates();
|
|
492
|
+
return () => {
|
|
493
|
+
if (timeout) clearTimeout(timeout);
|
|
494
|
+
};
|
|
495
|
+
};
|
|
496
|
+
const subscribeBalances = (tokens, addresses, chainConnectors, provider, balanceModules) => {
|
|
497
|
+
const tokenIds = Object.values(tokens).map(({
|
|
400
498
|
id
|
|
401
|
-
}) => id)
|
|
402
|
-
const addressesByToken =
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
unsub.then(unsubscribe => unsubscribe());
|
|
431
|
-
|
|
432
|
-
// set this subscription's balances in the store to status: cache
|
|
433
|
-
balances.db.transaction("rw", balances.db.balances, async () => await balances.db.balances.filter(balance => {
|
|
434
|
-
if (balance.source !== balanceModule.type) return false;
|
|
435
|
-
if (!Object.keys(addressesByModuleToken).includes(balance.tokenId)) return false;
|
|
436
|
-
if (!addressesByModuleToken[balance.tokenId].includes(balance.address)) return false;
|
|
437
|
-
return true;
|
|
438
|
-
}).modify({
|
|
439
|
-
status: "cache"
|
|
440
|
-
}));
|
|
441
|
-
};
|
|
442
|
-
});
|
|
443
|
-
const unsubscribe = subscribe(balances$1 => {
|
|
444
|
-
const putBalances = Object.entries(balances$1.toJSON()).map(([id, balance]) => ({
|
|
445
|
-
id,
|
|
446
|
-
...balance
|
|
447
|
-
}));
|
|
448
|
-
balances.db.transaction("rw", balances.db.balances, async () => await balances.db.balances.bulkPut(putBalances));
|
|
449
|
-
});
|
|
450
|
-
return unsubscribe;
|
|
499
|
+
}) => id);
|
|
500
|
+
const addressesByToken = Object.fromEntries(tokenIds.map(tokenId => [tokenId, addresses]));
|
|
501
|
+
const updateDb = balances$1 => {
|
|
502
|
+
const putBalances = Object.entries(balances$1.toJSON()).map(([id, balance]) => ({
|
|
503
|
+
id,
|
|
504
|
+
...balance
|
|
505
|
+
}));
|
|
506
|
+
balances.db.transaction("rw", balances.db.balances, async () => await balances.db.balances.bulkPut(putBalances));
|
|
507
|
+
};
|
|
508
|
+
let unsubscribed = false;
|
|
509
|
+
|
|
510
|
+
// eslint-disable-next-line no-console
|
|
511
|
+
log.log("subscribing to balance changes for %d tokens and %d addresses", tokenIds.length, addresses.length);
|
|
512
|
+
const unsubs = balanceModules.map(async balanceModule => {
|
|
513
|
+
// filter out tokens to only include those which this module knows how to fetch balances for
|
|
514
|
+
const moduleTokenIds = Object.values(tokens ?? {}).filter(({
|
|
515
|
+
type
|
|
516
|
+
}) => type === balanceModule.type).map(({
|
|
517
|
+
id
|
|
518
|
+
}) => id);
|
|
519
|
+
const addressesByModuleToken = Object.fromEntries(Object.entries(addressesByToken).filter(([tokenId]) => moduleTokenIds.includes(tokenId)));
|
|
520
|
+
const unsub = balances.balances(balanceModule, chainConnectors, provider, addressesByModuleToken, (error, balances) => {
|
|
521
|
+
// log errors
|
|
522
|
+
if (error) return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
|
|
523
|
+
// ignore empty balance responses
|
|
524
|
+
if (!balances) return;
|
|
525
|
+
// ignore balances from old subscriptions which are still in the process of unsubscribing
|
|
526
|
+
if (unsubscribed) return;
|
|
527
|
+
updateDb(balances);
|
|
451
528
|
});
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
529
|
+
return () => {
|
|
530
|
+
// TODO should we add a timeout before unsubscribe to prevent closing sockets to quickly ?
|
|
531
|
+
unsub.then(unsubscribe => unsubscribe());
|
|
532
|
+
balances.db.transaction("rw", balances.db.balances, async () => await balances.db.balances.filter(balance => {
|
|
533
|
+
if (balance.source !== balanceModule.type) return false;
|
|
534
|
+
if (!Object.keys(addressesByModuleToken).includes(balance.tokenId)) return false;
|
|
535
|
+
if (!addressesByModuleToken[balance.tokenId].includes(balance.address)) return false;
|
|
536
|
+
return true;
|
|
537
|
+
}).modify({
|
|
538
|
+
status: "cache"
|
|
539
|
+
}));
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
const unsubscribeAll = () => {
|
|
543
|
+
unsubscribed = true;
|
|
544
|
+
unsubs.forEach(unsub => unsub.then(unsubscribe => unsubscribe()));
|
|
545
|
+
};
|
|
546
|
+
return unsubscribeAll;
|
|
547
|
+
};
|
|
458
548
|
|
|
459
549
|
function useChains(withTestnets) {
|
|
460
550
|
// keep db data up to date
|
|
@@ -486,7 +576,7 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
|
|
|
486
576
|
|
|
487
577
|
function useTokenRates() {
|
|
488
578
|
// keep db data up to date
|
|
489
|
-
|
|
579
|
+
useDbCacheTokenRatesSubscription();
|
|
490
580
|
const {
|
|
491
581
|
tokenRatesMap
|
|
492
582
|
} = useDbCache();
|
|
@@ -497,21 +587,10 @@ function useTokenRate(tokenId) {
|
|
|
497
587
|
return tokenId ? tokenRates[tokenId] : undefined;
|
|
498
588
|
}
|
|
499
589
|
|
|
500
|
-
|
|
501
|
-
// keep db data up to date
|
|
502
|
-
useDbCacheSubscription("tokens");
|
|
590
|
+
const useBalancesHydrate = () => {
|
|
503
591
|
const {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
} = useDbCache();
|
|
507
|
-
return withTestnets ? tokensWithTestnetsMap : tokensWithoutTestnetsMap;
|
|
508
|
-
}
|
|
509
|
-
function useToken(tokenId, withTestnets) {
|
|
510
|
-
const tokens = useTokens(withTestnets);
|
|
511
|
-
return tokenId ? tokens[tokenId] : undefined;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
const useBalancesHydrate = withTestnets => {
|
|
592
|
+
withTestnets
|
|
593
|
+
} = useWithTestnets();
|
|
515
594
|
const chains = useChains(withTestnets);
|
|
516
595
|
const evmNetworks = useEvmNetworks(withTestnets);
|
|
517
596
|
const tokens = useTokens(withTestnets);
|
|
@@ -526,7 +605,7 @@ const useBalancesHydrate = withTestnets => {
|
|
|
526
605
|
|
|
527
606
|
function useBalances(addressesByToken) {
|
|
528
607
|
// keep db data up to date
|
|
529
|
-
|
|
608
|
+
useDbCacheBalancesSubscription();
|
|
530
609
|
const balanceModules = useBalanceModules();
|
|
531
610
|
const {
|
|
532
611
|
balances: balances$1
|
|
@@ -557,18 +636,18 @@ function useBalances(addressesByToken) {
|
|
|
557
636
|
const BalancesProvider = ({
|
|
558
637
|
balanceModules,
|
|
559
638
|
onfinalityApiKey,
|
|
560
|
-
|
|
639
|
+
withTestnets,
|
|
561
640
|
children
|
|
562
|
-
}) => /*#__PURE__*/jsxRuntime.jsx(
|
|
563
|
-
|
|
564
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
641
|
+
}) => /*#__PURE__*/jsxRuntime.jsx(WithTestnetsProvider, {
|
|
642
|
+
withTestnets: withTestnets,
|
|
643
|
+
children: /*#__PURE__*/jsxRuntime.jsx(ChaindataProvider, {
|
|
565
644
|
onfinalityApiKey: onfinalityApiKey,
|
|
566
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
570
|
-
|
|
571
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
645
|
+
children: /*#__PURE__*/jsxRuntime.jsx(ChainConnectorsProvider, {
|
|
646
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
647
|
+
children: /*#__PURE__*/jsxRuntime.jsx(AllAddressesProvider, {
|
|
648
|
+
children: /*#__PURE__*/jsxRuntime.jsx(BalanceModulesProvider, {
|
|
649
|
+
balanceModules: balanceModules,
|
|
650
|
+
children: /*#__PURE__*/jsxRuntime.jsx(DbCacheProvider, {
|
|
572
651
|
children: children
|
|
573
652
|
})
|
|
574
653
|
})
|
|
@@ -583,7 +662,7 @@ exports.BalancesProvider = BalancesProvider;
|
|
|
583
662
|
exports.ChainConnectorsProvider = ChainConnectorsProvider;
|
|
584
663
|
exports.ChaindataProvider = ChaindataProvider;
|
|
585
664
|
exports.DbCacheProvider = DbCacheProvider;
|
|
586
|
-
exports.
|
|
665
|
+
exports.WithTestnetsProvider = WithTestnetsProvider;
|
|
587
666
|
exports.createMulticastSubscription = createMulticastSubscription;
|
|
588
667
|
exports.provideContext = provideContext;
|
|
589
668
|
exports.useAllAddresses = useAllAddresses;
|
|
@@ -595,12 +674,14 @@ exports.useChainConnectors = useChainConnectors;
|
|
|
595
674
|
exports.useChaindata = useChaindata;
|
|
596
675
|
exports.useChains = useChains;
|
|
597
676
|
exports.useDbCache = useDbCache;
|
|
677
|
+
exports.useDbCacheBalancesSubscription = useDbCacheBalancesSubscription;
|
|
598
678
|
exports.useDbCacheSubscription = useDbCacheSubscription;
|
|
679
|
+
exports.useDbCacheTokenRatesSubscription = useDbCacheTokenRatesSubscription;
|
|
599
680
|
exports.useEvmNetwork = useEvmNetwork;
|
|
600
681
|
exports.useEvmNetworks = useEvmNetworks;
|
|
601
682
|
exports.useMulticastSubscription = useMulticastSubscription;
|
|
602
|
-
exports.useSubscriptions = useSubscriptions;
|
|
603
683
|
exports.useToken = useToken;
|
|
604
684
|
exports.useTokenRate = useTokenRate;
|
|
605
685
|
exports.useTokenRates = useTokenRates;
|
|
606
686
|
exports.useTokens = useTokens;
|
|
687
|
+
exports.useWithTestnets = useWithTestnets;
|