@talismn/balances 0.0.0-pr708-20230419032011 → 0.0.0-pr708-20230419072006

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,19 +1,23 @@
1
1
  # @talismn/balances
2
2
 
3
- ## 0.0.0-pr708-20230419032011
3
+ ## 0.0.0-pr708-20230419072006
4
4
 
5
5
  ### Patch Changes
6
6
 
7
7
  - fb8ee962: feat: proxy dapp websocket requests to talisman wallet backend when available
8
8
  - 8e63a8cf: eslint rules
9
+ - 01bf239b: feat: crowdloan and nom pool balances
10
+ - 01bf239b: fix: packages publishing with incorrect interdependency versions
9
11
  - Updated dependencies [fb8ee962]
10
12
  - Updated dependencies [c898da98]
11
13
  - Updated dependencies [8e63a8cf]
12
- - @talismn/chain-connector@0.0.0-pr708-20230419032011
13
- - @talismn/chain-connector-evm@0.0.0-pr708-20230419032011
14
- - @talismn/chaindata-provider@0.0.0-pr708-20230419032011
15
- - @talismn/token-rates@0.0.0-pr708-20230419032011
16
- - @talismn/util@0.0.0-pr708-20230419032011
14
+ - Updated dependencies [01bf239b]
15
+ - Updated dependencies [01bf239b]
16
+ - @talismn/chain-connector@0.0.0-pr708-20230419072006
17
+ - @talismn/chain-connector-evm@0.0.0-pr708-20230419072006
18
+ - @talismn/chaindata-provider@0.0.0-pr708-20230419072006
19
+ - @talismn/token-rates@0.0.0-pr708-20230419072006
20
+ - @talismn/util@0.0.0-pr708-20230419072006
17
21
 
18
22
  ## 0.4.0
19
23
 
@@ -1,4 +1,5 @@
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
5
  import { AddressesByToken, Balance, BalanceJson, Balances, SubscriptionCallback, UnsubscribeFn } from "./types";
@@ -8,8 +9,9 @@ import { AddressesByToken, Balance, BalanceJson, Balances, SubscriptionCallback,
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;
15
17
  export declare const getValidSubscriptionIds: () => Set<string>;
@@ -33,3 +35,20 @@ export declare class StorageHelper {
33
35
  tag(tags: any): this;
34
36
  decode(input?: string | null): import("@polkadot/types-codec/types").Codec | undefined;
35
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
+ }
@@ -61,6 +61,19 @@ export declare class Balances {
61
61
  * @returns All balances which match the query.
62
62
  */
63
63
  find: (query: BalanceSearchQuery | BalanceSearchQuery[]) => Balances;
64
+ /**
65
+ * Filters this collection to exclude token balances where the token has a `mirrorOf` field
66
+ * and another balance exists in this collection for the token specified by the `mirrorOf` field.
67
+ */
68
+ filterMirrorTokens: () => Balances;
69
+ /**
70
+ * Filters this collection to only include balances which are not zero.
71
+ */
72
+ filterNonZero: (type: "total" | "free" | "reserved" | "locked" | "frozen" | "transferable" | "feePayable") => Balances;
73
+ /**
74
+ * Filters this collection to only include balances which are not zero AND have a fiat conversion rate.
75
+ */
76
+ filterNonZeroFiat: (type: "total" | "free" | "reserved" | "locked" | "frozen" | "transferable" | "feePayable", currency: TokenRateCurrency) => Balances;
64
77
  /**
65
78
  * Add some balances to this collection.
66
79
  * Added balances take priority over existing balances.
@@ -134,9 +147,21 @@ export declare class Balance {
134
147
  get free(): BalanceFormatter;
135
148
  /** The reserved balance of this token. Is included in the total. */
136
149
  get reserved(): BalanceFormatter;
150
+ get reserves(): {
151
+ amount: BalanceFormatter;
152
+ label: string;
153
+ meta?: unknown;
154
+ }[];
137
155
  /** The frozen balance of this token. Is included in the free amount. */
138
156
  get locked(): BalanceFormatter;
139
- /** @depreacted - use balance.locked */
157
+ get locks(): {
158
+ amount: BalanceFormatter;
159
+ label: string;
160
+ meta?: unknown;
161
+ includeInTransferable?: boolean | undefined;
162
+ excludeFromFeePayable?: boolean | undefined;
163
+ }[];
164
+ /** @deprecated Use balance.locked */
140
165
  get frozen(): BalanceFormatter;
141
166
  /** The transferable balance of this token. Is generally the free amount - the miscFrozen amount. */
142
167
  get transferable(): BalanceFormatter;
@@ -164,7 +189,7 @@ export declare class FiatSumBalancesFormatter {
164
189
  get reserved(): number;
165
190
  /** The frozen balance of these tokens. Is included in the free amount. */
166
191
  get locked(): number;
167
- /** @deprecated - use balances.locked */
192
+ /** @deprecated Use balances.locked */
168
193
  get frozen(): number;
169
194
  /** The transferable balance of these tokens. Is generally the free amount - the miscFrozen amount. */
170
195
  get transferable(): number;
@@ -30,6 +30,11 @@ export type BalanceStatus = BalanceStatusLive | "live" | "cache" | "stale";
30
30
  export type IBalance = {
31
31
  /** The module that this balance was retrieved by */
32
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;
33
38
  /** Has this balance never been fetched, or is it from a cache, or is it up to date? */
34
39
  status: BalanceStatus;
35
40
  /** The address of the account which owns this balance */
@@ -59,6 +64,7 @@ export type Amount = string;
59
64
  export type AmountWithLabel<TLabel extends string> = {
60
65
  label: TLabel;
61
66
  amount: Amount;
67
+ meta?: unknown;
62
68
  };
63
69
  /** A labelled locked amount of a balance */
64
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-pr708-20230419032011",
72
+ version: "0.0.0-pr708-20230419072006",
71
73
  author: "Talisman",
72
74
  homepage: "https://talisman.xyz",
73
75
  license: "UNLICENSED",
@@ -94,26 +96,28 @@ var packageJson = {
94
96
  clean: "rm -rf dist && rm -rf .turbo rm -rf node_modules"
95
97
  },
96
98
  dependencies: {
97
- "@talismn/chain-connector": "workspace:^",
98
- "@talismn/chain-connector-evm": "workspace:^",
99
- "@talismn/chaindata-provider": "workspace:^",
100
- "@talismn/token-rates": "workspace:^",
101
- "@talismn/util": "workspace:^",
99
+ "@talismn/chain-connector": "workspace:*",
100
+ "@talismn/chain-connector-evm": "workspace:*",
101
+ "@talismn/chaindata-provider": "workspace:*",
102
+ "@talismn/token-rates": "workspace:*",
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",
107
- "@talismn/eslint-config": "workspace:^",
108
- "@talismn/tsconfig": "workspace:^",
109
+ "@polkadot/types": "^10.1.4",
110
+ "@talismn/eslint-config": "workspace:*",
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
+ }), cause);
160
+ }
151
161
  }
