@talismn/balances 0.0.0-pr2130-20250807071025 → 0.0.0-pr2134-20250813091030
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/declarations/src/BalancesProvider.d.ts +1 -0
- package/dist/declarations/src/getMiniMetadatas/getMetadataRpc.d.ts +2 -2
- package/dist/declarations/src/getMiniMetadatas/getSpecVersion.d.ts +2 -2
- package/dist/declarations/src/getMiniMetadatas/index.d.ts +2 -2
- package/dist/declarations/src/modules/index.d.ts +23 -0
- package/dist/declarations/src/modules/shared/errors.d.ts +1 -1
- package/dist/declarations/src/modules/shared/fetchRuntimeCallResult.d.ts +2 -2
- package/dist/declarations/src/modules/shared/rpcQueryPack.d.ts +3 -3
- package/dist/declarations/src/modules/sol-native/config.d.ts +3 -0
- package/dist/declarations/src/modules/sol-native/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/sol-native/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/sol-native/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/sol-native/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/sol-native/index.d.ts +2 -0
- package/dist/declarations/src/modules/sol-native/module.d.ts +3 -0
- package/dist/declarations/src/modules/sol-native/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/sol-native/types.d.ts +13 -0
- package/dist/declarations/src/modules/sol-spl/config.d.ts +3 -0
- package/dist/declarations/src/modules/sol-spl/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/sol-spl/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/sol-spl/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/sol-spl/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/sol-spl/index.d.ts +2 -0
- package/dist/declarations/src/modules/sol-spl/module.d.ts +3 -0
- package/dist/declarations/src/modules/sol-spl/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/sol-spl/types.d.ts +14 -0
- package/dist/declarations/src/modules/substrate-native/bittensor/getSubtensorStakingBalances.d.ts +2 -2
- package/dist/declarations/src/modules/substrate-native/fetchTokens.d.ts +2 -2
- package/dist/declarations/src/modules/substrate-psp22/util.d.ts +2 -2
- package/dist/declarations/src/types/IBalanceModule.d.ts +26 -19
- package/dist/declarations/src/types/balances.d.ts +52 -4
- package/dist/declarations/src/types/balancetypes.d.ts +0 -4
- package/dist/declarations/src/types/chainConnectors.d.ts +4 -4
- package/dist/talismn-balances.cjs.dev.js +750 -245
- package/dist/talismn-balances.cjs.prod.js +750 -245
- package/dist/talismn-balances.esm.js +740 -239
- package/package.json +13 -8
@@ -1,13 +1,18 @@
|
|
1
|
-
import { EvmErc20TokenSchema, parseTokenId, parseEvmErc20TokenId, evmErc20TokenId, isTokenOfType, TokenBaseSchema, EvmNativeTokenSchema, evmNativeTokenId, EvmUniswapV2TokenSchema, evmUniswapV2TokenId, SubAssetsTokenSchema, subAssetTokenId, MINIMETADATA_VERSION, SubForeignAssetsTokenSchema, subForeignAssetTokenId, SubHydrationTokenSchema, subHydrationTokenId, SubNativeTokenSchema, subNativeTokenId, SubPsp22TokenSchema, subPsp22TokenId, SubTokensTokenSchema, subTokensTokenId, isNetworkDot } from '@talismn/chaindata-provider';
|
1
|
+
import { EvmErc20TokenSchema, parseTokenId, parseEvmErc20TokenId, evmErc20TokenId, isTokenOfType, TokenBaseSchema, EvmNativeTokenSchema, evmNativeTokenId, EvmUniswapV2TokenSchema, evmUniswapV2TokenId, SolNativeTokenSchema, solNativeTokenId, SolSplTokenSchema, solSplTokenId, parseSolSplTokenId, SubAssetsTokenSchema, subAssetTokenId, MINIMETADATA_VERSION, SubForeignAssetsTokenSchema, subForeignAssetTokenId, SubHydrationTokenSchema, subHydrationTokenId, SubNativeTokenSchema, subNativeTokenId, SubPsp22TokenSchema, subPsp22TokenId, SubTokensTokenSchema, subTokensTokenId, isNetworkDot } from '@talismn/chaindata-provider';
|
2
2
|
export { MINIMETADATA_VERSION } from '@talismn/chaindata-provider';
|
3
|
-
import { isEthereumAddress,
|
3
|
+
import { isEthereumAddress, isSolanaAddress, normalizeAddress, getAccountPlatformFromAddress } from '@talismn/crypto';
|
4
4
|
import { parseAbi, erc20Abi, getContract, ContractFunctionExecutionError, hexToString, erc20Abi_bytes32, encodeFunctionData, withRetry } from 'viem';
|
5
5
|
import { assign, omit, isEqual, uniq, keyBy, toPairs, keys, fromPairs, values } from 'lodash-es';
|
6
6
|
import z from 'zod/v4';
|
7
7
|
import anylogger from 'anylogger';
|
8
8
|
import { of, Observable, distinctUntilChanged, map, timer, switchMap, from, firstValueFrom, combineLatest, BehaviorSubject, shareReplay, startWith, filter, defer, catchError, EMPTY, tap } from 'rxjs';
|
9
9
|
import BigNumber from 'bignumber.js';
|
10
|
+
import { PublicKey, SystemProgram } from '@solana/web3.js';
|
11
|
+
import { isNotNil, BigMath, isArrayOf, isBigInt, planckToTokens, isAbortError, getSharedObservable, keepAlive, isTruthy } from '@talismn/util';
|
10
12
|
import { parseMetadataRpc, toHex, unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, decodeScale, getStorageKeyPrefix, Twox128, compactMetadata, encodeMetadata, papiParse, papiStringify } from '@talismn/scale';
|
13
|
+
import { deserializeMetadata } from '@metaplex-foundation/mpl-token-metadata';
|
14
|
+
import { sol, publicKey } from '@metaplex-foundation/umi';
|
15
|
+
import { MintLayout, getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createTransferInstruction, TOKEN_PROGRAM_ID, getAccount } from '@solana/spl-token';
|
11
16
|
import { newTokenRates } from '@talismn/token-rates';
|
12
17
|
import { mergeUint8 } from '@polkadot-api/utils';
|
13
18
|
import { Binary, Enum, AccountId } from 'polkadot-api';
|
@@ -19,8 +24,8 @@ import { u8aToHex, u8aConcatStrict, u8aToString, hexToNumber } from '@polkadot/u
|
|
19
24
|
import PQueue from 'p-queue';
|
20
25
|
import { fetchBestMetadata } from '@talismn/sapi';
|
21
26
|
|
22
|
-
const MODULE_TYPE$
|
23
|
-
const PLATFORM$
|
27
|
+
const MODULE_TYPE$a = EvmErc20TokenSchema.shape.type.value;
|
28
|
+
const PLATFORM$a = EvmErc20TokenSchema.shape.platform.value;
|
24
29
|
|
25
30
|
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)"]);
|
26
31
|
|
@@ -616,7 +621,7 @@ const getBalanceDefs = addressesByToken => {
|
|
616
621
|
// if there is at least one storage entry, the results will be an array with a single object
|
617
622
|
// if the storage has no entries in it (ex: Assets on ewx or moonbeam), the response will be an empty array
|
618
623
|
|
619
|
-
const fetchBalances$
|
624
|
+
const fetchBalances$a = async ({
|
620
625
|
networkId,
|
621
626
|
tokensWithAddresses,
|
622
627
|
connector
|
@@ -628,7 +633,7 @@ const fetchBalances$8 = async ({
|
|
628
633
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
629
634
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
630
635
|
for (const [token, addresses] of tokensWithAddresses) {
|
631
|
-
if (token.type !== MODULE_TYPE$
|
636
|
+
if (token.type !== MODULE_TYPE$a || token.networkId !== networkId) throw new Error(`Invalid token type or networkId for EVM ERC20 balance module: ${token.type} on ${token.networkId}`);
|
632
637
|
for (const address of addresses) if (!isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
633
638
|
}
|
634
639
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
@@ -658,7 +663,7 @@ const fetchWithoutAggregator = async (client, balanceDefs) => {
|
|
658
663
|
address,
|
659
664
|
tokenId: token.id,
|
660
665
|
value: result.toString(),
|
661
|
-
source: MODULE_TYPE$
|
666
|
+
source: MODULE_TYPE$a,
|
662
667
|
networkId: parseEvmErc20TokenId(token.id).networkId,
|
663
668
|
status: "live"
|
664
669
|
};
|
@@ -701,7 +706,7 @@ const fetchWithAggregator = async (client, balanceDefs, erc20BalancesAggregatorA
|
|
701
706
|
address: balanceDef.address,
|
702
707
|
tokenId: balanceDef.token.id,
|
703
708
|
value: erc20Balances[index].toString(),
|
704
|
-
source: MODULE_TYPE$
|
709
|
+
source: MODULE_TYPE$a,
|
705
710
|
networkId: parseTokenId(balanceDef.token.id).networkId,
|
706
711
|
status: "live"
|
707
712
|
}));
|
@@ -758,7 +763,7 @@ const getTypedContract$1 = (client, abi, contractAddress) => getContract({
|
|
758
763
|
}
|
759
764
|
});
|
760
765
|
|
761
|
-
const TokenCacheSchema$
|
766
|
+
const TokenCacheSchema$2 = z.discriminatedUnion("isValid", [z.strictObject({
|
762
767
|
id: EvmErc20TokenSchema.shape.id,
|
763
768
|
isValid: z.literal(true),
|
764
769
|
...EvmErc20TokenSchema.pick({
|
@@ -770,7 +775,7 @@ const TokenCacheSchema$1 = z.discriminatedUnion("isValid", [z.strictObject({
|
|
770
775
|
id: EvmErc20TokenSchema.shape.id,
|
771
776
|
isValid: z.literal(false)
|
772
777
|
})]);
|
773
|
-
const fetchTokens$
|
778
|
+
const fetchTokens$a = async ({
|
774
779
|
networkId,
|
775
780
|
tokens,
|
776
781
|
connector,
|
@@ -779,7 +784,7 @@ const fetchTokens$8 = async ({
|
|
779
784
|
const result = [];
|
780
785
|
for (const tokenConfig of tokens) {
|
781
786
|
const tokenId = evmErc20TokenId(networkId, tokenConfig.contractAddress);
|
782
|
-
const cached = cache[tokenId] && TokenCacheSchema$
|
787
|
+
const cached = cache[tokenId] && TokenCacheSchema$2.safeParse(cache[tokenId]).data;
|
783
788
|
if (!cached) {
|
784
789
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
785
790
|
if (!client) {
|
@@ -792,7 +797,7 @@ const fetchTokens$8 = async ({
|
|
792
797
|
decimals,
|
793
798
|
symbol
|
794
799
|
} = await getErc20ContractData$1(client, tokenConfig.contractAddress);
|
795
|
-
cache[tokenId] = TokenCacheSchema$
|
800
|
+
cache[tokenId] = TokenCacheSchema$2.parse({
|
796
801
|
id: tokenId,
|
797
802
|
symbol,
|
798
803
|
decimals,
|
@@ -810,11 +815,11 @@ const fetchTokens$8 = async ({
|
|
810
815
|
}
|
811
816
|
const base = {
|
812
817
|
id: tokenId,
|
813
|
-
type: MODULE_TYPE$
|
814
|
-
platform: PLATFORM$
|
818
|
+
type: MODULE_TYPE$a,
|
819
|
+
platform: PLATFORM$a,
|
815
820
|
networkId
|
816
821
|
};
|
817
|
-
const cached2 = cache[tokenId] && TokenCacheSchema$
|
822
|
+
const cached2 = cache[tokenId] && TokenCacheSchema$2.safeParse(cache[tokenId]).data;
|
818
823
|
if (cached2?.isValid === false) continue;
|
819
824
|
const token = assign(base, cached2?.isValid ? omit(cached2, ["isValid"]) : {}, tokenConfig);
|
820
825
|
const parsed = EvmErc20TokenSchema.safeParse(token);
|
@@ -829,17 +834,17 @@ const fetchTokens$8 = async ({
|
|
829
834
|
return result;
|
830
835
|
};
|
831
836
|
|
832
|
-
const getMiniMetadata$
|
837
|
+
const getMiniMetadata$a = () => {
|
833
838
|
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
834
839
|
};
|
835
840
|
|
836
|
-
const getTransferCallData$
|
841
|
+
const getTransferCallData$a = ({
|
837
842
|
from,
|
838
843
|
to,
|
839
844
|
value,
|
840
845
|
token
|
841
846
|
}) => {
|
842
|
-
if (!isTokenOfType(token, MODULE_TYPE$
|
847
|
+
if (!isTokenOfType(token, MODULE_TYPE$a)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$a}.`);
|
843
848
|
if (!isEthereumAddress(from)) throw new Error("Invalid from address");
|
844
849
|
if (!isEthereumAddress(to)) throw new Error("Invalid to address");
|
845
850
|
const data = encodeFunctionData({
|
@@ -854,8 +859,8 @@ const getTransferCallData$8 = ({
|
|
854
859
|
};
|
855
860
|
};
|
856
861
|
|
857
|
-
const SUBSCRIPTION_INTERVAL$
|
858
|
-
const subscribeBalances$
|
862
|
+
const SUBSCRIPTION_INTERVAL$6 = 6_000;
|
863
|
+
const subscribeBalances$a = ({
|
859
864
|
networkId,
|
860
865
|
tokensWithAddresses,
|
861
866
|
connector
|
@@ -869,17 +874,17 @@ const subscribeBalances$8 = ({
|
|
869
874
|
const poll = async () => {
|
870
875
|
try {
|
871
876
|
if (abortController.signal.aborted) return;
|
872
|
-
const balances = await fetchBalances$
|
877
|
+
const balances = await fetchBalances$a({
|
873
878
|
networkId,
|
874
879
|
tokensWithAddresses: tokensWithAddresses,
|
875
880
|
connector
|
876
881
|
});
|
877
882
|
if (abortController.signal.aborted) return;
|
878
883
|
subscriber.next(balances);
|
879
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
884
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$6);
|
880
885
|
} catch (error) {
|
881
886
|
log.error("Error", {
|
882
|
-
module: MODULE_TYPE$
|
887
|
+
module: MODULE_TYPE$a,
|
883
888
|
networkId,
|
884
889
|
addressesByToken: tokensWithAddresses,
|
885
890
|
error
|
@@ -895,13 +900,13 @@ const subscribeBalances$8 = ({
|
|
895
900
|
};
|
896
901
|
|
897
902
|
const EvmErc20BalanceModule = {
|
898
|
-
type: MODULE_TYPE$
|
899
|
-
platform: PLATFORM$
|
900
|
-
getMiniMetadata: getMiniMetadata$
|
901
|
-
fetchTokens: fetchTokens$
|
902
|
-
fetchBalances: fetchBalances$
|
903
|
-
subscribeBalances: subscribeBalances$
|
904
|
-
getTransferCallData: getTransferCallData$
|
903
|
+
type: MODULE_TYPE$a,
|
904
|
+
platform: PLATFORM$a,
|
905
|
+
getMiniMetadata: getMiniMetadata$a,
|
906
|
+
fetchTokens: fetchTokens$a,
|
907
|
+
fetchBalances: fetchBalances$a,
|
908
|
+
subscribeBalances: subscribeBalances$a,
|
909
|
+
getTransferCallData: getTransferCallData$a
|
905
910
|
};
|
906
911
|
|
907
912
|
const TokenConfigBaseSchema = TokenBaseSchema.partial().omit({
|
@@ -914,10 +919,10 @@ const EvmErc20TokenConfigSchema = z.strictObject({
|
|
914
919
|
...TokenConfigBaseSchema.shape
|
915
920
|
});
|
916
921
|
|
917
|
-
const MODULE_TYPE$
|
918
|
-
const PLATFORM$
|
922
|
+
const MODULE_TYPE$9 = EvmNativeTokenSchema.shape.type.value;
|
923
|
+
const PLATFORM$9 = EvmNativeTokenSchema.shape.platform.value;
|
919
924
|
|
920
|
-
const fetchBalances$
|
925
|
+
const fetchBalances$9 = async ({
|
921
926
|
networkId,
|
922
927
|
tokensWithAddresses,
|
923
928
|
connector
|
@@ -929,7 +934,7 @@ const fetchBalances$7 = async ({
|
|
929
934
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
930
935
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
931
936
|
for (const [token, addresses] of tokensWithAddresses) {
|
932
|
-
if (token.type !== MODULE_TYPE$
|
937
|
+
if (token.type !== MODULE_TYPE$9 || token.networkId !== networkId) throw new Error(`Invalid token type or networkId for EVM ERC20 balance module: ${token.type} on ${token.networkId}`);
|
933
938
|
for (const address of addresses) if (!isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
934
939
|
}
|
935
940
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
@@ -956,7 +961,7 @@ const fetchWithoutMulticall = async (client, balanceDefs) => {
|
|
956
961
|
address,
|
957
962
|
tokenId: token.id,
|
958
963
|
value: result.toString(),
|
959
|
-
source: MODULE_TYPE$
|
964
|
+
source: MODULE_TYPE$9,
|
960
965
|
networkId: parseTokenId(token.id).networkId,
|
961
966
|
status: "live"
|
962
967
|
};
|
@@ -1002,7 +1007,7 @@ const fetchWithMulticall = async (client, balanceDefs, multicall3Address) => {
|
|
1002
1007
|
address: balanceDefs[index].address,
|
1003
1008
|
tokenId: balanceDefs[index].token.id,
|
1004
1009
|
value: result.result.toString(),
|
1005
|
-
source: MODULE_TYPE$
|
1010
|
+
source: MODULE_TYPE$9,
|
1006
1011
|
networkId: parseTokenId(balanceDefs[index].token.id).networkId,
|
1007
1012
|
status: "live"
|
1008
1013
|
});
|
@@ -1032,7 +1037,7 @@ const fetchWithMulticall = async (client, balanceDefs, multicall3Address) => {
|
|
1032
1037
|
}
|
1033
1038
|
};
|
1034
1039
|
|
1035
|
-
const fetchTokens$
|
1040
|
+
const fetchTokens$9 = async ({
|
1036
1041
|
networkId,
|
1037
1042
|
tokens
|
1038
1043
|
}) => {
|
@@ -1040,8 +1045,8 @@ const fetchTokens$7 = async ({
|
|
1040
1045
|
if (tokens.length !== 1) throw new Error("EVM Native module expects the nativeCurrency to be passed as a single token in the array");
|
1041
1046
|
const token = assign({
|
1042
1047
|
id: evmNativeTokenId(networkId),
|
1043
|
-
type: MODULE_TYPE$
|
1044
|
-
platform: PLATFORM$
|
1048
|
+
type: MODULE_TYPE$9,
|
1049
|
+
platform: PLATFORM$9,
|
1045
1050
|
networkId,
|
1046
1051
|
isDefault: true
|
1047
1052
|
}, tokens[0]);
|
@@ -1053,17 +1058,17 @@ const fetchTokens$7 = async ({
|
|
1053
1058
|
return [parsed.data];
|
1054
1059
|
};
|
1055
1060
|
|
1056
|
-
const getMiniMetadata$
|
1061
|
+
const getMiniMetadata$9 = () => {
|
1057
1062
|
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
1058
1063
|
};
|
1059
1064
|
|
1060
|
-
const getTransferCallData$
|
1065
|
+
const getTransferCallData$9 = ({
|
1061
1066
|
from,
|
1062
1067
|
to,
|
1063
1068
|
value,
|
1064
1069
|
token
|
1065
1070
|
}) => {
|
1066
|
-
if (!isTokenOfType(token, MODULE_TYPE$
|
1071
|
+
if (!isTokenOfType(token, MODULE_TYPE$9)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$9}.`);
|
1067
1072
|
if (!isEthereumAddress(from)) throw new Error("Invalid from address");
|
1068
1073
|
if (!isEthereumAddress(to)) throw new Error("Invalid to address");
|
1069
1074
|
return {
|
@@ -1074,8 +1079,8 @@ const getTransferCallData$7 = ({
|
|
1074
1079
|
};
|
1075
1080
|
};
|
1076
1081
|
|
1077
|
-
const SUBSCRIPTION_INTERVAL$
|
1078
|
-
const subscribeBalances$
|
1082
|
+
const SUBSCRIPTION_INTERVAL$5 = 6_000;
|
1083
|
+
const subscribeBalances$9 = ({
|
1079
1084
|
networkId,
|
1080
1085
|
tokensWithAddresses,
|
1081
1086
|
connector
|
@@ -1089,17 +1094,17 @@ const subscribeBalances$7 = ({
|
|
1089
1094
|
const poll = async () => {
|
1090
1095
|
try {
|
1091
1096
|
if (abortController.signal.aborted) return;
|
1092
|
-
const balances = await fetchBalances$
|
1097
|
+
const balances = await fetchBalances$9({
|
1093
1098
|
networkId,
|
1094
1099
|
tokensWithAddresses: tokensWithAddresses,
|
1095
1100
|
connector
|
1096
1101
|
});
|
1097
1102
|
if (abortController.signal.aborted) return;
|
1098
1103
|
subscriber.next(balances);
|
1099
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
1104
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$5);
|
1100
1105
|
} catch (error) {
|
1101
1106
|
log.error("Error", {
|
1102
|
-
module: MODULE_TYPE$
|
1107
|
+
module: MODULE_TYPE$9,
|
1103
1108
|
networkId,
|
1104
1109
|
addressesByToken: tokensWithAddresses,
|
1105
1110
|
error
|
@@ -1115,13 +1120,13 @@ const subscribeBalances$7 = ({
|
|
1115
1120
|
};
|
1116
1121
|
|
1117
1122
|
const EvmNativeBalanceModule = {
|
1118
|
-
type: MODULE_TYPE$
|
1119
|
-
platform: PLATFORM$
|
1120
|
-
getMiniMetadata: getMiniMetadata$
|
1121
|
-
fetchTokens: fetchTokens$
|
1122
|
-
fetchBalances: fetchBalances$
|
1123
|
-
subscribeBalances: subscribeBalances$
|
1124
|
-
getTransferCallData: getTransferCallData$
|
1123
|
+
type: MODULE_TYPE$9,
|
1124
|
+
platform: PLATFORM$9,
|
1125
|
+
getMiniMetadata: getMiniMetadata$9,
|
1126
|
+
fetchTokens: fetchTokens$9,
|
1127
|
+
fetchBalances: fetchBalances$9,
|
1128
|
+
subscribeBalances: subscribeBalances$9,
|
1129
|
+
getTransferCallData: getTransferCallData$9
|
1125
1130
|
};
|
1126
1131
|
|
1127
1132
|
// to be used by chaindata too
|
@@ -1129,10 +1134,10 @@ const EvmNativeTokenConfigSchema = z.strictObject({
|
|
1129
1134
|
...TokenConfigBaseSchema.shape
|
1130
1135
|
});
|
1131
1136
|
|
1132
|
-
const MODULE_TYPE$
|
1133
|
-
const PLATFORM$
|
1137
|
+
const MODULE_TYPE$8 = EvmUniswapV2TokenSchema.shape.type.value;
|
1138
|
+
const PLATFORM$8 = EvmUniswapV2TokenSchema.shape.platform.value;
|
1134
1139
|
|
1135
|
-
const fetchBalances$
|
1140
|
+
const fetchBalances$8 = async ({
|
1136
1141
|
networkId,
|
1137
1142
|
tokensWithAddresses,
|
1138
1143
|
connector
|
@@ -1144,7 +1149,7 @@ const fetchBalances$6 = async ({
|
|
1144
1149
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1145
1150
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
1146
1151
|
for (const [token, addresses] of tokensWithAddresses) {
|
1147
|
-
if (token.type !== MODULE_TYPE$
|
1152
|
+
if (token.type !== MODULE_TYPE$8 || token.networkId !== networkId) throw new Error(`Invalid token type or networkId for EVM ERC20 balance module: ${token.type} on ${token.networkId}`);
|
1148
1153
|
for (const address of addresses) if (!isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
1149
1154
|
}
|
1150
1155
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
@@ -1231,7 +1236,7 @@ const fetchPoolBalances = async (client, balanceDefs) => {
|
|
1231
1236
|
acc.success.push({
|
1232
1237
|
address,
|
1233
1238
|
tokenId: token.id,
|
1234
|
-
source: MODULE_TYPE$
|
1239
|
+
source: MODULE_TYPE$8,
|
1235
1240
|
networkId: parseTokenId(token.id).networkId,
|
1236
1241
|
status: "live",
|
1237
1242
|
values: [{
|
@@ -1291,7 +1296,7 @@ const getUniswapV2PairContractData = async (client, contractAddress) => {
|
|
1291
1296
|
};
|
1292
1297
|
};
|
1293
1298
|
|
1294
|
-
const TokenCacheSchema = z.discriminatedUnion("isValid", [z.strictObject({
|
1299
|
+
const TokenCacheSchema$1 = z.discriminatedUnion("isValid", [z.strictObject({
|
1295
1300
|
id: EvmUniswapV2TokenSchema.shape.id,
|
1296
1301
|
isValid: z.literal(true),
|
1297
1302
|
...EvmUniswapV2TokenSchema.pick({
|
@@ -1309,7 +1314,7 @@ const TokenCacheSchema = z.discriminatedUnion("isValid", [z.strictObject({
|
|
1309
1314
|
id: EvmUniswapV2TokenSchema.shape.id,
|
1310
1315
|
isValid: z.literal(false)
|
1311
1316
|
})]);
|
1312
|
-
const fetchTokens$
|
1317
|
+
const fetchTokens$8 = async ({
|
1313
1318
|
networkId,
|
1314
1319
|
tokens,
|
1315
1320
|
connector,
|
@@ -1318,7 +1323,7 @@ const fetchTokens$6 = async ({
|
|
1318
1323
|
const result = [];
|
1319
1324
|
for (const tokenConfig of tokens) {
|
1320
1325
|
const tokenId = evmUniswapV2TokenId(networkId, tokenConfig.contractAddress);
|
1321
|
-
const cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1326
|
+
const cached = cache[tokenId] && TokenCacheSchema$1.safeParse(cache[tokenId]).data;
|
1322
1327
|
if (!cached) {
|
1323
1328
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1324
1329
|
if (!client) {
|
@@ -1340,7 +1345,7 @@ const fetchTokens$6 = async ({
|
|
1340
1345
|
symbol: symbol1,
|
1341
1346
|
decimals: decimals1
|
1342
1347
|
} = await getErc20ContractData(client, token1);
|
1343
|
-
cache[tokenId] = TokenCacheSchema.parse({
|
1348
|
+
cache[tokenId] = TokenCacheSchema$1.parse({
|
1344
1349
|
id: tokenId,
|
1345
1350
|
symbol: `${symbol0}/${symbol1}`,
|
1346
1351
|
decimals,
|
@@ -1366,48 +1371,601 @@ const fetchTokens$6 = async ({
|
|
1366
1371
|
continue;
|
1367
1372
|
}
|
1368
1373
|
}
|
1374
|
+
const base = {
|
1375
|
+
id: tokenId,
|
1376
|
+
type: MODULE_TYPE$8,
|
1377
|
+
platform: PLATFORM$8,
|
1378
|
+
networkId
|
1379
|
+
};
|
1380
|
+
const cached2 = cache[tokenId] && TokenCacheSchema$1.safeParse(cache[tokenId]).data;
|
1381
|
+
if (cached2?.isValid === false) continue;
|
1382
|
+
const token = assign(base, cached2?.isValid ? omit(cached2, ["isValid"]) : {}, tokenConfig);
|
1383
|
+
const parsed = EvmUniswapV2TokenSchema.safeParse(token);
|
1384
|
+
if (!parsed.success) {
|
1385
|
+
log.warn("Ignoring token with invalid schema", token);
|
1386
|
+
continue;
|
1387
|
+
}
|
1388
|
+
result.push(parsed.data);
|
1389
|
+
}
|
1390
|
+
return result;
|
1391
|
+
};
|
1392
|
+
|
1393
|
+
const getMiniMetadata$8 = () => {
|
1394
|
+
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
1395
|
+
};
|
1396
|
+
|
1397
|
+
const getTransferCallData$8 = ({
|
1398
|
+
from,
|
1399
|
+
to,
|
1400
|
+
value,
|
1401
|
+
token
|
1402
|
+
}) => {
|
1403
|
+
if (!isTokenOfType(token, MODULE_TYPE$8)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$8}.`);
|
1404
|
+
if (!isEthereumAddress(from)) throw new Error("Invalid from address");
|
1405
|
+
if (!isEthereumAddress(to)) throw new Error("Invalid to address");
|
1406
|
+
const data = encodeFunctionData({
|
1407
|
+
abi: erc20Abi,
|
1408
|
+
functionName: "transfer",
|
1409
|
+
args: [to, BigInt(value)]
|
1410
|
+
});
|
1411
|
+
return {
|
1412
|
+
from,
|
1413
|
+
to: token.contractAddress,
|
1414
|
+
data
|
1415
|
+
};
|
1416
|
+
};
|
1417
|
+
|
1418
|
+
const SUBSCRIPTION_INTERVAL$4 = 6_000;
|
1419
|
+
const subscribeBalances$8 = ({
|
1420
|
+
networkId,
|
1421
|
+
tokensWithAddresses,
|
1422
|
+
connector
|
1423
|
+
}) => {
|
1424
|
+
if (!tokensWithAddresses.length) return of({
|
1425
|
+
success: [],
|
1426
|
+
errors: []
|
1427
|
+
});
|
1428
|
+
return new Observable(subscriber => {
|
1429
|
+
const abortController = new AbortController();
|
1430
|
+
const poll = async () => {
|
1431
|
+
try {
|
1432
|
+
if (abortController.signal.aborted) return;
|
1433
|
+
const balances = await fetchBalances$8({
|
1434
|
+
networkId,
|
1435
|
+
tokensWithAddresses: tokensWithAddresses,
|
1436
|
+
connector
|
1437
|
+
});
|
1438
|
+
if (abortController.signal.aborted) return;
|
1439
|
+
subscriber.next(balances);
|
1440
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
1441
|
+
} catch (error) {
|
1442
|
+
log.error("Error", {
|
1443
|
+
module: MODULE_TYPE$8,
|
1444
|
+
networkId,
|
1445
|
+
addressesByToken: tokensWithAddresses,
|
1446
|
+
error
|
1447
|
+
});
|
1448
|
+
subscriber.error(error);
|
1449
|
+
}
|
1450
|
+
};
|
1451
|
+
poll();
|
1452
|
+
return () => {
|
1453
|
+
abortController.abort();
|
1454
|
+
};
|
1455
|
+
}).pipe(distinctUntilChanged(isEqual));
|
1456
|
+
};
|
1457
|
+
|
1458
|
+
const EvmUniswapV2BalanceModule = {
|
1459
|
+
type: MODULE_TYPE$8,
|
1460
|
+
platform: PLATFORM$8,
|
1461
|
+
getMiniMetadata: getMiniMetadata$8,
|
1462
|
+
fetchTokens: fetchTokens$8,
|
1463
|
+
fetchBalances: fetchBalances$8,
|
1464
|
+
subscribeBalances: subscribeBalances$8,
|
1465
|
+
getTransferCallData: getTransferCallData$8
|
1466
|
+
};
|
1467
|
+
|
1468
|
+
// to be used by chaindata too
|
1469
|
+
const EvmUniswapV2TokenConfigSchema = z.strictObject({
|
1470
|
+
contractAddress: EvmUniswapV2TokenSchema.shape.contractAddress,
|
1471
|
+
...TokenConfigBaseSchema.shape
|
1472
|
+
});
|
1473
|
+
|
1474
|
+
const MODULE_TYPE$7 = SolNativeTokenSchema.shape.type.value;
|
1475
|
+
const PLATFORM$7 = SolNativeTokenSchema.shape.platform.value;
|
1476
|
+
|
1477
|
+
const fetchBalances$7 = async ({
|
1478
|
+
networkId,
|
1479
|
+
tokensWithAddresses,
|
1480
|
+
connector
|
1481
|
+
}) => {
|
1482
|
+
if (!tokensWithAddresses.length) return {
|
1483
|
+
success: [],
|
1484
|
+
errors: []
|
1485
|
+
};
|
1486
|
+
const connection = await connector.getConnection(networkId);
|
1487
|
+
if (!connection) throw new Error(`Could not get rpc provider for sol network ${networkId}`);
|
1488
|
+
for (const [token, addresses] of tokensWithAddresses) {
|
1489
|
+
if (token.type !== MODULE_TYPE$7 || token.networkId !== networkId) throw new Error(`Invalid token type or networkId for balance module: ${token.type} on ${token.networkId}`);
|
1490
|
+
for (const address of addresses) if (!isSolanaAddress(address)) throw new Error(`Invalid solana address for balance module: ${address} for token ${token.id}`);
|
1491
|
+
}
|
1492
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
1493
|
+
const results = await Promise.allSettled(balanceDefs.map(async ({
|
1494
|
+
token,
|
1495
|
+
address
|
1496
|
+
}) => {
|
1497
|
+
try {
|
1498
|
+
const publicKey = new PublicKey(address);
|
1499
|
+
const lamports = await connection.getBalance(publicKey);
|
1500
|
+
return {
|
1501
|
+
address: address,
|
1502
|
+
tokenId: token.id,
|
1503
|
+
value: lamports.toString(),
|
1504
|
+
source: MODULE_TYPE$7,
|
1505
|
+
networkId: token.networkId,
|
1506
|
+
status: "live"
|
1507
|
+
};
|
1508
|
+
} catch (err) {
|
1509
|
+
throw new BalanceFetchError(`Failed to get balance for token ${token.id} and address ${address} on chain ${networkId}`, token.id, address, err);
|
1510
|
+
}
|
1511
|
+
}));
|
1512
|
+
return results.reduce((acc, result) => {
|
1513
|
+
if (result.status === "fulfilled") acc.success.push(result.value);else {
|
1514
|
+
const error = result.reason;
|
1515
|
+
acc.errors.push({
|
1516
|
+
tokenId: error.tokenId,
|
1517
|
+
address: error.address,
|
1518
|
+
error
|
1519
|
+
});
|
1520
|
+
}
|
1521
|
+
return acc;
|
1522
|
+
}, {
|
1523
|
+
success: [],
|
1524
|
+
errors: []
|
1525
|
+
});
|
1526
|
+
};
|
1527
|
+
|
1528
|
+
const fetchTokens$7 = async ({
|
1529
|
+
networkId,
|
1530
|
+
tokens
|
1531
|
+
}) => {
|
1532
|
+
// assume there is one and only one token in the array
|
1533
|
+
if (tokens.length !== 1) throw new Error("EVM Native module expects the nativeCurrency to be passed as a single token in the array");
|
1534
|
+
const token = assign({
|
1535
|
+
id: solNativeTokenId(networkId),
|
1536
|
+
type: MODULE_TYPE$7,
|
1537
|
+
platform: PLATFORM$7,
|
1538
|
+
networkId,
|
1539
|
+
isDefault: true
|
1540
|
+
}, tokens[0]);
|
1541
|
+
const parsed = SolNativeTokenSchema.safeParse(token);
|
1542
|
+
if (!parsed.success) {
|
1543
|
+
log.warn("Ignoring token with invalid schema", token);
|
1544
|
+
return [];
|
1545
|
+
}
|
1546
|
+
return [parsed.data];
|
1547
|
+
};
|
1548
|
+
|
1549
|
+
const getMiniMetadata$7 = () => {
|
1550
|
+
throw new Error("MiniMetadata is not supported for sol-native tokens");
|
1551
|
+
};
|
1552
|
+
|
1553
|
+
const getTransferCallData$7 = ({
|
1554
|
+
from,
|
1555
|
+
to,
|
1556
|
+
value,
|
1557
|
+
token
|
1558
|
+
}) => {
|
1559
|
+
if (!isTokenOfType(token, MODULE_TYPE$7)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$7}.`);
|
1560
|
+
const fromPubkey = new PublicKey(from);
|
1561
|
+
const transferIx = SystemProgram.transfer({
|
1562
|
+
fromPubkey,
|
1563
|
+
toPubkey: new PublicKey(to),
|
1564
|
+
lamports: Number(value)
|
1565
|
+
});
|
1566
|
+
return [transferIx];
|
1567
|
+
};
|
1568
|
+
|
1569
|
+
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
1570
|
+
const subscribeBalances$7 = ({
|
1571
|
+
networkId,
|
1572
|
+
tokensWithAddresses,
|
1573
|
+
connector
|
1574
|
+
}) => {
|
1575
|
+
if (!tokensWithAddresses.length) return of({
|
1576
|
+
success: [],
|
1577
|
+
errors: []
|
1578
|
+
});
|
1579
|
+
return new Observable(subscriber => {
|
1580
|
+
const abortController = new AbortController();
|
1581
|
+
const poll = async () => {
|
1582
|
+
try {
|
1583
|
+
if (abortController.signal.aborted) return;
|
1584
|
+
const balances = await fetchBalances$7({
|
1585
|
+
networkId,
|
1586
|
+
tokensWithAddresses,
|
1587
|
+
connector
|
1588
|
+
});
|
1589
|
+
if (abortController.signal.aborted) return;
|
1590
|
+
subscriber.next(balances);
|
1591
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
1592
|
+
} catch (error) {
|
1593
|
+
log.error("Error", {
|
1594
|
+
module: MODULE_TYPE$7,
|
1595
|
+
networkId,
|
1596
|
+
tokensWithAddresses,
|
1597
|
+
error
|
1598
|
+
});
|
1599
|
+
subscriber.error(error);
|
1600
|
+
}
|
1601
|
+
};
|
1602
|
+
poll();
|
1603
|
+
return () => {
|
1604
|
+
abortController.abort();
|
1605
|
+
};
|
1606
|
+
}).pipe(distinctUntilChanged(isEqual));
|
1607
|
+
};
|
1608
|
+
|
1609
|
+
const SolNativeBalanceModule = {
|
1610
|
+
type: MODULE_TYPE$7,
|
1611
|
+
platform: PLATFORM$7,
|
1612
|
+
getMiniMetadata: getMiniMetadata$7,
|
1613
|
+
fetchTokens: fetchTokens$7,
|
1614
|
+
fetchBalances: fetchBalances$7,
|
1615
|
+
subscribeBalances: subscribeBalances$7,
|
1616
|
+
getTransferCallData: getTransferCallData$7
|
1617
|
+
};
|
1618
|
+
|
1619
|
+
// to be used by chaindata too
|
1620
|
+
const SolNativeTokenConfigSchema = z.strictObject({
|
1621
|
+
...TokenConfigBaseSchema.shape
|
1622
|
+
});
|
1623
|
+
|
1624
|
+
const MODULE_TYPE$6 = SolSplTokenSchema.shape.type.value;
|
1625
|
+
const PLATFORM$6 = SolSplTokenSchema.shape.platform.value;
|
1626
|
+
|
1627
|
+
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
1628
|
+
const {
|
1629
|
+
builder
|
1630
|
+
} = parseMetadataRpc(metadataRpc);
|
1631
|
+
const call = builder.buildRuntimeCall(apiName, method);
|
1632
|
+
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex(call.args.enc(args))]);
|
1633
|
+
return call.value.dec(hex);
|
1634
|
+
};
|
1635
|
+
|
1636
|
+
const hasStorageItem = (metadata, palletName, itemName) => {
|
1637
|
+
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1638
|
+
if (!pallet || !pallet.storage) return false;
|
1639
|
+
return pallet.storage.items.some(item => item.name === itemName);
|
1640
|
+
};
|
1641
|
+
const hasStorageItems = (metadata, palletName, itemNames) => {
|
1642
|
+
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1643
|
+
if (!pallet || !pallet.storage) return false;
|
1644
|
+
return itemNames.every(itemName => pallet.storage?.items.some(item => item.name === itemName));
|
1645
|
+
};
|
1646
|
+
const hasRuntimeApi = (metadata, apiName, method) => {
|
1647
|
+
const api = metadata.apis.find(api => api.name === apiName);
|
1648
|
+
if (!api || !api.methods) return false;
|
1649
|
+
return api.methods.some(m => m.name === method);
|
1650
|
+
};
|
1651
|
+
const getConstantValue = (metadataRpc, pallet, constant) => {
|
1652
|
+
const {
|
1653
|
+
unifiedMetadata,
|
1654
|
+
builder
|
1655
|
+
} = parseMetadataRpc(metadataRpc);
|
1656
|
+
const codec = builder.buildConstant(pallet, constant);
|
1657
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
1658
|
+
name
|
1659
|
+
}) => name === pallet)?.constants.find(({
|
1660
|
+
name
|
1661
|
+
}) => name === constant)?.value;
|
1662
|
+
if (!encodedValue) throw new Error(`Constant ${pallet}.${constant} not found`);
|
1663
|
+
return codec.dec(encodedValue);
|
1664
|
+
};
|
1665
|
+
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
1666
|
+
const {
|
1667
|
+
unifiedMetadata,
|
1668
|
+
builder
|
1669
|
+
} = parseMetadataRpc(metadataRpc);
|
1670
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
1671
|
+
name
|
1672
|
+
}) => name === pallet)?.constants.find(({
|
1673
|
+
name
|
1674
|
+
}) => name === constant)?.value;
|
1675
|
+
if (!encodedValue) return null;
|
1676
|
+
const codec = builder.buildConstant(pallet, constant);
|
1677
|
+
return codec.dec(encodedValue);
|
1678
|
+
};
|
1679
|
+
|
1680
|
+
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
1681
|
+
const allStateKeys = queries.flatMap(({
|
1682
|
+
stateKeys
|
1683
|
+
}) => stateKeys).filter(isNotNil);
|
1684
|
+
|
1685
|
+
// doing a query without keys would throw an error => return early
|
1686
|
+
if (!allStateKeys.length) return queries.map(({
|
1687
|
+
stateKeys,
|
1688
|
+
decodeResult
|
1689
|
+
}) => decodeResult(stateKeys.map(() => null)));
|
1690
|
+
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
1691
|
+
return decodeRpcQueryPack(queries, result);
|
1692
|
+
};
|
1693
|
+
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
1694
|
+
const allStateKeys = queries.flatMap(({
|
1695
|
+
stateKeys
|
1696
|
+
}) => stateKeys).filter(isNotNil);
|
1697
|
+
|
1698
|
+
// doing a query without keys would throw an error => return early
|
1699
|
+
if (!allStateKeys.length) return of(queries.map(({
|
1700
|
+
stateKeys,
|
1701
|
+
decodeResult
|
1702
|
+
}) => decodeResult(stateKeys.map(() => null))));
|
1703
|
+
return new Observable(subscriber => {
|
1704
|
+
// first subscription callback includes results for all state keys, but further callbacks will only include the ones that changed
|
1705
|
+
// => we need to keep all results in memory and update them after each callback, so we can emit the full result set each time
|
1706
|
+
const changesCache = {};
|
1707
|
+
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
1708
|
+
if (error) subscriber.error(error);else if (result) {
|
1709
|
+
// update the cache
|
1710
|
+
for (const [stateKey, encodedResult] of result.changes) changesCache[stateKey] = encodedResult;
|
1711
|
+
|
1712
|
+
// regenerate the full changes array
|
1713
|
+
const changes = toPairs(changesCache);
|
1714
|
+
|
1715
|
+
// decode and emit results for all queries
|
1716
|
+
subscriber.next(decodeRpcQueryPack(queries, {
|
1717
|
+
block: result.block,
|
1718
|
+
changes
|
1719
|
+
}));
|
1720
|
+
}
|
1721
|
+
}, timeout);
|
1722
|
+
return () => {
|
1723
|
+
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
1724
|
+
};
|
1725
|
+
});
|
1726
|
+
};
|
1727
|
+
const decodeRpcQueryPack = (queries, result) => {
|
1728
|
+
return queries.reduce((acc, {
|
1729
|
+
stateKeys,
|
1730
|
+
decodeResult
|
1731
|
+
}) => {
|
1732
|
+
const changes = stateKeys.map(stateKey => {
|
1733
|
+
if (!stateKey || !result) return null;
|
1734
|
+
const change = result.changes.find(([key]) => key === stateKey);
|
1735
|
+
if (!change) return null;
|
1736
|
+
return change[1];
|
1737
|
+
});
|
1738
|
+
acc.push(decodeResult(changes));
|
1739
|
+
return acc;
|
1740
|
+
}, []);
|
1741
|
+
};
|
1742
|
+
|
1743
|
+
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
1744
|
+
if (!miniMetadata.data) return null;
|
1745
|
+
const metadata = unifyMetadata(decAnyMetadata(miniMetadata.data));
|
1746
|
+
try {
|
1747
|
+
const scaleBuilder = getDynamicBuilder(getLookupFn(metadata));
|
1748
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
1749
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
1750
|
+
chainId
|
1751
|
+
}) : moduleMethodOrFn;
|
1752
|
+
try {
|
1753
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
1754
|
+
} catch (cause) {
|
1755
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
1756
|
+
return [];
|
1757
|
+
}
|
1758
|
+
}));
|
1759
|
+
return builtCoders;
|
1760
|
+
} catch (cause) {
|
1761
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
1762
|
+
}
|
1763
|
+
return null;
|
1764
|
+
};
|
1765
|
+
|
1766
|
+
const SPL_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
1767
|
+
const fetchBalances$6 = async ({
|
1768
|
+
networkId,
|
1769
|
+
tokensWithAddresses,
|
1770
|
+
connector
|
1771
|
+
}) => {
|
1772
|
+
if (!tokensWithAddresses.length) return {
|
1773
|
+
success: [],
|
1774
|
+
errors: []
|
1775
|
+
};
|
1776
|
+
const connection = await connector.getConnection(networkId);
|
1777
|
+
if (!connection) throw new Error(`Could not get connection for Solana network ${networkId}`);
|
1778
|
+
const accountAddresses = uniq(tokensWithAddresses.flatMap(([, addresses]) => addresses));
|
1779
|
+
const balancesPerAddress = await Promise.all(accountAddresses.map(async address => {
|
1780
|
+
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(new PublicKey(address), {
|
1781
|
+
programId: new PublicKey(SPL_PROGRAM_ID) // SPL Token Program ID
|
1782
|
+
});
|
1783
|
+
const balances = tokenAccounts.value.map(d => {
|
1784
|
+
try {
|
1785
|
+
const mintAddress = d.account.data.parsed.info.mint;
|
1786
|
+
const value = d.account.data.parsed.info.tokenAmount.amount ?? "0";
|
1787
|
+
return {
|
1788
|
+
tokenId: solSplTokenId(networkId, mintAddress),
|
1789
|
+
networkId,
|
1790
|
+
address,
|
1791
|
+
source: MODULE_TYPE$6,
|
1792
|
+
status: "live",
|
1793
|
+
value
|
1794
|
+
};
|
1795
|
+
} catch (err) {
|
1796
|
+
log.warn("Failed to parse token amount", {
|
1797
|
+
address,
|
1798
|
+
d
|
1799
|
+
});
|
1800
|
+
return null;
|
1801
|
+
}
|
1802
|
+
}).filter(isNotNil);
|
1803
|
+
return [address, balances];
|
1804
|
+
}));
|
1805
|
+
const allBalancesByKey = keyBy(balancesPerAddress.flatMap(([, addressBalances]) => addressBalances), b => getBalanceKey(b.tokenId, b.address));
|
1806
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
1807
|
+
|
1808
|
+
// return a balance entry for all token/address pairs that were requested
|
1809
|
+
const success = balanceDefs.map(bd => {
|
1810
|
+
const found = allBalancesByKey[getBalanceKey(bd.token.id, bd.address)];
|
1811
|
+
return found ?? {
|
1812
|
+
tokenId: bd.token.id,
|
1813
|
+
networkId: bd.token.networkId,
|
1814
|
+
address: bd.address,
|
1815
|
+
source: MODULE_TYPE$6,
|
1816
|
+
status: "live",
|
1817
|
+
value: "0"
|
1818
|
+
};
|
1819
|
+
});
|
1820
|
+
|
1821
|
+
// return only the balances that match the tokens we are interested in
|
1822
|
+
return {
|
1823
|
+
success,
|
1824
|
+
errors: []
|
1825
|
+
}; // TODO output errors if any
|
1826
|
+
};
|
1827
|
+
const getBalanceKey = (tokenId, address) => `${tokenId}:${address}`;
|
1828
|
+
|
1829
|
+
const TokenCacheSchema = z.discriminatedUnion("isValid", [z.strictObject({
|
1830
|
+
id: SolSplTokenSchema.shape.id,
|
1831
|
+
isValid: z.literal(true),
|
1832
|
+
...SolSplTokenSchema.pick({
|
1833
|
+
symbol: true,
|
1834
|
+
decimals: true,
|
1835
|
+
name: true,
|
1836
|
+
logo: true
|
1837
|
+
}).shape
|
1838
|
+
}), z.strictObject({
|
1839
|
+
id: SolSplTokenSchema.shape.id,
|
1840
|
+
isValid: z.literal(false)
|
1841
|
+
})]);
|
1842
|
+
const METAPLEX_PROGRAM_ID = new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
|
1843
|
+
const fetchTokens$6 = async ({
|
1844
|
+
networkId,
|
1845
|
+
tokens,
|
1846
|
+
connector,
|
1847
|
+
cache
|
1848
|
+
}) => {
|
1849
|
+
const result = [];
|
1850
|
+
for (const tokenConfig of tokens) {
|
1851
|
+
const tokenId = solSplTokenId(networkId, tokenConfig.mintAddress);
|
1852
|
+
let cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1853
|
+
if (!cached) {
|
1854
|
+
const tokenInfo = await fetchOnChainTokenData(connector, tokenId);
|
1855
|
+
if (tokenInfo) cache[tokenId] = tokenInfo;
|
1856
|
+
}
|
1857
|
+
cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1858
|
+
if (cached?.isValid === false) continue;
|
1369
1859
|
const base = {
|
1370
1860
|
id: tokenId,
|
1371
1861
|
type: MODULE_TYPE$6,
|
1372
1862
|
platform: PLATFORM$6,
|
1373
1863
|
networkId
|
1374
1864
|
};
|
1375
|
-
const
|
1376
|
-
|
1377
|
-
const token = assign(base, cached2?.isValid ? omit(cached2, ["isValid"]) : {}, tokenConfig);
|
1378
|
-
const parsed = EvmUniswapV2TokenSchema.safeParse(token);
|
1865
|
+
const token = assign(base, cached?.isValid ? omit(cached, ["isValid"]) : {}, tokenConfig);
|
1866
|
+
const parsed = SolSplTokenSchema.safeParse(token);
|
1379
1867
|
if (!parsed.success) {
|
1380
|
-
log.warn("Ignoring token with invalid
|
1868
|
+
log.warn("Ignoring token with invalid SolSplTokenSchema", {
|
1869
|
+
token
|
1870
|
+
});
|
1381
1871
|
continue;
|
1382
1872
|
}
|
1383
1873
|
result.push(parsed.data);
|
1384
1874
|
}
|
1385
1875
|
return result;
|
1386
1876
|
};
|
1877
|
+
const ERROR_NO_MINT = "No mint info available";
|
1878
|
+
const ERROR_NO_METADATA = "No metadata account found";
|
1879
|
+
const ERROR_INVALID_DATA = "Invalid on-chain data";
|
1880
|
+
const fetchOnChainTokenData = async (connector, tokenId) => {
|
1881
|
+
try {
|
1882
|
+
const {
|
1883
|
+
networkId,
|
1884
|
+
mintAddress
|
1885
|
+
} = parseSolSplTokenId(tokenId);
|
1886
|
+
const connection = await connector.getConnection(networkId);
|
1887
|
+
if (!connection) {
|
1888
|
+
log.warn(`No connection found for network ${networkId}`);
|
1889
|
+
return null;
|
1890
|
+
}
|
1891
|
+
const mintPubKey = new PublicKey(mintAddress);
|
1892
|
+
const mintInfo = await connection.getAccountInfo(mintPubKey);
|
1893
|
+
if (!mintInfo?.data) throw new Error(ERROR_NO_MINT);
|
1894
|
+
const mint = MintLayout.decode(mintInfo.data);
|
1895
|
+
const [metadataPDA] = PublicKey.findProgramAddressSync([Buffer.from("metadata"), METAPLEX_PROGRAM_ID.toBuffer(), mintPubKey.toBuffer()], METAPLEX_PROGRAM_ID);
|
1896
|
+
|
1897
|
+
// 3. Fetch metadata account directly (traditional way)
|
1898
|
+
const metadataAccount = await connection.getAccountInfo(new PublicKey(metadataPDA));
|
1899
|
+
if (!metadataAccount) throw new Error(ERROR_NO_METADATA);
|
1900
|
+
const metadata = deserializeMetadata({
|
1901
|
+
publicKey: publicKey(metadataPDA),
|
1902
|
+
executable: metadataAccount.executable,
|
1903
|
+
owner: publicKey(metadataAccount.owner),
|
1904
|
+
lamports: sol(metadataAccount.lamports),
|
1905
|
+
data: metadataAccount.data
|
1906
|
+
});
|
1907
|
+
const parsed = TokenCacheSchema.safeParse({
|
1908
|
+
id: tokenId,
|
1909
|
+
symbol: metadata.symbol.trim(),
|
1910
|
+
name: metadata.name.trim(),
|
1911
|
+
decimals: mint.decimals,
|
1912
|
+
isValid: true
|
1913
|
+
});
|
1914
|
+
if (!parsed.success) throw new Error(ERROR_INVALID_DATA);
|
1915
|
+
return parsed.data;
|
1916
|
+
} catch (err) {
|
1917
|
+
const msg = err.message;
|
1918
|
+
if ([ERROR_NO_MINT, ERROR_NO_METADATA, ERROR_INVALID_DATA].includes(msg)) return TokenCacheSchema.parse({
|
1919
|
+
id: tokenId,
|
1920
|
+
isValid: false
|
1921
|
+
});
|
1922
|
+
log.warn("Failed to fetch sol-spl token data for %s", tokenId, {
|
1923
|
+
err
|
1924
|
+
});
|
1925
|
+
}
|
1926
|
+
return null;
|
1927
|
+
};
|
1387
1928
|
|
1388
1929
|
const getMiniMetadata$6 = () => {
|
1389
|
-
throw new Error("MiniMetadata is not supported for
|
1930
|
+
throw new Error("MiniMetadata is not supported for solana tokens");
|
1390
1931
|
};
|
1391
1932
|
|
1392
|
-
const getTransferCallData$6 = ({
|
1933
|
+
const getTransferCallData$6 = async ({
|
1393
1934
|
from,
|
1394
1935
|
to,
|
1395
1936
|
value,
|
1396
|
-
token
|
1937
|
+
token,
|
1938
|
+
connector
|
1397
1939
|
}) => {
|
1398
1940
|
if (!isTokenOfType(token, MODULE_TYPE$6)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$6}.`);
|
1399
|
-
|
1400
|
-
|
1401
|
-
const
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1941
|
+
const connection = await connector.getConnection(token.networkId);
|
1942
|
+
const instructions = [];
|
1943
|
+
const mint = new PublicKey(token.mintAddress);
|
1944
|
+
const fromWallet = new PublicKey(from);
|
1945
|
+
const toWallet = new PublicKey(to);
|
1946
|
+
|
1947
|
+
// Get associated token accounts
|
1948
|
+
const fromTokenAccount = await getAssociatedTokenAddress(mint, fromWallet);
|
1949
|
+
const toTokenAccount = await getAssociatedTokenAddress(mint, toWallet);
|
1950
|
+
|
1951
|
+
// Create the target token account if it doesn't exist
|
1952
|
+
if (!(await tokenAccountExists(connection, toTokenAccount))) {
|
1953
|
+
instructions.push(createAssociatedTokenAccountInstruction(fromWallet,
|
1954
|
+
// funder
|
1955
|
+
toTokenAccount, toWallet, mint));
|
1956
|
+
}
|
1957
|
+
|
1958
|
+
// Transfer the tokens
|
1959
|
+
instructions.push(createTransferInstruction(fromTokenAccount, toTokenAccount, fromWallet, BigInt(value), [], TOKEN_PROGRAM_ID));
|
1960
|
+
return instructions;
|
1961
|
+
};
|
1962
|
+
const tokenAccountExists = async (connection, address) => {
|
1963
|
+
try {
|
1964
|
+
await getAccount(connection, address);
|
1965
|
+
return true;
|
1966
|
+
} catch {
|
1967
|
+
return false;
|
1968
|
+
}
|
1411
1969
|
};
|
1412
1970
|
|
1413
1971
|
const SUBSCRIPTION_INTERVAL$2 = 6_000;
|
@@ -1450,7 +2008,7 @@ const subscribeBalances$6 = ({
|
|
1450
2008
|
}).pipe(distinctUntilChanged(isEqual));
|
1451
2009
|
};
|
1452
2010
|
|
1453
|
-
const
|
2011
|
+
const SolSplBalanceModule = {
|
1454
2012
|
type: MODULE_TYPE$6,
|
1455
2013
|
platform: PLATFORM$6,
|
1456
2014
|
getMiniMetadata: getMiniMetadata$6,
|
@@ -1461,153 +2019,14 @@ const EvmUniswapV2BalanceModule = {
|
|
1461
2019
|
};
|
1462
2020
|
|
1463
2021
|
// to be used by chaindata too
|
1464
|
-
const
|
1465
|
-
|
2022
|
+
const SolSplTokenConfigSchema = z.strictObject({
|
2023
|
+
mintAddress: SolSplTokenSchema.shape.mintAddress,
|
1466
2024
|
...TokenConfigBaseSchema.shape
|
1467
2025
|
});
|
1468
2026
|
|
1469
2027
|
const MODULE_TYPE$5 = SubAssetsTokenSchema.shape.type.value;
|
1470
2028
|
const PLATFORM$5 = SubAssetsTokenSchema.shape.platform.value;
|
1471
2029
|
|
1472
|
-
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
1473
|
-
const allStateKeys = queries.flatMap(({
|
1474
|
-
stateKeys
|
1475
|
-
}) => stateKeys).filter(isNotNil);
|
1476
|
-
|
1477
|
-
// doing a query without keys would throw an error => return early
|
1478
|
-
if (!allStateKeys.length) return queries.map(({
|
1479
|
-
stateKeys,
|
1480
|
-
decodeResult
|
1481
|
-
}) => decodeResult(stateKeys.map(() => null)));
|
1482
|
-
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
1483
|
-
return decodeRpcQueryPack(queries, result);
|
1484
|
-
};
|
1485
|
-
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
1486
|
-
const allStateKeys = queries.flatMap(({
|
1487
|
-
stateKeys
|
1488
|
-
}) => stateKeys).filter(isNotNil);
|
1489
|
-
|
1490
|
-
// doing a query without keys would throw an error => return early
|
1491
|
-
if (!allStateKeys.length) return of(queries.map(({
|
1492
|
-
stateKeys,
|
1493
|
-
decodeResult
|
1494
|
-
}) => decodeResult(stateKeys.map(() => null))));
|
1495
|
-
return new Observable(subscriber => {
|
1496
|
-
// first subscription callback includes results for all state keys, but further callbacks will only include the ones that changed
|
1497
|
-
// => we need to keep all results in memory and update them after each callback, so we can emit the full result set each time
|
1498
|
-
const changesCache = {};
|
1499
|
-
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
1500
|
-
if (error) subscriber.error(error);else if (result) {
|
1501
|
-
// update the cache
|
1502
|
-
for (const [stateKey, encodedResult] of result.changes) changesCache[stateKey] = encodedResult;
|
1503
|
-
|
1504
|
-
// regenerate the full changes array
|
1505
|
-
const changes = toPairs(changesCache);
|
1506
|
-
|
1507
|
-
// decode and emit results for all queries
|
1508
|
-
subscriber.next(decodeRpcQueryPack(queries, {
|
1509
|
-
block: result.block,
|
1510
|
-
changes
|
1511
|
-
}));
|
1512
|
-
}
|
1513
|
-
}, timeout);
|
1514
|
-
return () => {
|
1515
|
-
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
1516
|
-
};
|
1517
|
-
});
|
1518
|
-
};
|
1519
|
-
const decodeRpcQueryPack = (queries, result) => {
|
1520
|
-
return queries.reduce((acc, {
|
1521
|
-
stateKeys,
|
1522
|
-
decodeResult
|
1523
|
-
}) => {
|
1524
|
-
const changes = stateKeys.map(stateKey => {
|
1525
|
-
if (!stateKey || !result) return null;
|
1526
|
-
const change = result.changes.find(([key]) => key === stateKey);
|
1527
|
-
if (!change) return null;
|
1528
|
-
return change[1];
|
1529
|
-
});
|
1530
|
-
acc.push(decodeResult(changes));
|
1531
|
-
return acc;
|
1532
|
-
}, []);
|
1533
|
-
};
|
1534
|
-
|
1535
|
-
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
1536
|
-
const {
|
1537
|
-
builder
|
1538
|
-
} = parseMetadataRpc(metadataRpc);
|
1539
|
-
const call = builder.buildRuntimeCall(apiName, method);
|
1540
|
-
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex(call.args.enc(args))]);
|
1541
|
-
return call.value.dec(hex);
|
1542
|
-
};
|
1543
|
-
|
1544
|
-
const hasStorageItem = (metadata, palletName, itemName) => {
|
1545
|
-
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1546
|
-
if (!pallet || !pallet.storage) return false;
|
1547
|
-
return pallet.storage.items.some(item => item.name === itemName);
|
1548
|
-
};
|
1549
|
-
const hasStorageItems = (metadata, palletName, itemNames) => {
|
1550
|
-
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1551
|
-
if (!pallet || !pallet.storage) return false;
|
1552
|
-
return itemNames.every(itemName => pallet.storage?.items.some(item => item.name === itemName));
|
1553
|
-
};
|
1554
|
-
const hasRuntimeApi = (metadata, apiName, method) => {
|
1555
|
-
const api = metadata.apis.find(api => api.name === apiName);
|
1556
|
-
if (!api || !api.methods) return false;
|
1557
|
-
return api.methods.some(m => m.name === method);
|
1558
|
-
};
|
1559
|
-
const getConstantValue = (metadataRpc, pallet, constant) => {
|
1560
|
-
const {
|
1561
|
-
unifiedMetadata,
|
1562
|
-
builder
|
1563
|
-
} = parseMetadataRpc(metadataRpc);
|
1564
|
-
const codec = builder.buildConstant(pallet, constant);
|
1565
|
-
const encodedValue = unifiedMetadata.pallets.find(({
|
1566
|
-
name
|
1567
|
-
}) => name === pallet)?.constants.find(({
|
1568
|
-
name
|
1569
|
-
}) => name === constant)?.value;
|
1570
|
-
if (!encodedValue) throw new Error(`Constant ${pallet}.${constant} not found`);
|
1571
|
-
return codec.dec(encodedValue);
|
1572
|
-
};
|
1573
|
-
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
1574
|
-
const {
|
1575
|
-
unifiedMetadata,
|
1576
|
-
builder
|
1577
|
-
} = parseMetadataRpc(metadataRpc);
|
1578
|
-
const encodedValue = unifiedMetadata.pallets.find(({
|
1579
|
-
name
|
1580
|
-
}) => name === pallet)?.constants.find(({
|
1581
|
-
name
|
1582
|
-
}) => name === constant)?.value;
|
1583
|
-
if (!encodedValue) return null;
|
1584
|
-
const codec = builder.buildConstant(pallet, constant);
|
1585
|
-
return codec.dec(encodedValue);
|
1586
|
-
};
|
1587
|
-
|
1588
|
-
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
1589
|
-
if (!miniMetadata.data) return null;
|
1590
|
-
const metadata = unifyMetadata(decAnyMetadata(miniMetadata.data));
|
1591
|
-
try {
|
1592
|
-
const scaleBuilder = getDynamicBuilder(getLookupFn(metadata));
|
1593
|
-
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
1594
|
-
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
1595
|
-
chainId
|
1596
|
-
}) : moduleMethodOrFn;
|
1597
|
-
try {
|
1598
|
-
return [[key, scaleBuilder.buildStorage(module, method)]];
|
1599
|
-
} catch (cause) {
|
1600
|
-
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
1601
|
-
return [];
|
1602
|
-
}
|
1603
|
-
}));
|
1604
|
-
return builtCoders;
|
1605
|
-
} catch (cause) {
|
1606
|
-
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
1607
|
-
}
|
1608
|
-
return null;
|
1609
|
-
};
|
1610
|
-
|
1611
2030
|
const buildQueries$2 = (networkId, balanceDefs, miniMetadata) => {
|
1612
2031
|
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
1613
2032
|
storage: ["Assets", "Account"]
|
@@ -1843,11 +2262,6 @@ function includeInTotalExtraAmount(extra) {
|
|
1843
2262
|
return extra.filter(extra => extra.includeInTotal).map(extra => extra.amount.planck).reduce((a, b) => a + b, 0n);
|
1844
2263
|
}
|
1845
2264
|
|
1846
|
-
/**
|
1847
|
-
* Have the importing library define its Token and BalanceJson enums (as a sum type of all plugins) and pass them into some
|
1848
|
-
* internal global typescript context, which is then picked up on by this module.
|
1849
|
-
*/
|
1850
|
-
|
1851
2265
|
/** A utility type used to extract the underlying `BalanceType` of a specific source from a generalised `BalanceJson` */
|
1852
2266
|
|
1853
2267
|
/** TODO: Remove this in favour of a frontend-friendly `ChaindataProvider` */
|
@@ -2584,8 +2998,6 @@ const getValueId = amount => {
|
|
2584
2998
|
|
2585
2999
|
/** A labelled extra amount of a balance */
|
2586
3000
|
|
2587
|
-
/** Used by plugins to help define their custom `BalanceType` */
|
2588
|
-
|
2589
3001
|
/** For fast db access, you can calculate the primary key for a miniMetadata using this method */
|
2590
3002
|
const deriveMiniMetadataId = ({
|
2591
3003
|
source,
|
@@ -6142,7 +6554,7 @@ const SubTokensMiniMetadataExtraSchema = z.strictObject({
|
|
6142
6554
|
palletId: z.string()
|
6143
6555
|
});
|
6144
6556
|
|
6145
|
-
const BALANCE_MODULES = [SubNativeBalanceModule, SubAssetsBalanceModule, SubHydrationBalanceModule, SubForeignAssetsBalanceModule, SubPsp22BalanceModule, SubTokensBalanceModule, EvmErc20BalanceModule, EvmUniswapV2BalanceModule, EvmNativeBalanceModule];
|
6557
|
+
const BALANCE_MODULES = [SubNativeBalanceModule, SubAssetsBalanceModule, SubHydrationBalanceModule, SubForeignAssetsBalanceModule, SubPsp22BalanceModule, SubTokensBalanceModule, EvmErc20BalanceModule, EvmUniswapV2BalanceModule, EvmNativeBalanceModule, SolNativeBalanceModule, SolSplBalanceModule];
|
6146
6558
|
|
6147
6559
|
// share requests as all modules will call this at once
|
6148
6560
|
const CACHE$1 = new Map();
|
@@ -6320,10 +6732,25 @@ class BalancesProvider {
|
|
6320
6732
|
{
|
6321
6733
|
return this.getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6322
6734
|
}
|
6735
|
+
case "solana":
|
6736
|
+
{
|
6737
|
+
return this.getSolanaNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6738
|
+
}
|
6323
6739
|
case "polkadot":
|
6324
6740
|
{
|
6325
6741
|
return this.getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6326
6742
|
}
|
6743
|
+
default:
|
6744
|
+
{
|
6745
|
+
log.warn("[balances] Unsupported network platform for module", {
|
6746
|
+
networkId,
|
6747
|
+
mod
|
6748
|
+
});
|
6749
|
+
return of({
|
6750
|
+
status: "live",
|
6751
|
+
balances: []
|
6752
|
+
});
|
6753
|
+
}
|
6327
6754
|
}
|
6328
6755
|
}));
|
6329
6756
|
}), map(results => {
|
@@ -6353,7 +6780,7 @@ class BalancesProvider {
|
|
6353
6780
|
address
|
6354
6781
|
})));
|
6355
6782
|
if (!this.#chainConnectors.substrate) {
|
6356
|
-
log.
|
6783
|
+
log.warn("[balances] no substrate connector or miniMetadata for module", mod.type);
|
6357
6784
|
return defer(() => of({
|
6358
6785
|
status: "initialising",
|
6359
6786
|
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
@@ -6404,7 +6831,7 @@ class BalancesProvider {
|
|
6404
6831
|
address
|
6405
6832
|
})));
|
6406
6833
|
if (!this.#chainConnectors.evm) {
|
6407
|
-
log.
|
6834
|
+
log.warn("[balances] no ethereum connector for module", mod.type);
|
6408
6835
|
return defer(() => of({
|
6409
6836
|
status: "initialising",
|
6410
6837
|
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
@@ -6436,6 +6863,56 @@ class BalancesProvider {
|
|
6436
6863
|
})));
|
6437
6864
|
});
|
6438
6865
|
}
|
6866
|
+
getSolanaNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6867
|
+
return getSharedObservable(`BalancesProvider.getSolanaNetworkModuleBalances$`, {
|
6868
|
+
networkId,
|
6869
|
+
mod,
|
6870
|
+
tokensWithAddresses
|
6871
|
+
}, () => {
|
6872
|
+
if (!tokensWithAddresses.length) return of({
|
6873
|
+
status: "live",
|
6874
|
+
balances: []
|
6875
|
+
});
|
6876
|
+
const moduleAddressesByTokenId = fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6877
|
+
|
6878
|
+
// all balance ids expected in result set
|
6879
|
+
const balanceIds = toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6880
|
+
tokenId,
|
6881
|
+
address
|
6882
|
+
})));
|
6883
|
+
if (!this.#chainConnectors.solana) {
|
6884
|
+
log.warn("[balances] no solana connector for module", mod.type);
|
6885
|
+
return defer(() => of({
|
6886
|
+
status: "initialising",
|
6887
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6888
|
+
}));
|
6889
|
+
}
|
6890
|
+
const moduleBalances$ = mod.subscribeBalances({
|
6891
|
+
networkId,
|
6892
|
+
tokensWithAddresses,
|
6893
|
+
connector: this.#chainConnectors.solana
|
6894
|
+
}).pipe(catchError(() => EMPTY),
|
6895
|
+
// don't emit, let provider mark balances stale
|
6896
|
+
map(results => ({
|
6897
|
+
status: "live",
|
6898
|
+
// exclude zero balances
|
6899
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6900
|
+
})), tap(results => {
|
6901
|
+
this.updateStorage$(balanceIds, results);
|
6902
|
+
}),
|
6903
|
+
// shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
|
6904
|
+
shareReplay({
|
6905
|
+
refCount: true,
|
6906
|
+
bufferSize: 1
|
6907
|
+
}), keepAlive(0));
|
6908
|
+
|
6909
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6910
|
+
return defer(() => moduleBalances$.pipe(startWith({
|
6911
|
+
status: "initialising",
|
6912
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6913
|
+
})));
|
6914
|
+
});
|
6915
|
+
}
|
6439
6916
|
updateStorage$(balanceIds, balancesResult) {
|
6440
6917
|
if (balancesResult.status !== "live") return;
|
6441
6918
|
const storage = this.#storage.getValue();
|
@@ -6546,18 +7023,42 @@ class BalancesProvider {
|
|
6546
7023
|
}));
|
6547
7024
|
}
|
6548
7025
|
}
|
6549
|
-
const
|
7026
|
+
const isAccountPlatformCompatibleWithNetwork = (network, platform) => {
|
6550
7027
|
switch (network.platform) {
|
6551
7028
|
case "ethereum":
|
6552
|
-
return
|
7029
|
+
return platform === "ethereum";
|
7030
|
+
case "solana":
|
7031
|
+
return platform === "solana";
|
6553
7032
|
case "polkadot":
|
6554
|
-
|
7033
|
+
{
|
7034
|
+
switch (network.account) {
|
7035
|
+
case "secp256k1":
|
7036
|
+
return platform === "ethereum";
|
7037
|
+
case "*25519":
|
7038
|
+
return platform === "polkadot";
|
7039
|
+
default:
|
7040
|
+
throw new Error(`Unsupported polkadot network account type ${network.account}`);
|
7041
|
+
}
|
7042
|
+
}
|
6555
7043
|
default:
|
6556
7044
|
log.warn("Unsupported network platform", network);
|
6557
7045
|
throw new Error("Unsupported network platform");
|
6558
7046
|
}
|
6559
7047
|
};
|
7048
|
+
|
7049
|
+
/**
|
7050
|
+
* If this is the address of an account, use isAccountCompatibleWithChain instead.
|
7051
|
+
* Otherwise it could lead to a loss of funds
|
7052
|
+
* @param chain
|
7053
|
+
* @param address
|
7054
|
+
* @returns
|
7055
|
+
*/
|
7056
|
+
const isAddressCompatibleWithNetwork = (network, address) => {
|
7057
|
+
// TODO try with return true to check if wallet filters correctly upfront
|
7058
|
+
const accountPlatform = getAccountPlatformFromAddress(address);
|
7059
|
+
return isAccountPlatformCompatibleWithNetwork(network, accountPlatform);
|
7060
|
+
};
|
6560
7061
|
const sortByBalanceId = (a, b) => getBalanceId(a).localeCompare(getBalanceId(b));
|
6561
7062
|
const sortByMiniMetadataId = (a, b) => a.id.localeCompare(b.id);
|
6562
7063
|
|
6563
|
-
export { BALANCE_MODULES, Balance, BalanceFormatter, BalanceValueGetter, Balances, BalancesProvider, Change24hCurrencyFormatter, EvmErc20BalanceModule, EvmErc20TokenConfigSchema, EvmNativeBalanceModule, EvmNativeTokenConfigSchema, EvmUniswapV2BalanceModule, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, ONE_ALPHA_TOKEN, PlanckSumBalancesFormatter, SCALE_FACTOR, SUBTENSOR_MIN_STAKE_AMOUNT_PLANK, SUBTENSOR_ROOT_NETUID, SubAssetsBalanceModule, SubAssetsTokenConfigSchema, SubForeignAssetsBalanceModule, SubForeignAssetsTokenConfigSchema, SubHydrationBalanceModule, SubHydrationTokenConfigSchema, SubNativeBalanceModule, SubNativeMiniMetadataExtraSchema, SubNativeModuleConfigSchema, SubNativeTokenConfigSchema, SubPsp22BalanceModule, SubPsp22TokenConfigSchema, SubTokensBalanceModule, SubTokensMiniMetadataExtraSchema, SubTokensModuleConfigSchema, SubTokensTokenConfigSchema, SumBalancesFormatter, abiMulticall, calculateAlphaPrice, calculateTaoAmountFromAlpha, calculateTaoFromDynamicInfo, deriveMiniMetadataId, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, getBalanceId, getLockTitle, getLockedType, getValueId, includeInTotalExtraAmount, uniswapV2PairAbi };
|
7064
|
+
export { BALANCE_MODULES, Balance, BalanceFormatter, BalanceValueGetter, Balances, BalancesProvider, Change24hCurrencyFormatter, EvmErc20BalanceModule, EvmErc20TokenConfigSchema, EvmNativeBalanceModule, EvmNativeTokenConfigSchema, EvmUniswapV2BalanceModule, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, ONE_ALPHA_TOKEN, PlanckSumBalancesFormatter, SCALE_FACTOR, SUBTENSOR_MIN_STAKE_AMOUNT_PLANK, SUBTENSOR_ROOT_NETUID, SolNativeBalanceModule, SolNativeTokenConfigSchema, SolSplBalanceModule, SolSplTokenConfigSchema, SubAssetsBalanceModule, SubAssetsTokenConfigSchema, SubForeignAssetsBalanceModule, SubForeignAssetsTokenConfigSchema, SubHydrationBalanceModule, SubHydrationTokenConfigSchema, SubNativeBalanceModule, SubNativeMiniMetadataExtraSchema, SubNativeModuleConfigSchema, SubNativeTokenConfigSchema, SubPsp22BalanceModule, SubPsp22TokenConfigSchema, SubTokensBalanceModule, SubTokensMiniMetadataExtraSchema, SubTokensModuleConfigSchema, SubTokensTokenConfigSchema, SumBalancesFormatter, abiMulticall, calculateAlphaPrice, calculateTaoAmountFromAlpha, calculateTaoFromDynamicInfo, deriveMiniMetadataId, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, getBalanceId, getLockTitle, getLockedType, getValueId, includeInTotalExtraAmount, uniswapV2PairAbi };
|