@talismn/chaindata-provider 0.8.0 → 0.8.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.
Files changed (77) hide show
  1. package/dist/declarations/src/ChaindataProvider.d.ts +90 -17
  2. package/dist/declarations/src/TalismanChaindataDatabase.d.ts +9 -0
  3. package/dist/declarations/src/constants.d.ts +13 -14
  4. package/dist/declarations/src/index.d.ts +5 -3
  5. package/dist/declarations/src/init/chains.d.ts +243 -0
  6. package/dist/declarations/src/init/evm-networks.d.ts +61 -0
  7. package/dist/declarations/src/init/index.d.ts +5 -0
  8. package/dist/declarations/src/init/mini-metadatas.d.ts +21 -0
  9. package/dist/declarations/src/init/tokens.d.ts +103 -0
  10. package/dist/declarations/src/log.d.ts +2 -0
  11. package/dist/declarations/src/net.d.ts +8 -0
  12. package/dist/declarations/src/types/Chain.d.ts +29 -1
  13. package/dist/declarations/src/types/ChaindataProviderInterface.d.ts +42 -0
  14. package/dist/declarations/src/types/EvmNetwork.d.ts +11 -1
  15. package/dist/declarations/src/types/Token/EvmErc20Token.d.ts +15 -0
  16. package/dist/declarations/src/types/Token/EvmNativeToken.d.ts +10 -0
  17. package/dist/declarations/src/types/Token/EvmUniswapV2Token.d.ts +22 -0
  18. package/dist/declarations/src/types/Token/SubstrateAssetsToken.d.ts +12 -0
  19. package/dist/declarations/src/types/Token/SubstrateEquilibriumToken.d.ts +11 -0
  20. package/dist/declarations/src/types/Token/SubstrateForeignAssetsToken.d.ts +12 -0
  21. package/dist/declarations/src/types/Token/SubstrateNativeToken.d.ts +13 -0
  22. package/dist/declarations/src/types/Token/SubstratePsp22Token.d.ts +11 -0
  23. package/dist/declarations/src/types/Token/SubstrateTokensToken.d.ts +11 -0
  24. package/dist/declarations/src/types/Token/index.d.ts +26 -0
  25. package/dist/declarations/src/types/Token/types.d.ts +27 -0
  26. package/dist/declarations/src/types/index.d.ts +1 -0
  27. package/dist/declarations/src/upgrades/2024-01-25-upgradeAddIsDefaultToExistingChains.d.ts +2 -0
  28. package/dist/declarations/src/upgrades/2024-01-25-upgradeRemoveSymbolFromNativeTokenId.d.ts +2 -0
  29. package/dist/declarations/src/upgrades/index.d.ts +1 -0
  30. package/dist/declarations/src/util.d.ts +27 -0
  31. package/dist/net-BE0MfrDv.cjs.dev.js +89 -0
  32. package/dist/net-BE0MfrDv.cjs.prod.js +89 -0
  33. package/dist/net-il4EYhJN.esm.js +55 -0
  34. package/dist/talismn-chaindata-provider.cjs.dev.js +893 -57
  35. package/dist/talismn-chaindata-provider.cjs.prod.js +893 -57
  36. package/dist/talismn-chaindata-provider.esm.js +842 -31
  37. package/init/chains/dist/talismn-chaindata-provider-init-chains.cjs.d.ts +1 -0
  38. package/init/chains/dist/talismn-chaindata-provider-init-chains.cjs.dev.js +910 -0
  39. package/init/chains/dist/talismn-chaindata-provider-init-chains.cjs.js +7 -0
  40. package/init/chains/dist/talismn-chaindata-provider-init-chains.cjs.prod.js +910 -0
  41. package/init/chains/dist/talismn-chaindata-provider-init-chains.esm.js +908 -0
  42. package/init/chains/package.json +4 -0
  43. package/init/evm-networks/dist/talismn-chaindata-provider-init-evm-networks.cjs.d.ts +1 -0
  44. package/init/evm-networks/dist/talismn-chaindata-provider-init-evm-networks.cjs.dev.js +69 -0
  45. package/init/evm-networks/dist/talismn-chaindata-provider-init-evm-networks.cjs.js +7 -0
  46. package/init/evm-networks/dist/talismn-chaindata-provider-init-evm-networks.cjs.prod.js +69 -0
  47. package/init/evm-networks/dist/talismn-chaindata-provider-init-evm-networks.esm.js +67 -0
  48. package/init/evm-networks/package.json +4 -0
  49. package/init/mini-metadatas/dist/talismn-chaindata-provider-init-mini-metadatas.cjs.d.ts +1 -0
  50. package/init/mini-metadatas/dist/talismn-chaindata-provider-init-mini-metadatas.cjs.dev.js +311 -0
  51. package/init/mini-metadatas/dist/talismn-chaindata-provider-init-mini-metadatas.cjs.js +7 -0
  52. package/init/mini-metadatas/dist/talismn-chaindata-provider-init-mini-metadatas.cjs.prod.js +311 -0
  53. package/init/mini-metadatas/dist/talismn-chaindata-provider-init-mini-metadatas.esm.js +309 -0
  54. package/init/mini-metadatas/package.json +4 -0
  55. package/init/tokens/dist/talismn-chaindata-provider-init-tokens.cjs.d.ts +1 -0
  56. package/init/tokens/dist/talismn-chaindata-provider-init-tokens.cjs.dev.js +406 -0
  57. package/init/tokens/dist/talismn-chaindata-provider-init-tokens.cjs.js +7 -0
  58. package/init/tokens/dist/talismn-chaindata-provider-init-tokens.cjs.prod.js +406 -0
  59. package/init/tokens/dist/talismn-chaindata-provider-init-tokens.esm.js +404 -0
  60. package/init/tokens/package.json +4 -0
  61. package/net/dist/talismn-chaindata-provider-net.cjs.d.ts +1 -0
  62. package/net/dist/talismn-chaindata-provider-net.cjs.dev.js +14 -0
  63. package/net/dist/talismn-chaindata-provider-net.cjs.js +7 -0
  64. package/net/dist/talismn-chaindata-provider-net.cjs.prod.js +14 -0
  65. package/net/dist/talismn-chaindata-provider-net.esm.js +1 -0
  66. package/net/package.json +4 -0
  67. package/package.json +26 -13
  68. package/CHANGELOG.md +0 -115
  69. package/dist/declarations/src/helpers.d.ts +0 -3
  70. package/dist/declarations/src/plugins.d.ts +0 -17
  71. package/dist/declarations/src/types/Token.d.ts +0 -58
  72. package/plugins/dist/talismn-chaindata-provider-plugins.cjs.d.ts +0 -1
  73. package/plugins/dist/talismn-chaindata-provider-plugins.cjs.dev.js +0 -2
  74. package/plugins/dist/talismn-chaindata-provider-plugins.cjs.js +0 -7
  75. package/plugins/dist/talismn-chaindata-provider-plugins.cjs.prod.js +0 -2
  76. package/plugins/dist/talismn-chaindata-provider-plugins.esm.js +0 -1
  77. package/plugins/package.json +0 -4