152
162
  typeRegistryCache.set(chainId, typeRegistry);
153
163
  return typeRegistry;
@@ -292,11 +302,77 @@ class StorageHelper {
292
302
  }
293
303
  }
294
304
 
305
+ /**
306
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
307
+ */
308
+
309
+ /**
310
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
311
+ */
312
+ class RpcStateQueryHelper {
313
+ #chainConnector;
314
+ #queries;
315
+ constructor(chainConnector, queries) {
316
+ this.#chainConnector = chainConnector;
317
+ this.#queries = queries;
318
+ }
319
+ async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
320
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
321
+ const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
322
+ const params = [queries.map(({
323
+ stateKey
324
+ }) => stateKey)];
325
+ const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
326
+ error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
327
+ }, timeout);
328
+ return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
329
+ });
330
+ return () => subscriptions.forEach(unsubscribe => unsubscribe());
331
+ }
332
+ async fetch(method = "state_queryStorageAt") {
333
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
334
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
335
+ const params = [queries.map(({
336
+ stateKey
337
+ }) => stateKey)];
338
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
339
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
340
+ }));
341
+ return resultsByChain.flatMap(result => result);
342
+ }
343
+ #distributeChangesToQueryDecoders(chainId, result) {
344
+ if (typeof result !== "object" || result === null) return [];
345
+ if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
346
+ if (!Array.isArray(result.changes)) return [];
347
+ return result.changes.flatMap(([reference, change]) => {
348
+ if (typeof reference !== "string") {
349
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
350
+ return [];
351
+ }
352
+ if (typeof change !== "string" && change !== null) {
353
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
354
+ return [];
355
+ }
356
+ const query = this.#queries.find(({
357
+ chainId: cId,
358
+ stateKey
359
+ }) => cId === chainId && stateKey === reference);
360
+ if (!query) {
361
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
362
+ stateKey
363
+ }) => stateKey)}`);
364
+ return [];
365
+ }
366
+ return [query.decodeResult(change)];
367
+ });
368
+ }
369
+ }
370
+
295
371
  const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
296
372
  function excludeFromTransferableAmount(locks) {
297
373
  if (typeof locks === "string") return BigInt(locks);
298
374
  if (!Array.isArray(locks)) locks = [locks];
299
- return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), BigInt("0"));
375
+ return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), 0n);
300
376
  }
301
377
  function excludeFromFeePayableLocks(locks) {
302
378
  if (typeof locks === "string") return [];
@@ -307,9 +383,9 @@ function excludeFromFeePayableLocks(locks) {
307
383
  /** A labelled extra amount of a balance */
308
384
 
309
385
  function includeInTotalExtraAmount(extra) {
310
- if (!extra) return BigInt("0");
386
+ if (!extra) return 0n;
311
387
  if (!Array.isArray(extra)) extra = [extra];
312
- return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, BigInt("0"));
388
+ return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, 0n);
313
389
  }
314
390
 
315
391
  /** Used by plugins to help define their custom `BalanceType` */
@@ -420,6 +496,27 @@ class Balances {
420
496
  return new Balances([...this].filter(filter));
421
497
  };
422
498
 
499
+ /**
500
+ * Filters this collection to exclude token balances where the token has a `mirrorOf` field
501
+ * and another balance exists in this collection for the token specified by the `mirrorOf` field.
502
+ */
503
+ filterMirrorTokens = () => new Balances([...this].filter(filterMirrorTokens));
504
+
505
+ /**
506
+ * Filters this collection to only include balances which are not zero.
507
+ */
508
+ filterNonZero = type => {
509
+ const filter = balance => balance[type].planck > 0n;
510
+ return this.find(filter);
511
+ };
512
+ /**
513
+ * Filters this collection to only include balances which are not zero AND have a fiat conversion rate.
514
+ */
515
+ filterNonZeroFiat = (type, currency) => {
516
+ const filter = balance => (balance[type].fiat(currency) ?? 0) > 0;
517
+ return this.find(filter);
518
+ };
519
+
423
520
  /**
424
521
  * Add some balances to this collection.
425
522
  * Added balances take priority over existing balances.
@@ -542,13 +639,14 @@ class Balance {
542
639
  get id() {
543
640
  const {
544
641
  source,
642
+ subSource,
545
643
  address,
546
644
  chainId,
547
645
  evmNetworkId,
548
646
  tokenId
549
647
  } = this.#storage;
550
648
  const locationId = chainId !== undefined ? chainId : evmNetworkId;
551
- return `${source}-${address}-${locationId}-${tokenId}`;
649
+ return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
552
650
  }
553
651
  get source() {
554
652
  return this.#storage.source;
@@ -597,17 +695,43 @@ class Balance {
597
695
  }
598
696
  /** The non-reserved balance of this token. Includes the frozen amount. Is included in the total. */
599
697
  get free() {
600
- return this.#format(typeof this.#storage.free === "string" ? BigInt(this.#storage.free) : Array.isArray(this.#storage.free) ? this.#storage.free.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, BigInt("0")) : BigInt(this.#storage.free?.amount || "0"));
698
+ return this.#format(typeof this.#storage.free === "string" ? BigInt(this.#storage.free) : Array.isArray(this.#storage.free) ? this.#storage.free.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, 0n) : BigInt(this.#storage.free?.amount || "0"));
601
699
  }
602
700
  /** The reserved balance of this token. Is included in the total. */
603
701
  get reserved() {
604
- 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"));
702
+ 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, 0n) : BigInt(this.#storage.reserves?.amount || "0"));
703
+ }
704
+ get reserves() {
705
+ return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
706
+ if (reserve === undefined) return [];
707
+ if (typeof reserve === "string") return {
708
+ label: "reserved",
709
+ amount: this.#format(reserve)
710
+ };
711
+ return {
712
+ ...reserve,
713
+ amount: this.#format(reserve.amount)
714
+ };
715
+ });
605
716
  }
606
717
  /** The frozen balance of this token. Is included in the free amount. */
607
718
  get locked() {
608
- 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"));
719
+ 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), 0n) : BigInt(this.#storage.locks?.amount || "0"));
720
+ }
721
+ get locks() {
722
+ return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
723
+ if (lock === undefined) return [];
724
+ if (typeof lock === "string") return {
725
+ label: "other",
726
+ amount: this.#format(lock)
727
+ };
728
+ return {
729
+ ...lock,
730
+ amount: this.#format(lock.amount)
731
+ };
732
+ });
609
733
  }
610
- /** @depreacted - use balance.locked */
734
+ /** @deprecated Use balance.locked */
611
735
  get frozen() {
612
736
  return this.locked;
613
737
  }
@@ -620,7 +744,7 @@ class Balance {
620
744
  const excludeAmount = excludeFromTransferableAmount(this.#storage.locks);
621
745
 
622
746
  // subtract the lock from the free amount (but don't go below 0)
623
- return this.#format(util.BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
747
+ return this.#format(util.BigMath.max(this.free.planck - excludeAmount, 0n));
624
748
  }
625
749
  /** The feePayable balance of this token. Is generally the free amount - the feeFrozen amount. */
626
750
  get feePayable() {
@@ -628,10 +752,10 @@ class Balance {
628
752
  if (!this.#storage.locks) return this.free;
629
753
 
630
754
  // find the largest lock which can't be used to pay tx fees
631
- const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), BigInt("0"));
755
+ const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), 0n);
632
756
 
633
757
  // subtract the lock from the free amount (but don't go below 0)
634
- return this.#format(util.BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
758
+ return this.#format(util.BigMath.max(this.free.planck - excludeAmount, 0n));
635
759
  }
636
760
  }
637
761
  class BalanceFormatter {
@@ -666,11 +790,11 @@ class FiatSumBalancesFormatter {
666
790
  }
667
791
  #sum = balanceField => {
668
792
  // a function to get a fiat amount from a balance
669
- const fiat = balance => balance[balanceField].fiat(this.#currency) || 0;
793
+ const fiat = balance => balance[balanceField].fiat(this.#currency) ?? 0;
670
794
 
671
795
  // a function to add two amounts
672
796
  const sum = (a, b) => a + b;
673
- return [...this.#balances].filter(filterMirrorTokens).reduce((total, balance) => sum(
797
+ return this.#balances.filterMirrorTokens().each.reduce((total, balance) => sum(
674
798
  // add the total amount...
675
799
  total,
676
800
  // ...to the fiat amount of each balance
@@ -697,7 +821,7 @@ class FiatSumBalancesFormatter {
697
821
  get locked() {
698
822
  return this.#sum("locked");
699
823
  }
700
- /** @deprecated - use balances.locked */
824
+ /** @deprecated Use balances.locked */
701
825
  get frozen() {
702
826
  return this.locked;
703
827
  }
@@ -726,6 +850,7 @@ exports.BalanceStatusLive = BalanceStatusLive;
726
850
  exports.Balances = Balances;
727
851
  exports.DefaultBalanceModule = DefaultBalanceModule;
728
852
  exports.FiatSumBalancesFormatter = FiatSumBalancesFormatter;
853
+ exports.RpcStateQueryHelper = RpcStateQueryHelper;
729
854
  exports.StorageHelper = StorageHelper;
730
855
  exports.SumBalancesFormatter = SumBalancesFormatter;
731
856
  exports.TalismanBalancesDatabase = TalismanBalancesDatabase;
@@ -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-pr708-20230419032011",
72
+ version: "0.0.0-pr708-20230419072006",
71
73
  author: "Talisman",
72
74
  homepage: "https://talisman.xyz",
73
75
  license: "UNLICENSED",
@@ -94,26 +96,28 @@ var packageJson = {
94
96
  clean: "rm -rf dist && rm -rf .turbo rm -rf node_modules"
95
97
  },
96
98
  dependencies: {
97
- "@talismn/chain-connector": "workspace:^",
98
- "@talismn/chain-connector-evm": "workspace:^",
99
- "@talismn/chaindata-provider": "workspace:^",
100
- "@talismn/token-rates": "workspace:^",
101
- "@talismn/util": "workspace:^",
99
+ "@talismn/chain-connector": "workspace:*",
100
+ "@talismn/chain-connector-evm": "workspace:*",
101
+ "@talismn/chaindata-provider": "workspace:*",
102
+ "@talismn/token-rates": "workspace:*",
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",
107
- "@talismn/eslint-config": "workspace:^",
108
- "@talismn/tsconfig": "workspace:^",
109
+ "@polkadot/types": "^10.1.4",
110
+ "@talismn/eslint-config": "workspace:*",
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
+ }), cause);
160
+ }
151
161
  }
152
162
  typeRegistryCache.set(chainId, typeRegistry);
153
163
  return typeRegistry;
@@ -292,11 +302,77 @@ class StorageHelper {
292
302
  }
293
303
  }
294
304
 
305
+ /**
306
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
307
+ */
308
+
309
+ /**
310
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
311
+ */
312
+ class RpcStateQueryHelper {
313
+ #chainConnector;
314
+ #queries;
315
+ constructor(chainConnector, queries) {
316
+ this.#chainConnector = chainConnector;
317
+ this.#queries = queries;
318
+ }
319
+ async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
320
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
321
+ const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
322
+ const params = [queries.map(({
323
+ stateKey
324
+ }) => stateKey)];
325
+ const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
326
+ error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
327
+ }, timeout);
328
+ return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
329
+ });
330
+ return () => subscriptions.forEach(unsubscribe => unsubscribe());
331
+ }
332
+ async fetch(method = "state_queryStorageAt") {
333
+ const queriesByChain = groupBy__default["default"](this.#queries, "chainId");
334
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
335
+ const params = [queries.map(({
336
+ stateKey
337
+ }) => stateKey)];
338
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
339
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
340
+ }));
341
+ return resultsByChain.flatMap(result => result);
342
+ }
343
+ #distributeChangesToQueryDecoders(chainId, result) {
344
+ if (typeof result !== "object" || result === null) return [];
345
+ if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
346
+ if (!Array.isArray(result.changes)) return [];
347
+ return result.changes.flatMap(([reference, change]) => {
348
+ if (typeof reference !== "string") {
349
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
350
+ return [];
351
+ }
352
+ if (typeof change !== "string" && change !== null) {
353
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
354
+ return [];
355
+ }
356
+ const query = this.#queries.find(({
357
+ chainId: cId,
358
+ stateKey
359
+ }) => cId === chainId && stateKey === reference);
360
+ if (!query) {
361
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
362
+ stateKey
363
+ }) => stateKey)}`);
364
+ return [];
365
+ }
366
+ return [query.decodeResult(change)];
367
+ });
368
+ }
369
+ }
370
+
295
371
  const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
