@talismn/balances 1.0.2 → 1.0.3
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 +2 -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/detectedTokens.d.ts +3 -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 +770 -248
- package/dist/talismn-balances.cjs.prod.js +770 -248
- package/dist/talismn-balances.esm.js +761 -243
- 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
|
-
import { of, Observable, distinctUntilChanged, map, timer, switchMap, from, firstValueFrom, combineLatest,
|
8
|
+
import { of, Observable, distinctUntilChanged, BehaviorSubject, map, timer, switchMap, from, firstValueFrom, combineLatest, 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,615 @@ 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 tokenIdsByAddress = new BehaviorSubject({});
|
1767
|
+
const setDetectedTokenIds = (address, type, tokenIds) => {
|
1768
|
+
// keep token ids from other token types (will be useful once we add token2022)
|
1769
|
+
const otherTokens = tokenIdsByAddress.value[address]?.filter(id => parseTokenId(id).type !== type) ?? [];
|
1770
|
+
tokenIdsByAddress.next({
|
1771
|
+
...tokenIdsByAddress.value,
|
1772
|
+
[address]: otherTokens.concat(tokenIds).sort()
|
1773
|
+
});
|
1774
|
+
};
|
1775
|
+
const getDetectedTokensIds$ = address => tokenIdsByAddress.pipe(map(ownedTokens => ownedTokens[address] ?? []), distinctUntilChanged(isEqual));
|
1776
|
+
|
1777
|
+
const SPL_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
1778
|
+
const fetchBalances$6 = async ({
|
1779
|
+
networkId,
|
1780
|
+
tokensWithAddresses,
|
1781
|
+
connector
|
1782
|
+
}) => {
|
1783
|
+
if (!tokensWithAddresses.length) return {
|
1784
|
+
success: [],
|
1785
|
+
errors: []
|
1786
|
+
};
|
1787
|
+
const connection = await connector.getConnection(networkId);
|
1788
|
+
if (!connection) throw new Error(`Could not get connection for Solana network ${networkId}`);
|
1789
|
+
const accountAddresses = uniq(tokensWithAddresses.flatMap(([, addresses]) => addresses));
|
1790
|
+
const balancesPerAddress = await Promise.all(accountAddresses.map(async address => {
|
1791
|
+
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(new PublicKey(address), {
|
1792
|
+
programId: new PublicKey(SPL_PROGRAM_ID) // SPL Token Program ID
|
1793
|
+
});
|
1794
|
+
const balances = tokenAccounts.value.map(d => {
|
1795
|
+
try {
|
1796
|
+
const mintAddress = d.account.data.parsed.info.mint;
|
1797
|
+
const value = d.account.data.parsed.info.tokenAmount.amount ?? "0";
|
1798
|
+
return {
|
1799
|
+
tokenId: solSplTokenId(networkId, mintAddress),
|
1800
|
+
networkId,
|
1801
|
+
address,
|
1802
|
+
source: MODULE_TYPE$6,
|
1803
|
+
status: "live",
|
1804
|
+
value
|
1805
|
+
};
|
1806
|
+
} catch (err) {
|
1807
|
+
log.warn("Failed to parse token amount", {
|
1808
|
+
address,
|
1809
|
+
d
|
1810
|
+
});
|
1811
|
+
return null;
|
1812
|
+
}
|
1813
|
+
}).filter(isNotNil);
|
1814
|
+
|
1815
|
+
// allows the wallet to detect new tokens, and enable them automatically
|
1816
|
+
setDetectedTokenIds(address, MODULE_TYPE$6, balances.map(b => b.tokenId));
|
1817
|
+
return [address, balances];
|
1818
|
+
}));
|
1819
|
+
const allBalancesByKey = keyBy(balancesPerAddress.flatMap(([, addressBalances]) => addressBalances), b => getBalanceKey(b.tokenId, b.address));
|
1820
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
1821
|
+
|
1822
|
+
// return a balance entry for all token/address pairs that were requested
|
1823
|
+
const success = balanceDefs.map(bd => {
|
1824
|
+
const found = allBalancesByKey[getBalanceKey(bd.token.id, bd.address)];
|
1825
|
+
return found ?? {
|
1826
|
+
tokenId: bd.token.id,
|
1827
|
+
networkId: bd.token.networkId,
|
1828
|
+
address: bd.address,
|
1829
|
+
source: MODULE_TYPE$6,
|
1830
|
+
status: "live",
|
1831
|
+
value: "0"
|
1832
|
+
};
|
1833
|
+
});
|
1834
|
+
|
1835
|
+
// return only the balances that match the tokens we are interested in
|
1836
|
+
return {
|
1837
|
+
success,
|
1838
|
+
errors: []
|
1839
|
+
}; // TODO output errors if any
|
1840
|
+
};
|
1841
|
+
const getBalanceKey = (tokenId, address) => `${tokenId}:${address}`;
|
1842
|
+
|
1843
|
+
const TokenCacheSchema = z.discriminatedUnion("isValid", [z.strictObject({
|
1844
|
+
id: SolSplTokenSchema.shape.id,
|
1845
|
+
isValid: z.literal(true),
|
1846
|
+
...SolSplTokenSchema.pick({
|
1847
|
+
symbol: true,
|
1848
|
+
decimals: true,
|
1849
|
+
name: true,
|
1850
|
+
logo: true
|
1851
|
+
}).shape
|
1852
|
+
}), z.strictObject({
|
1853
|
+
id: SolSplTokenSchema.shape.id,
|
1854
|
+
isValid: z.literal(false)
|
1855
|
+
})]);
|
1856
|
+
const METAPLEX_PROGRAM_ID = new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
|
1857
|
+
const fetchTokens$6 = async ({
|
1858
|
+
networkId,
|
1859
|
+
tokens,
|
1860
|
+
connector,
|
1861
|
+
cache
|
1862
|
+
}) => {
|
1863
|
+
const result = [];
|
1864
|
+
for (const tokenConfig of tokens) {
|
1865
|
+
const tokenId = solSplTokenId(networkId, tokenConfig.mintAddress);
|
1866
|
+
let cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1867
|
+
if (!cached) {
|
1868
|
+
const tokenInfo = await fetchOnChainTokenData(connector, tokenId);
|
1869
|
+
if (tokenInfo) cache[tokenId] = tokenInfo;
|
1870
|
+
}
|
1871
|
+
cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1872
|
+
if (cached?.isValid === false) continue;
|
1369
1873
|
const base = {
|
1370
1874
|
id: tokenId,
|
1371
1875
|
type: MODULE_TYPE$6,
|
1372
1876
|
platform: PLATFORM$6,
|
1373
1877
|
networkId
|
1374
1878
|
};
|
1375
|
-
const
|
1376
|
-
|
1377
|
-
const token = assign(base, cached2?.isValid ? omit(cached2, ["isValid"]) : {}, tokenConfig);
|
1378
|
-
const parsed = EvmUniswapV2TokenSchema.safeParse(token);
|
1879
|
+
const token = assign(base, cached?.isValid ? omit(cached, ["isValid"]) : {}, tokenConfig);
|
1880
|
+
const parsed = SolSplTokenSchema.safeParse(token);
|
1379
1881
|
if (!parsed.success) {
|
1380
|
-
log.warn("Ignoring token with invalid
|
1882
|
+
log.warn("Ignoring token with invalid SolSplTokenSchema", {
|
1883
|
+
token
|
1884
|
+
});
|
1381
1885
|
continue;
|
1382
1886
|
}
|
1383
1887
|
result.push(parsed.data);
|
1384
1888
|
}
|
1385
1889
|
return result;
|
1386
1890
|
};
|
1891
|
+
const ERROR_NO_MINT = "No mint info available";
|
1892
|
+
const ERROR_NO_METADATA = "No metadata account found";
|
1893
|
+
const ERROR_INVALID_DATA = "Invalid on-chain data";
|
1894
|
+
const fetchOnChainTokenData = async (connector, tokenId) => {
|
1895
|
+
try {
|
1896
|
+
const {
|
1897
|
+
networkId,
|
1898
|
+
mintAddress
|
1899
|
+
} = parseSolSplTokenId(tokenId);
|
1900
|
+
const connection = await connector.getConnection(networkId);
|
1901
|
+
if (!connection) {
|
1902
|
+
log.warn(`No connection found for network ${networkId}`);
|
1903
|
+
return null;
|
1904
|
+
}
|
1905
|
+
const mintPubKey = new PublicKey(mintAddress);
|
1906
|
+
const mintInfo = await connection.getAccountInfo(mintPubKey);
|
1907
|
+
if (!mintInfo?.data) throw new Error(ERROR_NO_MINT);
|
1908
|
+
const mint = MintLayout.decode(mintInfo.data);
|
1909
|
+
const [metadataPDA] = PublicKey.findProgramAddressSync([Buffer.from("metadata"), METAPLEX_PROGRAM_ID.toBuffer(), mintPubKey.toBuffer()], METAPLEX_PROGRAM_ID);
|
1910
|
+
|
1911
|
+
// 3. Fetch metadata account directly (traditional way)
|
1912
|
+
const metadataAccount = await connection.getAccountInfo(new PublicKey(metadataPDA));
|
1913
|
+
if (!metadataAccount) throw new Error(ERROR_NO_METADATA);
|
1914
|
+
const metadata = deserializeMetadata({
|
1915
|
+
publicKey: publicKey(metadataPDA),
|
1916
|
+
executable: metadataAccount.executable,
|
1917
|
+
owner: publicKey(metadataAccount.owner),
|
1918
|
+
lamports: sol(metadataAccount.lamports),
|
1919
|
+
data: metadataAccount.data
|
1920
|
+
});
|
1921
|
+
const parsed = TokenCacheSchema.safeParse({
|
1922
|
+
id: tokenId,
|
1923
|
+
symbol: metadata.symbol.trim(),
|
1924
|
+
name: metadata.name.trim(),
|
1925
|
+
decimals: mint.decimals,
|
1926
|
+
isValid: true
|
1927
|
+
});
|
1928
|
+
if (!parsed.success) throw new Error(ERROR_INVALID_DATA);
|
1929
|
+
return parsed.data;
|
1930
|
+
} catch (err) {
|
1931
|
+
const msg = err.message;
|
1932
|
+
if ([ERROR_NO_MINT, ERROR_NO_METADATA, ERROR_INVALID_DATA].includes(msg)) return TokenCacheSchema.parse({
|
1933
|
+
id: tokenId,
|
1934
|
+
isValid: false
|
1935
|
+
});
|
1936
|
+
log.warn("Failed to fetch sol-spl token data for %s", tokenId, {
|
1937
|
+
err
|
1938
|
+
});
|
1939
|
+
}
|
1940
|
+
return null;
|
1941
|
+
};
|
1387
1942
|
|
1388
1943
|
const getMiniMetadata$6 = () => {
|
1389
|
-
throw new Error("MiniMetadata is not supported for
|
1944
|
+
throw new Error("MiniMetadata is not supported for solana tokens");
|
1390
1945
|
};
|
1391
1946
|
|
1392
|
-
const getTransferCallData$6 = ({
|
1947
|
+
const getTransferCallData$6 = async ({
|
1393
1948
|
from,
|
1394
1949
|
to,
|
1395
1950
|
value,
|
1396
|
-
token
|
1951
|
+
token,
|
1952
|
+
connector
|
1397
1953
|
}) => {
|
1398
1954
|
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
|
-
|
1955
|
+
const connection = await connector.getConnection(token.networkId);
|
1956
|
+
const instructions = [];
|
1957
|
+
const mint = new PublicKey(token.mintAddress);
|
1958
|
+
const fromWallet = new PublicKey(from);
|
1959
|
+
const toWallet = new PublicKey(to);
|
1960
|
+
|
1961
|
+
// Get associated token accounts
|
1962
|
+
const fromTokenAccount = await getAssociatedTokenAddress(mint, fromWallet);
|
1963
|
+
const toTokenAccount = await getAssociatedTokenAddress(mint, toWallet);
|
1964
|
+
|
1965
|
+
// Create the target token account if it doesn't exist
|
1966
|
+
if (!(await tokenAccountExists(connection, toTokenAccount))) {
|
1967
|
+
instructions.push(createAssociatedTokenAccountInstruction(fromWallet,
|
1968
|
+
// funder
|
1969
|
+
toTokenAccount, toWallet, mint));
|
1970
|
+
}
|
1971
|
+
|
1972
|
+
// Transfer the tokens
|
1973
|
+
instructions.push(createTransferInstruction(fromTokenAccount, toTokenAccount, fromWallet, BigInt(value), [], TOKEN_PROGRAM_ID));
|
1974
|
+
return instructions;
|
1975
|
+
};
|
1976
|
+
const tokenAccountExists = async (connection, address) => {
|
1977
|
+
try {
|
1978
|
+
await getAccount(connection, address);
|
1979
|
+
return true;
|
1980
|
+
} catch {
|
1981
|
+
return false;
|
1982
|
+
}
|
1411
1983
|
};
|
1412
1984
|
|
1413
1985
|
const SUBSCRIPTION_INTERVAL$2 = 6_000;
|
@@ -1450,7 +2022,7 @@ const subscribeBalances$6 = ({
|
|
1450
2022
|
}).pipe(distinctUntilChanged(isEqual));
|
1451
2023
|
};
|
1452
2024
|
|
1453
|
-
const
|
2025
|
+
const SolSplBalanceModule = {
|
1454
2026
|
type: MODULE_TYPE$6,
|
1455
2027
|
platform: PLATFORM$6,
|
1456
2028
|
getMiniMetadata: getMiniMetadata$6,
|
@@ -1461,153 +2033,14 @@ const EvmUniswapV2BalanceModule = {
|
|
1461
2033
|
};
|
1462
2034
|
|
1463
2035
|
// to be used by chaindata too
|
1464
|
-
const
|
1465
|
-
|
2036
|
+
const SolSplTokenConfigSchema = z.strictObject({
|
2037
|
+
mintAddress: SolSplTokenSchema.shape.mintAddress,
|
1466
2038
|
...TokenConfigBaseSchema.shape
|
1467
2039
|
});
|
1468
2040
|
|
1469
2041
|
const MODULE_TYPE$5 = SubAssetsTokenSchema.shape.type.value;
|
1470
2042
|
const PLATFORM$5 = SubAssetsTokenSchema.shape.platform.value;
|
1471
2043
|
|
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
2044
|
const buildQueries$2 = (networkId, balanceDefs, miniMetadata) => {
|
1612
2045
|
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
1613
2046
|
storage: ["Assets", "Account"]
|
@@ -1843,11 +2276,6 @@ function includeInTotalExtraAmount(extra) {
|
|
1843
2276
|
return extra.filter(extra => extra.includeInTotal).map(extra => extra.amount.planck).reduce((a, b) => a + b, 0n);
|
1844
2277
|
}
|
1845
2278
|
|
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
2279
|
/** A utility type used to extract the underlying `BalanceType` of a specific source from a generalised `BalanceJson` */
|
1852
2280
|
|
1853
2281
|
/** TODO: Remove this in favour of a frontend-friendly `ChaindataProvider` */
|
@@ -2584,8 +3012,6 @@ const getValueId = amount => {
|
|
2584
3012
|
|
2585
3013
|
/** A labelled extra amount of a balance */
|
2586
3014
|
|
2587
|
-
/** Used by plugins to help define their custom `BalanceType` */
|
2588
|
-
|
2589
3015
|
/** For fast db access, you can calculate the primary key for a miniMetadata using this method */
|
2590
3016
|
const deriveMiniMetadataId = ({
|
2591
3017
|
source,
|
@@ -2712,12 +3138,12 @@ const getTransferAllEncodedArgs$2 = (assetId, to, codec) => {
|
|
2712
3138
|
return getEncodedValue$2(codec, [() => ({
|
2713
3139
|
id: Number(assetId),
|
2714
3140
|
// for most networks
|
2715
|
-
|
3141
|
+
dest: Enum("Id", to),
|
2716
3142
|
keep_alive: false
|
2717
3143
|
}), () => ({
|
2718
3144
|
id: BigInt(assetId),
|
2719
3145
|
// for Astar
|
2720
|
-
|
3146
|
+
dest: Enum("Id", to),
|
2721
3147
|
keep_alive: false
|
2722
3148
|
})]);
|
2723
3149
|
};
|
@@ -3095,7 +3521,7 @@ const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
|
|
3095
3521
|
return getEncodedValue$1(codec, [() => ({
|
3096
3522
|
id: papiParse(onChainId),
|
3097
3523
|
// for most networks
|
3098
|
-
|
3524
|
+
dest: Enum("Id", to),
|
3099
3525
|
keep_alive: false
|
3100
3526
|
})]);
|
3101
3527
|
};
|
@@ -6142,7 +6568,7 @@ const SubTokensMiniMetadataExtraSchema = z.strictObject({
|
|
6142
6568
|
palletId: z.string()
|
6143
6569
|
});
|
6144
6570
|
|
6145
|
-
const BALANCE_MODULES = [SubNativeBalanceModule, SubAssetsBalanceModule, SubHydrationBalanceModule, SubForeignAssetsBalanceModule, SubPsp22BalanceModule, SubTokensBalanceModule, EvmErc20BalanceModule, EvmUniswapV2BalanceModule, EvmNativeBalanceModule];
|
6571
|
+
const BALANCE_MODULES = [SubNativeBalanceModule, SubAssetsBalanceModule, SubHydrationBalanceModule, SubForeignAssetsBalanceModule, SubPsp22BalanceModule, SubTokensBalanceModule, EvmErc20BalanceModule, EvmUniswapV2BalanceModule, EvmNativeBalanceModule, SolNativeBalanceModule, SolSplBalanceModule];
|
6146
6572
|
|
6147
6573
|
// share requests as all modules will call this at once
|
6148
6574
|
const CACHE$1 = new Map();
|
@@ -6308,6 +6734,9 @@ class BalancesProvider {
|
|
6308
6734
|
balances
|
6309
6735
|
}) => balances)));
|
6310
6736
|
}
|
6737
|
+
getDetectedTokensId$(address) {
|
6738
|
+
return getDetectedTokensIds$(address);
|
6739
|
+
}
|
6311
6740
|
getNetworkBalances$(networkId, addressesByTokenId) {
|
6312
6741
|
const network$ = this.#chaindataProvider.getNetworkById$(networkId);
|
6313
6742
|
const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
|
@@ -6320,10 +6749,25 @@ class BalancesProvider {
|
|
6320
6749
|
{
|
6321
6750
|
return this.getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6322
6751
|
}
|
6752
|
+
case "solana":
|
6753
|
+
{
|
6754
|
+
return this.getSolanaNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6755
|
+
}
|
6323
6756
|
case "polkadot":
|
6324
6757
|
{
|
6325
6758
|
return this.getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6326
6759
|
}
|
6760
|
+
default:
|
6761
|
+
{
|
6762
|
+
log.warn("[balances] Unsupported network platform for module", {
|
6763
|
+
networkId,
|
6764
|
+
mod
|
6765
|
+
});
|
6766
|
+
return of({
|
6767
|
+
status: "live",
|
6768
|
+
balances: []
|
6769
|
+
});
|
6770
|
+
}
|
6327
6771
|
}
|
6328
6772
|
}));
|
6329
6773
|
}), map(results => {
|
@@ -6353,7 +6797,7 @@ class BalancesProvider {
|
|
6353
6797
|
address
|
6354
6798
|
})));
|
6355
6799
|
if (!this.#chainConnectors.substrate) {
|
6356
|
-
log.
|
6800
|
+
log.warn("[balances] no substrate connector or miniMetadata for module", mod.type);
|
6357
6801
|
return defer(() => of({
|
6358
6802
|
status: "initialising",
|
6359
6803
|
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
@@ -6404,7 +6848,7 @@ class BalancesProvider {
|
|
6404
6848
|
address
|
6405
6849
|
})));
|
6406
6850
|
if (!this.#chainConnectors.evm) {
|
6407
|
-
log.
|
6851
|
+
log.warn("[balances] no ethereum connector for module", mod.type);
|
6408
6852
|
return defer(() => of({
|
6409
6853
|
status: "initialising",
|
6410
6854
|
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
@@ -6436,6 +6880,56 @@ class BalancesProvider {
|
|
6436
6880
|
})));
|
6437
6881
|
});
|
6438
6882
|
}
|
6883
|
+
getSolanaNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6884
|
+
return getSharedObservable(`BalancesProvider.getSolanaNetworkModuleBalances$`, {
|
6885
|
+
networkId,
|
6886
|
+
mod,
|
6887
|
+
tokensWithAddresses
|
6888
|
+
}, () => {
|
6889
|
+
if (!tokensWithAddresses.length) return of({
|
6890
|
+
status: "live",
|
6891
|
+
balances: []
|
6892
|
+
});
|
6893
|
+
const moduleAddressesByTokenId = fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6894
|
+
|
6895
|
+
// all balance ids expected in result set
|
6896
|
+
const balanceIds = toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6897
|
+
tokenId,
|
6898
|
+
address
|
6899
|
+
})));
|
6900
|
+
if (!this.#chainConnectors.solana) {
|
6901
|
+
log.warn("[balances] no solana connector for module", mod.type);
|
6902
|
+
return defer(() => of({
|
6903
|
+
status: "initialising",
|
6904
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6905
|
+
}));
|
6906
|
+
}
|
6907
|
+
const moduleBalances$ = mod.subscribeBalances({
|
6908
|
+
networkId,
|
6909
|
+
tokensWithAddresses,
|
6910
|
+
connector: this.#chainConnectors.solana
|
6911
|
+
}).pipe(catchError(() => EMPTY),
|
6912
|
+
// don't emit, let provider mark balances stale
|
6913
|
+
map(results => ({
|
6914
|
+
status: "live",
|
6915
|
+
// exclude zero balances
|
6916
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6917
|
+
})), tap(results => {
|
6918
|
+
this.updateStorage$(balanceIds, results);
|
6919
|
+
}),
|
6920
|
+
// shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
|
6921
|
+
shareReplay({
|
6922
|
+
refCount: true,
|
6923
|
+
bufferSize: 1
|
6924
|
+
}), keepAlive(0));
|
6925
|
+
|
6926
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6927
|
+
return defer(() => moduleBalances$.pipe(startWith({
|
6928
|
+
status: "initialising",
|
6929
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6930
|
+
})));
|
6931
|
+
});
|
6932
|
+
}
|
6439
6933
|
updateStorage$(balanceIds, balancesResult) {
|
6440
6934
|
if (balancesResult.status !== "live") return;
|
6441
6935
|
const storage = this.#storage.getValue();
|
@@ -6546,18 +7040,42 @@ class BalancesProvider {
|
|
6546
7040
|
}));
|
6547
7041
|
}
|
6548
7042
|
}
|
6549
|
-
const
|
7043
|
+
const isAccountPlatformCompatibleWithNetwork = (network, platform) => {
|
6550
7044
|
switch (network.platform) {
|
6551
7045
|
case "ethereum":
|
6552
|
-
return
|
7046
|
+
return platform === "ethereum";
|
7047
|
+
case "solana":
|
7048
|
+
return platform === "solana";
|
6553
7049
|
case "polkadot":
|
6554
|
-
|
7050
|
+
{
|
7051
|
+
switch (network.account) {
|
7052
|
+
case "secp256k1":
|
7053
|
+
return platform === "ethereum";
|
7054
|
+
case "*25519":
|
7055
|
+
return platform === "polkadot";
|
7056
|
+
default:
|
7057
|
+
throw new Error(`Unsupported polkadot network account type ${network.account}`);
|
7058
|
+
}
|
7059
|
+
}
|
6555
7060
|
default:
|
6556
7061
|
log.warn("Unsupported network platform", network);
|
6557
7062
|
throw new Error("Unsupported network platform");
|
6558
7063
|
}
|
6559
7064
|
};
|
7065
|
+
|
7066
|
+
/**
|
7067
|
+
* If this is the address of an account, use isAccountCompatibleWithChain instead.
|
7068
|
+
* Otherwise it could lead to a loss of funds
|
7069
|
+
* @param chain
|
7070
|
+
* @param address
|
7071
|
+
* @returns
|
7072
|
+
*/
|
7073
|
+
const isAddressCompatibleWithNetwork = (network, address) => {
|
7074
|
+
// TODO try with return true to check if wallet filters correctly upfront
|
7075
|
+
const accountPlatform = getAccountPlatformFromAddress(address);
|
7076
|
+
return isAccountPlatformCompatibleWithNetwork(network, accountPlatform);
|
7077
|
+
};
|
6560
7078
|
const sortByBalanceId = (a, b) => getBalanceId(a).localeCompare(getBalanceId(b));
|
6561
7079
|
const sortByMiniMetadataId = (a, b) => a.id.localeCompare(b.id);
|
6562
7080
|
|
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 };
|
7081
|
+
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 };
|