@talismn/balances 0.0.0-pr2075-20250708143919 → 0.0.0-pr2075-20250708160640
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/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-tokens/buildQueries.d.ts +6 -0
- package/dist/talismn-balances.cjs.dev.js +620 -676
- package/dist/talismn-balances.cjs.prod.js +620 -676
- package/dist/talismn-balances.esm.js +622 -678
- package/package.json +10 -10
@@ -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) {
|
@@ -1571,7 +1575,6 @@ const fetchWithoutAggregator$1 = async (client, balanceDefs) => {
|
|
1571
1575
|
success: [],
|
1572
1576
|
errors: []
|
1573
1577
|
};
|
1574
|
-
log.debug("fetching %s balances without aggregator", MODULE_TYPE$8, balanceDefs.length);
|
1575
1578
|
const results = await Promise.allSettled(balanceDefs.map(async ({
|
1576
1579
|
token,
|
1577
1580
|
address
|
@@ -1589,7 +1592,7 @@ const fetchWithoutAggregator$1 = async (client, balanceDefs) => {
|
|
1589
1592
|
value: result.toString(),
|
1590
1593
|
source: MODULE_TYPE$8,
|
1591
1594
|
networkId: chaindataProvider.parseEvmErc20TokenId(token.id).networkId,
|
1592
|
-
status: "
|
1595
|
+
status: "live"
|
1593
1596
|
};
|
1594
1597
|
return balance;
|
1595
1598
|
} catch (err) {
|
@@ -1616,7 +1619,6 @@ const fetchWithAggregator$1 = async (client, balanceDefs, erc20BalancesAggregato
|
|
1616
1619
|
success: [],
|
1617
1620
|
errors: []
|
1618
1621
|
};
|
1619
|
-
log.debug("fetching %s balances with aggregator", MODULE_TYPE$8, balanceDefs.length);
|
1620
1622
|
try {
|
1621
1623
|
const erc20Balances = await client.readContract({
|
1622
1624
|
abi: erc20BalancesAggregatorAbi,
|
@@ -1633,7 +1635,7 @@ const fetchWithAggregator$1 = async (client, balanceDefs, erc20BalancesAggregato
|
|
1633
1635
|
value: erc20Balances[index].toString(),
|
1634
1636
|
source: MODULE_TYPE$8,
|
1635
1637
|
networkId: chaindataProvider.parseTokenId(balanceDef.token.id).networkId,
|
1636
|
-
status: "
|
1638
|
+
status: "live"
|
1637
1639
|
}));
|
1638
1640
|
return {
|
1639
1641
|
success,
|
@@ -1779,12 +1781,16 @@ const getTransferCallData$8 = ({
|
|
1779
1781
|
};
|
1780
1782
|
};
|
1781
1783
|
|
1782
|
-
const SUBSCRIPTION_INTERVAL$
|
1784
|
+
const SUBSCRIPTION_INTERVAL$4 = 6_000;
|
1783
1785
|
const subscribeBalances$8 = ({
|
1784
1786
|
networkId,
|
1785
1787
|
tokensWithAddresses,
|
1786
1788
|
connector
|
1787
1789
|
}) => {
|
1790
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
1791
|
+
success: [],
|
1792
|
+
errors: []
|
1793
|
+
});
|
1788
1794
|
return new rxjs.Observable(subscriber => {
|
1789
1795
|
const abortController = new AbortController();
|
1790
1796
|
const poll = async () => {
|
@@ -1797,7 +1803,7 @@ const subscribeBalances$8 = ({
|
|
1797
1803
|
});
|
1798
1804
|
if (abortController.signal.aborted) return;
|
1799
1805
|
subscriber.next(balances);
|
1800
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
1806
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
1801
1807
|
} catch (error) {
|
1802
1808
|
log.error("Error", {
|
1803
1809
|
module: MODULE_TYPE$8,
|
@@ -1843,6 +1849,10 @@ const fetchBalances$b = async ({
|
|
1843
1849
|
tokensWithAddresses,
|
1844
1850
|
connector
|
1845
1851
|
}) => {
|
1852
|
+
if (!tokensWithAddresses.length) return {
|
1853
|
+
success: [],
|
1854
|
+
errors: []
|
1855
|
+
};
|
1846
1856
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1847
1857
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
1848
1858
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -1861,7 +1871,6 @@ const fetchWithoutMulticall = async (client, balanceDefs) => {
|
|
1861
1871
|
success: [],
|
1862
1872
|
errors: []
|
1863
1873
|
};
|
1864
|
-
log.debug("fetching %s balances without multicall3", MODULE_TYPE$7, balanceDefs.length);
|
1865
1874
|
const results = await Promise.allSettled(balanceDefs.map(async ({
|
1866
1875
|
token,
|
1867
1876
|
address
|
@@ -1903,7 +1912,6 @@ const fetchWithMulticall = async (client, balanceDefs, multicall3Address) => {
|
|
1903
1912
|
success: [],
|
1904
1913
|
errors: []
|
1905
1914
|
};
|
1906
|
-
log.debug("fetching %s balances with multicall3", MODULE_TYPE$7, balanceDefs.length);
|
1907
1915
|
try {
|
1908
1916
|
const callResults = await client.multicall({
|
1909
1917
|
contracts: balanceDefs.map(({
|
@@ -1993,12 +2001,16 @@ const getTransferCallData$7 = ({
|
|
1993
2001
|
};
|
1994
2002
|
};
|
1995
2003
|
|
1996
|
-
const SUBSCRIPTION_INTERVAL$
|
2004
|
+
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
1997
2005
|
const subscribeBalances$7 = ({
|
1998
2006
|
networkId,
|
1999
2007
|
tokensWithAddresses,
|
2000
2008
|
connector
|
2001
2009
|
}) => {
|
2010
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
2011
|
+
success: [],
|
2012
|
+
errors: []
|
2013
|
+
});
|
2002
2014
|
return new rxjs.Observable(subscriber => {
|
2003
2015
|
const abortController = new AbortController();
|
2004
2016
|
const poll = async () => {
|
@@ -2011,7 +2023,7 @@ const subscribeBalances$7 = ({
|
|
2011
2023
|
});
|
2012
2024
|
if (abortController.signal.aborted) return;
|
2013
2025
|
subscriber.next(balances);
|
2014
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
2026
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
2015
2027
|
} catch (error) {
|
2016
2028
|
log.error("Error", {
|
2017
2029
|
module: MODULE_TYPE$7,
|
@@ -2052,6 +2064,10 @@ const fetchBalances$a = async ({
|
|
2052
2064
|
tokensWithAddresses,
|
2053
2065
|
connector
|
2054
2066
|
}) => {
|
2067
|
+
if (!tokensWithAddresses.length) return {
|
2068
|
+
success: [],
|
2069
|
+
errors: []
|
2070
|
+
};
|
2055
2071
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
2056
2072
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
2057
2073
|
for (const [token, addresses] of tokensWithAddresses) {
|
@@ -2070,7 +2086,6 @@ const fetchWithoutAggregator = async (client, balanceDefs) => {
|
|
2070
2086
|
success: [],
|
2071
2087
|
errors: []
|
2072
2088
|
};
|
2073
|
-
log.debug("fetching %s balances without aggregator", MODULE_TYPE$6, balanceDefs.length);
|
2074
2089
|
const results = await Promise.allSettled(balanceDefs.map(async ({
|
2075
2090
|
token,
|
2076
2091
|
address
|
@@ -2088,7 +2103,7 @@ const fetchWithoutAggregator = async (client, balanceDefs) => {
|
|
2088
2103
|
value: result.toString(),
|
2089
2104
|
source: MODULE_TYPE$6,
|
2090
2105
|
networkId: chaindataProvider.parseTokenId(token.id).networkId,
|
2091
|
-
status: "
|
2106
|
+
status: "live"
|
2092
2107
|
};
|
2093
2108
|
return balance;
|
2094
2109
|
} catch (err) {
|
@@ -2115,7 +2130,6 @@ const fetchWithAggregator = async (client, balanceDefs, erc20BalancesAggregatorA
|
|
2115
2130
|
success: [],
|
2116
2131
|
errors: []
|
2117
2132
|
};
|
2118
|
-
log.debug("fetching %s balances with aggregator", MODULE_TYPE$6, balanceDefs.length);
|
2119
2133
|
try {
|
2120
2134
|
const erc20Balances = await client.readContract({
|
2121
2135
|
abi: erc20BalancesAggregatorAbi,
|
@@ -2132,7 +2146,7 @@ const fetchWithAggregator = async (client, balanceDefs, erc20BalancesAggregatorA
|
|
2132
2146
|
value: erc20Balances[index].toString(),
|
2133
2147
|
source: MODULE_TYPE$6,
|
2134
2148
|
networkId: chaindataProvider.parseTokenId(balanceDef.token.id).networkId,
|
2135
|
-
status: "
|
2149
|
+
status: "live"
|
2136
2150
|
}));
|
2137
2151
|
return {
|
2138
2152
|
success,
|
@@ -2326,12 +2340,16 @@ const getTransferCallData$6 = ({
|
|
2326
2340
|
};
|
2327
2341
|
};
|
2328
2342
|
|
2329
|
-
const SUBSCRIPTION_INTERVAL$
|
2343
|
+
const SUBSCRIPTION_INTERVAL$2 = 6_000;
|
2330
2344
|
const subscribeBalances$6 = ({
|
2331
2345
|
networkId,
|
2332
2346
|
tokensWithAddresses,
|
2333
2347
|
connector
|
2334
2348
|
}) => {
|
2349
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
2350
|
+
success: [],
|
2351
|
+
errors: []
|
2352
|
+
});
|
2335
2353
|
return new rxjs.Observable(subscriber => {
|
2336
2354
|
const abortController = new AbortController();
|
2337
2355
|
const poll = async () => {
|
@@ -2344,7 +2362,7 @@ const subscribeBalances$6 = ({
|
|
2344
2362
|
});
|
2345
2363
|
if (abortController.signal.aborted) return;
|
2346
2364
|
subscriber.next(balances);
|
2347
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
2365
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
|
2348
2366
|
} catch (error) {
|
2349
2367
|
log.error("Error", {
|
2350
2368
|
module: MODULE_TYPE$6,
|
@@ -3208,273 +3226,138 @@ async function getPoolBalance(publicClient, contractAddress, accountAddress) {
|
|
3208
3226
|
const MODULE_TYPE$5 = chaindataProvider.SubAssetsTokenSchema.shape.type.value;
|
3209
3227
|
const PLATFORM$5 = chaindataProvider.SubAssetsTokenSchema.shape.platform.value;
|
3210
3228
|
|
3211
|
-
|
3212
|
-
|
3213
|
-
|
3214
|
-
|
3229
|
+
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
3230
|
+
const allStateKeys = queries.flatMap(({
|
3231
|
+
stateKeys
|
3232
|
+
}) => stateKeys).filter(util.isNotNil);
|
3215
3233
|
|
3216
|
-
|
3217
|
-
|
3218
|
-
|
3219
|
-
|
3220
|
-
}
|
3234
|
+
// doing a query without keys would throw an error => return early
|
3235
|
+
if (!allStateKeys.length) return queries.map(({
|
3236
|
+
stateKeys,
|
3237
|
+
decodeResult
|
3238
|
+
}) => decodeResult(stateKeys.map(() => null)));
|
3239
|
+
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
3240
|
+
return decodeRpcQueryPack(queries, result);
|
3241
|
+
};
|
3242
|
+
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
3243
|
+
const allStateKeys = queries.flatMap(({
|
3244
|
+
stateKeys
|
3245
|
+
}) => stateKeys).filter(util.isNotNil);
|
3221
3246
|
|
3222
|
-
//
|
3223
|
-
return
|
3224
|
-
|
3247
|
+
// doing a query without keys would throw an error => return early
|
3248
|
+
if (!allStateKeys.length) return rxjs.of(queries.map(({
|
3249
|
+
stateKeys,
|
3250
|
+
decodeResult
|
3251
|
+
}) => decodeResult(stateKeys.map(() => null))));
|
3252
|
+
return new rxjs.Observable(subscriber => {
|
3253
|
+
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
3254
|
+
if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
|
3255
|
+
}, timeout);
|
3256
|
+
return () => {
|
3257
|
+
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
3258
|
+
};
|
3259
|
+
});
|
3260
|
+
};
|
3261
|
+
const decodeRpcQueryPack = (queries, result) => {
|
3262
|
+
return queries.reduce((acc, {
|
3263
|
+
stateKeys,
|
3264
|
+
decodeResult
|
3265
|
+
}) => {
|
3266
|
+
const changes = stateKeys.map(stateKey => {
|
3267
|
+
if (!stateKey) return null;
|
3268
|
+
const change = result.changes.find(([key]) => key === stateKey);
|
3269
|
+
if (!change) return null;
|
3270
|
+
return change[1];
|
3271
|
+
});
|
3272
|
+
acc.push(decodeResult(changes));
|
3273
|
+
return acc;
|
3274
|
+
}, []);
|
3275
|
+
};
|
3225
3276
|
|
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]
|
3277
|
+
const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
|
3278
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3279
|
+
storage: ["Assets", "Account"]
|
3280
|
+
});
|
3281
|
+
return balanceDefs.map(({
|
3282
|
+
token,
|
3283
|
+
address
|
3284
|
+
}) => {
|
3285
|
+
const scaleCoder = networkStorageCoders?.storage;
|
3286
|
+
const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
|
3287
|
+
// Asset Hub
|
3288
|
+
tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
|
3259
3289
|
|
3260
|
-
|
3261
|
-
|
3262
|
-
|
3290
|
+
if (!stateKey) {
|
3291
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
|
3292
|
+
return null;
|
3293
|
+
}
|
3294
|
+
const decodeResult = changes => {
|
3295
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3296
|
+
|
3297
|
+
const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
3298
|
+
balance: 0n,
|
3299
|
+
status: {
|
3300
|
+
type: "Liquid"
|
3301
|
+
}};
|
3302
|
+
const isFrozen = decoded?.status?.type === "Frozen";
|
3303
|
+
const amount = (decoded?.balance ?? 0n).toString();
|
3304
|
+
|
3305
|
+
// due to the following balance calculations, which are made in the `Balance` type:
|
3306
|
+
//
|
3307
|
+
// total balance = (free balance) + (reserved balance)
|
3308
|
+
// transferable balance = (free balance) - (frozen balance)
|
3309
|
+
//
|
3310
|
+
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
3311
|
+
// of this balance to the value we received from the RPC.
|
3312
|
+
//
|
3313
|
+
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
3314
|
+
const free = amount;
|
3315
|
+
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
3316
|
+
|
3317
|
+
// include balance values even if zero, so that newly-zero values overwrite old values
|
3318
|
+
const balanceValues = [{
|
3319
|
+
type: "free",
|
3320
|
+
label: "free",
|
3321
|
+
amount: free.toString()
|
3322
|
+
}, {
|
3323
|
+
type: "locked",
|
3324
|
+
label: "frozen",
|
3325
|
+
amount: frozen.toString()
|
3326
|
+
}];
|
3327
|
+
const balance = {
|
3328
|
+
source: "substrate-assets",
|
3329
|
+
status: "live",
|
3330
|
+
address,
|
3331
|
+
networkId,
|
3332
|
+
tokenId: token.id,
|
3333
|
+
values: balanceValues
|
3334
|
+
};
|
3335
|
+
return balance;
|
3336
|
+
};
|
3337
|
+
return {
|
3338
|
+
stateKeys: [stateKey],
|
3339
|
+
decodeResult
|
3340
|
+
};
|
3341
|
+
}).filter(util.isNotNil);
|
3342
|
+
};
|
3343
|
+
const tryEncode$1 = (scaleCoder, ...args) => {
|
3263
3344
|
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);
|
3345
|
+
return scaleCoder?.keys?.enc?.(...args);
|
3346
|
+
} catch {
|
3347
|
+
return null;
|
3279
3348
|
}
|
3280
|
-
return null;
|
3281
3349
|
};
|
3282
3350
|
|
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
|
3351
|
+
const fetchBalances$6 = async ({
|
3352
|
+
networkId,
|
3353
|
+
tokensWithAddresses,
|
3354
|
+
connector,
|
3355
|
+
miniMetadata
|
3356
|
+
}) => {
|
3357
|
+
if (!tokensWithAddresses.length) return {
|
3358
|
+
success: [],
|
3359
|
+
errors: []
|
3318
3360
|
};
|
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
3361
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
3479
3362
|
if (!miniMetadata?.data) {
|
3480
3363
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$5} balances on ${networkId}.`);
|
@@ -3509,8 +3392,8 @@ const fetchBalances$6 = async ({
|
|
3509
3392
|
}))
|
3510
3393
|
};
|
3511
3394
|
}
|
3512
|
-
const queries = buildQueries$
|
3513
|
-
const balances = await
|
3395
|
+
const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
|
3396
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
3514
3397
|
return balanceDefs.reduce((acc, def) => {
|
3515
3398
|
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
3516
3399
|
if (balance) acc.success.push(balance);
|
@@ -3537,80 +3420,6 @@ const fetchBalances$6 = async ({
|
|
3537
3420
|
errors: []
|
3538
3421
|
});
|
3539
3422
|
};
|
3540
|
-
const buildQueries$7 = (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$2(scaleCoder, Number(token.assetId), address) ??
|
3550
|
-
// Asset Hub
|
3551
|
-
tryEncode$2(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$2 = (scaleCoder, ...args) => {
|
3608
|
-
try {
|
3609
|
-
return scaleCoder?.keys?.enc?.(...args);
|
3610
|
-
} catch {
|
3611
|
-
return null;
|
3612
|
-
}
|
3613
|
-
};
|
3614
3423
|
|
3615
3424
|
const fetchTokens$5 = async ({
|
3616
3425
|
networkId,
|
@@ -3871,93 +3680,335 @@ const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
|
3871
3680
|
return codec.dec(encodedValue);
|
3872
3681
|
};
|
3873
3682
|
|
3874
|
-
const
|
3875
|
-
|
3876
|
-
|
3877
|
-
|
3878
|
-
|
3879
|
-
|
3880
|
-
if (!
|
3881
|
-
|
3882
|
-
|
3883
|
-
})
|
3884
|
-
const
|
3885
|
-
|
3683
|
+
const subscribeBalances$5 = ({
|
3684
|
+
networkId,
|
3685
|
+
tokensWithAddresses,
|
3686
|
+
connector,
|
3687
|
+
miniMetadata
|
3688
|
+
}) => {
|
3689
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
3690
|
+
success: [],
|
3691
|
+
errors: []
|
3692
|
+
});
|
3693
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
3694
|
+
const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
|
3695
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
|
3696
|
+
success: balances,
|
3697
|
+
errors: []
|
3698
|
+
})));
|
3886
3699
|
};
|
3887
|
-
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
3888
|
-
const allStateKeys = queries.flatMap(({
|
3889
|
-
stateKeys
|
3890
|
-
}) => stateKeys).filter(util.isNotNil);
|
3891
3700
|
|
3892
|
-
|
3893
|
-
|
3894
|
-
|
3895
|
-
|
3896
|
-
|
3897
|
-
|
3898
|
-
|
3899
|
-
|
3900
|
-
}, timeout);
|
3901
|
-
return () => {
|
3902
|
-
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
3903
|
-
};
|
3904
|
-
});
|
3701
|
+
const SubAssetsBalanceModule = {
|
3702
|
+
type: MODULE_TYPE$5,
|
3703
|
+
platform: PLATFORM$5,
|
3704
|
+
getMiniMetadata: getMiniMetadata$6,
|
3705
|
+
fetchTokens: fetchTokens$5,
|
3706
|
+
fetchBalances: fetchBalances$6,
|
3707
|
+
subscribeBalances: subscribeBalances$5,
|
3708
|
+
getTransferCallData: getTransferCallData$5
|
3905
3709
|
};
|
3906
|
-
|
3907
|
-
|
3908
|
-
|
3909
|
-
|
3910
|
-
|
3911
|
-
|
3912
|
-
|
3913
|
-
|
3914
|
-
|
3915
|
-
|
3916
|
-
|
3917
|
-
|
3918
|
-
|
3919
|
-
|
3710
|
+
|
3711
|
+
// to be used by chaindata too
|
3712
|
+
const SubAssetsTokenConfigSchema = z__default.default.strictObject({
|
3713
|
+
assetId: chaindataProvider.SubAssetsTokenSchema.shape.assetId,
|
3714
|
+
...TokenConfigBaseSchema.shape
|
3715
|
+
});
|
3716
|
+
|
3717
|
+
const MODULE_TYPE$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.type.value;
|
3718
|
+
const PLATFORM$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.platform.value;
|
3719
|
+
|
3720
|
+
/**
|
3721
|
+
* Wraps a BalanceModule's fetch/subscribe methods with a single `balances` method.
|
3722
|
+
* This `balances` method will subscribe if a callback parameter is provided, or otherwise fetch.
|
3723
|
+
*/
|
3724
|
+
|
3725
|
+
async function balances(balanceModule, addressesByToken, callback) {
|
3726
|
+
// subscription request
|
3727
|
+
if (callback !== undefined) return await balanceModule.subscribeBalances({
|
3728
|
+
addressesByToken
|
3729
|
+
}, callback);
|
3730
|
+
|
3731
|
+
// one-off request
|
3732
|
+
return await balanceModule.fetchBalances(addressesByToken);
|
3733
|
+
}
|
3734
|
+
|
3735
|
+
// TODO remove this one in favor of the network specific one below
|
3736
|
+
const buildStorageCoders = ({
|
3737
|
+
chainIds,
|
3738
|
+
chains,
|
3739
|
+
miniMetadatas,
|
3740
|
+
coders
|
3741
|
+
}) => new Map([...chainIds].flatMap(chainId => {
|
3742
|
+
const chain = chains[chainId];
|
3743
|
+
if (!chain) return [];
|
3744
|
+
const miniMetadata = miniMetadatas.get(chainId); // findMiniMetadata<TBalanceModule>(miniMetadatas, moduleType, chain)
|
3745
|
+
if (!miniMetadata) return [];
|
3746
|
+
if (!miniMetadata.data) return [];
|
3747
|
+
const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
|
3748
|
+
try {
|
3749
|
+
const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
|
3750
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3751
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3752
|
+
chainId
|
3753
|
+
}) : moduleMethodOrFn;
|
3754
|
+
try {
|
3755
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3756
|
+
} catch (cause) {
|
3757
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3758
|
+
return [];
|
3759
|
+
}
|
3760
|
+
}));
|
3761
|
+
return [[chainId, builtCoders]];
|
3762
|
+
} catch (cause) {
|
3763
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3764
|
+
return [];
|
3765
|
+
}
|
3766
|
+
}));
|
3767
|
+
// type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
|
3768
|
+
|
3769
|
+
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
3770
|
+
if (!miniMetadata.data) return null;
|
3771
|
+
const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
|
3772
|
+
try {
|
3773
|
+
const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
|
3774
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
3775
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
3776
|
+
chainId
|
3777
|
+
}) : moduleMethodOrFn;
|
3778
|
+
try {
|
3779
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
3780
|
+
} catch (cause) {
|
3781
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
3782
|
+
return [];
|
3783
|
+
}
|
3784
|
+
}));
|
3785
|
+
return builtCoders;
|
3786
|
+
} catch (cause) {
|
3787
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
3788
|
+
}
|
3789
|
+
return null;
|
3920
3790
|
};
|
3921
3791
|
|
3922
|
-
|
3923
|
-
|
3924
|
-
|
3925
|
-
|
3926
|
-
|
3927
|
-
|
3928
|
-
|
3929
|
-
|
3930
|
-
|
3931
|
-
|
3932
|
-
|
3933
|
-
|
3792
|
+
/**
|
3793
|
+
* Decodes & unwraps outputs and errors of a given result, contract, and method.
|
3794
|
+
* Parsed error message can be found in `decodedOutput` if `isError` is true.
|
3795
|
+
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3796
|
+
*/
|
3797
|
+
function decodeOutput({
|
3798
|
+
result
|
3799
|
+
}, registry, abi, method) {
|
3800
|
+
let output;
|
3801
|
+
let decodedOutput = "";
|
3802
|
+
let isError = true;
|
3803
|
+
if (result.isOk) {
|
3804
|
+
const flags = result.asOk.flags.toHuman();
|
3805
|
+
isError = flags.includes("Revert");
|
3806
|
+
const abiMessage = getAbiMessage(abi, method);
|
3807
|
+
const returnType = abiMessage.returnType;
|
3808
|
+
const returnTypeName = getReturnTypeName(returnType);
|
3809
|
+
const r = returnType ? registry.createTypeUnsafe(returnTypeName, [result.asOk.data]).toHuman() : "()";
|
3810
|
+
output = isOk(r) ? r.Ok : isErr(r) ? r.Err : r;
|
3811
|
+
const errorText = isErr(output) ? typeof output.Err === "object" ? JSON.stringify(output.Err, null, 2) : output.Err?.toString() ?? "Error" : output !== "Ok" ? output?.toString() || "Error" : "Error";
|
3812
|
+
const okText = isOk(r) ? typeof output === "object" ? JSON.stringify(output, null, "\t") : output?.toString() ?? "()" : JSON.stringify(output, null, "\t") ?? "()";
|
3813
|
+
decodedOutput = isError ? errorText : okText;
|
3814
|
+
} else if (result.isErr) {
|
3815
|
+
output = result.toHuman();
|
3816
|
+
let errorText;
|
3817
|
+
if (isErr(output) && typeof output.Err === "object" && Object.keys(output.Err || {}).length && typeof Object.values(output.Err || {})[0] === "string") {
|
3818
|
+
const [errorKey, errorValue] = Object.entries(output.Err || {})[0];
|
3819
|
+
errorText = `${errorKey}${errorValue}`;
|
3820
|
+
}
|
3821
|
+
decodedOutput = errorText || "Error";
|
3822
|
+
}
|
3823
|
+
return {
|
3824
|
+
output,
|
3825
|
+
decodedOutput,
|
3826
|
+
isError
|
3827
|
+
};
|
3828
|
+
}
|
3829
|
+
|
3830
|
+
/**
|
3831
|
+
* Helper types & functions
|
3832
|
+
* SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
|
3833
|
+
*/
|
3834
|
+
|
3835
|
+
function isErr(o) {
|
3836
|
+
return typeof o === "object" && o !== null && "Err" in o;
|
3837
|
+
}
|
3838
|
+
function isOk(o) {
|
3839
|
+
return typeof o === "object" && o !== null && "Ok" in o;
|
3840
|
+
}
|
3841
|
+
function getReturnTypeName(type) {
|
3842
|
+
return type?.lookupName || type?.type || "";
|
3843
|
+
}
|
3844
|
+
function getAbiMessage(abi, method) {
|
3845
|
+
const abiMessage = abi.messages.find(m => util$1.stringCamelCase(m.method) === util$1.stringCamelCase(method));
|
3846
|
+
if (!abiMessage) {
|
3847
|
+
throw new Error(`"${method}" not found in Contract`);
|
3848
|
+
}
|
3849
|
+
return abiMessage;
|
3850
|
+
}
|
3851
|
+
|
3852
|
+
/**
|
3853
|
+
*
|
3854
|
+
* Detect Balances::transfer -> Balances::transfer_allow_death migration
|
3855
|
+
* https://github.com/paritytech/substrate/pull/12951
|
3856
|
+
*
|
3857
|
+
* `transfer_allow_death` is the preferred method,
|
3858
|
+
* so if something goes wrong during detection, we should assume the chain has migrated
|
3859
|
+
* @param metadataRpc string containing the hashed RPC metadata for the chain
|
3860
|
+
* @returns
|
3861
|
+
*/
|
3862
|
+
const detectTransferMethod = metadataRpc => {
|
3863
|
+
const pjsMetadata = new types.Metadata(new types.TypeRegistry(), metadataRpc);
|
3864
|
+
pjsMetadata.registry.setMetadata(pjsMetadata);
|
3865
|
+
const balancesPallet = pjsMetadata.asLatest.pallets.find(pallet => pallet.name.eq("Balances"));
|
3866
|
+
const balancesCallsTypeIndex = balancesPallet?.calls.value.type.toNumber();
|
3867
|
+
const balancesCallsType = balancesCallsTypeIndex !== undefined ? pjsMetadata.asLatest.lookup.types[balancesCallsTypeIndex] : undefined;
|
3868
|
+
const hasDeprecatedTransferCall = balancesCallsType?.type.def.asVariant?.variants.find(variant => variant.name.eq("transfer")) !== undefined;
|
3869
|
+
return hasDeprecatedTransferCall ? "transfer" : "transfer_allow_death";
|
3934
3870
|
};
|
3935
|
-
|
3871
|
+
|
3872
|
+
const getUniqueChainIds = (addressesByToken, tokens) => [...new Set(Object.keys(addressesByToken).map(tokenId => tokens[tokenId]?.networkId).flatMap(chainId => chainId ? [chainId] : []))];
|
3873
|
+
|
3874
|
+
const makeContractCaller = ({
|
3875
|
+
chainConnector,
|
3876
|
+
chainId,
|
3877
|
+
registry
|
3878
|
+
}) => async (callFrom, contractAddress, inputData) => registry.createType("ContractExecResult", await chainConnector.send(chainId, "state_call", ["ContractsApi_call", util$1.u8aToHex(util$1.u8aConcatStrict([
|
3879
|
+
// origin
|
3880
|
+
registry.createType("AccountId", callFrom).toU8a(),
|
3881
|
+
// dest
|
3882
|
+
registry.createType("AccountId", contractAddress).toU8a(),
|
3883
|
+
// value
|
3884
|
+
registry.createType("Balance", 0).toU8a(),
|
3885
|
+
// gasLimit
|
3886
|
+
registry.createType("Option<WeightV2>").toU8a(),
|
3887
|
+
// storageDepositLimit
|
3888
|
+
registry.createType("Option<Balance>").toU8a(),
|
3889
|
+
// inputData
|
3890
|
+
inputData instanceof Uint8Array ? inputData : inputData.toU8a()]))]));
|
3891
|
+
|
3892
|
+
/**
|
3893
|
+
* Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
|
3894
|
+
*/
|
3895
|
+
|
3896
|
+
/**
|
3897
|
+
* Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
|
3898
|
+
*/
|
3899
|
+
class RpcStateQueryHelper {
|
3900
|
+
#chainConnector;
|
3901
|
+
#queries;
|
3902
|
+
constructor(chainConnector, queries) {
|
3903
|
+
this.#chainConnector = chainConnector;
|
3904
|
+
this.#queries = queries;
|
3905
|
+
}
|
3906
|
+
async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
|
3907
|
+
const queriesByChain = groupBy__default.default(this.#queries, "chainId");
|
3908
|
+
const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
|
3909
|
+
const params = [queries.map(({
|
3910
|
+
stateKey
|
3911
|
+
}) => stateKey)];
|
3912
|
+
const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
|
3913
|
+
error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
|
3914
|
+
}, timeout);
|
3915
|
+
return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
|
3916
|
+
});
|
3917
|
+
return () => subscriptions.forEach(unsubscribe => unsubscribe());
|
3918
|
+
}
|
3919
|
+
async fetch(method = "state_queryStorageAt") {
|
3920
|
+
const queriesByChain = groupBy__default.default(this.#queries, "chainId");
|
3921
|
+
const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
|
3922
|
+
const params = [queries.map(({
|
3923
|
+
stateKey
|
3924
|
+
}) => stateKey)];
|
3925
|
+
const result = (await this.#chainConnector.send(chainId, method, params))[0];
|
3926
|
+
return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
|
3927
|
+
}));
|
3928
|
+
return resultsByChain.flatMap(result => result);
|
3929
|
+
}
|
3930
|
+
#distributeChangesToQueryDecoders(chainId, result) {
|
3931
|
+
if (typeof result !== "object" || result === null) return [];
|
3932
|
+
if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
|
3933
|
+
if (!Array.isArray(result.changes)) return [];
|
3934
|
+
return result.changes.flatMap(([reference, change]) => {
|
3935
|
+
if (typeof reference !== "string") {
|
3936
|
+
log.warn(`Received non-string reference in RPC result: ${reference}`);
|
3937
|
+
return [];
|
3938
|
+
}
|
3939
|
+
if (typeof change !== "string" && change !== null) {
|
3940
|
+
log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
|
3941
|
+
return [];
|
3942
|
+
}
|
3943
|
+
const query = this.#queries.find(({
|
3944
|
+
chainId: cId,
|
3945
|
+
stateKey
|
3946
|
+
}) => cId === chainId && stateKey === reference);
|
3947
|
+
if (!query) {
|
3948
|
+
log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
|
3949
|
+
stateKey
|
3950
|
+
}) => stateKey)}`);
|
3951
|
+
return [];
|
3952
|
+
}
|
3953
|
+
return [query.decodeResult(change)];
|
3954
|
+
});
|
3955
|
+
}
|
3956
|
+
}
|
3957
|
+
|
3958
|
+
const configureStore = (dbTable = db.balancesBlob) => ({
|
3959
|
+
persistData: async balances => {
|
3960
|
+
const output = compress(balances);
|
3961
|
+
await dbTable.clear();
|
3962
|
+
await dbTable.put({
|
3963
|
+
data: output,
|
3964
|
+
id: Date.now().toString()
|
3965
|
+
});
|
3966
|
+
},
|
3967
|
+
retrieveData: async () => {
|
3968
|
+
const compressedData = await dbTable.toCollection().first();
|
3969
|
+
if (!compressedData) return [];
|
3970
|
+
return decompress(compressedData.data);
|
3971
|
+
}
|
3972
|
+
});
|
3973
|
+
const compress = balances => pako__default.default.deflate(JSON.stringify(balances));
|
3974
|
+
const decompress = data => {
|
3975
|
+
const decompressed = pako__default.default.inflate(data, {
|
3976
|
+
to: "string"
|
3977
|
+
});
|
3978
|
+
return JSON.parse(decompressed);
|
3979
|
+
};
|
3980
|
+
|
3981
|
+
const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
3936
3982
|
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
3937
|
-
storage: ["
|
3983
|
+
storage: ["ForeignAssets", "Account"]
|
3938
3984
|
});
|
3939
3985
|
return balanceDefs.map(({
|
3940
3986
|
token,
|
3941
3987
|
address
|
3942
3988
|
}) => {
|
3943
3989
|
const scaleCoder = networkStorageCoders?.storage;
|
3944
|
-
const
|
3945
|
-
|
3946
|
-
|
3947
|
-
|
3990
|
+
const getStateKey = onChainId => {
|
3991
|
+
try {
|
3992
|
+
return scaleCoder?.keys?.enc?.(scale.papiParse(onChainId), address);
|
3993
|
+
} catch {
|
3994
|
+
return null;
|
3995
|
+
}
|
3996
|
+
};
|
3997
|
+
const stateKey = getStateKey(token.onChainId);
|
3948
3998
|
if (!stateKey) {
|
3949
|
-
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.
|
3999
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
3950
4000
|
return null;
|
3951
|
-
}
|
4001
|
+
}
|
3952
4002
|
const decodeResult = changes => {
|
3953
4003
|
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3954
4004
|
|
3955
4005
|
const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
3956
4006
|
balance: 0n,
|
4007
|
+
is_frozen: false,
|
3957
4008
|
status: {
|
3958
4009
|
type: "Liquid"
|
3959
4010
|
}};
|
3960
|
-
const isFrozen = decoded?.status?.type === "Frozen";
|
4011
|
+
const isFrozen = decoded.is_frozen ?? decoded?.status?.type === "Frozen";
|
3961
4012
|
const amount = (decoded?.balance ?? 0n).toString();
|
3962
4013
|
|
3963
4014
|
// due to the following balance calculations, which are made in the `Balance` type:
|
@@ -3998,32 +4049,6 @@ const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
|
|
3998
4049
|
};
|
3999
4050
|
}).filter(util.isNotNil);
|
4000
4051
|
};
|
4001
|
-
const tryEncode$1 = (scaleCoder, ...args) => {
|
4002
|
-
try {
|
4003
|
-
return scaleCoder?.keys?.enc?.(...args);
|
4004
|
-
} catch {
|
4005
|
-
return null;
|
4006
|
-
}
|
4007
|
-
};
|
4008
|
-
|
4009
|
-
const SubAssetsBalanceModule = {
|
4010
|
-
type: MODULE_TYPE$5,
|
4011
|
-
platform: PLATFORM$5,
|
4012
|
-
getMiniMetadata: getMiniMetadata$6,
|
4013
|
-
fetchTokens: fetchTokens$5,
|
4014
|
-
fetchBalances: fetchBalances$6,
|
4015
|
-
subscribeBalances: subscribeBalances$5,
|
4016
|
-
getTransferCallData: getTransferCallData$5
|
4017
|
-
};
|
4018
|
-
|
4019
|
-
// to be used by chaindata too
|
4020
|
-
const SubAssetsTokenConfigSchema = z__default.default.strictObject({
|
4021
|
-
assetId: chaindataProvider.SubAssetsTokenSchema.shape.assetId,
|
4022
|
-
...TokenConfigBaseSchema.shape
|
4023
|
-
});
|
4024
|
-
|
4025
|
-
const MODULE_TYPE$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.type.value;
|
4026
|
-
const PLATFORM$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.platform.value;
|
4027
4052
|
|
4028
4053
|
const fetchBalances$5 = async ({
|
4029
4054
|
networkId,
|
@@ -4031,6 +4056,10 @@ const fetchBalances$5 = async ({
|
|
4031
4056
|
connector,
|
4032
4057
|
miniMetadata
|
4033
4058
|
}) => {
|
4059
|
+
if (!tokensWithAddresses.length) return {
|
4060
|
+
success: [],
|
4061
|
+
errors: []
|
4062
|
+
};
|
4034
4063
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4035
4064
|
if (!miniMetadata?.data) {
|
4036
4065
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$4} balances on ${networkId}.`);
|
@@ -4066,7 +4095,7 @@ const fetchBalances$5 = async ({
|
|
4066
4095
|
};
|
4067
4096
|
}
|
4068
4097
|
const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
|
4069
|
-
const balances = await
|
4098
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
4070
4099
|
return balanceDefs.reduce((acc, def) => {
|
4071
4100
|
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
4072
4101
|
if (balance) acc.success.push(balance);
|
@@ -4075,95 +4104,23 @@ const fetchBalances$5 = async ({
|
|
4075
4104
|
address: def.address,
|
4076
4105
|
networkId,
|
4077
4106
|
tokenId: def.token.id,
|
4078
|
-
source: MODULE_TYPE$4,
|
4079
|
-
status: "live",
|
4080
|
-
values: [{
|
4081
|
-
type: "free",
|
4082
|
-
label: "free",
|
4083
|
-
amount: "0"
|
4084
|
-
}, {
|
4085
|
-
type: "locked",
|
4086
|
-
label: "frozen",
|
4087
|
-
amount: "0"
|
4088
|
-
}]
|
4089
|
-
});
|
4090
|
-
return acc;
|
4091
|
-
}, {
|
4092
|
-
success: [],
|
4093
|
-
errors: []
|
4094
|
-
});
|
4095
|
-
};
|
4096
|
-
const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
|
4097
|
-
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
4098
|
-
storage: ["ForeignAssets", "Account"]
|
4099
|
-
});
|
4100
|
-
return balanceDefs.map(({
|
4101
|
-
token,
|
4102
|
-
address
|
4103
|
-
}) => {
|
4104
|
-
const scaleCoder = networkStorageCoders?.storage;
|
4105
|
-
const getStateKey = onChainId => {
|
4106
|
-
try {
|
4107
|
-
return scaleCoder?.keys?.enc?.(scale.papiParse(onChainId), address);
|
4108
|
-
} catch {
|
4109
|
-
return null;
|
4110
|
-
}
|
4111
|
-
};
|
4112
|
-
const stateKey = getStateKey(token.onChainId);
|
4113
|
-
if (!stateKey) {
|
4114
|
-
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
4115
|
-
return null;
|
4116
|
-
}
|
4117
|
-
const decodeResult = change => {
|
4118
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
4119
|
-
|
4120
|
-
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
4121
|
-
balance: 0n,
|
4122
|
-
is_frozen: false,
|
4123
|
-
status: {
|
4124
|
-
type: "Liquid"
|
4125
|
-
}};
|
4126
|
-
const isFrozen = decoded.is_frozen ?? decoded?.status?.type === "Frozen";
|
4127
|
-
const amount = (decoded?.balance ?? 0n).toString();
|
4128
|
-
|
4129
|
-
// due to the following balance calculations, which are made in the `Balance` type:
|
4130
|
-
//
|
4131
|
-
// total balance = (free balance) + (reserved balance)
|
4132
|
-
// transferable balance = (free balance) - (frozen balance)
|
4133
|
-
//
|
4134
|
-
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
4135
|
-
// of this balance to the value we received from the RPC.
|
4136
|
-
//
|
4137
|
-
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
4138
|
-
const free = amount;
|
4139
|
-
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
4140
|
-
|
4141
|
-
// include balance values even if zero, so that newly-zero values overwrite old values
|
4142
|
-
const balanceValues = [{
|
4143
|
-
type: "free",
|
4144
|
-
label: "free",
|
4145
|
-
amount: free.toString()
|
4146
|
-
}, {
|
4147
|
-
type: "locked",
|
4148
|
-
label: "frozen",
|
4149
|
-
amount: frozen.toString()
|
4150
|
-
}];
|
4151
|
-
const balance = {
|
4152
|
-
source: "substrate-assets",
|
4153
|
-
status: "live",
|
4154
|
-
address,
|
4155
|
-
networkId,
|
4156
|
-
tokenId: token.id,
|
4157
|
-
values: balanceValues
|
4158
|
-
};
|
4159
|
-
return balance;
|
4160
|
-
};
|
4161
|
-
return {
|
4162
|
-
chainId: networkId,
|
4163
|
-
stateKey,
|
4164
|
-
decodeResult
|
4165
|
-
};
|
4166
|
-
}).filter(util.isNotNil);
|
4107
|
+
source: MODULE_TYPE$4,
|
4108
|
+
status: "live",
|
4109
|
+
values: [{
|
4110
|
+
type: "free",
|
4111
|
+
label: "free",
|
4112
|
+
amount: "0"
|
4113
|
+
}, {
|
4114
|
+
type: "locked",
|
4115
|
+
label: "frozen",
|
4116
|
+
amount: "0"
|
4117
|
+
}]
|
4118
|
+
});
|
4119
|
+
return acc;
|
4120
|
+
}, {
|
4121
|
+
success: [],
|
4122
|
+
errors: []
|
4123
|
+
});
|
4167
4124
|
};
|
4168
4125
|
|
4169
4126
|
const fetchTokens$4 = async ({
|
@@ -4362,46 +4319,22 @@ const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
|
|
4362
4319
|
})]);
|
4363
4320
|
};
|
4364
4321
|
|
4365
|
-
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
4366
4322
|
const subscribeBalances$4 = ({
|
4367
4323
|
networkId,
|
4368
4324
|
tokensWithAddresses,
|
4369
4325
|
connector,
|
4370
4326
|
miniMetadata
|
4371
4327
|
}) => {
|
4372
|
-
return
|
4373
|
-
|
4374
|
-
|
4375
|
-
|
4376
|
-
|
4377
|
-
|
4378
|
-
|
4379
|
-
|
4380
|
-
|
4381
|
-
|
4382
|
-
tokensWithAddresses: tokensWithAddresses,
|
4383
|
-
connector,
|
4384
|
-
miniMetadata
|
4385
|
-
});
|
4386
|
-
if (abortController.signal.aborted) return;
|
4387
|
-
subscriber.next(balances);
|
4388
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
4389
|
-
} catch (error) {
|
4390
|
-
log.error("Error", {
|
4391
|
-
module: MODULE_TYPE$4,
|
4392
|
-
networkId,
|
4393
|
-
miniMetadata,
|
4394
|
-
addressesByToken: tokensWithAddresses,
|
4395
|
-
error
|
4396
|
-
});
|
4397
|
-
subscriber.error(error);
|
4398
|
-
}
|
4399
|
-
};
|
4400
|
-
poll();
|
4401
|
-
return () => {
|
4402
|
-
abortController.abort();
|
4403
|
-
};
|
4404
|
-
}).pipe(rxjs.distinctUntilChanged(lodash.isEqual));
|
4328
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
4329
|
+
success: [],
|
4330
|
+
errors: []
|
4331
|
+
});
|
4332
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4333
|
+
const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
|
4334
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
|
4335
|
+
success: balances,
|
4336
|
+
errors: []
|
4337
|
+
})));
|
4405
4338
|
};
|
4406
4339
|
|
4407
4340
|
const SubForeignAssetsBalanceModule = {
|
@@ -4435,9 +4368,15 @@ const fetchBalances$4 = async ({
|
|
4435
4368
|
connector,
|
4436
4369
|
miniMetadata
|
4437
4370
|
}) => {
|
4371
|
+
if (!tokensWithAddresses.length) return {
|
4372
|
+
success: [],
|
4373
|
+
errors: []
|
4374
|
+
};
|
4438
4375
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
4439
4376
|
if (!miniMetadata?.data) {
|
4440
|
-
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}
|
4377
|
+
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}.`, {
|
4378
|
+
tokensWithAddresses
|
4379
|
+
});
|
4441
4380
|
return {
|
4442
4381
|
success: [],
|
4443
4382
|
errors: balanceDefs.map(def => ({
|
@@ -4493,7 +4432,7 @@ const fetchBalances$4 = async ({
|
|
4493
4432
|
networkId,
|
4494
4433
|
tokenId: token.id,
|
4495
4434
|
source: MODULE_TYPE$3,
|
4496
|
-
status: "
|
4435
|
+
status: "live",
|
4497
4436
|
values: [{
|
4498
4437
|
type: "free",
|
4499
4438
|
label: "free",
|
@@ -4684,13 +4623,17 @@ const getTransferCallData$3 = ({
|
|
4684
4623
|
};
|
4685
4624
|
};
|
4686
4625
|
|
4687
|
-
const SUBSCRIPTION_INTERVAL$
|
4626
|
+
const SUBSCRIPTION_INTERVAL$1 = 6_000;
|
4688
4627
|
const subscribeBalances$3 = ({
|
4689
4628
|
networkId,
|
4690
4629
|
tokensWithAddresses,
|
4691
4630
|
connector,
|
4692
4631
|
miniMetadata
|
4693
4632
|
}) => {
|
4633
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
4634
|
+
success: [],
|
4635
|
+
errors: []
|
4636
|
+
});
|
4694
4637
|
return new rxjs.Observable(subscriber => {
|
4695
4638
|
const abortController = new AbortController();
|
4696
4639
|
|
@@ -4707,7 +4650,7 @@ const subscribeBalances$3 = ({
|
|
4707
4650
|
});
|
4708
4651
|
if (abortController.signal.aborted) return;
|
4709
4652
|
subscriber.next(balances);
|
4710
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
4653
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$1);
|
4711
4654
|
} catch (error) {
|
4712
4655
|
log.error("Error", {
|
4713
4656
|
module: MODULE_TYPE$3,
|
@@ -5456,6 +5399,10 @@ const fetchBalances$3 = async ({
|
|
5456
5399
|
connector,
|
5457
5400
|
miniMetadata
|
5458
5401
|
}) => {
|
5402
|
+
if (!tokensWithAddresses.length) return {
|
5403
|
+
success: [],
|
5404
|
+
errors: []
|
5405
|
+
};
|
5459
5406
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
5460
5407
|
if (!miniMetadata?.data) {
|
5461
5408
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$2} balances on ${networkId}.`);
|
@@ -5737,6 +5684,11 @@ const subscribeBalances$2 = ({
|
|
5737
5684
|
connector,
|
5738
5685
|
miniMetadata
|
5739
5686
|
}) => {
|
5687
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
5688
|
+
success: [],
|
5689
|
+
errors: []
|
5690
|
+
});
|
5691
|
+
|
5740
5692
|
// could be use as shared observable key if we decide to cache the sub
|
5741
5693
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
5742
5694
|
const baseQueries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
|
@@ -6940,6 +6892,10 @@ const fetchBalances$2 = async ({
|
|
6940
6892
|
tokensWithAddresses,
|
6941
6893
|
connector
|
6942
6894
|
}) => {
|
6895
|
+
if (!tokensWithAddresses.length) return {
|
6896
|
+
success: [],
|
6897
|
+
errors: []
|
6898
|
+
};
|
6943
6899
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
6944
6900
|
if (!balanceDefs.length) return {
|
6945
6901
|
success: [],
|
@@ -7127,13 +7083,17 @@ const getTransferCallData$1 = async ({
|
|
7127
7083
|
};
|
7128
7084
|
};
|
7129
7085
|
|
7130
|
-
const SUBSCRIPTION_INTERVAL
|
7086
|
+
const SUBSCRIPTION_INTERVAL = 6_000;
|
7131
7087
|
const subscribeBalances$1 = ({
|
7132
7088
|
networkId,
|
7133
7089
|
tokensWithAddresses,
|
7134
7090
|
connector,
|
7135
7091
|
miniMetadata
|
7136
7092
|
}) => {
|
7093
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
7094
|
+
success: [],
|
7095
|
+
errors: []
|
7096
|
+
});
|
7137
7097
|
return new rxjs.Observable(subscriber => {
|
7138
7098
|
const abortController = new AbortController();
|
7139
7099
|
|
@@ -7150,7 +7110,7 @@ const subscribeBalances$1 = ({
|
|
7150
7110
|
});
|
7151
7111
|
if (abortController.signal.aborted) return;
|
7152
7112
|
subscriber.next(balances);
|
7153
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL
|
7113
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL);
|
7154
7114
|
} catch (error) {
|
7155
7115
|
log.error("Error", {
|
7156
7116
|
module: MODULE_TYPE$1,
|
@@ -7188,12 +7148,77 @@ const SubPsp22TokenConfigSchema = z__default.default.strictObject({
|
|
7188
7148
|
const MODULE_TYPE = chaindataProvider.SubTokensTokenSchema.shape.type.value;
|
7189
7149
|
const PLATFORM = chaindataProvider.SubTokensTokenSchema.shape.platform.value;
|
7190
7150
|
|
7151
|
+
const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
|
7152
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7153
|
+
storage: [miniMetadata.extra.palletId, "Accounts"]
|
7154
|
+
});
|
7155
|
+
return balanceDefs.map(({
|
7156
|
+
token,
|
7157
|
+
address
|
7158
|
+
}) => {
|
7159
|
+
const scaleCoder = networkStorageCoders?.storage;
|
7160
|
+
const getStateKey = onChainId => {
|
7161
|
+
try {
|
7162
|
+
return scaleCoder.keys.enc(address, scale.papiParse(onChainId));
|
7163
|
+
} catch {
|
7164
|
+
return null;
|
7165
|
+
}
|
7166
|
+
};
|
7167
|
+
const stateKey = getStateKey(token.onChainId);
|
7168
|
+
if (!stateKey) {
|
7169
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
7170
|
+
return null;
|
7171
|
+
}
|
7172
|
+
const decodeResult = changes => {
|
7173
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7174
|
+
|
7175
|
+
const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
|
7176
|
+
free: 0n,
|
7177
|
+
reserved: 0n,
|
7178
|
+
frozen: 0n
|
7179
|
+
};
|
7180
|
+
const free = (decoded?.free ?? 0n).toString();
|
7181
|
+
const reserved = (decoded?.reserved ?? 0n).toString();
|
7182
|
+
const frozen = (decoded?.frozen ?? 0n).toString();
|
7183
|
+
const balanceValues = [{
|
7184
|
+
type: "free",
|
7185
|
+
label: "free",
|
7186
|
+
amount: free.toString()
|
7187
|
+
}, {
|
7188
|
+
type: "reserved",
|
7189
|
+
label: "reserved",
|
7190
|
+
amount: reserved.toString()
|
7191
|
+
}, {
|
7192
|
+
type: "locked",
|
7193
|
+
label: "frozen",
|
7194
|
+
amount: frozen.toString()
|
7195
|
+
}];
|
7196
|
+
return {
|
7197
|
+
source: "substrate-tokens",
|
7198
|
+
status: "live",
|
7199
|
+
address,
|
7200
|
+
networkId,
|
7201
|
+
tokenId: token.id,
|
7202
|
+
values: balanceValues
|
7203
|
+
};
|
7204
|
+
};
|
7205
|
+
return {
|
7206
|
+
stateKeys: [stateKey],
|
7207
|
+
decodeResult
|
7208
|
+
};
|
7209
|
+
}).filter(util.isNotNil);
|
7210
|
+
};
|
7211
|
+
|
7191
7212
|
const fetchBalances$1 = async ({
|
7192
7213
|
networkId,
|
7193
7214
|
tokensWithAddresses,
|
7194
7215
|
connector,
|
7195
7216
|
miniMetadata
|
7196
7217
|
}) => {
|
7218
|
+
if (!tokensWithAddresses.length) return {
|
7219
|
+
success: [],
|
7220
|
+
errors: []
|
7221
|
+
};
|
7197
7222
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
7198
7223
|
if (!miniMetadata?.data) {
|
7199
7224
|
log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE} balances on ${networkId}.`);
|
@@ -7229,7 +7254,7 @@ const fetchBalances$1 = async ({
|
|
7229
7254
|
};
|
7230
7255
|
}
|
7231
7256
|
const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
|
7232
|
-
const balances = await
|
7257
|
+
const balances = await fetchRpcQueryPack(connector, networkId, queries);
|
7233
7258
|
return balanceDefs.reduce((acc, def) => {
|
7234
7259
|
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
7235
7260
|
if (balance) acc.success.push(balance);
|
@@ -7256,67 +7281,6 @@ const fetchBalances$1 = async ({
|
|
7256
7281
|
errors: []
|
7257
7282
|
});
|
7258
7283
|
};
|
7259
|
-
const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
|
7260
|
-
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7261
|
-
storage: [miniMetadata.extra.palletId, "Accounts"]
|
7262
|
-
});
|
7263
|
-
return balanceDefs.map(({
|
7264
|
-
token,
|
7265
|
-
address
|
7266
|
-
}) => {
|
7267
|
-
const scaleCoder = networkStorageCoders?.storage;
|
7268
|
-
const getStateKey = onChainId => {
|
7269
|
-
try {
|
7270
|
-
return scaleCoder.keys.enc(address, scale.papiParse(onChainId));
|
7271
|
-
} catch {
|
7272
|
-
return null;
|
7273
|
-
}
|
7274
|
-
};
|
7275
|
-
const stateKey = getStateKey(token.onChainId);
|
7276
|
-
if (!stateKey) {
|
7277
|
-
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
7278
|
-
return null;
|
7279
|
-
}
|
7280
|
-
const decodeResult = change => {
|
7281
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7282
|
-
|
7283
|
-
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
|
7284
|
-
free: 0n,
|
7285
|
-
reserved: 0n,
|
7286
|
-
frozen: 0n
|
7287
|
-
};
|
7288
|
-
const free = (decoded?.free ?? 0n).toString();
|
7289
|
-
const reserved = (decoded?.reserved ?? 0n).toString();
|
7290
|
-
const frozen = (decoded?.frozen ?? 0n).toString();
|
7291
|
-
const balanceValues = [{
|
7292
|
-
type: "free",
|
7293
|
-
label: "free",
|
7294
|
-
amount: free.toString()
|
7295
|
-
}, {
|
7296
|
-
type: "reserved",
|
7297
|
-
label: "reserved",
|
7298
|
-
amount: reserved.toString()
|
7299
|
-
}, {
|
7300
|
-
type: "locked",
|
7301
|
-
label: "frozen",
|
7302
|
-
amount: frozen.toString()
|
7303
|
-
}];
|
7304
|
-
return {
|
7305
|
-
source: "substrate-tokens",
|
7306
|
-
status: "live",
|
7307
|
-
address,
|
7308
|
-
networkId,
|
7309
|
-
tokenId: token.id,
|
7310
|
-
values: balanceValues
|
7311
|
-
};
|
7312
|
-
};
|
7313
|
-
return {
|
7314
|
-
chainId: networkId,
|
7315
|
-
stateKey,
|
7316
|
-
decodeResult
|
7317
|
-
};
|
7318
|
-
}).filter(util.isNotNil);
|
7319
|
-
};
|
7320
7284
|
|
7321
7285
|
const fetchTokens = async ({
|
7322
7286
|
networkId,
|
@@ -7517,46 +7481,22 @@ const getCallDataOptions = (to, token, value, type, config) => {
|
|
7517
7481
|
}] : []));
|
7518
7482
|
};
|
7519
7483
|
|
7520
|
-
const SUBSCRIPTION_INTERVAL = 6_000;
|
7521
7484
|
const subscribeBalances = ({
|
7522
7485
|
networkId,
|
7523
7486
|
tokensWithAddresses,
|
7524
7487
|
connector,
|
7525
7488
|
miniMetadata
|
7526
7489
|
}) => {
|
7527
|
-
return
|
7528
|
-
|
7529
|
-
|
7530
|
-
|
7531
|
-
|
7532
|
-
|
7533
|
-
|
7534
|
-
|
7535
|
-
|
7536
|
-
|
7537
|
-
tokensWithAddresses: tokensWithAddresses,
|
7538
|
-
connector,
|
7539
|
-
miniMetadata
|
7540
|
-
});
|
7541
|
-
if (abortController.signal.aborted) return;
|
7542
|
-
subscriber.next(balances);
|
7543
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL);
|
7544
|
-
} catch (error) {
|
7545
|
-
log.error("Error", {
|
7546
|
-
module: MODULE_TYPE,
|
7547
|
-
networkId,
|
7548
|
-
miniMetadata,
|
7549
|
-
addressesByToken: tokensWithAddresses,
|
7550
|
-
error
|
7551
|
-
});
|
7552
|
-
subscriber.error(error);
|
7553
|
-
}
|
7554
|
-
};
|
7555
|
-
poll();
|
7556
|
-
return () => {
|
7557
|
-
abortController.abort();
|
7558
|
-
};
|
7559
|
-
}).pipe(rxjs.distinctUntilChanged(lodash.isEqual));
|
7490
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
7491
|
+
success: [],
|
7492
|
+
errors: []
|
7493
|
+
});
|
7494
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
7495
|
+
const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
|
7496
|
+
return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
|
7497
|
+
success: balances,
|
7498
|
+
errors: []
|
7499
|
+
})));
|
7560
7500
|
};
|
7561
7501
|
|
7562
7502
|
const SubTokensBalanceModule = {
|
@@ -10426,14 +10366,18 @@ class BalancesProvider {
|
|
10426
10366
|
acc[networkId][tokenId] = addresses;
|
10427
10367
|
return acc;
|
10428
10368
|
}, {});
|
10429
|
-
return rxjs.combineLatest(
|
10430
|
-
|
10431
|
-
|
10432
|
-
|
10433
|
-
|
10434
|
-
|
10435
|
-
|
10436
|
-
|
10369
|
+
return rxjs.combineLatest({
|
10370
|
+
isStale: rxjs.timer(30_000).pipe(rxjs.map(() => true), rxjs.startWith(false)),
|
10371
|
+
results: rxjs.combineLatest(lodash.toPairs(addressesByTokenIdByNetworkId).map(([networkId]) => this.getNetworkBalances$(networkId, addressesByTokenIdByNetworkId[networkId])))
|
10372
|
+
}).pipe(rxjs.map(({
|
10373
|
+
isStale,
|
10374
|
+
results
|
10375
|
+
}) => ({
|
10376
|
+
status: !isStale && results.some(({
|
10377
|
+
status
|
10378
|
+
}) => status === "initialising") ? "initialising" : "live",
|
10379
|
+
balances: results.flatMap(result => result.balances)
|
10380
|
+
})), rxjs.startWith({
|
10437
10381
|
status: "initialising",
|
10438
10382
|
balances: this.getStoredBalances(addressesByTokenId)
|
10439
10383
|
}), rxjs.distinctUntilChanged(lodash.isEqual));
|