@talismn/balances-react 0.0.0-pr2080-20250710073919 → 0.0.0-pr2091-20250715125148
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/dist/declarations/src/atoms/balances.d.ts +1 -5
- package/dist/declarations/src/atoms/balancesProvider.d.ts +2 -0
- package/dist/declarations/src/atoms/chaindata.d.ts +427 -1450
- package/dist/declarations/src/atoms/config.d.ts +0 -4
- package/dist/declarations/src/hooks/useBalances.d.ts +1 -2
- package/dist/declarations/src/hooks/useChaindata.d.ts +633 -709
- package/dist/declarations/src/index.d.ts +2 -8
- package/dist/talismn-balances-react.cjs.dev.js +107 -352
- package/dist/talismn-balances-react.cjs.prod.js +107 -352
- package/dist/talismn-balances-react.esm.js +109 -342
- package/package.json +14 -13
- package/dist/declarations/src/atoms/balanceModules.d.ts +0 -2
- package/dist/declarations/src/util/balancesPersist.d.ts +0 -10
|
@@ -2,29 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
var jotai = require('jotai');
|
|
4
4
|
var react = require('react');
|
|
5
|
-
var balances = require('@talismn/balances');
|
|
6
5
|
var tokenRates = require('@talismn/token-rates');
|
|
7
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
7
|
var chaindataProvider = require('@talismn/chaindata-provider');
|
|
9
|
-
var
|
|
8
|
+
var balances = require('@talismn/balances');
|
|
10
9
|
var jotaiEffect = require('jotai-effect');
|
|
11
|
-
var
|
|
12
|
-
var lodash = require('lodash');
|
|
13
|
-
var rxjs = require('rxjs');
|
|
14
|
-
var anylogger = require('anylogger');
|
|
10
|
+
var lodashEs = require('lodash-es');
|
|
15
11
|
var chainConnector = require('@talismn/chain-connector');
|
|
16
12
|
var chainConnectorEvm = require('@talismn/chain-connector-evm');
|
|
13
|
+
var chainConnectorSol = require('@talismn/chain-connector-sol');
|
|
17
14
|
var connectionMeta = require('@talismn/connection-meta');
|
|
15
|
+
var util = require('@talismn/util');
|
|
16
|
+
var utils = require('jotai/utils');
|
|
17
|
+
var rxjs = require('rxjs');
|
|
18
18
|
var dexie = require('dexie');
|
|
19
|
-
var
|
|
19
|
+
var anylogger = require('anylogger');
|
|
20
20
|
var utilCrypto = require('@polkadot/util-crypto');
|
|
21
21
|
|
|
22
22
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
23
|
|
|
24
24
|
var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
|
|
25
|
-
var isEqual__default = /*#__PURE__*/_interopDefault(isEqual);
|
|
26
25
|
|
|
27
|
-
const balanceModuleCreatorsAtom = jotai.atom(balances.defaultBalanceModules);
|
|
28
26
|
const innerCoinsApiConfigAtom = jotai.atom(tokenRates.DEFAULT_COINSAPI_CONFIG);
|
|
29
27
|
const coinsApiConfigAtom = jotai.atom(get => get(innerCoinsApiConfigAtom), (_get, set, options) => set(innerCoinsApiConfigAtom, {
|
|
30
28
|
apiUrl: options.apiUrl ?? tokenRates.DEFAULT_COINSAPI_CONFIG.apiUrl
|
|
@@ -36,55 +34,6 @@ const enabledTokensAtom = jotai.atom(undefined);
|
|
|
36
34
|
/** Sets the list of addresses for which token balances will be fetched by the balances subscription */
|
|
37
35
|
const allAddressesAtom = jotai.atom([]);
|
|
38
36
|
|
|
39
|
-
var packageJson = {
|
|
40
|
-
name: "@talismn/balances-react"};
|
|
41
|
-
|
|
42
|
-
var log = anylogger__default.default(packageJson.name);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
// Persistence backend for indexedDB
|
|
46
|
-
// Add a new backend by implementing the BalancesPersistBackend interface
|
|
47
|
-
// configureStore can be called with a different indexedDB table
|
|
48
|
-
*/
|
|
49
|
-
const {
|
|
50
|
-
persistData,
|
|
51
|
-
retrieveData
|
|
52
|
-
} = balances.configureStore();
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
// Persistence backend for localStorage
|
|
56
|
-
*/
|
|
57
|
-
const localStoragePersist = async balances$1 => {
|
|
58
|
-
const storedBalances = balances$1.map(b => {
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
60
|
-
const {
|
|
61
|
-
status,
|
|
62
|
-
...rest
|
|
63
|
-
} = b;
|
|
64
|
-
return rest;
|
|
65
|
-
});
|
|
66
|
-
const deflated = balances.compress(storedBalances);
|
|
67
|
-
localStorage.setItem("talismanBalances", deflated.toString());
|
|
68
|
-
};
|
|
69
|
-
const localStorageRetrieve = async () => {
|
|
70
|
-
const deflated = localStorage.getItem("talismanBalances");
|
|
71
|
-
if (deflated) {
|
|
72
|
-
// deflated will be a long string of numbers separated by commas
|
|
73
|
-
const deflatedArray = deflated.split(",").map(n => parseInt(n, 10));
|
|
74
|
-
const deflatedBytes = new Uint8Array(deflatedArray.length);
|
|
75
|
-
deflatedArray.forEach((n, i) => deflatedBytes[i] = n);
|
|
76
|
-
return balances.decompress(deflatedBytes).map(b => ({
|
|
77
|
-
...b,
|
|
78
|
-
status: "cache"
|
|
79
|
-
}));
|
|
80
|
-
}
|
|
81
|
-
return [];
|
|
82
|
-
};
|
|
83
|
-
const localStorageBalancesPersistBackend = {
|
|
84
|
-
persist: localStoragePersist,
|
|
85
|
-
retrieve: localStorageRetrieve
|
|
86
|
-
};
|
|
87
|
-
|
|
88
37
|
const chaindataProviderAtom = jotai.atom(() => {
|
|
89
38
|
return new chaindataProvider.ChaindataProvider({});
|
|
90
39
|
});
|
|
@@ -93,25 +42,52 @@ const chainConnectorsAtom = jotai.atom(get => {
|
|
|
93
42
|
const chaindataProvider = get(chaindataProviderAtom);
|
|
94
43
|
const substrate = new chainConnector.ChainConnector(chaindataProvider, connectionMeta.connectionMetaDb);
|
|
95
44
|
const evm = new chainConnectorEvm.ChainConnectorEvm(chaindataProvider);
|
|
45
|
+
const solana = new chainConnectorSol.ChainConnectorSol(chaindataProvider);
|
|
96
46
|
return {
|
|
97
47
|
substrate,
|
|
98
|
-
evm
|
|
48
|
+
evm,
|
|
49
|
+
solana
|
|
99
50
|
};
|
|
100
51
|
});
|
|
101
52
|
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
53
|
+
const balancesProviderAtom = jotai.atom(get => {
|
|
54
|
+
return new balances.BalancesProvider(get(chaindataProviderAtom), get(chainConnectorsAtom)) // TODO pass storage
|
|
55
|
+
;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const chaindataAtom = utils.atomWithObservable(get => {
|
|
59
|
+
return rxjs.combineLatest({
|
|
60
|
+
networks: get(chaindataProviderAtom).networks$,
|
|
61
|
+
tokens: get(chaindataProviderAtom).tokens$
|
|
62
|
+
}).pipe(util.firstThenDebounce(1_000));
|
|
63
|
+
});
|
|
64
|
+
const filteredChaindataAtom = jotai.atom(async get => {
|
|
65
|
+
const enabledNetworkIds = get(enabledChainsAtom);
|
|
66
|
+
const enabledTokenIds = get(enabledTokensAtom);
|
|
67
|
+
const enableTestnets = get(enableTestnetsAtom);
|
|
68
|
+
const chaindata = await get(chaindataAtom);
|
|
69
|
+
const networks = chaindata.networks.filter(n => (enabledNetworkIds?.includes(n.id) || n.isDefault) && (enableTestnets || !n.isTestnet));
|
|
70
|
+
const networkById = lodashEs.keyBy(networks, n => n.id);
|
|
71
|
+
const tokens = chaindata.tokens.filter(token => (enabledTokenIds?.includes(token.id) || token.isDefault) && networkById[token.networkId]);
|
|
72
|
+
return {
|
|
73
|
+
networks,
|
|
74
|
+
tokens
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
const tokensAtom = jotai.atom(async get => {
|
|
78
|
+
const chaindata = await get(filteredChaindataAtom);
|
|
79
|
+
return chaindata.tokens;
|
|
80
|
+
});
|
|
81
|
+
const networksAtom = jotai.atom(async get => {
|
|
82
|
+
const chaindata = await get(filteredChaindataAtom);
|
|
83
|
+
return chaindata.networks;
|
|
113
84
|
});
|
|
114
85
|
|
|
86
|
+
var packageJson = {
|
|
87
|
+
name: "@talismn/balances-react"};
|
|
88
|
+
|
|
89
|
+
var log = anylogger__default.default(packageJson.name);
|
|
90
|
+
|
|
115
91
|
/**
|
|
116
92
|
* Converts a dexie Observable into an rxjs Observable.
|
|
117
93
|
*/
|
|
@@ -125,54 +101,6 @@ function dexieToRxjs(o) {
|
|
|
125
101
|
});
|
|
126
102
|
}
|
|
127
103
|
|
|
128
|
-
const chainsAtom = jotai.atom(async get => (await get(chaindataAtom)).chains);
|
|
129
|
-
const chainsByIdAtom = jotai.atom(async get => (await get(chaindataAtom)).chainsById);
|
|
130
|
-
const chainsByGenesisHashAtom = jotai.atom(async get => (await get(chaindataAtom)).chainsByGenesisHash);
|
|
131
|
-
const evmNetworksAtom = jotai.atom(async get => (await get(chaindataAtom)).evmNetworks);
|
|
132
|
-
const evmNetworksByIdAtom = jotai.atom(async get => (await get(chaindataAtom)).evmNetworksById);
|
|
133
|
-
const tokensAtom = jotai.atom(async get => (await get(chaindataAtom)).tokens);
|
|
134
|
-
const tokensByIdAtom = jotai.atom(async get => (await get(chaindataAtom)).tokensById);
|
|
135
|
-
const miniMetadatasAtom = jotai.atom(async get => (await get(chaindataAtom)).miniMetadatas);
|
|
136
|
-
const chaindataAtom = utils.atomWithObservable(get => {
|
|
137
|
-
const enableTestnets = get(enableTestnetsAtom);
|
|
138
|
-
const filterTestnets = items => enableTestnets ? items : items.filter(({
|
|
139
|
-
isTestnet
|
|
140
|
-
}) => !isTestnet);
|
|
141
|
-
const filterMapTestnets = items => enableTestnets ? items : Object.fromEntries(Object.entries(items).filter(([, {
|
|
142
|
-
isTestnet
|
|
143
|
-
}]) => !isTestnet));
|
|
144
|
-
const filterEnabledTokens = tokens => tokens.filter(token => token.isDefault || "isCustom" in token && token.isCustom);
|
|
145
|
-
const filterMapEnabledTokens = tokensById => Object.fromEntries(Object.entries(tokensById).filter(([, token]) => token.isDefault || "isCustom" in token && token.isCustom));
|
|
146
|
-
const distinctUntilIsEqual = rxjs.distinctUntilChanged((a, b) => isEqual__default.default(a, b));
|
|
147
|
-
const chains = get(chaindataProviderAtom).getNetworks$("polkadot").pipe(distinctUntilIsEqual, rxjs.map(filterTestnets), distinctUntilIsEqual);
|
|
148
|
-
const chainsById = get(chaindataProviderAtom).getNetworksMapById$("polkadot").pipe(distinctUntilIsEqual, rxjs.map(filterMapTestnets), distinctUntilIsEqual);
|
|
149
|
-
const chainsByGenesisHash = get(chaindataProviderAtom).getNetworksMapByGenesisHash$().pipe(distinctUntilIsEqual, rxjs.map(filterMapTestnets), distinctUntilIsEqual);
|
|
150
|
-
const evmNetworks = get(chaindataProviderAtom).getNetworks$("ethereum").pipe(distinctUntilIsEqual, rxjs.map(filterTestnets), distinctUntilIsEqual);
|
|
151
|
-
const networks = get(chaindataProviderAtom).getNetworks$().pipe(distinctUntilIsEqual, rxjs.map(filterTestnets), distinctUntilIsEqual);
|
|
152
|
-
const evmNetworksById = get(chaindataProviderAtom).getNetworksMapById$("ethereum").pipe(distinctUntilIsEqual, rxjs.map(filterMapTestnets), distinctUntilIsEqual);
|
|
153
|
-
const networksById = get(chaindataProviderAtom).getNetworksMapById$().pipe(distinctUntilIsEqual, rxjs.map(filterMapTestnets), distinctUntilIsEqual);
|
|
154
|
-
const tokens = get(chaindataProviderAtom).tokens$.pipe(distinctUntilIsEqual, rxjs.map(filterEnabledTokens), distinctUntilIsEqual);
|
|
155
|
-
const tokensById = get(chaindataProviderAtom).getTokensMapById$().pipe(distinctUntilIsEqual, rxjs.map(filterMapEnabledTokens), distinctUntilIsEqual);
|
|
156
|
-
const miniMetadatasObservable = dexieToRxjs(dexie.liveQuery(() => balances.db.miniMetadatas.toArray()));
|
|
157
|
-
const miniMetadatas = rxjs.combineLatest([miniMetadatasObservable.pipe(distinctUntilIsEqual), chainsById]).pipe(rxjs.map(([miniMetadatas, chainsById]) => miniMetadatas.filter(m => chainsById[m.chainId])), distinctUntilIsEqual);
|
|
158
|
-
return rxjs.combineLatest({
|
|
159
|
-
networks,
|
|
160
|
-
networksById,
|
|
161
|
-
chains,
|
|
162
|
-
chainsById,
|
|
163
|
-
chainsByGenesisHash,
|
|
164
|
-
evmNetworks,
|
|
165
|
-
evmNetworksById,
|
|
166
|
-
tokens,
|
|
167
|
-
tokensById,
|
|
168
|
-
miniMetadatas
|
|
169
|
-
}).pipe(
|
|
170
|
-
// debounce to prevent hammering UI with updates
|
|
171
|
-
util.firstThenDebounce(1_000), distinctUntilIsEqual);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
const cryptoWaitReadyAtom = jotai.atom(async () => await utilCrypto.cryptoWaitReady());
|
|
175
|
-
|
|
176
104
|
const tokenRatesAtom = jotai.atom(async get => {
|
|
177
105
|
// runs a timer to keep tokenRates up to date
|
|
178
106
|
get(tokenRatesFetcherAtomEffect);
|
|
@@ -193,9 +121,9 @@ const tokenRatesFetcherAtomEffect = jotaiEffect.atomEffect(get => {
|
|
|
193
121
|
|
|
194
122
|
// we have to get these synchronously so that jotai knows to restart our timer when they change
|
|
195
123
|
const coinsApiConfig = get(coinsApiConfigAtom);
|
|
196
|
-
const
|
|
124
|
+
const tokensPromise = get(tokensAtom);
|
|
197
125
|
(async () => {
|
|
198
|
-
const tokensById = await
|
|
126
|
+
const tokensById = lodashEs.keyBy(await tokensPromise, "id");
|
|
199
127
|
const tokenIds = Object.keys(tokensById);
|
|
200
128
|
const loopMs = 300_000; // 300_000ms = 300s = 5 minutes
|
|
201
129
|
const retryTimeout = 5_000; // 5_000ms = 5 seconds
|
|
@@ -236,213 +164,42 @@ const tokenRatesFetcherAtomEffect = jotaiEffect.atomEffect(get => {
|
|
|
236
164
|
return () => abort.abort("Unsubscribed");
|
|
237
165
|
});
|
|
238
166
|
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const [balances$1, hydrateData] = await Promise.all([get(balancesObservableAtom), get(balancesHydrateDataAtom)]);
|
|
243
|
-
return new balances.Balances(Object.values(balances$1).filter(balance => !!hydrateData?.tokens?.[balance.tokenId]),
|
|
244
|
-
// hydrate balance chains, evmNetworks, tokens and tokenRates
|
|
245
|
-
hydrateData);
|
|
167
|
+
const addressesByTokenIdAtom = jotai.atom(async get => {
|
|
168
|
+
const [tokens, addresses] = await Promise.all([get(tokensAtom), get(allAddressesAtom)]);
|
|
169
|
+
return lodashEs.fromPairs(tokens.map(token => [token.id, addresses]));
|
|
246
170
|
});
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
171
|
+
const rawBalancesAtom = jotai.atom([]);
|
|
172
|
+
const subscribeBalancesAtom = jotaiEffect.atomEffect((get, set) => {
|
|
173
|
+
const unsub = (async () => {
|
|
174
|
+
const balancesProvider = get(balancesProviderAtom);
|
|
175
|
+
const addressesByTokenId = await get(addressesByTokenIdAtom);
|
|
176
|
+
const sub = balancesProvider.getBalances$(addressesByTokenId).subscribe(balances => {
|
|
177
|
+
set(rawBalancesAtom, balances.balances);
|
|
178
|
+
});
|
|
179
|
+
return () => {
|
|
180
|
+
return sub.unsubscribe();
|
|
181
|
+
};
|
|
182
|
+
})();
|
|
183
|
+
return () => {
|
|
184
|
+
unsub.then(unsubscribe => unsubscribe());
|
|
185
|
+
};
|
|
257
186
|
});
|
|
258
187
|
const balancesHydrateDataAtom = jotai.atom(async get => {
|
|
259
|
-
const [
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}, tokenRates] = await Promise.all([get(chaindataAtom), get(tokenRatesAtom)]);
|
|
188
|
+
const [chaindata, tokenRates] = await Promise.all([get(chaindataAtom), get(tokenRatesAtom)]);
|
|
189
|
+
const networksById = lodashEs.keyBy(chaindata.networks, "id");
|
|
190
|
+
const tokensById = lodashEs.keyBy(chaindata.tokens, "id");
|
|
263
191
|
return {
|
|
264
192
|
networks: networksById,
|
|
265
193
|
tokens: tokensById,
|
|
266
194
|
tokenRates
|
|
267
195
|
};
|
|
268
196
|
});
|
|
269
|
-
const
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const atomDependencies = Promise.all([get(cryptoWaitReadyAtom), get(balanceModulesAtom), get(allAddressesAtom), get(chainsAtom), get(chainsByIdAtom), get(evmNetworksAtom), get(evmNetworksByIdAtom), get(tokensAtom), get(tokensByIdAtom), get(miniMetadatasAtom), get(enabledChainsAtom), get(enabledTokensAtom), get(hydrateBalancesObservableAtom)]);
|
|
276
|
-
const persistBackend = get(balancesPersistBackendAtom);
|
|
277
|
-
const unsubsPromise = (async () => {
|
|
278
|
-
const [_cryptoReady, balanceModules, allAddresses, chains, chainsById, evmNetworks, evmNetworksById, tokens, tokensById, _miniMetadatas, enabledChainsConfig, enabledTokensConfig] = await atomDependencies;
|
|
279
|
-
if (abort.signal.aborted) return;
|
|
280
|
-
|
|
281
|
-
// persist data every thirty seconds
|
|
282
|
-
balancesObservable.pipe(rxjs.debounceTime(10000)).subscribe(balancesUpdate => {
|
|
283
|
-
persistBackend.persist(Object.values(balancesUpdate));
|
|
284
|
-
});
|
|
285
|
-
const updateBalances = async balancesUpdates => {
|
|
286
|
-
if (abort.signal.aborted) return;
|
|
287
|
-
const updatesWithIds = new balances.Balances(balancesUpdates);
|
|
288
|
-
const existing = balancesObservable.value;
|
|
289
|
-
|
|
290
|
-
// update initialising set here - before filtering out zero balances
|
|
291
|
-
// while this may include stale balances, the important thing is that the balance is no longer "initialising"
|
|
292
|
-
// balancesUpdates.forEach((b) => this.#initialising.delete(getBalanceId(b)))
|
|
293
|
-
|
|
294
|
-
const newlyZeroBalances = [];
|
|
295
|
-
const changedBalances = Object.fromEntries(updatesWithIds.each.filter(newB => {
|
|
296
|
-
const isZero = newB.total.tokens === "0";
|
|
297
|
-
// Keep new balances which are not zeros
|
|
298
|
-
const existingB = existing[newB.id];
|
|
299
|
-
if (!existingB && !isZero) return true;
|
|
300
|
-
const hasChanged = !lodash.isEqual(existingB, newB.toJSON());
|
|
301
|
-
// Collect balances now confirmed to be zero separately, so they can be filtered out from the main set
|
|
302
|
-
if (existingB && hasChanged && isZero) newlyZeroBalances.push(newB.id);
|
|
303
|
-
// Keep changed balances, which are not known zeros
|
|
304
|
-
return hasChanged && !isZero;
|
|
305
|
-
}).map(b => [b.id, b.toJSON()]));
|
|
306
|
-
if (Object.keys(changedBalances).length === 0 && newlyZeroBalances.length === 0) return;
|
|
307
|
-
const nonZeroBalances = newlyZeroBalances.length > 0 ? Object.fromEntries(Object.entries(existing).filter(([id]) => !newlyZeroBalances.includes(id))) : existing;
|
|
308
|
-
const newBalancesState = {
|
|
309
|
-
...nonZeroBalances,
|
|
310
|
-
...changedBalances
|
|
311
|
-
};
|
|
312
|
-
if (Object.keys(newBalancesState).length === 0) return;
|
|
313
|
-
balancesObservable.next(newBalancesState);
|
|
314
|
-
};
|
|
315
|
-
const deleteBalances = async balancesFilter => {
|
|
316
|
-
if (abort.signal.aborted) return;
|
|
317
|
-
const balancesToKeep = Object.fromEntries(new balances.Balances(Object.values(await get(balancesObservableAtom))).each.filter(b => !balancesFilter(b)).map(b => [b.id, b.toJSON()]));
|
|
318
|
-
balancesObservable.next(balancesToKeep);
|
|
319
|
-
};
|
|
320
|
-
const enabledChainIds = enabledChainsConfig?.map(genesisHash => chains.find(chain => chain.genesisHash === genesisHash)?.id);
|
|
321
|
-
const enabledChainsFilter = enabledChainIds ? token => token.platform === "polkadot" && enabledChainIds?.includes(token.networkId) : () => true;
|
|
322
|
-
const enabledTokensFilter = enabledTokensConfig ? token => enabledTokensConfig.includes(token.id) : () => true;
|
|
323
|
-
const enabledTokenIds = tokens.filter(enabledChainsFilter).filter(enabledTokensFilter).map(({
|
|
324
|
-
id
|
|
325
|
-
}) => id);
|
|
326
|
-
if (enabledTokenIds.length < 1 || allAddresses.length < 1) return;
|
|
327
|
-
const addressesByTokenByModule = {};
|
|
328
|
-
enabledTokenIds.flatMap(tokenId => tokensById[tokenId]).forEach(token => {
|
|
329
|
-
// filter out tokens on chains/evmNetworks which have no rpcs
|
|
330
|
-
const hasRpcs = token.networkId && (chainsById[token.networkId]?.rpcs?.length ?? 0) > 0 || token.networkId && (evmNetworksById[token.networkId]?.rpcs?.length ?? 0) > 0;
|
|
331
|
-
if (!hasRpcs) return;
|
|
332
|
-
if (!addressesByTokenByModule[token.type]) addressesByTokenByModule[token.type] = {};
|
|
333
|
-
addressesByTokenByModule[token.type][token.id] = allAddresses.filter(address => {
|
|
334
|
-
// for each address, fetch balances only from compatible chains
|
|
335
|
-
return util.isEthereumAddress(address) ? token.networkId || chainsById[token.networkId ?? ""]?.account === "secp256k1" : token.networkId && chainsById[token.networkId ?? ""]?.account !== "secp256k1";
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Delete invalid cached balances
|
|
340
|
-
const chainIds = new Set(chains.map(chain => chain.id));
|
|
341
|
-
const evmNetworkIds = new Set(evmNetworks.map(evmNetwork => evmNetwork.id));
|
|
342
|
-
await deleteBalances(balance => {
|
|
343
|
-
// delete cached balances for accounts which don't exist anymore
|
|
344
|
-
if (!balance.address || !allAddresses.includes(balance.address)) return true;
|
|
345
|
-
|
|
346
|
-
// delete cached balances when chain/evmNetwork doesn't exist
|
|
347
|
-
if (!chainIds.has(balance.networkId) && !evmNetworkIds.has(balance.networkId)) return true;
|
|
348
|
-
|
|
349
|
-
// delete cached balance when token doesn't exist / is disabled
|
|
350
|
-
if (!enabledTokenIds.includes(balance.tokenId)) return true;
|
|
351
|
-
|
|
352
|
-
// delete cached balance when module doesn't exist
|
|
353
|
-
if (!balanceModules.find(module => module.type === balance.source)) return true;
|
|
354
|
-
|
|
355
|
-
// delete cached balance for accounts on incompatible chains
|
|
356
|
-
// (substrate accounts shouldn't have evm balances)
|
|
357
|
-
// (evm accounts shouldn't have substrate balances (unless the chain uses secp256k1 accounts))
|
|
358
|
-
const chain = chains.find(({
|
|
359
|
-
id
|
|
360
|
-
}) => id === balance.networkId) || null;
|
|
361
|
-
const hasChain = chainIds.has(balance.networkId);
|
|
362
|
-
const hasEvmNetwork = evmNetworkIds.has(balance.networkId);
|
|
363
|
-
const chainUsesSecp256k1Accounts = chain?.account === "secp256k1";
|
|
364
|
-
if (!util.isEthereumAddress(balance.address)) {
|
|
365
|
-
if (!hasChain) return true;
|
|
366
|
-
if (chainUsesSecp256k1Accounts) return true;
|
|
367
|
-
}
|
|
368
|
-
if (util.isEthereumAddress(balance.address)) {
|
|
369
|
-
if (!hasEvmNetwork && !chainUsesSecp256k1Accounts) return true;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// keep balance
|
|
373
|
-
return false;
|
|
374
|
-
});
|
|
375
|
-
if (abort.signal.aborted) return;
|
|
376
|
-
|
|
377
|
-
// after 30 seconds, change the status of all balances still initializing to stale
|
|
378
|
-
setTimeout(() => {
|
|
379
|
-
if (abort.signal.aborted) return;
|
|
380
|
-
const staleObservable = balancesObservable.pipe(rxjs.map(val => Object.values(val).filter(({
|
|
381
|
-
status
|
|
382
|
-
}) => status === "cache").map(balance => ({
|
|
383
|
-
...balance,
|
|
384
|
-
status: "stale"
|
|
385
|
-
}))));
|
|
386
|
-
rxjs.firstValueFrom(staleObservable).then(v => {
|
|
387
|
-
if (v.length) updateBalances(v);
|
|
388
|
-
});
|
|
389
|
-
}, 30_000);
|
|
390
|
-
return balanceModules.map(balanceModule => {
|
|
391
|
-
const unsub = balances.balances(balanceModule, addressesByTokenByModule[balanceModule.type] ?? {}, (error, balances) => {
|
|
392
|
-
// log errors
|
|
393
|
-
if (error) {
|
|
394
|
-
if (error?.type === "STALE_RPC_ERROR" || error?.type === "WEBSOCKET_ALLOCATION_EXHAUSTED_ERROR") {
|
|
395
|
-
const addressesByModuleToken = addressesByTokenByModule[balanceModule.type] ?? {};
|
|
396
|
-
const staleObservable = balancesObservable.pipe(rxjs.map(val => Object.values(val).filter(balance => {
|
|
397
|
-
const {
|
|
398
|
-
tokenId,
|
|
399
|
-
address,
|
|
400
|
-
source
|
|
401
|
-
} = balance;
|
|
402
|
-
const chainComparison = error.chainId ? "chainId" in balance && error.chainId === balance.chainId : error.evmNetworkId ? "evmNetworkId" in balance && error.evmNetworkId === balance.evmNetworkId : true;
|
|
403
|
-
return chainComparison && addressesByModuleToken[tokenId]?.includes(address) && source === balanceModule.type;
|
|
404
|
-
}).map(balance => ({
|
|
405
|
-
...balance,
|
|
406
|
-
status: "stale"
|
|
407
|
-
}))));
|
|
408
|
-
rxjs.firstValueFrom(staleObservable).then(v => {
|
|
409
|
-
if (v.length) updateBalances(v);
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// ignore empty balance responses
|
|
416
|
-
if (!balances) return;
|
|
417
|
-
// ignore balances from old subscriptions which are still in the process of unsubscribing
|
|
418
|
-
if (abort.signal.aborted) return;
|
|
419
|
-
|
|
420
|
-
// good balances
|
|
421
|
-
if (balances) {
|
|
422
|
-
if ("status" in balances) {
|
|
423
|
-
// For modules using the new SubscriptionResultWithStatus pattern
|
|
424
|
-
//TODO fix initialisin
|
|
425
|
-
// if (result.status === "initialising") this.#initialising.add(balanceModule.type)
|
|
426
|
-
// else this.#initialising.delete(balanceModule.type)
|
|
427
|
-
updateBalances(balances.data);
|
|
428
|
-
} else {
|
|
429
|
-
// add good ones to initialisedBalances
|
|
430
|
-
updateBalances(Object.values(balances.toJSON()));
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
return () => unsub.then(unsubscribe => unsubscribe());
|
|
435
|
-
});
|
|
436
|
-
})();
|
|
437
|
-
|
|
438
|
-
// close the existing subscriptions when our effect unmounts
|
|
439
|
-
// (wait 2 seconds before actually unsubscribing, to allow the websocket to be reused in that time)
|
|
440
|
-
const unsubscribe = () => unsubsPromise.then(unsubs => {
|
|
441
|
-
persistBackend.persist(Object.values(balancesObservable.value));
|
|
442
|
-
unsubs?.forEach(unsub => unsub());
|
|
443
|
-
});
|
|
444
|
-
abort.signal.addEventListener("abort", () => setTimeout(unsubscribe, 2_000));
|
|
445
|
-
return () => abort.abort("Unsubscribed");
|
|
197
|
+
const balancesAtom = jotai.atom(async get => {
|
|
198
|
+
// subscribe to balancesProvider getBalance with addressesByTokenIdAtom as param
|
|
199
|
+
get(subscribeBalancesAtom);
|
|
200
|
+
const hydrate = await get(balancesHydrateDataAtom);
|
|
201
|
+
const rawBalances = get(rawBalancesAtom);
|
|
202
|
+
return new balances.Balances(rawBalances, hydrate);
|
|
446
203
|
});
|
|
447
204
|
|
|
448
205
|
const useSetBalancesAddresses = addresses => {
|
|
@@ -459,10 +216,8 @@ const useSetBalancesAddresses = addresses => {
|
|
|
459
216
|
* @returns a Balances object containing the current balances state.
|
|
460
217
|
*/
|
|
461
218
|
|
|
462
|
-
const useBalances =
|
|
463
|
-
|
|
464
|
-
if (persistBackend) setPersistBackend(persistBackend);
|
|
465
|
-
return jotai.useAtomValue(allBalancesAtom);
|
|
219
|
+
const useBalances = () => {
|
|
220
|
+
return jotai.useAtomValue(balancesAtom);
|
|
466
221
|
};
|
|
467
222
|
|
|
468
223
|
// TODO: Extract to shared definition between extension and @talismn/balances-react
|
|
@@ -499,30 +254,41 @@ const useChainConnectors = () => jotai.useAtomValue(chainConnectorsAtom);
|
|
|
499
254
|
|
|
500
255
|
const useChaindataProvider = () => jotai.useAtomValue(chaindataProviderAtom);
|
|
501
256
|
const useChaindata = () => jotai.useAtomValue(chaindataAtom);
|
|
502
|
-
const
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const
|
|
257
|
+
const useNetworks = () => useChaindata().networks;
|
|
258
|
+
const useNetworksById = () => {
|
|
259
|
+
const {
|
|
260
|
+
networks
|
|
261
|
+
} = useChaindata();
|
|
262
|
+
return react.useMemo(() => lodashEs.keyBy(networks, n => n.id), [networks]);
|
|
263
|
+
};
|
|
264
|
+
const useNetwork = networkId => {
|
|
265
|
+
const networksById = useNetworksById();
|
|
266
|
+
return networksById[networkId ?? ""] ?? null;
|
|
267
|
+
};
|
|
268
|
+
const useTokens = () => useChaindata().tokens;
|
|
269
|
+
const useTokensById = () => {
|
|
270
|
+
const {
|
|
271
|
+
tokens
|
|
272
|
+
} = useChaindata();
|
|
273
|
+
return react.useMemo(() => lodashEs.keyBy(tokens, t => t.id), [tokens]);
|
|
274
|
+
};
|
|
275
|
+
const useToken = tokenId => {
|
|
276
|
+
const tokensById = useTokensById();
|
|
277
|
+
return tokensById[tokenId ?? ""] ?? null;
|
|
278
|
+
};
|
|
510
279
|
|
|
511
280
|
const useTokenRates = () => jotai.useAtomValue(tokenRatesAtom);
|
|
512
281
|
const useTokenRate = tokenId => useTokenRates()[tokenId ?? ""] ?? undefined;
|
|
513
282
|
|
|
283
|
+
const cryptoWaitReadyAtom = jotai.atom(async () => await utilCrypto.cryptoWaitReady());
|
|
284
|
+
|
|
514
285
|
const BalancesProvider = ({
|
|
515
|
-
balanceModules,
|
|
516
286
|
coinsApiUrl,
|
|
517
287
|
withTestnets,
|
|
518
288
|
enabledChains,
|
|
519
289
|
enabledTokens,
|
|
520
290
|
children
|
|
521
291
|
}) => {
|
|
522
|
-
const setBalanceModules = jotai.useSetAtom(balanceModuleCreatorsAtom);
|
|
523
|
-
react.useEffect(() => {
|
|
524
|
-
if (balanceModules !== undefined) setBalanceModules(balanceModules);
|
|
525
|
-
}, [balanceModules, setBalanceModules]);
|
|
526
292
|
const setCoinsApiConfig = jotai.useSetAtom(coinsApiConfigAtom);
|
|
527
293
|
react.useEffect(() => {
|
|
528
294
|
setCoinsApiConfig({
|
|
@@ -572,41 +338,30 @@ Object.defineProperty(exports, "subTokensTokenId", {
|
|
|
572
338
|
});
|
|
573
339
|
exports.BalancesProvider = BalancesProvider;
|
|
574
340
|
exports.allAddressesAtom = allAddressesAtom;
|
|
575
|
-
exports.
|
|
576
|
-
exports.balanceModuleCreatorsAtom = balanceModuleCreatorsAtom;
|
|
577
|
-
exports.balanceModulesAtom = balanceModulesAtom;
|
|
578
|
-
exports.balancesPersistBackendAtom = balancesPersistBackendAtom;
|
|
341
|
+
exports.balancesAtom = balancesAtom;
|
|
579
342
|
exports.chainConnectorsAtom = chainConnectorsAtom;
|
|
580
343
|
exports.chaindataAtom = chaindataAtom;
|
|
581
344
|
exports.chaindataProviderAtom = chaindataProviderAtom;
|
|
582
|
-
exports.chainsAtom = chainsAtom;
|
|
583
|
-
exports.chainsByGenesisHashAtom = chainsByGenesisHashAtom;
|
|
584
|
-
exports.chainsByIdAtom = chainsByIdAtom;
|
|
585
345
|
exports.coinsApiConfigAtom = coinsApiConfigAtom;
|
|
586
346
|
exports.cryptoWaitReadyAtom = cryptoWaitReadyAtom;
|
|
587
347
|
exports.enableTestnetsAtom = enableTestnetsAtom;
|
|
588
348
|
exports.enabledChainsAtom = enabledChainsAtom;
|
|
589
349
|
exports.enabledTokensAtom = enabledTokensAtom;
|
|
590
|
-
exports.evmNetworksAtom = evmNetworksAtom;
|
|
591
|
-
exports.evmNetworksByIdAtom = evmNetworksByIdAtom;
|
|
592
350
|
exports.getStaleChains = getStaleChains;
|
|
593
|
-
exports.
|
|
351
|
+
exports.networksAtom = networksAtom;
|
|
594
352
|
exports.tokenRatesAtom = tokenRatesAtom;
|
|
595
353
|
exports.tokensAtom = tokensAtom;
|
|
596
|
-
exports.tokensByIdAtom = tokensByIdAtom;
|
|
597
354
|
exports.useBalances = useBalances;
|
|
598
355
|
exports.useBalancesStatus = useBalancesStatus;
|
|
599
|
-
exports.useChain = useChain;
|
|
600
356
|
exports.useChainConnectors = useChainConnectors;
|
|
601
357
|
exports.useChaindata = useChaindata;
|
|
602
358
|
exports.useChaindataProvider = useChaindataProvider;
|
|
603
|
-
exports.
|
|
604
|
-
exports.
|
|
605
|
-
exports.
|
|
606
|
-
exports.useEvmNetworks = useEvmNetworks;
|
|
607
|
-
exports.useMiniMetadatas = useMiniMetadatas;
|
|
359
|
+
exports.useNetwork = useNetwork;
|
|
360
|
+
exports.useNetworks = useNetworks;
|
|
361
|
+
exports.useNetworksById = useNetworksById;
|
|
608
362
|
exports.useSetBalancesAddresses = useSetBalancesAddresses;
|
|
609
363
|
exports.useToken = useToken;
|
|
610
364
|
exports.useTokenRate = useTokenRate;
|
|
611
365
|
exports.useTokenRates = useTokenRates;
|
|
612
366
|
exports.useTokens = useTokens;
|
|
367
|
+
exports.useTokensById = useTokensById;
|