@@ -1,41 +1,126 @@
1
- // @dev : temporarily change branch here when testing changes in chaindata
2
- const CHAINDATA_BRANCH = "main";
1
+ import { f as fetchChain, a as fetchSubstrateToken, b as fetchEvmNetwork, c as fetchChains, d as fetchEvmNetworks, g as githubTokenLogoUrl, e as githubUnknownTokenLogoUrl, h as fetchSubstrateTokens } from './net-il4EYhJN.esm.js';
2
+ export { G as availableTokenLogoFilenames, y as chaindataChainByGenesisHashUrl, x as chaindataChainByIdUrl, v as chaindataChainsAllUrl, w as chaindataChainsSummaryUrl, B as chaindataEvmNetworkByIdUrl, z as chaindataEvmNetworksAllUrl, A as chaindataEvmNetworksSummaryUrl, E as chaindataMiniMetadatasAllUrl, D as chaindataTokenByIdUrl, C as chaindataTokensAllUrl, F as fetchMiniMetadatas, i as githubApi, j as githubCdn, s as githubChainLogoUrl, o as githubChaindataBaseUrl, m as githubChaindataBranch, q as githubChaindataChainsAssetsDir, n as githubChaindataDistDir, p as githubChaindataDistUrl, k as githubChaindataOrg, l as githubChaindataRepo, r as githubChaindataTokensAssetsDir, t as githubEvmNetworkLogoUrl, u as githubUnknownChainLogoUrl } from './net-il4EYhJN.esm.js';
3
+ import { chains } from '../init/chains/dist/talismn-chaindata-provider-init-chains.esm.js';
4
+ import { evmNetworks } from '../init/evm-networks/dist/talismn-chaindata-provider-init-evm-networks.esm.js';
5
+ import { miniMetadatas } from '../init/mini-metadatas/dist/talismn-chaindata-provider-init-mini-metadatas.esm.js';
6
+ import { tokens } from '../init/tokens/dist/talismn-chaindata-provider-init-tokens.esm.js';
7
+ import { firstValueFrom, ReplaySubject, map } from 'rxjs';
8
+ import { Dexie, liveQuery } from 'dexie';
9
+ import anylogger from 'anylogger';
3
10
 
11
+ // The init files imported in this module are generated by a script.
4
12
  //
5
- // Chaindata published files (dist folder)
13
+ // They are used in two places:
14
+ // 1. As fallbacks for fresh installations who are unable to pull an initial set of data from the upstream chaindata repo.
15
+ // 2. As mock data for tests.
6
16
  //
17
+ // They should be periodically updated with the latest state of chaindata.
18
+ // You can update them by running the following command:
19
+ // `pnpm chore:generate-init-data`
7
20
 
8
- const chaindataUrl = `https://raw.githubusercontent.com/TalismanSociety/chaindata/${CHAINDATA_BRANCH}/dist`;
9
- const chaindataChainsAllUrl = `${chaindataUrl}/chains/all.json`;
10
- const chaindataChainsSummaryUrl = `${chaindataUrl}/chains/summary.json`;
11
- const chaindataChainByIdUrl = chainId => `${chaindataUrl}/chains/byId/${chainId}.json`;
12
- const chaindataChainByGenesisHashUrl = genesisHash => `${chaindataUrl}/chains/byGenesisHash/${genesisHash}.json`;
13
- const chaindataEvmNetworksAllUrl = `${chaindataUrl}/evmNetworks/all.json`;
14
- const chaindataEvmNetworksSummaryUrl = `${chaindataUrl}/evmNetworks/summary.json`;
15
- const chaindataEvmNetworkByIdUrl = evmNetworkId => `${chaindataUrl}/evmNetworks/byId/${evmNetworkId}.json`;
16
- const chaindataTokensAllUrl = `${chaindataUrl}/tokens/all.json`;
17
- const chaindataTokenByIdUrl = tokenId => `${chaindataUrl}/tokens/byId/${tokenId}.json`;
18
- const chaindataMiniMetadatasAllUrl = `${chaindataUrl}/miniMetadatas/all.json`;
21
+ const fetchInitChains = async () => chains;
22
+ const fetchInitEvmNetworks = async () => evmNetworks;
23
+ const fetchInitSubstrateTokens = async () => tokens;
24
+
25
+ // TODO: Move `fetchMiniMetadatas` into `@talismn/balances`,
26
+ // so that we don't have a circular import between `@talismn/balances` and `@talismn/chaindata-provider`.
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ const fetchInitMiniMetadatas = async () => miniMetadatas;
29
+
30
+ /**
31
+ * Util to add our onfinality api key to any public onfinality RPC urls in an array of chains.
32
+ */
33
+ const addCustomChainRpcs = (chains, onfinalityApiKey) => chains.map(chain => {
34
+ // copy chain instead of mutating
35
+ const chainWithCustomRpcs = {
36
+ ...chain
37
+ };
38
+ if (typeof onfinalityApiKey !== "string" || !onfinalityApiKey) return chainWithCustomRpcs;
39
+
40
+ // add rpcs
41
+ chainWithCustomRpcs.rpcs = (chainWithCustomRpcs.rpcs || []
42
+ // convert public onfinality rpc endpoints to private onfinality rpc endpoints
43
+ ).map(rpc => {
44
+ rpc.url = rpc.url.replace(/^wss:\/\/([A-z-]+)\.api\.onfinality\.io\/public-ws\/?$/, `wss://$1.api.onfinality.io/ws?apikey=${onfinalityApiKey}`);
45
+ return rpc;
46
+ })
47
+ // prioritise onfinality rpcs
48
+ .sort((a, b) => {
49
+ if (a.url.includes("api.onfinality.io")) return -1;
50
+ if (b.url.includes("api.onfinality.io")) return 1;
51
+ return 0;
52
+ });
53
+
54
+ // return copy
55
+ return chainWithCustomRpcs;
56
+ });
19
57
 
20
58
  //
21
- // GitHub Repo Constants
59
+ // Utils for parsing chaindata tokens.json
22
60
  //
23
61
 
