@talismn/balances-react 0.3.0 → 0.3.2

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.
@@ -0,0 +1,356 @@
1
+ import { Balances, db, balances } from '@talismn/balances';
2
+ import { ChainConnector } from '@talismn/chain-connector';
3
+ import { ChainConnectorEvm } from '@talismn/chain-connector-evm';
4
+ import { useLiveQuery } from 'dexie-react-hooks';
5
+ import { useState, useEffect, useRef } from 'react';
6
+ import { useDebounce } from 'react-use';
7
+ import anylogger from 'anylogger';
8
+ import { ChaindataProviderExtension } from '@talismn/chaindata-provider-extension';
9
+ import { fetchTokenRates } from '@talismn/token-rates';
10
+
11
+ var packageJson = {
12
+ name: "@talismn/balances-react",
13
+ version: "0.3.2",
14
+ author: "Talisman",
15
+ homepage: "https://talisman.xyz",
16
+ license: "UNLICENSED",
17
+ publishConfig: {
18
+ access: "public"
19
+ },
20
+ repository: {
21
+ directory: "packages/balances-react",
22
+ type: "git",
23
+ url: "https://github.com/talismansociety/talisman.git"
24
+ },
25
+ main: "dist/talismn-balances-react.cjs.js",
26
+ module: "dist/talismn-balances-react.esm.js",
27
+ files: [
28
+ "/dist"
29
+ ],
30
+ engines: {
31
+ node: ">=14"
32
+ },
33
+ scripts: {
34
+ test: "jest",
35
+ lint: "eslint . --max-warnings 0",
36
+ clean: "rm -rf dist && rm -rf .turbo rm -rf node_modules"
37
+ },
38
+ dependencies: {
39
+ "@talismn/balances": "workspace:^",
40
+ "@talismn/chain-connector": "workspace:^",
41
+ "@talismn/chain-connector-evm": "workspace:^",
42
+ "@talismn/chaindata-provider": "workspace:^",
43
+ "@talismn/chaindata-provider-extension": "workspace:^",
44
+ "@talismn/token-rates": "workspace:^",
45
+ anylogger: "^1.0.11",
46
+ dexie: "^3.2.2",
47
+ "dexie-react-hooks": "^1.1.1",
48
+ "react-use": "^17.4.0"
49
+ },
50
+ devDependencies: {
51
+ "@talismn/eslint-config": "workspace:^",
52
+ "@talismn/tsconfig": "workspace:^",
53
+ "@types/jest": "^27.5.1",
54
+ "@types/react": "^18.0.17",
55
+ eslint: "^8.4.0",
56
+ jest: "^28.1.0",
57
+ react: "^18.2.0",
58
+ "ts-jest": "^28.0.2",
59
+ typescript: "^4.6.4"
60
+ },
61
+ peerDependencies: {
62
+ react: "*",
63
+ "react-dom": "*"
64
+ },
65
+ eslintConfig: {
66
+ root: true,
67
+ "extends": [
68
+ "@talismn/eslint-config/react"
69
+ ]
70
+ }
71
+ };
72
+
73
+ var log = anylogger(packageJson.name);
74
+
75
+ // TODO: Allow user to call useChaindata from multiple places
76
+ function useChaindata(options = {}) {
77
+ const [chaindataProvider, setChaindataProvider] = useState(null);
78
+
79
+ // this number is incremented each time the chaindataProvider has fetched new data
80
+ const [generation, setGeneration] = useState(0);
81
+ useEffect(() => {
82
+ const chaindataProvider = new ChaindataProviderExtension({
83
+ onfinalityApiKey: options.onfinalityApiKey
84
+ });
85
+ let shouldHydrate = true;
86
+ const timer = 300_000; // 300_000ms = 300s = 5 minutes
87
+ const hydrate = async () => {
88
+ if (!shouldHydrate) return;
89
+ try {
90
+ const updated = await chaindataProvider.hydrate();
91
+ if (updated) setGeneration(generation => (generation + 1) % Number.MAX_SAFE_INTEGER);
92
+ setTimeout(hydrate, timer);
93
+ } catch (error) {
94
+ const retryTimeout = 5_000; // 5_000ms = 5 seconds
95
+ log.error(`Failed to fetch chaindata, retrying in ${Math.round(retryTimeout / 1000)} seconds`, error);
96
+ setTimeout(hydrate, retryTimeout);
97
+ }
98
+ };
99
+ setChaindataProvider(chaindataProvider);
100
+ hydrate();
101
+ return () => {
102
+ shouldHydrate = false;
103
+ };
104
+ }, [options.onfinalityApiKey]);
105
+ if (chaindataProvider) chaindataProvider.generation = generation;
106
+ return chaindataProvider;
107
+ }
108
+ function useChains(chaindata) {
109
+ const [chains, setChains] = useState();
110
+ useEffect(() => {
111
+ if (!chaindata) return;
112
+ const thisGeneration = chaindata.generation;
113
+ chaindata.chains().then(chains => {
114
+ if (thisGeneration !== chaindata.generation) return;
115
+ setChains(chains);
116
+ });
117
+ }, [chaindata, chaindata?.generation]);
118
+ return chains || {};
119
+ }
120
+ function useChain(chaindata, chainId) {
121
+ const [chain, setChain] = useState();
122
+ useEffect(() => {
123
+ if (chaindata === null) return;
124
+ if (!chainId) return;
125
+ chaindata.getChain(chainId).then(setChain);
126
+ }, [chainId, chaindata, chaindata?.generation]);
127
+ return chain;
128
+ }
129
+ function useEvmNetworks(chaindata) {
130
+ const [evmNetworks, setEvmNetworks] = useState();
131
+ useEffect(() => {
132
+ if (!chaindata) return;
133
+ const thisGeneration = chaindata.generation;
134
+ chaindata.evmNetworks().then(evmNetworks => {
135
+ if (thisGeneration !== chaindata.generation) return;
136
+ setEvmNetworks(evmNetworks);
137
+ });
138
+ }, [chaindata, chaindata?.generation]);
139
+ return evmNetworks || {};
140
+ }
141
+ function useEvmNetwork(chaindata, evmNetworkId) {
142
+ const [evmNetwork, setEvmNetwork] = useState();
143
+ useEffect(() => {
144
+ if (chaindata === null) return;
145
+ if (!evmNetworkId) return;
146
+ chaindata.getEvmNetwork(evmNetworkId).then(setEvmNetwork);
147
+ }, [chaindata, chaindata?.generation, evmNetworkId]);
148
+ return evmNetwork;
149
+ }
150
+ function useTokens(chaindata) {
151
+ const [tokens, setTokens] = useState();
152
+ useEffect(() => {
153
+ if (!chaindata) return;
154
+ const thisGeneration = chaindata.generation;
155
+ chaindata.tokens().then(tokens => {
156
+ if (thisGeneration !== chaindata.generation) return;
157
+ setTokens(tokens);
158
+ });
159
+ }, [chaindata, chaindata?.generation]);
160
+ return tokens || {};
161
+ }
162
+ function useToken(chaindata, tokenId) {
163
+ const [token, setToken] = useState();
164
+ useEffect(() => {
165
+ if (chaindata === null) return;
166
+ if (!tokenId) return;
167
+ chaindata.getToken(tokenId).then(setToken);
168
+ }, [chaindata, chaindata?.generation, tokenId]);
169
+ return token;
170
+ }
171
+
172
+ function useTokenRates(tokens) {
173
+ const generation = useRef(0);
174
+ const [tokenRates, setTokenRates] = useState();
175
+ useEffect(() => {
176
+ if (!tokens) return;
177
+ if (Object.keys(tokens).length < 1) return;
178
+
179
+ // when we make a new request, we want to ignore any old requests which haven't yet completed
180
+ // otherwise we risk replacing the most recent data with older data
181
+ generation.current = (generation.current + 1) % Number.MAX_SAFE_INTEGER;
182
+ const thisGeneration = generation.current;
183
+ fetchTokenRates(tokens).then(tokenRates => {
184
+ if (thisGeneration !== generation.current) return;
185
+ setTokenRates(tokenRates);
186
+ });
187
+ }, [tokens]);
188
+ return tokenRates || {};
189
+ }
190
+
191
+ // TODO: Add the equivalent functionalty of `useDbCache` directly to this library.
192
+ //
193
+ // How it will work:
194
+ //
195
+ // useChains/useEvmNetworks/useTokens/useTokenRates will all make use of a
196
+ // useCachedDb hook, which internally subscribes to all of the db tables
197
+ // for everything, and then filters the subscribed data based on what params
198
+ // the caller of useChains/useTokens/etc has provided.
199
+ function useBalances(
200
+ // TODO: Make this array of BalanceModules more type-safe
201
+ balanceModules, chaindataProvider, addressesByToken, options = {}) {
202
+ useBalancesSubscriptions(balanceModules, chaindataProvider, addressesByToken, options);
203
+ const chains = useChains(chaindataProvider);
204
+ const evmNetworks = useEvmNetworks(chaindataProvider);
205
+ const tokens = useTokens(chaindataProvider);
206
+ const tokenRates = useTokenRates(tokens);
207
+ const balances = useLiveQuery(async () => new Balances(await db.balances.filter(balance => {
208
+ // check that this balance is included in our queried balance modules
209
+ if (!balanceModules.map(({
210
+ type
211
+ }) => type).includes(balance.source)) return false;
212
+
213
+ // check that our query includes some tokens and addresses
214
+ if (!addressesByToken) return false;
215
+
216
+ // check that this balance is included in our queried tokens
217
+ if (!Object.keys(addressesByToken).includes(balance.tokenId)) return false;
218
+
219
+ // check that this balance is included in our queried addresses for this token
220
+ if (!addressesByToken[balance.tokenId].includes(balance.address)) return false;
221
+
222
+ // keep this balance
223
+ return true;
224
+ }).toArray(),
225
+ // hydrate balance chains, evmNetworks, tokens and tokenRates
226
+ {
227
+ chains,
228
+ evmNetworks,
229
+ tokens,
230
+ tokenRates
231
+ }), [balanceModules, addressesByToken, chains, evmNetworks, tokens, tokenRates]);
232
+
233
+ // debounce every 100ms to prevent hammering UI with updates
234
+ const [debouncedBalances, setDebouncedBalances] = useState(balances);
235
+ useDebounce(() => balances && setDebouncedBalances(balances), 100, [balances]);
236
+ return debouncedBalances;
237
+ }
238
+
239
+ // TODO: Turn into react context
240
+ const subscriptions = {};
241
+
242
+ // This hook is responsible for allowing us to call useBalances
243
+ // from multiple components, without setting up unnecessary
244
+ // balance subscriptions
245
+ function useBalancesSubscriptions(
246
+ // TODO: Make this array of BalanceModules more type-safe
247
+ balanceModules, chaindataProvider, addressesByToken, options = {}) {
248
+ // const subscriptions = useRef<
249
+ // Record<string, { unsub: Promise<() => void>; refcount: number; generation: number }>
250
+ // >({})
251
+
252
+ const addSubscription = (key, balanceModule, chainConnectors, chaindataProvider, addressesByToken) => {
253
+ // create subscription if it doesn't already exist
254
+ if (!subscriptions[key] || subscriptions[key].refcount === 0) {
255
+ const generation = ((subscriptions[key]?.generation || 0) + 1) % Number.MAX_SAFE_INTEGER;
256
+ const unsub = balances(balanceModule, chainConnectors, chaindataProvider, addressesByToken, (error, balances) => {
257
+ if (error) return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
258
+ if (!balances) return;
259
+
260
+ // ignore balances from old subscriptions which are still in the process of unsubscribing
261
+ if (subscriptions[key].generation !== generation) return;
262
+ const putBalances = Object.entries(balances.toJSON()).map(([id, balance]) => ({
263
+ id,
264
+ ...balance
265
+ }));
266
+ db.transaction("rw", db.balances, async () => await db.balances.bulkPut(putBalances));
267
+ });
268
+ subscriptions[key] = {
269
+ unsub,
270
+ refcount: 0,
271
+ generation
272
+ };
273
+ }
274
+
275
+ // bump up the refcount by 1
276
+ subscriptions[key].refcount += 1;
277
+ };
278
+ const removeSubscription = (key, balanceModule, addressesByToken) => {
279
+ // ignore dead subscriptions
280
+ if (!subscriptions[key] || subscriptions[key].refcount === 0) return;
281
+
282
+ // drop the refcount by one
283
+ subscriptions[key].refcount -= 1;
284
+
285
+ // unsubscribe if refcount is now 0 (nobody wants this subcription anymore)
286
+ if (subscriptions[key].refcount < 1) {
287
+ // remove subscription
288
+ subscriptions[key].unsub.then(unsub => unsub());
289
+ delete subscriptions[key];
290
+
291
+ // set this subscription's balances in the store to status: cache
292
+ db.transaction("rw", db.balances, async () => await db.balances.filter(balance => {
293
+ if (balance.source !== balanceModule.type) return false;
294
+ if (!Object.keys(addressesByToken).includes(balance.tokenId)) return false;
295
+ if (!addressesByToken[balance.tokenId].includes(balance.address)) return false;
296
+ return true;
297
+ }).modify({
298
+ status: "cache"
299
+ }));
300
+ }
301
+ };
302
+ const chainConnector = useChainConnector(chaindataProvider);
303
+ const chainConnectorEvm = useChainConnectorEvm(chaindataProvider, options);
304
+ const tokens = useTokens(chaindataProvider);
305
+ useEffect(() => {
306
+ if (chainConnector === null) return;
307
+ if (chainConnectorEvm === null) return;
308
+ if (chaindataProvider === null) return;
309
+ if (addressesByToken === null) return;
310
+ const unsubs = balanceModules.map(balanceModule => {
311
+ const subscriptionKey = `${balanceModule.type}-${JSON.stringify(addressesByToken)}`;
312
+
313
+ // filter out tokens to only include those which this module knows how to fetch balances for
314
+ const moduleTokenIds = Object.values(tokens).filter(({
315
+ type
316
+ }) => type === balanceModule.type).map(({
317
+ id
318
+ }) => id);
319
+ const addressesByModuleToken = Object.fromEntries(Object.entries(addressesByToken).filter(([tokenId]) => moduleTokenIds.includes(tokenId)));
320
+
321
+ // add balance subscription for this module
322
+ addSubscription(subscriptionKey, balanceModule, {
323
+ substrate: chainConnector,
324
+ evm: chainConnectorEvm
325
+ }, chaindataProvider, addressesByModuleToken);
326
+
327
+ // return an unsub method, to be called when this effect unmounts
328
+ return () => removeSubscription(subscriptionKey, balanceModule, addressesByToken);
329
+ });
330
+ const unsubAll = () => unsubs.forEach(unsub => unsub());
331
+ return unsubAll;
332
+ }, [addressesByToken, balanceModules, chainConnector, chainConnectorEvm, chaindataProvider, tokens]);
333
+ }
334
+
335
+ // TODO: Allow advanced users of this library to provide their own chain connector
336
+ function useChainConnector(chaindataProvider) {
337
+ const [chainConnector, setChainConnector] = useState(null);
338
+ useEffect(() => {
339
+ if (chaindataProvider === null) return;
340
+ setChainConnector(new ChainConnector(chaindataProvider));
341
+ }, [chaindataProvider]);
342
+ return chainConnector;
343
+ }
344
+ // TODO: Allow advanced users of this library to provide their own chain connector
345
+ function useChainConnectorEvm(chaindataProvider, options = {}) {
346
+ const [chainConnectorEvm, setChainConnectorEvm] = useState(null);
347
+ useEffect(() => {
348
+ if (chaindataProvider === null) return;
349
+ setChainConnectorEvm(new ChainConnectorEvm(chaindataProvider, {
350
+ onfinalityApiKey: options.onfinalityApiKey
351
+ }));
352
+ }, [chaindataProvider, options.onfinalityApiKey]);
353
+ return chainConnectorEvm;
354
+ }
355
+
356
+ export { useBalances, useChain, useChaindata, useChains, useEvmNetwork, useEvmNetworks, useToken, useTokenRates, useTokens };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/balances-react",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "UNLICENSED",
@@ -12,8 +12,8 @@
12
12
  "type": "git",