296
372
  function excludeFromTransferableAmount(locks) {
297
373
  if (typeof locks === "string") return BigInt(locks);
298
374
  if (!Array.isArray(locks)) locks = [locks];
299
- return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), BigInt("0"));
375
+ return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), 0n);
300
376
  }
301
377
  function excludeFromFeePayableLocks(locks) {
302
378
  if (typeof locks === "string") return [];
@@ -307,9 +383,9 @@ function excludeFromFeePayableLocks(locks) {
307
383
  /** A labelled extra amount of a balance */
308
384
 
309
385
  function includeInTotalExtraAmount(extra) {
310
- if (!extra) return BigInt("0");
386
+ if (!extra) return 0n;
311
387
  if (!Array.isArray(extra)) extra = [extra];
312
- return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, BigInt("0"));
388
+ return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, 0n);
313
389
  }
314
390
 
315
391
  /** Used by plugins to help define their custom `BalanceType` */
@@ -420,6 +496,27 @@ class Balances {
420
496
  return new Balances([...this].filter(filter));
421
497
  };
422
498
 
499
+ /**
500
+ * Filters this collection to exclude token balances where the token has a `mirrorOf` field
501
+ * and another balance exists in this collection for the token specified by the `mirrorOf` field.
502
+ */
503
+ filterMirrorTokens = () => new Balances([...this].filter(filterMirrorTokens));
504
+
505
+ /**
506
+ * Filters this collection to only include balances which are not zero.
507
+ */
508
+ filterNonZero = type => {
509
+ const filter = balance => balance[type].planck > 0n;
510
+ return this.find(filter);
511
+ };
512
+ /**
513
+ * Filters this collection to only include balances which are not zero AND have a fiat conversion rate.
514
+ */
515
+ filterNonZeroFiat = (type, currency) => {
516
+ const filter = balance => (balance[type].fiat(currency) ?? 0) > 0;
517
+ return this.find(filter);
518
+ };
519
+
423
520
  /**
424
521
  * Add some balances to this collection.
425
522
  * Added balances take priority over existing balances.
@@ -542,13 +639,14 @@ class Balance {
542
639
  get id() {
543
640
  const {
544
641
  source,
642
+ subSource,
545
643
  address,
546
644
  chainId,
547
645
  evmNetworkId,
548
646
  tokenId
549
647
  } = this.#storage;
550
648
  const locationId = chainId !== undefined ? chainId : evmNetworkId;
551
- return `${source}-${address}-${locationId}-${tokenId}`;
649
+ return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
552
650
  }
553
651
  get source() {
554
652
  return this.#storage.source;
@@ -597,17 +695,43 @@ class Balance {
597
695
  }
598
696
  /** The non-reserved balance of this token. Includes the frozen amount. Is included in the total. */
