@talismn/balances 0.0.0-pr672-20230404063840 → 0.0.0-pr677-20230413171850

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
1
  # @talismn/balances
2
2
 
3
- ## 0.0.0-pr672-20230404063840
3
+ ## 0.0.0-pr677-20230413171850
4
+
5
+ ### Patch Changes
6
+
7
+ - fb8ee962: feat: proxy dapp websocket requests to talisman wallet backend when available
8
+ - 306e7160: feat: crowdloan and nom pool balances
9
+ - Updated dependencies [fb8ee962]
10
+ - Updated dependencies [c898da98]
11
+ - Updated dependencies [306e7160]
12
+ - @talismn/chain-connector@0.0.0-pr677-20230413171850
13
+ - @talismn/chain-connector-evm@0.0.0-pr677-20230413171850
14
+ - @talismn/util@0.0.0-pr677-20230413171850
15
+ - @talismn/chaindata-provider@0.0.0-pr677-20230413171850
16
+ - @talismn/token-rates@0.0.0-pr677-20230413171850
17
+
18
+ ## 0.4.0
4
19
 
5
20
  ### Patch Changes
6
21
 
@@ -11,11 +26,11 @@
11
26
  - Updated dependencies [6643a4e4]
12
27
  - Updated dependencies [79f6ccf6]
13
28
  - Updated dependencies [c24dc1fb]
14
- - @talismn/chain-connector@0.0.0-pr672-20230404063840
15
- - @talismn/util@0.0.0-pr672-20230404063840
16
- - @talismn/token-rates@0.0.0-pr672-20230404063840
17
- - @talismn/chaindata-provider@0.0.0-pr672-20230404063840
18
- - @talismn/chain-connector-evm@0.0.0-pr672-20230404063840
29
+ - @talismn/chain-connector@0.4.3
30
+ - @talismn/util@0.1.8
31
+ - @talismn/token-rates@0.1.15
32
+ - @talismn/chaindata-provider@0.4.3
33
+ - @talismn/chain-connector-evm@0.4.3
19
34
 
20
35
  ## 0.3.3
21
36
 
@@ -1,17 +1,26 @@
1
1
  import type { Registry } from "@polkadot/types-codec/types";
2
+ import { ChainConnector } from "@talismn/chain-connector";
2
3
  import { ChainId } from "@talismn/chaindata-provider";
3
4
  import { BalanceModule, DefaultChainMeta, DefaultModuleConfig, DefaultTransferParams, ExtendableChainMeta, ExtendableModuleConfig, ExtendableTokenType, ExtendableTransferParams } from "./BalanceModule";
4
- import { AddressesByToken, Balance, Balances, SubscriptionCallback, UnsubscribeFn } from "./types";
5
+ import { AddressesByToken, Balance, BalanceJson, Balances, SubscriptionCallback, UnsubscribeFn } from "./types";
5
6
  /**
6
7
  * Wraps a BalanceModule's fetch/subscribe methods with a single `balances` method.
7
8
  * This `balances` method will subscribe if a callback parameter is provided, or otherwise fetch.
8
9
  */
9
10
  export declare function balances<TModuleType extends string, TTokenType extends ExtendableTokenType, TChainMeta extends ExtendableChainMeta = DefaultChainMeta, TModuleConfig extends ExtendableModuleConfig = DefaultModuleConfig, TTransferParams extends ExtendableTransferParams = DefaultTransferParams>(balanceModule: BalanceModule<TModuleType, TTokenType, TChainMeta, TModuleConfig, TTransferParams>, addressesByToken: AddressesByToken<TTokenType>): Promise<Balances>;
10
11
  export declare function balances<TModuleType extends string, TTokenType extends ExtendableTokenType, TChainMeta extends ExtendableChainMeta = DefaultChainMeta, TModuleConfig extends ExtendableModuleConfig = DefaultModuleConfig, TTransferParams extends ExtendableTransferParams = DefaultTransferParams>(balanceModule: BalanceModule<TModuleType, TTokenType, TChainMeta, TModuleConfig, TTransferParams>, addressesByToken: AddressesByToken<TTokenType>, callback: SubscriptionCallback<Balances>): Promise<UnsubscribeFn>;
12
+ export type GetOrCreateTypeRegistry = (chainId: ChainId, metadataRpc?: `0x${string}`) => Registry;
11
13
  export declare const createTypeRegistryCache: () => {
12
- getOrCreateTypeRegistry: (chainId: ChainId, metadataRpc?: `0x${string}`) => Registry;
14
+ getOrCreateTypeRegistry: GetOrCreateTypeRegistry;
13
15
  };
14
16
  export declare const filterMirrorTokens: (balance: Balance, i: number, balances: Balance[]) => boolean;
17
+ export declare const getValidSubscriptionIds: () => Set<string>;
18
+ export declare const createSubscriptionId: () => string;
19
+ export declare const deleteSubscriptionId: () => void;
20
+ /**
21
+ * Sets all balance statuses from `live-${string}` to either `live` or `cached`
22
+ */
23
+ export declare const deriveStatuses: (validSubscriptionIds: string[], balances: BalanceJson[]) => BalanceJson[];
15
24
  /**
16
25
  * Used by a variety of balance modules to help encode and decode substrate state calls.
17
26
  */
@@ -26,3 +35,20 @@ export declare class StorageHelper {
26
35
  tag(tags: any): this;
27
36
  decode(input?: string | null): import("@polkadot/types-codec/types").Codec | undefined;
28
37
  }
