@talismn/balances-react 0.0.0-pr589-20230302073822 → 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
|
@@ -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-pr593-20230306050934",
|
|
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,193 @@ 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
|
+
// wait 2 seconds before actually unsubscribing, allowing for websocket to be reused
|
|
531
|
+
unsub.then(unsubscribe => {
|
|
532
|
+
setTimeout(unsubscribe, 2_000);
|
|
533
|
+
});
|
|
534
|
+
balances.db.transaction("rw", balances.db.balances, async () => await balances.db.balances.filter(balance => {
|
|
535
|
+
if (balance.source !== balanceModule.type) return false;
|
|
536
|
+
if (!Object.keys(addressesByModuleToken).includes(balance.tokenId)) return false;
|
|
537
|
+
if (!addressesByModuleToken[balance.tokenId].includes(balance.address)) return false;
|
|
538
|
+
return true;
|
|
539
|
+
}).modify({
|
|
540
|
+
status: "cache"
|
|
541
|
+
}));
|
|
542
|
+
};
|
|
543
|
+
});
|
|
544
|
+
const unsubscribeAll = () => {
|
|
545
|
+
unsubscribed = true;
|
|
546
|
+
unsubs.forEach(unsub => unsub.then(unsubscribe => unsubscribe()));
|
|
547
|
+
};
|
|
548
|
+
return unsubscribeAll;
|
|
549
|
+
};
|
|
458
550
|
|
|
459
551
|
function useChains(withTestnets) {
|
|
460
552
|
// keep db data up to date
|
|
@@ -486,7 +578,7 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
|
|
|
486
578
|
|
|
487
579
|
function useTokenRates() {
|
|
488
580
|
// keep db data up to date
|
|
489
|
-
|
|
581
|
+
useDbCacheTokenRatesSubscription();
|
|
490
582
|
const {
|
|
491
583
|
tokenRatesMap
|
|
492
584
|
} = useDbCache();
|
|
@@ -497,21 +589,10 @@ function useTokenRate(tokenId) {
|
|
|
497
589
|
return tokenId ? tokenRates[tokenId] : undefined;
|
|
498
590
|
}
|
|
499
591
|
|
|
500
|
-
|
|
501
|
-
// keep db data up to date
|
|
502
|
-
useDbCacheSubscription("tokens");
|
|
592
|
+
const useBalancesHydrate = () => {
|
|
503
593
|
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 => {
|
|
594
|
+
withTestnets
|
|
595
|
+
} = useWithTestnets();
|
|
515
596
|
const chains = useChains(withTestnets);
|
|
516
597
|
const evmNetworks = useEvmNetworks(withTestnets);
|
|
517
598
|
const tokens = useTokens(withTestnets);
|
|
@@ -526,7 +607,7 @@ const useBalancesHydrate = withTestnets => {
|
|
|
526
607
|
|
|
527
608
|
function useBalances(addressesByToken) {
|
|
528
609
|
// keep db data up to date
|
|
529
|
-
|
|
610
|
+
useDbCacheBalancesSubscription();
|
|
530
611
|
const balanceModules = useBalanceModules();
|
|
531
612
|
const {
|
|
532
613
|
balances: balances$1
|
|
@@ -557,18 +638,18 @@ function useBalances(addressesByToken) {
|
|
|
557
638
|
const BalancesProvider = ({
|
|
558
639
|
balanceModules,
|
|
559
640
|
onfinalityApiKey,
|
|
560
|
-
|
|
641
|
+
withTestnets,
|
|
561
642
|
children
|
|
562
|
-
}) => /*#__PURE__*/jsxRuntime.jsx(
|
|
563
|
-
|
|
564
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
643
|
+
}) => /*#__PURE__*/jsxRuntime.jsx(WithTestnetsProvider, {
|
|
644
|
+
withTestnets: withTestnets,
|
|
645
|
+
children: /*#__PURE__*/jsxRuntime.jsx(ChaindataProvider, {
|
|
565
646
|
onfinalityApiKey: onfinalityApiKey,
|
|
566
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
570
|
-
|
|
571
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
647
|
+
children: /*#__PURE__*/jsxRuntime.jsx(ChainConnectorsProvider, {
|
|
648
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
649
|
+
children: /*#__PURE__*/jsxRuntime.jsx(AllAddressesProvider, {
|
|
650
|
+
children: /*#__PURE__*/jsxRuntime.jsx(BalanceModulesProvider, {
|
|
651
|
+
balanceModules: balanceModules,
|
|
652
|
+
children: /*#__PURE__*/jsxRuntime.jsx(DbCacheProvider, {
|
|
572
653
|
children: children
|
|
573
654
|
})
|
|
574
655
|
})
|
|
@@ -583,7 +664,7 @@ exports.BalancesProvider = BalancesProvider;
|
|
|
583
664
|
exports.ChainConnectorsProvider = ChainConnectorsProvider;
|
|
584
665
|
exports.ChaindataProvider = ChaindataProvider;
|
|
585
666
|
exports.DbCacheProvider = DbCacheProvider;
|
|
586
|
-
exports.
|
|
667
|
+
exports.WithTestnetsProvider = WithTestnetsProvider;
|
|
587
668
|
exports.createMulticastSubscription = createMulticastSubscription;
|
|
588
669
|
exports.provideContext = provideContext;
|
|
589
670
|
exports.useAllAddresses = useAllAddresses;
|
|
@@ -595,12 +676,14 @@ exports.useChainConnectors = useChainConnectors;
|
|
|
595
676
|
exports.useChaindata = useChaindata;
|
|
596
677
|
exports.useChains = useChains;
|
|
597
678
|
exports.useDbCache = useDbCache;
|
|
679
|
+
exports.useDbCacheBalancesSubscription = useDbCacheBalancesSubscription;
|
|
598
680
|
exports.useDbCacheSubscription = useDbCacheSubscription;
|
|
681
|
+
exports.useDbCacheTokenRatesSubscription = useDbCacheTokenRatesSubscription;
|
|
599
682
|
exports.useEvmNetwork = useEvmNetwork;
|
|
600
683
|
exports.useEvmNetworks = useEvmNetworks;
|
|
601
684
|
exports.useMulticastSubscription = useMulticastSubscription;
|
|
602
|
-
exports.useSubscriptions = useSubscriptions;
|
|
603
685
|
exports.useToken = useToken;
|
|
604
686
|
exports.useTokenRate = useTokenRate;
|
|
605
687
|
exports.useTokenRates = useTokenRates;
|
|
606
688
|
exports.useTokens = useTokens;
|
|
689
|
+
exports.useWithTestnets = useWithTestnets;
|