13
13
  "url": "https://github.com/talismansociety/talisman.git"
14
14
  },
15
- "main": "dist/index.js",
16
- "types": "dist/index.d.ts",
15
+ "main": "dist/talismn-balances-react.cjs.js",
16
+ "module": "dist/talismn-balances-react.esm.js",
17
17
  "files": [
18
18
  "/dist"
19
19
  ],
@@ -21,29 +21,25 @@
21
21
  "node": ">=14"
22
22
  },
23
23
  "scripts": {
24
- "dev": "tsc --watch --declarationMap",
25
- "build": "tsc --declarationMap",
26
- "build:packages:prod": "rm -rf dist && tsc",
27
- "prepack": "yarn build:packages:prod",
28
24
  "test": "jest",
29
25
  "lint": "eslint . --max-warnings 0",
30
26
  "clean": "rm -rf dist && rm -rf .turbo rm -rf node_modules"
31
27
  },
32
28
  "dependencies": {
33
- "@talismn/balances": "^0.3.0",
34
- "@talismn/chain-connector": "^0.2.1",
35
- "@talismn/chain-connector-evm": "^0.4.0",
36
- "@talismn/chaindata-provider": "^0.2.1",
37
- "@talismn/chaindata-provider-extension": "^0.2.1",
38
- "@talismn/token-rates": "^0.1.12",
29
+ "@talismn/balances": "^0.3.2",
30
+ "@talismn/chain-connector": "^0.4.1",
31
+ "@talismn/chain-connector-evm": "^0.4.1",
32
+ "@talismn/chaindata-provider": "^0.4.1",
33
+ "@talismn/chaindata-provider-extension": "^0.4.1",
34
+ "@talismn/token-rates": "^0.1.13",
39
35
  "anylogger": "^1.0.11",
40
36
  "dexie": "^3.2.2",
41
37
  "dexie-react-hooks": "^1.1.1",
42
38
  "react-use": "^17.4.0"
43
39
  },
