@talismn/balances 0.0.0-pr2075-20250708125508 → 0.0.0-pr2075-20250708151500
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/dist/declarations/src/BalancesProvider.d.ts +5 -1
- package/dist/declarations/src/modules/IBalanceModule.d.ts +1 -1
- package/dist/declarations/src/modules/SubstrateNativeModule/util/sortChains.d.ts +1 -1
- package/dist/declarations/src/modules/shared/types.d.ts +1 -4
- package/dist/declarations/src/modules/substrate-assets/buildQueries.d.ts +6 -0
- package/dist/declarations/src/modules/substrate-foreignassets/buildQueries.d.ts +6 -0
- package/dist/declarations/src/modules/substrate-native/bittensor/getSubtensorStakingBalances.d.ts +3 -2
- package/dist/declarations/src/modules/substrate-native/queries/buildBaseQueries.d.ts +2 -4
- package/dist/declarations/src/modules/substrate-native/queries/buildNomPoolQueries.d.ts +2 -4
- package/dist/declarations/src/modules/substrate-tokens/buildQueries.d.ts +6 -0
- package/dist/declarations/src/modules/util/rpcQueryPack.d.ts +1 -2
- package/dist/talismn-balances.cjs.dev.js +705 -670
- package/dist/talismn-balances.cjs.prod.js +705 -670
- package/dist/talismn-balances.esm.js +707 -672
- package/package.json +8 -8
@@ -11,13 +11,13 @@ import pako from 'pako';
|
|
11
11
|
import { parseAbi, erc20Abi, getContract, ContractFunctionExecutionError, hexToString, erc20Abi_bytes32, encodeFunctionData, isHex, hexToBigInt, withRetry } from 'viem';
|
12
12
|
import { assign, omit, isEqual, fromPairs, toPairs, keys, keyBy, uniq, values, groupBy as groupBy$1 } from 'lodash';
|
13
13
|
import z from 'zod/v4';
|
14
|
-
import { Observable, distinctUntilChanged,
|
14
|
+
import { of, Observable, distinctUntilChanged, map, timer, switchMap, from, firstValueFrom, combineLatest, scan, share, switchAll, mergeMap, toArray, interval, startWith, exhaustMap, BehaviorSubject, debounceTime, takeUntil, withLatestFrom, concatMap, filter, tap } from 'rxjs';
|
15
15
|
import isEqual$1 from 'lodash/isEqual';
|
16
|
-
import {
|
17
|
-
import { Metadata, TypeRegistry } from '@polkadot/types';
|
18
|
-
import groupBy from 'lodash/groupBy';
|
16
|
+
import { decodeScale, parseMetadataRpc, getStorageKeyPrefix, compactMetadata, encodeMetadata, toHex, unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, papiParse, papiStringify, getMetadataVersion, encodeStateKey } from '@talismn/scale';
|
19
17
|
import { mergeUint8, toHex as toHex$1 } from '@polkadot-api/utils';
|
20
18
|
import { Binary, Enum, AccountId } from 'polkadot-api';
|
19
|
+
import { Metadata, TypeRegistry } from '@polkadot/types';
|
20
|
+
import groupBy from 'lodash/groupBy';
|
21
21
|
import upperFirst from 'lodash/upperFirst';
|
22
22
|
import { u32, Struct, u128 } from 'scale-ts';
|
23
23
|
import { Abi } from '@polkadot/api-contract';
|
@@ -1540,6 +1540,10 @@ const fetchBalances$c = async ({
|
|
1540
1540
|
tokensWithAddresses,
|
1541
1541
|
connector
|
1542
1542
|
}) => {
|
1543
|
+
if (!tokensWithAddresses.length) return {
|
1544
|
+
success: [],
|
1545
|
+
errors: []
|
1546
|
+
};
|
1543
1547
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1544
1548
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
1545
1549
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -1766,12 +1770,16 @@ const getTransferCallData$8 = ({
|
|
1766
1770
|
};
|
1767
1771
|
};
|
1768
1772
|
|
1769
|
-
const SUBSCRIPTION_INTERVAL$
|
1773
|
+
const SUBSCRIPTION_INTERVAL$4 = 6_000;
|
1770
1774
|
const subscribeBalances$8 = ({
|
1771
1775
|
networkId,
|
1772
1776
|
tokensWithAddresses,
|
1773
1777
|
connector
|
1774
1778
|
}) => {
|
1779
|
+
if (!tokensWithAddresses.length) return of({
|
1780
|
+
success: [],
|
1781
|
+
errors: []
|
1782
|
+
});
|
1775
1783
|
return new Observable(subscriber => {
|
1776
1784
|
const abortController = new AbortController();
|
1777
1785
|
const poll = async () => {
|
@@ -1784,7 +1792,7 @@ const subscribeBalances$8 = ({
|
|
1784
1792
|
});
|
1785
1793
|
if (abortController.signal.aborted) return;
|
1786
1794
|
subscriber.next(balances);
|
1787
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
1795
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
1788
1796
|
} catch (error) {
|
1789
1797
|
log.error("Error", {
|
1790
1798
|
module: MODULE_TYPE$8,
|
@@ -1830,6 +1838,10 @@ const fetchBalances$b = async ({
|
|
1830
1838
|
tokensWithAddresses,
|
1831
1839
|
connector
|
1832
1840
|
}) => {
|
1841
|
+
if (!tokensWithAddresses.length) return {
|
1842
|
+
success: [],
|
1843
|
+
errors: []
|
1844
|
+
};
|
1833
1845
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1834
1846
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
1835
1847
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -1980,12 +1992,16 @@ const getTransferCallData$7 = ({
|
|
1980
1992
|
};
|
1981
1993
|
};
|
1982
1994
|
|
1983
|
-
const SUBSCRIPTION_INTERVAL$
|
1995
|
+
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
1984
1996
|
const subscribeBalances$7 = ({
|
1985
1997
|
networkId,
|
1986
1998
|
tokensWithAddresses,
|
1987
1999
|
connector
|
1988
2000
|
}) => {
|
2001
|
+
if (!tokensWithAddresses.length) return of({
|
2002
|
+
success: [],
|
2003
|
+
errors: []
|
2004
|
+
});
|
1989
2005
|
return new Observable(subscriber => {
|
1990
2006
|
const abortController = new AbortController();
|
1991
2007
|
const poll = async () => {
|
@@ -1998,7 +2014,7 @@ const subscribeBalances$7 = ({
|
|
1998
2014
|
});
|
1999
2015
|
if (abortController.signal.aborted) return;
|
2000
2016
|
subscriber.next(balances);
|
2001
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
2017
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
2002
2018
|
} catch (error) {
|
2003
2019
|
log.error("Error", {
|
2004
2020
|
module: MODULE_TYPE$7,
|
@@ -2039,6 +2055,10 @@ const fetchBalances$a = async ({
|
|
2039
2055
|
tokensWithAddresses,
|
2040
2056
|
connector
|
2041
2057
|
}) => {
|
2058
|
+
if (!tokensWithAddresses.length) return {
|
2059
|
+
success: [],
|
2060
|
+
errors: []
|
2061
|
+
};
|
2042
2062
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
2043
2063
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
2044
2064
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -2313,12 +2333,16 @@ const getTransferCallData$6 = ({
|
|
2313
2333
|
};
|
2314
2334
|
};
|
2315
2335
|
|
2316
|
-
const SUBSCRIPTION_INTERVAL$
|
2336
|
+
const SUBSCRIPTION_INTERVAL$2 = 6_000;
|
2317
2337
|
const subscribeBalances$6 = ({
|
2318
2338
|
networkId,
|
2319
2339
|
tokensWithAddresses,
|
2320
2340
|
connector
|
2321
2341
|
}) => {
|
2342
|
+
if (!tokensWithAddresses.length) return of({
|
2343
|
+
success: [],
|
2344
|
+
errors: []
|
2345
|
+
});
|
2322
2346
|
return new Observable(subscriber => {
|
2323
2347
|
const abortController = new AbortController();
|
2324
2348
|
const poll = async () => {
|
@@ -2331,7 +2355,7 @@ const subscribeBalances$6 = ({
|
|
2331
2355
|
});
|
2332
2356
|
if (abortController.signal.aborted) return;
|
2333
2357
|
subscriber.next(balances);
|
2334
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
2358
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
|
2335
2359
|
} catch (error) {
|
2336
2360
|
log.error("Error", {
|
2337
2361
|
module: MODULE_TYPE$6,
|
@@ -3195,273 +3219,138 @@ async function getPoolBalance(publicClient, contractAddress, accountAddress) {
|
|
3195
3219
|
const MODULE_TYPE$5 = SubAssetsTokenSchema.shape.type.value;
|
3196
3220
|
const PLATFORM$5 = SubAssetsTokenSchema.shape.platform.value;
|
3197
3221
|
|
3198
|
-
|
3199
|
-
|
3200
|
-
|
3201
|
-
|
3222
|
+
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
3223
|
+
const allStateKeys = queries.flatMap(({
|
3224
|
+
stateKeys
|
3225
|
+
}) => stateKeys).filter(isNotNil);
|
3202
3226
|
|
3203
|
-
|
3204
|
-
|
3205
|
-
|
3206
|
-
|
3207
|
-
}
|
3227
|
+
// doing a query without keys would throw an error => return early
|
3228
|
+
if (!allStateKeys.length) return queries.map(({
|
3229
|
+
stateKeys,
|
3230
|
+
decodeResult
|
3231
|
+
}) => decodeResult(stateKeys.map(() => null)));
|
3232
|
+
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
3233
|
+
return decodeRpcQueryPack(queries, result);
|
3234
|
+
};
|
3235
|
+
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
3236
|
+
const allStateKeys = queries.flatMap(({
|
3237
|
+
stateKeys
|
3238
|
+
}) => stateKeys).filter(isNotNil);
|
3208
3239
|
|
3209
|
-
//
|
3210
|
-
return
|
3211
|
-
|
3240
|
+
// doing a query without keys would throw an error => return early
|
3241
|
+
if (!allStateKeys.length) return of(queries.map(({
|
3242
|
+
stateKeys,
|
3243
|
+
decodeResult
|
3244
|
+
}) => decodeResult(stateKeys.map(() => null))));
|
3245
|
+
return new Observable(subscriber => {
|
3246
|
+
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
3247
|
+
if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
|
3248
|
+
}, timeout);
|
3249
|
+
return () => {
|
3250
|
+
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
3251
|
+
};
|
3252
|
+
});
|
3253
|
+
};
|
3254
|
+
const decodeRpcQueryPack = (queries, result) => {
|
3255
|
+
return queries.reduce((acc, {
|
3256
|
+
stateKeys,
|
3257
|
+
decodeResult
|
3258
|
+
}) => {
|
3259
|
+
const changes = stateKeys.map(stateKey => {
|
3260
|
+
if (!stateKey) return null;
|
3261
|
+
const change = result.changes.find(([key]) => key === stateKey);
|
3262
|
+
if (!change) return null;
|
3263
|
+
return change[1];
|
3264
|
+
});
|
3265
|
+
acc.push(decodeResult(changes));
|
3266
|
+
return acc;
|
3267
|
+
}, []);
|
3268
|
+
};
|
3212
3269
|
|
3213
|
-
|
3214
|
-
const
|
3215
|
-
|
3216
|
-
|
3217
|
-
|
3218
|
-
|
3219
|
-
|
3220
|
-
|
3221
|
-
|
3222
|
-
|
3223
|
-
|
3224
|
-
|
3225
|
-
const metadata = unifyMetadata(decAnyMetadata(miniMetadata.data));
|
3226
|
-
try {
|
3227
|
-
const scaleBuilder = getDynamicBuilder(getLookupFn(metadata));
|
3228
|
-
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3229
|
-
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3230
|
-
chainId
|
3231
|
-
}) : moduleMethodOrFn;
|
3232
|
-
try {
|
3233
|
-
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3234
|
-
} catch (cause) {
|
3235
|
-
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3236
|
-
return [];
|
3237
|
-
}
|
3238
|
-
}));
|
3239
|
-
return [[chainId, builtCoders]];
|
3240
|
-
} catch (cause) {
|
3241
|
-
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3242
|
-
return [];
|
3243
|
-
}
|
3244
|
-
}));
|
3245
|
-
// type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
|
3270
|
+
const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
|
3271
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3272
|
+
storage: ["Assets", "Account"]
|
3273
|
+
});
|
3274
|
+
return balanceDefs.map(({
|
3275
|
+
token,
|
3276
|
+
address
|
3277
|
+
}) => {
|
3278
|
+
const scaleCoder = networkStorageCoders?.storage;
|
3279
|
+
const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
|
3280
|
+
// Asset Hub
|
3281
|
+
tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
|
3246
3282
|
|
3247
|
-
|
3248
|
-
|
3249
|
-
|
3283
|
+
if (!stateKey) {
|
3284
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
|
3285
|
+
return null;
|
3286
|
+
}
|
3287
|
+
const decodeResult = changes => {
|
3288
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3289
|
+
|
3290
|
+
const decoded = decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
3291
|
+
balance: 0n,
|
3292
|
+
status: {
|
3293
|
+
type: "Liquid"
|
3294
|
+
}};
|
3295
|
+
const isFrozen = decoded?.status?.type === "Frozen";
|
3296
|
+
const amount = (decoded?.balance ?? 0n).toString();
|
3297
|
+
|
3298
|
+
// due to the following balance calculations, which are made in the `Balance` type:
|
3299
|
+
//
|
3300
|
+
// total balance = (free balance) + (reserved balance)
|
3301
|
+
// transferable balance = (free balance) - (frozen balance)
|
3302
|
+
//
|
3303
|
+
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
3304
|
+
// of this balance to the value we received from the RPC.
|
3305
|
+
//
|
3306
|
+
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
3307
|
+
const free = amount;
|
3308
|
+
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
3309
|
+
|
3310
|
+
// include balance values even if zero, so that newly-zero values overwrite old values
|
3311
|
+
const balanceValues = [{
|
3312
|
+
type: "free",
|
3313
|
+
label: "free",
|
3314
|
+
amount: free.toString()
|
3315
|
+
}, {
|
3316
|
+
type: "locked",
|
3317
|
+
label: "frozen",
|
3318
|
+
amount: frozen.toString()
|
3319
|
+
}];
|
3320
|
+
const balance = {
|
3321
|
+
source: "substrate-assets",
|
3322
|
+
status: "live",
|
3323
|
+
address,
|
3324
|
+
networkId,
|
3325
|
+
tokenId: token.id,
|
3326
|
+
values: balanceValues
|
3327
|
+
};
|
3328
|
+
return balance;
|
3329
|
+
};
|
3330
|
+
return {
|
3331
|
+
stateKeys: [stateKey],
|
3332
|
+
decodeResult
|
3333
|
+
};
|
3334
|
+
}).filter(isNotNil);
|
3335
|
+
};
|
3336
|
+
const tryEncode$1 = (scaleCoder, ...args) => {
|
3250
3337
|
try {
|
3251
|
-
|
3252
|
-
|
3253
|
-
|
3254
|
-
chainId
|
3255
|
-
}) : moduleMethodOrFn;
|
3256
|
-
try {
|
3257
|
-
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3258
|
-
} catch (cause) {
|
3259
|
-
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3260
|
-
return [];
|
3261
|
-
}
|
3262
|
-
}));
|
3263
|
-
return builtCoders;
|
3264
|
-
} catch (cause) {
|
3265
|
-
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3338
|
+
return scaleCoder?.keys?.enc?.(...args);
|
3339
|
+
} catch {
|
3340
|
+
return null;
|
3266
3341
|
}
|
3267
|
-
return null;
|
3268
3342
|
};
|
3269
3343
|
|
3270
|
-
|
3271
|
-
|
3272
|
-
|
3273
|
-
|
3274
|
-
|
3275
|
-
|
3276
|
-
|
3277
|
-
|
3278
|
-
|
3279
|
-
let decodedOutput = "";
|
3280
|
-
let isError = true;
|
3281
|
-
if (result.isOk) {
|
3282
|
-
const flags = result.asOk.flags.toHuman();
|
3283
|
-
isError = flags.includes("Revert");
|
3284
|
-
const abiMessage = getAbiMessage(abi, method);
|
3285
|
-
const returnType = abiMessage.returnType;
|
3286
|
-
const returnTypeName = getReturnTypeName(returnType);
|
3287
|
-
const r = returnType ? registry.createTypeUnsafe(returnTypeName, [result.asOk.data]).toHuman() : "()";
|
3288
|
-
output = isOk(r) ? r.Ok : isErr(r) ? r.Err : r;
|
3289
|
-
const errorText = isErr(output) ? typeof output.Err === "object" ? JSON.stringify(output.Err, null, 2) : output.Err?.toString() ?? "Error" : output !== "Ok" ? output?.toString() || "Error" : "Error";
|
3290
|
-
const okText = isOk(r) ? typeof output === "object" ? JSON.stringify(output, null, "\t") : output?.toString() ?? "()" : JSON.stringify(output, null, "\t") ?? "()";
|
3291
|
-
decodedOutput = isError ? errorText : okText;
|
3292
|
-
} else if (result.isErr) {
|
3293
|
-
output = result.toHuman();
|
3294
|
-
let errorText;
|
3295
|
-
if (isErr(output) && typeof output.Err === "object" && Object.keys(output.Err || {}).length && typeof Object.values(output.Err || {})[0] === "string") {
|
3296
|
-
const [errorKey, errorValue] = Object.entries(output.Err || {})[0];
|
3297
|
-
errorText = `${errorKey}${errorValue}`;
|
3298
|
-
}
|
3299
|
-
decodedOutput = errorText || "Error";
|
3300
|
-
}
|
3301
|
-
return {
|
3302
|
-
output,
|
3303
|
-
decodedOutput,
|
3304
|
-
isError
|
3344
|
+
const fetchBalances$6 = async ({
|
3345
|
+
networkId,
|
3346
|
+
tokensWithAddresses,
|
3347
|
+
connector,
|
3348
|
+
miniMetadata
|
3349
|
+
}) => {
|
3350
|
+
if (!tokensWithAddresses.length) return {
|
3351
|
+
success: [],
|
3352
|
+
errors: []
|
3305
3353
|
};
|
3306
|
-
}
|
3307
|
-
|
3308
|
-
/**
|
3309
|
-
* Helper types & functions
|
3310
|
-
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3311
|
-
*/
|
3312
|
-
|
3313
|
-
function isErr(o) {
|
3314
|
-
return typeof o === "object" && o !== null && "Err" in o;
|
3315
|
-
}
|
3316
|
-
function isOk(o) {
|
3317
|
-
return typeof o === "object" && o !== null && "Ok" in o;
|
3318
|
-
}
|
3319
|
-
function getReturnTypeName(type) {
|
3320
|
-
return type?.lookupName || type?.type || "";
|
3321
|
-
}
|
3322
|
-
function getAbiMessage(abi, method) {
|
3323
|
-
const abiMessage = abi.messages.find(m => stringCamelCase(m.method) === stringCamelCase(method));
|
3324
|
-
if (!abiMessage) {
|
3325
|
-
throw new Error(`"${method}" not found in Contract`);
|
3326
|
-
}
|
3327
|
-
return abiMessage;
|
3328
|
-
}
|
3329
|
-
|
3330
|
-
/**
|
3331
|
-
*
|
3332
|
-
* Detect Balances::transfer -> Balances::transfer_allow_death migration
|
3333
|
-
* https://github.com/paritytech/substrate/pull/12951
|
3334
|
-
*
|
3335
|
-
* `transfer_allow_death` is the preferred method,
|
3336
|
-
* so if something goes wrong during detection, we should assume the chain has migrated
|
3337
|
-
* @param metadataRpc string containing the hashed RPC metadata for the chain
|
3338
|
-
* @returns
|
3339
|
-
*/
|
3340
|
-
const detectTransferMethod = metadataRpc => {
|
3341
|
-
const pjsMetadata = new Metadata(new TypeRegistry(), metadataRpc);
|
3342
|
-
pjsMetadata.registry.setMetadata(pjsMetadata);
|
3343
|
-
const balancesPallet = pjsMetadata.asLatest.pallets.find(pallet => pallet.name.eq("Balances"));
|
3344
|
-
const balancesCallsTypeIndex = balancesPallet?.calls.value.type.toNumber();
|
3345
|
-
const balancesCallsType = balancesCallsTypeIndex !== undefined ? pjsMetadata.asLatest.lookup.types[balancesCallsTypeIndex] : undefined;
|
3346
|
-
const hasDeprecatedTransferCall = balancesCallsType?.type.def.asVariant?.variants.find(variant => variant.name.eq("transfer")) !== undefined;
|
3347
|
-
return hasDeprecatedTransferCall ? "transfer" : "transfer_allow_death";
|
3348
|
-
};
|
3349
|
-
|
3350
|
-
const getUniqueChainIds = (addressesByToken, tokens) => [...new Set(Object.keys(addressesByToken).map(tokenId => tokens[tokenId]?.networkId).flatMap(chainId => chainId ? [chainId] : []))];
|
3351
|
-
|
3352
|
-
const makeContractCaller = ({
|
3353
|
-
chainConnector,
|
3354
|
-
chainId,
|
3355
|
-
registry
|
3356
|
-
}) => async (callFrom, contractAddress, inputData) => registry.createType("ContractExecResult", await chainConnector.send(chainId, "state_call", ["ContractsApi_call", u8aToHex(u8aConcatStrict([
|
3357
|
-
// origin
|
3358
|
-
registry.createType("AccountId", callFrom).toU8a(),
|
3359
|
-
// dest
|
3360
|
-
registry.createType("AccountId", contractAddress).toU8a(),
|
3361
|
-
// value
|
3362
|
-
registry.createType("Balance", 0).toU8a(),
|
3363
|
-
// gasLimit
|
3364
|
-
registry.createType("Option<WeightV2>").toU8a(),
|
3365
|
-
// storageDepositLimit
|
3366
|
-
registry.createType("Option<Balance>").toU8a(),
|
3367
|
-
// inputData
|
3368
|
-
inputData instanceof Uint8Array ? inputData : inputData.toU8a()]))]));
|
3369
|
-
|
3370
|
-
/**
|
3371
|
-
* Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
|
3372
|
-
*/
|
3373
|
-
|
3374
|
-
/**
|
3375
|
-
* Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
|
3376
|
-
*/
|
3377
|
-
class RpcStateQueryHelper {
|
3378
|
-
#chainConnector;
|
3379
|
-
#queries;
|
3380
|
-
constructor(chainConnector, queries) {
|
3381
|
-
this.#chainConnector = chainConnector;
|
3382
|
-
this.#queries = queries;
|
3383
|
-
}
|
3384
|
-
async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
|
3385
|
-
const queriesByChain = groupBy(this.#queries, "chainId");
|
3386
|
-
const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
|
3387
|
-
const params = [queries.map(({
|
3388
|
-
stateKey
|
3389
|
-
}) => stateKey)];
|
3390
|
-
const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
|
3391
|
-
error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
|
3392
|
-
}, timeout);
|
3393
|
-
return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
|
3394
|
-
});
|
3395
|
-
return () => subscriptions.forEach(unsubscribe => unsubscribe());
|
3396
|
-
}
|
3397
|
-
async fetch(method = "state_queryStorageAt") {
|
3398
|
-
const queriesByChain = groupBy(this.#queries, "chainId");
|
3399
|
-
const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
|
3400
|
-
const params = [queries.map(({
|
3401
|
-
stateKey
|
3402
|
-
}) => stateKey)];
|
3403
|
-
const result = (await this.#chainConnector.send(chainId, method, params))[0];
|
3404
|
-
return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
|
3405
|
-
}));
|
3406
|
-
return resultsByChain.flatMap(result => result);
|
3407
|
-
}
|
3408
|
-
#distributeChangesToQueryDecoders(chainId, result) {
|
3409
|
-
if (typeof result !== "object" || result === null) return [];
|
3410
|
-
if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
|
3411
|
-
if (!Array.isArray(result.changes)) return [];
|
3412
|
-
return result.changes.flatMap(([reference, change]) => {
|
3413
|
-
if (typeof reference !== "string") {
|
3414
|
-
log.warn(`Received non-string reference in RPC result: ${reference}`);
|
3415
|
-
return [];
|
3416
|
-
}
|
3417
|
-
if (typeof change !== "string" && change !== null) {
|
3418
|
-
log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
|
3419
|
-
return [];
|
3420
|
-
}
|
3421
|
-
const query = this.#queries.find(({
|
3422
|
-
chainId: cId,
|
3423
|
-
stateKey
|
3424
|
-
}) => cId === chainId && stateKey === reference);
|
3425
|
-
if (!query) {
|
3426
|
-
log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
|
3427
|
-
stateKey
|
3428
|
-
}) => stateKey)}`);
|
3429
|
-
return [];
|
3430
|
-
}
|
3431
|
-
return [query.decodeResult(change)];
|
3432
|
-
});
|
3433
|
-
}
|
3434
|
-
}
|
3435
|
-
|
3436
|
-
const configureStore = (dbTable = db.balancesBlob) => ({
|
3437
|
-
persistData: async balances => {
|
3438
|
-
const output = compress(balances);
|
3439
|
-
await dbTable.clear();
|
3440
|
-
await dbTable.put({
|
3441
|
-
data: output,
|
3442
|
-
id: Date.now().toString()
|
3443
|
-
});
|
3444
|
-
},
|
3445
|
-
retrieveData: async () => {
|
3446
|
-
const compressedData = await dbTable.toCollection().first();
|
3447
|
-
if (!compressedData) return [];
|
3448
|
-
return decompress(compressedData.data);
|
3449
|
-
}
|
3450
|
-
});
|
3451
|
-
const compress = balances => pako.deflate(JSON.stringify(balances));
|
3452
|
-
const decompress = data => {
|
3453
|
-
const decompressed = pako.inflate(data, {
|
3454
|
-
to: "string"
|
3455
|
-
});
|
3456
|
-
return JSON.parse(decompressed);
|
3457
|
-
};
|
3458
|
-
|
3459
|
-
const fetchBalances$6 = async ({
|
3460
|
-
networkId,
|
3461
|
-
tokensWithAddresses,
|
3462
|
-
connector,
|
3463
|
-
miniMetadata
|
3464
|
-
}) => {
|
3465
3354
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
3466
3355
|
if (!miniMetadata?.data) {
|
3467
3356
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$5} balances on ${networkId}.`);
|
@@ -3497,7 +3386,7 @@ const fetchBalances$6 = async ({
|
|
3497
3386
|
};
|
3498
3387
|
}
|
3499
3388
|
const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
|
3500
|
-
const balances = await
|
3389
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
3501
3390
|
return balanceDefs.reduce((acc, def) => {
|
3502
3391
|
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
3503
3392
|
if (balance) acc.success.push(balance);
|
@@ -3524,80 +3413,6 @@ const fetchBalances$6 = async ({
|
|
3524
3413
|
errors: []
|
3525
3414
|
});
|
3526
3415
|
};
|
3527
|
-
const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
|
3528
|
-
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3529
|
-
storage: ["Assets", "Account"]
|
3530
|
-
});
|
3531
|
-
return balanceDefs.map(({
|
3532
|
-
token,
|
3533
|
-
address
|
3534
|
-
}) => {
|
3535
|
-
const scaleCoder = networkStorageCoders?.storage;
|
3536
|
-
const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
|
3537
|
-
// Asset Hub
|
3538
|
-
tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
|
3539
|
-
|
3540
|
-
if (!stateKey) {
|
3541
|
-
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
|
3542
|
-
return null;
|
3543
|
-
}
|
3544
|
-
const decodeResult = change => {
|
3545
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3546
|
-
|
3547
|
-
const decoded = decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
3548
|
-
balance: 0n,
|
3549
|
-
status: {
|
3550
|
-
type: "Liquid"
|
3551
|
-
}};
|
3552
|
-
const isFrozen = decoded?.status?.type === "Frozen";
|
3553
|
-
const amount = (decoded?.balance ?? 0n).toString();
|
3554
|
-
|
3555
|
-
// due to the following balance calculations, which are made in the `Balance` type:
|
3556
|
-
//
|
3557
|
-
// total balance = (free balance) + (reserved balance)
|
3558
|
-
// transferable balance = (free balance) - (frozen balance)
|
3559
|
-
//
|
3560
|
-
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
3561
|
-
// of this balance to the value we received from the RPC.
|
3562
|
-
//
|
3563
|
-
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
3564
|
-
const free = amount;
|
3565
|
-
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
3566
|
-
|
3567
|
-
// include balance values even if zero, so that newly-zero values overwrite old values
|
3568
|
-
const balanceValues = [{
|
3569
|
-
type: "free",
|
3570
|
-
label: "free",
|
3571
|
-
amount: free.toString()
|
3572
|
-
}, {
|
3573
|
-
type: "locked",
|
3574
|
-
label: "frozen",
|
3575
|
-
amount: frozen.toString()
|
3576
|
-
}];
|
3577
|
-
const balance = {
|
3578
|
-
source: "substrate-assets",
|
3579
|
-
status: "live",
|
3580
|
-
address,
|
3581
|
-
networkId,
|
3582
|
-
tokenId: token.id,
|
3583
|
-
values: balanceValues
|
3584
|
-
};
|
3585
|
-
return balance;
|
3586
|
-
};
|
3587
|
-
return {
|
3588
|
-
chainId: networkId,
|
3589
|
-
stateKey,
|
3590
|
-
decodeResult
|
3591
|
-
};
|
3592
|
-
}).filter(isNotNil);
|
3593
|
-
};
|
3594
|
-
const tryEncode$1 = (scaleCoder, ...args) => {
|
3595
|
-
try {
|
3596
|
-
return scaleCoder?.keys?.enc?.(...args);
|
3597
|
-
} catch {
|
3598
|
-
return null;
|
3599
|
-
}
|
3600
|
-
};
|
3601
3416
|
|
3602
3417
|
const fetchTokens$5 = async ({
|
3603
3418
|
networkId,
|
@@ -3834,47 +3649,47 @@ const getTransferAllEncodedArgs$2 = (assetId, to, codec) => {
|
|
3834
3649
|
})]);
|
3835
3650
|
};
|
3836
3651
|
|
3837
|
-
const
|
3652
|
+
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
3653
|
+
const {
|
3654
|
+
builder
|
3655
|
+
} = parseMetadataRpc(metadataRpc);
|
3656
|
+
const call = builder.buildRuntimeCall(apiName, method);
|
3657
|
+
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex(call.args.enc(args))]);
|
3658
|
+
return call.value.dec(hex);
|
3659
|
+
};
|
3660
|
+
|
3661
|
+
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
3662
|
+
const {
|
3663
|
+
unifiedMetadata,
|
3664
|
+
builder
|
3665
|
+
} = parseMetadataRpc(metadataRpc);
|
3666
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
3667
|
+
name
|
3668
|
+
}) => name === pallet)?.constants.find(({
|
3669
|
+
name
|
3670
|
+
}) => name === constant)?.value;
|
3671
|
+
if (!encodedValue) return null;
|
3672
|
+
const codec = builder.buildConstant(pallet, constant);
|
3673
|
+
return codec.dec(encodedValue);
|
3674
|
+
};
|
3675
|
+
|
3838
3676
|
const subscribeBalances$5 = ({
|
3839
3677
|
networkId,
|
3840
3678
|
tokensWithAddresses,
|
3841
3679
|
connector,
|
3842
3680
|
miniMetadata
|
3843
3681
|
}) => {
|
3844
|
-
|
3845
|
-
|
3846
|
-
|
3847
|
-
|
3848
|
-
|
3849
|
-
|
3850
|
-
|
3851
|
-
|
3852
|
-
|
3853
|
-
|
3854
|
-
|
3855
|
-
connector,
|
3856
|
-
miniMetadata
|
3857
|
-
});
|
3858
|
-
if (abortController.signal.aborted) return;
|
3859
|
-
subscriber.next(balances);
|
3860
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
3861
|
-
} catch (error) {
|
3862
|
-
log.error("Error", {
|
3863
|
-
module: MODULE_TYPE$5,
|
3864
|
-
networkId,
|
3865
|
-
miniMetadata,
|
3866
|
-
addressesByToken: tokensWithAddresses,
|
3867
|
-
error
|
3868
|
-
});
|
3869
|
-
subscriber.error(error);
|
3870
|
-
}
|
3871
|
-
};
|
3872
|
-
poll();
|
3873
|
-
return () => {
|
3874
|
-
abortController.abort();
|
3875
|
-
};
|
3876
|
-
}).pipe(distinctUntilChanged(isEqual));
|
3877
|
-
};
|
3682
|
+
if (!tokensWithAddresses.length) return of({
|
3683
|
+
success: [],
|
3684
|
+
errors: []
|
3685
|
+
});
|
3686
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
3687
|
+
const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
|
3688
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(map(balances => ({
|
3689
|
+
success: balances,
|
3690
|
+
errors: []
|
3691
|
+
})));
|
3692
|
+
};
|
3878
3693
|
|
3879
3694
|
const SubAssetsBalanceModule = {
|
3880
3695
|
type: MODULE_TYPE$5,
|
@@ -3895,74 +3710,267 @@ const SubAssetsTokenConfigSchema = z.strictObject({
|
|
3895
3710
|
const MODULE_TYPE$4 = SubForeignAssetsTokenSchema.shape.type.value;
|
3896
3711
|
const PLATFORM$4 = SubForeignAssetsTokenSchema.shape.platform.value;
|
3897
3712
|
|
3898
|
-
|
3899
|
-
|
3900
|
-
|
3901
|
-
|
3902
|
-
|
3903
|
-
|
3904
|
-
|
3905
|
-
if (
|
3906
|
-
|
3907
|
-
|
3908
|
-
|
3909
|
-
|
3910
|
-
|
3911
|
-
|
3912
|
-
|
3913
|
-
|
3914
|
-
|
3713
|
+
/**
|
3714
|
+
* Wraps a BalanceModule's fetch/subscribe methods with a single `balances` method.
|
3715
|
+
* This `balances` method will subscribe if a callback parameter is provided, or otherwise fetch.
|
3716
|
+
*/
|
3717
|
+
|
3718
|
+
async function balances(balanceModule, addressesByToken, callback) {
|
3719
|
+
// subscription request
|
3720
|
+
if (callback !== undefined) return await balanceModule.subscribeBalances({
|
3721
|
+
addressesByToken
|
3722
|
+
}, callback);
|
3723
|
+
|
3724
|
+
// one-off request
|
3725
|
+
return await balanceModule.fetchBalances(addressesByToken);
|
3726
|
+
}
|
3727
|
+
|
3728
|
+
// TODO remove this one in favor of the network specific one below
|
3729
|
+
const buildStorageCoders = ({
|
3730
|
+
chainIds,
|
3731
|
+
chains,
|
3732
|
+
miniMetadatas,
|
3733
|
+
coders
|
3734
|
+
}) => new Map([...chainIds].flatMap(chainId => {
|
3735
|
+
const chain = chains[chainId];
|
3736
|
+
if (!chain) return [];
|
3737
|
+
const miniMetadata = miniMetadatas.get(chainId); // findMiniMetadata<TBalanceModule>(miniMetadatas, moduleType, chain)
|
3738
|
+
if (!miniMetadata) return [];
|
3739
|
+
if (!miniMetadata.data) return [];
|
3740
|
+
const metadata = unifyMetadata(decAnyMetadata(miniMetadata.data));
|
3741
|
+
try {
|
3742
|
+
const scaleBuilder = getDynamicBuilder(getLookupFn(metadata));
|
3743
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3744
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3745
|
+
chainId
|
3746
|
+
}) : moduleMethodOrFn;
|
3747
|
+
try {
|
3748
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3749
|
+
} catch (cause) {
|
3750
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3751
|
+
return [];
|
3752
|
+
}
|
3753
|
+
}));
|
3754
|
+
return [[chainId, builtCoders]];
|
3755
|
+
} catch (cause) {
|
3756
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3757
|
+
return [];
|
3915
3758
|
}
|
3916
|
-
|
3917
|
-
|
3918
|
-
|
3919
|
-
|
3920
|
-
|
3921
|
-
|
3922
|
-
|
3923
|
-
|
3924
|
-
|
3925
|
-
|
3759
|
+
}));
|
3760
|
+
// type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
|
3761
|
+
|
3762
|
+
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
3763
|
+
if (!miniMetadata.data) return null;
|
3764
|
+
const metadata = unifyMetadata(decAnyMetadata(miniMetadata.data));
|
3765
|
+
try {
|
3766
|
+
const scaleBuilder = getDynamicBuilder(getLookupFn(metadata));
|
3767
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3768
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3769
|
+
chainId
|
3770
|
+
}) : moduleMethodOrFn;
|
3771
|
+
try {
|
3772
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3773
|
+
} catch (cause) {
|
3774
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3775
|
+
return [];
|
3776
|
+
}
|
3777
|
+
}));
|
3778
|
+
return builtCoders;
|
3779
|
+
} catch (cause) {
|
3780
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3926
3781
|
}
|
3927
|
-
|
3928
|
-
|
3929
|
-
|
3930
|
-
|
3931
|
-
|
3932
|
-
|
3933
|
-
|
3934
|
-
|
3935
|
-
|
3936
|
-
|
3782
|
+
return null;
|
3783
|
+
};
|
3784
|
+
|
3785
|
+
/**
|
3786
|
+
* Decodes & unwraps outputs and errors of a given result, contract, and method.
|
3787
|
+
* Parsed error message can be found in `decodedOutput` if `isError` is true.
|
3788
|
+
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3789
|
+
*/
|
3790
|
+
function decodeOutput({
|
3791
|
+
result
|
3792
|
+
}, registry, abi, method) {
|
3793
|
+
let output;
|
3794
|
+
let decodedOutput = "";
|
3795
|
+
let isError = true;
|
3796
|
+
if (result.isOk) {
|
3797
|
+
const flags = result.asOk.flags.toHuman();
|
3798
|
+
isError = flags.includes("Revert");
|
3799
|
+
const abiMessage = getAbiMessage(abi, method);
|
3800
|
+
const returnType = abiMessage.returnType;
|
3801
|
+
const returnTypeName = getReturnTypeName(returnType);
|
3802
|
+
const r = returnType ? registry.createTypeUnsafe(returnTypeName, [result.asOk.data]).toHuman() : "()";
|
3803
|
+
output = isOk(r) ? r.Ok : isErr(r) ? r.Err : r;
|
3804
|
+
const errorText = isErr(output) ? typeof output.Err === "object" ? JSON.stringify(output.Err, null, 2) : output.Err?.toString() ?? "Error" : output !== "Ok" ? output?.toString() || "Error" : "Error";
|
3805
|
+
const okText = isOk(r) ? typeof output === "object" ? JSON.stringify(output, null, "\t") : output?.toString() ?? "()" : JSON.stringify(output, null, "\t") ?? "()";
|
3806
|
+
decodedOutput = isError ? errorText : okText;
|
3807
|
+
} else if (result.isErr) {
|
3808
|
+
output = result.toHuman();
|
3809
|
+
let errorText;
|
3810
|
+
if (isErr(output) && typeof output.Err === "object" && Object.keys(output.Err || {}).length && typeof Object.values(output.Err || {})[0] === "string") {
|
3811
|
+
const [errorKey, errorValue] = Object.entries(output.Err || {})[0];
|
3812
|
+
errorText = `${errorKey}${errorValue}`;
|
3813
|
+
}
|
3814
|
+
decodedOutput = errorText || "Error";
|
3937
3815
|
}
|
3938
|
-
|
3939
|
-
|
3940
|
-
|
3941
|
-
|
3942
|
-
|
3943
|
-
|
3944
|
-
|
3945
|
-
|
3946
|
-
|
3947
|
-
|
3948
|
-
|
3949
|
-
|
3950
|
-
|
3951
|
-
|
3952
|
-
|
3953
|
-
|
3954
|
-
|
3955
|
-
|
3956
|
-
|
3957
|
-
|
3958
|
-
|
3816
|
+
return {
|
3817
|
+
output,
|
3818
|
+
decodedOutput,
|
3819
|
+
isError
|
3820
|
+
};
|
3821
|
+
}
|
3822
|
+
|
3823
|
+
/**
|
3824
|
+
* Helper types & functions
|
3825
|
+
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3826
|
+
*/
|
3827
|
+
|
3828
|
+
function isErr(o) {
|
3829
|
+
return typeof o === "object" && o !== null && "Err" in o;
|
3830
|
+
}
|
3831
|
+
function isOk(o) {
|
3832
|
+
return typeof o === "object" && o !== null && "Ok" in o;
|
3833
|
+
}
|
3834
|
+
function getReturnTypeName(type) {
|
3835
|
+
return type?.lookupName || type?.type || "";
|
3836
|
+
}
|
3837
|
+
function getAbiMessage(abi, method) {
|
3838
|
+
const abiMessage = abi.messages.find(m => stringCamelCase(m.method) === stringCamelCase(method));
|
3839
|
+
if (!abiMessage) {
|
3840
|
+
throw new Error(`"${method}" not found in Contract`);
|
3841
|
+
}
|
3842
|
+
return abiMessage;
|
3843
|
+
}
|
3844
|
+
|
3845
|
+
/**
|
3846
|
+
*
|
3847
|
+
* Detect Balances::transfer -> Balances::transfer_allow_death migration
|
3848
|
+
* https://github.com/paritytech/substrate/pull/12951
|
3849
|
+
*
|
3850
|
+
* `transfer_allow_death` is the preferred method,
|
3851
|
+
* so if something goes wrong during detection, we should assume the chain has migrated
|
3852
|
+
* @param metadataRpc string containing the hashed RPC metadata for the chain
|
3853
|
+
* @returns
|
3854
|
+
*/
|
3855
|
+
const detectTransferMethod = metadataRpc => {
|
3856
|
+
const pjsMetadata = new Metadata(new TypeRegistry(), metadataRpc);
|
3857
|
+
pjsMetadata.registry.setMetadata(pjsMetadata);
|
3858
|
+
const balancesPallet = pjsMetadata.asLatest.pallets.find(pallet => pallet.name.eq("Balances"));
|
3859
|
+
const balancesCallsTypeIndex = balancesPallet?.calls.value.type.toNumber();
|
3860
|
+
const balancesCallsType = balancesCallsTypeIndex !== undefined ? pjsMetadata.asLatest.lookup.types[balancesCallsTypeIndex] : undefined;
|
3861
|
+
const hasDeprecatedTransferCall = balancesCallsType?.type.def.asVariant?.variants.find(variant => variant.name.eq("transfer")) !== undefined;
|
3862
|
+
return hasDeprecatedTransferCall ? "transfer" : "transfer_allow_death";
|
3863
|
+
};
|
3864
|
+
|
3865
|
+
const getUniqueChainIds = (addressesByToken, tokens) => [...new Set(Object.keys(addressesByToken).map(tokenId => tokens[tokenId]?.networkId).flatMap(chainId => chainId ? [chainId] : []))];
|
3866
|
+
|
3867
|
+
const makeContractCaller = ({
|
3868
|
+
chainConnector,
|
3869
|
+
chainId,
|
3870
|
+
registry
|
3871
|
+
}) => async (callFrom, contractAddress, inputData) => registry.createType("ContractExecResult", await chainConnector.send(chainId, "state_call", ["ContractsApi_call", u8aToHex(u8aConcatStrict([
|
3872
|
+
// origin
|
3873
|
+
registry.createType("AccountId", callFrom).toU8a(),
|
3874
|
+
// dest
|
3875
|
+
registry.createType("AccountId", contractAddress).toU8a(),
|
3876
|
+
// value
|
3877
|
+
registry.createType("Balance", 0).toU8a(),
|
3878
|
+
// gasLimit
|
3879
|
+
registry.createType("Option<WeightV2>").toU8a(),
|
3880
|
+
// storageDepositLimit
|
3881
|
+
registry.createType("Option<Balance>").toU8a(),
|
3882
|
+
// inputData
|
3883
|
+
inputData instanceof Uint8Array ? inputData : inputData.toU8a()]))]));
|
3884
|
+
|
3885
|
+
/**
|
3886
|
+
* Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
|
3887
|
+
*/
|
3888
|
+
|
3889
|
+
/**
|
3890
|
+
* Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
|
3891
|
+
*/
|
3892
|
+
class RpcStateQueryHelper {
|
3893
|
+
#chainConnector;
|
3894
|
+
#queries;
|
3895
|
+
constructor(chainConnector, queries) {
|
3896
|
+
this.#chainConnector = chainConnector;
|
3897
|
+
this.#queries = queries;
|
3898
|
+
}
|
3899
|
+
async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
|
3900
|
+
const queriesByChain = groupBy(this.#queries, "chainId");
|
3901
|
+
const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
|
3902
|
+
const params = [queries.map(({
|
3903
|
+
stateKey
|
3904
|
+
}) => stateKey)];
|
3905
|
+
const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
|
3906
|
+
error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
|
3907
|
+
}, timeout);
|
3908
|
+
return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
|
3959
3909
|
});
|
3960
|
-
return
|
3961
|
-
}
|
3962
|
-
|
3963
|
-
|
3910
|
+
return () => subscriptions.forEach(unsubscribe => unsubscribe());
|
3911
|
+
}
|
3912
|
+
async fetch(method = "state_queryStorageAt") {
|
3913
|
+
const queriesByChain = groupBy(this.#queries, "chainId");
|
3914
|
+
const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
|
3915
|
+
const params = [queries.map(({
|
3916
|
+
stateKey
|
3917
|
+
}) => stateKey)];
|
3918
|
+
const result = (await this.#chainConnector.send(chainId, method, params))[0];
|
3919
|
+
return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
|
3920
|
+
}));
|
3921
|
+
return resultsByChain.flatMap(result => result);
|
3922
|
+
}
|
3923
|
+
#distributeChangesToQueryDecoders(chainId, result) {
|
3924
|
+
if (typeof result !== "object" || result === null) return [];
|
3925
|
+
if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
|
3926
|
+
if (!Array.isArray(result.changes)) return [];
|
3927
|
+
return result.changes.flatMap(([reference, change]) => {
|
3928
|
+
if (typeof reference !== "string") {
|
3929
|
+
log.warn(`Received non-string reference in RPC result: ${reference}`);
|
3930
|
+
return [];
|
3931
|
+
}
|
3932
|
+
if (typeof change !== "string" && change !== null) {
|
3933
|
+
log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
|
3934
|
+
return [];
|
3935
|
+
}
|
3936
|
+
const query = this.#queries.find(({
|
3937
|
+
chainId: cId,
|
3938
|
+
stateKey
|
3939
|
+
}) => cId === chainId && stateKey === reference);
|
3940
|
+
if (!query) {
|
3941
|
+
log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
|
3942
|
+
stateKey
|
3943
|
+
}) => stateKey)}`);
|
3944
|
+
return [];
|
3945
|
+
}
|
3946
|
+
return [query.decodeResult(change)];
|
3947
|
+
});
|
3948
|
+
}
|
3949
|
+
}
|
3950
|
+
|
3951
|
+
const configureStore = (dbTable = db.balancesBlob) => ({
|
3952
|
+
persistData: async balances => {
|
3953
|
+
const output = compress(balances);
|
3954
|
+
await dbTable.clear();
|
3955
|
+
await dbTable.put({
|
3956
|
+
data: output,
|
3957
|
+
id: Date.now().toString()
|
3958
|
+
});
|
3959
|
+
},
|
3960
|
+
retrieveData: async () => {
|
3961
|
+
const compressedData = await dbTable.toCollection().first();
|
3962
|
+
if (!compressedData) return [];
|
3963
|
+
return decompress(compressedData.data);
|
3964
|
+
}
|
3965
|
+
});
|
3966
|
+
const compress = balances => pako.deflate(JSON.stringify(balances));
|
3967
|
+
const decompress = data => {
|
3968
|
+
const decompressed = pako.inflate(data, {
|
3969
|
+
to: "string"
|
3964
3970
|
});
|
3971
|
+
return JSON.parse(decompressed);
|
3965
3972
|
};
|
3973
|
+
|
3966
3974
|
const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
3967
3975
|
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3968
3976
|
storage: ["ForeignAssets", "Account"]
|
@@ -3984,10 +3992,10 @@ const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
|
3984
3992
|
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
3985
3993
|
return null;
|
3986
3994
|
}
|
3987
|
-
const decodeResult =
|
3995
|
+
const decodeResult = changes => {
|
3988
3996
|
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3989
3997
|
|
3990
|
-
const decoded = decodeScale(scaleCoder,
|
3998
|
+
const decoded = decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
3991
3999
|
balance: 0n,
|
3992
4000
|
is_frozen: false,
|
3993
4001
|
status: {
|
@@ -4029,13 +4037,85 @@ const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
|
4029
4037
|
return balance;
|
4030
4038
|
};
|
4031
4039
|
return {
|
4032
|
-
|
4033
|
-
stateKey,
|
4040
|
+
stateKeys: [stateKey],
|
4034
4041
|
decodeResult
|
4035
4042
|
};
|
4036
4043
|
}).filter(isNotNil);
|
4037
4044
|
};
|
4038
4045
|
|
4046
|
+
const fetchBalances$5 = async ({
|
4047
|
+
networkId,
|
4048
|
+
tokensWithAddresses,
|
4049
|
+
connector,
|
4050
|
+
miniMetadata
|
4051
|
+
}) => {
|
4052
|
+
if (!tokensWithAddresses.length) return {
|
4053
|
+
success: [],
|
4054
|
+
errors: []
|
4055
|
+
};
|
4056
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4057
|
+
if (!miniMetadata?.data) {
|
4058
|
+
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$4} balances on ${networkId}.`);
|
4059
|
+
return {
|
4060
|
+
success: [],
|
4061
|
+
errors: balanceDefs.map(def => ({
|
4062
|
+
tokenId: def.token.id,
|
4063
|
+
address: def.address,
|
4064
|
+
error: new Error("Minimetadata is required for fetching balances")
|
4065
|
+
}))
|
4066
|
+
};
|
4067
|
+
}
|
4068
|
+
if (miniMetadata.source !== MODULE_TYPE$4) {
|
4069
|
+
log.warn(`Ignoring miniMetadata with source ${miniMetadata.source} in ${MODULE_TYPE$4}.`);
|
4070
|
+
return {
|
4071
|
+
success: [],
|
4072
|
+
errors: balanceDefs.map(def => ({
|
4073
|
+
tokenId: def.token.id,
|
4074
|
+
address: def.address,
|
4075
|
+
error: new Error(`Invalid request: miniMetadata source is not ${MODULE_TYPE$4}`)
|
4076
|
+
}))
|
4077
|
+
};
|
4078
|
+
}
|
4079
|
+
if (miniMetadata.chainId !== networkId) {
|
4080
|
+
log.warn(`Ignoring miniMetadata with chainId ${miniMetadata.chainId} in ${MODULE_TYPE$4}. Expected chainId is ${networkId}`);
|
4081
|
+
return {
|
4082
|
+
success: [],
|
4083
|
+
errors: balanceDefs.map(def => ({
|
4084
|
+
tokenId: def.token.id,
|
4085
|
+
address: def.address,
|
4086
|
+
error: new Error(`Invalid request: Expected chainId is ${networkId}`)
|
4087
|
+
}))
|
4088
|
+
};
|
4089
|
+
}
|
4090
|
+
const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
|
4091
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
4092
|
+
return balanceDefs.reduce((acc, def) => {
|
4093
|
+
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
4094
|
+
if (balance) acc.success.push(balance);
|
4095
|
+
//if no entry consider empty balance
|
4096
|
+
else acc.success.push({
|
4097
|
+
address: def.address,
|
4098
|
+
networkId,
|
4099
|
+
tokenId: def.token.id,
|
4100
|
+
source: MODULE_TYPE$4,
|
4101
|
+
status: "live",
|
4102
|
+
values: [{
|
4103
|
+
type: "free",
|
4104
|
+
label: "free",
|
4105
|
+
amount: "0"
|
4106
|
+
}, {
|
4107
|
+
type: "locked",
|
4108
|
+
label: "frozen",
|
4109
|
+
amount: "0"
|
4110
|
+
}]
|
4111
|
+
});
|
4112
|
+
return acc;
|
4113
|
+
}, {
|
4114
|
+
success: [],
|
4115
|
+
errors: []
|
4116
|
+
});
|
4117
|
+
};
|
4118
|
+
|
4039
4119
|
const fetchTokens$4 = async ({
|
4040
4120
|
networkId,
|
4041
4121
|
tokens,
|
@@ -4232,46 +4312,22 @@ const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
|
|
4232
4312
|
})]);
|
4233
4313
|
};
|
4234
4314
|
|
4235
|
-
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
4236
4315
|
const subscribeBalances$4 = ({
|
4237
4316
|
networkId,
|
4238
4317
|
tokensWithAddresses,
|
4239
4318
|
connector,
|
4240
4319
|
miniMetadata
|
4241
4320
|
}) => {
|
4242
|
-
|
4243
|
-
|
4244
|
-
|
4245
|
-
|
4246
|
-
|
4247
|
-
|
4248
|
-
|
4249
|
-
|
4250
|
-
|
4251
|
-
|
4252
|
-
tokensWithAddresses: tokensWithAddresses,
|
4253
|
-
connector,
|
4254
|
-
miniMetadata
|
4255
|
-
});
|
4256
|
-
if (abortController.signal.aborted) return;
|
4257
|
-
subscriber.next(balances);
|
4258
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
4259
|
-
} catch (error) {
|
4260
|
-
log.error("Error", {
|
4261
|
-
module: MODULE_TYPE$4,
|
4262
|
-
networkId,
|
4263
|
-
miniMetadata,
|
4264
|
-
addressesByToken: tokensWithAddresses,
|
4265
|
-
error
|
4266
|
-
});
|
4267
|
-
subscriber.error(error);
|
4268
|
-
}
|
4269
|
-
};
|
4270
|
-
poll();
|
4271
|
-
return () => {
|
4272
|
-
abortController.abort();
|
4273
|
-
};
|
4274
|
-
}).pipe(distinctUntilChanged(isEqual));
|
4321
|
+
if (!tokensWithAddresses.length) return of({
|
4322
|
+
success: [],
|
4323
|
+
errors: []
|
4324
|
+
});
|
4325
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4326
|
+
const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
|
4327
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(map(balances => ({
|
4328
|
+
success: balances,
|
4329
|
+
errors: []
|
4330
|
+
})));
|
4275
4331
|
};
|
4276
4332
|
|
4277
4333
|
const SubForeignAssetsBalanceModule = {
|
@@ -4290,48 +4346,30 @@ const SubForeignAssetsTokenConfigSchema = z.strictObject({
|
|
4290
4346
|
...TokenConfigBaseSchema.shape
|
4291
4347
|
});
|
4292
4348
|
|
4293
|
-
// to be used by chaindata too
|
4294
|
-
const SubHydrationTokenConfigSchema = z.strictObject({
|
4295
|
-
onChainId: SubHydrationTokenSchema.shape.onChainId,
|
4296
|
-
...TokenConfigBaseSchema.shape
|
4297
|
-
});
|
4298
|
-
|
4299
|
-
const MODULE_TYPE$3 = SubHydrationTokenSchema.shape.type.value;
|
4300
|
-
const PLATFORM$3 = SubHydrationTokenSchema.shape.platform.value;
|
4301
|
-
|
4302
|
-
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
4303
|
-
const {
|
4304
|
-
builder
|
4305
|
-
} = parseMetadataRpc(metadataRpc);
|
4306
|
-
const call = builder.buildRuntimeCall(apiName, method);
|
4307
|
-
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex(call.args.enc(args))]);
|
4308
|
-
return call.value.dec(hex);
|
4309
|
-
};
|
4310
|
-
|
4311
|
-
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
4312
|
-
const {
|
4313
|
-
unifiedMetadata,
|
4314
|
-
builder
|
4315
|
-
} = parseMetadataRpc(metadataRpc);
|
4316
|
-
const encodedValue = unifiedMetadata.pallets.find(({
|
4317
|
-
name
|
4318
|
-
}) => name === pallet)?.constants.find(({
|
4319
|
-
name
|
4320
|
-
}) => name === constant)?.value;
|
4321
|
-
if (!encodedValue) return null;
|
4322
|
-
const codec = builder.buildConstant(pallet, constant);
|
4323
|
-
return codec.dec(encodedValue);
|
4324
|
-
};
|
4325
|
-
|
4349
|
+
// to be used by chaindata too
|
4350
|
+
const SubHydrationTokenConfigSchema = z.strictObject({
|
4351
|
+
onChainId: SubHydrationTokenSchema.shape.onChainId,
|
4352
|
+
...TokenConfigBaseSchema.shape
|
4353
|
+
});
|
4354
|
+
|
4355
|
+
const MODULE_TYPE$3 = SubHydrationTokenSchema.shape.type.value;
|
4356
|
+
const PLATFORM$3 = SubHydrationTokenSchema.shape.platform.value;
|
4357
|
+
|
4326
4358
|
const fetchBalances$4 = async ({
|
4327
4359
|
networkId,
|
4328
4360
|
tokensWithAddresses,
|
4329
4361
|
connector,
|
4330
4362
|
miniMetadata
|
4331
4363
|
}) => {
|
4364
|
+
if (!tokensWithAddresses.length) return {
|
4365
|
+
success: [],
|
4366
|
+
errors: []
|
4367
|
+
};
|
4332
4368
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4333
4369
|
if (!miniMetadata?.data) {
|
4334
|
-
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}
|
4370
|
+
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}.`, {
|
4371
|
+
tokensWithAddresses
|
4372
|
+
});
|
4335
4373
|
return {
|
4336
4374
|
success: [],
|
4337
4375
|
errors: balanceDefs.map(def => ({
|
@@ -4578,13 +4616,17 @@ const getTransferCallData$3 = ({
|
|
4578
4616
|
};
|
4579
4617
|
};
|
4580
4618
|
|
4581
|
-
const SUBSCRIPTION_INTERVAL$
|
4619
|
+
const SUBSCRIPTION_INTERVAL$1 = 6_000;
|
4582
4620
|
const subscribeBalances$3 = ({
|
4583
4621
|
networkId,
|
4584
4622
|
tokensWithAddresses,
|
4585
4623
|
connector,
|
4586
4624
|
miniMetadata
|
4587
4625
|
}) => {
|
4626
|
+
if (!tokensWithAddresses.length) return of({
|
4627
|
+
success: [],
|
4628
|
+
errors: []
|
4629
|
+
});
|
4588
4630
|
return new Observable(subscriber => {
|
4589
4631
|
const abortController = new AbortController();
|
4590
4632
|
|
@@ -4601,7 +4643,7 @@ const subscribeBalances$3 = ({
|
|
4601
4643
|
});
|
4602
4644
|
if (abortController.signal.aborted) return;
|
4603
4645
|
subscriber.next(balances);
|
4604
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
4646
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$1);
|
4605
4647
|
} catch (error) {
|
4606
4648
|
log.error("Error", {
|
4607
4649
|
module: MODULE_TYPE$3,
|
@@ -4633,54 +4675,6 @@ const SubHydrationBalanceModule = {
|
|
4633
4675
|
const MODULE_TYPE$2 = SubNativeTokenSchema.shape.type.value;
|
4634
4676
|
const PLATFORM$2 = SubNativeTokenSchema.shape.platform.value;
|
4635
4677
|
|
4636
|
-
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
4637
|
-
const allStateKeys = queries.flatMap(({
|
4638
|
-
stateKeys
|
4639
|
-
}) => stateKeys).filter(isNotNil);
|
4640
|
-
|
4641
|
-
// doing a query without keys would throw an error => return early
|
4642
|
-
if (!allStateKeys.length) return queries.map(({
|
4643
|
-
stateKeys,
|
4644
|
-
decodeResult
|
4645
|
-
}) => decodeResult(stateKeys.map(() => null)));
|
4646
|
-
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
4647
|
-
return decodeRpcQueryPack(queries, result);
|
4648
|
-
};
|
4649
|
-
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
4650
|
-
const allStateKeys = queries.flatMap(({
|
4651
|
-
stateKeys
|
4652
|
-
}) => stateKeys).filter(isNotNil);
|
4653
|
-
|
4654
|
-
// doing a query without keys would throw an error => return early
|
4655
|
-
if (!allStateKeys.length) return of(queries.map(({
|
4656
|
-
stateKeys,
|
4657
|
-
decodeResult
|
4658
|
-
}) => decodeResult(stateKeys.map(() => null))));
|
4659
|
-
return new Observable(subscriber => {
|
4660
|
-
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
4661
|
-
if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
|
4662
|
-
}, timeout);
|
4663
|
-
return () => {
|
4664
|
-
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
4665
|
-
};
|
4666
|
-
});
|
4667
|
-
};
|
4668
|
-
const decodeRpcQueryPack = (queries, result) => {
|
4669
|
-
return queries.reduce((acc, {
|
4670
|
-
stateKeys,
|
4671
|
-
decodeResult
|
4672
|
-
}) => {
|
4673
|
-
const changes = stateKeys.map(stateKey => {
|
4674
|
-
if (!stateKey) return null;
|
4675
|
-
const change = result.changes.find(([key]) => key === stateKey);
|
4676
|
-
if (!change) return null;
|
4677
|
-
return change[1];
|
4678
|
-
});
|
4679
|
-
acc.push(decodeResult(changes));
|
4680
|
-
return acc;
|
4681
|
-
}, []);
|
4682
|
-
};
|
4683
|
-
|
4684
4678
|
const SUBTENSOR_ROOT_NETUID$1 = 0;
|
4685
4679
|
const SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1 = 1000000n;
|
4686
4680
|
const TAO_DECIMALS$1 = 9n;
|
@@ -5398,6 +5392,10 @@ const fetchBalances$3 = async ({
|
|
5398
5392
|
connector,
|
5399
5393
|
miniMetadata
|
5400
5394
|
}) => {
|
5395
|
+
if (!tokensWithAddresses.length) return {
|
5396
|
+
success: [],
|
5397
|
+
errors: []
|
5398
|
+
};
|
5401
5399
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
5402
5400
|
if (!miniMetadata?.data) {
|
5403
5401
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$2} balances on ${networkId}.`);
|
@@ -5679,6 +5677,11 @@ const subscribeBalances$2 = ({
|
|
5679
5677
|
connector,
|
5680
5678
|
miniMetadata
|
5681
5679
|
}) => {
|
5680
|
+
if (!tokensWithAddresses.length) return of({
|
5681
|
+
success: [],
|
5682
|
+
errors: []
|
5683
|
+
});
|
5684
|
+
|
5682
5685
|
// could be use as shared observable key if we decide to cache the sub
|
5683
5686
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
5684
5687
|
const baseQueries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
|
@@ -6882,6 +6885,10 @@ const fetchBalances$2 = async ({
|
|
6882
6885
|
tokensWithAddresses,
|
6883
6886
|
connector
|
6884
6887
|
}) => {
|
6888
|
+
if (!tokensWithAddresses.length) return {
|
6889
|
+
success: [],
|
6890
|
+
errors: []
|
6891
|
+
};
|
6885
6892
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
6886
6893
|
if (!balanceDefs.length) return {
|
6887
6894
|
success: [],
|
@@ -7069,13 +7076,17 @@ const getTransferCallData$1 = async ({
|
|
7069
7076
|
};
|
7070
7077
|
};
|
7071
7078
|
|
7072
|
-
const SUBSCRIPTION_INTERVAL
|
7079
|
+
const SUBSCRIPTION_INTERVAL = 6_000;
|
7073
7080
|
const subscribeBalances$1 = ({
|
7074
7081
|
networkId,
|
7075
7082
|
tokensWithAddresses,
|
7076
7083
|
connector,
|
7077
7084
|
miniMetadata
|
7078
7085
|
}) => {
|
7086
|
+
if (!tokensWithAddresses.length) return of({
|
7087
|
+
success: [],
|
7088
|
+
errors: []
|
7089
|
+
});
|
7079
7090
|
return new Observable(subscriber => {
|
7080
7091
|
const abortController = new AbortController();
|
7081
7092
|
|
@@ -7092,7 +7103,7 @@ const subscribeBalances$1 = ({
|
|
7092
7103
|
});
|
7093
7104
|
if (abortController.signal.aborted) return;
|
7094
7105
|
subscriber.next(balances);
|
7095
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL
|
7106
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL);
|
7096
7107
|
} catch (error) {
|
7097
7108
|
log.error("Error", {
|
7098
7109
|
module: MODULE_TYPE$1,
|
@@ -7130,12 +7141,77 @@ const SubPsp22TokenConfigSchema = z.strictObject({
|
|
7130
7141
|
const MODULE_TYPE = SubTokensTokenSchema.shape.type.value;
|
7131
7142
|
const PLATFORM = SubTokensTokenSchema.shape.platform.value;
|
7132
7143
|
|
7144
|
+
const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
|
7145
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7146
|
+
storage: [miniMetadata.extra.palletId, "Accounts"]
|
7147
|
+
});
|
7148
|
+
return balanceDefs.map(({
|
7149
|
+
token,
|
7150
|
+
address
|
7151
|
+
}) => {
|
7152
|
+
const scaleCoder = networkStorageCoders?.storage;
|
7153
|
+
const getStateKey = onChainId => {
|
7154
|
+
try {
|
7155
|
+
return scaleCoder.keys.enc(address, papiParse(onChainId));
|
7156
|
+
} catch {
|
7157
|
+
return null;
|
7158
|
+
}
|
7159
|
+
};
|
7160
|
+
const stateKey = getStateKey(token.onChainId);
|
7161
|
+
if (!stateKey) {
|
7162
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
7163
|
+
return null;
|
7164
|
+
}
|
7165
|
+
const decodeResult = changes => {
|
7166
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7167
|
+
|
7168
|
+
const decoded = decodeScale(scaleCoder, changes[0], `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
|
7169
|
+
free: 0n,
|
7170
|
+
reserved: 0n,
|
7171
|
+
frozen: 0n
|
7172
|
+
};
|
7173
|
+
const free = (decoded?.free ?? 0n).toString();
|
7174
|
+
const reserved = (decoded?.reserved ?? 0n).toString();
|
7175
|
+
const frozen = (decoded?.frozen ?? 0n).toString();
|
7176
|
+
const balanceValues = [{
|
7177
|
+
type: "free",
|
7178
|
+
label: "free",
|
7179
|
+
amount: free.toString()
|
7180
|
+
}, {
|
7181
|
+
type: "reserved",
|
7182
|
+
label: "reserved",
|
7183
|
+
amount: reserved.toString()
|
7184
|
+
}, {
|
7185
|
+
type: "locked",
|
7186
|
+
label: "frozen",
|
7187
|
+
amount: frozen.toString()
|
7188
|
+
}];
|
7189
|
+
return {
|
7190
|
+
source: "substrate-tokens",
|
7191
|
+
status: "live",
|
7192
|
+
address,
|
7193
|
+
networkId,
|
7194
|
+
tokenId: token.id,
|
7195
|
+
values: balanceValues
|
7196
|
+
};
|
7197
|
+
};
|
7198
|
+
return {
|
7199
|
+
stateKeys: [stateKey],
|
7200
|
+
decodeResult
|
7201
|
+
};
|
7202
|
+
}).filter(isNotNil);
|
7203
|
+
};
|
7204
|
+
|
7133
7205
|
const fetchBalances$1 = async ({
|
7134
7206
|
networkId,
|
7135
7207
|
tokensWithAddresses,
|
7136
7208
|
connector,
|
7137
7209
|
miniMetadata
|
7138
7210
|
}) => {
|
7211
|
+
if (!tokensWithAddresses.length) return {
|
7212
|
+
success: [],
|
7213
|
+
errors: []
|
7214
|
+
};
|
7139
7215
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
7140
7216
|
if (!miniMetadata?.data) {
|
7141
7217
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE} balances on ${networkId}.`);
|
@@ -7171,7 +7247,7 @@ const fetchBalances$1 = async ({
|
|
7171
7247
|
};
|
7172
7248
|
}
|
7173
7249
|
const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
|
7174
|
-
const balances = await
|
7250
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
7175
7251
|
return balanceDefs.reduce((acc, def) => {
|
7176
7252
|
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
7177
7253
|
if (balance) acc.success.push(balance);
|
@@ -7198,67 +7274,6 @@ const fetchBalances$1 = async ({
|
|
7198
7274
|
errors: []
|
7199
7275
|
});
|
7200
7276
|
};
|
7201
|
-
const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
|
7202
|
-
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7203
|
-
storage: [miniMetadata.extra.palletId, "Accounts"]
|
7204
|
-
});
|
7205
|
-
return balanceDefs.map(({
|
7206
|
-
token,
|
7207
|
-
address
|
7208
|
-
}) => {
|
7209
|
-
const scaleCoder = networkStorageCoders?.storage;
|
7210
|
-
const getStateKey = onChainId => {
|
7211
|
-
try {
|
7212
|
-
return scaleCoder.keys.enc(address, papiParse(onChainId));
|
7213
|
-
} catch {
|
7214
|
-
return null;
|
7215
|
-
}
|
7216
|
-
};
|
7217
|
-
const stateKey = getStateKey(token.onChainId);
|
7218
|
-
if (!stateKey) {
|
7219
|
-
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
7220
|
-
return null;
|
7221
|
-
}
|
7222
|
-
const decodeResult = change => {
|
7223
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7224
|
-
|
7225
|
-
const decoded = decodeScale(scaleCoder, change, `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
|
7226
|
-
free: 0n,
|
7227
|
-
reserved: 0n,
|
7228
|
-
frozen: 0n
|
7229
|
-
};
|
7230
|
-
const free = (decoded?.free ?? 0n).toString();
|
7231
|
-
const reserved = (decoded?.reserved ?? 0n).toString();
|
7232
|
-
const frozen = (decoded?.frozen ?? 0n).toString();
|
7233
|
-
const balanceValues = [{
|
7234
|
-
type: "free",
|
7235
|
-
label: "free",
|
7236
|
-
amount: free.toString()
|
7237
|
-
}, {
|
7238
|
-
type: "reserved",
|
7239
|
-
label: "reserved",
|
7240
|
-
amount: reserved.toString()
|
7241
|
-
}, {
|
7242
|
-
type: "locked",
|
7243
|
-
label: "frozen",
|
7244
|
-
amount: frozen.toString()
|
7245
|
-
}];
|
7246
|
-
return {
|
7247
|
-
source: "substrate-tokens",
|
7248
|
-
status: "live",
|
7249
|
-
address,
|
7250
|
-
networkId,
|
7251
|
-
tokenId: token.id,
|
7252
|
-
values: balanceValues
|
7253
|
-
};
|
7254
|
-
};
|
7255
|
-
return {
|
7256
|
-
chainId: networkId,
|
7257
|
-
stateKey,
|
7258
|
-
decodeResult
|
7259
|
-
};
|
7260
|
-
}).filter(isNotNil);
|
7261
|
-
};
|
7262
7277
|
|
7263
7278
|
const fetchTokens = async ({
|
7264
7279
|
networkId,
|
@@ -7459,46 +7474,22 @@ const getCallDataOptions = (to, token, value, type, config) => {
|
|
7459
7474
|
}] : []));
|
7460
7475
|
};
|
7461
7476
|
|
7462
|
-
const SUBSCRIPTION_INTERVAL = 6_000;
|
7463
7477
|
const subscribeBalances = ({
|
7464
7478
|
networkId,
|
7465
7479
|
tokensWithAddresses,
|
7466
7480
|
connector,
|
7467
7481
|
miniMetadata
|
7468
7482
|
}) => {
|
7469
|
-
|
7470
|
-
|
7471
|
-
|
7472
|
-
|
7473
|
-
|
7474
|
-
|
7475
|
-
|
7476
|
-
|
7477
|
-
|
7478
|
-
|
7479
|
-
tokensWithAddresses: tokensWithAddresses,
|
7480
|
-
connector,
|
7481
|
-
miniMetadata
|
7482
|
-
});
|
7483
|
-
if (abortController.signal.aborted) return;
|
7484
|
-
subscriber.next(balances);
|
7485
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL);
|
7486
|
-
} catch (error) {
|
7487
|
-
log.error("Error", {
|
7488
|
-
module: MODULE_TYPE,
|
7489
|
-
networkId,
|
7490
|
-
miniMetadata,
|
7491
|
-
addressesByToken: tokensWithAddresses,
|
7492
|
-
error
|
7493
|
-
});
|
7494
|
-
subscriber.error(error);
|
7495
|
-
}
|
7496
|
-
};
|
7497
|
-
poll();
|
7498
|
-
return () => {
|
7499
|
-
abortController.abort();
|
7500
|
-
};
|
7501
|
-
}).pipe(distinctUntilChanged(isEqual));
|
7483
|
+
if (!tokensWithAddresses.length) return of({
|
7484
|
+
success: [],
|
7485
|
+
errors: []
|
7486
|
+
});
|
7487
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
7488
|
+
const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
|
7489
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(map(balances => ({
|
7490
|
+
success: balances,
|
7491
|
+
errors: []
|
7492
|
+
})));
|
7502
7493
|
};
|
7503
7494
|
|
7504
7495
|
const SubTokensBalanceModule = {
|
@@ -10358,9 +10349,17 @@ class BalancesProvider {
|
|
10358
10349
|
miniMetadatas: values(miniMetadatas).filter(isNotNil)
|
10359
10350
|
})));
|
10360
10351
|
}
|
10361
|
-
|
10362
|
-
|
10363
|
-
|
10352
|
+
|
10353
|
+
// this is the only public method
|
10354
|
+
getBalances$(addressesByTokenId) {
|
10355
|
+
// split by network
|
10356
|
+
const addressesByTokenIdByNetworkId = toPairs(addressesByTokenId).reduce((acc, [tokenId, addresses]) => {
|
10357
|
+
const networkId = parseTokenId(tokenId).networkId;
|
10358
|
+
if (!acc[networkId]) acc[networkId] = {};
|
10359
|
+
acc[networkId][tokenId] = addresses;
|
10360
|
+
return acc;
|
10361
|
+
}, {});
|
10362
|
+
return combineLatest(toPairs(addressesByTokenIdByNetworkId).map(([networkId]) => this.getNetworkBalances$(networkId, addressesByTokenIdByNetworkId[networkId]))).pipe(map(results => {
|
10364
10363
|
return {
|
10365
10364
|
status: results.some(({
|
10366
10365
|
status
|
@@ -10369,9 +10368,17 @@ class BalancesProvider {
|
|
10369
10368
|
};
|
10370
10369
|
}), startWith({
|
10371
10370
|
status: "initialising",
|
10372
|
-
balances: this.getStoredBalances(
|
10371
|
+
balances: this.getStoredBalances(addressesByTokenId)
|
10373
10372
|
}), distinctUntilChanged(isEqual));
|
10374
10373
|
}
|
10374
|
+
fetchBalances(addressesByTokenId) {
|
10375
|
+
// TODO: better
|
10376
|
+
return firstValueFrom(this.getBalances$(addressesByTokenId).pipe(filter(({
|
10377
|
+
status
|
10378
|
+
}) => status === "live"), map(({
|
10379
|
+
balances
|
10380
|
+
}) => balances)));
|
10381
|
+
}
|
10375
10382
|
getNetworkBalances$(networkId, addressesByTokenId) {
|
10376
10383
|
const network$ = this.#chaindataProvider.getNetworkById$(networkId);
|
10377
10384
|
const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
|
@@ -10443,7 +10450,42 @@ class BalancesProvider {
|
|
10443
10450
|
}));
|
10444
10451
|
}
|
10445
10452
|
getNetworkMiniMetadatas$(networkId) {
|
10446
|
-
return this.#chaindataProvider.getNetworkById$(networkId).pipe(switchMap(network => isNetworkDot(network) && this.#chainConnectors.substrate ? from(
|
10453
|
+
return this.#chaindataProvider.getNetworkById$(networkId).pipe(switchMap(network => isNetworkDot(network) && this.#chainConnectors.substrate ? from(getSpecVersion(this.#chainConnectors.substrate, networkId)).pipe(switchMap(specVersion => this.getMiniMetadatas$(networkId, specVersion))) : of([])));
|
10454
|
+
}
|
10455
|
+
getMiniMetadatas$(networkId, specVersion) {
|
10456
|
+
return combineLatest({
|
10457
|
+
defaultMiniMetadatas: this.getDefaultMiniMetadatas$(networkId, specVersion),
|
10458
|
+
storedMiniMetadatas: this.getStoredMiniMetadatas$(networkId, specVersion)
|
10459
|
+
}).pipe(switchMap(({
|
10460
|
+
storedMiniMetadatas,
|
10461
|
+
defaultMiniMetadatas
|
10462
|
+
}) => {
|
10463
|
+
if (defaultMiniMetadatas.length) return of(defaultMiniMetadatas);
|
10464
|
+
if (storedMiniMetadatas.length) return of(storedMiniMetadatas);
|
10465
|
+
if (!this.#chainConnectors.substrate) return of([]);
|
10466
|
+
return from(
|
10467
|
+
// fetch them from the chain
|
10468
|
+
getMiniMetadatas(this.#chainConnectors.substrate, this.#chaindataProvider, networkId)).pipe(
|
10469
|
+
// and persist in storage for later reuse
|
10470
|
+
tap(newMiniMetadatas => {
|
10471
|
+
if (!newMiniMetadatas.length) return;
|
10472
|
+
const storage = this.#storage.getValue();
|
10473
|
+
const miniMetadatas = assign(
|
10474
|
+
// keep minimetadatas of other networks
|
10475
|
+
keyBy(values(storage.miniMetadatas).filter(m => m.chainId !== networkId), m => m.id),
|
10476
|
+
// add the ones for our network
|
10477
|
+
keyBy(newMiniMetadatas, m => m.id));
|
10478
|
+
this.#storage.next(assign({}, storage, {
|
10479
|
+
miniMetadatas
|
10480
|
+
}));
|
10481
|
+
}));
|
10482
|
+
}));
|
10483
|
+
}
|
10484
|
+
getStoredMiniMetadatas$(networkId, specVersion) {
|
10485
|
+
return this.storage$.pipe(map(storage => storage.miniMetadatas.filter(m => m.chainId === networkId && m.specVersion === specVersion && m.version === MINIMETADATA_VERSION)), distinctUntilChanged(isEqual));
|
10486
|
+
}
|
10487
|
+
getDefaultMiniMetadatas$(networkId, specVersion) {
|
10488
|
+
return this.#chaindataProvider.miniMetadatas$.pipe(map(miniMetadatas => miniMetadatas.filter(m => m.chainId === networkId && m.specVersion === specVersion && m.version === MINIMETADATA_VERSION)), distinctUntilChanged(isEqual));
|
10447
10489
|
}
|
10448
10490
|
getStoredBalances(addressesByToken) {
|
10449
10491
|
const balanceDefs = toPairs(addressesByToken).flatMap(([tokenId, addresses]) => addresses.map(address => [tokenId, address]));
|
@@ -10454,11 +10496,4 @@ class BalancesProvider {
|
|
10454
10496
|
}
|
10455
10497
|
}
|
10456
10498
|
|
10457
|
-
// const getStoredBalances = (
|
10458
|
-
// storedBalances: Record<string, IBalance>,
|
10459
|
-
// addressesByToken: Record<TokenId, Address[]>,
|
10460
|
-
// ): IBalance[] => {
|
10461
|
-
|
10462
|
-
// }
|
10463
|
-
|
10464
10499
|
export { BALANCE_MODULES, Balance, BalanceFormatter, BalanceValueGetter, Balances, BalancesProvider, Change24hCurrencyFormatter, DefaultBalanceModule, EvmErc20BalanceModule, EvmErc20TokenConfigSchema, EvmNativeBalanceModule, EvmNativeTokenConfigSchema, EvmUniswapV2BalanceModule, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, ONE_ALPHA_TOKEN$1 as ONE_ALPHA_TOKEN, PlanckSumBalancesFormatter, RpcStateQueryHelper, SCALE_FACTOR$1 as SCALE_FACTOR, SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1 as SUBTENSOR_MIN_STAKE_AMOUNT_PLANK, SUBTENSOR_ROOT_NETUID$1 as SUBTENSOR_ROOT_NETUID, SubAssetsBalanceModule, SubAssetsTokenConfigSchema, SubForeignAssetsBalanceModule, SubForeignAssetsTokenConfigSchema, SubHydrationBalanceModule, SubHydrationTokenConfigSchema, SubNativeBalanceModule, SubNativeMiniMetadataExtraSchema, SubNativeModuleConfigSchema, SubNativeTokenConfigSchema, SubPsp22BalanceModule, SubPsp22TokenConfigSchema, SubTokensBalanceModule, SubTokensMiniMetadataExtraSchema, SubTokensModuleConfigSchema, SubTokensTokenConfigSchema, SumBalancesFormatter, TalismanBalancesDatabase, abiMulticall, balances, buildNetworkStorageCoders, buildStorageCoders, calculateAlphaPrice$1 as calculateAlphaPrice, calculateTaoAmountFromAlpha$1 as calculateTaoAmountFromAlpha, calculateTaoFromDynamicInfo$1 as calculateTaoFromDynamicInfo, compress, configureStore, db, decodeOutput, decompress, defaultBalanceModules, deriveMiniMetadataId, detectTransferMethod, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, getBalanceId, getLockTitle, getLockedType$1 as getLockedType, getUniqueChainIds, getValueId, includeInTotalExtraAmount, makeContractCaller, uniswapV2PairAbi };
|