599
697
  get free() {
600
- return this.#format(typeof this.#storage.free === "string" ? BigInt(this.#storage.free) : Array.isArray(this.#storage.free) ? this.#storage.free.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, BigInt("0")) : BigInt(this.#storage.free?.amount || "0"));
698
+ return this.#format(typeof this.#storage.free === "string" ? BigInt(this.#storage.free) : Array.isArray(this.#storage.free) ? this.#storage.free.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, 0n) : BigInt(this.#storage.free?.amount || "0"));
601
699
  }
602
700
  /** The reserved balance of this token. Is included in the total. */
603
701
  get reserved() {
604
- 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"));
702
+ 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, 0n) : BigInt(this.#storage.reserves?.amount || "0"));
703
+ }
704
+ get reserves() {
705
+ return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
706
+ if (reserve === undefined) return [];
707
+ if (typeof reserve === "string") return {
708
+ label: "reserved",
709
+ amount: this.#format(reserve)
710
+ };
711
+ return {
712
+ ...reserve,
713
+ amount: this.#format(reserve.amount)
714
+ };
715
+ });
605
716
  }
606
717
  /** The frozen balance of this token. Is included in the free amount. */
607
718
  get locked() {
608
- 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"));
719
+ 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), 0n) : BigInt(this.#storage.locks?.amount || "0"));
720
+ }
721
+ get locks() {
722
+ return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
723
+ if (lock === undefined) return [];
724
+ if (typeof lock === "string") return {
725
+ label: "other",
726
+ amount: this.#format(lock)
727
+ };
728
+ return {
729
+ ...lock,
730
+ amount: this.#format(lock.amount)
731
+ };
732
+ });
609
733
  }
