@talismn/balances 0.3.3 → 0.4.1
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 +38 -0
- package/dist/declarations/src/BalanceModule.d.ts +41 -19
- package/dist/declarations/src/helpers.d.ts +67 -14
- package/dist/declarations/src/types/balances.d.ts +49 -2
- package/dist/declarations/src/types/balancetypes.d.ts +9 -1
- package/dist/talismn-balances.cjs.dev.js +371 -108
- package/dist/talismn-balances.cjs.prod.js +371 -108
- package/dist/talismn-balances.esm.js +363 -109
- package/package.json +15 -15
@@ -1,13 +1,8 @@
|
|
1
1
|
import { Dexie } from 'dexie';
|
2
|
-
import { decorateStorage, StorageKey } from '@polkadot/types';
|
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
|
-
import memoize from 'lodash/memoize';
|
6
|
-
import { Memoize } from 'typescript-memoize';
|
7
|
-
|
8
|
-
//
|
9
|
-
// exported
|
10
|
-
//
|
11
6
|
|
12
7
|
// TODO: Document default balances module purpose/usage
|
13
8
|
const DefaultBalanceModule = type => ({
|
@@ -26,12 +21,15 @@ const DefaultBalanceModule = type => ({
|
|
26
21
|
async fetchEvmChainTokens() {
|
27
22
|
return Promise.resolve({});
|
28
23
|
},
|
29
|
-
async subscribeBalances(
|
24
|
+
async subscribeBalances(_, callback) {
|
30
25
|
callback(new Error("Balance subscriptions are not implemented in this module."));
|
31
26
|
return () => {};
|
32
27
|
},
|
33
28
|
async fetchBalances() {
|
34
29
|
throw new Error("Balance fetching is not implemented in this module.");
|
30
|
+
},
|
31
|
+
async transferToken() {
|
32
|
+
throw new Error("Token transfers are not implemented in this module.");
|
35
33
|
}
|
36
34
|
});
|
37
35
|
|
@@ -62,7 +60,7 @@ const db = new TalismanBalancesDatabase();
|
|
62
60
|
|
63
61
|
var packageJson = {
|
64
62
|
name: "@talismn/balances",
|
65
|
-
version: "0.
|
63
|
+
version: "0.4.1",
|
66
64
|
author: "Talisman",
|
67
65
|
homepage: "https://talisman.xyz",
|
68
66
|
license: "UNLICENSED",
|
@@ -81,36 +79,36 @@ var packageJson = {
|
|
81
79
|
"/plugins"
|
82
80
|
],
|
83
81
|
engines: {
|
84
|
-
node: ">=
|
82
|
+
node: ">=18"
|
85
83
|
},
|
86
84
|
scripts: {
|
87
85
|
test: "jest",
|
88
|
-
lint: "eslint
|
86
|
+
lint: "eslint src --max-warnings 0",
|
89
87
|
clean: "rm -rf dist && rm -rf .turbo rm -rf node_modules"
|
90
88
|
},
|
91
89
|
dependencies: {
|
92
|
-
"@talismn/chain-connector": "workspace
|
93
|
-
"@talismn/chain-connector-evm": "workspace
|
94
|
-
"@talismn/chaindata-provider": "workspace
|
95
|
-
"@talismn/token-rates": "workspace
|
96
|
-
"@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:*",
|
97
95
|
anylogger: "^1.0.11",
|
98
|
-
dexie: "^3.2.
|
99
|
-
lodash: "
|
100
|
-
"typescript-memoize": "^1.1.0"
|
96
|
+
dexie: "^3.2.3",
|
97
|
+
lodash: "4.17.21"
|
101
98
|
},
|
102
99
|
devDependencies: {
|
103
|
-
"@polkadot/types": "^
|
104
|
-
"@talismn/eslint-config": "workspace
|
105
|
-
"@talismn/tsconfig": "workspace
|
100
|
+
"@polkadot/types": "^10.1.4",
|
101
|
+
"@talismn/eslint-config": "workspace:*",
|
102
|
+
"@talismn/tsconfig": "workspace:*",
|
106
103
|
"@types/jest": "^27.5.1",
|
104
|
+
"@types/lodash": "^4.14.180",
|
107
105
|
eslint: "^8.4.0",
|
108
106
|
jest: "^28.1.0",
|
109
107
|
"ts-jest": "^28.0.2",
|
110
108
|
typescript: "^4.6.4"
|
111
109
|
},
|
112
110
|
peerDependencies: {
|
113
|
-
"@polkadot/types": "
|
111
|
+
"@polkadot/types": "10.x"
|
114
112
|
},
|
115
113
|
preconstruct: {
|
116
114
|
entrypoints: [
|
@@ -128,18 +126,114 @@ var packageJson = {
|
|
128
126
|
|
129
127
|
var log = anylogger(packageJson.name);
|
130
128
|
|
131
|
-
|
129
|
+
/**
|
130
|
+
* Wraps a BalanceModule's fetch/subscribe methods with a single `balances` method.
|
131
|
+
* This `balances` method will subscribe if a callback parameter is provided, or otherwise fetch.
|
132
|
+
*/
|
133
|
+
|
134
|
+
async function balances(balanceModule, addressesByToken, callback) {
|
132
135
|
// subscription request
|
133
|
-
if (callback !== undefined) return await balanceModule.subscribeBalances(
|
136
|
+
if (callback !== undefined) return await balanceModule.subscribeBalances(addressesByToken, callback);
|
134
137
|
|
135
138
|
// one-off request
|
136
|
-
return await balanceModule.fetchBalances(
|
139
|
+
return await balanceModule.fetchBalances(addressesByToken);
|
137
140
|
}
|
141
|
+
const createTypeRegistryCache = () => {
|
142
|
+
const typeRegistryCache = new Map();
|
143
|
+
const getOrCreateTypeRegistry = (chainId, metadataRpc) => {
|
144
|
+
// TODO: Delete cache when metadataRpc is different from last time
|
145
|
+
const cached = typeRegistryCache.get(chainId);
|
146
|
+
if (cached) return cached;
|
147
|
+
const typeRegistry = new TypeRegistry();
|
148
|
+
if (typeof metadataRpc === "string") {
|
149
|
+
try {
|
150
|
+
const metadata = new Metadata(typeRegistry, metadataRpc);
|
151
|
+
metadata.registry.setMetadata(metadata);
|
152
|
+
} catch (cause) {
|
153
|
+
log.warn(new Error(`Failed to set metadata for chain ${chainId}`, {
|
154
|
+
cause
|
155
|
+
}), cause);
|
156
|
+
}
|
157
|
+
}
|
158
|
+
typeRegistryCache.set(chainId, typeRegistry);
|
159
|
+
return typeRegistry;
|
160
|
+
};
|
161
|
+
return {
|
162
|
+
getOrCreateTypeRegistry
|
163
|
+
};
|
164
|
+
};
|
165
|
+
|
166
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
167
|
+
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
169
|
+
|
170
|
+
/**
|
171
|
+
* The following `Infer*` collection of generic types can be used when you want to
|
172
|
+
* extract one of the generic type arguments from an existing BalanceModule.
|
173
|
+
*
|
174
|
+
* For example, you might want to write a function which can accept any BalanceModule
|
175
|
+
* as an input, and then return the specific TokenType for that module:
|
176
|
+
* function getTokens<T extends AnyBalanceModule>(module: T): InferTokenType<T>
|
177
|
+
*
|
178
|
+
* Or for another example, you might want a function which can take any BalanceModule `type`
|
179
|
+
* string as input, and then return some data associated with that module with the correct type:
|
180
|
+
* function getChainMeta<T extends AnyBalanceModule>(type: InferModuleType<T>): InferChainMeta<T> | undefined
|
181
|
+
*/
|
182
|
+
|
183
|
+
/**
|
184
|
+
* Given a `moduleType` and a `chain` from a chaindataProvider, this function will find the chainMeta
|
185
|
+
* associated with the given balanceModule for the given chain.
|
186
|
+
*/
|
187
|
+
const findChainMeta = (moduleType, chain) => {
|
188
|
+
return (chain?.balanceMetadata ?? []).find(meta => meta.moduleType === moduleType)?.metadata;
|
189
|
+
};
|
138
190
|
const filterMirrorTokens = (balance, i, balances) => {
|
139
|
-
// TODO implement a mirrorOf property, which should be set from chaindata
|
140
191
|
const mirrorOf = balance.token?.mirrorOf;
|
141
192
|
return !mirrorOf || !balances.find(b => b.tokenId === mirrorOf);
|
142
193
|
};
|
194
|
+
const getValidSubscriptionIds = () => {
|
195
|
+
return new Set(localStorage.getItem("TalismanBalancesSubscriptionIds")?.split(",") ?? []);
|
196
|
+
};
|
197
|
+
const createSubscriptionId = () => {
|
198
|
+
// delete current id (if exists)
|
199
|
+
deleteSubscriptionId();
|
200
|
+
|
201
|
+
// create new id
|
202
|
+
const subscriptionId = Date.now().toString();
|
203
|
+
sessionStorage.setItem("TalismanBalancesSubscriptionId", subscriptionId);
|
204
|
+
|
205
|
+
// add to list of current ids
|
206
|
+
const subscriptionIds = getValidSubscriptionIds();
|
207
|
+
subscriptionIds.add(subscriptionId);
|
208
|
+
localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
|
209
|
+
return subscriptionId;
|
210
|
+
};
|
211
|
+
const deleteSubscriptionId = () => {
|
212
|
+
const subscriptionId = sessionStorage.getItem("TalismanBalancesSubscriptionId");
|
213
|
+
if (!subscriptionId) return;
|
214
|
+
const subscriptionIds = getValidSubscriptionIds();
|
215
|
+
subscriptionIds.delete(subscriptionId);
|
216
|
+
localStorage.setItem("TalismanBalancesSubscriptionIds", [...subscriptionIds].filter(Boolean).join(","));
|
217
|
+
};
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Sets all balance statuses from `live-${string}` to either `live` or `cached`
|
221
|
+
*/
|
222
|
+
const deriveStatuses = (validSubscriptionIds, balances) => balances.map(balance => {
|
223
|
+
if (balance.status === "live" || balance.status === "cache" || balance.status === "stale") return balance;
|
224
|
+
if (validSubscriptionIds.length < 1) return {
|
225
|
+
...balance,
|
226
|
+
status: "cache"
|
227
|
+
};
|
228
|
+
if (!validSubscriptionIds.includes(balance.status.slice("live-".length))) return {
|
229
|
+
...balance,
|
230
|
+
status: "cache"
|
231
|
+
};
|
232
|
+
return {
|
233
|
+
...balance,
|
234
|
+
status: "live"
|
235
|
+
};
|
236
|
+
});
|
143
237
|
|
144
238
|
/**
|
145
239
|
* Used by a variety of balance modules to help encode and decode substrate state calls.
|
@@ -150,7 +244,11 @@ class StorageHelper {
|
|
150
244
|
#module;
|
151
245
|
#method;
|
152
246
|
#parameters;
|
247
|
+
|
248
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
153
249
|
tags = null;
|
250
|
+
|
251
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
154
252
|
constructor(registry, module, method, ...parameters) {
|
155
253
|
this.#registry = registry;
|
156
254
|
this.#module = module;
|
@@ -184,6 +282,8 @@ class StorageHelper {
|
|
184
282
|
get parameters() {
|
185
283
|
return this.#parameters;
|
186
284
|
}
|
285
|
+
|
286
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
187
287
|
tag(tags) {
|
188
288
|
this.tags = tags;
|
189
289
|
return this;
|
@@ -222,34 +322,106 @@ class StorageHelper {
|
|
222
322
|
}
|
223
323
|
}
|
224
324
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
325
|
+
/**
|
326
|
+
* Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
|
327
|
+
*/
|
328
|
+
|
329
|
+
/**
|
330
|
+
* Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
|
331
|
+
*/
|
332
|
+
class RpcStateQueryHelper {
|
333
|
+
#chainConnector;
|
334
|
+
#queries;
|
335
|
+
constructor(chainConnector, queries) {
|
336
|
+
this.#chainConnector = chainConnector;
|
337
|
+
this.#queries = queries;
|
338
|
+
}
|
339
|
+
async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
|
340
|
+
const queriesByChain = groupBy(this.#queries, "chainId");
|
341
|
+
const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
|
342
|
+
const params = [queries.map(({
|
343
|
+
stateKey
|
344
|
+
}) => stateKey)];
|
345
|
+
const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
|
346
|
+
error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
|
347
|
+
}, timeout);
|
348
|
+
return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
|
349
|
+
});
|
350
|
+
return () => subscriptions.forEach(unsubscribe => unsubscribe());
|
351
|
+
}
|
352
|
+
async fetch(method = "state_queryStorageAt") {
|
353
|
+
const queriesByChain = groupBy(this.#queries, "chainId");
|
354
|
+
const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
|
355
|
+
const params = [queries.map(({
|
356
|
+
stateKey
|
357
|
+
}) => stateKey)];
|
358
|
+
const result = (await this.#chainConnector.send(chainId, method, params))[0];
|
359
|
+
return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
|
360
|
+
}));
|
361
|
+
return resultsByChain.flatMap(result => result);
|
362
|
+
}
|
363
|
+
#distributeChangesToQueryDecoders(chainId, result) {
|
364
|
+
if (typeof result !== "object" || result === null) return [];
|
365
|
+
if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
|
366
|
+
if (!Array.isArray(result.changes)) return [];
|
367
|
+
return result.changes.flatMap(([reference, change]) => {
|
368
|
+
if (typeof reference !== "string") {
|
369
|
+
log.warn(`Received non-string reference in RPC result: ${reference}`);
|
370
|
+
return [];
|
371
|
+
}
|
372
|
+
if (typeof change !== "string" && change !== null) {
|
373
|
+
log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
|
374
|
+
return [];
|
375
|
+
}
|
376
|
+
const query = this.#queries.find(({
|
377
|
+
chainId: cId,
|
378
|
+
stateKey
|
379
|
+
}) => cId === chainId && stateKey === reference);
|
380
|
+
if (!query) {
|
381
|
+
log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
|
382
|
+
stateKey
|
383
|
+
}) => stateKey)}`);
|
384
|
+
return [];
|
385
|
+
}
|
386
|
+
return [query.decodeResult(change)];
|
387
|
+
});
|
388
|
+
}
|
247
389
|
}
|
248
390
|
|
391
|
+
/**
|
392
|
+
* `BalanceTypes` is an automatically determined sub-selection of `PluginBalanceTypes`.
|
393
|
+
*
|
394
|
+
* It is the same list, but with any invalid `BalanceType` definitions filtered out.
|
395
|
+
*/
|
396
|
+
|
397
|
+
/**
|
398
|
+
* The `BalanceJson` sum type, which is a union of all of the possible `BalanceTypes`.
|
399
|
+
*
|
400
|
+
* Each variant comes from a plugin in use by the consuming app.
|
401
|
+
*
|
402
|
+
* For example, in an app with the `substrate-native`, `evm-native`, `substrate-orml` and `evm-erc20` plugins:
|
403
|
+
*
|
404
|
+
* type BalanceJson = SubNativeBalance | EvmNativeBalance | SubOrmlBalance | EvmErc20Balance
|
405
|
+
*
|
406
|
+
* If `BalanceTypes` is empty then `BalanceJson` will fall back to the common `IBalance` interface, which every balance must implement.
|
407
|
+
*/
|
408
|
+
|
409
|
+
/** A collection of `BalanceJson` objects */
|
410
|
+
|
411
|
+
const BalanceStatusLive = subscriptionId => `live-${subscriptionId}`;
|
412
|
+
|
413
|
+
/** `IBalance` is a common interface which all balance types must implement. */
|
414
|
+
|
415
|
+
/** An unlabelled amount of a balance */
|
416
|
+
|
417
|
+
/** A labelled amount of a balance */
|
418
|
+
|
419
|
+
/** A labelled locked amount of a balance */
|
420
|
+
|
249
421
|
function excludeFromTransferableAmount(locks) {
|
250
422
|
if (typeof locks === "string") return BigInt(locks);
|
251
423
|
if (!Array.isArray(locks)) locks = [locks];
|
252
|
-
return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock),
|
424
|
+
return locks.filter(lock => lock.includeInTransferable !== true).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), 0n);
|
253
425
|
}
|
254
426
|
function excludeFromFeePayableLocks(locks) {
|
255
427
|
if (typeof locks === "string") return [];
|
@@ -260,31 +432,29 @@ function excludeFromFeePayableLocks(locks) {
|
|
260
432
|
/** A labelled extra amount of a balance */
|
261
433
|
|
262
434
|
function includeInTotalExtraAmount(extra) {
|
263
|
-
if (!extra) return
|
435
|
+
if (!extra) return 0n;
|
264
436
|
if (!Array.isArray(extra)) extra = [extra];
|
265
|
-
return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b,
|
437
|
+
return extra.filter(extra => extra.includeInTotal).map(extra => BigInt(extra.amount)).reduce((a, b) => a + b, 0n);
|
266
438
|
}
|
267
439
|
|
268
440
|
/** Used by plugins to help define their custom `BalanceType` */
|
269
441
|
|
270
|
-
var _dec, _dec2, _dec3, _class, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _class2, _dec11, _class3, _dec12, _dec13, _dec14, _dec15, _dec16, _dec17, _dec18, _class4;
|
271
|
-
|
272
442
|
/**
|
273
443
|
* Have the importing library define its Token and BalanceJson enums (as a sum type of all plugins) and pass them into some
|
274
444
|
* internal global typescript context, which is then picked up on by this module.
|
275
445
|
*/
|
276
446
|
|
277
447
|
/** A utility type used to extract the underlying `BalanceType` of a specific source from a generalised `BalanceJson` */
|
278
|
-
|
448
|
+
/** TODO: Remove this in favour of a frontend-friendly `ChaindataProvider` */
|
279
449
|
/**
|
280
450
|
* A collection of balances.
|
281
451
|
*/
|
282
|
-
|
452
|
+
class Balances {
|
283
453
|
//
|
284
454
|
// Properties
|
285
455
|
//
|
286
456
|
|
287
|
-
#balances =
|
457
|
+
#balances = [];
|
288
458
|
|
289
459
|
//
|
290
460
|
// Methods
|
@@ -292,7 +462,7 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
292
462
|
|
293
463
|
constructor(balances, hydrate) {
|
294
464
|
// handle Balances (convert to Balance[])
|
295
|
-
if (balances instanceof Balances) return new Balances(
|
465
|
+
if (balances instanceof Balances) return new Balances(balances.each, hydrate);
|
296
466
|
|
297
467
|
// handle Balance (convert to Balance[])
|
298
468
|
if (balances instanceof Balance) return new Balances([balances], hydrate);
|
@@ -307,19 +477,19 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
307
477
|
if (!isArrayOf(balances, Balance)) return new Balances(balances.map(storage => new Balance(storage)), hydrate);
|
308
478
|
|
309
479
|
// handle Balance[]
|
310
|
-
this.#balances =
|
480
|
+
this.#balances = balances;
|
311
481
|
if (hydrate !== undefined) this.hydrate(hydrate);
|
312
482
|
}
|
313
483
|
|
314
484
|
/**
|
315
485
|
* Calling toJSON on a collection of balances will return the underlying BalanceJsonList.
|
316
486
|
*/
|
317
|
-
toJSON = () => Object.fromEntries(
|
487
|
+
toJSON = () => Object.fromEntries(this.#balances.map(balance => {
|
318
488
|
try {
|
319
|
-
return [id, balance.toJSON()];
|
489
|
+
return [balance.id, balance.toJSON()];
|
320
490
|
} catch (error) {
|
321
491
|
log.error("Failed to convert balance to JSON", error, {
|
322
|
-
id,
|
492
|
+
id: balance.id,
|
323
493
|
balance
|
324
494
|
});
|
325
495
|
return null;
|
@@ -339,7 +509,7 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
339
509
|
*/
|
340
510
|
[Symbol.iterator] = () =>
|
341
511
|
// Create an array of the balances in this collection and return the result of its iterator.
|
342
|
-
|
512
|
+
this.#balances[Symbol.iterator]();
|
343
513
|
|
344
514
|
/**
|
345
515
|
* Hydrates all balances in this collection.
|
@@ -347,7 +517,7 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
347
517
|
* @param sources - The sources to hydrate from.
|
348
518
|
*/
|
349
519
|
hydrate = sources => {
|
350
|
-
|
520
|
+
this.#balances.map(balance => balance.hydrate(sources));
|
351
521
|
};
|
352
522
|
|
353
523
|
/**
|
@@ -356,7 +526,7 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
356
526
|
* @param id - The id of the balance to fetch.
|
357
527
|
* @returns The balance if one exists, or none.
|
358
528
|
*/
|
359
|
-
get = id => this.#balances
|
529
|
+
get = id => this.#balances.find(balance => balance.id === id) ?? null;
|
360
530
|
|
361
531
|
/**
|
362
532
|
* Retrieve balances from this collection by search query.
|
@@ -375,6 +545,27 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
375
545
|
return new Balances([...this].filter(filter));
|
376
546
|
};
|
377
547
|
|
548
|
+
/**
|
549
|
+
* Filters this collection to exclude token balances where the token has a `mirrorOf` field
|
550
|
+
* and another balance exists in this collection for the token specified by the `mirrorOf` field.
|
551
|
+
*/
|
552
|
+
filterMirrorTokens = () => new Balances([...this].filter(filterMirrorTokens));
|
553
|
+
|
554
|
+
/**
|
555
|
+
* Filters this collection to only include balances which are not zero.
|
556
|
+
*/
|
557
|
+
filterNonZero = type => {
|
558
|
+
const filter = balance => balance[type].planck > 0n;
|
559
|
+
return this.find(filter);
|
560
|
+
};
|
561
|
+
/**
|
562
|
+
* Filters this collection to only include balances which are not zero AND have a fiat conversion rate.
|
563
|
+
*/
|
564
|
+
filterNonZeroFiat = (type, currency) => {
|
565
|
+
const filter = balance => (balance[type].fiat(currency) ?? 0) > 0;
|
566
|
+
return this.find(filter);
|
567
|
+
};
|
568
|
+
|
378
569
|
/**
|
379
570
|
* Add some balances to this collection.
|
380
571
|
* Added balances take priority over existing balances.
|
@@ -389,10 +580,8 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
389
580
|
if (balances instanceof Balance) return this.add(new Balances(balances));
|
390
581
|
|
391
582
|
// merge balances
|
392
|
-
const mergedBalances =
|
393
|
-
|
394
|
-
};
|
395
|
-
[...balances].forEach(balance => mergedBalances[balance.id] = balance);
|
583
|
+
const mergedBalances = Object.fromEntries(this.#balances.map(balance => [balance.id, balance]));
|
584
|
+
balances.each.forEach(balance => mergedBalances[balance.id] = balance);
|
396
585
|
|
397
586
|
// return new balances
|
398
587
|
return new Balances(Object.values(mergedBalances));
|
@@ -410,25 +599,23 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
410
599
|
// handle single id
|
411
600
|
if (!Array.isArray(ids)) return this.remove([ids]);
|
412
601
|
|
413
|
-
// merge balances
|
414
|
-
|
415
|
-
...this.#balances
|
416
|
-
};
|
417
|
-
ids.forEach(id => delete removedBalances[id]);
|
418
|
-
|
419
|
-
// return new balances
|
420
|
-
return new Balances(Object.values(removedBalances));
|
602
|
+
// merge and return new balances
|
603
|
+
return new Balances(this.#balances.filter(balance => !ids.includes(balance.id)));
|
421
604
|
};
|
422
605
|
|
423
606
|
// TODO: Add some more useful aggregator methods
|
424
607
|
|
608
|
+
get each() {
|
609
|
+
return [...this];
|
610
|
+
}
|
611
|
+
|
425
612
|
/**
|
426
613
|
* Get an array of balances in this collection, sorted by chain sortIndex.
|
427
614
|
*
|
428
615
|
* @returns A sorted array of the balances in this collection.
|
429
616
|
*/
|
430
617
|
get sorted() {
|
431
|
-
return [...this].sort((a, b) => ((a.chain || a.evmNetwork)?.sortIndex
|
618
|
+
return [...this].sort((a, b) => ((a.chain || a.evmNetwork)?.sortIndex ?? Number.MAX_SAFE_INTEGER) - ((b.chain || b.evmNetwork)?.sortIndex ?? Number.MAX_SAFE_INTEGER));
|
432
619
|
}
|
433
620
|
|
434
621
|
/**
|
@@ -451,12 +638,12 @@ let Balances = (_dec = Memoize(), _dec2 = Memoize(), _dec3 = Memoize(), (_class
|
|
451
638
|
get sum() {
|
452
639
|
return new SumBalancesFormatter(this);
|
453
640
|
}
|
454
|
-
}
|
641
|
+
}
|
455
642
|
|
456
643
|
/**
|
457
644
|
* An individual balance.
|
458
645
|
*/
|
459
|
-
|
646
|
+
class Balance {
|
460
647
|
//
|
461
648
|
// Properties
|
462
649
|
//
|
@@ -470,7 +657,6 @@ let Balance = (_dec4 = Memoize(), _dec5 = Memoize(), _dec6 = Memoize(), _dec7 =
|
|
470
657
|
//
|
471
658
|
|
472
659
|
constructor(storage, hydrate) {
|
473
|
-
this.#format = memoize(this.#format);
|
474
660
|
this.#storage = storage;
|
475
661
|
if (hydrate !== undefined) this.hydrate(hydrate);
|
476
662
|
}
|
@@ -502,13 +688,14 @@ let Balance = (_dec4 = Memoize(), _dec5 = Memoize(), _dec6 = Memoize(), _dec7 =
|
|
502
688
|
get id() {
|
503
689
|
const {
|
504
690
|
source,
|
691
|
+
subSource,
|
505
692
|
address,
|
506
693
|
chainId,
|
507
694
|
evmNetworkId,
|
508
695
|
tokenId
|
509
696
|
} = this.#storage;
|
510
697
|
const locationId = chainId !== undefined ? chainId : evmNetworkId;
|
511
|
-
return
|
698
|
+
return [source, address, locationId, tokenId, subSource].filter(Boolean).join("-");
|
512
699
|
}
|
513
700
|
get source() {
|
514
701
|
return this.#storage.source;
|
@@ -557,17 +744,43 @@ let Balance = (_dec4 = Memoize(), _dec5 = Memoize(), _dec6 = Memoize(), _dec7 =
|
|
557
744
|
}
|
558
745
|
/** The non-reserved balance of this token. Includes the frozen amount. Is included in the total. */
|
559
746
|
get free() {
|
560
|
-
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,
|
747
|
+
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"));
|
561
748
|
}
|
562
749
|
/** The reserved balance of this token. Is included in the total. */
|
563
750
|
get reserved() {
|
564
|
-
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,
|
751
|
+
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"));
|
752
|
+
}
|
753
|
+
get reserves() {
|
754
|
+
return (Array.isArray(this.#storage.reserves) ? this.#storage.reserves : [this.#storage.reserves]).flatMap(reserve => {
|
755
|
+
if (reserve === undefined) return [];
|
756
|
+
if (typeof reserve === "string") return {
|
757
|
+
label: "reserved",
|
758
|
+
amount: this.#format(reserve)
|
759
|
+
};
|
760
|
+
return {
|
761
|
+
...reserve,
|
762
|
+
amount: this.#format(reserve.amount)
|
763
|
+
};
|
764
|
+
});
|
565
765
|
}
|
566
766
|
/** The frozen balance of this token. Is included in the free amount. */
|
567
767
|
get locked() {
|
568
|
-
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),
|
768
|
+
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"));
|
769
|
+
}
|
770
|
+
get locks() {
|
771
|
+
return (Array.isArray(this.#storage.locks) ? this.#storage.locks : [this.#storage.locks]).flatMap(lock => {
|
772
|
+
if (lock === undefined) return [];
|
773
|
+
if (typeof lock === "string") return {
|
774
|
+
label: "other",
|
775
|
+
amount: this.#format(lock)
|
776
|
+
};
|
777
|
+
return {
|
778
|
+
...lock,
|
779
|
+
amount: this.#format(lock.amount)
|
780
|
+
};
|
781
|
+
});
|
569
782
|
}
|
570
|
-
/** @
|
783
|
+
/** @deprecated Use balance.locked */
|
571
784
|
get frozen() {
|
572
785
|
return this.locked;
|
573
786
|
}
|
@@ -580,7 +793,7 @@ let Balance = (_dec4 = Memoize(), _dec5 = Memoize(), _dec6 = Memoize(), _dec7 =
|
|
580
793
|
const excludeAmount = excludeFromTransferableAmount(this.#storage.locks);
|
581
794
|
|
582
795
|
// subtract the lock from the free amount (but don't go below 0)
|
583
|
-
return this.#format(BigMath.max(this.free.planck - excludeAmount,
|
796
|
+
return this.#format(BigMath.max(this.free.planck - excludeAmount, 0n));
|
584
797
|
}
|
585
798
|
/** The feePayable balance of this token. Is generally the free amount - the feeFrozen amount. */
|
586
799
|
get feePayable() {
|
@@ -588,13 +801,13 @@ let Balance = (_dec4 = Memoize(), _dec5 = Memoize(), _dec6 = Memoize(), _dec7 =
|
|
588
801
|
if (!this.#storage.locks) return this.free;
|
589
802
|
|
590
803
|
// find the largest lock which can't be used to pay tx fees
|
591
|
-
const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock),
|
804
|
+
const excludeAmount = excludeFromFeePayableLocks(this.#storage.locks).map(lock => BigInt(lock.amount)).reduce((max, lock) => BigMath.max(max, lock), 0n);
|
592
805
|
|
593
806
|
// subtract the lock from the free amount (but don't go below 0)
|
594
|
-
return this.#format(BigMath.max(this.free.planck - excludeAmount,
|
807
|
+
return this.#format(BigMath.max(this.free.planck - excludeAmount, 0n));
|
595
808
|
}
|
596
|
-
}
|
597
|
-
|
809
|
+
}
|
810
|
+
class BalanceFormatter {
|
598
811
|
#planck;
|
599
812
|
#decimals;
|
600
813
|
#fiatRatios;
|
@@ -602,7 +815,6 @@ let BalanceFormatter = (_dec11 = Memoize(), (_class3 = class BalanceFormatter {
|
|
602
815
|
this.#planck = typeof planck === "bigint" ? planck.toString() : planck ?? "0";
|
603
816
|
this.#decimals = decimals || 0;
|
604
817
|
this.#fiatRatios = fiatRatios || null;
|
605
|
-
this.fiat = memoize(this.fiat);
|
606
818
|
}
|
607
819
|
toJSON = () => this.#planck;
|
608
820
|
get planck() {
|
@@ -617,26 +829,66 @@ let BalanceFormatter = (_dec11 = Memoize(), (_class3 = class BalanceFormatter {
|
|
617
829
|
if (!ratio) return null;
|
618
830
|
return parseFloat(this.tokens) * ratio;
|
619
831
|
}
|
620
|
-
}
|
621
|
-
|
832
|
+
}
|
833
|
+
class PlanckSumBalancesFormatter {
|
834
|
+
#balances;
|
835
|
+
constructor(balances) {
|
836
|
+
this.#balances = balances;
|
837
|
+
}
|
838
|
+
#sum = balanceField => {
|
839
|
+
// a function to get a planck amount from a balance
|
840
|
+
const planck = balance => balance[balanceField].planck ?? 0n;
|
841
|
+
return this.#balances.filterMirrorTokens().each.reduce(
|
842
|
+
// add the total amount to the planck amount of each balance
|
843
|
+
(total, balance) => total + planck(balance),
|
844
|
+
// start with a total of 0
|
845
|
+
0n);
|
846
|
+
};
|
847
|
+
|
848
|
+
/**
|
849
|
+
* The total balance of these tokens. Includes the free and the reserved amount.
|
850
|
+
*/
|
851
|
+
get total() {
|
852
|
+
return this.#sum("total");
|
853
|
+
}
|
854
|
+
/** The non-reserved balance of these tokens. Includes the frozen amount. Is included in the total. */
|
855
|
+
get free() {
|
856
|
+
return this.#sum("free");
|
857
|
+
}
|
858
|
+
/** The reserved balance of these tokens. Is included in the total. */
|
859
|
+
get reserved() {
|
860
|
+
return this.#sum("reserved");
|
861
|
+
}
|
862
|
+
/** The frozen balance of these tokens. Is included in the free amount. */
|
863
|
+
get locked() {
|
864
|
+
return this.#sum("locked");
|
865
|
+
}
|
866
|
+
/** @deprecated Use balances.locked */
|
867
|
+
get frozen() {
|
868
|
+
return this.locked;
|
869
|
+
}
|
870
|
+
/** The transferable balance of these tokens. Is generally the free amount - the miscFrozen amount. */
|
871
|
+
get transferable() {
|
872
|
+
return this.#sum("transferable");
|
873
|
+
}
|
874
|
+
/** The feePayable balance of these tokens. Is generally the free amount - the feeFrozen amount. */
|
875
|
+
get feePayable() {
|
876
|
+
return this.#sum("feePayable");
|
877
|
+
}
|
878
|
+
}
|
879
|
+
class FiatSumBalancesFormatter {
|
622
880
|
#balances;
|
623
881
|
#currency;
|
624
882
|
constructor(balances, currency) {
|
625
883
|
this.#balances = balances;
|
626
884
|
this.#currency = currency;
|
627
|
-
this.#sum = memoize(this.#sum);
|
628
885
|
}
|
629
886
|
#sum = balanceField => {
|
630
887
|
// a function to get a fiat amount from a balance
|
631
|
-
const fiat = balance => balance[balanceField].fiat(this.#currency)
|
632
|
-
|
633
|
-
//
|
634
|
-
|
635
|
-
return [...this.#balances].filter(filterMirrorTokens).reduce((total, balance) => sum(
|
636
|
-
// add the total amount...
|
637
|
-
total,
|
638
|
-
// ...to the fiat amount of each balance
|
639
|
-
fiat(balance)),
|
888
|
+
const fiat = balance => balance[balanceField].fiat(this.#currency) ?? 0;
|
889
|
+
return this.#balances.filterMirrorTokens().each.reduce(
|
890
|
+
// add the total amount to the fiat amount of each balance
|
891
|
+
(total, balance) => total + fiat(balance),
|
640
892
|
// start with a total of 0
|
641
893
|
0);
|
642
894
|
};
|
@@ -659,7 +911,7 @@ let FiatSumBalancesFormatter = (_dec12 = Memoize(), _dec13 = Memoize(), _dec14 =
|
|
659
911
|
get locked() {
|
660
912
|
return this.#sum("locked");
|
661
913
|
}
|
662
|
-
/** @deprecated
|
914
|
+
/** @deprecated Use balances.locked */
|
663
915
|
get frozen() {
|
664
916
|
return this.locked;
|
665
917
|
}
|
@@ -671,16 +923,18 @@ let FiatSumBalancesFormatter = (_dec12 = Memoize(), _dec13 = Memoize(), _dec14 =
|
|
671
923
|
get feePayable() {
|
672
924
|
return this.#sum("feePayable");
|
673
925
|
}
|
674
|
-
}
|
926
|
+
}
|
675
927
|
class SumBalancesFormatter {
|
676
928
|
#balances;
|
677
929
|
constructor(balances) {
|
678
930
|
this.#balances = balances;
|
679
|
-
|
931
|
+
}
|
932
|
+
get planck() {
|
933
|
+
return new PlanckSumBalancesFormatter(this.#balances);
|
680
934
|
}
|
681
935
|
fiat(currency) {
|
682
936
|
return new FiatSumBalancesFormatter(this.#balances, currency);
|
683
937
|
}
|
684
938
|
}
|
685
939
|
|
686
|
-
export { Balance, BalanceFormatter, Balances, DefaultBalanceModule, FiatSumBalancesFormatter, StorageHelper, SumBalancesFormatter, TalismanBalancesDatabase, balances, db, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterMirrorTokens, includeInTotalExtraAmount };
|
940
|
+
export { Balance, BalanceFormatter, BalanceStatusLive, Balances, DefaultBalanceModule, FiatSumBalancesFormatter, PlanckSumBalancesFormatter, RpcStateQueryHelper, StorageHelper, SumBalancesFormatter, TalismanBalancesDatabase, balances, createSubscriptionId, createTypeRegistryCache, db, deleteSubscriptionId, deriveStatuses, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterMirrorTokens, findChainMeta, getValidSubscriptionIds, includeInTotalExtraAmount };
|