24
- const githubApi = "https://api.github.com";
25
- const githubChaindataOrg = "TalismanSociety";
26
- const githubChaindataRepo = "chaindata";
27
- const githubChaindataBranch = CHAINDATA_BRANCH;
28
- const githubChaindataBaseUrl = `https://raw.githubusercontent.com/${githubChaindataOrg}/${githubChaindataRepo}/${githubChaindataBranch}`;
29
- const githubChainsUrl = `${githubChaindataBaseUrl}/data/chaindata.json`;
30
- const githubTestnetChainsUrl = `${githubChaindataBaseUrl}/data/testnets-chaindata.json`;
31
- const githubEvmNetworksUrl = `${githubChaindataBaseUrl}/data/evm-networks.json`;
32
- const githubChaindataChainsAssetsDir = "assets/chains";
33
- const githubChaindataTokensAssetsDir = "assets/tokens";
34
- const githubChainLogoUrl = chainId => `${githubChaindataBaseUrl}/${githubChaindataChainsAssetsDir}/${chainId}.svg`;
35
- const githubEvmNetworkLogoUrl = networkId => `${githubChaindataBaseUrl}/${githubChaindataChainsAssetsDir}/${networkId}.svg`;
36
- const githubTokenLogoUrl = tokenId => `${githubChaindataBaseUrl}/${githubChaindataTokensAssetsDir}/${tokenId}.svg`;
37
- const githubUnknownChainLogoUrl = githubChainLogoUrl("unknown");
38
- const githubUnknownTokenLogoUrl = githubTokenLogoUrl("unknown");
62
+ const parseTokensResponse = tokens => tokens.filter(isTokenPartial).filter(isToken);
63
+ const isTokenPartial = token => typeof token === "object" && token !== null;
64
+ const isToken = token => {
65
+ const id = token.id;
66
+ if (typeof id !== "string") return false;
67
+ const type = token.type;
68
+ if (typeof type !== "string") return false;
69
+ const isTestnet = token.isTestnet;
70
+ if (typeof isTestnet !== "boolean") return false;
71
+ const symbol = token.symbol;
72
+ if (typeof symbol !== "string") return false;
73
+ const decimals = token.decimals;
74
+ if (typeof decimals !== "number") return false;
75
+ const logo = token.logo;
76
+ if (typeof logo !== "string") return false;
77
+
78
+ // coingeckoId can be undefined
79
+ // const coingeckoId = token.coingeckoId
80
+ // if (typeof coingeckoId !== "string") return false
81
+
82
+ return true;
83
+ };
84
+
85
+ //
86
+ // map from Item[] to another type
87
+ //
88
+
89
+ const itemsToIds = items => items.map(({
90
+ id
91
+ }) => id);
92
+ const itemsToMapById = items => Object.fromEntries(items.map(item => [item.id, item]));
93
+ const itemsToMapByGenesisHash = items => Object.fromEntries(items.flatMap(item => item.genesisHash ? [[item.genesisHash, item]] : []));
94
+
95
+ //
96
+ // filters for Item[] where Item.isCustom == true
97
+ //
98
+
99
+ const customChainsFilter = chains => chains.filter(chain => "isCustom" in chain && chain.isCustom);
100
+ const customEvmNetworksFilter = evmNetworks => evmNetworks.filter(evmNetwork => "isCustom" in evmNetwork && evmNetwork.isCustom);
101
+ const customTokensFilter = tokens => tokens.filter(token => "isCustom" in token && token.isCustom);
102
+
103
+ //
104
+ // Utils to wrap Observable methods with one-shot Promise methods
105
+ //
106
+
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ const wrapObservableWithGetter = async (errorReason, observable) => {
109
+ return await withErrorReason(errorReason, () => firstValueFrom(observable));
110
+ };
111
+ const withErrorReason = async (reason, task) => {
112
+ try {
113
+ return await task();
114
+ } catch (cause) {
115
+ throw new Error(reason, {
116
+ cause
117
+ });
118
+ }
119
+ };
120
+
121
+ //
122
+ // Utils which aren't used by this package, but are helpful for other packages
123
+ //
39
124
 