610
- /** @depreacted - use balance.locked */
734
+ /** @deprecated Use balance.locked */
611
735
  get frozen() {
612
736
  return this.locked;
613
737
  }
@@ -620,7 +744,7 @@ class Balance {
620
744
  const excludeAmount = excludeFromTransferableAmount(this.#storage.locks);
621
745
 
622
746
  // subtract the lock from the free amount (but don't go below 0)
623
- return this.#format(util.BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
747
+ return this.#format(util.BigMath.max(this.free.planck - excludeAmount, 0n));
624
748
  }
625
749
  /** The feePayable balance of this token. Is generally the free amount - the feeFrozen amount. */
626
750
  get feePayable() {
@@ -628,10 +752,10 @@ class Balance {
628
752
  if (!this.#storage.locks) return this.free;
629
753
 
630
754
  // find the largest lock which can't be used to pay tx fees
631
- const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), BigInt("0"));
755
+ const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => util.BigMath.max(max, lock), 0n);
632
756
 
633
757
  // subtract the lock from the free amount (but don't go below 0)
634
- return this.#format(util.BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
758
+ return this.#format(util.BigMath.max(this.free.planck - excludeAmount, 0n));
635
759
  }
636
760
  }
637
761
  class BalanceFormatter {
@@ -666,11 +790,11 @@ class FiatSumBalancesFormatter {
666
790
  }
667
791
  #sum = balanceField => {
668
792
  // a function to get a fiat amount from a balance
669
- const fiat = balance => balance[balanceField].fiat(this.#currency) || 0;
793
+ const fiat = balance => balance[balanceField].fiat(this.#currency) ?? 0;
670
794
 
671
795
  // a function to add two amounts
672
796
  const sum = (a, b) => a + b;
673
- return [...this.#balances].filter(filterMirrorTokens).reduce((total, balance) => sum(
797
+ return this.#balances.filterMirrorTokens().each.reduce((total, balance) => sum(
674
798
  // add the total amount...
675
799
  total,
676
800
  // ...to the fiat amount of each balance
@@ -697,7 +821,7 @@ class FiatSumBalancesFormatter {
697
821
  get locked() {
698
822
  return this.#sum("locked");
699
823
  }
700
- /** @deprecated - use balances.locked */
824
+ /** @deprecated Use balances.locked */
701
825
  get frozen() {
702
826
  return this.locked;
703
827
  }
@@ -726,6 +850,7 @@ exports.BalanceStatusLive = BalanceStatusLive;
726
850
  exports.Balances = Balances;
727
851
  exports.DefaultBalanceModule = DefaultBalanceModule;
728
852
  exports.FiatSumBalancesFormatter = FiatSumBalancesFormatter;
853
+ exports.RpcStateQueryHelper = RpcStateQueryHelper;
729
854
  exports.StorageHelper = StorageHelper;
730
855
  exports.SumBalancesFormatter = SumBalancesFormatter;
731
856
  exports.TalismanBalancesDatabase = TalismanBalancesDatabase;
@@ -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-pr708-20230419032011",
63
+ version: "0.0.0-pr708-20230419072006",
63
64
  author: "Talisman",
64
65
  homepage: "https://talisman.xyz",
65
66
  license: "UNLICENSED",
@@ -86,26 +87,28 @@ var packageJson = {
86
87
  clean: "rm -rf dist && rm -rf .turbo rm -rf node_modules"
87
88
  },
88
89
  dependencies: {
89
- "@talismn/chain-connector": "workspace:^",
90
- "@talismn/chain-connector-evm": "workspace:^",
91
- "@talismn/chaindata-provider": "workspace:^",
92
- "@talismn/token-rates": "workspace:^",
93
- "@talismn/util": "workspace:^",
90
+ "@talismn/chain-connector": "workspace:*",
91
+ "@talismn/chain-connector-evm": "workspace:*",
92
+ "@talismn/chaindata-provider": "workspace:*",
93
+ "@talismn/token-rates": "workspace:*",
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",
99
- "@talismn/eslint-config": "workspace:^",
100
- "@talismn/tsconfig": "workspace:^",
100
+ "@polkadot/types": "^10.1.4",
101
+ "@talismn/eslint-config": "workspace:*",
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
+ }), cause);
151
+ }
143
152
  }
144
153
  typeRegistryCache.set(chainId, typeRegistry);
145
154
  return typeRegistry;
@@ -284,11 +293,77 @@ class StorageHelper {
284
293
  }
285
294
  }