44
40
  "devDependencies": {
45
- "@talismn/eslint-config": "^0.0.0",
46
- "@talismn/tsconfig": "^0.0.1",
41
+ "@talismn/eslint-config": "^0.0.1",
42
+ "@talismn/tsconfig": "^0.0.2",
47
43
  "@types/jest": "^27.5.1",
48
44
  "@types/react": "^18.0.17",
49
45
  "eslint": "^8.4.0",
@@ -59,7 +55,7 @@
59
55
  "eslintConfig": {
60
56
  "root": true,
61
57
  "extends": [
62
- "@talismn/eslint-config"
58
+ "@talismn/eslint-config/react"
63
59
  ]
64
60
  }
65
61
  }
@@ -1,3 +0,0 @@
1
- export * from "./useBalances";
2
- export * from "./useChaindata";
3
- export * from "./useTokenRates";
@@ -1,3 +0,0 @@
1
- import { AddressesByToken, BalanceModule, Balances } from "@talismn/balances";
2
- import { ChaindataProvider, Token } from "@talismn/chaindata-provider";
3
- export declare function useBalances(balanceModules: Array<BalanceModule<any, any, any, any>>, chaindataProvider: ChaindataProvider | null, addressesByToken: AddressesByToken<Token> | null): Balances | undefined;
@@ -1,158 +0,0 @@
1
- import { Balances, balances as balancesFn, } from "@talismn/balances";
2
- import { db } from "@talismn/balances";
3
- import { ChainConnector } from "@talismn/chain-connector";
4
- import { ChainConnectorEvm } from "@talismn/chain-connector-evm";
5
- import { useLiveQuery } from "dexie-react-hooks";
6
- import { useEffect, useState } from "react";
7
- import { useDebounce } from "react-use";
8
- import log from "../log";
9
- import { useChains, useEvmNetworks, useTokens } from "./useChaindata";
10
- import { useTokenRates } from "./useTokenRates";
11
- // TODO: Add the equivalent functionalty of `useDbCache` directly to this library.
12
- //
13
- // How it will work:
14
- //
15
- // useChains/useEvmNetworks/useTokens/useTokenRates will all make use of a
16
- // useCachedDb hook, which internally subscribes to all of the db tables
17
- // for everything, and then filters the subscribed data based on what params
18
- // the caller of useChains/useTokens/etc has provided.
19
- export function useBalances(
20
- // TODO: Make this array of BalanceModules more type-safe
21
- balanceModules, chaindataProvider, addressesByToken) {
22
- useBalancesSubscriptions(balanceModules, chaindataProvider, addressesByToken);
23
- const chains = useChains(chaindataProvider);
24
- const evmNetworks = useEvmNetworks(chaindataProvider);
25
- const tokens = useTokens(chaindataProvider);
26
- const tokenRates = useTokenRates(tokens);
27
- const balances = useLiveQuery(async () => new Balances(await db.balances
28
- .filter((balance) => {
29
- // check that this balance is included in our queried balance modules
30
- if (!balanceModules.map(({ type }) => type).includes(balance.source))
31
- return false;
32
- // check that our query includes some tokens and addresses
33
- if (!addressesByToken)
34
- return false;
35
- // check that this balance is included in our queried tokens
36
- if (!Object.keys(addressesByToken).includes(balance.tokenId))
37
- return false;
38
- // check that this balance is included in our queried addresses for this token
39
- if (!addressesByToken[balance.tokenId].includes(balance.address))
40
- return false;
41
- // keep this balance
42
- return true;
43
- })
44
- .toArray(),
45
- // hydrate balance chains, evmNetworks, tokens and tokenRates
46
- { chains, evmNetworks, tokens, tokenRates }), [balanceModules, addressesByToken, chains, evmNetworks, tokens, tokenRates]);
47
- // debounce every 100ms to prevent hammering UI with updates
48
- const [debouncedBalances, setDebouncedBalances] = useState(balances);
49
- useDebounce(() => balances && setDebouncedBalances(balances), 100, [balances]);
50
- return debouncedBalances;
51
- }
52
- // TODO: Turn into react context
53
- const subscriptions = {};
54
- // This hook is responsible for allowing us to call useBalances
55
- // from multiple components, without setting up unnecessary
56
- // balance subscriptions
57
- function useBalancesSubscriptions(
58
- // TODO: Make this array of BalanceModules more type-safe
59
- balanceModules, chaindataProvider, addressesByToken) {
60
- // const subscriptions = useRef<
61
- // Record<string, { unsub: Promise<() => void>; refcount: number; generation: number }>
62
- // >({})
63
- const addSubscription = (key, balanceModule, chainConnectors, chaindataProvider, addressesByToken) => {
64
- // create subscription if it doesn't already exist
65
- if (!subscriptions[key] || subscriptions[key].refcount === 0) {
66
- const generation = ((subscriptions[key]?.generation || 0) + 1) % Number.MAX_SAFE_INTEGER;
67
- const unsub = balancesFn(balanceModule, chainConnectors, chaindataProvider, addressesByToken, (error, balances) => {
68
- if (error)
69
- return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
70
- if (!balances)
71
- return;
72
- // ignore balances from old subscriptions which are still in the process of unsubscribing
73
- if (subscriptions[key].generation !== generation)
74
- return;
75
- const putBalances = Object.entries(balances.toJSON()).map(([id, balance]) => ({
76
- id,
77
- ...balance,
78
- }));
79
- db.transaction("rw", db.balances, async () => await db.balances.bulkPut(putBalances));
80
- });
81
- subscriptions[key] = { unsub, refcount: 0, generation };
82
- }
83
- // bump up the refcount by 1
84
- subscriptions[key].refcount += 1;
85
- };
86
- const removeSubscription = (key, balanceModule, addressesByToken) => {
87
- // ignore dead subscriptions
88
- if (!subscriptions[key] || subscriptions[key].refcount === 0)
89
- return;
90
- // drop the refcount by one
91
- subscriptions[key].refcount -= 1;
92
- // unsubscribe if refcount is now 0 (nobody wants this subcription anymore)
93
- if (subscriptions[key].refcount < 1) {
94
- // remove subscription
95
- subscriptions[key].unsub.then((unsub) => unsub());
96
- delete subscriptions[key];
97
- // set this subscription's balances in the store to status: cache
98
- db.transaction("rw", db.balances, async () => await db.balances
99
- .filter((balance) => {
100
- if (balance.source !== balanceModule.type)
101
- return false;
102
- if (!Object.keys(addressesByToken).includes(balance.tokenId))
103
- return false;
104
- if (!addressesByToken[balance.tokenId].includes(balance.address))
105
- return false;
106
- return true;
107
- })
108
- .modify({ status: "cache" }));
109
- }
110
- };
111
- const chainConnector = useChainConnector(chaindataProvider);
112
- const chainConnectorEvm = useChainConnectorEvm(chaindataProvider);
113
- const tokens = useTokens(chaindataProvider);
114
- useEffect(() => {
115
- if (chainConnector === null)
116
- return;
117
- if (chainConnectorEvm === null)
118
- return;
119
- if (chaindataProvider === null)
120
- return;
121
- if (addressesByToken === null)
122
- return;
123
- const unsubs = balanceModules.map((balanceModule) => {
124
- const subscriptionKey = `${balanceModule.type}-${JSON.stringify(addressesByToken)}`;
125
- // filter out tokens to only include those which this module knows how to fetch balances for
126
- const moduleTokenIds = Object.values(tokens)
127
- .filter(({ type }) => type === balanceModule.type)
128
- .map(({ id }) => id);
129
- const addressesByModuleToken = Object.fromEntries(Object.entries(addressesByToken).filter(([tokenId]) => moduleTokenIds.includes(tokenId)));
130
- // add balance subscription for this module
131
- addSubscription(subscriptionKey, balanceModule, { substrate: chainConnector, evm: chainConnectorEvm }, chaindataProvider, addressesByModuleToken);
132
- // return an unsub method, to be called when this effect unmounts
133
- return () => removeSubscription(subscriptionKey, balanceModule, addressesByToken);
134
- });
135
- const unsubAll = () => unsubs.forEach((unsub) => unsub());
136
- return unsubAll;
137
- }, [addressesByToken, chainConnector, chainConnectorEvm, chaindataProvider, tokens]);
138
- }
139
- // TODO: Allow advanced users of this library to provide their own chain connector
140
- function useChainConnector(chaindataProvider) {
141
- const [chainConnector, setChainConnector] = useState(null);
142
- useEffect(() => {
143
- if (chaindataProvider === null)
144
- return;
145
- setChainConnector(new ChainConnector(chaindataProvider));
146
- }, [chaindataProvider]);
147
- return chainConnector;
148
- }
149
- // TODO: Allow advanced users of this library to provide their own chain connector
150
- function useChainConnectorEvm(chaindataProvider) {
151
- const [chainConnectorEvm, setChainConnectorEvm] = useState(null);
152
- useEffect(() => {
153
- if (chaindataProvider === null)
154
- return;
155
- setChainConnectorEvm(new ChainConnectorEvm(chaindataProvider));
156
- }, [chaindataProvider]);
157
- return chainConnectorEvm;
158
- }