40
125
  const isCustomChain = chain => {
41
126
  return "isCustom" in chain && chain.isCustom === true;
@@ -44,4 +129,730 @@ const isCustomEvmNetwork = evmNetwork => {
44
129
  return "isCustom" in evmNetwork && evmNetwork.isCustom === true;
45
130
  };
46
131
 
47
- export { chaindataChainByGenesisHashUrl, chaindataChainByIdUrl, chaindataChainsAllUrl, chaindataChainsSummaryUrl, chaindataEvmNetworkByIdUrl, chaindataEvmNetworksAllUrl, chaindataEvmNetworksSummaryUrl, chaindataMiniMetadatasAllUrl, chaindataTokenByIdUrl, chaindataTokensAllUrl, chaindataUrl, githubApi, githubChainLogoUrl, githubChaindataBaseUrl, githubChaindataBranch, githubChaindataChainsAssetsDir, githubChaindataOrg, githubChaindataRepo, githubChaindataTokensAssetsDir, githubChainsUrl, githubEvmNetworkLogoUrl, githubEvmNetworksUrl, githubTestnetChainsUrl, githubTokenLogoUrl, githubUnknownChainLogoUrl, githubUnknownTokenLogoUrl, isCustomChain, isCustomEvmNetwork };
132
+ var packageJson = {
133
+ name: "@talismn/chaindata-provider",
134
+ version: "0.8.2",
135
+ author: "Talisman",
136
+ homepage: "https://talisman.xyz",
137
+ license: "GPL-3.0-or-later",
138
+ publishConfig: {
139
+ access: "public"
140
+ },
141
+ repository: {
142
+ directory: "packages/chaindata-provider",
143
+ type: "git",
144
+ url: "https://github.com/talismansociety/talisman.git"
145
+ },
146
+ main: "dist/talismn-chaindata-provider.cjs.js",
147
+ module: "dist/talismn-chaindata-provider.esm.js",
148
+ files: [
149
+ "/dist",
150
+ "/init",
151
+ "/net"
152
+ ],
153
+ engines: {
154
+ node: ">=18"
155
+ },
156
+ scripts: {
157
+ test: "jest",
158
+ lint: "eslint src --max-warnings 0",
159
+ "chore:generate-init-data": "ts-node scripts/generateInitData.ts",
160
+ clean: "rm -rf dist init/*/dist net/dist .turbo node_modules"
161
+ },
162
+ dependencies: {
163
+ anylogger: "^1.0.11",
164
+ dexie: "^4.0.9",
165
+ rxjs: "^7.8.1"
166
+ },
167
+ devDependencies: {
168
+ "@talismn/eslint-config": "workspace:*",
169
+ "@talismn/tsconfig": "workspace:*",
170
+ "@types/jest": "^29.5.14",
171
+ eslint: "^8.57.1",
172
+ jest: "^29.7.0",
173
+ prettier: "^3.3.3",
174
+ "ts-jest": "^29.2.5",
175
+ "ts-node": "^10.9.2",
176
+ typescript: "^5.6.3"
177
+ },
178
+ preconstruct: {
179
+ entrypoints: [
180
+ "index.ts",
181
+ "init/chains.ts",
182
+ "init/evm-networks.ts",
183
+ "init/mini-metadatas.ts",
184
+ "init/tokens.ts",
185
+ "net.ts"
186
+ ]
187
+ },
188
+ eslintConfig: {
189
+ root: true,
190
+ "extends": [
191
+ "@talismn/eslint-config/base"
192
+ ]
193
+ }
194
+ };
195
+
196
+ var log = anylogger(packageJson.name);
197
+
198
+ const subNativeTokenId = chainId => `${chainId}-substrate-native`.toLowerCase().replace(/ /g, "-");
199
+ const evmNativeTokenId = chainId => `${chainId}-evm-native`.toLowerCase().replace(/ /g, "-");
200
+
201
+ // for DB version 2, Wallet version 1.21.0
202
+ const upgradeRemoveSymbolFromNativeTokenId = async tx => {
203
+ const tokensTable = tx.table("tokens");
204
+ const chainsTable = tx.table("chains");
205
+ const evmNetworksTable = tx.table("evmNetworks");
206
+ const nativeTokens = (await tokensTable.toArray()).filter(t => ["substrate-native", "evm-native"].includes(t.type));
207
+ const chains = await chainsTable.toArray();
208
+ const evmNetworks = await evmNetworksTable.toArray();
209
+ const tokenIdsToDelete = [];
210
+ const tokensToUpsert = [];
211
+ const chainsToUpsert = [];
212
+ const evmNetworksToUpsert = [];
213
+ for (const nativeToken of nativeTokens) {
214
+ const networkId = nativeToken.chain?.id || nativeToken.evmNetwork?.id;
215
+ if (!networkId) continue;
216
+ const id = nativeToken.type === "substrate-native" ? subNativeTokenId(networkId) : nativeToken.type === "evm-native" ? evmNativeTokenId(networkId) : undefined;
217
+ if (!id) continue;
218
+ const chain = chains.find(({
219
+ id
220
+ }) => id === networkId);
221
+ const evmNetwork = evmNetworks.find(({
222
+ id
223
+ }) => id === networkId);
224
+ tokenIdsToDelete.push(nativeToken.id);
225
+ tokensToUpsert.push({
226
+ ...nativeToken,
227
+ id
228
+ });
229
+ if (chain) chainsToUpsert.push({
230
+ ...chain,
231
+ nativeToken: {
232
+ id
233
+ }
234
+ });
235
+ if (evmNetwork) evmNetworksToUpsert.push({
236
+ ...evmNetwork,
237
+ nativeToken: {
238
+ id
239
+ }
240
+ });
241
+ }
242
+ await tokensTable.bulkPut(tokensToUpsert);
243
+ await chainsTable.bulkPut(chainsToUpsert);
244
+ await evmNetworksTable.bulkPut(evmNetworksToUpsert);
245
+ await tokensTable.bulkDelete(tokenIdsToDelete);
246
+ };
247
+
248
+ // for DB version 2, Wallet version 1.21.0
249
+ const upgradeAddIsDefaultToExistingChains = async tx => {
250
+ const chainsTable = tx.table("chains");
251
+ const evmNetworksTable = tx.table("evmNetworks");
252
+ const tokensTable = tx.table("tokens");
253
+ await chainsTable.toCollection().modify(chain => {
254
+ if ("isCustom" in chain && chain.isCustom) return;
255
+ chain.isDefault = true;
256
+ });
257
+ await evmNetworksTable.toCollection().modify(evmNetwork => {
258
+ if ("isCustom" in evmNetwork && evmNetwork.isCustom) return;
259
+ evmNetwork.isDefault = true;
260
+ });
261
+ await tokensTable.toCollection().modify(token => {
262
+ if ("isCustom" in token && token.isCustom) return;
263
+ token.isDefault = true;
264
+ });
265
+ };
266
+
267
+ class TalismanChaindataDatabase extends Dexie {
268
+ constructor() {
269
+ super("TalismanChaindata");
270
+
271
+ // https://dexie.org/docs/Tutorial/Design#database-versioning
272
+ this.version(2).stores({
273
+ // You only need to specify properties that you wish to index.
274
+ // The object store will allow any properties on your stored objects but you can only query them by indexed properties
275
+ // https://dexie.org/docs/API-Reference#declare-database
276
+ //
277
+ // Never index properties containing images, movies or large (huge) strings. Store them in IndexedDB, yes! but just don’t index them!
278
+ // https://dexie.org/docs/Version/Version.stores()#warning
279
+ chains: "id, genesisHash, name",
280
+ evmNetworks: "id, name",
281
+ tokens: "id, type, symbol, coingeckoId, dcentName, contractAddress"
282
+ }).upgrade(upgradeRemoveSymbolFromNativeTokenId).upgrade(upgradeAddIsDefaultToExistingChains);
283
+ }
284
+ }
285
+ new TalismanChaindataDatabase();
286
+
287
+ // removes the need to reference @talismn/balances in this package. should we ?
288
+ const getNativeTokenId = (chainId, moduleType) => `${chainId}-${moduleType}`.toLowerCase().replace(/ /g, "-");
289
+ const minimumHydrationInterval = 300_000; // 300_000ms = 300s = 5 minutes
290
+
291
+ class ChaindataProvider {
292
+ #db;
293
+ #onfinalityApiKey;
294
+ #liveQueries;
295
+ #lastHydratedChainsAt = 0;
296
+ #lastHydratedEvmNetworksAt = 0;
297
+ #lastHydratedTokensAt = 0;
298
+ constructor(options) {
299
+ this.#db = new TalismanChaindataDatabase();
300
+ this.#onfinalityApiKey = options?.onfinalityApiKey ?? undefined;
301
+ this.#liveQueries = [liveQuery(() => this.#db.chains.toArray()).subscribe(this.chainsObservable), liveQuery(() => this.#db.evmNetworks.toArray()).subscribe(this.evmNetworksObservable), liveQuery(() => this.#db.tokens.toArray()).subscribe(this.tokensObservable)];
302
+ }
303
+ destroy() {
304
+ this.#liveQueries.forEach(subscription => subscription.unsubscribe());
305
+ }
306
+ setOnfinalityApiKey(apiKey) {
307
+ this.#onfinalityApiKey = apiKey;
308
+ }
309
+
310
+ //
311
+ // base items
312
+ //
313
+
314
+ chainsObservable = new ReplaySubject(1);
315
+ async chains() {
316
+ return await wrapObservableWithGetter("Failed to get chains", this.chainsObservable);
317
+ }
318
+ evmNetworksObservable = new ReplaySubject(1);
319
+ async evmNetworks() {
320
+ return await wrapObservableWithGetter("Failed to get evmNetworks", this.evmNetworksObservable);
321
+ }
322
+ tokensObservable = new ReplaySubject(1);
323
+ async tokens() {
324
+ return await wrapObservableWithGetter("Failed to get tokens", this.tokensObservable);
325
+ }
326
+
327
+ //
328
+ // custom item observables
329
+ //
330
+
331
+ get customChainsObservable() {
332
+ return this.chainsObservable.pipe(map(customChainsFilter));
333
+ }
334
+ async customChains() {
335
+ return await wrapObservableWithGetter("Failed to get custom chains", this.customChainsObservable);
336
+ }
337
+ get customEvmNetworksObservable() {
338
+ return this.evmNetworksObservable.pipe(map(customEvmNetworksFilter));
339
+ }
340
+ async customEvmNetworks() {
341
+ return await wrapObservableWithGetter("Failed to get custom evmNetworks", this.customEvmNetworksObservable);
342
+ }
343
+ get customTokensObservable() {
344
+ return this.tokensObservable.pipe(map(customTokensFilter));
345
+ }
346
+ async customTokens() {
347
+ return await wrapObservableWithGetter("Failed to get custom tokens", this.customTokensObservable);
348
+ }
349
+
350
+ //
351
+ // item ids
352
+ //
353
+
354
+ get chainIdsObservable() {
355
+ return this.chainsObservable.pipe(map(itemsToIds));
356
+ }
357
+ async chainIds() {
358
+ return await wrapObservableWithGetter("Failed to get chainIds", this.chainIdsObservable);
359
+ }
360
+ get evmNetworkIdsObservable() {
361
+ return this.evmNetworksObservable.pipe(map(itemsToIds));
362
+ }
363
+ async evmNetworkIds() {
364
+ return await wrapObservableWithGetter("Failed to get evmNetworkIds", this.evmNetworkIdsObservable);
365
+ }
366
+ get tokenIdsObservable() {
367
+ return this.tokensObservable.pipe(map(itemsToIds));
368
+ }
369
+ async tokenIds() {
370
+ return await wrapObservableWithGetter("Failed to get tokenIds", this.tokenIdsObservable);
371
+ }
372
+
373
+ //
374
+ // items by id
375
+ //
376
+
377
+ get chainsByIdObservable() {
378
+ return this.chainsObservable.pipe(map(itemsToMapById));
379
+ }
380
+ async chainsById() {
381
+ return await wrapObservableWithGetter("Failed to get chains by id", this.chainsByIdObservable);
382
+ }
383
+ get evmNetworksByIdObservable() {
384
+ return this.evmNetworksObservable.pipe(map(itemsToMapById));
385
+ }
386
+ async evmNetworksById() {
387
+ return await wrapObservableWithGetter("Failed to get evmNetworks by id", this.evmNetworksByIdObservable);
388
+ }
389
+ get tokensByIdObservable() {
390
+ return this.tokensObservable.pipe(map(itemsToMapById));
391
+ }
392
+ async tokensById() {
393
+ return await wrapObservableWithGetter("Failed to get tokens by id", this.tokensByIdObservable);
394
+ }
395
+ async tokensByIdForType(type) {
396
+ const tokensByIdForTypeObservable = this.tokensObservable.pipe(map(tokens => tokens.filter(token => token.type === type))).pipe(map(itemsToMapById));
397
+ return await wrapObservableWithGetter("Failed to get tokenIds", tokensByIdForTypeObservable);
398
+ }
399
+
400
+ //
401
+ // items by genesisHash
402
+ //
403
+
404
+ get chainsByGenesisHashObservable() {
405
+ return this.chainsObservable.pipe(map(itemsToMapByGenesisHash));
406
+ }
407
+ async chainsByGenesisHash() {
408
+ return await wrapObservableWithGetter("Failed to get chains by genesisHash", this.chainsByGenesisHashObservable);
409
+ }
410
+
411
+ //
412
+ // filters for a single item
413
+ //
414
+
415
+ async chainById(chainId) {
416
+ return await withErrorReason("Failed to get chain by id", async () => (await this.chainsById())[chainId] ?? null);
417
+ }
418
+ async chainByGenesisHash(genesisHash) {
419
+ return await withErrorReason("Failed to get chain by genesisHash", async () => (await this.chainsByGenesisHash())[genesisHash] ?? null);
420
+ }
421
+ async evmNetworkById(evmNetworkId) {
422
+ return await withErrorReason("Failed to get evmNetwork by id", async () => (await this.evmNetworksById())[evmNetworkId] ?? null);
423
+ }
424
+ async tokenById(tokenId) {
425
+ return await withErrorReason("Failed to get token by id", async () => (await this.tokensById())[tokenId] ?? null);
426
+ }
427
+
428
+ //
429
+ // mutations / methods with side-effects
430
+ //
431
+
432
+ async addCustomChain(customChain) {
433
+ try {
434
+ if (!("isCustom" in customChain && customChain.isCustom)) return;
435
+ return await this.#db.chains.put(customChain);
436
+ } catch (cause) {
437
+ throw new Error("Failed to add custom chain", {
438
+ cause
439
+ });
440
+ }
441
+ }
442
+ async removeCustomChain(chainId) {
443
+ try {
444
+ return await this.#db.chains
445
+ // only affect custom chains
446
+ .filter(chain => "isCustom" in chain && chain.isCustom)
447
+ // only affect the provided chainId
448
+ .filter(chain => chain.id === chainId)
449
+ // delete the chain (if exists)
450
+ .delete();
451
+ } catch (cause) {
452
+ throw new Error("Failed to remove custom chain", {
453
+ cause
454
+ });
455
+ }
456
+ }
457
+ async setCustomChains(chains) {
458
+ return await this.#db.transaction("rw", this.#db.chains, async () => {
459
+ const keys = await this.#db.chains.filter(chain => "isCustom" in chain && chain.isCustom).primaryKeys();
460
+ await this.#db.chains.bulkDelete(keys);
461
+ await this.#db.chains.bulkPut(chains.filter(chain => chain.isCustom));
462
+ });
463
+ }
464
+ async resetChain(chainId) {
465
+ const builtInChain = await fetchChain(chainId);
466
+ if (!builtInChain) throw new Error("Cannot reset non-built-in chain");
467
+ if (!builtInChain.nativeToken?.id) throw new Error("Failed to lookup native token (no token exists for chain)");
468
+ const builtInNativeToken = await fetchSubstrateToken(builtInChain?.nativeToken?.id);
469
+ if (!isTokenPartial(builtInNativeToken)) throw new Error("Failed to lookup native token");
470
+ if (!isToken(builtInNativeToken)) throw new Error("Failed to lookup native token (isToken test failed)");
471
+ try {
472
+ return await this.#db.transaction("rw", this.#db.chains, this.#db.tokens, async () => {
473
+ // delete chain and its native tokens (ensures cleanup of tokens with legacy ids)
474
+ await this.#db.tokens.filter(token => token.type === "substrate-native" && token.chain?.id === chainId).delete();
475
+ await this.#db.chains.delete(chainId);
476
+
477
+ // reprovision them from subsquid data
478
+ await this.#db.chains.put(builtInChain);
479
+ await this.#db.tokens.put(builtInNativeToken);
480
+ });
481
+ } catch (cause) {
482
+ throw new Error("Failed to reset chain", {
483
+ cause
484
+ });
485
+ }
486
+ }
487
+ async addCustomEvmNetwork(customEvmNetwork) {
488
+ try {
489
+ if (!("isCustom" in customEvmNetwork && customEvmNetwork.isCustom)) return Promise.resolve();
490
+ return await this.#db.evmNetworks.put(customEvmNetwork);
491
+ } catch (cause) {
492
+ throw new Error("Failed to add custom evm network", {
493
+ cause
494
+ });
495
+ }
496
+ }
497
+ async removeCustomEvmNetwork(evmNetworkId) {
498
+ if (await this.getIsBuiltInEvmNetwork(evmNetworkId)) throw new Error("Cannot remove built-in EVM network");
499
+ try {
500
+ return this.#db.transaction("rw", [this.#db.evmNetworks, this.#db.tokens], async () => {
501
+ await this.#db.evmNetworks.delete(evmNetworkId);
502
+ await this.#db.tokens.filter(token => token.evmNetwork?.id === evmNetworkId).delete();
503
+ });
504
+ } catch (cause) {
505
+ throw new Error("Failed to remove custom evm network", {
506
+ cause
507
+ });
508
+ }
509
+ }
510
+ async setCustomEvmNetworks(networks) {
511
+ return await this.#db.transaction("rw", this.#db.evmNetworks, async () => {
512
+ const keys = await this.#db.evmNetworks.filter(network => "isCustom" in network && network.isCustom).primaryKeys();
513
+ await this.#db.evmNetworks.bulkDelete(keys);
514
+ await this.#db.evmNetworks.bulkPut(networks.filter(network => network.isCustom));
515
+ });
516
+ }
517
+ async resetEvmNetwork(evmNetworkId) {
518
+ const builtInEvmNetwork = await fetchEvmNetwork(evmNetworkId);
519
+ if (!builtInEvmNetwork) throw new Error("Cannot reset non-built-in EVM network");
520
+ const nativeModule = builtInEvmNetwork.balancesConfig.find(c => c.moduleType === "evm-native");
521
+ if (!nativeModule?.moduleConfig) throw new Error("Failed to lookup native token (no token exists for network)");
522
+ const {
523
+ symbol,
524
+ decimals,
525
+ coingeckoId,
526
+ logo,
527
+ mirrorOf,
528
+ dcentName,
529
+ noDiscovery
530
+ } = nativeModule.moduleConfig;
531
+ if (!symbol) throw new Error("Missing native token symbol");
532
+ if (!decimals) throw new Error("Missing native token decimals");
533
+ const builtInNativeToken = {
534
+ id: getNativeTokenId(evmNetworkId, "evm-native"),
535
+ type: "evm-native",
536
+ evmNetwork: {
537
+ id: evmNetworkId
538
+ },
539
+ isTestnet: builtInEvmNetwork.isTestnet ?? false,
540
+ isDefault: true,
541
+ symbol,
542
+ decimals,
543
+ coingeckoId,
544
+ logo
545
+ };
546
+ if (mirrorOf) builtInNativeToken.mirrorOf = mirrorOf;
547
+ if (dcentName) builtInNativeToken.dcentName = dcentName;
548
+ if (noDiscovery) builtInNativeToken.noDiscovery = noDiscovery;
549
+ builtInEvmNetwork.nativeToken = {
550
+ id: builtInNativeToken.id
551
+ };
552
+ try {
553
+ return await this.#db.transaction("rw", this.#db.evmNetworks, this.#db.tokens, async () => {
554
+ // delete chain and its native tokens (ensures cleanup of tokens with legacy ids)
555
+ await this.#db.tokens.filter(token => token.type === "evm-native" && token.evmNetwork?.id === evmNetworkId).delete();
556
+ const networkToDelete = await this.#db.evmNetworks.get(evmNetworkId);
557
+ if (networkToDelete?.nativeToken?.id) await this.#db.tokens.delete(networkToDelete.nativeToken.id);
558
+ await this.#db.evmNetworks.delete(evmNetworkId);
559
+
560
+ // reprovision them from chaindata
561
+ await this.#db.evmNetworks.put(builtInEvmNetwork);
562
+ await this.#db.tokens.put(builtInNativeToken);
563
+ });
564
+ } catch (cause) {
565
+ throw new Error("Failed to reset evm network", {
566
+ cause
567
+ });
568
+ }
569
+ }
570
+ async addCustomToken(customToken) {
571
+ try {
572
+ if (!("isCustom" in customToken && customToken.isCustom)) return Promise.resolve();
573
+ return await this.#db.tokens.put(customToken);
574
+ } catch (cause) {
575
+ throw new Error("Failed to add custom token", {
576
+ cause
577
+ });
578
+ }
579
+ }
580
+ async removeCustomToken(tokenId) {
581
+ try {
582
+ return await this.#db.tokens
583
+ // only affect custom tokens
584
+ .filter(token => "isCustom" in token && Boolean(token.isCustom))
585
+ // only affect the provided token
586
+ .filter(token => token.id === tokenId)
587
+ // delete the token (if exists)
588
+ .delete();
589
+ } catch (cause) {
590
+ throw new Error("Failed to remove custom token", {
591
+ cause
592
+ });
593
+ }
594
+ }
595
+ async setCustomTokens(tokens) {
596
+ // TODO custom tokens have to go into localstorage
597
+ return await this.#db.transaction("rw", this.#db.tokens, async () => {
598
+ const keys = await this.#db.tokens.filter(token => "isCustom" in token && Boolean(token.isCustom)).primaryKeys();
599
+ await this.#db.tokens.bulkDelete(keys);
600
+ await this.#db.tokens.bulkPut(tokens.filter(token => "isCustom" in token && token.isCustom));
601
+ });
602
+ }
603
+ async removeToken(tokenId) {
604
+ try {
605
+ return await this.#db.tokens.delete(tokenId);
606
+ } catch (cause) {
607
+ throw new Error("Failed to remove token", {
608
+ cause
609
+ });
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Hydrate the db with the latest chaindata from subsquid.
615
+ *
616
+ * @returns A promise which resolves to true if any db table has been hydrated, or false if all hydration has been skipped.
617
+ */
618
+ async hydrate({
619
+ // chainsArgs, // hydrateChains has no args
620
+ // evmNetworksArgs, // hydrateEvmNetworks has no args
621
+ tokensArgs
622
+ } = {}) {
623
+ return (await Promise.all([
624
+ // call inner hydration methods
625
+ this.hydrateChains(), this.hydrateEvmNetworks(), this.hydrateSubstrateTokens(...(tokensArgs ? tokensArgs : []))])
626
+
627
+ // return true if any hydration occurred
628
+ ).some(Boolean);
629
+ }
630
+
631
+ /**
632
+ * Hydrate the db with the latest chains from subsquid.
633
+ * Hydration is skipped when the last successful hydration was less than minimumHydrationInterval ms ago.
634
+ *
635
+ * @returns A promise which resolves to true if the db has been hydrated, or false if the hydration was skipped.
636
+ */
637
+ async hydrateChains() {
638
+ const now = Date.now();
639
+ if (now - this.#lastHydratedChainsAt < minimumHydrationInterval) return false;
640
+ const dbHasChains = (await this.#db.chains.count()) > 0;
641
+ try {
642
+ try {
643
+ var chains = addCustomChainRpcs(await fetchChains(), this.#onfinalityApiKey); // eslint-disable-line no-var
644
+ if (chains.length <= 0) throw new Error("Ignoring empty chaindata chains response");
645
+ } catch (error) {
646
+ if (dbHasChains) throw error;
647
+
648
+ // On first start-up (db is empty), if we fail to fetch chains then we should
649
+ // initialize the DB with the list of chains inside our init/chains.json file.
650
+ // This data will represent a relatively recent copy of what's in the squid,
651
+ // which will be better for our users than to have nothing at all.
652
+ var chains = addCustomChainRpcs(await fetchInitChains(), this.#onfinalityApiKey); // eslint-disable-line no-var
653
+ }
654
+
655
+ // TODO check if alec is this the right way to set native token
656
+ // note : many chains don't have a native module provisionned from chaindata => breaks edit network screen and probably send funds and tx screens
657
+ for (const chain of chains) {
658
+ const nativeTokenModule = chain.balancesConfig.find(c => c.moduleType === "substrate-native");
659
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
660
+ const symbol = nativeTokenModule?.moduleConfig?.symbol;
661
+ if (!symbol) continue;
662
+ chain.nativeToken = {
663
+ id: getNativeTokenId(chain.id, "substrate-native")
664
+ };
665
+ }
666
+ await this.#db.transaction("rw", this.#db.chains, async () => {
667
+ await this.#db.chains.filter(chain => !("isCustom" in chain && chain.isCustom)).delete();
668
+
669
+ // add all except ones matching custom existing ones (user may customize built-in chains)
670
+ const customChainIds = (await this.#db.chains.toArray()).map(chain => chain.id);
671
+ const newChains = chains.filter(chain => !customChainIds.includes(chain.id));
672
+ await this.#db.chains.bulkPut(newChains);
673
+ });
674
+ this.#lastHydratedChainsAt = now;
675
+ return true;
676
+ } catch (error) {
677
+ log.warn(`Failed to hydrate chains from chaindata`, error);
678
+ return false;
679
+ }
680
+ }
681
+
682
+ /**
683
+ * Hydrate the db with the latest evmNetworks from subsquid.
684
+ * Hydration is skipped when the last successful hydration was less than minimumHydrationInterval ms ago.
685
+ *
686
+ * @returns A promise which resolves to true if the db has been hydrated, or false if the hydration was skipped.
687
+ */
688
+ async hydrateEvmNetworks() {
689
+ const now = Date.now();
690
+ if (now - this.#lastHydratedEvmNetworksAt < minimumHydrationInterval) return false;
691
+ const dbHasEvmNetworks = (await this.#db.evmNetworks.count()) > 0;
692
+ try {
693
+ try {
694
+ var evmNetworks = await fetchEvmNetworks(); // eslint-disable-line no-var
695
+ if (evmNetworks.length <= 0) throw new Error("Ignoring empty chaindata evmNetworks response");
696
+ } catch (error) {
697
+ if (dbHasEvmNetworks) throw error;
698
+
699
+ // On first start-up (db is empty), if we fail to fetch evmNetworks then we should
700
+ // initialize the DB with the list of evmNetworks inside our init/evm-networks.json file.
701
+ // This data will represent a relatively recent copy of what's in the squid,
702
+ // which will be better for our users than to have nothing at all.
703
+ var evmNetworks = await fetchInitEvmNetworks(); // eslint-disable-line no-var
704
+ }
705
+
706
+ // set native token
707
+ for (const evmNetwork of evmNetworks) {
708
+ evmNetwork.nativeToken = {
709
+ id: getNativeTokenId(evmNetwork.id, "evm-native")
710
+ };
711
+ }
712
+ await this.#db.transaction("rw", this.#db.evmNetworks, async () => {
713
+ await this.#db.evmNetworks.filter(network => !("isCustom" in network && network.isCustom)).delete();
714
+
715
+ // remaining entries are custom networks
716
+ const existingCustomNetworks = await this.#db.evmNetworks.toArray();
717
+ const existingCustomNetworksById = Object.fromEntries(existingCustomNetworks.map(network => [network.id, network]));
718
+
719
+ // dont override custom networks, except for the balancesConfig property
720
+ const newNetworks = evmNetworks.map(network => {
721
+ const existing = existingCustomNetworksById[network.id];
722
+ return existing ? Object.assign({}, existing, {
723
+ balancesConfig: network.balancesConfig
724
+ }) : network;
725
+ });
726
+ await this.#db.evmNetworks.bulkPut(newNetworks);
727
+ });
728
+ this.#lastHydratedEvmNetworksAt = now;
729
+ return true;
730
+ } catch (error) {
731
+ log.warn(`Failed to hydrate evmNetworks from chaindata`, error);
732
+ return false;
733
+ }
734
+ }
735
+ async updateChainTokens(chainId, source, newTokens, availableTokenLogoFilenames) {
736
+ // TODO: Test logos and fall back to unknown token logo url
737
+ // (Maybe put the test into each balance module itself)
738
+
739
+ const existingChainTokens = await this.#db.tokens.filter(token => token.chain?.id === chainId && token.type === source).toArray();
740
+ newTokens.forEach(token => {
741
+ if (token.logo) return;
742
+ const symbolLogo = token.symbol.toLowerCase().replace(/ /g, "_");
743
+ if (availableTokenLogoFilenames.includes(`${symbolLogo}.svg`)) {
744
+ return token.logo = githubTokenLogoUrl(symbolLogo);
745
+ }
746
+
747
+ // TODO: Use coingeckoId logo if exists
748
+
749
+ return token.logo = githubUnknownTokenLogoUrl;
750
+ });
751
+ const notCustomTokenIds = existingChainTokens.filter(token => !("isCustom" in token && token.isCustom)).map(token => token.id);
752
+ const customTokenIds = existingChainTokens.filter(token => "isCustom" in token && token.isCustom).map(token => token.id);
753
+ await this.#db.transaction("rw", this.#db.tokens, this.#db.chains, async () => {
754
+ await this.#db.tokens.bulkDelete(notCustomTokenIds);
755
+ await this.#db.tokens.bulkPut(newTokens.filter(token => !customTokenIds.includes(token.id)));
756
+ //if (chain && shouldUpdateChain) await this.#db.chains.put(chain)
757
+ });
758
+ }
759
+ async updateEvmNetworkTokens(newTokens) {
760
+ const existingEvmNetworkTokens = await this.#db.tokens.filter(t => t.type.startsWith("evm-")).toArray();
761
+ const isCustomToken = token => "isCustom" in token && token.isCustom;
762
+
763
+ // don't override custom tokens
764
+ const customTokenIds = new Set();
765
+
766
+ // delete non-custom tokens which aren't in `newTokens`
767
+ const deleteTokenIds = new Set();
768
+ for (const token of existingEvmNetworkTokens) {
769
+ if (isCustomToken(token)) customTokenIds.add(token.id);else deleteTokenIds.add(token.id);
770
+ }
771
+ const tokensToUpdate = newTokens.filter(token => {
772
+ deleteTokenIds.delete(token.id);
773
+ return !customTokenIds.has(token.id);
774
+ });
775
+ this.#db.transaction("rw", this.#db.tokens, async () => {
776
+ // delete all existing non custom tokens
777
+ await this.#db.tokens.bulkDelete([...deleteTokenIds]);
778
+
779
+ // force update on all non custom tokens
780
+ await this.#db.tokens.bulkPut(tokensToUpdate);
781
+ });
782
+ }
783
+
784
+ /**
785
+ * Hydrate the db with the latest tokens from subsquid.
786
+ * Hydration is skipped when the last successful hydration was less than minimumHydrationInterval ms ago.
787
+ *
788
+ * @returns A promise which resolves to true if the db has been hydrated, or false if the hydration was skipped.
789
+ */
790
+ async hydrateSubstrateTokens(chainIdFilter) {
791
+ const now = Date.now();
792
+ if (now - this.#lastHydratedTokensAt < minimumHydrationInterval) return false;
793
+ const dbHasTokens = (await this.#db.tokens.count()) > 0;
794
+ try {
795
+ try {
796
+ var tokens = parseTokensResponse(await fetchSubstrateTokens()); // eslint-disable-line no-var
797
+ if (tokens.length <= 0) throw new Error("Ignoring empty chaindata tokens response");
798
+ } catch (error) {
799
+ if (dbHasTokens) throw error;
800
+
801
+ // On first start-up (db is empty), if we fail to fetch tokens then we should
802
+ // initialize the DB with the list of tokens inside our init/tokens.json file.
803
+ // This data will represent a relatively recent copy of what's in the squid,
804
+ // which will be better for our users than to have nothing at all.
805
+ var tokens = parseTokensResponse(await fetchInitSubstrateTokens()); // eslint-disable-line no-var
806
+ }
807
+ await this.#db.transaction("rw", this.#db.tokens, async () => {
808
+ const deleteChains = chainIdFilter ? new Set(chainIdFilter) : undefined;
809
+ const tokensToDelete = (await this.#db.tokens.toArray()).filter(token => {
810
+ // don't delete custom tokens
811
+ if ("isCustom" in token && token.isCustom) return false;
812
+
813
+ // delete all other tokens if chainIdFilter is not specified
814
+ if (deleteChains === undefined) return true;
815
+
816
+ // delete tokens on chainIdFilter chains is it is specified
817
+ if (token.chain?.id && deleteChains.has(token.chain.id)) return true;
818
+ return false;
819
+ }).map(token => token.id);
820
+ if (tokensToDelete.length) await this.#db.tokens.bulkDelete(tokensToDelete);
821
+
822
+ // add all except ones matching custom existing ones (user may customize built-in tokens)
823
+ const customTokenIds = (await this.#db.tokens.toArray()).map(token => token.id);
824
+ const newTokens = tokens.filter(token => {
825
+ // don't replace custom tokens
826
+ if (customTokenIds.includes(token.id)) return false;
827
+ if (deleteChains === undefined) return true;
828
+ if (!token.chain?.id) return true;
829
+ if (deleteChains.has(token.chain.id)) return true;
830
+ return false;
831
+ });
832
+ await this.#db.tokens.bulkPut(newTokens);
833
+ });
834
+ this.#lastHydratedTokensAt = now;
835
+ return true;
836
+ } catch (error) {
837
+ log.warn(`Failed to hydrate tokens from chaindata`, error);
838
+ return false;
839
+ }
840
+ }
841
+ async getIsBuiltInChain(chainId) {
842
+ const chain = await fetchChain(chainId);
843
+ return !!chain;
844
+ }
845
+ async getIsBuiltInEvmNetwork(evmNetworkId) {
846
+ try {
847
+ const evmNetwork = await fetchEvmNetwork(evmNetworkId);
848
+ return !!evmNetwork;
849
+ } catch (e) {
850
+ return false;
851
+ }
852
+ }
853
+ async transaction(mode, tables, scope) {
854
+ return await this.#db.transaction(mode, tables, scope);
855
+ }
856
+ }
857
+
858
+ export { ChaindataProvider, addCustomChainRpcs, customChainsFilter, customEvmNetworksFilter, customTokensFilter, fetchChain, fetchChains, fetchEvmNetwork, fetchEvmNetworks, fetchInitChains, fetchInitEvmNetworks, fetchInitMiniMetadatas, fetchInitSubstrateTokens, fetchSubstrateToken, fetchSubstrateTokens, githubTokenLogoUrl, githubUnknownTokenLogoUrl, isCustomChain, isCustomEvmNetwork, isToken, isTokenPartial, itemsToIds, itemsToMapByGenesisHash, itemsToMapById, parseTokensResponse, withErrorReason, wrapObservableWithGetter };