@talismn/balances 0.0.0-pr708-20230419030143 → 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 +12 -5
- package/dist/declarations/src/helpers.d.ts +20 -1
- package/dist/declarations/src/types/balances.d.ts +27 -2
- package/dist/declarations/src/types/balancetypes.d.ts +6 -0
- package/dist/talismn-balances.cjs.dev.js +153 -28
- package/dist/talismn-balances.cjs.prod.js +153 -28
- package/dist/talismn-balances.esm.js +152 -29
- package/package.json +13 -11
package/CHANGELOG.md
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
# @talismn/balances
|
2
2
|
|
3
|
-
## 0.0.0-pr708-
|
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
|
+
- 8e63a8cf: eslint rules
|
9
|
+
- 01bf239b: feat: crowdloan and nom pool balances
|
10
|
+
- 01bf239b: fix: packages publishing with incorrect interdependency versions
|
8
11
|
- Updated dependencies [fb8ee962]
|
9
12
|
- Updated dependencies [c898da98]
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
- @talismn/
|
13
|
+
- Updated dependencies [8e63a8cf]
|
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
|
14
21
|
|
15
22
|
## 0.4.0
|
16
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:
|
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
|
-
|
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
|
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-
|
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": "^
|
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": "
|
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
|
-
|
150
|
-
|
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),
|
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
|
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,
|
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
|
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,
|
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,
|
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),
|
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
|
-
/** @
|
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,
|
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),
|
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,
|
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)
|
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
|
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
|
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-
|
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": "^
|
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": "
|
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
|
-
|
150
|
-
|
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),
|
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
|
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,
|
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
|
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,
|
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,
|
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),
|
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
|
-
/** @
|
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,
|
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),
|
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,
|
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)
|
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
|
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
|
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-
|
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": "^
|
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": "
|
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
|
-
|
142
|
-
|
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),
|
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
|
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,
|
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
|
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,
|
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,
|
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),
|
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
|
-
/** @
|
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,
|
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),
|
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,
|
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)
|
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
|
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
|
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-
|
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": "
|
31
|
-
"@talismn/chain-connector-evm": "
|
32
|
-
"@talismn/chaindata-provider": "
|
33
|
-
"@talismn/token-rates": "
|
34
|
-
"@talismn/util": "
|
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": "^
|
40
|
-
"@talismn/eslint-config": "
|
41
|
-
"@talismn/tsconfig": "
|
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": "
|
51
|
+
"@polkadot/types": "10.x"
|
50
52
|
},
|
51
53
|
"preconstruct": {
|
52
54
|
"entrypoints": [
|