@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
@@ -15,10 +15,10 @@ var z = require('zod/v4');
|
|
15
15
|
var rxjs = require('rxjs');
|
16
16
|
var isEqual = require('lodash/isEqual');
|
17
17
|
var scale = require('@talismn/scale');
|
18
|
-
var types = require('@polkadot/types');
|
19
|
-
var groupBy = require('lodash/groupBy');
|
20
18
|
var utils = require('@polkadot-api/utils');
|
21
19
|
var polkadotApi = require('polkadot-api');
|
20
|
+
var types = require('@polkadot/types');
|
21
|
+
var groupBy = require('lodash/groupBy');
|
22
22
|
var upperFirst = require('lodash/upperFirst');
|
23
23
|
var scaleTs = require('scale-ts');
|
24
24
|
var apiContract = require('@polkadot/api-contract');
|
@@ -1553,6 +1553,10 @@ const fetchBalances$c = async ({
|
|
1553
1553
|
tokensWithAddresses,
|
1554
1554
|
connector
|
1555
1555
|
}) => {
|
1556
|
+
if (!tokensWithAddresses.length) return {
|
1557
|
+
success: [],
|
1558
|
+
errors: []
|
1559
|
+
};
|
1556
1560
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1557
1561
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
1558
1562
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -1779,12 +1783,16 @@ const getTransferCallData$8 = ({
|
|
1779
1783
|
};
|
1780
1784
|
};
|
1781
1785
|
|
1782
|
-
const SUBSCRIPTION_INTERVAL$
|
1786
|
+
const SUBSCRIPTION_INTERVAL$4 = 6_000;
|
1783
1787
|
const subscribeBalances$8 = ({
|
1784
1788
|
networkId,
|
1785
1789
|
tokensWithAddresses,
|
1786
1790
|
connector
|
1787
1791
|
}) => {
|
1792
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
1793
|
+
success: [],
|
1794
|
+
errors: []
|
1795
|
+
});
|
1788
1796
|
return new rxjs.Observable(subscriber => {
|
1789
1797
|
const abortController = new AbortController();
|
1790
1798
|
const poll = async () => {
|
@@ -1797,7 +1805,7 @@ const subscribeBalances$8 = ({
|
|
1797
1805
|
});
|
1798
1806
|
if (abortController.signal.aborted) return;
|
1799
1807
|
subscriber.next(balances);
|
1800
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
1808
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
1801
1809
|
} catch (error) {
|
1802
1810
|
log.error("Error", {
|
1803
1811
|
module: MODULE_TYPE$8,
|
@@ -1843,6 +1851,10 @@ const fetchBalances$b = async ({
|
|
1843
1851
|
tokensWithAddresses,
|
1844
1852
|
connector
|
1845
1853
|
}) => {
|
1854
|
+
if (!tokensWithAddresses.length) return {
|
1855
|
+
success: [],
|
1856
|
+
errors: []
|
1857
|
+
};
|
1846
1858
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1847
1859
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
1848
1860
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -1993,12 +2005,16 @@ const getTransferCallData$7 = ({
|
|
1993
2005
|
};
|
1994
2006
|
};
|
1995
2007
|
|
1996
|
-
const SUBSCRIPTION_INTERVAL$
|
2008
|
+
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
1997
2009
|
const subscribeBalances$7 = ({
|
1998
2010
|
networkId,
|
1999
2011
|
tokensWithAddresses,
|
2000
2012
|
connector
|
2001
2013
|
}) => {
|
2014
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
2015
|
+
success: [],
|
2016
|
+
errors: []
|
2017
|
+
});
|
2002
2018
|
return new rxjs.Observable(subscriber => {
|
2003
2019
|
const abortController = new AbortController();
|
2004
2020
|
const poll = async () => {
|
@@ -2011,7 +2027,7 @@ const subscribeBalances$7 = ({
|
|
2011
2027
|
});
|
2012
2028
|
if (abortController.signal.aborted) return;
|
2013
2029
|
subscriber.next(balances);
|
2014
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
2030
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
2015
2031
|
} catch (error) {
|
2016
2032
|
log.error("Error", {
|
2017
2033
|
module: MODULE_TYPE$7,
|
@@ -2052,6 +2068,10 @@ const fetchBalances$a = async ({
|
|
2052
2068
|
tokensWithAddresses,
|
2053
2069
|
connector
|
2054
2070
|
}) => {
|
2071
|
+
if (!tokensWithAddresses.length) return {
|
2072
|
+
success: [],
|
2073
|
+
errors: []
|
2074
|
+
};
|
2055
2075
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
2056
2076
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
2057
2077
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -2326,12 +2346,16 @@ const getTransferCallData$6 = ({
|
|
2326
2346
|
};
|
2327
2347
|
};
|
2328
2348
|
|
2329
|
-
const SUBSCRIPTION_INTERVAL$
|
2349
|
+
const SUBSCRIPTION_INTERVAL$2 = 6_000;
|
2330
2350
|
const subscribeBalances$6 = ({
|
2331
2351
|
networkId,
|
2332
2352
|
tokensWithAddresses,
|
2333
2353
|
connector
|
2334
2354
|
}) => {
|
2355
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
2356
|
+
success: [],
|
2357
|
+
errors: []
|
2358
|
+
});
|
2335
2359
|
return new rxjs.Observable(subscriber => {
|
2336
2360
|
const abortController = new AbortController();
|
2337
2361
|
const poll = async () => {
|
@@ -2344,7 +2368,7 @@ const subscribeBalances$6 = ({
|
|
2344
2368
|
});
|
2345
2369
|
if (abortController.signal.aborted) return;
|
2346
2370
|
subscriber.next(balances);
|
2347
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
2371
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
|
2348
2372
|
} catch (error) {
|
2349
2373
|
log.error("Error", {
|
2350
2374
|
module: MODULE_TYPE$6,
|
@@ -3208,273 +3232,138 @@ async function getPoolBalance(publicClient, contractAddress, accountAddress) {
|
|
3208
3232
|
const MODULE_TYPE$5 = chaindataProvider.SubAssetsTokenSchema.shape.type.value;
|
3209
3233
|
const PLATFORM$5 = chaindataProvider.SubAssetsTokenSchema.shape.platform.value;
|
3210
3234
|
|
3211
|
-
|
3212
|
-
|
3213
|
-
|
3214
|
-
|
3235
|
+
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
3236
|
+
const allStateKeys = queries.flatMap(({
|
3237
|
+
stateKeys
|
3238
|
+
}) => stateKeys).filter(util.isNotNil);
|
3215
3239
|
|
3216
|
-
|
3217
|
-
|
3218
|
-
|
3219
|
-
|
3220
|
-
}
|
3240
|
+
// doing a query without keys would throw an error => return early
|
3241
|
+
if (!allStateKeys.length) return queries.map(({
|
3242
|
+
stateKeys,
|
3243
|
+
decodeResult
|
3244
|
+
}) => decodeResult(stateKeys.map(() => null)));
|
3245
|
+
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
3246
|
+
return decodeRpcQueryPack(queries, result);
|
3247
|
+
};
|
3248
|
+
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
3249
|
+
const allStateKeys = queries.flatMap(({
|
3250
|
+
stateKeys
|
3251
|
+
}) => stateKeys).filter(util.isNotNil);
|
3221
3252
|
|
3222
|
-
//
|
3223
|
-
return
|
3224
|
-
|
3253
|
+
// doing a query without keys would throw an error => return early
|
3254
|
+
if (!allStateKeys.length) return rxjs.of(queries.map(({
|
3255
|
+
stateKeys,
|
3256
|
+
decodeResult
|
3257
|
+
}) => decodeResult(stateKeys.map(() => null))));
|
3258
|
+
return new rxjs.Observable(subscriber => {
|
3259
|
+
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
3260
|
+
if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
|
3261
|
+
}, timeout);
|
3262
|
+
return () => {
|
3263
|
+
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
3264
|
+
};
|
3265
|
+
});
|
3266
|
+
};
|
3267
|
+
const decodeRpcQueryPack = (queries, result) => {
|
3268
|
+
return queries.reduce((acc, {
|
3269
|
+
stateKeys,
|
3270
|
+
decodeResult
|
3271
|
+
}) => {
|
3272
|
+
const changes = stateKeys.map(stateKey => {
|
3273
|
+
if (!stateKey) return null;
|
3274
|
+
const change = result.changes.find(([key]) => key === stateKey);
|
3275
|
+
if (!change) return null;
|
3276
|
+
return change[1];
|
3277
|
+
});
|
3278
|
+
acc.push(decodeResult(changes));
|
3279
|
+
return acc;
|
3280
|
+
}, []);
|
3281
|
+
};
|
3225
3282
|
|
3226
|
-
|
3227
|
-
const
|
3228
|
-
|
3229
|
-
|
3230
|
-
|
3231
|
-
|
3232
|
-
|
3233
|
-
|
3234
|
-
|
3235
|
-
|
3236
|
-
|
3237
|
-
|
3238
|
-
const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
|
3239
|
-
try {
|
3240
|
-
const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
|
3241
|
-
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3242
|
-
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3243
|
-
chainId
|
3244
|
-
}) : moduleMethodOrFn;
|
3245
|
-
try {
|
3246
|
-
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3247
|
-
} catch (cause) {
|
3248
|
-
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3249
|
-
return [];
|
3250
|
-
}
|
3251
|
-
}));
|
3252
|
-
return [[chainId, builtCoders]];
|
3253
|
-
} catch (cause) {
|
3254
|
-
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3255
|
-
return [];
|
3256
|
-
}
|
3257
|
-
}));
|
3258
|
-
// type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
|
3283
|
+
const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
|
3284
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3285
|
+
storage: ["Assets", "Account"]
|
3286
|
+
});
|
3287
|
+
return balanceDefs.map(({
|
3288
|
+
token,
|
3289
|
+
address
|
3290
|
+
}) => {
|
3291
|
+
const scaleCoder = networkStorageCoders?.storage;
|
3292
|
+
const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
|
3293
|
+
// Asset Hub
|
3294
|
+
tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
|
3259
3295
|
|
3260
|
-
|
3261
|
-
|
3262
|
-
|
3296
|
+
if (!stateKey) {
|
3297
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
|
3298
|
+
return null;
|
3299
|
+
}
|
3300
|
+
const decodeResult = changes => {
|
3301
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3302
|
+
|
3303
|
+
const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
3304
|
+
balance: 0n,
|
3305
|
+
status: {
|
3306
|
+
type: "Liquid"
|
3307
|
+
}};
|
3308
|
+
const isFrozen = decoded?.status?.type === "Frozen";
|
3309
|
+
const amount = (decoded?.balance ?? 0n).toString();
|
3310
|
+
|
3311
|
+
// due to the following balance calculations, which are made in the `Balance` type:
|
3312
|
+
//
|
3313
|
+
// total balance = (free balance) + (reserved balance)
|
3314
|
+
// transferable balance = (free balance) - (frozen balance)
|
3315
|
+
//
|
3316
|
+
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
3317
|
+
// of this balance to the value we received from the RPC.
|
3318
|
+
//
|
3319
|
+
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
3320
|
+
const free = amount;
|
3321
|
+
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
3322
|
+
|
3323
|
+
// include balance values even if zero, so that newly-zero values overwrite old values
|
3324
|
+
const balanceValues = [{
|
3325
|
+
type: "free",
|
3326
|
+
label: "free",
|
3327
|
+
amount: free.toString()
|
3328
|
+
}, {
|
3329
|
+
type: "locked",
|
3330
|
+
label: "frozen",
|
3331
|
+
amount: frozen.toString()
|
3332
|
+
}];
|
3333
|
+
const balance = {
|
3334
|
+
source: "substrate-assets",
|
3335
|
+
status: "live",
|
3336
|
+
address,
|
3337
|
+
networkId,
|
3338
|
+
tokenId: token.id,
|
3339
|
+
values: balanceValues
|
3340
|
+
};
|
3341
|
+
return balance;
|
3342
|
+
};
|
3343
|
+
return {
|
3344
|
+
stateKeys: [stateKey],
|
3345
|
+
decodeResult
|
3346
|
+
};
|
3347
|
+
}).filter(util.isNotNil);
|
3348
|
+
};
|
3349
|
+
const tryEncode$1 = (scaleCoder, ...args) => {
|
3263
3350
|
try {
|
3264
|
-
|
3265
|
-
|
3266
|
-
|
3267
|
-
chainId
|
3268
|
-
}) : moduleMethodOrFn;
|
3269
|
-
try {
|
3270
|
-
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3271
|
-
} catch (cause) {
|
3272
|
-
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3273
|
-
return [];
|
3274
|
-
}
|
3275
|
-
}));
|
3276
|
-
return builtCoders;
|
3277
|
-
} catch (cause) {
|
3278
|
-
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3351
|
+
return scaleCoder?.keys?.enc?.(...args);
|
3352
|
+
} catch {
|
3353
|
+
return null;
|
3279
3354
|
}
|
3280
|
-
return null;
|
3281
3355
|
};
|
3282
3356
|
|
3283
|
-
|
3284
|
-
|
3285
|
-
|
3286
|
-
|
3287
|
-
|
3288
|
-
|
3289
|
-
|
3290
|
-
|
3291
|
-
|
3292
|
-
let decodedOutput = "";
|
3293
|
-
let isError = true;
|
3294
|
-
if (result.isOk) {
|
3295
|
-
const flags = result.asOk.flags.toHuman();
|
3296
|
-
isError = flags.includes("Revert");
|
3297
|
-
const abiMessage = getAbiMessage(abi, method);
|
3298
|
-
const returnType = abiMessage.returnType;
|
3299
|
-
const returnTypeName = getReturnTypeName(returnType);
|
3300
|
-
const r = returnType ? registry.createTypeUnsafe(returnTypeName, [result.asOk.data]).toHuman() : "()";
|
3301
|
-
output = isOk(r) ? r.Ok : isErr(r) ? r.Err : r;
|
3302
|
-
const errorText = isErr(output) ? typeof output.Err === "object" ? JSON.stringify(output.Err, null, 2) : output.Err?.toString() ?? "Error" : output !== "Ok" ? output?.toString() || "Error" : "Error";
|
3303
|
-
const okText = isOk(r) ? typeof output === "object" ? JSON.stringify(output, null, "\t") : output?.toString() ?? "()" : JSON.stringify(output, null, "\t") ?? "()";
|
3304
|
-
decodedOutput = isError ? errorText : okText;
|
3305
|
-
} else if (result.isErr) {
|
3306
|
-
output = result.toHuman();
|
3307
|
-
let errorText;
|
3308
|
-
if (isErr(output) && typeof output.Err === "object" && Object.keys(output.Err || {}).length && typeof Object.values(output.Err || {})[0] === "string") {
|
3309
|
-
const [errorKey, errorValue] = Object.entries(output.Err || {})[0];
|
3310
|
-
errorText = `${errorKey}${errorValue}`;
|
3311
|
-
}
|
3312
|
-
decodedOutput = errorText || "Error";
|
3313
|
-
}
|
3314
|
-
return {
|
3315
|
-
output,
|
3316
|
-
decodedOutput,
|
3317
|
-
isError
|
3357
|
+
const fetchBalances$6 = async ({
|
3358
|
+
networkId,
|
3359
|
+
tokensWithAddresses,
|
3360
|
+
connector,
|
3361
|
+
miniMetadata
|
3362
|
+
}) => {
|
3363
|
+
if (!tokensWithAddresses.length) return {
|
3364
|
+
success: [],
|
3365
|
+
errors: []
|
3318
3366
|
};
|
3319
|
-
}
|
3320
|
-
|
3321
|
-
/**
|
3322
|
-
* Helper types & functions
|
3323
|
-
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3324
|
-
*/
|
3325
|
-
|
3326
|
-
function isErr(o) {
|
3327
|
-
return typeof o === "object" && o !== null && "Err" in o;
|
3328
|
-
}
|
3329
|
-
function isOk(o) {
|
3330
|
-
return typeof o === "object" && o !== null && "Ok" in o;
|
3331
|
-
}
|
3332
|
-
function getReturnTypeName(type) {
|
3333
|
-
return type?.lookupName || type?.type || "";
|
3334
|
-
}
|
3335
|
-
function getAbiMessage(abi, method) {
|
3336
|
-
const abiMessage = abi.messages.find(m => util$1.stringCamelCase(m.method) === util$1.stringCamelCase(method));
|
3337
|
-
if (!abiMessage) {
|
3338
|
-
throw new Error(`"${method}" not found in Contract`);
|
3339
|
-
}
|
3340
|
-
return abiMessage;
|
3341
|
-
}
|
3342
|
-
|
3343
|
-
/**
|
3344
|
-
*
|
3345
|
-
* Detect Balances::transfer -> Balances::transfer_allow_death migration
|
3346
|
-
* https://github.com/paritytech/substrate/pull/12951
|
3347
|
-
*
|
3348
|
-
* `transfer_allow_death` is the preferred method,
|
3349
|
-
* so if something goes wrong during detection, we should assume the chain has migrated
|
3350
|
-
* @param metadataRpc string containing the hashed RPC metadata for the chain
|
3351
|
-
* @returns
|
3352
|
-
*/
|
3353
|
-
const detectTransferMethod = metadataRpc => {
|
3354
|
-
const pjsMetadata = new types.Metadata(new types.TypeRegistry(), metadataRpc);
|
3355
|
-
pjsMetadata.registry.setMetadata(pjsMetadata);
|
3356
|
-
const balancesPallet = pjsMetadata.asLatest.pallets.find(pallet => pallet.name.eq("Balances"));
|
3357
|
-
const balancesCallsTypeIndex = balancesPallet?.calls.value.type.toNumber();
|
3358
|
-
const balancesCallsType = balancesCallsTypeIndex !== undefined ? pjsMetadata.asLatest.lookup.types[balancesCallsTypeIndex] : undefined;
|
3359
|
-
const hasDeprecatedTransferCall = balancesCallsType?.type.def.asVariant?.variants.find(variant => variant.name.eq("transfer")) !== undefined;
|
3360
|
-
return hasDeprecatedTransferCall ? "transfer" : "transfer_allow_death";
|
3361
|
-
};
|
3362
|
-
|
3363
|
-
const getUniqueChainIds = (addressesByToken, tokens) => [...new Set(Object.keys(addressesByToken).map(tokenId => tokens[tokenId]?.networkId).flatMap(chainId => chainId ? [chainId] : []))];
|
3364
|
-
|
3365
|
-
const makeContractCaller = ({
|
3366
|
-
chainConnector,
|
3367
|
-
chainId,
|
3368
|
-
registry
|
3369
|
-
}) => async (callFrom, contractAddress, inputData) => registry.createType("ContractExecResult", await chainConnector.send(chainId, "state_call", ["ContractsApi_call", util$1.u8aToHex(util$1.u8aConcatStrict([
|
3370
|
-
// origin
|
3371
|
-
registry.createType("AccountId", callFrom).toU8a(),
|
3372
|
-
// dest
|
3373
|
-
registry.createType("AccountId", contractAddress).toU8a(),
|
3374
|
-
// value
|
3375
|
-
registry.createType("Balance", 0).toU8a(),
|
3376
|
-
// gasLimit
|
3377
|
-
registry.createType("Option<WeightV2>").toU8a(),
|
3378
|
-
// storageDepositLimit
|
3379
|
-
registry.createType("Option<Balance>").toU8a(),
|
3380
|
-
// inputData
|
3381
|
-
inputData instanceof Uint8Array ? inputData : inputData.toU8a()]))]));
|
3382
|
-
|
3383
|
-
/**
|
3384
|
-
* Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
|
3385
|
-
*/
|
3386
|
-
|
3387
|
-
/**
|
3388
|
-
* Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
|
3389
|
-
*/
|
3390
|
-
class RpcStateQueryHelper {
|
3391
|
-
#chainConnector;
|
3392
|
-
#queries;
|
3393
|
-
constructor(chainConnector, queries) {
|
3394
|
-
this.#chainConnector = chainConnector;
|
3395
|
-
this.#queries = queries;
|
3396
|
-
}
|
3397
|
-
async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
|
3398
|
-
const queriesByChain = groupBy__default.default(this.#queries, "chainId");
|
3399
|
-
const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
|
3400
|
-
const params = [queries.map(({
|
3401
|
-
stateKey
|
3402
|
-
}) => stateKey)];
|
3403
|
-
const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
|
3404
|
-
error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
|
3405
|
-
}, timeout);
|
3406
|
-
return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
|
3407
|
-
});
|
3408
|
-
return () => subscriptions.forEach(unsubscribe => unsubscribe());
|
3409
|
-
}
|
3410
|
-
async fetch(method = "state_queryStorageAt") {
|
3411
|
-
const queriesByChain = groupBy__default.default(this.#queries, "chainId");
|
3412
|
-
const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
|
3413
|
-
const params = [queries.map(({
|
3414
|
-
stateKey
|
3415
|
-
}) => stateKey)];
|
3416
|
-
const result = (await this.#chainConnector.send(chainId, method, params))[0];
|
3417
|
-
return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
|
3418
|
-
}));
|
3419
|
-
return resultsByChain.flatMap(result => result);
|
3420
|
-
}
|
3421
|
-
#distributeChangesToQueryDecoders(chainId, result) {
|
3422
|
-
if (typeof result !== "object" || result === null) return [];
|
3423
|
-
if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
|
3424
|
-
if (!Array.isArray(result.changes)) return [];
|
3425
|
-
return result.changes.flatMap(([reference, change]) => {
|
3426
|
-
if (typeof reference !== "string") {
|
3427
|
-
log.warn(`Received non-string reference in RPC result: ${reference}`);
|
3428
|
-
return [];
|
3429
|
-
}
|
3430
|
-
if (typeof change !== "string" && change !== null) {
|
3431
|
-
log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
|
3432
|
-
return [];
|
3433
|
-
}
|
3434
|
-
const query = this.#queries.find(({
|
3435
|
-
chainId: cId,
|
3436
|
-
stateKey
|
3437
|
-
}) => cId === chainId && stateKey === reference);
|
3438
|
-
if (!query) {
|
3439
|
-
log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
|
3440
|
-
stateKey
|
3441
|
-
}) => stateKey)}`);
|
3442
|
-
return [];
|
3443
|
-
}
|
3444
|
-
return [query.decodeResult(change)];
|
3445
|
-
});
|
3446
|
-
}
|
3447
|
-
}
|
3448
|
-
|
3449
|
-
const configureStore = (dbTable = db.balancesBlob) => ({
|
3450
|
-
persistData: async balances => {
|
3451
|
-
const output = compress(balances);
|
3452
|
-
await dbTable.clear();
|
3453
|
-
await dbTable.put({
|
3454
|
-
data: output,
|
3455
|
-
id: Date.now().toString()
|
3456
|
-
});
|
3457
|
-
},
|
3458
|
-
retrieveData: async () => {
|
3459
|
-
const compressedData = await dbTable.toCollection().first();
|
3460
|
-
if (!compressedData) return [];
|
3461
|
-
return decompress(compressedData.data);
|
3462
|
-
}
|
3463
|
-
});
|
3464
|
-
const compress = balances => pako__default.default.deflate(JSON.stringify(balances));
|
3465
|
-
const decompress = data => {
|
3466
|
-
const decompressed = pako__default.default.inflate(data, {
|
3467
|
-
to: "string"
|
3468
|
-
});
|
3469
|
-
return JSON.parse(decompressed);
|
3470
|
-
};
|
3471
|
-
|
3472
|
-
const fetchBalances$6 = async ({
|
3473
|
-
networkId,
|
3474
|
-
tokensWithAddresses,
|
3475
|
-
connector,
|
3476
|
-
miniMetadata
|
3477
|
-
}) => {
|
3478
3367
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
3479
3368
|
if (!miniMetadata?.data) {
|
3480
3369
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$5} balances on ${networkId}.`);
|
@@ -3510,7 +3399,7 @@ const fetchBalances$6 = async ({
|
|
3510
3399
|
};
|
3511
3400
|
}
|
3512
3401
|
const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
|
3513
|
-
const balances = await
|
3402
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
3514
3403
|
return balanceDefs.reduce((acc, def) => {
|
3515
3404
|
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
3516
3405
|
if (balance) acc.success.push(balance);
|
@@ -3537,80 +3426,6 @@ const fetchBalances$6 = async ({
|
|
3537
3426
|
errors: []
|
3538
3427
|
});
|
3539
3428
|
};
|
3540
|
-
const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
|
3541
|
-
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3542
|
-
storage: ["Assets", "Account"]
|
3543
|
-
});
|
3544
|
-
return balanceDefs.map(({
|
3545
|
-
token,
|
3546
|
-
address
|
3547
|
-
}) => {
|
3548
|
-
const scaleCoder = networkStorageCoders?.storage;
|
3549
|
-
const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
|
3550
|
-
// Asset Hub
|
3551
|
-
tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
|
3552
|
-
|
3553
|
-
if (!stateKey) {
|
3554
|
-
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
|
3555
|
-
return null;
|
3556
|
-
}
|
3557
|
-
const decodeResult = change => {
|
3558
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3559
|
-
|
3560
|
-
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
3561
|
-
balance: 0n,
|
3562
|
-
status: {
|
3563
|
-
type: "Liquid"
|
3564
|
-
}};
|
3565
|
-
const isFrozen = decoded?.status?.type === "Frozen";
|
3566
|
-
const amount = (decoded?.balance ?? 0n).toString();
|
3567
|
-
|
3568
|
-
// due to the following balance calculations, which are made in the `Balance` type:
|
3569
|
-
//
|
3570
|
-
// total balance = (free balance) + (reserved balance)
|
3571
|
-
// transferable balance = (free balance) - (frozen balance)
|
3572
|
-
//
|
3573
|
-
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
3574
|
-
// of this balance to the value we received from the RPC.
|
3575
|
-
//
|
3576
|
-
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
3577
|
-
const free = amount;
|
3578
|
-
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
3579
|
-
|
3580
|
-
// include balance values even if zero, so that newly-zero values overwrite old values
|
3581
|
-
const balanceValues = [{
|
3582
|
-
type: "free",
|
3583
|
-
label: "free",
|
3584
|
-
amount: free.toString()
|
3585
|
-
}, {
|
3586
|
-
type: "locked",
|
3587
|
-
label: "frozen",
|
3588
|
-
amount: frozen.toString()
|
3589
|
-
}];
|
3590
|
-
const balance = {
|
3591
|
-
source: "substrate-assets",
|
3592
|
-
status: "live",
|
3593
|
-
address,
|
3594
|
-
networkId,
|
3595
|
-
tokenId: token.id,
|
3596
|
-
values: balanceValues
|
3597
|
-
};
|
3598
|
-
return balance;
|
3599
|
-
};
|
3600
|
-
return {
|
3601
|
-
chainId: networkId,
|
3602
|
-
stateKey,
|
3603
|
-
decodeResult
|
3604
|
-
};
|
3605
|
-
}).filter(util.isNotNil);
|
3606
|
-
};
|
3607
|
-
const tryEncode$1 = (scaleCoder, ...args) => {
|
3608
|
-
try {
|
3609
|
-
return scaleCoder?.keys?.enc?.(...args);
|
3610
|
-
} catch {
|
3611
|
-
return null;
|
3612
|
-
}
|
3613
|
-
};
|
3614
3429
|
|
3615
3430
|
const fetchTokens$5 = async ({
|
3616
3431
|
networkId,
|
@@ -3847,47 +3662,47 @@ const getTransferAllEncodedArgs$2 = (assetId, to, codec) => {
|
|
3847
3662
|
})]);
|
3848
3663
|
};
|
3849
3664
|
|
3850
|
-
const
|
3665
|
+
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
3666
|
+
const {
|
3667
|
+
builder
|
3668
|
+
} = scale.parseMetadataRpc(metadataRpc);
|
3669
|
+
const call = builder.buildRuntimeCall(apiName, method);
|
3670
|
+
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, scale.toHex(call.args.enc(args))]);
|
3671
|
+
return call.value.dec(hex);
|
3672
|
+
};
|
3673
|
+
|
3674
|
+
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
3675
|
+
const {
|
3676
|
+
unifiedMetadata,
|
3677
|
+
builder
|
3678
|
+
} = scale.parseMetadataRpc(metadataRpc);
|
3679
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
3680
|
+
name
|
3681
|
+
}) => name === pallet)?.constants.find(({
|
3682
|
+
name
|
3683
|
+
}) => name === constant)?.value;
|
3684
|
+
if (!encodedValue) return null;
|
3685
|
+
const codec = builder.buildConstant(pallet, constant);
|
3686
|
+
return codec.dec(encodedValue);
|
3687
|
+
};
|
3688
|
+
|
3851
3689
|
const subscribeBalances$5 = ({
|
3852
3690
|
networkId,
|
3853
3691
|
tokensWithAddresses,
|
3854
3692
|
connector,
|
3855
3693
|
miniMetadata
|
3856
3694
|
}) => {
|
3857
|
-
return
|
3858
|
-
|
3859
|
-
|
3860
|
-
|
3861
|
-
|
3862
|
-
|
3863
|
-
|
3864
|
-
|
3865
|
-
|
3866
|
-
|
3867
|
-
|
3868
|
-
connector,
|
3869
|
-
miniMetadata
|
3870
|
-
});
|
3871
|
-
if (abortController.signal.aborted) return;
|
3872
|
-
subscriber.next(balances);
|
3873
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
3874
|
-
} catch (error) {
|
3875
|
-
log.error("Error", {
|
3876
|
-
module: MODULE_TYPE$5,
|
3877
|
-
networkId,
|
3878
|
-
miniMetadata,
|
3879
|
-
addressesByToken: tokensWithAddresses,
|
3880
|
-
error
|
3881
|
-
});
|
3882
|
-
subscriber.error(error);
|
3883
|
-
}
|
3884
|
-
};
|
3885
|
-
poll();
|
3886
|
-
return () => {
|
3887
|
-
abortController.abort();
|
3888
|
-
};
|
3889
|
-
}).pipe(rxjs.distinctUntilChanged(lodash.isEqual));
|
3890
|
-
};
|
3695
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
3696
|
+
success: [],
|
3697
|
+
errors: []
|
3698
|
+
});
|
3699
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
3700
|
+
const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
|
3701
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
|
3702
|
+
success: balances,
|
3703
|
+
errors: []
|
3704
|
+
})));
|
3705
|
+
};
|
3891
3706
|
|
3892
3707
|
const SubAssetsBalanceModule = {
|
3893
3708
|
type: MODULE_TYPE$5,
|
@@ -3908,74 +3723,267 @@ const SubAssetsTokenConfigSchema = z__default.default.strictObject({
|
|
3908
3723
|
const MODULE_TYPE$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.type.value;
|
3909
3724
|
const PLATFORM$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.platform.value;
|
3910
3725
|
|
3911
|
-
|
3912
|
-
|
3913
|
-
|
3914
|
-
|
3915
|
-
|
3916
|
-
|
3917
|
-
|
3918
|
-
if (
|
3919
|
-
|
3920
|
-
|
3921
|
-
|
3922
|
-
|
3923
|
-
|
3924
|
-
|
3925
|
-
|
3926
|
-
|
3927
|
-
|
3726
|
+
/**
|
3727
|
+
* Wraps a BalanceModule's fetch/subscribe methods with a single `balances` method.
|
3728
|
+
* This `balances` method will subscribe if a callback parameter is provided, or otherwise fetch.
|
3729
|
+
*/
|
3730
|
+
|
3731
|
+
async function balances(balanceModule, addressesByToken, callback) {
|
3732
|
+
// subscription request
|
3733
|
+
if (callback !== undefined) return await balanceModule.subscribeBalances({
|
3734
|
+
addressesByToken
|
3735
|
+
}, callback);
|
3736
|
+
|
3737
|
+
// one-off request
|
3738
|
+
return await balanceModule.fetchBalances(addressesByToken);
|
3739
|
+
}
|
3740
|
+
|
3741
|
+
// TODO remove this one in favor of the network specific one below
|
3742
|
+
const buildStorageCoders = ({
|
3743
|
+
chainIds,
|
3744
|
+
chains,
|
3745
|
+
miniMetadatas,
|
3746
|
+
coders
|
3747
|
+
}) => new Map([...chainIds].flatMap(chainId => {
|
3748
|
+
const chain = chains[chainId];
|
3749
|
+
if (!chain) return [];
|
3750
|
+
const miniMetadata = miniMetadatas.get(chainId); // findMiniMetadata<TBalanceModule>(miniMetadatas, moduleType, chain)
|
3751
|
+
if (!miniMetadata) return [];
|
3752
|
+
if (!miniMetadata.data) return [];
|
3753
|
+
const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
|
3754
|
+
try {
|
3755
|
+
const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
|
3756
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3757
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3758
|
+
chainId
|
3759
|
+
}) : moduleMethodOrFn;
|
3760
|
+
try {
|
3761
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3762
|
+
} catch (cause) {
|
3763
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3764
|
+
return [];
|
3765
|
+
}
|
3766
|
+
}));
|
3767
|
+
return [[chainId, builtCoders]];
|
3768
|
+
} catch (cause) {
|
3769
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3770
|
+
return [];
|
3928
3771
|
}
|
3929
|
-
|
3930
|
-
|
3931
|
-
|
3932
|
-
|
3933
|
-
|
3934
|
-
|
3935
|
-
|
3936
|
-
|
3937
|
-
|
3938
|
-
|
3772
|
+
}));
|
3773
|
+
// type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
|
3774
|
+
|
3775
|
+
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
3776
|
+
if (!miniMetadata.data) return null;
|
3777
|
+
const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
|
3778
|
+
try {
|
3779
|
+
const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
|
3780
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3781
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3782
|
+
chainId
|
3783
|
+
}) : moduleMethodOrFn;
|
3784
|
+
try {
|
3785
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3786
|
+
} catch (cause) {
|
3787
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3788
|
+
return [];
|
3789
|
+
}
|
3790
|
+
}));
|
3791
|
+
return builtCoders;
|
3792
|
+
} catch (cause) {
|
3793
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3939
3794
|
}
|
3940
|
-
|
3941
|
-
|
3942
|
-
|
3943
|
-
|
3944
|
-
|
3945
|
-
|
3946
|
-
|
3947
|
-
|
3948
|
-
|
3949
|
-
|
3795
|
+
return null;
|
3796
|
+
};
|
3797
|
+
|
3798
|
+
/**
|
3799
|
+
* Decodes & unwraps outputs and errors of a given result, contract, and method.
|
3800
|
+
* Parsed error message can be found in `decodedOutput` if `isError` is true.
|
3801
|
+
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3802
|
+
*/
|
3803
|
+
function decodeOutput({
|
3804
|
+
result
|
3805
|
+
}, registry, abi, method) {
|
3806
|
+
let output;
|
3807
|
+
let decodedOutput = "";
|
3808
|
+
let isError = true;
|
3809
|
+
if (result.isOk) {
|
3810
|
+
const flags = result.asOk.flags.toHuman();
|
3811
|
+
isError = flags.includes("Revert");
|
3812
|
+
const abiMessage = getAbiMessage(abi, method);
|
3813
|
+
const returnType = abiMessage.returnType;
|
3814
|
+
const returnTypeName = getReturnTypeName(returnType);
|
3815
|
+
const r = returnType ? registry.createTypeUnsafe(returnTypeName, [result.asOk.data]).toHuman() : "()";
|
3816
|
+
output = isOk(r) ? r.Ok : isErr(r) ? r.Err : r;
|
3817
|
+
const errorText = isErr(output) ? typeof output.Err === "object" ? JSON.stringify(output.Err, null, 2) : output.Err?.toString() ?? "Error" : output !== "Ok" ? output?.toString() || "Error" : "Error";
|
3818
|
+
const okText = isOk(r) ? typeof output === "object" ? JSON.stringify(output, null, "\t") : output?.toString() ?? "()" : JSON.stringify(output, null, "\t") ?? "()";
|
3819
|
+
decodedOutput = isError ? errorText : okText;
|
3820
|
+
} else if (result.isErr) {
|
3821
|
+
output = result.toHuman();
|
3822
|
+
let errorText;
|
3823
|
+
if (isErr(output) && typeof output.Err === "object" && Object.keys(output.Err || {}).length && typeof Object.values(output.Err || {})[0] === "string") {
|
3824
|
+
const [errorKey, errorValue] = Object.entries(output.Err || {})[0];
|
3825
|
+
errorText = `${errorKey}${errorValue}`;
|
3826
|
+
}
|
3827
|
+
decodedOutput = errorText || "Error";
|
3950
3828
|
}
|
3951
|
-
|
3952
|
-
|
3953
|
-
|
3954
|
-
|
3955
|
-
|
3956
|
-
|
3957
|
-
|
3958
|
-
|
3959
|
-
|
3960
|
-
|
3961
|
-
|
3962
|
-
|
3963
|
-
|
3964
|
-
|
3965
|
-
|
3966
|
-
|
3967
|
-
|
3968
|
-
|
3969
|
-
|
3970
|
-
|
3971
|
-
|
3829
|
+
return {
|
3830
|
+
output,
|
3831
|
+
decodedOutput,
|
3832
|
+
isError
|
3833
|
+
};
|
3834
|
+
}
|
3835
|
+
|
3836
|
+
/**
|
3837
|
+
* Helper types & functions
|
3838
|
+
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3839
|
+
*/
|
3840
|
+
|
3841
|
+
function isErr(o) {
|
3842
|
+
return typeof o === "object" && o !== null && "Err" in o;
|
3843
|
+
}
|
3844
|
+
function isOk(o) {
|
3845
|
+
return typeof o === "object" && o !== null && "Ok" in o;
|
3846
|
+
}
|
3847
|
+
function getReturnTypeName(type) {
|
3848
|
+
return type?.lookupName || type?.type || "";
|
3849
|
+
}
|
3850
|
+
function getAbiMessage(abi, method) {
|
3851
|
+
const abiMessage = abi.messages.find(m => util$1.stringCamelCase(m.method) === util$1.stringCamelCase(method));
|
3852
|
+
if (!abiMessage) {
|
3853
|
+
throw new Error(`"${method}" not found in Contract`);
|
3854
|
+
}
|
3855
|
+
return abiMessage;
|
3856
|
+
}
|
3857
|
+
|
3858
|
+
/**
|
3859
|
+
*
|
3860
|
+
* Detect Balances::transfer -> Balances::transfer_allow_death migration
|
3861
|
+
* https://github.com/paritytech/substrate/pull/12951
|
3862
|
+
*
|
3863
|
+
* `transfer_allow_death` is the preferred method,
|
3864
|
+
* so if something goes wrong during detection, we should assume the chain has migrated
|
3865
|
+
* @param metadataRpc string containing the hashed RPC metadata for the chain
|
3866
|
+
* @returns
|
3867
|
+
*/
|
3868
|
+
const detectTransferMethod = metadataRpc => {
|
3869
|
+
const pjsMetadata = new types.Metadata(new types.TypeRegistry(), metadataRpc);
|
3870
|
+
pjsMetadata.registry.setMetadata(pjsMetadata);
|
3871
|
+
const balancesPallet = pjsMetadata.asLatest.pallets.find(pallet => pallet.name.eq("Balances"));
|
3872
|
+
const balancesCallsTypeIndex = balancesPallet?.calls.value.type.toNumber();
|
3873
|
+
const balancesCallsType = balancesCallsTypeIndex !== undefined ? pjsMetadata.asLatest.lookup.types[balancesCallsTypeIndex] : undefined;
|
3874
|
+
const hasDeprecatedTransferCall = balancesCallsType?.type.def.asVariant?.variants.find(variant => variant.name.eq("transfer")) !== undefined;
|
3875
|
+
return hasDeprecatedTransferCall ? "transfer" : "transfer_allow_death";
|
3876
|
+
};
|
3877
|
+
|
3878
|
+
const getUniqueChainIds = (addressesByToken, tokens) => [...new Set(Object.keys(addressesByToken).map(tokenId => tokens[tokenId]?.networkId).flatMap(chainId => chainId ? [chainId] : []))];
|
3879
|
+
|
3880
|
+
const makeContractCaller = ({
|
3881
|
+
chainConnector,
|
3882
|
+
chainId,
|
3883
|
+
registry
|
3884
|
+
}) => async (callFrom, contractAddress, inputData) => registry.createType("ContractExecResult", await chainConnector.send(chainId, "state_call", ["ContractsApi_call", util$1.u8aToHex(util$1.u8aConcatStrict([
|
3885
|
+
// origin
|
3886
|
+
registry.createType("AccountId", callFrom).toU8a(),
|
3887
|
+
// dest
|
3888
|
+
registry.createType("AccountId", contractAddress).toU8a(),
|
3889
|
+
// value
|
3890
|
+
registry.createType("Balance", 0).toU8a(),
|
3891
|
+
// gasLimit
|
3892
|
+
registry.createType("Option<WeightV2>").toU8a(),
|
3893
|
+
// storageDepositLimit
|
3894
|
+
registry.createType("Option<Balance>").toU8a(),
|
3895
|
+
// inputData
|
3896
|
+
inputData instanceof Uint8Array ? inputData : inputData.toU8a()]))]));
|
3897
|
+
|
3898
|
+
/**
|
3899
|
+
* Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
|
3900
|
+
*/
|
3901
|
+
|
3902
|
+
/**
|
3903
|
+
* Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
|
3904
|
+
*/
|
3905
|
+
class RpcStateQueryHelper {
|
3906
|
+
#chainConnector;
|
3907
|
+
#queries;
|
3908
|
+
constructor(chainConnector, queries) {
|
3909
|
+
this.#chainConnector = chainConnector;
|
3910
|
+
this.#queries = queries;
|
3911
|
+
}
|
3912
|
+
async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
|
3913
|
+
const queriesByChain = groupBy__default.default(this.#queries, "chainId");
|
3914
|
+
const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
|
3915
|
+
const params = [queries.map(({
|
3916
|
+
stateKey
|
3917
|
+
}) => stateKey)];
|
3918
|
+
const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
|
3919
|
+
error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
|
3920
|
+
}, timeout);
|
3921
|
+
return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
|
3972
3922
|
});
|
3973
|
-
return
|
3974
|
-
}
|
3975
|
-
|
3976
|
-
|
3923
|
+
return () => subscriptions.forEach(unsubscribe => unsubscribe());
|
3924
|
+
}
|
3925
|
+
async fetch(method = "state_queryStorageAt") {
|
3926
|
+
const queriesByChain = groupBy__default.default(this.#queries, "chainId");
|
3927
|
+
const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
|
3928
|
+
const params = [queries.map(({
|
3929
|
+
stateKey
|
3930
|
+
}) => stateKey)];
|
3931
|
+
const result = (await this.#chainConnector.send(chainId, method, params))[0];
|
3932
|
+
return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
|
3933
|
+
}));
|
3934
|
+
return resultsByChain.flatMap(result => result);
|
3935
|
+
}
|
3936
|
+
#distributeChangesToQueryDecoders(chainId, result) {
|
3937
|
+
if (typeof result !== "object" || result === null) return [];
|
3938
|
+
if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
|
3939
|
+
if (!Array.isArray(result.changes)) return [];
|
3940
|
+
return result.changes.flatMap(([reference, change]) => {
|
3941
|
+
if (typeof reference !== "string") {
|
3942
|
+
log.warn(`Received non-string reference in RPC result: ${reference}`);
|
3943
|
+
return [];
|
3944
|
+
}
|
3945
|
+
if (typeof change !== "string" && change !== null) {
|
3946
|
+
log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
|
3947
|
+
return [];
|
3948
|
+
}
|
3949
|
+
const query = this.#queries.find(({
|
3950
|
+
chainId: cId,
|
3951
|
+
stateKey
|
3952
|
+
}) => cId === chainId && stateKey === reference);
|
3953
|
+
if (!query) {
|
3954
|
+
log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
|
3955
|
+
stateKey
|
3956
|
+
}) => stateKey)}`);
|
3957
|
+
return [];
|
3958
|
+
}
|
3959
|
+
return [query.decodeResult(change)];
|
3960
|
+
});
|
3961
|
+
}
|
3962
|
+
}
|
3963
|
+
|
3964
|
+
const configureStore = (dbTable = db.balancesBlob) => ({
|
3965
|
+
persistData: async balances => {
|
3966
|
+
const output = compress(balances);
|
3967
|
+
await dbTable.clear();
|
3968
|
+
await dbTable.put({
|
3969
|
+
data: output,
|
3970
|
+
id: Date.now().toString()
|
3971
|
+
});
|
3972
|
+
},
|
3973
|
+
retrieveData: async () => {
|
3974
|
+
const compressedData = await dbTable.toCollection().first();
|
3975
|
+
if (!compressedData) return [];
|
3976
|
+
return decompress(compressedData.data);
|
3977
|
+
}
|
3978
|
+
});
|
3979
|
+
const compress = balances => pako__default.default.deflate(JSON.stringify(balances));
|
3980
|
+
const decompress = data => {
|
3981
|
+
const decompressed = pako__default.default.inflate(data, {
|
3982
|
+
to: "string"
|
3977
3983
|
});
|
3984
|
+
return JSON.parse(decompressed);
|
3978
3985
|
};
|
3986
|
+
|
3979
3987
|
const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
3980
3988
|
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3981
3989
|
storage: ["ForeignAssets", "Account"]
|
@@ -3997,10 +4005,10 @@ const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
|
3997
4005
|
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
3998
4006
|
return null;
|
3999
4007
|
}
|
4000
|
-
const decodeResult =
|
4008
|
+
const decodeResult = changes => {
|
4001
4009
|
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
4002
4010
|
|
4003
|
-
const decoded = scale.decodeScale(scaleCoder,
|
4011
|
+
const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
4004
4012
|
balance: 0n,
|
4005
4013
|
is_frozen: false,
|
4006
4014
|
status: {
|
@@ -4042,13 +4050,85 @@ const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
|
4042
4050
|
return balance;
|
4043
4051
|
};
|
4044
4052
|
return {
|
4045
|
-
|
4046
|
-
stateKey,
|
4053
|
+
stateKeys: [stateKey],
|
4047
4054
|
decodeResult
|
4048
4055
|
};
|
4049
4056
|
}).filter(util.isNotNil);
|
4050
4057
|
};
|
4051
4058
|
|
4059
|
+
const fetchBalances$5 = async ({
|
4060
|
+
networkId,
|
4061
|
+
tokensWithAddresses,
|
4062
|
+
connector,
|
4063
|
+
miniMetadata
|
4064
|
+
}) => {
|
4065
|
+
if (!tokensWithAddresses.length) return {
|
4066
|
+
success: [],
|
4067
|
+
errors: []
|
4068
|
+
};
|
4069
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4070
|
+
if (!miniMetadata?.data) {
|
4071
|
+
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$4} balances on ${networkId}.`);
|
4072
|
+
return {
|
4073
|
+
success: [],
|
4074
|
+
errors: balanceDefs.map(def => ({
|
4075
|
+
tokenId: def.token.id,
|
4076
|
+
address: def.address,
|
4077
|
+
error: new Error("Minimetadata is required for fetching balances")
|
4078
|
+
}))
|
4079
|
+
};
|
4080
|
+
}
|
4081
|
+
if (miniMetadata.source !== MODULE_TYPE$4) {
|
4082
|
+
log.warn(`Ignoring miniMetadata with source ${miniMetadata.source} in ${MODULE_TYPE$4}.`);
|
4083
|
+
return {
|
4084
|
+
success: [],
|
4085
|
+
errors: balanceDefs.map(def => ({
|
4086
|
+
tokenId: def.token.id,
|
4087
|
+
address: def.address,
|
4088
|
+
error: new Error(`Invalid request: miniMetadata source is not ${MODULE_TYPE$4}`)
|
4089
|
+
}))
|
4090
|
+
};
|
4091
|
+
}
|
4092
|
+
if (miniMetadata.chainId !== networkId) {
|
4093
|
+
log.warn(`Ignoring miniMetadata with chainId ${miniMetadata.chainId} in ${MODULE_TYPE$4}. Expected chainId is ${networkId}`);
|
4094
|
+
return {
|
4095
|
+
success: [],
|
4096
|
+
errors: balanceDefs.map(def => ({
|
4097
|
+
tokenId: def.token.id,
|
4098
|
+
address: def.address,
|
4099
|
+
error: new Error(`Invalid request: Expected chainId is ${networkId}`)
|
4100
|
+
}))
|
4101
|
+
};
|
4102
|
+
}
|
4103
|
+
const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
|
4104
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
4105
|
+
return balanceDefs.reduce((acc, def) => {
|
4106
|
+
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
4107
|
+
if (balance) acc.success.push(balance);
|
4108
|
+
//if no entry consider empty balance
|
4109
|
+
else acc.success.push({
|
4110
|
+
address: def.address,
|
4111
|
+
networkId,
|
4112
|
+
tokenId: def.token.id,
|
4113
|
+
source: MODULE_TYPE$4,
|
4114
|
+
status: "live",
|
4115
|
+
values: [{
|
4116
|
+
type: "free",
|
4117
|
+
label: "free",
|
4118
|
+
amount: "0"
|
4119
|
+
}, {
|
4120
|
+
type: "locked",
|
4121
|
+
label: "frozen",
|
4122
|
+
amount: "0"
|
4123
|
+
}]
|
4124
|
+
});
|
4125
|
+
return acc;
|
4126
|
+
}, {
|
4127
|
+
success: [],
|
4128
|
+
errors: []
|
4129
|
+
});
|
4130
|
+
};
|
4131
|
+
|
4052
4132
|
const fetchTokens$4 = async ({
|
4053
4133
|
networkId,
|
4054
4134
|
tokens,
|
@@ -4245,46 +4325,22 @@ const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
|
|
4245
4325
|
})]);
|
4246
4326
|
};
|
4247
4327
|
|
4248
|
-
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
4249
4328
|
const subscribeBalances$4 = ({
|
4250
4329
|
networkId,
|
4251
4330
|
tokensWithAddresses,
|
4252
4331
|
connector,
|
4253
4332
|
miniMetadata
|
4254
4333
|
}) => {
|
4255
|
-
return
|
4256
|
-
|
4257
|
-
|
4258
|
-
|
4259
|
-
|
4260
|
-
|
4261
|
-
|
4262
|
-
|
4263
|
-
|
4264
|
-
|
4265
|
-
tokensWithAddresses: tokensWithAddresses,
|
4266
|
-
connector,
|
4267
|
-
miniMetadata
|
4268
|
-
});
|
4269
|
-
if (abortController.signal.aborted) return;
|
4270
|
-
subscriber.next(balances);
|
4271
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
4272
|
-
} catch (error) {
|
4273
|
-
log.error("Error", {
|
4274
|
-
module: MODULE_TYPE$4,
|
4275
|
-
networkId,
|
4276
|
-
miniMetadata,
|
4277
|
-
addressesByToken: tokensWithAddresses,
|
4278
|
-
error
|
4279
|
-
});
|
4280
|
-
subscriber.error(error);
|
4281
|
-
}
|
4282
|
-
};
|
4283
|
-
poll();
|
4284
|
-
return () => {
|
4285
|
-
abortController.abort();
|
4286
|
-
};
|
4287
|
-
}).pipe(rxjs.distinctUntilChanged(lodash.isEqual));
|
4334
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
4335
|
+
success: [],
|
4336
|
+
errors: []
|
4337
|
+
});
|
4338
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4339
|
+
const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
|
4340
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
|
4341
|
+
success: balances,
|
4342
|
+
errors: []
|
4343
|
+
})));
|
4288
4344
|
};
|
4289
4345
|
|
4290
4346
|
const SubForeignAssetsBalanceModule = {
|
@@ -4303,48 +4359,30 @@ const SubForeignAssetsTokenConfigSchema = z__default.default.strictObject({
|
|
4303
4359
|
...TokenConfigBaseSchema.shape
|
4304
4360
|
});
|
4305
4361
|
|
4306
|
-
// to be used by chaindata too
|
4307
|
-
const SubHydrationTokenConfigSchema = z__default.default.strictObject({
|
4308
|
-
onChainId: chaindataProvider.SubHydrationTokenSchema.shape.onChainId,
|
4309
|
-
...TokenConfigBaseSchema.shape
|
4310
|
-
});
|
4311
|
-
|
4312
|
-
const MODULE_TYPE$3 = chaindataProvider.SubHydrationTokenSchema.shape.type.value;
|
4313
|
-
const PLATFORM$3 = chaindataProvider.SubHydrationTokenSchema.shape.platform.value;
|
4314
|
-
|
4315
|
-
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
4316
|
-
const {
|
4317
|
-
builder
|
4318
|
-
} = scale.parseMetadataRpc(metadataRpc);
|
4319
|
-
const call = builder.buildRuntimeCall(apiName, method);
|
4320
|
-
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, scale.toHex(call.args.enc(args))]);
|
4321
|
-
return call.value.dec(hex);
|
4322
|
-
};
|
4323
|
-
|
4324
|
-
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
4325
|
-
const {
|
4326
|
-
unifiedMetadata,
|
4327
|
-
builder
|
4328
|
-
} = scale.parseMetadataRpc(metadataRpc);
|
4329
|
-
const encodedValue = unifiedMetadata.pallets.find(({
|
4330
|
-
name
|
4331
|
-
}) => name === pallet)?.constants.find(({
|
4332
|
-
name
|
4333
|
-
}) => name === constant)?.value;
|
4334
|
-
if (!encodedValue) return null;
|
4335
|
-
const codec = builder.buildConstant(pallet, constant);
|
4336
|
-
return codec.dec(encodedValue);
|
4337
|
-
};
|
4338
|
-
|
4362
|
+
// to be used by chaindata too
|
4363
|
+
const SubHydrationTokenConfigSchema = z__default.default.strictObject({
|
4364
|
+
onChainId: chaindataProvider.SubHydrationTokenSchema.shape.onChainId,
|
4365
|
+
...TokenConfigBaseSchema.shape
|
4366
|
+
});
|
4367
|
+
|
4368
|
+
const MODULE_TYPE$3 = chaindataProvider.SubHydrationTokenSchema.shape.type.value;
|
4369
|
+
const PLATFORM$3 = chaindataProvider.SubHydrationTokenSchema.shape.platform.value;
|
4370
|
+
|
4339
4371
|
const fetchBalances$4 = async ({
|
4340
4372
|
networkId,
|
4341
4373
|
tokensWithAddresses,
|
4342
4374
|
connector,
|
4343
4375
|
miniMetadata
|
4344
4376
|
}) => {
|
4377
|
+
if (!tokensWithAddresses.length) return {
|
4378
|
+
success: [],
|
4379
|
+
errors: []
|
4380
|
+
};
|
4345
4381
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4346
4382
|
if (!miniMetadata?.data) {
|
4347
|
-
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}
|
4383
|
+
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}.`, {
|
4384
|
+
tokensWithAddresses
|
4385
|
+
});
|
4348
4386
|
return {
|
4349
4387
|
success: [],
|
4350
4388
|
errors: balanceDefs.map(def => ({
|
@@ -4591,13 +4629,17 @@ const getTransferCallData$3 = ({
|
|
4591
4629
|
};
|
4592
4630
|
};
|
4593
4631
|
|
4594
|
-
const SUBSCRIPTION_INTERVAL$
|
4632
|
+
const SUBSCRIPTION_INTERVAL$1 = 6_000;
|
4595
4633
|
const subscribeBalances$3 = ({
|
4596
4634
|
networkId,
|
4597
4635
|
tokensWithAddresses,
|
4598
4636
|
connector,
|
4599
4637
|
miniMetadata
|
4600
4638
|
}) => {
|
4639
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
4640
|
+
success: [],
|
4641
|
+
errors: []
|
4642
|
+
});
|
4601
4643
|
return new rxjs.Observable(subscriber => {
|
4602
4644
|
const abortController = new AbortController();
|
4603
4645
|
|
@@ -4614,7 +4656,7 @@ const subscribeBalances$3 = ({
|
|
4614
4656
|
});
|
4615
4657
|
if (abortController.signal.aborted) return;
|
4616
4658
|
subscriber.next(balances);
|
4617
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
4659
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$1);
|
4618
4660
|
} catch (error) {
|
4619
4661
|
log.error("Error", {
|
4620
4662
|
module: MODULE_TYPE$3,
|
@@ -4646,54 +4688,6 @@ const SubHydrationBalanceModule = {
|
|
4646
4688
|
const MODULE_TYPE$2 = chaindataProvider.SubNativeTokenSchema.shape.type.value;
|
4647
4689
|
const PLATFORM$2 = chaindataProvider.SubNativeTokenSchema.shape.platform.value;
|
4648
4690
|
|
4649
|
-
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
4650
|
-
const allStateKeys = queries.flatMap(({
|
4651
|
-
stateKeys
|
4652
|
-
}) => stateKeys).filter(util.isNotNil);
|
4653
|
-
|
4654
|
-
// doing a query without keys would throw an error => return early
|
4655
|
-
if (!allStateKeys.length) return queries.map(({
|
4656
|
-
stateKeys,
|
4657
|
-
decodeResult
|
4658
|
-
}) => decodeResult(stateKeys.map(() => null)));
|
4659
|
-
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
4660
|
-
return decodeRpcQueryPack(queries, result);
|
4661
|
-
};
|
4662
|
-
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
4663
|
-
const allStateKeys = queries.flatMap(({
|
4664
|
-
stateKeys
|
4665
|
-
}) => stateKeys).filter(util.isNotNil);
|
4666
|
-
|
4667
|
-
// doing a query without keys would throw an error => return early
|
4668
|
-
if (!allStateKeys.length) return rxjs.of(queries.map(({
|
4669
|
-
stateKeys,
|
4670
|
-
decodeResult
|
4671
|
-
}) => decodeResult(stateKeys.map(() => null))));
|
4672
|
-
return new rxjs.Observable(subscriber => {
|
4673
|
-
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
4674
|
-
if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
|
4675
|
-
}, timeout);
|
4676
|
-
return () => {
|
4677
|
-
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
4678
|
-
};
|
4679
|
-
});
|
4680
|
-
};
|
4681
|
-
const decodeRpcQueryPack = (queries, result) => {
|
4682
|
-
return queries.reduce((acc, {
|
4683
|
-
stateKeys,
|
4684
|
-
decodeResult
|
4685
|
-
}) => {
|
4686
|
-
const changes = stateKeys.map(stateKey => {
|
4687
|
-
if (!stateKey) return null;
|
4688
|
-
const change = result.changes.find(([key]) => key === stateKey);
|
4689
|
-
if (!change) return null;
|
4690
|
-
return change[1];
|
4691
|
-
});
|
4692
|
-
acc.push(decodeResult(changes));
|
4693
|
-
return acc;
|
4694
|
-
}, []);
|
4695
|
-
};
|
4696
|
-
|
4697
4691
|
const SUBTENSOR_ROOT_NETUID$1 = 0;
|
4698
4692
|
const SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1 = 1000000n;
|
4699
4693
|
const TAO_DECIMALS$1 = 9n;
|
@@ -5411,6 +5405,10 @@ const fetchBalances$3 = async ({
|
|
5411
5405
|
connector,
|
5412
5406
|
miniMetadata
|
5413
5407
|
}) => {
|
5408
|
+
if (!tokensWithAddresses.length) return {
|
5409
|
+
success: [],
|
5410
|
+
errors: []
|
5411
|
+
};
|
5414
5412
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
5415
5413
|
if (!miniMetadata?.data) {
|
5416
5414
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$2} balances on ${networkId}.`);
|
@@ -5692,6 +5690,11 @@ const subscribeBalances$2 = ({
|
|
5692
5690
|
connector,
|
5693
5691
|
miniMetadata
|
5694
5692
|
}) => {
|
5693
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
5694
|
+
success: [],
|
5695
|
+
errors: []
|
5696
|
+
});
|
5697
|
+
|
5695
5698
|
// could be use as shared observable key if we decide to cache the sub
|
5696
5699
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
5697
5700
|
const baseQueries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
|
@@ -6895,6 +6898,10 @@ const fetchBalances$2 = async ({
|
|
6895
6898
|
tokensWithAddresses,
|
6896
6899
|
connector
|
6897
6900
|
}) => {
|
6901
|
+
if (!tokensWithAddresses.length) return {
|
6902
|
+
success: [],
|
6903
|
+
errors: []
|
6904
|
+
};
|
6898
6905
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
6899
6906
|
if (!balanceDefs.length) return {
|
6900
6907
|
success: [],
|
@@ -7082,13 +7089,17 @@ const getTransferCallData$1 = async ({
|
|
7082
7089
|
};
|
7083
7090
|
};
|
7084
7091
|
|
7085
|
-
const SUBSCRIPTION_INTERVAL
|
7092
|
+
const SUBSCRIPTION_INTERVAL = 6_000;
|
7086
7093
|
const subscribeBalances$1 = ({
|
7087
7094
|
networkId,
|
7088
7095
|
tokensWithAddresses,
|
7089
7096
|
connector,
|
7090
7097
|
miniMetadata
|
7091
7098
|
}) => {
|
7099
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
7100
|
+
success: [],
|
7101
|
+
errors: []
|
7102
|
+
});
|
7092
7103
|
return new rxjs.Observable(subscriber => {
|
7093
7104
|
const abortController = new AbortController();
|
7094
7105
|
|
@@ -7105,7 +7116,7 @@ const subscribeBalances$1 = ({
|
|
7105
7116
|
});
|
7106
7117
|
if (abortController.signal.aborted) return;
|
7107
7118
|
subscriber.next(balances);
|
7108
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL
|
7119
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL);
|
7109
7120
|
} catch (error) {
|
7110
7121
|
log.error("Error", {
|
7111
7122
|
module: MODULE_TYPE$1,
|
@@ -7143,12 +7154,77 @@ const SubPsp22TokenConfigSchema = z__default.default.strictObject({
|
|
7143
7154
|
const MODULE_TYPE = chaindataProvider.SubTokensTokenSchema.shape.type.value;
|
7144
7155
|
const PLATFORM = chaindataProvider.SubTokensTokenSchema.shape.platform.value;
|
7145
7156
|
|
7157
|
+
const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
|
7158
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7159
|
+
storage: [miniMetadata.extra.palletId, "Accounts"]
|
7160
|
+
});
|
7161
|
+
return balanceDefs.map(({
|
7162
|
+
token,
|
7163
|
+
address
|
7164
|
+
}) => {
|
7165
|
+
const scaleCoder = networkStorageCoders?.storage;
|
7166
|
+
const getStateKey = onChainId => {
|
7167
|
+
try {
|
7168
|
+
return scaleCoder.keys.enc(address, scale.papiParse(onChainId));
|
7169
|
+
} catch {
|
7170
|
+
return null;
|
7171
|
+
}
|
7172
|
+
};
|
7173
|
+
const stateKey = getStateKey(token.onChainId);
|
7174
|
+
if (!stateKey) {
|
7175
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
7176
|
+
return null;
|
7177
|
+
}
|
7178
|
+
const decodeResult = changes => {
|
7179
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7180
|
+
|
7181
|
+
const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
|
7182
|
+
free: 0n,
|
7183
|
+
reserved: 0n,
|
7184
|
+
frozen: 0n
|
7185
|
+
};
|
7186
|
+
const free = (decoded?.free ?? 0n).toString();
|
7187
|
+
const reserved = (decoded?.reserved ?? 0n).toString();
|
7188
|
+
const frozen = (decoded?.frozen ?? 0n).toString();
|
7189
|
+
const balanceValues = [{
|
7190
|
+
type: "free",
|
7191
|
+
label: "free",
|
7192
|
+
amount: free.toString()
|
7193
|
+
}, {
|
7194
|
+
type: "reserved",
|
7195
|
+
label: "reserved",
|
7196
|
+
amount: reserved.toString()
|
7197
|
+
}, {
|
7198
|
+
type: "locked",
|
7199
|
+
label: "frozen",
|
7200
|
+
amount: frozen.toString()
|
7201
|
+
}];
|
7202
|
+
return {
|
7203
|
+
source: "substrate-tokens",
|
7204
|
+
status: "live",
|
7205
|
+
address,
|
7206
|
+
networkId,
|
7207
|
+
tokenId: token.id,
|
7208
|
+
values: balanceValues
|
7209
|
+
};
|
7210
|
+
};
|
7211
|
+
return {
|
7212
|
+
stateKeys: [stateKey],
|
7213
|
+
decodeResult
|
7214
|
+
};
|
7215
|
+
}).filter(util.isNotNil);
|
7216
|
+
};
|
7217
|
+
|
7146
7218
|
const fetchBalances$1 = async ({
|
7147
7219
|
networkId,
|
7148
7220
|
tokensWithAddresses,
|
7149
7221
|
connector,
|
7150
7222
|
miniMetadata
|
7151
7223
|
}) => {
|
7224
|
+
if (!tokensWithAddresses.length) return {
|
7225
|
+
success: [],
|
7226
|
+
errors: []
|
7227
|
+
};
|
7152
7228
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
7153
7229
|
if (!miniMetadata?.data) {
|
7154
7230
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE} balances on ${networkId}.`);
|
@@ -7184,7 +7260,7 @@ const fetchBalances$1 = async ({
|
|
7184
7260
|
};
|
7185
7261
|
}
|
7186
7262
|
const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
|
7187
|
-
const balances = await
|
7263
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
7188
7264
|
return balanceDefs.reduce((acc, def) => {
|
7189
7265
|
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
7190
7266
|
if (balance) acc.success.push(balance);
|
@@ -7211,67 +7287,6 @@ const fetchBalances$1 = async ({
|
|
7211
7287
|
errors: []
|
7212
7288
|
});
|
7213
7289
|
};
|
7214
|
-
const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
|
7215
|
-
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7216
|
-
storage: [miniMetadata.extra.palletId, "Accounts"]
|
7217
|
-
});
|
7218
|
-
return balanceDefs.map(({
|
7219
|
-
token,
|
7220
|
-
address
|
7221
|
-
}) => {
|
7222
|
-
const scaleCoder = networkStorageCoders?.storage;
|
7223
|
-
const getStateKey = onChainId => {
|
7224
|
-
try {
|
7225
|
-
return scaleCoder.keys.enc(address, scale.papiParse(onChainId));
|
7226
|
-
} catch {
|
7227
|
-
return null;
|
7228
|
-
}
|
7229
|
-
};
|
7230
|
-
const stateKey = getStateKey(token.onChainId);
|
7231
|
-
if (!stateKey) {
|
7232
|
-
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
7233
|
-
return null;
|
7234
|
-
}
|
7235
|
-
const decodeResult = change => {
|
7236
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7237
|
-
|
7238
|
-
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
|
7239
|
-
free: 0n,
|
7240
|
-
reserved: 0n,
|
7241
|
-
frozen: 0n
|
7242
|
-
};
|
7243
|
-
const free = (decoded?.free ?? 0n).toString();
|
7244
|
-
const reserved = (decoded?.reserved ?? 0n).toString();
|
7245
|
-
const frozen = (decoded?.frozen ?? 0n).toString();
|
7246
|
-
const balanceValues = [{
|
7247
|
-
type: "free",
|
7248
|
-
label: "free",
|
7249
|
-
amount: free.toString()
|
7250
|
-
}, {
|
7251
|
-
type: "reserved",
|
7252
|
-
label: "reserved",
|
7253
|
-
amount: reserved.toString()
|
7254
|
-
}, {
|
7255
|
-
type: "locked",
|
7256
|
-
label: "frozen",
|
7257
|
-
amount: frozen.toString()
|
7258
|
-
}];
|
7259
|
-
return {
|
7260
|
-
source: "substrate-tokens",
|
7261
|
-
status: "live",
|
7262
|
-
address,
|
7263
|
-
networkId,
|
7264
|
-
tokenId: token.id,
|
7265
|
-
values: balanceValues
|
7266
|
-
};
|
7267
|
-
};
|
7268
|
-
return {
|
7269
|
-
chainId: networkId,
|
7270
|
-
stateKey,
|
7271
|
-
decodeResult
|
7272
|
-
};
|
7273
|
-
}).filter(util.isNotNil);
|
7274
|
-
};
|
7275
7290
|
|
7276
7291
|
const fetchTokens = async ({
|
7277
7292
|
networkId,
|
@@ -7472,46 +7487,22 @@ const getCallDataOptions = (to, token, value, type, config) => {
|
|
7472
7487
|
}] : []));
|
7473
7488
|
};
|
7474
7489
|
|
7475
|
-
const SUBSCRIPTION_INTERVAL = 6_000;
|
7476
7490
|
const subscribeBalances = ({
|
7477
7491
|
networkId,
|
7478
7492
|
tokensWithAddresses,
|
7479
7493
|
connector,
|
7480
7494
|
miniMetadata
|
7481
7495
|
}) => {
|
7482
|
-
return
|
7483
|
-
|
7484
|
-
|
7485
|
-
|
7486
|
-
|
7487
|
-
|
7488
|
-
|
7489
|
-
|
7490
|
-
|
7491
|
-
|
7492
|
-
tokensWithAddresses: tokensWithAddresses,
|
7493
|
-
connector,
|
7494
|
-
miniMetadata
|
7495
|
-
});
|
7496
|
-
if (abortController.signal.aborted) return;
|
7497
|
-
subscriber.next(balances);
|
7498
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL);
|
7499
|
-
} catch (error) {
|
7500
|
-
log.error("Error", {
|
7501
|
-
module: MODULE_TYPE,
|
7502
|
-
networkId,
|
7503
|
-
miniMetadata,
|
7504
|
-
addressesByToken: tokensWithAddresses,
|
7505
|
-
error
|
7506
|
-
});
|
7507
|
-
subscriber.error(error);
|
7508
|
-
}
|
7509
|
-
};
|
7510
|
-
poll();
|
7511
|
-
return () => {
|
7512
|
-
abortController.abort();
|
7513
|
-
};
|
7514
|
-
}).pipe(rxjs.distinctUntilChanged(lodash.isEqual));
|
7496
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
7497
|
+
success: [],
|
7498
|
+
errors: []
|
7499
|
+
});
|
7500
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
7501
|
+
const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
|
7502
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
|
7503
|
+
success: balances,
|
7504
|
+
errors: []
|
7505
|
+
})));
|
7515
7506
|
};
|
7516
7507
|
|
7517
7508
|
const SubTokensBalanceModule = {
|
@@ -10371,9 +10362,17 @@ class BalancesProvider {
|
|
10371
10362
|
miniMetadatas: lodash.values(miniMetadatas).filter(util.isNotNil)
|
10372
10363
|
})));
|
10373
10364
|
}
|
10374
|
-
|
10375
|
-
|
10376
|
-
|
10365
|
+
|
10366
|
+
// this is the only public method
|
10367
|
+
getBalances$(addressesByTokenId) {
|
10368
|
+
// split by network
|
10369
|
+
const addressesByTokenIdByNetworkId = lodash.toPairs(addressesByTokenId).reduce((acc, [tokenId, addresses]) => {
|
10370
|
+
const networkId = chaindataProvider.parseTokenId(tokenId).networkId;
|
10371
|
+
if (!acc[networkId]) acc[networkId] = {};
|
10372
|
+
acc[networkId][tokenId] = addresses;
|
10373
|
+
return acc;
|
10374
|
+
}, {});
|
10375
|
+
return rxjs.combineLatest(lodash.toPairs(addressesByTokenIdByNetworkId).map(([networkId]) => this.getNetworkBalances$(networkId, addressesByTokenIdByNetworkId[networkId]))).pipe(rxjs.map(results => {
|
10377
10376
|
return {
|
10378
10377
|
status: results.some(({
|
10379
10378
|
status
|
@@ -10382,9 +10381,17 @@ class BalancesProvider {
|
|
10382
10381
|
};
|
10383
10382
|
}), rxjs.startWith({
|
10384
10383
|
status: "initialising",
|
10385
|
-
balances: this.getStoredBalances(
|
10384
|
+
balances: this.getStoredBalances(addressesByTokenId)
|
10386
10385
|
}), rxjs.distinctUntilChanged(lodash.isEqual));
|
10387
10386
|
}
|
10387
|
+
fetchBalances(addressesByTokenId) {
|
10388
|
+
// TODO: better
|
10389
|
+
return rxjs.firstValueFrom(this.getBalances$(addressesByTokenId).pipe(rxjs.filter(({
|
10390
|
+
status
|
10391
|
+
}) => status === "live"), rxjs.map(({
|
10392
|
+
balances
|
10393
|
+
}) => balances)));
|
10394
|
+
}
|
10388
10395
|
getNetworkBalances$(networkId, addressesByTokenId) {
|
10389
10396
|
const network$ = this.#chaindataProvider.getNetworkById$(networkId);
|
10390
10397
|
const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
|
@@ -10456,7 +10463,42 @@ class BalancesProvider {
|
|
10456
10463
|
}));
|
10457
10464
|
}
|
10458
10465
|
getNetworkMiniMetadatas$(networkId) {
|
10459
|
-
return this.#chaindataProvider.getNetworkById$(networkId).pipe(rxjs.switchMap(network => chaindataProvider.isNetworkDot(network) && this.#chainConnectors.substrate ? rxjs.from(
|
10466
|
+
return this.#chaindataProvider.getNetworkById$(networkId).pipe(rxjs.switchMap(network => chaindataProvider.isNetworkDot(network) && this.#chainConnectors.substrate ? rxjs.from(getSpecVersion(this.#chainConnectors.substrate, networkId)).pipe(rxjs.switchMap(specVersion => this.getMiniMetadatas$(networkId, specVersion))) : rxjs.of([])));
|
10467
|
+
}
|
10468
|
+
getMiniMetadatas$(networkId, specVersion) {
|
10469
|
+
return rxjs.combineLatest({
|
10470
|
+
defaultMiniMetadatas: this.getDefaultMiniMetadatas$(networkId, specVersion),
|
10471
|
+
storedMiniMetadatas: this.getStoredMiniMetadatas$(networkId, specVersion)
|
10472
|
+
}).pipe(rxjs.switchMap(({
|
10473
|
+
storedMiniMetadatas,
|
10474
|
+
defaultMiniMetadatas
|
10475
|
+
}) => {
|
10476
|
+
if (defaultMiniMetadatas.length) return rxjs.of(defaultMiniMetadatas);
|
10477
|
+
if (storedMiniMetadatas.length) return rxjs.of(storedMiniMetadatas);
|
10478
|
+
if (!this.#chainConnectors.substrate) return rxjs.of([]);
|
10479
|
+
return rxjs.from(
|
10480
|
+
// fetch them from the chain
|
10481
|
+
getMiniMetadatas(this.#chainConnectors.substrate, this.#chaindataProvider, networkId)).pipe(
|
10482
|
+
// and persist in storage for later reuse
|
10483
|
+
rxjs.tap(newMiniMetadatas => {
|
10484
|
+
if (!newMiniMetadatas.length) return;
|
10485
|
+
const storage = this.#storage.getValue();
|
10486
|
+
const miniMetadatas = lodash.assign(
|
10487
|
+
// keep minimetadatas of other networks
|
10488
|
+
lodash.keyBy(lodash.values(storage.miniMetadatas).filter(m => m.chainId !== networkId), m => m.id),
|
10489
|
+
// add the ones for our network
|
10490
|
+
lodash.keyBy(newMiniMetadatas, m => m.id));
|
10491
|
+
this.#storage.next(lodash.assign({}, storage, {
|
10492
|
+
miniMetadatas
|
10493
|
+
}));
|
10494
|
+
}));
|
10495
|
+
}));
|
10496
|
+
}
|
10497
|
+
getStoredMiniMetadatas$(networkId, specVersion) {
|
10498
|
+
return this.storage$.pipe(rxjs.map(storage => storage.miniMetadatas.filter(m => m.chainId === networkId && m.specVersion === specVersion && m.version === chaindataProvider.MINIMETADATA_VERSION)), rxjs.distinctUntilChanged(lodash.isEqual));
|
10499
|
+
}
|
10500
|
+
getDefaultMiniMetadatas$(networkId, specVersion) {
|
10501
|
+
return this.#chaindataProvider.miniMetadatas$.pipe(rxjs.map(miniMetadatas => miniMetadatas.filter(m => m.chainId === networkId && m.specVersion === specVersion && m.version === chaindataProvider.MINIMETADATA_VERSION)), rxjs.distinctUntilChanged(lodash.isEqual));
|
10460
10502
|
}
|
10461
10503
|
getStoredBalances(addressesByToken) {
|
10462
10504
|
const balanceDefs = lodash.toPairs(addressesByToken).flatMap(([tokenId, addresses]) => addresses.map(address => [tokenId, address]));
|
@@ -10467,13 +10509,6 @@ class BalancesProvider {
|
|
10467
10509
|
}
|
10468
10510
|
}
|
10469
10511
|
|
10470
|
-
// const getStoredBalances = (
|
10471
|
-
// storedBalances: Record<string, IBalance>,
|
10472
|
-
// addressesByToken: Record<TokenId, Address[]>,
|
10473
|
-
// ): IBalance[] => {
|
10474
|
-
|
10475
|
-
// }
|
10476
|
-
|
10477
10512
|
Object.defineProperty(exports, "MINIMETADATA_VERSION", {
|
10478
10513
|
enumerable: true,
|
10479
10514
|
get: function () { return chaindataProvider.MINIMETADATA_VERSION; }
|