@talismn/balances 0.0.0-pr705-20230418063922 → 0.0.0-pr707-20230418101721

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.
@@ -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-pr705-20230418063922",
63
+ version: "0.0.0-pr707-20230418101721",
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;
@@ -278,11 +287,77 @@ class StorageHelper {
278
287
  }
279
288
  }
280
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 unsub = 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 () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
314
+ });
315
+ return () => subscriptions.forEach(unsubscribe => unsubscribe());
316
+ }
317
+ async fetch(method = "state_queryStorageAt") {
318
+ const queriesByChain = groupBy(this.#queries, "chainId");
319
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
320
+ const params = [queries.map(({
321
+ stateKey
322
+ }) => stateKey)];
323
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
324
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
325
+ }));
326
+ return resultsByChain.flatMap(result => result);
327
+ }
328
+ #distributeChangesToQueryDecoders(chainId, result) {
329
+ if (typeof result !== "object" || result === null) return [];
330
+ if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
331
+ if (!Array.isArray(result.changes)) return [];
332
+ return result.changes.flatMap(([reference, change]) => {
333
+ if (typeof reference !== "string") {
334
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
335
+ return [];
336
+ }
337
+ if (typeof change !== "string" && change !== null) {
338
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
339
+ return [];
340
+ }
341
+ const query = this.#queries.find(({
342
+ chainId: cId,
343
+ stateKey
344
+ }) => cId === chainId && stateKey === reference);
345
+ if (!query) {
346
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
347
+ stateKey
348
+ }) => stateKey)}`);
349
+ return [];
350
+ }
351
+ return [query.decodeResult(change)];
352
+ });
353
+ }
354
+ }
355
+
281
356
  const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
282
357
  function excludeFromTransferableAmount(locks) {
283
358
  if (typeof locks === "string") return BigInt(locks);
284
359
  if (!Array.isArray(locks)) locks = [locks];
285
- return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), BigInt("0"));
360
+ return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), 0n);
286
361
  }
287
362
  function excludeFromFeePayableLocks(locks) {
288
363
  if (typeof locks === "string") return [];
@@ -293,9 +368,9 @@ function excludeFromFeePayableLocks(locks) {
293
368
  /** A labelled extra amount of a balance */
294
369
 
295
370
  function includeInTotalExtraAmount(extra) {
296
- if (!extra) return BigInt("0");
371
+ if (!extra) return 0n;
297
372
  if (!Array.isArray(extra)) extra = [extra];
298
- return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, BigInt("0"));
373
+ return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, 0n);
299
374
  }
300
375
 
301
376
  /** Used by plugins to help define their custom `BalanceType` */
@@ -406,6 +481,27 @@ class Balances {
406
481
  return new Balances([...this].filter(filter));
407
482
  };
408
483
 
484
+ /**
485
+ * Filters this collection to exclude token balances where the token has a `mirrorOf` field
486
+ * and another balance exists in this collection for the token specified by the `mirrorOf` field.
487
+ */
488
+ filterMirrorTokens = () => new Balances([...this].filter(filterMirrorTokens));
489
+
490
+ /**
491
+ * Filters this collection to only include balances which are not zero.
492
+ */
493
+ filterNonZero = type => {
494
+ const filter = balance => balance[type].planck > 0n;
495
+ return this.find(filter);
496
+ };
497
+ /**
498
+ * Filters this collection to only include balances which are not zero AND have a fiat conversion rate.
499
+ */
500
+ filterNonZeroFiat = (type, currency) => {
501
+ const filter = balance => (balance[type].fiat(currency) ?? 0) > 0;
502
+ return this.find(filter);
503
+ };
504
+
409
505
  /**
410
506
  * Add some balances to this collection.
411
507
  * Added balances take priority over existing balances.
@@ -528,13 +624,14 @@ class Balance {
528
624
  get id() {
529
625
  const {
530
626
  source,
627
+ subSource,
531
628
  address,
532
629
  chainId,
533
630
  evmNetworkId,
534
631
  tokenId
535
632
  } = this.#storage;
536
633
  const locationId = chainId !== undefined ? chainId : evmNetworkId;
537
- return `${source}-${address}-${locationId}-${tokenId}`;
634
+ return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
538
635
  }
539
636
  get source() {
540
637
  return this.#storage.source;
@@ -583,17 +680,43 @@ class Balance {
583
680
  }
584
681
  /** The non-reserved balance of this token. Includes the frozen amount. Is included in the total. */
585
682
  get free() {
586
- 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"));
683
+ 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"));
587
684
  }
588
685
  /** The reserved balance of this token. Is included in the total. */
589
686
  get reserved() {
590
- 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"));
687
+ 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"));
688
+ }
689
+ get reserves() {
690
+ return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
691
+ if (reserve === undefined) return [];
692
+ if (typeof reserve === "string") return {
693
+ label: "reserved",
694
+ amount: this.#format(reserve)
695
+ };
696
+ return {
697
+ ...reserve,
698
+ amount: this.#format(reserve.amount)
699
+ };
700
+ });
591
701
  }
592
702
  /** The frozen balance of this token. Is included in the free amount. */
593
703
  get locked() {
594
- 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"));
704
+ 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"));
705
+ }
706
+ get locks() {
707
+ return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
708
+ if (lock === undefined) return [];
709
+ if (typeof lock === "string") return {
710
+ label: "other",
711
+ amount: this.#format(lock)
712
+ };
713
+ return {
714
+ ...lock,
715
+ amount: this.#format(lock.amount)
716
+ };
717
+ });
595
718
  }
596
- /** @depreacted - use balance.locked */
719
+ /** @deprecated Use balance.locked */
597
720
  get frozen() {
598
721
  return this.locked;
599
722
  }
@@ -606,7 +729,7 @@ class Balance {
606
729
  const excludeAmount = excludeFromTransferableAmount(this.#storage.locks);
607
730
 
608
731
  // subtract the lock from the free amount (but don't go below 0)
609
- return this.#format(BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
732
+ return this.#format(BigMath.max(this.free.planck - excludeAmount, 0n));
610
733
  }
611
734
  /** The feePayable balance of this token. Is generally the free amount - the feeFrozen amount. */
612
735
  get feePayable() {
@@ -614,10 +737,10 @@ class Balance {
614
737
  if (!this.#storage.locks) return this.free;
615
738
 
616
739
  // find the largest lock which can't be used to pay tx fees
617
- const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), BigInt("0"));
740
+ const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), 0n);
618
741
 
619
742
  // subtract the lock from the free amount (but don't go below 0)
620
- return this.#format(BigMath.max(this.free.planck - excludeAmount, BigInt("0")));
743
+ return this.#format(BigMath.max(this.free.planck - excludeAmount, 0n));
621
744
  }
622
745
  }
623
746
  class BalanceFormatter {
@@ -643,6 +766,52 @@ class BalanceFormatter {
643
766
  return parseFloat(this.tokens) * ratio;
644
767
  }
645
768
  }
769
+ class PlanckSumBalancesFormatter {
770
+ #balances;
771
+ constructor(balances) {
772
+ this.#balances = balances;
773
+ }
774
+ #sum = balanceField => {
775
+ // a function to get a planck amount from a balance
776
+ const planck = balance => balance[balanceField].planck ?? 0n;
777
+ return this.#balances.filterMirrorTokens().each.reduce(
778
+ // add the total amount to the planck amount of each balance
779
+ (total, balance) => total + planck(balance),
780
+ // start with a total of 0
781
+ 0n);
782
+ };
783
+
784
+ /**
785
+ * The total balance of these tokens. Includes the free and the reserved amount.
786
+ */
787
+ get total() {
788
+ return this.#sum("total");
789
+ }
790
+ /** The non-reserved balance of these tokens. Includes the frozen amount. Is included in the total. */
791
+ get free() {
792
+ return this.#sum("free");
793
+ }
794
+ /** The reserved balance of these tokens. Is included in the total. */
795
+ get reserved() {
796
+ return this.#sum("reserved");
797
+ }
798
+ /** The frozen balance of these tokens. Is included in the free amount. */
799
+ get locked() {
800
+ return this.#sum("locked");
801
+ }
802
+ /** @deprecated Use balances.locked */
803
+ get frozen() {
804
+ return this.locked;
805
+ }
806
+ /** The transferable balance of these tokens. Is generally the free amount - the miscFrozen amount. */
807
+ get transferable() {
808
+ return this.#sum("transferable");
809
+ }
810
+ /** The feePayable balance of these tokens. Is generally the free amount - the feeFrozen amount. */
811
+ get feePayable() {
812
+ return this.#sum("feePayable");
813
+ }
814
+ }
646
815
  class FiatSumBalancesFormatter {
647
816
  #balances;
648
817
  #currency;
@@ -652,15 +821,10 @@ class FiatSumBalancesFormatter {
652
821
  }
653
822
  #sum = balanceField => {
654
823
  // a function to get a fiat amount from a balance
655
- const fiat = balance => balance[balanceField].fiat(this.#currency) || 0;
656
-
657
- // a function to add two amounts
658
- const sum = (a, b) => a + b;
659
- return [...this.#balances].filter(filterMirrorTokens).reduce((total, balance) => sum(
660
- // add the total amount...
661
- total,
662
- // ...to the fiat amount of each balance
663
- fiat(balance)),
824
+ const fiat = balance => balance[balanceField].fiat(this.#currency) ?? 0;
825
+ return this.#balances.filterMirrorTokens().each.reduce(
826
+ // add the total amount to the fiat amount of each balance
827
+ (total, balance) => total + fiat(balance),
664
828
  // start with a total of 0
665
829
  0);
666
830
  };
@@ -683,7 +847,7 @@ class FiatSumBalancesFormatter {
683
847
  get locked() {
684
848
  return this.#sum("locked");
685
849
  }
686
- /** @deprecated - use balances.locked */
850
+ /** @deprecated Use balances.locked */
687
851
  get frozen() {
688
852
  return this.locked;
689
853
  }
@@ -701,9 +865,12 @@ class SumBalancesFormatter {
701
865
  constructor(balances) {
702
866
  this.#balances = balances;
703
867
  }
868
+ get planck() {
869
+ return new PlanckSumBalancesFormatter(this.#balances);
870
+ }
704
871
  fiat(currency) {
705
872
  return new FiatSumBalancesFormatter(this.#balances, currency);
706
873
  }
707
874
  }
708
875
 
709
- export { Balance, BalanceFormatter, BalanceStatusLive, Balances, DefaultBalanceModule, FiatSumBalancesFormatter, StorageHelper, SumBalancesFormatter, TalismanBalancesDatabase, balances, createSubscriptionId, createTypeRegistryCache, db, deleteSubscriptionId, deriveStatuses, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterMirrorTokens, getValidSubscriptionIds, includeInTotalExtraAmount };
876
+ export { Balance, BalanceFormatter, BalanceStatusLive, Balances, DefaultBalanceModule, FiatSumBalancesFormatter, PlanckSumBalancesFormatter, 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-pr705-20230418063922",
3
+ "version": "0.0.0-pr707-20230418101721",
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-pr705-20230418063922",
31
- "@talismn/chain-connector-evm": "^0.0.0-pr705-20230418063922",
32
- "@talismn/chaindata-provider": "^0.0.0-pr705-20230418063922",
33
- "@talismn/token-rates": "^0.0.0-pr705-20230418063922",
34
- "@talismn/util": "^0.1.8",
30
+ "@talismn/chain-connector": "0.0.0-pr707-20230418101721",
31
+ "@talismn/chain-connector-evm": "0.0.0-pr707-20230418101721",
32
+ "@talismn/chaindata-provider": "0.0.0-pr707-20230418101721",
33
+ "@talismn/token-rates": "0.0.0-pr707-20230418101721",
34
+ "@talismn/util": "0.0.0-pr707-20230418101721",
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.1",
41
- "@talismn/tsconfig": "^0.0.2",
40
+ "@polkadot/types": "^10.1.4",
41
+ "@talismn/eslint-config": "0.0.1",
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": [