38
+ /**
39
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
40
+ */
41
+ export type RpcStateQuery<T> = {
42
+ chainId: string;
43
+ stateKey: string;
44
+ decodeResult: (change: string | null) => T;
45
+ };
46
+ /**
47
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
48
+ */
49
+ export declare class RpcStateQueryHelper<T> {
50
+ #private;
51
+ constructor(chainConnector: ChainConnector, queries: Array<RpcStateQuery<T>>);
52
+ subscribe(callback: SubscriptionCallback<T[]>, timeout?: number | false, subscribeMethod?: string, responseMethod?: string, unsubscribeMethod?: string): Promise<UnsubscribeFn>;
53
+ fetch(method?: string): Promise<T[]>;
54
+ }
@@ -134,8 +134,20 @@ export declare class Balance {
134
134
  get free(): BalanceFormatter;
135
135
  /** The reserved balance of this token. Is included in the total. */
136
136
  get reserved(): BalanceFormatter;
137
+ get reserves(): {
138
+ amount: BalanceFormatter;
139
+ label: string;
140
+ meta?: unknown;
141
+ }[];
137
142
  /** The frozen balance of this token. Is included in the free amount. */
138
143
  get locked(): BalanceFormatter;
144
+ get locks(): {
145
+ amount: BalanceFormatter;
146
+ label: string;
147
+ meta?: unknown;
148
+ includeInTransferable?: boolean | undefined;
149
+ excludeFromFeePayable?: boolean | undefined;
150
+ }[];
139
151
  /** @depreacted - use balance.locked */
140
152
  get frozen(): BalanceFormatter;
141
153
  /** The transferable balance of this token. Is generally the free amount - the miscFrozen amount. */
@@ -23,11 +23,18 @@ export type BalanceTypes = {
23
23
  export type BalanceJson = BalanceTypes[keyof BalanceTypes] extends never ? IBalance : BalanceTypes[keyof BalanceTypes];
24
24
  /** A collection of `BalanceJson` objects */
25
25
  export type BalanceJsonList = Record<string, BalanceJson>;
26
- export type BalanceStatus = "live" | "cache" | "stale";
26
+ export type BalanceStatusLive = `live-${string}`;
27
+ export declare const BalanceStatusLive: (subscriptionId: string) => BalanceStatusLive;
28
+ export type BalanceStatus = BalanceStatusLive | "live" | "cache" | "stale";
27
29
  /** `IBalance` is a common interface which all balance types must implement. */
28
30
  export type IBalance = {
29
31
  /** The module that this balance was retrieved by */
30
32
  source: string;
33
+ /**
34
+ * For modules which fetch balances via module sources, this is the sub-source
35
+ * e.g. `staking` or `crowdloans`
36
+ **/
37
+ subSource?: string;
31
38
  /** Has this balance never been fetched, or is it from a cache, or is it up to date? */
32
39
  status: BalanceStatus;
33
40
  /** The address of the account which owns this balance */
@@ -57,6 +64,7 @@ export type Amount = string;
57
64
  export type AmountWithLabel<TLabel extends string> = {
58
65
  label: TLabel;
59
66
  amount: Amount;
67
+ meta?: unknown;
60
68
  };
61
69
  /** A labelled locked amount of a balance */
62
70
  export type LockedAmount<TLabel extends string> = AmountWithLabel<TLabel> & {
@@ -4,11 +4,13 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var dexie = require('dexie');
6
6
  var types = require('@polkadot/types');
7
- var anylogger = require('anylogger');
8
7
  var util = require('@talismn/util');
8
+ var groupBy = require('lodash/groupBy');
9
+ var anylogger = require('anylogger');
9
10
 
10
11
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
11
12
 
13
+ var groupBy__default = /*#__PURE__*/_interopDefault(groupBy);
12
14
  var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
13
15
 
14
16
  // TODO: Document default balances module purpose/usage
@@ -67,7 +69,7 @@ const db = new TalismanBalancesDatabase();
67
69
 
68
70
  var packageJson = {
69
71
  name: "@talismn/balances",
70
- version: "0.0.0-pr672-20230404063840",
72
+ version: "0.0.0-pr677-20230413171850",
71
73
  author: "Talisman",
72
74
  homepage: "https://talisman.xyz",
73
75
  license: "UNLICENSED",
@@ -100,20 +102,22 @@ var packageJson = {
100
102
  "@talismn/token-rates": "workspace:^",
101
103
  "@talismn/util": "workspace:^",
102
104
  anylogger: "^1.0.11",
103
- dexie: "^3.2.3"
105
+ dexie: "^3.2.3",
106
+ lodash: "4.17.21"
104
107
  },
105
108
  devDependencies: {
106
- "@polkadot/types": "^9.10.5",
109
+ "@polkadot/types": "^10.1.4",
107
110
  "@talismn/eslint-config": "workspace:^",
108
111
  "@talismn/tsconfig": "workspace:^",
109
112
  "@types/jest": "^27.5.1",
113
+ "@types/lodash": "^4.14.180",
110
114
  eslint: "^8.4.0",
111
115
  jest: "^28.1.0",
112
116
  "ts-jest": "^28.0.2",
113
117
  typescript: "^4.6.4"
114
118
  },
115
119
  peerDependencies: {
116
- "@polkadot/types": "9.x"
120
+ "@polkadot/types": "10.x"
117
121
  },
118
122
  preconstruct: {
119
123
  entrypoints: [
@@ -146,8 +150,14 @@ const createTypeRegistryCache = () => {
146
150
  if (cached) return cached;
147
151
  const typeRegistry = new types.TypeRegistry();
148
152
  if (typeof metadataRpc === "string") {
149
- const metadata = new types.Metadata(typeRegistry, metadataRpc);
150
- metadata.registry.setMetadata(metadata);
153
+ try {
154
+ const metadata = new types.Metadata(typeRegistry, metadataRpc);
155
+ metadata.registry.setMetadata(metadata);
156
+ } catch (cause) {
157
+ log.warn(new Error(`Failed to set metadata for chain ${chainId}`, {
158
+ cause
159
+ }));
160
+ }
151
161
  }
152
162
  typeRegistryCache.set(chainId, typeRegistry);
153
163
  return typeRegistry;
@@ -161,6 +171,49 @@ const filterMirrorTokens = (balance, i, balances) => {
161
171
  const mirrorOf = balance.token?.mirrorOf;
162
172
  return !mirrorOf || !balances.find(b => b.tokenId === mirrorOf);
163
173
  };
174
+ const getValidSubscriptionIds = () => {
175
+ return new Set(localStorage.getItem("TalismanBalancesSubscriptionIds")?.split(",") ?? []);
176
+ };
177
+ const createSubscriptionId = () => {
178
+ // delete current id (if exists)
179
+ deleteSubscriptionId();
180
+
181
+ // create new id
182
+ const subscriptionId = Date.now().toString();
183
+ sessionStorage.setItem("TalismanBalancesSubscriptionId", subscriptionId);
184
+
185
+ // add to list of current ids
186
+ const subscriptionIds = getValidSubscriptionIds();
187
+ subscriptionIds.add(subscriptionId);
188
+ localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
189
+ return subscriptionId;
190
+ };
191
+ const deleteSubscriptionId = () => {
192
+ const subscriptionId = sessionStorage.getItem("TalismanBalancesSubscriptionId");
193
+ if (!subscriptionId) return;
194
+ const subscriptionIds = getValidSubscriptionIds();
195
+ subscriptionIds.delete(subscriptionId);
196
+ localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
197
+ };
198
+
199
+ /**
200
+ * Sets all balance statuses from `live-${string}` to either `live` or `cached`
201
+ */
202
+ const deriveStatuses = (validSubscriptionIds, balances) => balances.map(balance => {
203
+ if (balance.status === "live" || balance.status === "cache" || balance.status === "stale") return balance;
204
+ if (validSubscriptionIds.length < 1) return {
205
+ ...balance,
206
+ status: "cache"
207
+ };
208
+ if (!validSubscriptionIds.includes(balance.status.slice("live-".length))) return {
209
+ ...balance,
210
+ status: "cache"
211
+ };
212
+ return {
213
+ ...balance,
214
+ status: "live"
215
+ };
216
+ });
164
217
 
165
218
  /**
166
219
  * Used by a variety of balance modules to help encode and decode substrate state calls.
@@ -243,6 +296,76 @@ class StorageHelper {
243
296
  }
244
297
  }
245
298
 
299
+ /**
300
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
301
+ */
302
+
303
+ /**
304
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
305
+ */
306
+ class RpcStateQueryHelper {
307
+ #chainConnector;
308
+ #queries;
309
+ constructor(chainConnector, queries) {
310
+ this.#chainConnector = chainConnector;
311
+ this.#queries = queries;
312
+ }
313
+ async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
314
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
315
+ const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
316
+ const params = [queries.map(({
317
+ stateKey
318
+ }) => stateKey)];
319
+ const unsubscribe = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
320
+ error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
321
+ }, timeout);
322
+ return () => unsubscribe(unsubscribeMethod);
323
+ }).map(subscription => subscription.catch(error => {
324
+ log.warn(`Failed to create subscription: ${error.message}`);
325
+ return () => {};
326
+ }));
327
+ return () => subscriptions.forEach(subscription => subscription.then(unsubscribe => unsubscribe()));
328
+ }
329
+ async fetch(method = "state_queryStorageAt") {
330
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
331
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
332
+ const params = [queries.map(({
333
+ stateKey
334
+ }) => stateKey)];
335
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
336
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
337
+ }));
338
+ return resultsByChain.flatMap(result => result);
339
+ }
340
+ #distributeChangesToQueryDecoders(chainId, result) {
341
+ if (typeof result !== "object" || result === null) return [];
342
+ if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
343
+ if (!Array.isArray(result.changes)) return [];
344
+ return result.changes.flatMap(([reference, change]) => {
345
+ if (typeof reference !== "string") {
346
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
347
+ return [];
348
+ }
349
+ if (typeof change !== "string" && change !== null) {
350
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
351
+ return [];
352
+ }
353
+ const query = this.#queries.find(({
354
+ chainId: cId,
355
+ stateKey
356
+ }) => cId === chainId && stateKey === reference);
357
+ if (!query) {
358
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
359
+ stateKey
360
+ }) => stateKey)}`);
361
+ return [];
362
+ }
363
+ return query.decodeResult(change);
364
+ });
365
+ }
366
+ }
367
+
368
+ const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
246
369
  function excludeFromTransferableAmount(locks) {
247
370
  if (typeof locks === "string") return BigInt(locks);
248
371
  if (!Array.isArray(locks)) locks = [locks];
@@ -492,13 +615,14 @@ class Balance {
492
615
  get id() {
493
616
  const {
494
617
  source,
618
+ subSource,
495
619
  address,
496
620
  chainId,
497
621
  evmNetworkId,
498
622
  tokenId
499
623
  } = this.#storage;
500
624
  const locationId = chainId !== undefined ? chainId : evmNetworkId;
501
- return `${source}-${address}-${locationId}-${tokenId}`;
625
+ return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
502
626
  }
503
627
  get source() {
504
628
  return this.#storage.source;
@@ -553,10 +677,36 @@ class Balance {
553
677
  get reserved() {
554
678
  return this.#format(typeof this.#storage.reserves === "string" ? BigInt(this.#storage.reserves) : Array.isArray(this.#storage.reserves) ? this.#storage.reserves.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, BigInt("0")) : BigInt(this.#storage.reserves?.amount || "0"));
555
679
  }
680
+ get reserves() {
681
+ return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
682
+ if (reserve === undefined) return [];
683
+ if (typeof reserve === "string") return {
684
+ label: "other",
685
+ amount: this.#format(reserve)
686
+ };
687
+ return {
688
+ ...reserve,
689
+ amount: this.#format(reserve.amount)
690
+ };
691
+ });
692
+ }
556
693
  /** The frozen balance of this token. Is included in the free amount. */
557
694
  get locked() {
558
695
  return this.#format(typeof this.#storage.locks === "string" ? BigInt(this.#storage.locks) : Array.isArray(this.#storage.locks) ? this.#storage.locks.map(lock => BigInt(lock.amount)).reduce((a, b) => util.BigMath.max(a, b), BigInt("0")) : BigInt(this.#storage.locks?.amount || "0"));
559
696
  }
697
+ get locks() {
698
+ return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
699
+ if (lock === undefined) return [];
700
+ if (typeof lock === "string") return {
701
+ label: "other",
702
+ amount: this.#format(lock)
703
+ };
704
+ return {
705
+ ...lock,
706
+ amount: this.#format(lock.amount)
707
+ };
708
+ });
709
+ }
560
710
  /** @depreacted - use balance.locked */
561
711
  get frozen() {
562
712
  return this.locked;
@@ -672,16 +822,22 @@ class SumBalancesFormatter {
672
822
 
673
823
  exports.Balance = Balance;
674
824
  exports.BalanceFormatter = BalanceFormatter;
825
+ exports.BalanceStatusLive = BalanceStatusLive;
675
826
  exports.Balances = Balances;
676
827
  exports.DefaultBalanceModule = DefaultBalanceModule;
677
828
  exports.FiatSumBalancesFormatter = FiatSumBalancesFormatter;
829
+ exports.RpcStateQueryHelper = RpcStateQueryHelper;
678
830
  exports.StorageHelper = StorageHelper;
679
831
  exports.SumBalancesFormatter = SumBalancesFormatter;
680
832
  exports.TalismanBalancesDatabase = TalismanBalancesDatabase;
681
833
  exports.balances = balances;
834
+ exports.createSubscriptionId = createSubscriptionId;
682
835
  exports.createTypeRegistryCache = createTypeRegistryCache;
683
836
  exports.db = db;
837
+ exports.deleteSubscriptionId = deleteSubscriptionId;
838
+ exports.deriveStatuses = deriveStatuses;
684
839
  exports.excludeFromFeePayableLocks = excludeFromFeePayableLocks;
685
840
  exports.excludeFromTransferableAmount = excludeFromTransferableAmount;
686
841
  exports.filterMirrorTokens = filterMirrorTokens;
842
+ exports.getValidSubscriptionIds = getValidSubscriptionIds;
687
843
  exports.includeInTotalExtraAmount = includeInTotalExtraAmount;
@@ -4,11 +4,13 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var dexie = require('dexie');
6
6
  var types = require('@polkadot/types');
7
- var anylogger = require('anylogger');
8
7
  var util = require('@talismn/util');
8
+ var groupBy = require('lodash/groupBy');
9
+ var anylogger = require('anylogger');
9
10
 
10
11
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
11
12
 
13
+ var groupBy__default = /*#__PURE__*/_interopDefault(groupBy);
12
14
  var anylogger__default = /*#__PURE__*/_interopDefault(anylogger);
13
15
 
14
16
  // TODO: Document default balances module purpose/usage
@@ -67,7 +69,7 @@ const db = new TalismanBalancesDatabase();
67
69
 
68
70
  var packageJson = {
69
71
  name: "@talismn/balances",
70
- version: "0.0.0-pr672-20230404063840",
72
+ version: "0.0.0-pr677-20230413171850",
71
73
  author: "Talisman",
72
74
  homepage: "https://talisman.xyz",
73
75
  license: "UNLICENSED",
@@ -100,20 +102,22 @@ var packageJson = {
100
102
  "@talismn/token-rates": "workspace:^",
101
103
  "@talismn/util": "workspace:^",
102
104
  anylogger: "^1.0.11",
103
- dexie: "^3.2.3"
105
+ dexie: "^3.2.3",
106
+ lodash: "4.17.21"
104
107
  },
105
108
  devDependencies: {
106
- "@polkadot/types": "^9.10.5",
109
+ "@polkadot/types": "^10.1.4",
107
110
  "@talismn/eslint-config": "workspace:^",
108
111
  "@talismn/tsconfig": "workspace:^",
109
112
  "@types/jest": "^27.5.1",
113
+ "@types/lodash": "^4.14.180",
110
114
  eslint: "^8.4.0",
111
115
  jest: "^28.1.0",
112
116
  "ts-jest": "^28.0.2",
113
117
  typescript: "^4.6.4"
114
118
  },
115
119
  peerDependencies: {
116
- "@polkadot/types": "9.x"
120
+ "@polkadot/types": "10.x"
117
121
  },
118
122
  preconstruct: {
119
123
  entrypoints: [
@@ -146,8 +150,14 @@ const createTypeRegistryCache = () => {
146
150
  if (cached) return cached;
147
151
  const typeRegistry = new types.TypeRegistry();
148
152
  if (typeof metadataRpc === "string") {
149
- const metadata = new types.Metadata(typeRegistry, metadataRpc);
150
- metadata.registry.setMetadata(metadata);
153
+ try {
154
+ const metadata = new types.Metadata(typeRegistry, metadataRpc);
155
+ metadata.registry.setMetadata(metadata);
156
+ } catch (cause) {
157
+ log.warn(new Error(`Failed to set metadata for chain ${chainId}`, {
158
+ cause
159
+ }));
160
+ }
151
161
  }
152
162
  typeRegistryCache.set(chainId, typeRegistry);
153
163
  return typeRegistry;
@@ -161,6 +171,49 @@ const filterMirrorTokens = (balance, i, balances) => {
161
171
  const mirrorOf = balance.token?.mirrorOf;
162
172
  return !mirrorOf || !balances.find(b => b.tokenId === mirrorOf);
163
173
  };
174
+ const getValidSubscriptionIds = () => {
175
+ return new Set(localStorage.getItem("TalismanBalancesSubscriptionIds")?.split(",") ?? []);
176
+ };
177
+ const createSubscriptionId = () => {
178
+ // delete current id (if exists)
179
+ deleteSubscriptionId();
180
+
181
+ // create new id
182
+ const subscriptionId = Date.now().toString();
183
+ sessionStorage.setItem("TalismanBalancesSubscriptionId", subscriptionId);
184
+
185
+ // add to list of current ids
186
+ const subscriptionIds = getValidSubscriptionIds();
187
+ subscriptionIds.add(subscriptionId);
188
+ localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
189
+ return subscriptionId;
190
+ };
191
+ const deleteSubscriptionId = () => {
192
+ const subscriptionId = sessionStorage.getItem("TalismanBalancesSubscriptionId");
193
+ if (!subscriptionId) return;
194
+ const subscriptionIds = getValidSubscriptionIds();
195
+ subscriptionIds.delete(subscriptionId);
196
+ localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
197
+ };
198
+
199
+ /**
200
+ * Sets all balance statuses from `live-${string}` to either `live` or `cached`
201
+ */
202
+ const deriveStatuses = (validSubscriptionIds, balances) => balances.map(balance => {
203
+ if (balance.status === "live" || balance.status === "cache" || balance.status === "stale") return balance;
204
+ if (validSubscriptionIds.length < 1) return {
205
+ ...balance,
206
+ status: "cache"
207
+ };
208
+ if (!validSubscriptionIds.includes(balance.status.slice("live-".length))) return {
209
+ ...balance,
210
+ status: "cache"
211
+ };
212
+ return {
213
+ ...balance,
214
+ status: "live"
215
+ };
216
+ });
164
217
 
165
218
  /**
166
219
  * Used by a variety of balance modules to help encode and decode substrate state calls.
@@ -243,6 +296,76 @@ class StorageHelper {
243
296
  }
244
297
  }
245
298
 
299
+ /**
300
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
301
+ */
302
+
303
+ /**
304
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
305
+ */
306
+ class RpcStateQueryHelper {
307
+ #chainConnector;
308
+ #queries;
309
+ constructor(chainConnector, queries) {
310
+ this.#chainConnector = chainConnector;
311
+ this.#queries = queries;
312
+ }
313
+ async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
314
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
315
+ const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
316
+ const params = [queries.map(({
317
+ stateKey
318
+ }) => stateKey)];
319
+ const unsubscribe = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
320
+ error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
321
+ }, timeout);
322
+ return () => unsubscribe(unsubscribeMethod);
323
+ }).map(subscription => subscription.catch(error => {
324
+ log.warn(`Failed to create subscription: ${error.message}`);
325
+ return () => {};
326
+ }));
327
+ return () => subscriptions.forEach(subscription => subscription.then(unsubscribe => unsubscribe()));
328
+ }
329
+ async fetch(method = "state_queryStorageAt") {
330
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
331
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
332
+ const params = [queries.map(({
333
+ stateKey
334
+ }) => stateKey)];
335
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
336
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
337
+ }));
338
+ return resultsByChain.flatMap(result => result);
339
+ }
340
+ #distributeChangesToQueryDecoders(chainId, result) {
341
+ if (typeof result !== "object" || result === null) return [];
342
+ if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
343
+ if (!Array.isArray(result.changes)) return [];
344
+ return result.changes.flatMap(([reference, change]) => {
345
+ if (typeof reference !== "string") {
346
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
347
+ return [];
348
+ }
349
+ if (typeof change !== "string" && change !== null) {
350
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
351
+ return [];
352
+ }
353
+ const query = this.#queries.find(({
354
+ chainId: cId,
355
+ stateKey
356
+ }) => cId === chainId && stateKey === reference);
357
+ if (!query) {
358
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
359
+ stateKey
360
+ }) => stateKey)}`);
361
+ return [];
362
+ }
363
+ return query.decodeResult(change);
364
+ });
365
+ }
366
+ }
367
+
368
+ const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
246
369
  function excludeFromTransferableAmount(locks) {
247
370
  if (typeof locks === "string") return BigInt(locks);
248
371
  if (!Array.isArray(locks)) locks = [locks];
@@ -492,13 +615,14 @@ class Balance {
492
615
  get id() {
493
616
  const {
494
617
  source,
618
+ subSource,
495
619
  address,
496
620
  chainId,
497
621
  evmNetworkId,
498
622
  tokenId
499
623
  } = this.#storage;
500
624
  const locationId = chainId !== undefined ? chainId : evmNetworkId;
501
- return `${source}-${address}-${locationId}-${tokenId}`;
625
+ return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
502
626
  }
503
627
  get source() {
504
628
  return this.#storage.source;
@@ -553,10 +677,36 @@ class Balance {
553
677
  get reserved() {
554
678
  return this.#format(typeof this.#storage.reserves === "string" ? BigInt(this.#storage.reserves) : Array.isArray(this.#storage.reserves) ? this.#storage.reserves.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, BigInt("0")) : BigInt(this.#storage.reserves?.amount || "0"));
555
679
  }
680
+ get reserves() {
681
+ return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
682
+ if (reserve === undefined) return [];
683
+ if (typeof reserve === "string") return {
684
+ label: "other",
685
+ amount: this.#format(reserve)
686
+ };
687
+ return {
688
+ ...reserve,
689
+ amount: this.#format(reserve.amount)
690
+ };
691
+ });
692
+ }
556
693
  /** The frozen balance of this token. Is included in the free amount. */
557
694
  get locked() {
558
695
  return this.#format(typeof this.#storage.locks === "string" ? BigInt(this.#storage.locks) : Array.isArray(this.#storage.locks) ? this.#storage.locks.map(lock => BigInt(lock.amount)).reduce((a, b) => util.BigMath.max(a, b), BigInt("0")) : BigInt(this.#storage.locks?.amount || "0"));
559
696
  }
697
+ get locks() {
698
+ return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
699
+ if (lock === undefined) return [];
700
+ if (typeof lock === "string") return {
701
+ label: "other",
702
+ amount: this.#format(lock)
703
+ };
704
+ return {
705
+ ...lock,
706
+ amount: this.#format(lock.amount)
707
+ };
708
+ });
709
+ }
560
710
  /** @depreacted - use balance.locked */
