@talismn/balances-react 0.0.0-pr563-20230221193038 → 0.0.0-pr563-20230222051735
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 +4 -4
- package/dist/declarations/src/hooks/useDbCache.d.ts +1 -1
- package/dist/declarations/src/hooks/useDbCacheSubscription.d.ts +1 -1
- package/dist/declarations/src/hooks/useTokenRates.d.ts +3 -3
- package/dist/talismn-balances-react.cjs.dev.js +108 -71
- package/dist/talismn-balances-react.cjs.prod.js +108 -71
- package/dist/talismn-balances-react.esm.js +111 -75
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @talismn/balances-react
|
|
2
2
|
|
|
3
|
-
## 0.0.0-pr563-
|
|
3
|
+
## 0.0.0-pr563-20230222051735
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- Updated dependencies [
|
|
9
|
-
- @talismn/balances@0.0.0-pr563-
|
|
7
|
+
- 536eddb5: fix: ported useDbCache related perf fixes to @talismn/balances-react
|
|
8
|
+
- Updated dependencies [536eddb5]
|
|
9
|
+
- @talismn/balances@0.0.0-pr563-20230222051735
|
|
10
10
|
|
|
11
11
|
## 0.3.3
|
|
12
12
|
|
|
@@ -15,8 +15,8 @@ type DbCache = {
|
|
|
15
15
|
evmNetworksWithoutTestnetsMap: Record<EvmNetworkId, EvmNetwork | CustomEvmNetwork>;
|
|
16
16
|
tokensWithTestnetsMap: Record<TokenId, Token>;
|
|
17
17
|
tokensWithoutTestnetsMap: Record<TokenId, Token>;
|
|
18
|
-
balances: BalanceJson[];
|
|
19
18
|
tokenRatesMap: Record<TokenId, TokenRates>;
|
|
19
|
+
balances: BalanceJson[];
|
|
20
20
|
};
|
|
21
21
|
type DbCacheProviderProps = {
|
|
22
22
|
useTestnets?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
export type DbEntityType = "chains" | "evmNetworks" | "tokens" | "balances";
|
|
2
|
+
export type DbEntityType = "chains" | "evmNetworks" | "tokens" | "tokenRates" | "balances";
|
|
3
3
|
export declare const SubscriptionsProvider: import("react").FC<{
|
|
4
4
|
children?: import("react").ReactNode;
|
|
5
5
|
}>, useSubscriptions: () => ((callback?: ((val: unknown) => void) | undefined) => import("../util").Unsubscribe)[];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
1
|
+
import { TokenId } from "@talismn/chaindata-provider";
|
|
2
|
+
export declare function useTokenRates(): Record<string, import("@talismn/token-rates").TokenRates>;
|
|
3
|
+
export declare function useTokenRate(tokenId?: TokenId): import("@talismn/token-rates").TokenRates | undefined;
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var react = require('react');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
7
|
var balances = require('@talismn/balances');
|
|
8
|
+
var tokenRates = require('@talismn/token-rates');
|
|
7
9
|
var dexieReactHooks = require('dexie-react-hooks');
|
|
8
10
|
var reactUse = require('react-use');
|
|
9
11
|
var chaindataProviderExtension = require('@talismn/chaindata-provider-extension');
|
|
10
|
-
var tokenRates = require('@talismn/token-rates');
|
|
11
12
|
var anylogger = require('anylogger');
|
|
12
13
|
var rxjs = require('rxjs');
|
|
13
14
|
var chainConnector = require('@talismn/chain-connector');
|
|
@@ -17,11 +18,6 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
|
|
|
17
18
|
|
|
18
19
|
var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
-
* This utility generates a context provider from a react hook passed as argument
|
|
22
|
-
*
|
|
23
|
-
* @returns an array containing the provider and the consumer hook
|
|
24
|
-
*/
|
|
25
21
|
const provideContext = useProviderContext => {
|
|
26
22
|
// automatic typing based on our hook's return type
|
|
27
23
|
|
|
@@ -33,9 +29,10 @@ const provideContext = useProviderContext => {
|
|
|
33
29
|
...props
|
|
34
30
|
}) => {
|
|
35
31
|
const ctx = useProviderContext(props);
|
|
36
|
-
return /*#__PURE__*/
|
|
37
|
-
value: ctx
|
|
38
|
-
|
|
32
|
+
return /*#__PURE__*/jsxRuntime.jsx(Context.Provider, {
|
|
33
|
+
value: ctx,
|
|
34
|
+
children: children
|
|
35
|
+
});
|
|
39
36
|
};
|
|
40
37
|
const useProvidedContext = () => {
|
|
41
38
|
const context = react.useContext(Context);
|
|
@@ -65,25 +62,6 @@ function useChaindataProvider(options = {}) {
|
|
|
65
62
|
}
|
|
66
63
|
const [ChaindataProvider, useChaindata] = provideContext(useChaindataProvider);
|
|
67
64
|
|
|
68
|
-
function useTokenRates(tokens) {
|
|
69
|
-
const generation = react.useRef(0);
|
|
70
|
-
const [tokenRates$1, setTokenRates] = react.useState({});
|
|
71
|
-
react.useEffect(() => {
|
|
72
|
-
if (!tokens) return;
|
|
73
|
-
if (Object.keys(tokens).length < 1) return;
|
|
74
|
-
|
|
75
|
-
// when we make a new request, we want to ignore any old requests which haven't yet completed
|
|
76
|
-
// otherwise we risk replacing the most recent data with older data
|
|
77
|
-
generation.current = (generation.current + 1) % Number.MAX_SAFE_INTEGER;
|
|
78
|
-
const thisGeneration = generation.current;
|
|
79
|
-
tokenRates.fetchTokenRates(tokens).then(tokenRates => {
|
|
80
|
-
if (thisGeneration !== generation.current) return;
|
|
81
|
-
setTokenRates(tokenRates);
|
|
82
|
-
});
|
|
83
|
-
}, [tokens]);
|
|
84
|
-
return tokenRates$1;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
65
|
const filterNoTestnet = ({
|
|
88
66
|
isTestnet
|
|
89
67
|
}) => isTestnet === false;
|
|
@@ -100,13 +78,11 @@ const DEFAULT_VALUE = {
|
|
|
100
78
|
evmNetworksWithoutTestnetsMap: {},
|
|
101
79
|
tokensWithTestnetsMap: {},
|
|
102
80
|
tokensWithoutTestnetsMap: {},
|
|
103
|
-
|
|
104
|
-
|
|
81
|
+
tokenRatesMap: {},
|
|
82
|
+
balances: []
|
|
105
83
|
};
|
|
106
|
-
const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap,
|
|
107
|
-
if (!chainsMap || !evmNetworksMap || !tokensMap || !allBalances
|
|
108
|
-
// TODO: Store tokenRates in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
109
|
-
/* || !tokenRates */) return DEFAULT_VALUE;
|
|
84
|
+
const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, tokenRates, allBalances) => {
|
|
85
|
+
if (!chainsMap || !evmNetworksMap || !tokensMap || !tokenRates || !allBalances) return DEFAULT_VALUE;
|
|
110
86
|
|
|
111
87
|
// BEGIN: temp hack to indicate that
|
|
112
88
|
// - EVM GLMR is a mirror of substrate GLMR
|
|
@@ -134,6 +110,10 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, allBalances, t
|
|
|
134
110
|
const tokensWithoutTestnets = tokensWithTestnets.filter(filterNoTestnet).filter(token => token.chain && chainsWithoutTestnetsMap[token.chain.id] || token.evmNetwork && evmNetworksWithoutTestnetsMap[token.evmNetwork.id]);
|
|
135
111
|
const tokensWithTestnetsMap = Object.fromEntries(tokensWithTestnets.map(token => [token.id, token]));
|
|
136
112
|
const tokensWithoutTestnetsMap = Object.fromEntries(tokensWithoutTestnets.map(token => [token.id, token]));
|
|
113
|
+
const tokenRatesMap = Object.fromEntries(tokenRates.map(({
|
|
114
|
+
tokenId,
|
|
115
|
+
rates
|
|
116
|
+
}) => [tokenId, rates]));
|
|
137
117
|
|
|
138
118
|
// return only balances for which we have a token
|
|
139
119
|
const balances = allBalances.filter(b => tokensWithTestnetsMap[b.tokenId]);
|
|
@@ -150,46 +130,41 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, allBalances, t
|
|
|
150
130
|
evmNetworksWithoutTestnetsMap,
|
|
151
131
|
tokensWithTestnetsMap,
|
|
152
132
|
tokensWithoutTestnetsMap,
|
|
153
|
-
|
|
154
|
-
|
|
133
|
+
tokenRatesMap,
|
|
134
|
+
balances
|
|
155
135
|
};
|
|
156
136
|
};
|
|
157
137
|
const useDbCacheProvider = ({
|
|
158
138
|
useTestnets = false
|
|
159
139
|
}) => {
|
|
160
140
|
const chaindataProvider = useChaindata();
|
|
161
|
-
const chainList = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
162
|
-
const evmNetworkList = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
163
|
-
const tokenList = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
141
|
+
const chainList = dexieReactHooks.useLiveQuery(() => chaindataProvider?.chains(), [chaindataProvider]);
|
|
142
|
+
const evmNetworkList = dexieReactHooks.useLiveQuery(() => chaindataProvider?.evmNetworks(), [chaindataProvider]);
|
|
143
|
+
const tokenList = dexieReactHooks.useLiveQuery(() => chaindataProvider?.tokens(), [chaindataProvider]);
|
|
144
|
+
const tokenRates$1 = dexieReactHooks.useLiveQuery(() => tokenRates.db.tokenRates.toArray(), []);
|
|
164
145
|
const rawBalances = dexieReactHooks.useLiveQuery(() => balances.db.balances.toArray(), []);
|
|
165
|
-
|
|
166
|
-
// TODO: Store in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
167
|
-
const tokenRates = useTokenRates();
|
|
168
146
|
const [dbData, setDbData] = react.useState(DEFAULT_VALUE);
|
|
169
147
|
|
|
170
148
|
// debounce every 500ms to prevent hammering UI with updates
|
|
171
149
|
reactUse.useDebounce(() => {
|
|
172
|
-
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList,
|
|
173
|
-
}, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates, useTestnets]);
|
|
150
|
+
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates$1, rawBalances));
|
|
151
|
+
}, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates$1, useTestnets]);
|
|
174
152
|
const refInitialized = react.useRef(false);
|
|
175
153
|
|
|
176
154
|
// force an update as soon as all datasources are fetched, so UI can display data ASAP
|
|
177
155
|
react.useEffect(() => {
|
|
178
|
-
if (!refInitialized.current && chainList && evmNetworkList && tokenList && rawBalances
|
|
179
|
-
|
|
180
|
-
// && tokenRates
|
|
181
|
-
) {
|
|
182
|
-
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, rawBalances, tokenRates));
|
|
156
|
+
if (!refInitialized.current && chainList && evmNetworkList && tokenList && tokenRates$1 && rawBalances) {
|
|
157
|
+
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates$1, rawBalances));
|
|
183
158
|
refInitialized.current = true;
|
|
184
159
|
}
|
|
185
|
-
}, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates, useTestnets]);
|
|
160
|
+
}, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates$1, useTestnets]);
|
|
186
161
|
return dbData;
|
|
187
162
|
};
|
|
188
163
|
const [DbCacheProvider, useDbCache] = provideContext(useDbCacheProvider);
|
|
189
164
|
|
|
190
165
|
var packageJson = {
|
|
191
166
|
name: "@talismn/balances-react",
|
|
192
|
-
version: "0.0.0-pr563-
|
|
167
|
+
version: "0.0.0-pr563-20230222051735",
|
|
193
168
|
author: "Talisman",
|
|
194
169
|
homepage: "https://talisman.xyz",
|
|
195
170
|
license: "UNLICENSED",
|
|
@@ -222,7 +197,7 @@ var packageJson = {
|
|
|
222
197
|
"@talismn/chaindata-provider-extension": "workspace:^",
|
|
223
198
|
"@talismn/token-rates": "workspace:^",
|
|
224
199
|
anylogger: "^1.0.11",
|
|
225
|
-
dexie: "^3.2.
|
|
200
|
+
dexie: "^3.2.2",
|
|
226
201
|
"dexie-react-hooks": "^1.1.1",
|
|
227
202
|
"react-use": "^17.4.0",
|
|
228
203
|
rxjs: "^7.8.0"
|
|
@@ -319,14 +294,14 @@ function useChainConnectorsProvider(options) {
|
|
|
319
294
|
}
|
|
320
295
|
const [ChainConnectorsProvider, useChainConnectors] = provideContext(useChainConnectorsProvider);
|
|
321
296
|
|
|
322
|
-
const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeBalances()];
|
|
297
|
+
const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeTokenRates(), useSubscribeBalances()];
|
|
323
298
|
const [SubscriptionsProvider, useSubscriptions] = provideContext(useSubscriptionsProvider);
|
|
324
299
|
|
|
325
300
|
/**
|
|
326
301
|
* This hook is responsible for fetching the data used for balances and inserting it into the db.
|
|
327
302
|
*/
|
|
328
303
|
const useDbCacheSubscription = subscribeTo => {
|
|
329
|
-
const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeBalances] = useSubscriptions();
|
|
304
|
+
const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances] = useSubscriptions();
|
|
330
305
|
react.useEffect(() => {
|
|
331
306
|
switch (subscribeTo) {
|
|
332
307
|
case "chains":
|
|
@@ -335,10 +310,12 @@ const useDbCacheSubscription = subscribeTo => {
|
|
|
335
310
|
return subscribeHydrateEvmNetworks();
|
|
336
311
|
case "tokens":
|
|
337
312
|
return subscribeHydrateTokens();
|
|
313
|
+
case "tokenRates":
|
|
314
|
+
return subscribeTokenRates();
|
|
338
315
|
case "balances":
|
|
339
316
|
return subscribeBalances();
|
|
340
317
|
}
|
|
341
|
-
}, [
|
|
318
|
+
}, [subscribeTo, subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances]);
|
|
342
319
|
};
|
|
343
320
|
function useSubscribeChaindataHydrate(type) {
|
|
344
321
|
const chaindata =
|
|
@@ -370,12 +347,55 @@ function useSubscribeChaindataHydrate(type) {
|
|
|
370
347
|
const subscribe = useMulticastSubscription(createSubscription);
|
|
371
348
|
return subscribe;
|
|
372
349
|
}
|
|
350
|
+
function useSubscribeTokenRates() {
|
|
351
|
+
const chaindataProvider = useChaindata();
|
|
352
|
+
const tokens = dexieReactHooks.useLiveQuery(() => 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;
|
|
392
|
+
}
|
|
373
393
|
function useSubscribeBalances() {
|
|
374
394
|
const balanceModules = useBalanceModules();
|
|
375
395
|
const chaindataProvider = useChaindata();
|
|
376
396
|
const chainConnectors = useChainConnectors();
|
|
377
397
|
const [allAddresses] = useAllAddresses();
|
|
378
|
-
const tokens = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
398
|
+
const tokens = dexieReactHooks.useLiveQuery(() => chaindataProvider?.tokens(), [chaindataProvider]);
|
|
379
399
|
const tokenIds = react.useMemo(() => Object.values(tokens ?? {}).map(({
|
|
380
400
|
id
|
|
381
401
|
}) => id), [tokens]);
|
|
@@ -464,6 +484,19 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
|
|
|
464
484
|
return evmNetworkId ? evmNetworks[evmNetworkId] : undefined;
|
|
465
485
|
}
|
|
466
486
|
|
|
487
|
+
function useTokenRates() {
|
|
488
|
+
// keep db data up to date
|
|
489
|
+
useDbCacheSubscription("tokenRates");
|
|
490
|
+
const {
|
|
491
|
+
tokenRatesMap
|
|
492
|
+
} = useDbCache();
|
|
493
|
+
return tokenRatesMap;
|
|
494
|
+
}
|
|
495
|
+
function useTokenRate(tokenId) {
|
|
496
|
+
const tokenRates = useTokenRates();
|
|
497
|
+
return tokenId ? tokenRates[tokenId] : undefined;
|
|
498
|
+
}
|
|
499
|
+
|
|
467
500
|
function useTokens(withTestnets) {
|
|
468
501
|
// keep db data up to date
|
|
469
502
|
useDbCacheSubscription("tokens");
|
|
@@ -482,12 +515,7 @@ const useBalancesHydrate = withTestnets => {
|
|
|
482
515
|
const chains = useChains(withTestnets);
|
|
483
516
|
const evmNetworks = useEvmNetworks(withTestnets);
|
|
484
517
|
const tokens = useTokens(withTestnets);
|
|
485
|
-
|
|
486
|
-
// TODO: Store in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
487
|
-
// useDbCacheSubscription("tokenRates")
|
|
488
|
-
const {
|
|
489
|
-
tokenRatesMap: tokenRates
|
|
490
|
-
} = useDbCache();
|
|
518
|
+
const tokenRates = useTokenRates();
|
|
491
519
|
return react.useMemo(() => ({
|
|
492
520
|
chains,
|
|
493
521
|
evmNetworks,
|
|
@@ -531,15 +559,23 @@ const BalancesProvider = ({
|
|
|
531
559
|
onfinalityApiKey,
|
|
532
560
|
useTestnets,
|
|
533
561
|
children
|
|
534
|
-
}) => /*#__PURE__*/
|
|
535
|
-
onfinalityApiKey: onfinalityApiKey
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
562
|
+
}) => /*#__PURE__*/jsxRuntime.jsx(ChaindataProvider, {
|
|
563
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
564
|
+
children: /*#__PURE__*/jsxRuntime.jsx(ChainConnectorsProvider, {
|
|
565
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
566
|
+
children: /*#__PURE__*/jsxRuntime.jsx(AllAddressesProvider, {
|
|
567
|
+
children: /*#__PURE__*/jsxRuntime.jsx(BalanceModulesProvider, {
|
|
568
|
+
balanceModules: balanceModules,
|
|
569
|
+
children: /*#__PURE__*/jsxRuntime.jsx(DbCacheProvider, {
|
|
570
|
+
useTestnets: useTestnets,
|
|
571
|
+
children: /*#__PURE__*/jsxRuntime.jsx(SubscriptionsProvider, {
|
|
572
|
+
children: children
|
|
573
|
+
})
|
|
574
|
+
})
|
|
575
|
+
})
|
|
576
|
+
})
|
|
577
|
+
})
|
|
578
|
+
});
|
|
543
579
|
|
|
544
580
|
exports.AllAddressesProvider = AllAddressesProvider;
|
|
545
581
|
exports.BalanceModulesProvider = BalanceModulesProvider;
|
|
@@ -565,5 +601,6 @@ exports.useEvmNetworks = useEvmNetworks;
|
|
|
565
601
|
exports.useMulticastSubscription = useMulticastSubscription;
|
|
566
602
|
exports.useSubscriptions = useSubscriptions;
|
|
567
603
|
exports.useToken = useToken;
|
|
604
|
+
exports.useTokenRate = useTokenRate;
|
|
568
605
|
exports.useTokenRates = useTokenRates;
|
|
569
606
|
exports.useTokens = useTokens;
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var react = require('react');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
7
|
var balances = require('@talismn/balances');
|
|
8
|
+
var tokenRates = require('@talismn/token-rates');
|
|
7
9
|
var dexieReactHooks = require('dexie-react-hooks');
|
|
8
10
|
var reactUse = require('react-use');
|
|
9
11
|
var chaindataProviderExtension = require('@talismn/chaindata-provider-extension');
|
|
10
|
-
var tokenRates = require('@talismn/token-rates');
|
|
11
12
|
var anylogger = require('anylogger');
|
|
12
13
|
var rxjs = require('rxjs');
|
|
13
14
|
var chainConnector = require('@talismn/chain-connector');
|
|
@@ -17,11 +18,6 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
|
|
|
17
18
|
|
|
18
19
|
var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
-
* This utility generates a context provider from a react hook passed as argument
|
|
22
|
-
*
|
|
23
|
-
* @returns an array containing the provider and the consumer hook
|
|
24
|
-
*/
|
|
25
21
|
const provideContext = useProviderContext => {
|
|
26
22
|
// automatic typing based on our hook's return type
|
|
27
23
|
|
|
@@ -33,9 +29,10 @@ const provideContext = useProviderContext => {
|
|
|
33
29
|
...props
|
|
34
30
|
}) => {
|
|
35
31
|
const ctx = useProviderContext(props);
|
|
36
|
-
return /*#__PURE__*/
|
|
37
|
-
value: ctx
|
|
38
|
-
|
|
32
|
+
return /*#__PURE__*/jsxRuntime.jsx(Context.Provider, {
|
|
33
|
+
value: ctx,
|
|
34
|
+
children: children
|
|
35
|
+
});
|
|
39
36
|
};
|
|
40
37
|
const useProvidedContext = () => {
|
|
41
38
|
const context = react.useContext(Context);
|
|
@@ -65,25 +62,6 @@ function useChaindataProvider(options = {}) {
|
|
|
65
62
|
}
|
|
66
63
|
const [ChaindataProvider, useChaindata] = provideContext(useChaindataProvider);
|
|
67
64
|
|
|
68
|
-
function useTokenRates(tokens) {
|
|
69
|
-
const generation = react.useRef(0);
|
|
70
|
-
const [tokenRates$1, setTokenRates] = react.useState({});
|
|
71
|
-
react.useEffect(() => {
|
|
72
|
-
if (!tokens) return;
|
|
73
|
-
if (Object.keys(tokens).length < 1) return;
|
|
74
|
-
|
|
75
|
-
// when we make a new request, we want to ignore any old requests which haven't yet completed
|
|
76
|
-
// otherwise we risk replacing the most recent data with older data
|
|
77
|
-
generation.current = (generation.current + 1) % Number.MAX_SAFE_INTEGER;
|
|
78
|
-
const thisGeneration = generation.current;
|
|
79
|
-
tokenRates.fetchTokenRates(tokens).then(tokenRates => {
|
|
80
|
-
if (thisGeneration !== generation.current) return;
|
|
81
|
-
setTokenRates(tokenRates);
|
|
82
|
-
});
|
|
83
|
-
}, [tokens]);
|
|
84
|
-
return tokenRates$1;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
65
|
const filterNoTestnet = ({
|
|
88
66
|
isTestnet
|
|
89
67
|
}) => isTestnet === false;
|
|
@@ -100,13 +78,11 @@ const DEFAULT_VALUE = {
|
|
|
100
78
|
evmNetworksWithoutTestnetsMap: {},
|
|
101
79
|
tokensWithTestnetsMap: {},
|
|
102
80
|
tokensWithoutTestnetsMap: {},
|
|
103
|
-
|
|
104
|
-
|
|
81
|
+
tokenRatesMap: {},
|
|
82
|
+
balances: []
|
|
105
83
|
};
|
|
106
|
-
const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap,
|
|
107
|
-
if (!chainsMap || !evmNetworksMap || !tokensMap || !allBalances
|
|
108
|
-
// TODO: Store tokenRates in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
109
|
-
/* || !tokenRates */) return DEFAULT_VALUE;
|
|
84
|
+
const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, tokenRates, allBalances) => {
|
|
85
|
+
if (!chainsMap || !evmNetworksMap || !tokensMap || !tokenRates || !allBalances) return DEFAULT_VALUE;
|
|
110
86
|
|
|
111
87
|
// BEGIN: temp hack to indicate that
|
|
112
88
|
// - EVM GLMR is a mirror of substrate GLMR
|
|
@@ -134,6 +110,10 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, allBalances, t
|
|
|
134
110
|
const tokensWithoutTestnets = tokensWithTestnets.filter(filterNoTestnet).filter(token => token.chain && chainsWithoutTestnetsMap[token.chain.id] || token.evmNetwork && evmNetworksWithoutTestnetsMap[token.evmNetwork.id]);
|
|
135
111
|
const tokensWithTestnetsMap = Object.fromEntries(tokensWithTestnets.map(token => [token.id, token]));
|
|
136
112
|
const tokensWithoutTestnetsMap = Object.fromEntries(tokensWithoutTestnets.map(token => [token.id, token]));
|
|
113
|
+
const tokenRatesMap = Object.fromEntries(tokenRates.map(({
|
|
114
|
+
tokenId,
|
|
115
|
+
rates
|
|
116
|
+
}) => [tokenId, rates]));
|
|
137
117
|
|
|
138
118
|
// return only balances for which we have a token
|
|
139
119
|
const balances = allBalances.filter(b => tokensWithTestnetsMap[b.tokenId]);
|
|
@@ -150,46 +130,41 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, allBalances, t
|
|
|
150
130
|
evmNetworksWithoutTestnetsMap,
|
|
151
131
|
tokensWithTestnetsMap,
|
|
152
132
|
tokensWithoutTestnetsMap,
|
|
153
|
-
|
|
154
|
-
|
|
133
|
+
tokenRatesMap,
|
|
134
|
+
balances
|
|
155
135
|
};
|
|
156
136
|
};
|
|
157
137
|
const useDbCacheProvider = ({
|
|
158
138
|
useTestnets = false
|
|
159
139
|
}) => {
|
|
160
140
|
const chaindataProvider = useChaindata();
|
|
161
|
-
const chainList = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
162
|
-
const evmNetworkList = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
163
|
-
const tokenList = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
141
|
+
const chainList = dexieReactHooks.useLiveQuery(() => chaindataProvider?.chains(), [chaindataProvider]);
|
|
142
|
+
const evmNetworkList = dexieReactHooks.useLiveQuery(() => chaindataProvider?.evmNetworks(), [chaindataProvider]);
|
|
143
|
+
const tokenList = dexieReactHooks.useLiveQuery(() => chaindataProvider?.tokens(), [chaindataProvider]);
|
|
144
|
+
const tokenRates$1 = dexieReactHooks.useLiveQuery(() => tokenRates.db.tokenRates.toArray(), []);
|
|
164
145
|
const rawBalances = dexieReactHooks.useLiveQuery(() => balances.db.balances.toArray(), []);
|
|
165
|
-
|
|
166
|
-
// TODO: Store in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
167
|
-
const tokenRates = useTokenRates();
|
|
168
146
|
const [dbData, setDbData] = react.useState(DEFAULT_VALUE);
|
|
169
147
|
|
|
170
148
|
// debounce every 500ms to prevent hammering UI with updates
|
|
171
149
|
reactUse.useDebounce(() => {
|
|
172
|
-
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList,
|
|
173
|
-
}, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates, useTestnets]);
|
|
150
|
+
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates$1, rawBalances));
|
|
151
|
+
}, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates$1, useTestnets]);
|
|
174
152
|
const refInitialized = react.useRef(false);
|
|
175
153
|
|
|
176
154
|
// force an update as soon as all datasources are fetched, so UI can display data ASAP
|
|
177
155
|
react.useEffect(() => {
|
|
178
|
-
if (!refInitialized.current && chainList && evmNetworkList && tokenList && rawBalances
|
|
179
|
-
|
|
180
|
-
// && tokenRates
|
|
181
|
-
) {
|
|
182
|
-
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, rawBalances, tokenRates));
|
|
156
|
+
if (!refInitialized.current && chainList && evmNetworkList && tokenList && tokenRates$1 && rawBalances) {
|
|
157
|
+
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates$1, rawBalances));
|
|
183
158
|
refInitialized.current = true;
|
|
184
159
|
}
|
|
185
|
-
}, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates, useTestnets]);
|
|
160
|
+
}, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates$1, useTestnets]);
|
|
186
161
|
return dbData;
|
|
187
162
|
};
|
|
188
163
|
const [DbCacheProvider, useDbCache] = provideContext(useDbCacheProvider);
|
|
189
164
|
|
|
190
165
|
var packageJson = {
|
|
191
166
|
name: "@talismn/balances-react",
|
|
192
|
-
version: "0.0.0-pr563-
|
|
167
|
+
version: "0.0.0-pr563-20230222051735",
|
|
193
168
|
author: "Talisman",
|
|
194
169
|
homepage: "https://talisman.xyz",
|
|
195
170
|
license: "UNLICENSED",
|
|
@@ -222,7 +197,7 @@ var packageJson = {
|
|
|
222
197
|
"@talismn/chaindata-provider-extension": "workspace:^",
|
|
223
198
|
"@talismn/token-rates": "workspace:^",
|
|
224
199
|
anylogger: "^1.0.11",
|
|
225
|
-
dexie: "^3.2.
|
|
200
|
+
dexie: "^3.2.2",
|
|
226
201
|
"dexie-react-hooks": "^1.1.1",
|
|
227
202
|
"react-use": "^17.4.0",
|
|
228
203
|
rxjs: "^7.8.0"
|
|
@@ -319,14 +294,14 @@ function useChainConnectorsProvider(options) {
|
|
|
319
294
|
}
|
|
320
295
|
const [ChainConnectorsProvider, useChainConnectors] = provideContext(useChainConnectorsProvider);
|
|
321
296
|
|
|
322
|
-
const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeBalances()];
|
|
297
|
+
const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeTokenRates(), useSubscribeBalances()];
|
|
323
298
|
const [SubscriptionsProvider, useSubscriptions] = provideContext(useSubscriptionsProvider);
|
|
324
299
|
|
|
325
300
|
/**
|
|
326
301
|
* This hook is responsible for fetching the data used for balances and inserting it into the db.
|
|
327
302
|
*/
|
|
328
303
|
const useDbCacheSubscription = subscribeTo => {
|
|
329
|
-
const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeBalances] = useSubscriptions();
|
|
304
|
+
const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances] = useSubscriptions();
|
|
330
305
|
react.useEffect(() => {
|
|
331
306
|
switch (subscribeTo) {
|
|
332
307
|
case "chains":
|
|
@@ -335,10 +310,12 @@ const useDbCacheSubscription = subscribeTo => {
|
|
|
335
310
|
return subscribeHydrateEvmNetworks();
|
|
336
311
|
case "tokens":
|
|
337
312
|
return subscribeHydrateTokens();
|
|
313
|
+
case "tokenRates":
|
|
314
|
+
return subscribeTokenRates();
|
|
338
315
|
case "balances":
|
|
339
316
|
return subscribeBalances();
|
|
340
317
|
}
|
|
341
|
-
}, [
|
|
318
|
+
}, [subscribeTo, subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances]);
|
|
342
319
|
};
|
|
343
320
|
function useSubscribeChaindataHydrate(type) {
|
|
344
321
|
const chaindata =
|
|
@@ -370,12 +347,55 @@ function useSubscribeChaindataHydrate(type) {
|
|
|
370
347
|
const subscribe = useMulticastSubscription(createSubscription);
|
|
371
348
|
return subscribe;
|
|
372
349
|
}
|
|
350
|
+
function useSubscribeTokenRates() {
|
|
351
|
+
const chaindataProvider = useChaindata();
|
|
352
|
+
const tokens = dexieReactHooks.useLiveQuery(() => 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;
|
|
392
|
+
}
|
|
373
393
|
function useSubscribeBalances() {
|
|
374
394
|
const balanceModules = useBalanceModules();
|
|
375
395
|
const chaindataProvider = useChaindata();
|
|
376
396
|
const chainConnectors = useChainConnectors();
|
|
377
397
|
const [allAddresses] = useAllAddresses();
|
|
378
|
-
const tokens = dexieReactHooks.useLiveQuery(() => chaindataProvider
|
|
398
|
+
const tokens = dexieReactHooks.useLiveQuery(() => chaindataProvider?.tokens(), [chaindataProvider]);
|
|
379
399
|
const tokenIds = react.useMemo(() => Object.values(tokens ?? {}).map(({
|
|
380
400
|
id
|
|
381
401
|
}) => id), [tokens]);
|
|
@@ -464,6 +484,19 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
|
|
|
464
484
|
return evmNetworkId ? evmNetworks[evmNetworkId] : undefined;
|
|
465
485
|
}
|
|
466
486
|
|
|
487
|
+
function useTokenRates() {
|
|
488
|
+
// keep db data up to date
|
|
489
|
+
useDbCacheSubscription("tokenRates");
|
|
490
|
+
const {
|
|
491
|
+
tokenRatesMap
|
|
492
|
+
} = useDbCache();
|
|
493
|
+
return tokenRatesMap;
|
|
494
|
+
}
|
|
495
|
+
function useTokenRate(tokenId) {
|
|
496
|
+
const tokenRates = useTokenRates();
|
|
497
|
+
return tokenId ? tokenRates[tokenId] : undefined;
|
|
498
|
+
}
|
|
499
|
+
|
|
467
500
|
function useTokens(withTestnets) {
|
|
468
501
|
// keep db data up to date
|
|
469
502
|
useDbCacheSubscription("tokens");
|
|
@@ -482,12 +515,7 @@ const useBalancesHydrate = withTestnets => {
|
|
|
482
515
|
const chains = useChains(withTestnets);
|
|
483
516
|
const evmNetworks = useEvmNetworks(withTestnets);
|
|
484
517
|
const tokens = useTokens(withTestnets);
|
|
485
|
-
|
|
486
|
-
// TODO: Store in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
487
|
-
// useDbCacheSubscription("tokenRates")
|
|
488
|
-
const {
|
|
489
|
-
tokenRatesMap: tokenRates
|
|
490
|
-
} = useDbCache();
|
|
518
|
+
const tokenRates = useTokenRates();
|
|
491
519
|
return react.useMemo(() => ({
|
|
492
520
|
chains,
|
|
493
521
|
evmNetworks,
|
|
@@ -531,15 +559,23 @@ const BalancesProvider = ({
|
|
|
531
559
|
onfinalityApiKey,
|
|
532
560
|
useTestnets,
|
|
533
561
|
children
|
|
534
|
-
}) => /*#__PURE__*/
|
|
535
|
-
onfinalityApiKey: onfinalityApiKey
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
562
|
+
}) => /*#__PURE__*/jsxRuntime.jsx(ChaindataProvider, {
|
|
563
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
564
|
+
children: /*#__PURE__*/jsxRuntime.jsx(ChainConnectorsProvider, {
|
|
565
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
566
|
+
children: /*#__PURE__*/jsxRuntime.jsx(AllAddressesProvider, {
|
|
567
|
+
children: /*#__PURE__*/jsxRuntime.jsx(BalanceModulesProvider, {
|
|
568
|
+
balanceModules: balanceModules,
|
|
569
|
+
children: /*#__PURE__*/jsxRuntime.jsx(DbCacheProvider, {
|
|
570
|
+
useTestnets: useTestnets,
|
|
571
|
+
children: /*#__PURE__*/jsxRuntime.jsx(SubscriptionsProvider, {
|
|
572
|
+
children: children
|
|
573
|
+
})
|
|
574
|
+
})
|
|
575
|
+
})
|
|
576
|
+
})
|
|
577
|
+
})
|
|
578
|
+
});
|
|
543
579
|
|
|
544
580
|
exports.AllAddressesProvider = AllAddressesProvider;
|
|
545
581
|
exports.BalanceModulesProvider = BalanceModulesProvider;
|
|
@@ -565,5 +601,6 @@ exports.useEvmNetworks = useEvmNetworks;
|
|
|
565
601
|
exports.useMulticastSubscription = useMulticastSubscription;
|
|
566
602
|
exports.useSubscriptions = useSubscriptions;
|
|
567
603
|
exports.useToken = useToken;
|
|
604
|
+
exports.useTokenRate = useTokenRate;
|
|
568
605
|
exports.useTokenRates = useTokenRates;
|
|
569
606
|
exports.useTokens = useTokens;
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import { useContext, createContext, useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { db as db$1, balances, Balances } from '@talismn/balances';
|
|
4
|
+
import { db, fetchTokenRates } from '@talismn/token-rates';
|
|
3
5
|
import { useLiveQuery } from 'dexie-react-hooks';
|
|
4
6
|
import { useDebounce } from 'react-use';
|
|
5
7
|
import { ChaindataProviderExtension } from '@talismn/chaindata-provider-extension';
|
|
6
|
-
import { fetchTokenRates } from '@talismn/token-rates';
|
|
7
8
|
import anylogger from 'anylogger';
|
|
8
9
|
import { Observable, defer, shareReplay } from 'rxjs';
|
|
9
10
|
import { ChainConnector } from '@talismn/chain-connector';
|
|
10
11
|
import { ChainConnectorEvm } from '@talismn/chain-connector-evm';
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* This utility generates a context provider from a react hook passed as argument
|
|
14
|
-
*
|
|
15
|
-
* @returns an array containing the provider and the consumer hook
|
|
16
|
-
*/
|
|
17
13
|
const provideContext = useProviderContext => {
|
|
18
14
|
// automatic typing based on our hook's return type
|
|
19
15
|
|
|
@@ -25,9 +21,10 @@ const provideContext = useProviderContext => {
|
|
|
25
21
|
...props
|
|
26
22
|
}) => {
|
|
27
23
|
const ctx = useProviderContext(props);
|
|
28
|
-
return /*#__PURE__*/
|
|
29
|
-
value: ctx
|
|
30
|
-
|
|
24
|
+
return /*#__PURE__*/jsx(Context.Provider, {
|
|
25
|
+
value: ctx,
|
|
26
|
+
children: children
|
|
27
|
+
});
|
|
31
28
|
};
|
|
32
29
|
const useProvidedContext = () => {
|
|
33
30
|
const context = useContext(Context);
|
|
@@ -57,25 +54,6 @@ function useChaindataProvider(options = {}) {
|
|
|
57
54
|
}
|
|
58
55
|
const [ChaindataProvider, useChaindata] = provideContext(useChaindataProvider);
|
|
59
56
|
|
|
60
|
-
function useTokenRates(tokens) {
|
|
61
|
-
const generation = useRef(0);
|
|
62
|
-
const [tokenRates, setTokenRates] = useState({});
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
if (!tokens) return;
|
|
65
|
-
if (Object.keys(tokens).length < 1) return;
|
|
66
|
-
|
|
67
|
-
// when we make a new request, we want to ignore any old requests which haven't yet completed
|
|
68
|
-
// otherwise we risk replacing the most recent data with older data
|
|
69
|
-
generation.current = (generation.current + 1) % Number.MAX_SAFE_INTEGER;
|
|
70
|
-
const thisGeneration = generation.current;
|
|
71
|
-
fetchTokenRates(tokens).then(tokenRates => {
|
|
72
|
-
if (thisGeneration !== generation.current) return;
|
|
73
|
-
setTokenRates(tokenRates);
|
|
74
|
-
});
|
|
75
|
-
}, [tokens]);
|
|
76
|
-
return tokenRates;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
57
|
const filterNoTestnet = ({
|
|
80
58
|
isTestnet
|
|
81
59
|
}) => isTestnet === false;
|
|
@@ -92,13 +70,11 @@ const DEFAULT_VALUE = {
|
|
|
92
70
|
evmNetworksWithoutTestnetsMap: {},
|
|
93
71
|
tokensWithTestnetsMap: {},
|
|
94
72
|
tokensWithoutTestnetsMap: {},
|
|
95
|
-
|
|
96
|
-
|
|
73
|
+
tokenRatesMap: {},
|
|
74
|
+
balances: []
|
|
97
75
|
};
|
|
98
|
-
const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap,
|
|
99
|
-
if (!chainsMap || !evmNetworksMap || !tokensMap || !allBalances
|
|
100
|
-
// TODO: Store tokenRates in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
101
|
-
/* || !tokenRates */) return DEFAULT_VALUE;
|
|
76
|
+
const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, tokenRates, allBalances) => {
|
|
77
|
+
if (!chainsMap || !evmNetworksMap || !tokensMap || !tokenRates || !allBalances) return DEFAULT_VALUE;
|
|
102
78
|
|
|
103
79
|
// BEGIN: temp hack to indicate that
|
|
104
80
|
// - EVM GLMR is a mirror of substrate GLMR
|
|
@@ -126,6 +102,10 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, allBalances, t
|
|
|
126
102
|
const tokensWithoutTestnets = tokensWithTestnets.filter(filterNoTestnet).filter(token => token.chain && chainsWithoutTestnetsMap[token.chain.id] || token.evmNetwork && evmNetworksWithoutTestnetsMap[token.evmNetwork.id]);
|
|
127
103
|
const tokensWithTestnetsMap = Object.fromEntries(tokensWithTestnets.map(token => [token.id, token]));
|
|
128
104
|
const tokensWithoutTestnetsMap = Object.fromEntries(tokensWithoutTestnets.map(token => [token.id, token]));
|
|
105
|
+
const tokenRatesMap = Object.fromEntries(tokenRates.map(({
|
|
106
|
+
tokenId,
|
|
107
|
+
rates
|
|
108
|
+
}) => [tokenId, rates]));
|
|
129
109
|
|
|
130
110
|
// return only balances for which we have a token
|
|
131
111
|
const balances = allBalances.filter(b => tokensWithTestnetsMap[b.tokenId]);
|
|
@@ -142,36 +122,31 @@ const consolidateDbCache = (chainsMap, evmNetworksMap, tokensMap, allBalances, t
|
|
|
142
122
|
evmNetworksWithoutTestnetsMap,
|
|
143
123
|
tokensWithTestnetsMap,
|
|
144
124
|
tokensWithoutTestnetsMap,
|
|
145
|
-
|
|
146
|
-
|
|
125
|
+
tokenRatesMap,
|
|
126
|
+
balances
|
|
147
127
|
};
|
|
148
128
|
};
|
|
149
129
|
const useDbCacheProvider = ({
|
|
150
130
|
useTestnets = false
|
|
151
131
|
}) => {
|
|
152
132
|
const chaindataProvider = useChaindata();
|
|
153
|
-
const chainList = useLiveQuery(() => chaindataProvider
|
|
154
|
-
const evmNetworkList = useLiveQuery(() => chaindataProvider
|
|
155
|
-
const tokenList = useLiveQuery(() => chaindataProvider
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
// TODO: Store in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
159
|
-
const tokenRates = useTokenRates();
|
|
133
|
+
const chainList = useLiveQuery(() => chaindataProvider?.chains(), [chaindataProvider]);
|
|
134
|
+
const evmNetworkList = useLiveQuery(() => chaindataProvider?.evmNetworks(), [chaindataProvider]);
|
|
135
|
+
const tokenList = useLiveQuery(() => chaindataProvider?.tokens(), [chaindataProvider]);
|
|
136
|
+
const tokenRates = useLiveQuery(() => db.tokenRates.toArray(), []);
|
|
137
|
+
const rawBalances = useLiveQuery(() => db$1.balances.toArray(), []);
|
|
160
138
|
const [dbData, setDbData] = useState(DEFAULT_VALUE);
|
|
161
139
|
|
|
162
140
|
// debounce every 500ms to prevent hammering UI with updates
|
|
163
141
|
useDebounce(() => {
|
|
164
|
-
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList,
|
|
142
|
+
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates, rawBalances));
|
|
165
143
|
}, 500, [chainList, evmNetworkList, tokenList, rawBalances, tokenRates, useTestnets]);
|
|
166
144
|
const refInitialized = useRef(false);
|
|
167
145
|
|
|
168
146
|
// force an update as soon as all datasources are fetched, so UI can display data ASAP
|
|
169
147
|
useEffect(() => {
|
|
170
|
-
if (!refInitialized.current && chainList && evmNetworkList && tokenList && rawBalances
|
|
171
|
-
|
|
172
|
-
// && tokenRates
|
|
173
|
-
) {
|
|
174
|
-
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, rawBalances, tokenRates));
|
|
148
|
+
if (!refInitialized.current && chainList && evmNetworkList && tokenList && tokenRates && rawBalances) {
|
|
149
|
+
setDbData(consolidateDbCache(chainList, evmNetworkList, tokenList, tokenRates, rawBalances));
|
|
175
150
|
refInitialized.current = true;
|
|
176
151
|
}
|
|
177
152
|
}, [chainList, evmNetworkList, rawBalances, tokenList, tokenRates, useTestnets]);
|
|
@@ -181,7 +156,7 @@ const [DbCacheProvider, useDbCache] = provideContext(useDbCacheProvider);
|
|
|
181
156
|
|
|
182
157
|
var packageJson = {
|
|
183
158
|
name: "@talismn/balances-react",
|
|
184
|
-
version: "0.0.0-pr563-
|
|
159
|
+
version: "0.0.0-pr563-20230222051735",
|
|
185
160
|
author: "Talisman",
|
|
186
161
|
homepage: "https://talisman.xyz",
|
|
187
162
|
license: "UNLICENSED",
|
|
@@ -214,7 +189,7 @@ var packageJson = {
|
|
|
214
189
|
"@talismn/chaindata-provider-extension": "workspace:^",
|
|
215
190
|
"@talismn/token-rates": "workspace:^",
|
|
216
191
|
anylogger: "^1.0.11",
|
|
217
|
-
dexie: "^3.2.
|
|
192
|
+
dexie: "^3.2.2",
|
|
218
193
|
"dexie-react-hooks": "^1.1.1",
|
|
219
194
|
"react-use": "^17.4.0",
|
|
220
195
|
rxjs: "^7.8.0"
|
|
@@ -311,14 +286,14 @@ function useChainConnectorsProvider(options) {
|
|
|
311
286
|
}
|
|
312
287
|
const [ChainConnectorsProvider, useChainConnectors] = provideContext(useChainConnectorsProvider);
|
|
313
288
|
|
|
314
|
-
const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeBalances()];
|
|
289
|
+
const useSubscriptionsProvider = () => [useSubscribeChaindataHydrate("chains"), useSubscribeChaindataHydrate("evmNetworks"), useSubscribeChaindataHydrate("tokens"), useSubscribeTokenRates(), useSubscribeBalances()];
|
|
315
290
|
const [SubscriptionsProvider, useSubscriptions] = provideContext(useSubscriptionsProvider);
|
|
316
291
|
|
|
317
292
|
/**
|
|
318
293
|
* This hook is responsible for fetching the data used for balances and inserting it into the db.
|
|
319
294
|
*/
|
|
320
295
|
const useDbCacheSubscription = subscribeTo => {
|
|
321
|
-
const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeBalances] = useSubscriptions();
|
|
296
|
+
const [subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances] = useSubscriptions();
|
|
322
297
|
useEffect(() => {
|
|
323
298
|
switch (subscribeTo) {
|
|
324
299
|
case "chains":
|
|
@@ -327,10 +302,12 @@ const useDbCacheSubscription = subscribeTo => {
|
|
|
327
302
|
return subscribeHydrateEvmNetworks();
|
|
328
303
|
case "tokens":
|
|
329
304
|
return subscribeHydrateTokens();
|
|
305
|
+
case "tokenRates":
|
|
306
|
+
return subscribeTokenRates();
|
|
330
307
|
case "balances":
|
|
331
308
|
return subscribeBalances();
|
|
332
309
|
}
|
|
333
|
-
}, [
|
|
310
|
+
}, [subscribeTo, subscribeHydrateChains, subscribeHydrateEvmNetworks, subscribeHydrateTokens, subscribeTokenRates, subscribeBalances]);
|
|
334
311
|
};
|
|
335
312
|
function useSubscribeChaindataHydrate(type) {
|
|
336
313
|
const chaindata =
|
|
@@ -362,12 +339,55 @@ function useSubscribeChaindataHydrate(type) {
|
|
|
362
339
|
const subscribe = useMulticastSubscription(createSubscription);
|
|
363
340
|
return subscribe;
|
|
364
341
|
}
|
|
342
|
+
function useSubscribeTokenRates() {
|
|
343
|
+
const chaindataProvider = useChaindata();
|
|
344
|
+
const tokens = useLiveQuery(() => 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
|
+
}
|
|
365
385
|
function useSubscribeBalances() {
|
|
366
386
|
const balanceModules = useBalanceModules();
|
|
367
387
|
const chaindataProvider = useChaindata();
|
|
368
388
|
const chainConnectors = useChainConnectors();
|
|
369
389
|
const [allAddresses] = useAllAddresses();
|
|
370
|
-
const tokens = useLiveQuery(() => chaindataProvider
|
|
390
|
+
const tokens = useLiveQuery(() => chaindataProvider?.tokens(), [chaindataProvider]);
|
|
371
391
|
const tokenIds = useMemo(() => Object.values(tokens ?? {}).map(({
|
|
372
392
|
id
|
|
373
393
|
}) => id), [tokens]);
|
|
@@ -402,7 +422,7 @@ function useSubscribeBalances() {
|
|
|
402
422
|
unsub.then(unsubscribe => unsubscribe());
|
|
403
423
|
|
|
404
424
|
// set this subscription's balances in the store to status: cache
|
|
405
|
-
db.transaction("rw", db.balances, async () => await db.balances.filter(balance => {
|
|
425
|
+
db$1.transaction("rw", db$1.balances, async () => await db$1.balances.filter(balance => {
|
|
406
426
|
if (balance.source !== balanceModule.type) return false;
|
|
407
427
|
if (!Object.keys(addressesByModuleToken).includes(balance.tokenId)) return false;
|
|
408
428
|
if (!addressesByModuleToken[balance.tokenId].includes(balance.address)) return false;
|
|
@@ -417,7 +437,7 @@ function useSubscribeBalances() {
|
|
|
417
437
|
id,
|
|
418
438
|
...balance
|
|
419
439
|
}));
|
|
420
|
-
db.transaction("rw", db.balances, async () => await db.balances.bulkPut(putBalances));
|
|
440
|
+
db$1.transaction("rw", db$1.balances, async () => await db$1.balances.bulkPut(putBalances));
|
|
421
441
|
});
|
|
422
442
|
return unsubscribe;
|
|
423
443
|
});
|
|
@@ -456,6 +476,19 @@ function useEvmNetwork(evmNetworkId, withTestnets) {
|
|
|
456
476
|
return evmNetworkId ? evmNetworks[evmNetworkId] : undefined;
|
|
457
477
|
}
|
|
458
478
|
|
|
479
|
+
function useTokenRates() {
|
|
480
|
+
// keep db data up to date
|
|
481
|
+
useDbCacheSubscription("tokenRates");
|
|
482
|
+
const {
|
|
483
|
+
tokenRatesMap
|
|
484
|
+
} = useDbCache();
|
|
485
|
+
return tokenRatesMap;
|
|
486
|
+
}
|
|
487
|
+
function useTokenRate(tokenId) {
|
|
488
|
+
const tokenRates = useTokenRates();
|
|
489
|
+
return tokenId ? tokenRates[tokenId] : undefined;
|
|
490
|
+
}
|
|
491
|
+
|
|
459
492
|
function useTokens(withTestnets) {
|
|
460
493
|
// keep db data up to date
|
|
461
494
|
useDbCacheSubscription("tokens");
|
|
@@ -474,12 +507,7 @@ const useBalancesHydrate = withTestnets => {
|
|
|
474
507
|
const chains = useChains(withTestnets);
|
|
475
508
|
const evmNetworks = useEvmNetworks(withTestnets);
|
|
476
509
|
const tokens = useTokens(withTestnets);
|
|
477
|
-
|
|
478
|
-
// TODO: Store in a DB so that we don't have to wait for tokens before we can begin to fetch tokenRates
|
|
479
|
-
// useDbCacheSubscription("tokenRates")
|
|
480
|
-
const {
|
|
481
|
-
tokenRatesMap: tokenRates
|
|
482
|
-
} = useDbCache();
|
|
510
|
+
const tokenRates = useTokenRates();
|
|
483
511
|
return useMemo(() => ({
|
|
484
512
|
chains,
|
|
485
513
|
evmNetworks,
|
|
@@ -523,14 +551,22 @@ const BalancesProvider = ({
|
|
|
523
551
|
onfinalityApiKey,
|
|
524
552
|
useTestnets,
|
|
525
553
|
children
|
|
526
|
-
}) => /*#__PURE__*/
|
|
527
|
-
onfinalityApiKey: onfinalityApiKey
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
554
|
+
}) => /*#__PURE__*/jsx(ChaindataProvider, {
|
|
555
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
556
|
+
children: /*#__PURE__*/jsx(ChainConnectorsProvider, {
|
|
557
|
+
onfinalityApiKey: onfinalityApiKey,
|
|
558
|
+
children: /*#__PURE__*/jsx(AllAddressesProvider, {
|
|
559
|
+
children: /*#__PURE__*/jsx(BalanceModulesProvider, {
|
|
560
|
+
balanceModules: balanceModules,
|
|
561
|
+
children: /*#__PURE__*/jsx(DbCacheProvider, {
|
|
562
|
+
useTestnets: useTestnets,
|
|
563
|
+
children: /*#__PURE__*/jsx(SubscriptionsProvider, {
|
|
564
|
+
children: children
|
|
565
|
+
})
|
|
566
|
+
})
|
|
567
|
+
})
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
});
|
|
571
|
+
|
|
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-pr563-
|
|
3
|
+
"version": "0.0.0-pr563-20230222051735",
|
|
4
4
|
"author": "Talisman",
|
|
5
5
|
"homepage": "https://talisman.xyz",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -26,14 +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-pr563-
|
|
29
|
+
"@talismn/balances": "^0.0.0-pr563-20230222051735",
|
|
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
34
|
"@talismn/token-rates": "^0.1.14",
|
|
35
35
|
"anylogger": "^1.0.11",
|
|
36
|
-
"dexie": "^3.2.
|
|
36
|
+
"dexie": "^3.2.2",
|
|
37
37
|
"dexie-react-hooks": "^1.1.1",
|
|
38
38
|
"react-use": "^17.4.0",
|
|
39
39
|
"rxjs": "^7.8.0"
|