286
295
 
296
+ /**
297
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
298
+ */
299
+
300
+ /**
301
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
302
+ */
303
+ class RpcStateQueryHelper {
304
+ #chainConnector;
305
+ #queries;
306
+ constructor(chainConnector, queries) {
307
+ this.#chainConnector = chainConnector;
308
+ this.#queries = queries;
309
+ }
310
+ async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
311
+ const queriesByChain = groupBy(this.#queries, "chainId");
312
+ const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
313
+ const params = [queries.map(({
314
+ stateKey
315
+ }) => stateKey)];
316
+ const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
317
+ error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
318
+ }, timeout);
319
+ return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
320
+ });
321
+ return () => subscriptions.forEach(unsubscribe => unsubscribe());
322
+ }
323
+ async fetch(method = "state_queryStorageAt") {
324
+ const queriesByChain = groupBy(this.#queries, "chainId");
325
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
326
+ const params = [queries.map(({
327
+ stateKey
328
+ }) => stateKey)];
329
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
330
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
331
+ }));
332
+ return resultsByChain.flatMap(result => result);
333
+ }
334
+ #distributeChangesToQueryDecoders(chainId, result) {
335
+ if (typeof result !== "object" || result === null) return [];
336
+ if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
337
+ if (!Array.isArray(result.changes)) return [];
338
+ return result.changes.flatMap(([reference, change]) => {
339
+ if (typeof reference !== "string") {
340
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
341
+ return [];
342
+ }
343
+ if (typeof change !== "string" && change !== null) {
344
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
345
+ return [];
346
+ }
347
+ const query = this.#queries.find(({
348
+ chainId: cId,
349
+ stateKey
350
+ }) => cId === chainId && stateKey === reference);
351
+ if (!query) {
352
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
353
+ stateKey
354
+ }) => stateKey)}`);
355
+ return [];
356
+ }
357
+ return [query.decodeResult(change)];
358
+ });
359
+ }
360
+ }
361
+
287
362
  const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
288
363
  function excludeFromTransferableAmount(locks) {
289
364
  if (typeof locks === "string") return BigInt(locks);
290
365
  if (!Array.isArray(locks)) locks = [locks];
291
- return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), BigInt("0"));
366
+ return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), 0n);
292
367
  }
293
368
  function excludeFromFeePayableLocks(locks) {
294
369
  if (typeof locks === "string") return [];
@@ -299,9 +374,9 @@ function excludeFromFeePayableLocks(locks) {
299
374
  /** A labelled extra amount of a balance */
300
375
 
301
376
  function includeInTotalExtraAmount(extra) {
302
- if (!extra) return BigInt("0");
377
+ if (!extra) return 0n;
303
378
  if (!Array.isArray(extra)) extra = [extra];
304
- return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, BigInt("0"));
379
+ return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, 0n);
305
380
  }
306
381
 
307
382
  /** Used by plugins to help define their custom `BalanceType` */
@@ -412,6 +487,27 @@ class Balances {
412
487
  return new Balances([...this].filter(filter));
413
488
  };
414
489
 
490
+ /**
491
+ * Filters this collection to exclude token balances where the token has a `mirrorOf` field
492
+ * and another balance exists in this collection for the token specified by the `mirrorOf` field.
493
+ */
494
+ filterMirrorTokens = () => new Balances([...this].filter(filterMirrorTokens));
495
+
496
+ /**
497
+ * Filters this collection to only include balances which are not zero.
498
+ */
499
+ filterNonZero = type => {
500
+ const filter = balance => balance[type].planck > 0n;
501
+ return this.find(filter);
502
+ };
503
+ /**
504
+ * Filters this collection to only include balances which are not zero AND have a fiat conversion rate.
505
+ */
506
+ filterNonZeroFiat = (type, currency) => {
507
+ const filter = balance => (balance[type].fiat(currency) ?? 0) > 0;
508
+ return this.find(filter);
509
+ };
510
+
415
511
  /**
416
512
  * Add some balances to this collection.
417
513
  * Added balances take priority over existing balances.
@@ -534,13 +630,14 @@ class Balance {
534
630
  get id() {
535
631
  const {
536
632
  source,
633
+ subSource,
537
634
  address,
538
635
  chainId,
539
636
  evmNetworkId,
540
637
  tokenId
541
638
  } = this.#storage;
542
639
  const locationId = chainId !== undefined ? chainId : evmNetworkId;
543
- return `${source}-${address}-${locationId}-${tokenId}`;
640
+ return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
544
641
  }
545
642
  get source() {
546
643
  return this.#storage.source;
@@ -589,17 +686,43 @@ class Balance {
589
686
  }
590
687
  /** The non-reserved balance of this token. Includes the frozen amount. Is included in the total. */
591
688
  get free() {
592
- return this.#format(typeof this.#storage.free === "string" ? BigInt(this.#storage.free) : Array.isArray(this.#storage.free) ? this.#storage.free.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, BigInt("0")) : BigInt(this.#storage.free?.amount || "0"));
689
+ return this.#format(typeof this.#storage.free === "string" ? BigInt(this.#storage.free) : Array.isArray(this.#storage.free) ? this.#storage.free.map(reserve => BigInt(reserve.amount)).reduce((a, b) => a + b, 0n) : BigInt(this.#storage.free?.amount || "0"));
593
690
  }
594
691
  /** The reserved balance of this token. Is included in the total. */
595
692
  get reserved() {
596
- 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"));
693
+ 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, 0n) : BigInt(this.#storage.reserves?.amount || "0"));
694
+ }
695
+ get reserves() {
696
+ return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
697
+ if (reserve === undefined) return [];
698
+ if (typeof reserve === "string") return {
699
+ label: "reserved",
700
+ amount: this.#format(reserve)
701
+ };
702
+ return {
703
+ ...reserve,
704
+ amount: this.#format(reserve.amount)
705
+ };
706
+ });
597
707
  }
598
708
  /** The frozen balance of this token. Is included in the free amount. */
599
709
  get locked() {
600
- 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"));
710
+ 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), 0n) : BigInt(this.#storage.locks?.amount || "0"));
711
+ }
712
+ get locks() {
713
+ return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
714
+ if (lock === undefined) return [];
715
+ if (typeof lock === "string") return {
716
+ label: "other",
717
+ amount: this.#format(lock)
718
+ };
719
+ return {
720
+ ...lock,
721
+ amount: this.#format(lock.amount)
722
+ };
723
+ });
601
724
  }
602
- /** @depreacted - use balance.locked */
725
+ /** @deprecated Use balance.locked */
603
726
  get frozen() {
604
727
  return this.locked;
605
728
  }
@@ -612,7 +735,7 @@ class Balance {
612
735
  const excludeAmount = excludeFromTransferableAmount(this.#storage.locks);
613
736
 
614
737
  // subtract the lock from the free amount (but don't go below 0)
615
- return this.#format(BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
738
+ return this.#format(BigMath.max(this.free.planck - excludeAmount, 0n));
616
739
  }
617
740
  /** The feePayable balance of this token. Is generally the free amount - the feeFrozen amount. */
618
741
  get feePayable() {
@@ -620,10 +743,10 @@ class Balance {
620
743
  if (!this.#storage.locks) return this.free;
621
744
 
622
745
  // find the largest lock which can't be used to pay tx fees
623
- const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), BigInt("0"));
746
+ const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), 0n);
624
747
 
625
748
  // subtract the lock from the free amount (but don't go below 0)
626
- return this.#format(BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
749
+ return this.#format(BigMath.max(this.free.planck - excludeAmount, 0n));
627
750
  }
628
751
  }
629
752
  class BalanceFormatter {
@@ -658,11 +781,11 @@ class FiatSumBalancesFormatter {
658
781
  }
659
782
  #sum = balanceField => {
660
783
  // a function to get a fiat amount from a balance
661
- const fiat = balance => balance[balanceField].fiat(this.#currency) || 0;
784
+ const fiat = balance => balance[balanceField].fiat(this.#currency) ?? 0;
662
785
 
663
786
  // a function to add two amounts
664
787
  const sum = (a, b) => a + b;
665
- return [...this.#balances].filter(filterMirrorTokens).reduce((total, balance) => sum(
788
+ return this.#balances.filterMirrorTokens().each.reduce((total, balance) => sum(
666
789
  // add the total amount...
667
790
  total,
668
791
  // ...to the fiat amount of each balance
@@ -689,7 +812,7 @@ class FiatSumBalancesFormatter {
689
812
  get locked() {
690
813
  return this.#sum("locked");
691
814
  }
692
- /** @deprecated - use balances.locked */
815
+ /** @deprecated Use balances.locked */
693
816
  get frozen() {
694
817
  return this.locked;
695
818
  }
@@ -712,4 +835,4 @@ class SumBalancesFormatter {
712
835
  }
713
836
  }
714
837
 
715
- export { Balance, BalanceFormatter, BalanceStatusLive, Balances, DefaultBalanceModule, FiatSumBalancesFormatter, StorageHelper, SumBalancesFormatter, TalismanBalancesDatabase, balances, createSubscriptionId, createTypeRegistryCache, db, deleteSubscriptionId, deriveStatuses, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterMirrorTokens, getValidSubscriptionIds, includeInTotalExtraAmount };
838
+ 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-pr708-20230419032011",
3
+ "version": "0.0.0-pr708-20230419072006",
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-pr708-20230419032011",
31
- "@talismn/chain-connector-evm": "^0.0.0-pr708-20230419032011",
32
- "@talismn/chaindata-provider": "^0.0.0-pr708-20230419032011",
33
- "@talismn/token-rates": "^0.0.0-pr708-20230419032011",
34
- "@talismn/util": "^0.0.0-pr708-20230419032011",
30
+ "@talismn/chain-connector": "0.0.0-pr708-20230419072006",
31
+ "@talismn/chain-connector-evm": "0.0.0-pr708-20230419072006",
32
+ "@talismn/chaindata-provider": "0.0.0-pr708-20230419072006",
33
+ "@talismn/token-rates": "0.0.0-pr708-20230419072006",
34
+ "@talismn/util": "0.0.0-pr708-20230419072006",
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
- "@talismn/eslint-config": "^0.0.0-pr708-20230419032011",
41
- "@talismn/tsconfig": "^0.0.2",
40
+ "@polkadot/types": "^10.1.4",
41
+ "@talismn/eslint-config": "0.0.0-pr708-20230419072006",
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": [