561
711
  get frozen() {
562
712
  return this.locked;
@@ -672,16 +822,22 @@ class SumBalancesFormatter {
672
822
 
673
823
  exports.Balance = Balance;
674
824
  exports.BalanceFormatter = BalanceFormatter;
825
+ exports.BalanceStatusLive = BalanceStatusLive;
675
826
  exports.Balances = Balances;
676
827
  exports.DefaultBalanceModule = DefaultBalanceModule;
677
828
  exports.FiatSumBalancesFormatter = FiatSumBalancesFormatter;
829
+ exports.RpcStateQueryHelper = RpcStateQueryHelper;
678
830
  exports.StorageHelper = StorageHelper;
679
831
  exports.SumBalancesFormatter = SumBalancesFormatter;
680
832
  exports.TalismanBalancesDatabase = TalismanBalancesDatabase;
681
833
  exports.balances = balances;
834
+ exports.createSubscriptionId = createSubscriptionId;
682
835
  exports.createTypeRegistryCache = createTypeRegistryCache;
683
836
  exports.db = db;
837
+ exports.deleteSubscriptionId = deleteSubscriptionId;
838
+ exports.deriveStatuses = deriveStatuses;
684
839
  exports.excludeFromFeePayableLocks = excludeFromFeePayableLocks;
685
840
  exports.excludeFromTransferableAmount = excludeFromTransferableAmount;
686
841
  exports.filterMirrorTokens = filterMirrorTokens;
842
+ exports.getValidSubscriptionIds = getValidSubscriptionIds;
687
843
  exports.includeInTotalExtraAmount = includeInTotalExtraAmount;
@@ -1,7 +1,8 @@
1
1
  import { Dexie } from 'dexie';
2
2
  import { decorateStorage, StorageKey, TypeRegistry, Metadata } from '@polkadot/types';
3
+ import { hasOwnProperty, BigMath, isArrayOf, planckToTokens } from '@talismn/util';
4
+ import groupBy from 'lodash/groupBy';
3
5
  import anylogger from 'anylogger';
4
- import { BigMath, isArrayOf, planckToTokens } from '@talismn/util';
5
6
 
6
7
  // TODO: Document default balances module purpose/usage
7
8
  const DefaultBalanceModule = type => ({
@@ -59,7 +60,7 @@ const db = new TalismanBalancesDatabase();
59
60
 
60
61
  var packageJson = {
61
62
  name: "@talismn/balances",
62
- version: "0.0.0-pr672-20230404063840",
63
+ version: "0.0.0-pr677-20230413171850",
63
64
  author: "Talisman",
64
65
  homepage: "https://talisman.xyz",
65
66
  license: "UNLICENSED",
@@ -92,20 +93,22 @@ var packageJson = {
92
93
  "@talismn/token-rates": "workspace:^",
93
94
  "@talismn/util": "workspace:^",
94
95
  anylogger: "^1.0.11",
95
- dexie: "^3.2.3"
96
+ dexie: "^3.2.3",
97
+ lodash: "4.17.21"
96
98
  },
97
99
  devDependencies: {
98
- "@polkadot/types": "^9.10.5",
100
+ "@polkadot/types": "^10.1.4",
99
101
  "@talismn/eslint-config": "workspace:^",
100
102
  "@talismn/tsconfig": "workspace:^",
101
103
  "@types/jest": "^27.5.1",
104
+ "@types/lodash": "^4.14.180",
102
105
  eslint: "^8.4.0",
103
106
  jest: "^28.1.0",
104
107
  "ts-jest": "^28.0.2",
105
108
  typescript: "^4.6.4"
106
109
  },
107
110
  peerDependencies: {
108
- "@polkadot/types": "9.x"
111
+ "@polkadot/types": "10.x"
109
112
  },
110
113
  preconstruct: {
111
114
  entrypoints: [
@@ -138,8 +141,14 @@ const createTypeRegistryCache = () => {
138
141
  if (cached) return cached;
139
142
  const typeRegistry = new TypeRegistry();
140
143
  if (typeof metadataRpc === "string") {
141
- const metadata = new Metadata(typeRegistry, metadataRpc);
142
- metadata.registry.setMetadata(metadata);
144
+ try {
145
+ const metadata = new Metadata(typeRegistry, metadataRpc);
146
+ metadata.registry.setMetadata(metadata);
147
+ } catch (cause) {
148
+ log.warn(new Error(`Failed to set metadata for chain ${chainId}`, {
149
+ cause
150
+ }));
151
+ }
143
152
  }
144
153
  typeRegistryCache.set(chainId, typeRegistry);
145
154
  return typeRegistry;
@@ -153,6 +162,49 @@ const filterMirrorTokens = (balance, i, balances) => {
153
162
  const mirrorOf = balance.token?.mirrorOf;
154
163
  return !mirrorOf || !balances.find(b => b.tokenId === mirrorOf);
155
164
  };
165
+ const getValidSubscriptionIds = () => {
166
+ return new Set(localStorage.getItem("TalismanBalancesSubscriptionIds")?.split(",") ?? []);
167
+ };
168
+ const createSubscriptionId = () => {
169
+ // delete current id (if exists)
170
+ deleteSubscriptionId();
171
+
172
+ // create new id
173
+ const subscriptionId = Date.now().toString();
174
+ sessionStorage.setItem("TalismanBalancesSubscriptionId", subscriptionId);
175
+
176
+ // add to list of current ids
177
+ const subscriptionIds = getValidSubscriptionIds();
178
+ subscriptionIds.add(subscriptionId);
179
+ localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
180
+ return subscriptionId;
181
+ };
182
+ const deleteSubscriptionId = () => {
183
+ const subscriptionId = sessionStorage.getItem("TalismanBalancesSubscriptionId");
184
+ if (!subscriptionId) return;
185
+ const subscriptionIds = getValidSubscriptionIds();
186
+ subscriptionIds.delete(subscriptionId);
187
+ localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
188
+ };
189
+
190
+ /**
191
+ * Sets all balance statuses from `live-${string}` to either `live` or `cached`
192
+ */
193
+ const deriveStatuses = (validSubscriptionIds, balances) => balances.map(balance => {
194
+ if (balance.status === "live" || balance.status === "cache" || balance.status === "stale") return balance;
195
+ if (validSubscriptionIds.length < 1) return {
196
+ ...balance,
197
+ status: "cache"
198
+ };
199
+ if (!validSubscriptionIds.includes(balance.status.slice("live-".length))) return {
200
+ ...balance,
201
+ status: "cache"
202
+ };
203
+ return {
204
+ ...balance,
205
+ status: "live"
206
+ };
207
+ });
156
208
 
157
209
  /**
158
210
  * Used by a variety of balance modules to help encode and decode substrate state calls.
@@ -235,6 +287,76 @@ class StorageHelper {
235
287
  }
236
288
  }
237
289
 
290
+ /**
291
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
292
+ */
293
+
294
+ /**
295
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
296
+ */
297
+ class RpcStateQueryHelper {
298
+ #chainConnector;
299
+ #queries;
300
+ constructor(chainConnector, queries) {
301
+ this.#chainConnector = chainConnector;
302
+ this.#queries = queries;
303
+ }
304
+ async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
305
+ const queriesByChain = groupBy(this.#queries, "chainId");
306
+ const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
307
+ const params = [queries.map(({
308
+ stateKey
309
+ }) => stateKey)];
310
+ const unsubscribe = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
311
+ error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
312
+ }, timeout);
313
+ return () => unsubscribe(unsubscribeMethod);
314
+ }).map(subscription => subscription.catch(error => {
315
+ log.warn(`Failed to create subscription: ${error.message}`);
316
+ return () => {};
317
+ }));
318
+ return () => subscriptions.forEach(subscription => subscription.then(unsubscribe => unsubscribe()));
319
+ }
320
+ async fetch(method = "state_queryStorageAt") {
321
+ const queriesByChain = groupBy(this.#queries, "chainId");
322
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
323
+ const params = [queries.map(({
324
+ stateKey
325
+ }) => stateKey)];
326
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
327
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
328
+ }));
329
+ return resultsByChain.flatMap(result => result);
330
+ }
331
+ #distributeChangesToQueryDecoders(chainId, result) {
332
+ if (typeof result !== "object" || result === null) return [];
333
+ if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
334
+ if (!Array.isArray(result.changes)) return [];
335
+ return result.changes.flatMap(([reference, change]) => {
336
+ if (typeof reference !== "string") {
337
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
338
+ return [];
339
+ }
340
+ if (typeof change !== "string" && change !== null) {
341
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
342
+ return [];
343
+ }
344
+ const query = this.#queries.find(({
345
+ chainId: cId,
346
+ stateKey
347
+ }) => cId === chainId && stateKey === reference);
348
+ if (!query) {
349
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
350
+ stateKey
351
+ }) => stateKey)}`);
352
+ return [];
353
+ }
354
+ return query.decodeResult(change);
355
+ });
356
+ }
357
+ }
358
+
359
+ const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
238
360
  function excludeFromTransferableAmount(locks) {
239
361
  if (typeof locks === "string") return BigInt(locks);
240
362
  if (!Array.isArray(locks)) locks = [locks];
@@ -484,13 +606,14 @@ class Balance {
484
606
  get id() {
485
607
  const {
486
608
  source,
609
+ subSource,
487
610
  address,
488
611
  chainId,
489
612
  evmNetworkId,
490
613
  tokenId
491
614
  } = this.#storage;
492
615
  const locationId = chainId !== undefined ? chainId : evmNetworkId;
493
- return `${source}-${address}-${locationId}-${tokenId}`;
616
+ return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
494
617
  }
495
618
  get source() {
496
619
  return this.#storage.source;
@@ -545,10 +668,36 @@ class Balance {
545
668
  get reserved() {
546
669
  return this.#format(typeof this.#storage.reserves === "string" ? BigInt(this.#storage.reserves) : Array.isArray(this.#storage.reserves) ? this.#storage.reserves.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, BigInt("0")) : BigInt(this.#storage.reserves?.amount || "0"));
547
670
  }
671
+ get reserves() {
672
+ return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
673
+ if (reserve === undefined) return [];
674
+ if (typeof reserve === "string") return {
675
+ label: "other",
676
+ amount: this.#format(reserve)
677
+ };
678
+ return {
679
+ ...reserve,
680
+ amount: this.#format(reserve.amount)
681
+ };
682
+ });
683
+ }
548
684
  /** The frozen balance of this token. Is included in the free amount. */
549
685
  get locked() {
550
686
  return this.#format(typeof this.#storage.locks === "string" ? BigInt(this.#storage.locks) : Array.isArray(this.#storage.locks) ? this.#storage.locks.map(lock => BigInt(lock.amount)).reduce((a, b) => BigMath.max(a, b), BigInt("0")) : BigInt(this.#storage.locks?.amount || "0"));
551
687
  }
688
+ get locks() {
689
+ return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
690
+ if (lock === undefined) return [];
691
+ if (typeof lock === "string") return {
692
+ label: "other",
693
+ amount: this.#format(lock)
694
+ };
695
+ return {
696
+ ...lock,
697
+ amount: this.#format(lock.amount)
698
+ };
699
+ });
700
+ }
552
701
  /** @depreacted - use balance.locked */
553
702
  get frozen() {
554
703
  return this.locked;
@@ -662,4 +811,4 @@ class SumBalancesFormatter {
662
811
  }
663
812
  }
664
813
 
665
- export { Balance, BalanceFormatter, Balances, DefaultBalanceModule, FiatSumBalancesFormatter, StorageHelper, SumBalancesFormatter, TalismanBalancesDatabase, balances, createTypeRegistryCache, db, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterMirrorTokens, includeInTotalExtraAmount };
814
+ export { Balance, BalanceFormatter, BalanceStatusLive, Balances, DefaultBalanceModule, FiatSumBalancesFormatter, RpcStateQueryHelper, StorageHelper, SumBalancesFormatter, TalismanBalancesDatabase, balances, createSubscriptionId, createTypeRegistryCache, db, deleteSubscriptionId, deriveStatuses, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterMirrorTokens, getValidSubscriptionIds, includeInTotalExtraAmount };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/balances",
3
- "version": "0.0.0-pr672-20230404063840",
3
+ "version": "0.0.0-pr677-20230413171850",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "UNLICENSED",
@@ -27,26 +27,28 @@
27
27
  "clean": "rm -rf dist && rm -rf .turbo rm -rf node_modules"
28
28
  },
29
29
  "dependencies": {
30
- "@talismn/chain-connector": "^0.0.0-pr672-20230404063840",
31
- "@talismn/chain-connector-evm": "^0.0.0-pr672-20230404063840",
32
- "@talismn/chaindata-provider": "^0.0.0-pr672-20230404063840",
33
- "@talismn/token-rates": "^0.0.0-pr672-20230404063840",
34
- "@talismn/util": "^0.0.0-pr672-20230404063840",
30
+ "@talismn/chain-connector": "^0.0.0-pr677-20230413171850",
31
+ "@talismn/chain-connector-evm": "^0.0.0-pr677-20230413171850",
32
+ "@talismn/chaindata-provider": "^0.0.0-pr677-20230413171850",
33
+ "@talismn/token-rates": "^0.0.0-pr677-20230413171850",
34
+ "@talismn/util": "^0.0.0-pr677-20230413171850",
35
35
  "anylogger": "^1.0.11",
36
- "dexie": "^3.2.3"
36
+ "dexie": "^3.2.3",
37
+ "lodash": "4.17.21"
37
38
  },
38
39
  "devDependencies": {
39
- "@polkadot/types": "^9.10.5",
40
+ "@polkadot/types": "^10.1.4",
40
41
  "@talismn/eslint-config": "^0.0.1",
41
42
  "@talismn/tsconfig": "^0.0.2",
42
43
  "@types/jest": "^27.5.1",
44
+ "@types/lodash": "^4.14.180",
43
45
  "eslint": "^8.4.0",
44
46
  "jest": "^28.1.0",
45
47
  "ts-jest": "^28.0.2",
46
48
  "typescript": "^4.6.4"
47
49
  },
48
50
  "peerDependencies": {
49
- "@polkadot/types": "9.x"
51
+ "@polkadot/types": "10.x"
50
52
  },
51
53
  "preconstruct": {
52
54
  "entrypoints": [