@talismn/balances 0.0.0-pr2043-20250619165342 → 0.0.0-pr2043-20250620074243
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/BalanceModule.d.ts +14 -7
- package/dist/declarations/src/modules/EvmErc20Module.d.ts +18 -11
- package/dist/declarations/src/modules/EvmNativeModule.d.ts +17 -4
- package/dist/declarations/src/modules/EvmUniswapV2Module.d.ts +26 -17
- package/dist/declarations/src/modules/SubstrateAssetsModule.d.ts +19 -8
- package/dist/declarations/src/modules/SubstrateForeignAssetsModule.d.ts +19 -8
- package/dist/declarations/src/modules/SubstrateNativeModule/index.d.ts +14 -1
- package/dist/declarations/src/modules/SubstrateNativeModule/types.d.ts +2 -4
- package/dist/declarations/src/modules/SubstratePsp22Module.d.ts +19 -11
- package/dist/declarations/src/modules/SubstrateTokensModule.d.ts +18 -11
- package/dist/declarations/src/modules/index.d.ts +110 -8
- package/dist/declarations/src/modules/util/InferBalanceModuleTypes.d.ts +5 -3
- package/dist/declarations/src/types/balances.d.ts +18 -2
- package/dist/declarations/src/types/tokens.d.ts +11 -0
- package/dist/talismn-balances.cjs.dev.js +241 -339
- package/dist/talismn-balances.cjs.prod.js +241 -339
- package/dist/talismn-balances.esm.js +238 -343
- package/package.json +7 -7
- package/dist/declarations/src/modules/SubstrateNativeModule/subscribeCrowdloans.d.ts +0 -5
- package/dist/declarations/src/modules/SubstrateNativeModule/util/crowdloanFundContributionsChildKey.d.ts +0 -4
@@ -1,11 +1,11 @@
|
|
1
1
|
import { Dexie } from 'dexie';
|
2
2
|
import anylogger from 'anylogger';
|
3
|
-
import { evmErc20TokenId, EvmErc20TokenSchema, networkIdFromTokenId,
|
3
|
+
import { evmErc20TokenId, TokenBaseSchema, EvmErc20TokenSchema, networkIdFromTokenId, EvmUniswapV2TokenSchema, evmUniswapV2TokenId, getGithubTokenLogoUrl, DotNetworkBalancesConfigSchema, SubAssetsTokenSchema, parseSubAssetTokenId, subAssetTokenId, SubForeignAssetsTokenSchema, parseSubForeignAssetTokenId, subForeignAssetTokenId, parseTokenId, subNativeTokenId, SubPsp22TokenSchema, subPsp22TokenId, SubTokensTokenSchema, parseSubTokensTokenId, subTokensTokenId } from '@talismn/chaindata-provider';
|
4
4
|
import { newTokenRates } from '@talismn/token-rates';
|
5
|
-
import { isBigInt, BigMath, planckToTokens, isArrayOf, isTruthy, isEthereumAddress, hasOwnProperty, isAbortError,
|
5
|
+
import { isBigInt, BigMath, planckToTokens, isArrayOf, isTruthy, isEthereumAddress, hasOwnProperty, isAbortError, isNotNil, decodeAnyAddress, blake2Concat, Deferred } from '@talismn/util';
|
6
6
|
import BigNumber from 'bignumber.js';
|
7
|
-
import { u8aToHex, assert, stringCamelCase, u8aConcatStrict,
|
8
|
-
import { xxhashAsU8a
|
7
|
+
import { u8aToHex, assert, stringCamelCase, u8aConcatStrict, arrayChunk, u8aToString, hexToNumber, hexToU8a } from '@polkadot/util';
|
8
|
+
import { xxhashAsU8a } from '@polkadot/util-crypto';
|
9
9
|
import pako from 'pako';
|
10
10
|
import { parseAbi, isHex, hexToBigInt } from 'viem';
|
11
11
|
import { fromPairs, toPairs, keys, groupBy as groupBy$1 } from 'lodash';
|
@@ -14,17 +14,16 @@ import { defineMethod } from '@substrate/txwrapper-core';
|
|
14
14
|
import { unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, compactMetadata, encodeMetadata, decodeScale, papiParse, getMetadataVersion, encodeStateKey } from '@talismn/scale';
|
15
15
|
import camelCase from 'lodash/camelCase';
|
16
16
|
import PQueue from 'p-queue';
|
17
|
+
import z from 'zod/v4';
|
17
18
|
import { fetchBestMetadata, getScaleApi } from '@talismn/sapi';
|
18
19
|
import { Metadata, TypeRegistry } from '@polkadot/types';
|
19
20
|
import groupBy from 'lodash/groupBy';
|
20
21
|
import { mergeUint8, toHex } from '@polkadot-api/utils';
|
21
22
|
import { Binary, AccountId } from 'polkadot-api';
|
22
|
-
import PromisePool from '@supercharge/promise-pool';
|
23
23
|
import { ChainConnectionError } from '@talismn/chain-connector';
|
24
24
|
import { Observable, scan, share, map, switchAll, combineLatest, from, mergeMap, toArray, interval, startWith, exhaustMap, BehaviorSubject, debounceTime, takeUntil, distinctUntilChanged, switchMap, withLatestFrom, concatMap } from 'rxjs';
|
25
|
-
import { u32,
|
25
|
+
import { u32, Struct, u128 } from 'scale-ts';
|
26
26
|
import upperFirst from 'lodash/upperFirst';
|
27
|
-
import z from 'zod/v4';
|
28
27
|
import { Abi } from '@polkadot/api-contract';
|
29
28
|
|
30
29
|
// Record<string, unknown> | undefined
|
@@ -113,7 +112,7 @@ class EvmTokenFetcher {
|
|
113
112
|
|
114
113
|
var pkg = {
|
115
114
|
name: "@talismn/balances",
|
116
|
-
version: "0.0.0-pr2043-
|
115
|
+
version: "0.0.0-pr2043-20250620074243"};
|
117
116
|
|
118
117
|
var log = anylogger(pkg.name);
|
119
118
|
|
@@ -1017,6 +1016,10 @@ class TalismanBalancesDatabase extends Dexie {
|
|
1017
1016
|
}
|
1018
1017
|
const db = new TalismanBalancesDatabase();
|
1019
1018
|
|
1019
|
+
const TokenConfigBaseSchema = TokenBaseSchema.partial().omit({
|
1020
|
+
id: true
|
1021
|
+
});
|
1022
|
+
|
1020
1023
|
const erc20Abi = [{
|
1021
1024
|
constant: true,
|
1022
1025
|
inputs: [],
|
@@ -1248,6 +1251,20 @@ const erc20Abi = [{
|
|
1248
1251
|
const erc20BalancesAggregatorAbi = parseAbi(["struct AccountToken {address account; address token;}", "function balances(AccountToken[] memory accountTokens) public view returns (uint256[] memory)"]);
|
1249
1252
|
|
1250
1253
|
const moduleType$7 = "evm-erc20";
|
1254
|
+
// {
|
1255
|
+
// tokens?: Array<
|
1256
|
+
// {
|
1257
|
+
// symbol?: string
|
1258
|
+
// decimals?: number
|
1259
|
+
// name?: string
|
1260
|
+
// contractAddress?: `0x${string}`
|
1261
|
+
// } & BalancesConfigTokenParams
|
1262
|
+
// >
|
1263
|
+
// }
|
1264
|
+
|
1265
|
+
const EvmErc20TokenConfigSchema = TokenConfigBaseSchema.extend({
|
1266
|
+
contractAddress: EvmErc20TokenSchema.shape.contractAddress
|
1267
|
+
});
|
1251
1268
|
const EvmErc20Module = hydrate => {
|
1252
1269
|
const {
|
1253
1270
|
chainConnectors,
|
@@ -1293,11 +1310,11 @@ const EvmErc20Module = hydrate => {
|
|
1293
1310
|
* This method is currently executed on [a squid](https://github.com/TalismanSociety/chaindata-squid/blob/0ee02818bf5caa7362e3f3664e55ef05ec8df078/src/steps/updateEvmNetworksFromGithub.ts#L338-L343).
|
1294
1311
|
* In a future version of the balance libraries, we may build some kind of async scheduling system which will keep the list of tokens for each chain up to date without relying on a squid.
|
1295
1312
|
*/
|
1296
|
-
async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig) {
|
1313
|
+
async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig, tokens) {
|
1297
1314
|
//const { isTestnet } = chainMeta
|
1298
1315
|
|
1299
1316
|
const chainTokens = {};
|
1300
|
-
for (const tokenConfig of
|
1317
|
+
for (const tokenConfig of tokens ?? []) {
|
1301
1318
|
const {
|
1302
1319
|
contractAddress,
|
1303
1320
|
symbol: contractSymbol,
|
@@ -1581,6 +1598,7 @@ function groupAddressesByTokenByEvmNetwork$1(addressesByToken, tokens) {
|
|
1581
1598
|
const abiMulticall = parseAbi(["struct Call { address target; bytes callData; }", "struct Call3 { address target; bool allowFailure; bytes callData; }", "struct Call3Value { address target; bool allowFailure; uint256 value; bytes callData; }", "struct Result { bool success; bytes returnData; }", "function aggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes[] memory returnData)", "function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData)", "function aggregate3Value(Call3Value[] calldata calls) public payable returns (Result[] memory returnData)", "function blockAndAggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData)", "function getBasefee() view returns (uint256 basefee)", "function getBlockHash(uint256 blockNumber) view returns (bytes32 blockHash)", "function getBlockNumber() view returns (uint256 blockNumber)", "function getChainId() view returns (uint256 chainid)", "function getCurrentBlockCoinbase() view returns (address coinbase)", "function getCurrentBlockDifficulty() view returns (uint256 difficulty)", "function getCurrentBlockGasLimit() view returns (uint256 gaslimit)", "function getCurrentBlockTimestamp() view returns (uint256 timestamp)", "function getEthBalance(address addr) view returns (uint256 balance)", "function getLastBlockHash() view returns (bytes32 blockHash)", "function tryAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (Result[] memory returnData)", "function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData)"]);
|
1582
1599
|
|
1583
1600
|
const moduleType$6 = "evm-native";
|
1601
|
+
const EvmNativeTokenConfigSchema = TokenConfigBaseSchema;
|
1584
1602
|
const EvmNativeModule = hydrate => {
|
1585
1603
|
const {
|
1586
1604
|
chainConnectors,
|
@@ -1608,29 +1626,34 @@ const EvmNativeModule = hydrate => {
|
|
1608
1626
|
* This method is currently executed on [a squid](https://github.com/TalismanSociety/chaindata-squid/blob/0ee02818bf5caa7362e3f3664e55ef05ec8df078/src/steps/updateEvmNetworksFromGithub.ts#L338-L343).
|
1609
1627
|
* In a future version of the balance libraries, we may build some kind of async scheduling system which will keep the list of tokens for each chain up to date without relying on a squid.
|
1610
1628
|
*/
|
1611
|
-
async fetchEvmChainTokens(
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
const
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1629
|
+
async fetchEvmChainTokens() {
|
1630
|
+
// networkId, chainMeta, moduleConfig, tokens
|
1631
|
+
// TODO ? seems unneeded, this info is set on the EthNetworkConfig["nativeCurrency"] field
|
1632
|
+
return {};
|
1633
|
+
|
1634
|
+
// const symbol = moduleConfig?.symbol ?? "ETH"
|
1635
|
+
// const decimals = typeof moduleConfig?.decimals === "number" ? moduleConfig.decimals : 18
|
1636
|
+
// const name = moduleConfig?.name ?? symbol
|
1637
|
+
|
1638
|
+
// const id = evmNativeTokenId(networkId)
|
1639
|
+
// const nativeToken: EvmNativeToken = {
|
1640
|
+
// platform: "ethereum",
|
1641
|
+
// id,
|
1642
|
+
// type: "evm-native",
|
1643
|
+
// isDefault: true,
|
1644
|
+
// symbol,
|
1645
|
+
// decimals,
|
1646
|
+
// name,
|
1647
|
+
// logo: moduleConfig?.logo,
|
1648
|
+
// networkId,
|
1649
|
+
// }
|
1650
|
+
|
1651
|
+
// if (moduleConfig?.symbol) nativeToken.symbol = moduleConfig?.symbol
|
1652
|
+
// if (moduleConfig?.coingeckoId) nativeToken.coingeckoId = moduleConfig?.coingeckoId
|
1653
|
+
// if (moduleConfig?.mirrorOf) nativeToken.mirrorOf = moduleConfig?.mirrorOf
|
1654
|
+
// if (moduleConfig?.noDiscovery) nativeToken.noDiscovery = moduleConfig?.noDiscovery
|
1655
|
+
|
1656
|
+
// return { [nativeToken.id]: nativeToken }
|
1634
1657
|
},
|
1635
1658
|
async subscribeBalances({
|
1636
1659
|
addressesByToken,
|
@@ -2372,6 +2395,36 @@ const uniswapV2PairAbi = [{
|
|
2372
2395
|
}];
|
2373
2396
|
|
2374
2397
|
const moduleType$5 = "evm-uniswapv2";
|
2398
|
+
const EvmUniswapV2TokenConfigSchema = TokenConfigBaseSchema.extend({
|
2399
|
+
contractAddress: EvmUniswapV2TokenSchema.shape.contractAddress,
|
2400
|
+
// the ones below are unused and prone to error, feels better to always fetch these from chain
|
2401
|
+
symbol0: EvmUniswapV2TokenSchema.shape.symbol0.optional(),
|
2402
|
+
symbol1: EvmUniswapV2TokenSchema.shape.symbol1.optional(),
|
2403
|
+
decimals0: EvmUniswapV2TokenSchema.shape.decimals0.optional(),
|
2404
|
+
decimals1: EvmUniswapV2TokenSchema.shape.decimals1.optional(),
|
2405
|
+
tokenAddress0: EvmUniswapV2TokenSchema.shape.tokenAddress0.optional(),
|
2406
|
+
tokenAddress1: EvmUniswapV2TokenSchema.shape.tokenAddress1.optional(),
|
2407
|
+
coingeckoId0: EvmUniswapV2TokenSchema.shape.coingeckoId0.optional(),
|
2408
|
+
coingeckoId1: EvmUniswapV2TokenSchema.shape.coingeckoId1.optional()
|
2409
|
+
});
|
2410
|
+
|
2411
|
+
// {
|
2412
|
+
// pools?: Array<
|
2413
|
+
// {
|
2414
|
+
// contractAddress?: `0x${string}`
|
2415
|
+
// decimals?: number
|
2416
|
+
// symbol0?: string
|
2417
|
+
// symbol1?: string
|
2418
|
+
// decimals0?: number
|
2419
|
+
// decimals1?: number
|
2420
|
+
// tokenAddress0?: `0x${string}`
|
2421
|
+
// tokenAddress1?: `0x${string}`
|
2422
|
+
// coingeckoId0?: string
|
2423
|
+
// coingeckoId1?: string
|
2424
|
+
// } & BalancesConfigTokenParams
|
2425
|
+
// >
|
2426
|
+
// }
|
2427
|
+
|
2375
2428
|
const EvmUniswapV2Module = hydrate => {
|
2376
2429
|
const {
|
2377
2430
|
chainConnectors,
|
@@ -2387,9 +2440,9 @@ const EvmUniswapV2Module = hydrate => {
|
|
2387
2440
|
extra: null
|
2388
2441
|
};
|
2389
2442
|
},
|
2390
|
-
async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig) {
|
2443
|
+
async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig, pools) {
|
2391
2444
|
const tokens = {};
|
2392
|
-
for (const tokenConfig of
|
2445
|
+
for (const tokenConfig of pools ?? []) {
|
2393
2446
|
const {
|
2394
2447
|
contractAddress,
|
2395
2448
|
decimals,
|
@@ -2679,12 +2732,15 @@ const getSpecVersion = async (chainConnector, networkId) => {
|
|
2679
2732
|
const CACHE$1 = new Map();
|
2680
2733
|
const getMetadataRpc = async (chainConnector, networkId) => {
|
2681
2734
|
if (CACHE$1.has(networkId)) return CACHE$1.get(networkId);
|
2682
|
-
const pResult = fetchBestMetadata((
|
2735
|
+
const pResult = fetchBestMetadata((method, params, isCacheable) => chainConnector.send(networkId, method, params, isCacheable, {
|
2736
|
+
expectErrors: true
|
2737
|
+
}), true // allow fallback to 14 as modules dont use any v15 or v16 specifics yet
|
2683
2738
|
);
|
2684
2739
|
CACHE$1.set(networkId, pResult);
|
2685
2740
|
try {
|
2686
2741
|
return await pResult;
|
2687
2742
|
} catch (cause) {
|
2743
|
+
if (isAbortError(cause)) throw cause;
|
2688
2744
|
throw new Error(`Failed to fetch metadataRpc for network ${networkId}`, {
|
2689
2745
|
cause
|
2690
2746
|
});
|
@@ -2702,6 +2758,8 @@ const POOL = new PQueue({
|
|
2702
2758
|
});
|
2703
2759
|
const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, specVersion, signal) => {
|
2704
2760
|
if (CACHE.has(networkId)) return CACHE.get(networkId);
|
2761
|
+
if (!signal) log.warn("[miniMetadata] getMiniMetadatas called without signal, this may hang the updates", new Error("No signal provided") // this will show the stack trace
|
2762
|
+
);
|
2705
2763
|
const pResult = POOL.add(() => fetchMiniMetadatas(chainConnector, chaindataProvider, networkId, specVersion), {
|
2706
2764
|
signal
|
2707
2765
|
});
|
@@ -2709,6 +2767,7 @@ const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, sp
|
|
2709
2767
|
try {
|
2710
2768
|
return await pResult;
|
2711
2769
|
} catch (cause) {
|
2770
|
+
if (isAbortError(cause)) throw cause;
|
2712
2771
|
throw new Error(`Failed to fetch metadataRpc for network ${networkId}`, {
|
2713
2772
|
cause
|
2714
2773
|
});
|
@@ -2716,9 +2775,10 @@ const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, sp
|
|
2716
2775
|
CACHE.delete(networkId);
|
2717
2776
|
}
|
2718
2777
|
};
|
2778
|
+
const DotBalanceModuleTypeSchema = z.keyof(DotNetworkBalancesConfigSchema);
|
2719
2779
|
const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, specVersion, signal) => {
|
2720
2780
|
const start = performance.now();
|
2721
|
-
log.
|
2781
|
+
log.info("[miniMetadata] fetching minimetadatas for %s", chainId);
|
2722
2782
|
try {
|
2723
2783
|
const metadataRpc = await getMetadataRpc(chainConnector, chainId);
|
2724
2784
|
signal?.throwIfAborted();
|
@@ -2729,10 +2789,14 @@ const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, sp
|
|
2729
2789
|
const modules = defaultBalanceModules.map(mod => mod({
|
2730
2790
|
chainConnectors,
|
2731
2791
|
chaindataProvider
|
2732
|
-
})).filter(mod => mod.type.
|
2792
|
+
})).filter(mod => DotBalanceModuleTypeSchema.safeParse(mod.type).success);
|
2733
2793
|
return Promise.all(modules.map(async mod => {
|
2734
2794
|
const source = mod.type;
|
2735
|
-
const
|
2795
|
+
const chain = await chaindataProvider.chainById(chainId);
|
2796
|
+
const balancesConfig = chain?.balancesConfig?.[mod.type];
|
2797
|
+
const chainMeta = await mod.fetchSubstrateChainMeta(chainId, balancesConfig,
|
2798
|
+
// TODO fix typings
|
2799
|
+
metadataRpc);
|
2736
2800
|
return {
|
2737
2801
|
id: deriveMiniMetadataId({
|
2738
2802
|
source,
|
@@ -3058,10 +3122,23 @@ const decompress = data => {
|
|
3058
3122
|
};
|
3059
3123
|
|
3060
3124
|
const moduleType$4 = "substrate-assets";
|
3125
|
+
const SubAssetsTokenConfigSchema = TokenConfigBaseSchema.extend({
|
3126
|
+
assetId: SubAssetsTokenSchema.shape.assetId,
|
3127
|
+
existentialDeposit: SubAssetsTokenSchema.shape.existentialDeposit
|
3128
|
+
});
|
3061
3129
|
const UNSUPPORTED_CHAIN_META$3 = {
|
3062
3130
|
miniMetadata: null,
|
3063
3131
|
extra: null
|
3064
3132
|
};
|
3133
|
+
|
3134
|
+
// {
|
3135
|
+
// tokens?: Array<
|
3136
|
+
// {
|
3137
|
+
// assetId: number | string
|
3138
|
+
// } & BalancesConfigTokenParams
|
3139
|
+
// >
|
3140
|
+
// }
|
3141
|
+
|
3065
3142
|
const SubAssetsModule = hydrate => {
|
3066
3143
|
const {
|
3067
3144
|
chainConnectors,
|
@@ -3085,8 +3162,8 @@ const SubAssetsModule = hydrate => {
|
|
3085
3162
|
extra: null
|
3086
3163
|
};
|
3087
3164
|
},
|
3088
|
-
async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
|
3089
|
-
if (
|
3165
|
+
async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
|
3166
|
+
if (!tokens?.length) return {};
|
3090
3167
|
const {
|
3091
3168
|
miniMetadata
|
3092
3169
|
} = chainMeta;
|
@@ -3095,8 +3172,8 @@ const SubAssetsModule = hydrate => {
|
|
3095
3172
|
const scaleBuilder = getDynamicBuilder(getLookupFn(metadata));
|
3096
3173
|
const assetCoder = scaleBuilder.buildStorage("Assets", "Asset");
|
3097
3174
|
const metadataCoder = scaleBuilder.buildStorage("Assets", "Metadata");
|
3098
|
-
const
|
3099
|
-
for (const tokenConfig of
|
3175
|
+
const tokenList = {};
|
3176
|
+
for (const tokenConfig of tokens ?? []) {
|
3100
3177
|
try {
|
3101
3178
|
const assetId = String(tokenConfig.assetId);
|
3102
3179
|
const assetStateKey = tryEncode(assetCoder, BigInt(assetId)) ?? tryEncode(assetCoder, assetId);
|
@@ -3128,13 +3205,13 @@ const SubAssetsModule = hydrate => {
|
|
3128
3205
|
}
|
3129
3206
|
if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
|
3130
3207
|
if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
|
3131
|
-
|
3208
|
+
tokenList[token.id] = token;
|
3132
3209
|
} catch (error) {
|
3133
3210
|
log.error(`Failed to build substrate-assets token ${tokenConfig.assetId} (${tokenConfig.symbol}) on ${chainId}`, error);
|
3134
3211
|
continue;
|
3135
3212
|
}
|
3136
3213
|
}
|
3137
|
-
return
|
3214
|
+
return tokenList;
|
3138
3215
|
},
|
3139
3216
|
// TODO: Don't create empty subscriptions
|
3140
3217
|
async subscribeBalances({
|
@@ -3164,10 +3241,10 @@ const SubAssetsModule = hydrate => {
|
|
3164
3241
|
}
|
3165
3242
|
}));
|
3166
3243
|
return () => {
|
3167
|
-
controller.abort();
|
3168
3244
|
pUnsubs.then(unsubs => {
|
3169
3245
|
unsubs.forEach(unsubscribe => unsubscribe());
|
3170
3246
|
});
|
3247
|
+
controller.abort();
|
3171
3248
|
};
|
3172
3249
|
},
|
3173
3250
|
async fetchBalances(addressesByToken) {
|
@@ -3351,6 +3428,19 @@ const UNSUPPORTED_CHAIN_META$2 = {
|
|
3351
3428
|
miniMetadata: null,
|
3352
3429
|
extra: null
|
3353
3430
|
};
|
3431
|
+
const SubForeignAssetsTokenConfigSchema = TokenConfigBaseSchema.extend({
|
3432
|
+
onChainId: SubForeignAssetsTokenSchema.shape.onChainId,
|
3433
|
+
existentialDeposit: SubForeignAssetsTokenSchema.shape.existentialDeposit
|
3434
|
+
});
|
3435
|
+
|
3436
|
+
// {
|
3437
|
+
// tokens?: Array<
|
3438
|
+
// {
|
3439
|
+
// onChainId: string
|
3440
|
+
// } & BalancesConfigTokenParams
|
3441
|
+
// >
|
3442
|
+
// }
|
3443
|
+
|
3354
3444
|
const SubForeignAssetsModule = hydrate => {
|
3355
3445
|
const {
|
3356
3446
|
chainConnectors,
|
@@ -3375,8 +3465,8 @@ const SubForeignAssetsModule = hydrate => {
|
|
3375
3465
|
extra: null
|
3376
3466
|
};
|
3377
3467
|
},
|
3378
|
-
async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
|
3379
|
-
if (
|
3468
|
+
async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
|
3469
|
+
if (!tokens?.length) return {};
|
3380
3470
|
const {
|
3381
3471
|
miniMetadata
|
3382
3472
|
} = chainMeta;
|
@@ -3386,8 +3476,8 @@ const SubForeignAssetsModule = hydrate => {
|
|
3386
3476
|
const scaleBuilder = getDynamicBuilder(getLookupFn(unifiedMetadata));
|
3387
3477
|
const assetCoder = scaleBuilder.buildStorage("ForeignAssets", "Asset");
|
3388
3478
|
const metadataCoder = scaleBuilder.buildStorage("ForeignAssets", "Metadata");
|
3389
|
-
const
|
3390
|
-
for (const tokenConfig of
|
3479
|
+
const tokenList = {};
|
3480
|
+
for (const tokenConfig of tokens ?? []) {
|
3391
3481
|
try {
|
3392
3482
|
const onChainId = (() => {
|
3393
3483
|
try {
|
@@ -3422,13 +3512,13 @@ const SubForeignAssetsModule = hydrate => {
|
|
3422
3512
|
};
|
3423
3513
|
if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
|
3424
3514
|
if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
|
3425
|
-
|
3515
|
+
tokenList[token.id] = token;
|
3426
3516
|
} catch (error) {
|
3427
3517
|
log.error(`Failed to build substrate-foreignassets token ${tokenConfig.onChainId} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
|
3428
3518
|
continue;
|
3429
3519
|
}
|
3430
3520
|
}
|
3431
|
-
return
|
3521
|
+
return tokenList;
|
3432
3522
|
},
|
3433
3523
|
// TODO: Don't create empty subscriptions
|
3434
3524
|
async subscribeBalances({
|
@@ -3457,10 +3547,10 @@ const SubForeignAssetsModule = hydrate => {
|
|
3457
3547
|
}
|
3458
3548
|
}));
|
3459
3549
|
return () => {
|
3460
|
-
controller.abort();
|
3461
3550
|
pUnsubs.then(unsubs => {
|
3462
3551
|
unsubs.forEach(unsubscribe => unsubscribe());
|
3463
3552
|
});
|
3553
|
+
controller.abort();
|
3464
3554
|
};
|
3465
3555
|
},
|
3466
3556
|
async fetchBalances(addressesByToken) {
|
@@ -3647,263 +3737,6 @@ const asObservable = handler => (...args) => new Observable(subscriber => {
|
|
3647
3737
|
return unsubscribe;
|
3648
3738
|
});
|
3649
3739
|
|
3650
|
-
/**
|
3651
|
-
* Crowdloan contributions are stored in the `childstate` key returned by this function.
|
3652
|
-
*/
|
3653
|
-
const crowdloanFundContributionsChildKey = fundIndex => u8aToHex(u8aConcat(":child_storage:default:", blake2AsU8a(u8aConcat("crowdloan", u32.enc(fundIndex)))));
|
3654
|
-
|
3655
|
-
// TODO make this method chain-specific
|
3656
|
-
async function subscribeCrowdloans(chaindataProvider, chainConnector, addressesByToken, callback, signal) {
|
3657
|
-
const allChains = await chaindataProvider.chainsById();
|
3658
|
-
const tokens = await chaindataProvider.tokensById();
|
3659
|
-
|
3660
|
-
// there should be only one network here when subscribing to balances, we've split it up by network at the top level
|
3661
|
-
const networkIds = keys(addressesByToken).map(tokenId => parseTokenId(tokenId).networkId);
|
3662
|
-
const miniMetadatas = new Map();
|
3663
|
-
for (const networkId of networkIds) miniMetadatas.set(networkId, await getMiniMetadata(chaindataProvider, chainConnector, networkId, "substrate-native"));
|
3664
|
-
const crowdloanTokenIds = Object.entries(tokens).filter(([, token]) => {
|
3665
|
-
// ignore non-native tokens
|
3666
|
-
if (token.type !== "substrate-native") return;
|
3667
|
-
// ignore tokens on chains with no crowdloans pallet
|
3668
|
-
const miniMetadata = miniMetadatas.get(token.networkId);
|
3669
|
-
return typeof miniMetadata?.extra?.crowdloanPalletId === "string";
|
3670
|
-
}).map(([tokenId]) => tokenId);
|
3671
|
-
|
3672
|
-
// crowdloan contributions can only be done by the native token on chains with the crowdloan pallet
|
3673
|
-
const addressesByCrowdloanToken = Object.fromEntries(Object.entries(addressesByToken)
|
3674
|
-
// remove ethereum addresses
|
3675
|
-
.map(([tokenId, addresses]) => [tokenId, addresses.filter(address => !isEthereumAddress(address))])
|
3676
|
-
// remove tokens which aren't crowdloan tokens
|
3677
|
-
.filter(([tokenId]) => crowdloanTokenIds.includes(tokenId)));
|
3678
|
-
const uniqueChainIds = getUniqueChainIds(addressesByCrowdloanToken, tokens);
|
3679
|
-
const chains = Object.fromEntries(Object.entries(allChains).filter(([chainId]) => uniqueChainIds.includes(chainId)));
|
3680
|
-
const chainStorageCoders = buildStorageCoders({
|
3681
|
-
chainIds: uniqueChainIds,
|
3682
|
-
chains,
|
3683
|
-
miniMetadatas,
|
3684
|
-
coders: {
|
3685
|
-
parachains: ["Paras", "Parachains"],
|
3686
|
-
funds: ["Crowdloan", "Funds"]
|
3687
|
-
}
|
3688
|
-
});
|
3689
|
-
const tokenSubscriptions = [];
|
3690
|
-
for (const [tokenId, addresses] of Object.entries(addressesByCrowdloanToken)) {
|
3691
|
-
const token = tokens[tokenId];
|
3692
|
-
if (!token) {
|
3693
|
-
log.warn(`Token ${tokenId} not found`);
|
3694
|
-
continue;
|
3695
|
-
}
|
3696
|
-
if (token.type !== "substrate-native") {
|
3697
|
-
log.debug(`This module doesn't handle tokens of type ${token.type}`);
|
3698
|
-
continue;
|
3699
|
-
}
|
3700
|
-
const chainId = token.networkId;
|
3701
|
-
if (!chainId) {
|
3702
|
-
log.warn(`Token ${tokenId} has no chain`);
|
3703
|
-
continue;
|
3704
|
-
}
|
3705
|
-
const chain = chains[chainId];
|
3706
|
-
if (!chain) {
|
3707
|
-
log.warn(`Chain ${chainId} for token ${tokenId} not found`);
|
3708
|
-
continue;
|
3709
|
-
}
|
3710
|
-
const subscribeParaIds = callback => {
|
3711
|
-
const scaleCoder = chainStorageCoders.get(chainId)?.parachains;
|
3712
|
-
const queries = [0].flatMap(() => {
|
3713
|
-
const stateKey = encodeStateKey(scaleCoder);
|
3714
|
-
if (!stateKey) return [];
|
3715
|
-
const decodeResult = change => {
|
3716
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3717
|
-
|
3718
|
-
const decoded = decodeScale(scaleCoder, change, `Failed to decode parachains on chain ${chainId}`);
|
3719
|
-
const paraIds = decoded ?? [];
|
3720
|
-
return paraIds;
|
3721
|
-
};
|
3722
|
-
return {
|
3723
|
-
chainId,
|
3724
|
-
stateKey,
|
3725
|
-
decodeResult
|
3726
|
-
};
|
3727
|
-
});
|
3728
|
-
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3729
|
-
return () => subscription.then(unsubscribe => unsubscribe());
|
3730
|
-
};
|
3731
|
-
const subscribeParaFundIndexes = (paraIds, callback) => {
|
3732
|
-
const scaleCoder = chainStorageCoders.get(chainId)?.funds;
|
3733
|
-
const queries = paraIds.flatMap(paraId => {
|
3734
|
-
const stateKey = encodeStateKey(scaleCoder, `Invalid paraId in ${chainId} funds query ${paraId}`, paraId);
|
3735
|
-
if (!stateKey) return [];
|
3736
|
-
const decodeResult = change => {
|
3737
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3738
|
-
|
3739
|
-
const decoded = decodeScale(scaleCoder, change, `Failed to decode paras on chain ${chainId}`);
|
3740
|
-
const firstPeriod = decoded?.first_period?.toString?.() ?? "";
|
3741
|
-
const lastPeriod = decoded?.last_period?.toString?.() ?? "";
|
3742
|
-
const fundPeriod = `${firstPeriod}-${lastPeriod}`;
|
3743
|
-
const fundIndex = decoded?.fund_index ?? decoded?.trie_index;
|
3744
|
-
return {
|
3745
|
-
paraId,
|
3746
|
-
fundPeriod,
|
3747
|
-
fundIndex
|
3748
|
-
};
|
3749
|
-
};
|
3750
|
-
return {
|
3751
|
-
chainId,
|
3752
|
-
stateKey,
|
3753
|
-
decodeResult
|
3754
|
-
};
|
3755
|
-
});
|
3756
|
-
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3757
|
-
return () => subscription.then(unsubscribe => unsubscribe());
|
3758
|
-
};
|
3759
|
-
const subscribeFundContributions = (funds, addresses, callback) => {
|
3760
|
-
// TODO: Watch system_events in order to subscribe to changes, then redo the contributions query when changes are detected:
|
3761
|
-
// https://github.com/polkadot-js/api/blob/8fe02a14345b57e6abb8f7f2c2b624cf70c51b23/packages/api-derive/src/crowdloan/ownContributions.ts#L32-L47
|
3762
|
-
//
|
3763
|
-
// For now we just re-fetch all contributions on a timer and then only send them to the subscription callback when they have changed
|
3764
|
-
|
3765
|
-
const queries = funds.map(({
|
3766
|
-
paraId,
|
3767
|
-
fundIndex
|
3768
|
-
}) => ({
|
3769
|
-
paraId,
|
3770
|
-
fundIndex,
|
3771
|
-
addresses,
|
3772
|
-
childKey: crowdloanFundContributionsChildKey(fundIndex),
|
3773
|
-
storageKeys: addresses.map(address => u8aToHex(decodeAnyAddress(address)))
|
3774
|
-
}));
|
3775
|
-
|
3776
|
-
// track whether our caller is still subscribed
|
3777
|
-
let subscriptionActive = true;
|
3778
|
-
let previousContributions = null;
|
3779
|
-
const fetchContributions = async () => {
|
3780
|
-
try {
|
3781
|
-
const results = await Promise.all(queries.map(async ({
|
3782
|
-
paraId,
|
3783
|
-
fundIndex,
|
3784
|
-
addresses,
|
3785
|
-
childKey,
|
3786
|
-
storageKeys
|
3787
|
-
}) => ({
|
3788
|
-
paraId,
|
3789
|
-
fundIndex,
|
3790
|
-
addresses,
|
3791
|
-
result: await chainConnector.send(chainId, "childstate_getStorageEntries", [childKey, storageKeys])
|
3792
|
-
})));
|
3793
|
-
const contributions = results.flatMap(queryResult => {
|
3794
|
-
const {
|
3795
|
-
paraId,
|
3796
|
-
fundIndex,
|
3797
|
-
addresses,
|
3798
|
-
result
|
3799
|
-
} = queryResult;
|
3800
|
-
return (Array.isArray(result) ? result : []).flatMap((encoded, index) => {
|
3801
|
-
const amount = (() => {
|
3802
|
-
try {
|
3803
|
-
return typeof encoded === "string" ? u128.dec(encoded) ?? 0n : 0n;
|
3804
|
-
} catch {
|
3805
|
-
return 0n;
|
3806
|
-
}
|
3807
|
-
})().toString();
|
3808
|
-
return {
|
3809
|
-
paraId,
|
3810
|
-
fundIndex,
|
3811
|
-
address: addresses[index],
|
3812
|
-
amount
|
3813
|
-
};
|
3814
|
-
});
|
3815
|
-
});
|
3816
|
-
|
3817
|
-
// ignore these results if our caller has tried to close this subscription
|
3818
|
-
if (!subscriptionActive) return;
|
3819
|
-
|
3820
|
-
// ignore these results if they're the same as what we previously fetched
|
3821
|
-
if (isEqual(previousContributions, contributions)) return;
|
3822
|
-
previousContributions = contributions;
|
3823
|
-
callback(null, contributions);
|
3824
|
-
} catch (error) {
|
3825
|
-
callback(error);
|
3826
|
-
}
|
3827
|
-
};
|
3828
|
-
|
3829
|
-
// set up polling for contributions
|
3830
|
-
const crowdloanContributionsPollInterval = 60_000; // 60_000ms === 1 minute
|
3831
|
-
const pollContributions = async () => {
|
3832
|
-
if (!subscriptionActive) return;
|
3833
|
-
try {
|
3834
|
-
await fetchContributions();
|
3835
|
-
} catch (error) {
|
3836
|
-
// log any errors, but don't cancel the poll for contributions when one fetch fails
|
3837
|
-
log.error(error);
|
3838
|
-
}
|
3839
|
-
if (!subscriptionActive) return;
|
3840
|
-
setTimeout(pollContributions, crowdloanContributionsPollInterval);
|
3841
|
-
};
|
3842
|
-
|
3843
|
-
// start polling
|
3844
|
-
pollContributions();
|
3845
|
-
return () => {
|
3846
|
-
// stop polling
|
3847
|
-
subscriptionActive = false;
|
3848
|
-
};
|
3849
|
-
};
|
3850
|
-
const paraIds$ = asObservable(subscribeParaIds)().pipe(scan((_, next) => Array.from(new Set(next.flatMap(paraIds => paraIds))), []), share());
|
3851
|
-
const fundIndexesByParaId$ = paraIds$.pipe(map(paraIds => asObservable(subscribeParaFundIndexes)(paraIds)), switchAll(), scan((state, next) => {
|
3852
|
-
for (const fund of next) {
|
3853
|
-
const {
|
3854
|
-
paraId,
|
3855
|
-
fundIndex
|
3856
|
-
} = fund;
|
3857
|
-
if (typeof fundIndex === "number") {
|
3858
|
-
state.set(paraId, (state.get(paraId) ?? new Set()).add(fundIndex));
|
3859
|
-
}
|
3860
|
-
}
|
3861
|
-
return state;
|
3862
|
-
}, new Map()));
|
3863
|
-
const contributionsByAddress$ = fundIndexesByParaId$.pipe(map(fundIndexesByParaId => Array.from(fundIndexesByParaId).flatMap(([paraId, fundIndexes]) => Array.from(fundIndexes).map(fundIndex => ({
|
3864
|
-
paraId,
|
3865
|
-
fundIndex
|
3866
|
-
})))), map(funds => asObservable(subscribeFundContributions)(funds, addresses)), switchAll(), scan((state, next) => {
|
3867
|
-
for (const contribution of next) {
|
3868
|
-
const {
|
3869
|
-
address
|
3870
|
-
} = contribution;
|
3871
|
-
state.set(address, (state.get(address) ?? new Set()).add(contribution));
|
3872
|
-
}
|
3873
|
-
return state;
|
3874
|
-
}, new Map()));
|
3875
|
-
const subscription = contributionsByAddress$.subscribe({
|
3876
|
-
next: contributionsByAddress => {
|
3877
|
-
const balances = Array.from(contributionsByAddress).map(([address, contributions]) => {
|
3878
|
-
return {
|
3879
|
-
source: "substrate-native",
|
3880
|
-
status: "live",
|
3881
|
-
address,
|
3882
|
-
networkId: chainId,
|
3883
|
-
tokenId,
|
3884
|
-
values: Array.from(contributions).map(({
|
3885
|
-
amount,
|
3886
|
-
paraId
|
3887
|
-
}) => ({
|
3888
|
-
type: "crowdloan",
|
3889
|
-
label: "crowdloan",
|
3890
|
-
source: "crowdloan",
|
3891
|
-
amount: amount,
|
3892
|
-
meta: {
|
3893
|
-
paraId
|
3894
|
-
}
|
3895
|
-
}))
|
3896
|
-
};
|
3897
|
-
});
|
3898
|
-
if (balances.length > 0) callback(null, balances);
|
3899
|
-
},
|
3900
|
-
error: error => callback(error)
|
3901
|
-
});
|
3902
|
-
tokenSubscriptions.push(() => subscription.unsubscribe());
|
3903
|
-
}
|
3904
|
-
return () => tokenSubscriptions.forEach(unsub => unsub());
|
3905
|
-
}
|
3906
|
-
|
3907
3740
|
/**
|
3908
3741
|
* Each nominationPool in the nominationPools pallet has access to some accountIds which have no
|
3909
3742
|
* associated private key. Instead, they are derived from this function.
|
@@ -3935,6 +3768,7 @@ async function subscribeNompoolStaking(chaindataProvider, chainConnector, addres
|
|
3935
3768
|
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, "substrate-native");
|
3936
3769
|
miniMetadatas.set(networkId, miniMetadata);
|
3937
3770
|
}
|
3771
|
+
signal?.throwIfAborted();
|
3938
3772
|
const nomPoolTokenIds = Object.entries(tokens).filter(([, token]) => {
|
3939
3773
|
// ignore non-native tokens
|
3940
3774
|
if (token.type !== "substrate-native") return false;
|
@@ -4252,9 +4086,10 @@ async function subscribeSubtensorStaking(chaindataProvider, chainConnector, addr
|
|
4252
4086
|
const networkIds = keys(addressesByToken).map(tokenId => parseTokenId(tokenId).networkId);
|
4253
4087
|
const miniMetadatas = new Map();
|
4254
4088
|
for (const networkId of networkIds) {
|
4255
|
-
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, "substrate-native");
|
4089
|
+
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, "substrate-native", signal);
|
4256
4090
|
miniMetadatas.set(networkId, miniMetadata);
|
4257
4091
|
}
|
4092
|
+
signal?.throwIfAborted();
|
4258
4093
|
const subtensorTokenIds = Object.entries(tokens).filter(([, token]) => {
|
4259
4094
|
// ignore non-native tokens
|
4260
4095
|
if (token.type !== "substrate-native") return false;
|
@@ -4460,7 +4295,9 @@ async function subscribeSubtensorStaking(chaindataProvider, chainConnector, addr
|
|
4460
4295
|
});
|
4461
4296
|
|
4462
4297
|
// use the abortController to tear the subscription down when we don't need it anymore
|
4463
|
-
abortController.signal.
|
4298
|
+
abortController.signal.addEventListener("abort", () => {
|
4299
|
+
subscription.unsubscribe();
|
4300
|
+
});
|
4464
4301
|
}
|
4465
4302
|
return () => abortController.abort();
|
4466
4303
|
}
|
@@ -4546,6 +4383,10 @@ const getLockTitle = (lock, {
|
|
4546
4383
|
|
4547
4384
|
const moduleType$2 = "substrate-native";
|
4548
4385
|
|
4386
|
+
// {
|
4387
|
+
// disable?: boolean
|
4388
|
+
// } & BalancesConfigTokenParams
|
4389
|
+
|
4549
4390
|
/**
|
4550
4391
|
* Function to merge two 'sub sources' of the same balance together, or
|
4551
4392
|
* two instances of the same balance with different values.
|
@@ -5023,6 +4864,7 @@ const UNSUPPORTED_CHAIN_META$1 = {
|
|
5023
4864
|
miniMetadata: null,
|
5024
4865
|
extra: null
|
5025
4866
|
};
|
4867
|
+
const SubNativeTokenConfigSchema = TokenConfigBaseSchema;
|
5026
4868
|
const SubNativeModule = hydrate => {
|
5027
4869
|
const {
|
5028
4870
|
chainConnectors,
|
@@ -5038,7 +4880,7 @@ const SubNativeModule = hydrate => {
|
|
5038
4880
|
// subscribeBalances was split by network to prevent all subs to wait for all minimetadatas to be ready.
|
5039
4881
|
// however the multichain logic in there is so deep in the function below that i had to keep it as-is, and call it by per-network chunks
|
5040
4882
|
// TODO refactor this be actually network specific
|
5041
|
-
const subscribeChainBalances =
|
4883
|
+
const subscribeChainBalances = (chainId, opts, callback, signal) => {
|
5042
4884
|
const {
|
5043
4885
|
addressesByToken,
|
5044
4886
|
initialBalances
|
@@ -5137,13 +4979,19 @@ const SubNativeModule = hydrate => {
|
|
5137
4979
|
return from(queryCache.getQueries(newAddressesByToken)).pipe(switchMap(baseQueries => {
|
5138
4980
|
return new Observable(subscriber => {
|
5139
4981
|
if (!chainConnectors.substrate) return;
|
5140
|
-
const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"));
|
5141
|
-
const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"));
|
5142
|
-
const unsubCrowdloans = subscribeCrowdloans(
|
4982
|
+
const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"), signal);
|
4983
|
+
const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"), signal);
|
4984
|
+
// const unsubCrowdloans = subscribeCrowdloans(
|
4985
|
+
// chaindataProvider,
|
4986
|
+
// chainConnectors.substrate,
|
4987
|
+
// newAddressesByToken,
|
4988
|
+
// handleUpdateForSource("crowdloan"),
|
4989
|
+
// signal,
|
4990
|
+
// )
|
5143
4991
|
const unsubBase = subscribeBase(baseQueries, chainConnectors.substrate, handleUpdateForSource("base"));
|
5144
4992
|
subscriber.add(async () => (await unsubSubtensorStaking)());
|
5145
4993
|
subscriber.add(async () => (await unsubNompoolStaking)());
|
5146
|
-
subscriber.add(async () => (await unsubCrowdloans)())
|
4994
|
+
// subscriber.add(async () => (await unsubCrowdloans)())
|
5147
4995
|
subscriber.add(async () => (await unsubBase)());
|
5148
4996
|
});
|
5149
4997
|
}));
|
@@ -5175,8 +5023,13 @@ const SubNativeModule = hydrate => {
|
|
5175
5023
|
const nonCurrentTokens = Object.keys(addressesByToken).filter(tokenId => !currentTokens.has(tokenId)).sort(sortChains);
|
5176
5024
|
|
5177
5025
|
// break nonCurrentTokens into chunks of POLLING_WINDOW_SIZE
|
5178
|
-
|
5026
|
+
const pool = new PQueue({
|
5027
|
+
concurrency: POLLING_WINDOW_SIZE
|
5028
|
+
});
|
5029
|
+
nonCurrentTokens.forEach(nonCurrentTokenId => pool.add(() => poll({
|
5179
5030
|
[nonCurrentTokenId]: addressesByToken[nonCurrentTokenId]
|
5031
|
+
}), {
|
5032
|
+
signal
|
5180
5033
|
}));
|
5181
5034
|
|
5182
5035
|
// now poll every 30s on chains which are not subscriptionTokens
|
@@ -5189,7 +5042,9 @@ const SubNativeModule = hydrate => {
|
|
5189
5042
|
Object.keys(addressesByToken).filter(tokenId => !subscribedTokenIds.includes(tokenId))), exhaustMap(tokenIds => from(arrayChunk(tokenIds, POLLING_WINDOW_SIZE)).pipe(concatMap(async tokenChunk => {
|
5190
5043
|
// tokenChunk is a chunk of tokenIds with size POLLING_WINDOW_SIZE
|
5191
5044
|
const pollingTokenAddresses = Object.fromEntries(tokenChunk.map(tokenId => [tokenId, addressesByToken[tokenId]]));
|
5192
|
-
await poll(pollingTokenAddresses)
|
5045
|
+
await pool.add(() => poll(pollingTokenAddresses), {
|
5046
|
+
signal
|
5047
|
+
});
|
5193
5048
|
return true;
|
5194
5049
|
})))).subscribe();
|
5195
5050
|
return () => {
|
@@ -5234,7 +5089,7 @@ const SubNativeModule = hydrate => {
|
|
5234
5089
|
};
|
5235
5090
|
const existentialDeposit = getConstantValue("Balances", "ExistentialDeposit")?.toString();
|
5236
5091
|
const nominationPoolsPalletId = getConstantValue("NominationPools", "PalletId")?.asText();
|
5237
|
-
const crowdloanPalletId = getConstantValue("Crowdloan", "PalletId")?.asText();
|
5092
|
+
const crowdloanPalletId = getConstantValue("Crowdloan", "PalletId")?.asText(); // TODO yeet
|
5238
5093
|
const hasSubtensorPallet = getConstantValue("SubtensorModule", "KeySwapCost") !== undefined;
|
5239
5094
|
|
5240
5095
|
//
|
@@ -5257,10 +5112,13 @@ const SubNativeModule = hydrate => {
|
|
5257
5112
|
}, {
|
5258
5113
|
pallet: "Crowdloan",
|
5259
5114
|
items: ["Funds"]
|
5260
|
-
},
|
5115
|
+
},
|
5116
|
+
// TODO yeet
|
5117
|
+
{
|
5261
5118
|
pallet: "Paras",
|
5262
5119
|
items: ["Parachains"]
|
5263
5120
|
},
|
5121
|
+
// TODO yeet
|
5264
5122
|
// TotalColdkeyStake is used until v.2.2.1, then it is replaced by StakingHotkeys+Stake
|
5265
5123
|
// Need to keep TotalColdkeyStake for a while so chaindata keeps including it in miniMetadatas, so it doesnt break old versions of the wallet
|
5266
5124
|
{
|
@@ -5303,22 +5161,24 @@ const SubNativeModule = hydrate => {
|
|
5303
5161
|
const {
|
5304
5162
|
existentialDeposit
|
5305
5163
|
} = chainMeta.extra ?? {};
|
5164
|
+
if (existentialDeposit === undefined) log.warn("Substrate native module: existentialDeposit is undefined for %s, using 0", chainId);
|
5306
5165
|
const id = subNativeTokenId(chainId);
|
5307
5166
|
const nativeToken = {
|
5308
5167
|
id,
|
5309
5168
|
type: "substrate-native",
|
5310
5169
|
platform: "polkadot",
|
5311
|
-
isDefault:
|
5170
|
+
isDefault: true,
|
5312
5171
|
symbol: symbol,
|
5313
|
-
name:
|
5172
|
+
name: symbol,
|
5314
5173
|
decimals: decimals,
|
5315
|
-
logo: moduleConfig?.logo,
|
5316
5174
|
existentialDeposit: existentialDeposit ?? "0",
|
5317
5175
|
networkId: chainId
|
5318
5176
|
};
|
5319
|
-
|
5320
|
-
if (moduleConfig?.
|
5321
|
-
if (moduleConfig?.
|
5177
|
+
|
5178
|
+
// if (moduleConfig?.symbol) nativeToken.symbol = moduleConfig?.symbol
|
5179
|
+
// if (moduleConfig?.coingeckoId) nativeToken.coingeckoId = moduleConfig?.coingeckoId
|
5180
|
+
// if (moduleConfig?.mirrorOf) nativeToken.mirrorOf = moduleConfig?.mirrorOf
|
5181
|
+
|
5322
5182
|
return {
|
5323
5183
|
[nativeToken.id]: nativeToken
|
5324
5184
|
};
|
@@ -5350,11 +5210,11 @@ const SubNativeModule = hydrate => {
|
|
5350
5210
|
return subscribeChainBalances(networkId, {
|
5351
5211
|
addressesByToken: addressesByTokenByNetwork[networkId] ?? {},
|
5352
5212
|
initialBalances: initialBalancesByNetwork[networkId] ?? []
|
5353
|
-
}, safeCallback);
|
5213
|
+
}, safeCallback, controller.signal);
|
5354
5214
|
}));
|
5355
5215
|
return () => {
|
5356
|
-
controller.abort();
|
5357
5216
|
unsubsribeFns.then(fns => fns.forEach(unsubscribe => unsubscribe()));
|
5217
|
+
controller.abort();
|
5358
5218
|
};
|
5359
5219
|
},
|
5360
5220
|
fetchBalances,
|
@@ -6572,6 +6432,22 @@ var psp22Abi = {
|
|
6572
6432
|
};
|
6573
6433
|
|
6574
6434
|
const moduleType$1 = "substrate-psp22";
|
6435
|
+
const SubPsp22TokenConfigSchema = TokenConfigBaseSchema.extend({
|
6436
|
+
contractAddress: SubPsp22TokenSchema.shape.contractAddress,
|
6437
|
+
existentialDeposit: SubPsp22TokenSchema.shape.existentialDeposit.optional()
|
6438
|
+
});
|
6439
|
+
|
6440
|
+
// {
|
6441
|
+
// tokens?: Array<
|
6442
|
+
// {
|
6443
|
+
// symbol?: string
|
6444
|
+
// decimals?: number
|
6445
|
+
// ed?: string
|
6446
|
+
// contractAddress: string
|
6447
|
+
// } & BalancesConfigTokenParams
|
6448
|
+
// >
|
6449
|
+
// }
|
6450
|
+
|
6575
6451
|
const SubPsp22Module = hydrate => {
|
6576
6452
|
const {
|
6577
6453
|
chainConnectors,
|
@@ -6588,7 +6464,8 @@ const SubPsp22Module = hydrate => {
|
|
6588
6464
|
extra: null
|
6589
6465
|
};
|
6590
6466
|
},
|
6591
|
-
async fetchSubstrateChainTokens(chainId, _chainMeta, moduleConfig) {
|
6467
|
+
async fetchSubstrateChainTokens(chainId, _chainMeta, moduleConfig, tokens) {
|
6468
|
+
if (!tokens?.length) return {};
|
6592
6469
|
// const { isTestnet } = chainMeta
|
6593
6470
|
|
6594
6471
|
const registry = new TypeRegistry();
|
@@ -6600,12 +6477,12 @@ const SubPsp22Module = hydrate => {
|
|
6600
6477
|
chainId,
|
6601
6478
|
registry
|
6602
6479
|
});
|
6603
|
-
const
|
6604
|
-
for (const tokenConfig of
|
6480
|
+
const tokenList = {};
|
6481
|
+
for (const tokenConfig of tokens ?? []) {
|
6605
6482
|
try {
|
6606
6483
|
let symbol = tokenConfig?.symbol ?? "Unit";
|
6607
6484
|
let decimals = tokenConfig?.decimals ?? 0;
|
6608
|
-
const existentialDeposit = tokenConfig?.
|
6485
|
+
const existentialDeposit = tokenConfig?.existentialDeposit ?? "0";
|
6609
6486
|
const contractAddress = tokenConfig?.contractAddress ?? undefined;
|
6610
6487
|
if (contractAddress === undefined) continue;
|
6611
6488
|
await (async () => {
|
@@ -6638,13 +6515,13 @@ const SubPsp22Module = hydrate => {
|
|
6638
6515
|
};
|
6639
6516
|
if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
|
6640
6517
|
if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
|
6641
|
-
|
6518
|
+
tokenList[token.id] = token;
|
6642
6519
|
} catch (error) {
|
6643
6520
|
log.error(`Failed to build substrate-psp22 token ${tokenConfig.contractAddress} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
|
6644
6521
|
continue;
|
6645
6522
|
}
|
6646
6523
|
}
|
6647
|
-
return
|
6524
|
+
return tokenList;
|
6648
6525
|
},
|
6649
6526
|
// TODO: Don't create empty subscriptions
|
6650
6527
|
async subscribeBalances({
|
@@ -6819,11 +6696,28 @@ const fetchBalances = async (chainConnector, tokens, addressesByToken) => {
|
|
6819
6696
|
};
|
6820
6697
|
|
6821
6698
|
const moduleType = "substrate-tokens";
|
6699
|
+
const SubTokensTokenConfigSchema = TokenConfigBaseSchema.extend({
|
6700
|
+
onChainId: SubTokensTokenSchema.shape.onChainId,
|
6701
|
+
existentialDeposit: SubTokensTokenSchema.shape.existentialDeposit
|
6702
|
+
});
|
6822
6703
|
const defaultPalletId = "Tokens";
|
6823
6704
|
const UNSUPPORTED_CHAIN_META = {
|
6824
6705
|
miniMetadata: null,
|
6825
6706
|
extra: {}
|
6826
6707
|
};
|
6708
|
+
|
6709
|
+
// {
|
6710
|
+
// palletId?: string // TODO unlikely it will ever be used - remove this ?
|
6711
|
+
// tokens?: Array<
|
6712
|
+
// {
|
6713
|
+
// symbol?: string
|
6714
|
+
// decimals?: number
|
6715
|
+
// ed?: string
|
6716
|
+
// onChainId?: string | number
|
6717
|
+
// } & BalancesConfigTokenParams
|
6718
|
+
// >
|
6719
|
+
// }
|
6720
|
+
|
6827
6721
|
const SubTokensModule = hydrate => {
|
6828
6722
|
const {
|
6829
6723
|
chainConnectors,
|
@@ -6849,13 +6743,14 @@ const SubTokensModule = hydrate => {
|
|
6849
6743
|
}
|
6850
6744
|
};
|
6851
6745
|
},
|
6852
|
-
async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
|
6853
|
-
const
|
6854
|
-
for (const tokenConfig of
|
6746
|
+
async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
|
6747
|
+
const tokenList = {};
|
6748
|
+
for (const tokenConfig of tokens ?? []) {
|
6855
6749
|
try {
|
6750
|
+
// TODO fetch metadata from chain, like we do for assets
|
6856
6751
|
const symbol = tokenConfig?.symbol ?? "Unit";
|
6857
6752
|
const decimals = tokenConfig?.decimals ?? 0;
|
6858
|
-
const existentialDeposit = tokenConfig?.
|
6753
|
+
const existentialDeposit = tokenConfig?.existentialDeposit ?? "0";
|
6859
6754
|
const onChainId = tokenConfig?.onChainId ?? undefined;
|
6860
6755
|
if (onChainId === undefined) continue;
|
6861
6756
|
const id = subTokensTokenId(chainId, onChainId);
|
@@ -6874,13 +6769,13 @@ const SubTokensModule = hydrate => {
|
|
6874
6769
|
};
|
6875
6770
|
if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
|
6876
6771
|
if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
|
6877
|
-
|
6772
|
+
tokenList[token.id] = token;
|
6878
6773
|
} catch (error) {
|
6879
6774
|
log.error(`Failed to build substrate-tokens token ${tokenConfig.onChainId} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
|
6880
6775
|
continue;
|
6881
6776
|
}
|
6882
6777
|
}
|
6883
|
-
return
|
6778
|
+
return tokenList;
|
6884
6779
|
},
|
6885
6780
|
// TODO: Don't create empty subscriptions
|
6886
6781
|
async subscribeBalances({
|
@@ -6910,10 +6805,10 @@ const SubTokensModule = hydrate => {
|
|
6910
6805
|
}
|
6911
6806
|
}));
|
6912
6807
|
return () => {
|
6913
|
-
controller.abort();
|
6914
6808
|
pUnsubs.then(unsubs => {
|
6915
6809
|
unsubs.forEach(unsubscribe => unsubscribe());
|
6916
6810
|
});
|
6811
|
+
controller.abort();
|
6917
6812
|
};
|
6918
6813
|
},
|
6919
6814
|
async fetchBalances(addressesByToken) {
|
@@ -7122,4 +7017,4 @@ async function buildQueries(chainConnector, chaindataProvider, addressesByToken,
|
|
7122
7017
|
|
7123
7018
|
const defaultBalanceModules = [EvmErc20Module, EvmNativeModule, EvmUniswapV2Module, SubAssetsModule, SubForeignAssetsModule, SubNativeModule, SubPsp22Module, SubTokensModule];
|
7124
7019
|
|
7125
|
-
export { Balance, BalanceFormatter, BalanceValueGetter, Balances, Change24hCurrencyFormatter, DefaultBalanceModule, EvmErc20Module, EvmNativeModule, EvmTokenFetcher, EvmUniswapV2Module, FiatSumBalancesFormatter, ONE_ALPHA_TOKEN, PlanckSumBalancesFormatter, RpcStateQueryHelper, SCALE_FACTOR, SUBTENSOR_MIN_STAKE_AMOUNT_PLANK, SUBTENSOR_ROOT_NETUID, SubAssetsModule, SubForeignAssetsModule, SubNativeModule, SubPsp22Module, SubTokensModule, SumBalancesFormatter, TalismanBalancesDatabase, abiMulticall, balances, buildNetworkStorageCoders, buildStorageCoders, calculateAlphaPrice, calculateTaoAmountFromAlpha, calculateTaoFromDynamicInfo, compress, configureStore, db, decodeOutput, decompress, defaultBalanceModules, deriveMiniMetadataId, detectTransferMethod, erc20Abi, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, getBalanceId, getLockTitle, getUniqueChainIds, getValueId, includeInTotalExtraAmount, makeContractCaller, uniswapV2PairAbi };
|
7020
|
+
export { Balance, BalanceFormatter, BalanceValueGetter, Balances, Change24hCurrencyFormatter, DefaultBalanceModule, EvmErc20Module, EvmErc20TokenConfigSchema, EvmNativeModule, EvmNativeTokenConfigSchema, EvmTokenFetcher, EvmUniswapV2Module, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, ONE_ALPHA_TOKEN, PlanckSumBalancesFormatter, RpcStateQueryHelper, SCALE_FACTOR, SUBTENSOR_MIN_STAKE_AMOUNT_PLANK, SUBTENSOR_ROOT_NETUID, SubAssetsModule, SubAssetsTokenConfigSchema, SubForeignAssetsModule, SubForeignAssetsTokenConfigSchema, SubNativeModule, SubNativeTokenConfigSchema, SubPsp22Module, SubPsp22TokenConfigSchema, SubTokensModule, SubTokensTokenConfigSchema, SumBalancesFormatter, TalismanBalancesDatabase, abiMulticall, balances, buildNetworkStorageCoders, buildStorageCoders, calculateAlphaPrice, calculateTaoAmountFromAlpha, calculateTaoFromDynamicInfo, compress, configureStore, db, decodeOutput, decompress, defaultBalanceModules, deriveMiniMetadataId, detectTransferMethod, erc20Abi, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, getBalanceId, getLockTitle, getUniqueChainIds, getValueId, includeInTotalExtraAmount, makeContractCaller, uniswapV2PairAbi };
|