@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,14 +1,19 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
3
|
var chaindataProvider = require('@talismn/chaindata-provider');
|
4
|
-
var
|
4
|
+
var crypto = require('@talismn/crypto');
|
5
5
|
var viem = require('viem');
|
6
6
|
var lodashEs = require('lodash-es');
|
7
7
|
var z = require('zod/v4');
|
8
8
|
var anylogger = require('anylogger');
|
9
9
|
var rxjs = require('rxjs');
|
10
10
|
var BigNumber = require('bignumber.js');
|
11
|
+
var web3_js = require('@solana/web3.js');
|
12
|
+
var util = require('@talismn/util');
|
11
13
|
var scale = require('@talismn/scale');
|
14
|
+
var mplTokenMetadata = require('@metaplex-foundation/mpl-token-metadata');
|
15
|
+
var umi = require('@metaplex-foundation/umi');
|
16
|
+
var splToken = require('@solana/spl-token');
|
12
17
|
var tokenRates = require('@talismn/token-rates');
|
13
18
|
var utils = require('@polkadot-api/utils');
|
14
19
|
var polkadotApi = require('polkadot-api');
|
@@ -28,8 +33,8 @@ var BigNumber__default = /*#__PURE__*/_interopDefault(BigNumber);
|
|
28
33
|
var upperFirst__default = /*#__PURE__*/_interopDefault(upperFirst);
|
29
34
|
var PQueue__default = /*#__PURE__*/_interopDefault(PQueue);
|
30
35
|
|
31
|
-
const MODULE_TYPE$
|
32
|
-
const PLATFORM$
|
36
|
+
const MODULE_TYPE$a = chaindataProvider.EvmErc20TokenSchema.shape.type.value;
|
37
|
+
const PLATFORM$a = chaindataProvider.EvmErc20TokenSchema.shape.platform.value;
|
33
38
|
|
34
39
|
const abiMulticall = viem.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)"]);
|
35
40
|
|
@@ -625,7 +630,7 @@ const getBalanceDefs = addressesByToken => {
|
|
625
630
|
// if there is at least one storage entry, the results will be an array with a single object
|
626
631
|
// if the storage has no entries in it (ex: Assets on ewx or moonbeam), the response will be an empty array
|
627
632
|
|
628
|
-
const fetchBalances$
|
633
|
+
const fetchBalances$a = async ({
|
629
634
|
networkId,
|
630
635
|
tokensWithAddresses,
|
631
636
|
connector
|
@@ -637,8 +642,8 @@ const fetchBalances$8 = async ({
|
|
637
642
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
638
643
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
639
644
|
for (const [token, addresses] of tokensWithAddresses) {
|
640
|
-
if (token.type !== MODULE_TYPE$
|
641
|
-
for (const address of addresses) if (!
|
645
|
+
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}`);
|
646
|
+
for (const address of addresses) if (!crypto.isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
642
647
|
}
|
643
648
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
644
649
|
if (client.chain?.contracts?.erc20Aggregator && balanceDefs.length > 1) {
|
@@ -667,7 +672,7 @@ const fetchWithoutAggregator = async (client, balanceDefs) => {
|
|
667
672
|
address,
|
668
673
|
tokenId: token.id,
|
669
674
|
value: result.toString(),
|
670
|
-
source: MODULE_TYPE$
|
675
|
+
source: MODULE_TYPE$a,
|
671
676
|
networkId: chaindataProvider.parseEvmErc20TokenId(token.id).networkId,
|
672
677
|
status: "live"
|
673
678
|
};
|
@@ -710,7 +715,7 @@ const fetchWithAggregator = async (client, balanceDefs, erc20BalancesAggregatorA
|
|
710
715
|
address: balanceDef.address,
|
711
716
|
tokenId: balanceDef.token.id,
|
712
717
|
value: erc20Balances[index].toString(),
|
713
|
-
source: MODULE_TYPE$
|
718
|
+
source: MODULE_TYPE$a,
|
714
719
|
networkId: chaindataProvider.parseTokenId(balanceDef.token.id).networkId,
|
715
720
|
status: "live"
|
716
721
|
}));
|
@@ -767,7 +772,7 @@ const getTypedContract$1 = (client, abi, contractAddress) => viem.getContract({
|
|
767
772
|
}
|
768
773
|
});
|
769
774
|
|
770
|
-
const TokenCacheSchema$
|
775
|
+
const TokenCacheSchema$2 = z__default.default.discriminatedUnion("isValid", [z__default.default.strictObject({
|
771
776
|
id: chaindataProvider.EvmErc20TokenSchema.shape.id,
|
772
777
|
isValid: z__default.default.literal(true),
|
773
778
|
...chaindataProvider.EvmErc20TokenSchema.pick({
|
@@ -779,7 +784,7 @@ const TokenCacheSchema$1 = z__default.default.discriminatedUnion("isValid", [z__
|
|
779
784
|
id: chaindataProvider.EvmErc20TokenSchema.shape.id,
|
780
785
|
isValid: z__default.default.literal(false)
|
781
786
|
})]);
|
782
|
-
const fetchTokens$
|
787
|
+
const fetchTokens$a = async ({
|
783
788
|
networkId,
|
784
789
|
tokens,
|
785
790
|
connector,
|
@@ -788,7 +793,7 @@ const fetchTokens$8 = async ({
|
|
788
793
|
const result = [];
|
789
794
|
for (const tokenConfig of tokens) {
|
790
795
|
const tokenId = chaindataProvider.evmErc20TokenId(networkId, tokenConfig.contractAddress);
|
791
|
-
const cached = cache[tokenId] && TokenCacheSchema$
|
796
|
+
const cached = cache[tokenId] && TokenCacheSchema$2.safeParse(cache[tokenId]).data;
|
792
797
|
if (!cached) {
|
793
798
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
794
799
|
if (!client) {
|
@@ -801,7 +806,7 @@ const fetchTokens$8 = async ({
|
|
801
806
|
decimals,
|
802
807
|
symbol
|
803
808
|
} = await getErc20ContractData$1(client, tokenConfig.contractAddress);
|
804
|
-
cache[tokenId] = TokenCacheSchema$
|
809
|
+
cache[tokenId] = TokenCacheSchema$2.parse({
|
805
810
|
id: tokenId,
|
806
811
|
symbol,
|
807
812
|
decimals,
|
@@ -819,11 +824,11 @@ const fetchTokens$8 = async ({
|
|
819
824
|
}
|
820
825
|
const base = {
|
821
826
|
id: tokenId,
|
822
|
-
type: MODULE_TYPE$
|
823
|
-
platform: PLATFORM$
|
827
|
+
type: MODULE_TYPE$a,
|
828
|
+
platform: PLATFORM$a,
|
824
829
|
networkId
|
825
830
|
};
|
826
|
-
const cached2 = cache[tokenId] && TokenCacheSchema$
|
831
|
+
const cached2 = cache[tokenId] && TokenCacheSchema$2.safeParse(cache[tokenId]).data;
|
827
832
|
if (cached2?.isValid === false) continue;
|
828
833
|
const token = lodashEs.assign(base, cached2?.isValid ? lodashEs.omit(cached2, ["isValid"]) : {}, tokenConfig);
|
829
834
|
const parsed = chaindataProvider.EvmErc20TokenSchema.safeParse(token);
|
@@ -838,19 +843,19 @@ const fetchTokens$8 = async ({
|
|
838
843
|
return result;
|
839
844
|
};
|
840
845
|
|
841
|
-
const getMiniMetadata$
|
846
|
+
const getMiniMetadata$a = () => {
|
842
847
|
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
843
848
|
};
|
844
849
|
|
845
|
-
const getTransferCallData$
|
850
|
+
const getTransferCallData$a = ({
|
846
851
|
from,
|
847
852
|
to,
|
848
853
|
value,
|
849
854
|
token
|
850
855
|
}) => {
|
851
|
-
if (!chaindataProvider.isTokenOfType(token, MODULE_TYPE$
|
852
|
-
if (!
|
853
|
-
if (!
|
856
|
+
if (!chaindataProvider.isTokenOfType(token, MODULE_TYPE$a)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$a}.`);
|
857
|
+
if (!crypto.isEthereumAddress(from)) throw new Error("Invalid from address");
|
858
|
+
if (!crypto.isEthereumAddress(to)) throw new Error("Invalid to address");
|
854
859
|
const data = viem.encodeFunctionData({
|
855
860
|
abi: viem.erc20Abi,
|
856
861
|
functionName: "transfer",
|
@@ -863,8 +868,8 @@ const getTransferCallData$8 = ({
|
|
863
868
|
};
|
864
869
|
};
|
865
870
|
|
866
|
-
const SUBSCRIPTION_INTERVAL$
|
867
|
-
const subscribeBalances$
|
871
|
+
const SUBSCRIPTION_INTERVAL$6 = 6_000;
|
872
|
+
const subscribeBalances$a = ({
|
868
873
|
networkId,
|
869
874
|
tokensWithAddresses,
|
870
875
|
connector
|
@@ -878,17 +883,17 @@ const subscribeBalances$8 = ({
|
|
878
883
|
const poll = async () => {
|
879
884
|
try {
|
880
885
|
if (abortController.signal.aborted) return;
|
881
|
-
const balances = await fetchBalances$
|
886
|
+
const balances = await fetchBalances$a({
|
882
887
|
networkId,
|
883
888
|
tokensWithAddresses: tokensWithAddresses,
|
884
889
|
connector
|
885
890
|
});
|
886
891
|
if (abortController.signal.aborted) return;
|
887
892
|
subscriber.next(balances);
|
888
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
893
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$6);
|
889
894
|
} catch (error) {
|
890
895
|
log.error("Error", {
|
891
|
-
module: MODULE_TYPE$
|
896
|
+
module: MODULE_TYPE$a,
|
892
897
|
networkId,
|
893
898
|
addressesByToken: tokensWithAddresses,
|
894
899
|
error
|
@@ -904,13 +909,13 @@ const subscribeBalances$8 = ({
|
|
904
909
|
};
|
905
910
|
|
906
911
|
const EvmErc20BalanceModule = {
|
907
|
-
type: MODULE_TYPE$
|
908
|
-
platform: PLATFORM$
|
909
|
-
getMiniMetadata: getMiniMetadata$
|
910
|
-
fetchTokens: fetchTokens$
|
911
|
-
fetchBalances: fetchBalances$
|
912
|
-
subscribeBalances: subscribeBalances$
|
913
|
-
getTransferCallData: getTransferCallData$
|
912
|
+
type: MODULE_TYPE$a,
|
913
|
+
platform: PLATFORM$a,
|
914
|
+
getMiniMetadata: getMiniMetadata$a,
|
915
|
+
fetchTokens: fetchTokens$a,
|
916
|
+
fetchBalances: fetchBalances$a,
|
917
|
+
subscribeBalances: subscribeBalances$a,
|
918
|
+
getTransferCallData: getTransferCallData$a
|
914
919
|
};
|
915
920
|
|
916
921
|
const TokenConfigBaseSchema = chaindataProvider.TokenBaseSchema.partial().omit({
|
@@ -923,10 +928,10 @@ const EvmErc20TokenConfigSchema = z__default.default.strictObject({
|
|
923
928
|
...TokenConfigBaseSchema.shape
|
924
929
|
});
|
925
930
|
|
926
|
-
const MODULE_TYPE$
|
927
|
-
const PLATFORM$
|
931
|
+
const MODULE_TYPE$9 = chaindataProvider.EvmNativeTokenSchema.shape.type.value;
|
932
|
+
const PLATFORM$9 = chaindataProvider.EvmNativeTokenSchema.shape.platform.value;
|
928
933
|
|
929
|
-
const fetchBalances$
|
934
|
+
const fetchBalances$9 = async ({
|
930
935
|
networkId,
|
931
936
|
tokensWithAddresses,
|
932
937
|
connector
|
@@ -938,8 +943,8 @@ const fetchBalances$7 = async ({
|
|
938
943
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
939
944
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
940
945
|
for (const [token, addresses] of tokensWithAddresses) {
|
941
|
-
if (token.type !== MODULE_TYPE$
|
942
|
-
for (const address of addresses) if (!
|
946
|
+
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}`);
|
947
|
+
for (const address of addresses) if (!crypto.isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
943
948
|
}
|
944
949
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
945
950
|
if (client.chain?.contracts?.multicall3 && balanceDefs.length > 1) {
|
@@ -965,7 +970,7 @@ const fetchWithoutMulticall = async (client, balanceDefs) => {
|
|
965
970
|
address,
|
966
971
|
tokenId: token.id,
|
967
972
|
value: result.toString(),
|
968
|
-
source: MODULE_TYPE$
|
973
|
+
source: MODULE_TYPE$9,
|
969
974
|
networkId: chaindataProvider.parseTokenId(token.id).networkId,
|
970
975
|
status: "live"
|
971
976
|
};
|
@@ -1011,7 +1016,7 @@ const fetchWithMulticall = async (client, balanceDefs, multicall3Address) => {
|
|
1011
1016
|
address: balanceDefs[index].address,
|
1012
1017
|
tokenId: balanceDefs[index].token.id,
|
1013
1018
|
value: result.result.toString(),
|
1014
|
-
source: MODULE_TYPE$
|
1019
|
+
source: MODULE_TYPE$9,
|
1015
1020
|
networkId: chaindataProvider.parseTokenId(balanceDefs[index].token.id).networkId,
|
1016
1021
|
status: "live"
|
1017
1022
|
});
|
@@ -1041,7 +1046,7 @@ const fetchWithMulticall = async (client, balanceDefs, multicall3Address) => {
|
|
1041
1046
|
}
|
1042
1047
|
};
|
1043
1048
|
|
1044
|
-
const fetchTokens$
|
1049
|
+
const fetchTokens$9 = async ({
|
1045
1050
|
networkId,
|
1046
1051
|
tokens
|
1047
1052
|
}) => {
|
@@ -1049,8 +1054,8 @@ const fetchTokens$7 = async ({
|
|
1049
1054
|
if (tokens.length !== 1) throw new Error("EVM Native module expects the nativeCurrency to be passed as a single token in the array");
|
1050
1055
|
const token = lodashEs.assign({
|
1051
1056
|
id: chaindataProvider.evmNativeTokenId(networkId),
|
1052
|
-
type: MODULE_TYPE$
|
1053
|
-
platform: PLATFORM$
|
1057
|
+
type: MODULE_TYPE$9,
|
1058
|
+
platform: PLATFORM$9,
|
1054
1059
|
networkId,
|
1055
1060
|
isDefault: true
|
1056
1061
|
}, tokens[0]);
|
@@ -1062,19 +1067,19 @@ const fetchTokens$7 = async ({
|
|
1062
1067
|
return [parsed.data];
|
1063
1068
|
};
|
1064
1069
|
|
1065
|
-
const getMiniMetadata$
|
1070
|
+
const getMiniMetadata$9 = () => {
|
1066
1071
|
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
1067
1072
|
};
|
1068
1073
|
|
1069
|
-
const getTransferCallData$
|
1074
|
+
const getTransferCallData$9 = ({
|
1070
1075
|
from,
|
1071
1076
|
to,
|
1072
1077
|
value,
|
1073
1078
|
token
|
1074
1079
|
}) => {
|
1075
|
-
if (!chaindataProvider.isTokenOfType(token, MODULE_TYPE$
|
1076
|
-
if (!
|
1077
|
-
if (!
|
1080
|
+
if (!chaindataProvider.isTokenOfType(token, MODULE_TYPE$9)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$9}.`);
|
1081
|
+
if (!crypto.isEthereumAddress(from)) throw new Error("Invalid from address");
|
1082
|
+
if (!crypto.isEthereumAddress(to)) throw new Error("Invalid to address");
|
1078
1083
|
return {
|
1079
1084
|
from,
|
1080
1085
|
to,
|
@@ -1083,8 +1088,8 @@ const getTransferCallData$7 = ({
|
|
1083
1088
|
};
|
1084
1089
|
};
|
1085
1090
|
|
1086
|
-
const SUBSCRIPTION_INTERVAL$
|
1087
|
-
const subscribeBalances$
|
1091
|
+
const SUBSCRIPTION_INTERVAL$5 = 6_000;
|
1092
|
+
const subscribeBalances$9 = ({
|
1088
1093
|
networkId,
|
1089
1094
|
tokensWithAddresses,
|
1090
1095
|
connector
|
@@ -1098,17 +1103,17 @@ const subscribeBalances$7 = ({
|
|
1098
1103
|
const poll = async () => {
|
1099
1104
|
try {
|
1100
1105
|
if (abortController.signal.aborted) return;
|
1101
|
-
const balances = await fetchBalances$
|
1106
|
+
const balances = await fetchBalances$9({
|
1102
1107
|
networkId,
|
1103
1108
|
tokensWithAddresses: tokensWithAddresses,
|
1104
1109
|
connector
|
1105
1110
|
});
|
1106
1111
|
if (abortController.signal.aborted) return;
|
1107
1112
|
subscriber.next(balances);
|
1108
|
-
setTimeout(poll, SUBSCRIPTION_INTERVAL$
|
1113
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$5);
|
1109
1114
|
} catch (error) {
|
1110
1115
|
log.error("Error", {
|
1111
|
-
module: MODULE_TYPE$
|
1116
|
+
module: MODULE_TYPE$9,
|
1112
1117
|
networkId,
|
1113
1118
|
addressesByToken: tokensWithAddresses,
|
1114
1119
|
error
|
@@ -1124,13 +1129,13 @@ const subscribeBalances$7 = ({
|
|
1124
1129
|
};
|
1125
1130
|
|
1126
1131
|
const EvmNativeBalanceModule = {
|
1127
|
-
type: MODULE_TYPE$
|
1128
|
-
platform: PLATFORM$
|
1129
|
-
getMiniMetadata: getMiniMetadata$
|
1130
|
-
fetchTokens: fetchTokens$
|
1131
|
-
fetchBalances: fetchBalances$
|
1132
|
-
subscribeBalances: subscribeBalances$
|
1133
|
-
getTransferCallData: getTransferCallData$
|
1132
|
+
type: MODULE_TYPE$9,
|
1133
|
+
platform: PLATFORM$9,
|
1134
|
+
getMiniMetadata: getMiniMetadata$9,
|
1135
|
+
fetchTokens: fetchTokens$9,
|
1136
|
+
fetchBalances: fetchBalances$9,
|
1137
|
+
subscribeBalances: subscribeBalances$9,
|
1138
|
+
getTransferCallData: getTransferCallData$9
|
1134
1139
|
};
|
1135
1140
|
|
1136
1141
|
// to be used by chaindata too
|
@@ -1138,10 +1143,10 @@ const EvmNativeTokenConfigSchema = z__default.default.strictObject({
|
|
1138
1143
|
...TokenConfigBaseSchema.shape
|
1139
1144
|
});
|
1140
1145
|
|
1141
|
-
const MODULE_TYPE$
|
1142
|
-
const PLATFORM$
|
1146
|
+
const MODULE_TYPE$8 = chaindataProvider.EvmUniswapV2TokenSchema.shape.type.value;
|
1147
|
+
const PLATFORM$8 = chaindataProvider.EvmUniswapV2TokenSchema.shape.platform.value;
|
1143
1148
|
|
1144
|
-
const fetchBalances$
|
1149
|
+
const fetchBalances$8 = async ({
|
1145
1150
|
networkId,
|
1146
1151
|
tokensWithAddresses,
|
1147
1152
|
connector
|
@@ -1153,8 +1158,8 @@ const fetchBalances$6 = async ({
|
|
1153
1158
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1154
1159
|
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
1155
1160
|
for (const [token, addresses] of tokensWithAddresses) {
|
1156
|
-
if (token.type !== MODULE_TYPE$
|
1157
|
-
for (const address of addresses) if (!
|
1161
|
+
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}`);
|
1162
|
+
for (const address of addresses) if (!crypto.isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
1158
1163
|
}
|
1159
1164
|
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
1160
1165
|
return fetchPoolBalances(client, balanceDefs);
|
@@ -1240,7 +1245,7 @@ const fetchPoolBalances = async (client, balanceDefs) => {
|
|
1240
1245
|
acc.success.push({
|
1241
1246
|
address,
|
1242
1247
|
tokenId: token.id,
|
1243
|
-
source: MODULE_TYPE$
|
1248
|
+
source: MODULE_TYPE$8,
|
1244
1249
|
networkId: chaindataProvider.parseTokenId(token.id).networkId,
|
1245
1250
|
status: "live",
|
1246
1251
|
values: [{
|
@@ -1300,7 +1305,7 @@ const getUniswapV2PairContractData = async (client, contractAddress) => {
|
|
1300
1305
|
};
|
1301
1306
|
};
|
1302
1307
|
|
1303
|
-
const TokenCacheSchema = z__default.default.discriminatedUnion("isValid", [z__default.default.strictObject({
|
1308
|
+
const TokenCacheSchema$1 = z__default.default.discriminatedUnion("isValid", [z__default.default.strictObject({
|
1304
1309
|
id: chaindataProvider.EvmUniswapV2TokenSchema.shape.id,
|
1305
1310
|
isValid: z__default.default.literal(true),
|
1306
1311
|
...chaindataProvider.EvmUniswapV2TokenSchema.pick({
|
@@ -1318,7 +1323,7 @@ const TokenCacheSchema = z__default.default.discriminatedUnion("isValid", [z__de
|
|
1318
1323
|
id: chaindataProvider.EvmUniswapV2TokenSchema.shape.id,
|
1319
1324
|
isValid: z__default.default.literal(false)
|
1320
1325
|
})]);
|
1321
|
-
const fetchTokens$
|
1326
|
+
const fetchTokens$8 = async ({
|
1322
1327
|
networkId,
|
1323
1328
|
tokens,
|
1324
1329
|
connector,
|
@@ -1327,7 +1332,7 @@ const fetchTokens$6 = async ({
|
|
1327
1332
|
const result = [];
|
1328
1333
|
for (const tokenConfig of tokens) {
|
1329
1334
|
const tokenId = chaindataProvider.evmUniswapV2TokenId(networkId, tokenConfig.contractAddress);
|
1330
|
-
const cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1335
|
+
const cached = cache[tokenId] && TokenCacheSchema$1.safeParse(cache[tokenId]).data;
|
1331
1336
|
if (!cached) {
|
1332
1337
|
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
1333
1338
|
if (!client) {
|
@@ -1349,7 +1354,7 @@ const fetchTokens$6 = async ({
|
|
1349
1354
|
symbol: symbol1,
|
1350
1355
|
decimals: decimals1
|
1351
1356
|
} = await getErc20ContractData(client, token1);
|
1352
|
-
cache[tokenId] = TokenCacheSchema.parse({
|
1357
|
+
cache[tokenId] = TokenCacheSchema$1.parse({
|
1353
1358
|
id: tokenId,
|
1354
1359
|
symbol: `${symbol0}/${symbol1}`,
|
1355
1360
|
decimals,
|
@@ -1375,48 +1380,615 @@ const fetchTokens$6 = async ({
|
|
1375
1380
|
continue;
|
1376
1381
|
}
|
1377
1382
|
}
|
1383
|
+
const base = {
|
1384
|
+
id: tokenId,
|
1385
|
+
type: MODULE_TYPE$8,
|
1386
|
+
platform: PLATFORM$8,
|
1387
|
+
networkId
|
1388
|
+
};
|
1389
|
+
const cached2 = cache[tokenId] && TokenCacheSchema$1.safeParse(cache[tokenId]).data;
|
1390
|
+
if (cached2?.isValid === false) continue;
|
1391
|
+
const token = lodashEs.assign(base, cached2?.isValid ? lodashEs.omit(cached2, ["isValid"]) : {}, tokenConfig);
|
1392
|
+
const parsed = chaindataProvider.EvmUniswapV2TokenSchema.safeParse(token);
|
1393
|
+
if (!parsed.success) {
|
1394
|
+
log.warn("Ignoring token with invalid schema", token);
|
1395
|
+
continue;
|
1396
|
+
}
|
1397
|
+
result.push(parsed.data);
|
1398
|
+
}
|
1399
|
+
return result;
|
1400
|
+
};
|
1401
|
+
|
1402
|
+
const getMiniMetadata$8 = () => {
|
1403
|
+
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
1404
|
+
};
|
1405
|
+
|
1406
|
+
const getTransferCallData$8 = ({
|
1407
|
+
from,
|
1408
|
+
to,
|
1409
|
+
value,
|
1410
|
+
token
|
1411
|
+
}) => {
|
1412
|
+
if (!chaindataProvider.isTokenOfType(token, MODULE_TYPE$8)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$8}.`);
|
1413
|
+
if (!crypto.isEthereumAddress(from)) throw new Error("Invalid from address");
|
1414
|
+
if (!crypto.isEthereumAddress(to)) throw new Error("Invalid to address");
|
1415
|
+
const data = viem.encodeFunctionData({
|
1416
|
+
abi: viem.erc20Abi,
|
1417
|
+
functionName: "transfer",
|
1418
|
+
args: [to, BigInt(value)]
|
1419
|
+
});
|
1420
|
+
return {
|
1421
|
+
from,
|
1422
|
+
to: token.contractAddress,
|
1423
|
+
data
|
1424
|
+
};
|
1425
|
+
};
|
1426
|
+
|
1427
|
+
const SUBSCRIPTION_INTERVAL$4 = 6_000;
|
1428
|
+
const subscribeBalances$8 = ({
|
1429
|
+
networkId,
|
1430
|
+
tokensWithAddresses,
|
1431
|
+
connector
|
1432
|
+
}) => {
|
1433
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
1434
|
+
success: [],
|
1435
|
+
errors: []
|
1436
|
+
});
|
1437
|
+
return new rxjs.Observable(subscriber => {
|
1438
|
+
const abortController = new AbortController();
|
1439
|
+
const poll = async () => {
|
1440
|
+
try {
|
1441
|
+
if (abortController.signal.aborted) return;
|
1442
|
+
const balances = await fetchBalances$8({
|
1443
|
+
networkId,
|
1444
|
+
tokensWithAddresses: tokensWithAddresses,
|
1445
|
+
connector
|
1446
|
+
});
|
1447
|
+
if (abortController.signal.aborted) return;
|
1448
|
+
subscriber.next(balances);
|
1449
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
1450
|
+
} catch (error) {
|
1451
|
+
log.error("Error", {
|
1452
|
+
module: MODULE_TYPE$8,
|
1453
|
+
networkId,
|
1454
|
+
addressesByToken: tokensWithAddresses,
|
1455
|
+
error
|
1456
|
+
});
|
1457
|
+
subscriber.error(error);
|
1458
|
+
}
|
1459
|
+
};
|
1460
|
+
poll();
|
1461
|
+
return () => {
|
1462
|
+
abortController.abort();
|
1463
|
+
};
|
1464
|
+
}).pipe(rxjs.distinctUntilChanged(lodashEs.isEqual));
|
1465
|
+
};
|
1466
|
+
|
1467
|
+
const EvmUniswapV2BalanceModule = {
|
1468
|
+
type: MODULE_TYPE$8,
|
1469
|
+
platform: PLATFORM$8,
|
1470
|
+
getMiniMetadata: getMiniMetadata$8,
|
1471
|
+
fetchTokens: fetchTokens$8,
|
1472
|
+
fetchBalances: fetchBalances$8,
|
1473
|
+
subscribeBalances: subscribeBalances$8,
|
1474
|
+
getTransferCallData: getTransferCallData$8
|
1475
|
+
};
|
1476
|
+
|
1477
|
+
// to be used by chaindata too
|
1478
|
+
const EvmUniswapV2TokenConfigSchema = z__default.default.strictObject({
|
1479
|
+
contractAddress: chaindataProvider.EvmUniswapV2TokenSchema.shape.contractAddress,
|
1480
|
+
...TokenConfigBaseSchema.shape
|
1481
|
+
});
|
1482
|
+
|
1483
|
+
const MODULE_TYPE$7 = chaindataProvider.SolNativeTokenSchema.shape.type.value;
|
1484
|
+
const PLATFORM$7 = chaindataProvider.SolNativeTokenSchema.shape.platform.value;
|
1485
|
+
|
1486
|
+
const fetchBalances$7 = async ({
|
1487
|
+
networkId,
|
1488
|
+
tokensWithAddresses,
|
1489
|
+
connector
|
1490
|
+
}) => {
|
1491
|
+
if (!tokensWithAddresses.length) return {
|
1492
|
+
success: [],
|
1493
|
+
errors: []
|
1494
|
+
};
|
1495
|
+
const connection = await connector.getConnection(networkId);
|
1496
|
+
if (!connection) throw new Error(`Could not get rpc provider for sol network ${networkId}`);
|
1497
|
+
for (const [token, addresses] of tokensWithAddresses) {
|
1498
|
+
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}`);
|
1499
|
+
for (const address of addresses) if (!crypto.isSolanaAddress(address)) throw new Error(`Invalid solana address for balance module: ${address} for token ${token.id}`);
|
1500
|
+
}
|
1501
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
1502
|
+
const results = await Promise.allSettled(balanceDefs.map(async ({
|
1503
|
+
token,
|
1504
|
+
address
|
1505
|
+
}) => {
|
1506
|
+
try {
|
1507
|
+
const publicKey = new web3_js.PublicKey(address);
|
1508
|
+
const lamports = await connection.getBalance(publicKey);
|
1509
|
+
return {
|
1510
|
+
address: address,
|
1511
|
+
tokenId: token.id,
|
1512
|
+
value: lamports.toString(),
|
1513
|
+
source: MODULE_TYPE$7,
|
1514
|
+
networkId: token.networkId,
|
1515
|
+
status: "live"
|
1516
|
+
};
|
1517
|
+
} catch (err) {
|
1518
|
+
throw new BalanceFetchError(`Failed to get balance for token ${token.id} and address ${address} on chain ${networkId}`, token.id, address, err);
|
1519
|
+
}
|
1520
|
+
}));
|
1521
|
+
return results.reduce((acc, result) => {
|
1522
|
+
if (result.status === "fulfilled") acc.success.push(result.value);else {
|
1523
|
+
const error = result.reason;
|
1524
|
+
acc.errors.push({
|
1525
|
+
tokenId: error.tokenId,
|
1526
|
+
address: error.address,
|
1527
|
+
error
|
1528
|
+
});
|
1529
|
+
}
|
1530
|
+
return acc;
|
1531
|
+
}, {
|
1532
|
+
success: [],
|
1533
|
+
errors: []
|
1534
|
+
});
|
1535
|
+
};
|
1536
|
+
|
1537
|
+
const fetchTokens$7 = async ({
|
1538
|
+
networkId,
|
1539
|
+
tokens
|
1540
|
+
}) => {
|
1541
|
+
// assume there is one and only one token in the array
|
1542
|
+
if (tokens.length !== 1) throw new Error("EVM Native module expects the nativeCurrency to be passed as a single token in the array");
|
1543
|
+
const token = lodashEs.assign({
|
1544
|
+
id: chaindataProvider.solNativeTokenId(networkId),
|
1545
|
+
type: MODULE_TYPE$7,
|
1546
|
+
platform: PLATFORM$7,
|
1547
|
+
networkId,
|
1548
|
+
isDefault: true
|
1549
|
+
}, tokens[0]);
|
1550
|
+
const parsed = chaindataProvider.SolNativeTokenSchema.safeParse(token);
|
1551
|
+
if (!parsed.success) {
|
1552
|
+
log.warn("Ignoring token with invalid schema", token);
|
1553
|
+
return [];
|
1554
|
+
}
|
1555
|
+
return [parsed.data];
|
1556
|
+
};
|
1557
|
+
|
1558
|
+
const getMiniMetadata$7 = () => {
|
1559
|
+
throw new Error("MiniMetadata is not supported for sol-native tokens");
|
1560
|
+
};
|
1561
|
+
|
1562
|
+
const getTransferCallData$7 = ({
|
1563
|
+
from,
|
1564
|
+
to,
|
1565
|
+
value,
|
1566
|
+
token
|
1567
|
+
}) => {
|
1568
|
+
if (!chaindataProvider.isTokenOfType(token, MODULE_TYPE$7)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$7}.`);
|
1569
|
+
const fromPubkey = new web3_js.PublicKey(from);
|
1570
|
+
const transferIx = web3_js.SystemProgram.transfer({
|
1571
|
+
fromPubkey,
|
1572
|
+
toPubkey: new web3_js.PublicKey(to),
|
1573
|
+
lamports: Number(value)
|
1574
|
+
});
|
1575
|
+
return [transferIx];
|
1576
|
+
};
|
1577
|
+
|
1578
|
+
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
1579
|
+
const subscribeBalances$7 = ({
|
1580
|
+
networkId,
|
1581
|
+
tokensWithAddresses,
|
1582
|
+
connector
|
1583
|
+
}) => {
|
1584
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
1585
|
+
success: [],
|
1586
|
+
errors: []
|
1587
|
+
});
|
1588
|
+
return new rxjs.Observable(subscriber => {
|
1589
|
+
const abortController = new AbortController();
|
1590
|
+
const poll = async () => {
|
1591
|
+
try {
|
1592
|
+
if (abortController.signal.aborted) return;
|
1593
|
+
const balances = await fetchBalances$7({
|
1594
|
+
networkId,
|
1595
|
+
tokensWithAddresses,
|
1596
|
+
connector
|
1597
|
+
});
|
1598
|
+
if (abortController.signal.aborted) return;
|
1599
|
+
subscriber.next(balances);
|
1600
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
1601
|
+
} catch (error) {
|
1602
|
+
log.error("Error", {
|
1603
|
+
module: MODULE_TYPE$7,
|
1604
|
+
networkId,
|
1605
|
+
tokensWithAddresses,
|
1606
|
+
error
|
1607
|
+
});
|
1608
|
+
subscriber.error(error);
|
1609
|
+
}
|
1610
|
+
};
|
1611
|
+
poll();
|
1612
|
+
return () => {
|
1613
|
+
abortController.abort();
|
1614
|
+
};
|
1615
|
+
}).pipe(rxjs.distinctUntilChanged(lodashEs.isEqual));
|
1616
|
+
};
|
1617
|
+
|
1618
|
+
const SolNativeBalanceModule = {
|
1619
|
+
type: MODULE_TYPE$7,
|
1620
|
+
platform: PLATFORM$7,
|
1621
|
+
getMiniMetadata: getMiniMetadata$7,
|
1622
|
+
fetchTokens: fetchTokens$7,
|
1623
|
+
fetchBalances: fetchBalances$7,
|
1624
|
+
subscribeBalances: subscribeBalances$7,
|
1625
|
+
getTransferCallData: getTransferCallData$7
|
1626
|
+
};
|
1627
|
+
|
1628
|
+
// to be used by chaindata too
|
1629
|
+
const SolNativeTokenConfigSchema = z__default.default.strictObject({
|
1630
|
+
...TokenConfigBaseSchema.shape
|
1631
|
+
});
|
1632
|
+
|
1633
|
+
const MODULE_TYPE$6 = chaindataProvider.SolSplTokenSchema.shape.type.value;
|
1634
|
+
const PLATFORM$6 = chaindataProvider.SolSplTokenSchema.shape.platform.value;
|
1635
|
+
|
1636
|
+
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
1637
|
+
const {
|
1638
|
+
builder
|
1639
|
+
} = scale.parseMetadataRpc(metadataRpc);
|
1640
|
+
const call = builder.buildRuntimeCall(apiName, method);
|
1641
|
+
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, scale.toHex(call.args.enc(args))]);
|
1642
|
+
return call.value.dec(hex);
|
1643
|
+
};
|
1644
|
+
|
1645
|
+
const hasStorageItem = (metadata, palletName, itemName) => {
|
1646
|
+
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1647
|
+
if (!pallet || !pallet.storage) return false;
|
1648
|
+
return pallet.storage.items.some(item => item.name === itemName);
|
1649
|
+
};
|
1650
|
+
const hasStorageItems = (metadata, palletName, itemNames) => {
|
1651
|
+
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1652
|
+
if (!pallet || !pallet.storage) return false;
|
1653
|
+
return itemNames.every(itemName => pallet.storage?.items.some(item => item.name === itemName));
|
1654
|
+
};
|
1655
|
+
const hasRuntimeApi = (metadata, apiName, method) => {
|
1656
|
+
const api = metadata.apis.find(api => api.name === apiName);
|
1657
|
+
if (!api || !api.methods) return false;
|
1658
|
+
return api.methods.some(m => m.name === method);
|
1659
|
+
};
|
1660
|
+
const getConstantValue = (metadataRpc, pallet, constant) => {
|
1661
|
+
const {
|
1662
|
+
unifiedMetadata,
|
1663
|
+
builder
|
1664
|
+
} = scale.parseMetadataRpc(metadataRpc);
|
1665
|
+
const codec = builder.buildConstant(pallet, constant);
|
1666
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
1667
|
+
name
|
1668
|
+
}) => name === pallet)?.constants.find(({
|
1669
|
+
name
|
1670
|
+
}) => name === constant)?.value;
|
1671
|
+
if (!encodedValue) throw new Error(`Constant ${pallet}.${constant} not found`);
|
1672
|
+
return codec.dec(encodedValue);
|
1673
|
+
};
|
1674
|
+
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
1675
|
+
const {
|
1676
|
+
unifiedMetadata,
|
1677
|
+
builder
|
1678
|
+
} = scale.parseMetadataRpc(metadataRpc);
|
1679
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
1680
|
+
name
|
1681
|
+
}) => name === pallet)?.constants.find(({
|
1682
|
+
name
|
1683
|
+
}) => name === constant)?.value;
|
1684
|
+
if (!encodedValue) return null;
|
1685
|
+
const codec = builder.buildConstant(pallet, constant);
|
1686
|
+
return codec.dec(encodedValue);
|
1687
|
+
};
|
1688
|
+
|
1689
|
+
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
1690
|
+
const allStateKeys = queries.flatMap(({
|
1691
|
+
stateKeys
|
1692
|
+
}) => stateKeys).filter(util.isNotNil);
|
1693
|
+
|
1694
|
+
// doing a query without keys would throw an error => return early
|
1695
|
+
if (!allStateKeys.length) return queries.map(({
|
1696
|
+
stateKeys,
|
1697
|
+
decodeResult
|
1698
|
+
}) => decodeResult(stateKeys.map(() => null)));
|
1699
|
+
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
1700
|
+
return decodeRpcQueryPack(queries, result);
|
1701
|
+
};
|
1702
|
+
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
1703
|
+
const allStateKeys = queries.flatMap(({
|
1704
|
+
stateKeys
|
1705
|
+
}) => stateKeys).filter(util.isNotNil);
|
1706
|
+
|
1707
|
+
// doing a query without keys would throw an error => return early
|
1708
|
+
if (!allStateKeys.length) return rxjs.of(queries.map(({
|
1709
|
+
stateKeys,
|
1710
|
+
decodeResult
|
1711
|
+
}) => decodeResult(stateKeys.map(() => null))));
|
1712
|
+
return new rxjs.Observable(subscriber => {
|
1713
|
+
// first subscription callback includes results for all state keys, but further callbacks will only include the ones that changed
|
1714
|
+
// => we need to keep all results in memory and update them after each callback, so we can emit the full result set each time
|
1715
|
+
const changesCache = {};
|
1716
|
+
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
1717
|
+
if (error) subscriber.error(error);else if (result) {
|
1718
|
+
// update the cache
|
1719
|
+
for (const [stateKey, encodedResult] of result.changes) changesCache[stateKey] = encodedResult;
|
1720
|
+
|
1721
|
+
// regenerate the full changes array
|
1722
|
+
const changes = lodashEs.toPairs(changesCache);
|
1723
|
+
|
1724
|
+
// decode and emit results for all queries
|
1725
|
+
subscriber.next(decodeRpcQueryPack(queries, {
|
1726
|
+
block: result.block,
|
1727
|
+
changes
|
1728
|
+
}));
|
1729
|
+
}
|
1730
|
+
}, timeout);
|
1731
|
+
return () => {
|
1732
|
+
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
1733
|
+
};
|
1734
|
+
});
|
1735
|
+
};
|
1736
|
+
const decodeRpcQueryPack = (queries, result) => {
|
1737
|
+
return queries.reduce((acc, {
|
1738
|
+
stateKeys,
|
1739
|
+
decodeResult
|
1740
|
+
}) => {
|
1741
|
+
const changes = stateKeys.map(stateKey => {
|
1742
|
+
if (!stateKey || !result) return null;
|
1743
|
+
const change = result.changes.find(([key]) => key === stateKey);
|
1744
|
+
if (!change) return null;
|
1745
|
+
return change[1];
|
1746
|
+
});
|
1747
|
+
acc.push(decodeResult(changes));
|
1748
|
+
return acc;
|
1749
|
+
}, []);
|
1750
|
+
};
|
1751
|
+
|
1752
|
+
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
1753
|
+
if (!miniMetadata.data) return null;
|
1754
|
+
const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
|
1755
|
+
try {
|
1756
|
+
const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
|
1757
|
+
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
1758
|
+
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
1759
|
+
chainId
|
1760
|
+
}) : moduleMethodOrFn;
|
1761
|
+
try {
|
1762
|
+
return [[key, scaleBuilder.buildStorage(module, method)]];
|
1763
|
+
} catch (cause) {
|
1764
|
+
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
1765
|
+
return [];
|
1766
|
+
}
|
1767
|
+
}));
|
1768
|
+
return builtCoders;
|
1769
|
+
} catch (cause) {
|
1770
|
+
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
1771
|
+
}
|
1772
|
+
return null;
|
1773
|
+
};
|
1774
|
+
|
1775
|
+
const tokenIdsByAddress = new rxjs.BehaviorSubject({});
|
1776
|
+
const setDetectedTokenIds = (address, type, tokenIds) => {
|
1777
|
+
// keep token ids from other token types (will be useful once we add token2022)
|
1778
|
+
const otherTokens = tokenIdsByAddress.value[address]?.filter(id => chaindataProvider.parseTokenId(id).type !== type) ?? [];
|
1779
|
+
tokenIdsByAddress.next({
|
1780
|
+
...tokenIdsByAddress.value,
|
1781
|
+
[address]: otherTokens.concat(tokenIds).sort()
|
1782
|
+
});
|
1783
|
+
};
|
1784
|
+
const getDetectedTokensIds$ = address => tokenIdsByAddress.pipe(rxjs.map(ownedTokens => ownedTokens[address] ?? []), rxjs.distinctUntilChanged(lodashEs.isEqual));
|
1785
|
+
|
1786
|
+
const SPL_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
1787
|
+
const fetchBalances$6 = async ({
|
1788
|
+
networkId,
|
1789
|
+
tokensWithAddresses,
|
1790
|
+
connector
|
1791
|
+
}) => {
|
1792
|
+
if (!tokensWithAddresses.length) return {
|
1793
|
+
success: [],
|
1794
|
+
errors: []
|
1795
|
+
};
|
1796
|
+
const connection = await connector.getConnection(networkId);
|
1797
|
+
if (!connection) throw new Error(`Could not get connection for Solana network ${networkId}`);
|
1798
|
+
const accountAddresses = lodashEs.uniq(tokensWithAddresses.flatMap(([, addresses]) => addresses));
|
1799
|
+
const balancesPerAddress = await Promise.all(accountAddresses.map(async address => {
|
1800
|
+
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(new web3_js.PublicKey(address), {
|
1801
|
+
programId: new web3_js.PublicKey(SPL_PROGRAM_ID) // SPL Token Program ID
|
1802
|
+
});
|
1803
|
+
const balances = tokenAccounts.value.map(d => {
|
1804
|
+
try {
|
1805
|
+
const mintAddress = d.account.data.parsed.info.mint;
|
1806
|
+
const value = d.account.data.parsed.info.tokenAmount.amount ?? "0";
|
1807
|
+
return {
|
1808
|
+
tokenId: chaindataProvider.solSplTokenId(networkId, mintAddress),
|
1809
|
+
networkId,
|
1810
|
+
address,
|
1811
|
+
source: MODULE_TYPE$6,
|
1812
|
+
status: "live",
|
1813
|
+
value
|
1814
|
+
};
|
1815
|
+
} catch (err) {
|
1816
|
+
log.warn("Failed to parse token amount", {
|
1817
|
+
address,
|
1818
|
+
d
|
1819
|
+
});
|
1820
|
+
return null;
|
1821
|
+
}
|
1822
|
+
}).filter(util.isNotNil);
|
1823
|
+
|
1824
|
+
// allows the wallet to detect new tokens, and enable them automatically
|
1825
|
+
setDetectedTokenIds(address, MODULE_TYPE$6, balances.map(b => b.tokenId));
|
1826
|
+
return [address, balances];
|
1827
|
+
}));
|
1828
|
+
const allBalancesByKey = lodashEs.keyBy(balancesPerAddress.flatMap(([, addressBalances]) => addressBalances), b => getBalanceKey(b.tokenId, b.address));
|
1829
|
+
const balanceDefs = getBalanceDefs(tokensWithAddresses);
|
1830
|
+
|
1831
|
+
// return a balance entry for all token/address pairs that were requested
|
1832
|
+
const success = balanceDefs.map(bd => {
|
1833
|
+
const found = allBalancesByKey[getBalanceKey(bd.token.id, bd.address)];
|
1834
|
+
return found ?? {
|
1835
|
+
tokenId: bd.token.id,
|
1836
|
+
networkId: bd.token.networkId,
|
1837
|
+
address: bd.address,
|
1838
|
+
source: MODULE_TYPE$6,
|
1839
|
+
status: "live",
|
1840
|
+
value: "0"
|
1841
|
+
};
|
1842
|
+
});
|
1843
|
+
|
1844
|
+
// return only the balances that match the tokens we are interested in
|
1845
|
+
return {
|
1846
|
+
success,
|
1847
|
+
errors: []
|
1848
|
+
}; // TODO output errors if any
|
1849
|
+
};
|
1850
|
+
const getBalanceKey = (tokenId, address) => `${tokenId}:${address}`;
|
1851
|
+
|
1852
|
+
const TokenCacheSchema = z__default.default.discriminatedUnion("isValid", [z__default.default.strictObject({
|
1853
|
+
id: chaindataProvider.SolSplTokenSchema.shape.id,
|
1854
|
+
isValid: z__default.default.literal(true),
|
1855
|
+
...chaindataProvider.SolSplTokenSchema.pick({
|
1856
|
+
symbol: true,
|
1857
|
+
decimals: true,
|
1858
|
+
name: true,
|
1859
|
+
logo: true
|
1860
|
+
}).shape
|
1861
|
+
}), z__default.default.strictObject({
|
1862
|
+
id: chaindataProvider.SolSplTokenSchema.shape.id,
|
1863
|
+
isValid: z__default.default.literal(false)
|
1864
|
+
})]);
|
1865
|
+
const METAPLEX_PROGRAM_ID = new web3_js.PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
|
1866
|
+
const fetchTokens$6 = async ({
|
1867
|
+
networkId,
|
1868
|
+
tokens,
|
1869
|
+
connector,
|
1870
|
+
cache
|
1871
|
+
}) => {
|
1872
|
+
const result = [];
|
1873
|
+
for (const tokenConfig of tokens) {
|
1874
|
+
const tokenId = chaindataProvider.solSplTokenId(networkId, tokenConfig.mintAddress);
|
1875
|
+
let cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1876
|
+
if (!cached) {
|
1877
|
+
const tokenInfo = await fetchOnChainTokenData(connector, tokenId);
|
1878
|
+
if (tokenInfo) cache[tokenId] = tokenInfo;
|
1879
|
+
}
|
1880
|
+
cached = cache[tokenId] && TokenCacheSchema.safeParse(cache[tokenId]).data;
|
1881
|
+
if (cached?.isValid === false) continue;
|
1378
1882
|
const base = {
|
1379
1883
|
id: tokenId,
|
1380
1884
|
type: MODULE_TYPE$6,
|
1381
1885
|
platform: PLATFORM$6,
|
1382
1886
|
networkId
|
1383
1887
|
};
|
1384
|
-
const
|
1385
|
-
|
1386
|
-
const token = lodashEs.assign(base, cached2?.isValid ? lodashEs.omit(cached2, ["isValid"]) : {}, tokenConfig);
|
1387
|
-
const parsed = chaindataProvider.EvmUniswapV2TokenSchema.safeParse(token);
|
1888
|
+
const token = lodashEs.assign(base, cached?.isValid ? lodashEs.omit(cached, ["isValid"]) : {}, tokenConfig);
|
1889
|
+
const parsed = chaindataProvider.SolSplTokenSchema.safeParse(token);
|
1388
1890
|
if (!parsed.success) {
|
1389
|
-
log.warn("Ignoring token with invalid
|
1891
|
+
log.warn("Ignoring token with invalid SolSplTokenSchema", {
|
1892
|
+
token
|
1893
|
+
});
|
1390
1894
|
continue;
|
1391
1895
|
}
|
1392
1896
|
result.push(parsed.data);
|
1393
1897
|
}
|
1394
1898
|
return result;
|
1395
1899
|
};
|
1900
|
+
const ERROR_NO_MINT = "No mint info available";
|
1901
|
+
const ERROR_NO_METADATA = "No metadata account found";
|
1902
|
+
const ERROR_INVALID_DATA = "Invalid on-chain data";
|
1903
|
+
const fetchOnChainTokenData = async (connector, tokenId) => {
|
1904
|
+
try {
|
1905
|
+
const {
|
1906
|
+
networkId,
|
1907
|
+
mintAddress
|
1908
|
+
} = chaindataProvider.parseSolSplTokenId(tokenId);
|
1909
|
+
const connection = await connector.getConnection(networkId);
|
1910
|
+
if (!connection) {
|
1911
|
+
log.warn(`No connection found for network ${networkId}`);
|
1912
|
+
return null;
|
1913
|
+
}
|
1914
|
+
const mintPubKey = new web3_js.PublicKey(mintAddress);
|
1915
|
+
const mintInfo = await connection.getAccountInfo(mintPubKey);
|
1916
|
+
if (!mintInfo?.data) throw new Error(ERROR_NO_MINT);
|
1917
|
+
const mint = splToken.MintLayout.decode(mintInfo.data);
|
1918
|
+
const [metadataPDA] = web3_js.PublicKey.findProgramAddressSync([Buffer.from("metadata"), METAPLEX_PROGRAM_ID.toBuffer(), mintPubKey.toBuffer()], METAPLEX_PROGRAM_ID);
|
1919
|
+
|
1920
|
+
// 3. Fetch metadata account directly (traditional way)
|
1921
|
+
const metadataAccount = await connection.getAccountInfo(new web3_js.PublicKey(metadataPDA));
|
1922
|
+
if (!metadataAccount) throw new Error(ERROR_NO_METADATA);
|
1923
|
+
const metadata = mplTokenMetadata.deserializeMetadata({
|
1924
|
+
publicKey: umi.publicKey(metadataPDA),
|
1925
|
+
executable: metadataAccount.executable,
|
1926
|
+
owner: umi.publicKey(metadataAccount.owner),
|
1927
|
+
lamports: umi.sol(metadataAccount.lamports),
|
1928
|
+
data: metadataAccount.data
|
1929
|
+
});
|
1930
|
+
const parsed = TokenCacheSchema.safeParse({
|
1931
|
+
id: tokenId,
|
1932
|
+
symbol: metadata.symbol.trim(),
|
1933
|
+
name: metadata.name.trim(),
|
1934
|
+
decimals: mint.decimals,
|
1935
|
+
isValid: true
|
1936
|
+
});
|
1937
|
+
if (!parsed.success) throw new Error(ERROR_INVALID_DATA);
|
1938
|
+
return parsed.data;
|
1939
|
+
} catch (err) {
|
1940
|
+
const msg = err.message;
|
1941
|
+
if ([ERROR_NO_MINT, ERROR_NO_METADATA, ERROR_INVALID_DATA].includes(msg)) return TokenCacheSchema.parse({
|
1942
|
+
id: tokenId,
|
1943
|
+
isValid: false
|
1944
|
+
});
|
1945
|
+
log.warn("Failed to fetch sol-spl token data for %s", tokenId, {
|
1946
|
+
err
|
1947
|
+
});
|
1948
|
+
}
|
1949
|
+
return null;
|
1950
|
+
};
|
1396
1951
|
|
1397
1952
|
const getMiniMetadata$6 = () => {
|
1398
|
-
throw new Error("MiniMetadata is not supported for
|
1953
|
+
throw new Error("MiniMetadata is not supported for solana tokens");
|
1399
1954
|
};
|
1400
1955
|
|
1401
|
-
const getTransferCallData$6 = ({
|
1956
|
+
const getTransferCallData$6 = async ({
|
1402
1957
|
from,
|
1403
1958
|
to,
|
1404
1959
|
value,
|
1405
|
-
token
|
1960
|
+
token,
|
1961
|
+
connector
|
1406
1962
|
}) => {
|
1407
1963
|
if (!chaindataProvider.isTokenOfType(token, MODULE_TYPE$6)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$6}.`);
|
1408
|
-
|
1409
|
-
|
1410
|
-
const
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1964
|
+
const connection = await connector.getConnection(token.networkId);
|
1965
|
+
const instructions = [];
|
1966
|
+
const mint = new web3_js.PublicKey(token.mintAddress);
|
1967
|
+
const fromWallet = new web3_js.PublicKey(from);
|
1968
|
+
const toWallet = new web3_js.PublicKey(to);
|
1969
|
+
|
1970
|
+
// Get associated token accounts
|
1971
|
+
const fromTokenAccount = await splToken.getAssociatedTokenAddress(mint, fromWallet);
|
1972
|
+
const toTokenAccount = await splToken.getAssociatedTokenAddress(mint, toWallet);
|
1973
|
+
|
1974
|
+
// Create the target token account if it doesn't exist
|
1975
|
+
if (!(await tokenAccountExists(connection, toTokenAccount))) {
|
1976
|
+
instructions.push(splToken.createAssociatedTokenAccountInstruction(fromWallet,
|
1977
|
+
// funder
|
1978
|
+
toTokenAccount, toWallet, mint));
|
1979
|
+
}
|
1980
|
+
|
1981
|
+
// Transfer the tokens
|
1982
|
+
instructions.push(splToken.createTransferInstruction(fromTokenAccount, toTokenAccount, fromWallet, BigInt(value), [], splToken.TOKEN_PROGRAM_ID));
|
1983
|
+
return instructions;
|
1984
|
+
};
|
1985
|
+
const tokenAccountExists = async (connection, address) => {
|
1986
|
+
try {
|
1987
|
+
await splToken.getAccount(connection, address);
|
1988
|
+
return true;
|
1989
|
+
} catch {
|
1990
|
+
return false;
|
1991
|
+
}
|
1420
1992
|
};
|
1421
1993
|
|
1422
1994
|
const SUBSCRIPTION_INTERVAL$2 = 6_000;
|
@@ -1459,7 +2031,7 @@ const subscribeBalances$6 = ({
|
|
1459
2031
|
}).pipe(rxjs.distinctUntilChanged(lodashEs.isEqual));
|
1460
2032
|
};
|
1461
2033
|
|
1462
|
-
const
|
2034
|
+
const SolSplBalanceModule = {
|
1463
2035
|
type: MODULE_TYPE$6,
|
1464
2036
|
platform: PLATFORM$6,
|
1465
2037
|
getMiniMetadata: getMiniMetadata$6,
|
@@ -1470,153 +2042,14 @@ const EvmUniswapV2BalanceModule = {
|
|
1470
2042
|
};
|
1471
2043
|
|
1472
2044
|
// to be used by chaindata too
|
1473
|
-
const
|
1474
|
-
|
2045
|
+
const SolSplTokenConfigSchema = z__default.default.strictObject({
|
2046
|
+
mintAddress: chaindataProvider.SolSplTokenSchema.shape.mintAddress,
|
1475
2047
|
...TokenConfigBaseSchema.shape
|
1476
2048
|
});
|
1477
2049
|
|
1478
2050
|
const MODULE_TYPE$5 = chaindataProvider.SubAssetsTokenSchema.shape.type.value;
|
1479
2051
|
const PLATFORM$5 = chaindataProvider.SubAssetsTokenSchema.shape.platform.value;
|
1480
2052
|
|
1481
|
-
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
1482
|
-
const allStateKeys = queries.flatMap(({
|
1483
|
-
stateKeys
|
1484
|
-
}) => stateKeys).filter(util.isNotNil);
|
1485
|
-
|
1486
|
-
// doing a query without keys would throw an error => return early
|
1487
|
-
if (!allStateKeys.length) return queries.map(({
|
1488
|
-
stateKeys,
|
1489
|
-
decodeResult
|
1490
|
-
}) => decodeResult(stateKeys.map(() => null)));
|
1491
|
-
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
1492
|
-
return decodeRpcQueryPack(queries, result);
|
1493
|
-
};
|
1494
|
-
const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
|
1495
|
-
const allStateKeys = queries.flatMap(({
|
1496
|
-
stateKeys
|
1497
|
-
}) => stateKeys).filter(util.isNotNil);
|
1498
|
-
|
1499
|
-
// doing a query without keys would throw an error => return early
|
1500
|
-
if (!allStateKeys.length) return rxjs.of(queries.map(({
|
1501
|
-
stateKeys,
|
1502
|
-
decodeResult
|
1503
|
-
}) => decodeResult(stateKeys.map(() => null))));
|
1504
|
-
return new rxjs.Observable(subscriber => {
|
1505
|
-
// first subscription callback includes results for all state keys, but further callbacks will only include the ones that changed
|
1506
|
-
// => we need to keep all results in memory and update them after each callback, so we can emit the full result set each time
|
1507
|
-
const changesCache = {};
|
1508
|
-
const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
|
1509
|
-
if (error) subscriber.error(error);else if (result) {
|
1510
|
-
// update the cache
|
1511
|
-
for (const [stateKey, encodedResult] of result.changes) changesCache[stateKey] = encodedResult;
|
1512
|
-
|
1513
|
-
// regenerate the full changes array
|
1514
|
-
const changes = lodashEs.toPairs(changesCache);
|
1515
|
-
|
1516
|
-
// decode and emit results for all queries
|
1517
|
-
subscriber.next(decodeRpcQueryPack(queries, {
|
1518
|
-
block: result.block,
|
1519
|
-
changes
|
1520
|
-
}));
|
1521
|
-
}
|
1522
|
-
}, timeout);
|
1523
|
-
return () => {
|
1524
|
-
promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
|
1525
|
-
};
|
1526
|
-
});
|
1527
|
-
};
|
1528
|
-
const decodeRpcQueryPack = (queries, result) => {
|
1529
|
-
return queries.reduce((acc, {
|
1530
|
-
stateKeys,
|
1531
|
-
decodeResult
|
1532
|
-
}) => {
|
1533
|
-
const changes = stateKeys.map(stateKey => {
|
1534
|
-
if (!stateKey || !result) return null;
|
1535
|
-
const change = result.changes.find(([key]) => key === stateKey);
|
1536
|
-
if (!change) return null;
|
1537
|
-
return change[1];
|
1538
|
-
});
|
1539
|
-
acc.push(decodeResult(changes));
|
1540
|
-
return acc;
|
1541
|
-
}, []);
|
1542
|
-
};
|
1543
|
-
|
1544
|
-
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
1545
|
-
const {
|
1546
|
-
builder
|
1547
|
-
} = scale.parseMetadataRpc(metadataRpc);
|
1548
|
-
const call = builder.buildRuntimeCall(apiName, method);
|
1549
|
-
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, scale.toHex(call.args.enc(args))]);
|
1550
|
-
return call.value.dec(hex);
|
1551
|
-
};
|
1552
|
-
|
1553
|
-
const hasStorageItem = (metadata, palletName, itemName) => {
|
1554
|
-
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1555
|
-
if (!pallet || !pallet.storage) return false;
|
1556
|
-
return pallet.storage.items.some(item => item.name === itemName);
|
1557
|
-
};
|
1558
|
-
const hasStorageItems = (metadata, palletName, itemNames) => {
|
1559
|
-
const pallet = metadata.pallets.find(p => p.name === palletName);
|
1560
|
-
if (!pallet || !pallet.storage) return false;
|
1561
|
-
return itemNames.every(itemName => pallet.storage?.items.some(item => item.name === itemName));
|
1562
|
-
};
|
1563
|
-
const hasRuntimeApi = (metadata, apiName, method) => {
|
1564
|
-
const api = metadata.apis.find(api => api.name === apiName);
|
1565
|
-
if (!api || !api.methods) return false;
|
1566
|
-
return api.methods.some(m => m.name === method);
|
1567
|
-
};
|
1568
|
-
const getConstantValue = (metadataRpc, pallet, constant) => {
|
1569
|
-
const {
|
1570
|
-
unifiedMetadata,
|
1571
|
-
builder
|
1572
|
-
} = scale.parseMetadataRpc(metadataRpc);
|
1573
|
-
const codec = builder.buildConstant(pallet, constant);
|
1574
|
-
const encodedValue = unifiedMetadata.pallets.find(({
|
1575
|
-
name
|
1576
|
-
}) => name === pallet)?.constants.find(({
|
1577
|
-
name
|
1578
|
-
}) => name === constant)?.value;
|
1579
|
-
if (!encodedValue) throw new Error(`Constant ${pallet}.${constant} not found`);
|
1580
|
-
return codec.dec(encodedValue);
|
1581
|
-
};
|
1582
|
-
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
1583
|
-
const {
|
1584
|
-
unifiedMetadata,
|
1585
|
-
builder
|
1586
|
-
} = scale.parseMetadataRpc(metadataRpc);
|
1587
|
-
const encodedValue = unifiedMetadata.pallets.find(({
|
1588
|
-
name
|
1589
|
-
}) => name === pallet)?.constants.find(({
|
1590
|
-
name
|
1591
|
-
}) => name === constant)?.value;
|
1592
|
-
if (!encodedValue) return null;
|
1593
|
-
const codec = builder.buildConstant(pallet, constant);
|
1594
|
-
return codec.dec(encodedValue);
|
1595
|
-
};
|
1596
|
-
|
1597
|
-
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
1598
|
-
if (!miniMetadata.data) return null;
|
1599
|
-
const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
|
1600
|
-
try {
|
1601
|
-
const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
|
1602
|
-
const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
|
1603
|
-
const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
|
1604
|
-
chainId
|
1605
|
-
}) : moduleMethodOrFn;
|
1606
|
-
try {
|
1607
|
-
return [[key, scaleBuilder.buildStorage(module, method)]];
|
1608
|
-
} catch (cause) {
|
1609
|
-
log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
|
1610
|
-
return [];
|
1611
|
-
}
|
1612
|
-
}));
|
1613
|
-
return builtCoders;
|
1614
|
-
} catch (cause) {
|
1615
|
-
log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
|
1616
|
-
}
|
1617
|
-
return null;
|
1618
|
-
};
|
1619
|
-
|
1620
2053
|
const buildQueries$2 = (networkId, balanceDefs, miniMetadata) => {
|
1621
2054
|
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
1622
2055
|
storage: ["Assets", "Account"]
|
@@ -1852,11 +2285,6 @@ function includeInTotalExtraAmount(extra) {
|
|
1852
2285
|
return extra.filter(extra => extra.includeInTotal).map(extra => extra.amount.planck).reduce((a, b) => a + b, 0n);
|
1853
2286
|
}
|
1854
2287
|
|
1855
|
-
/**
|
1856
|
-
* Have the importing library define its Token and BalanceJson enums (as a sum type of all plugins) and pass them into some
|
1857
|
-
* internal global typescript context, which is then picked up on by this module.
|
1858
|
-
*/
|
1859
|
-
|
1860
2288
|
/** A utility type used to extract the underlying `BalanceType` of a specific source from a generalised `BalanceJson` */
|
1861
2289
|
|
1862
2290
|
/** TODO: Remove this in favour of a frontend-friendly `ChaindataProvider` */
|
@@ -2593,8 +3021,6 @@ const getValueId = amount => {
|
|
2593
3021
|
|
2594
3022
|
/** A labelled extra amount of a balance */
|
2595
3023
|
|
2596
|
-
/** Used by plugins to help define their custom `BalanceType` */
|
2597
|
-
|
2598
3024
|
/** For fast db access, you can calculate the primary key for a miniMetadata using this method */
|
2599
3025
|
const deriveMiniMetadataId = ({
|
2600
3026
|
source,
|
@@ -2721,12 +3147,12 @@ const getTransferAllEncodedArgs$2 = (assetId, to, codec) => {
|
|
2721
3147
|
return getEncodedValue$2(codec, [() => ({
|
2722
3148
|
id: Number(assetId),
|
2723
3149
|
// for most networks
|
2724
|
-
|
3150
|
+
dest: polkadotApi.Enum("Id", to),
|
2725
3151
|
keep_alive: false
|
2726
3152
|
}), () => ({
|
2727
3153
|
id: BigInt(assetId),
|
2728
3154
|
// for Astar
|
2729
|
-
|
3155
|
+
dest: polkadotApi.Enum("Id", to),
|
2730
3156
|
keep_alive: false
|
2731
3157
|
})]);
|
2732
3158
|
};
|
@@ -3104,7 +3530,7 @@ const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
|
|
3104
3530
|
return getEncodedValue$1(codec, [() => ({
|
3105
3531
|
id: scale.papiParse(onChainId),
|
3106
3532
|
// for most networks
|
3107
|
-
|
3533
|
+
dest: polkadotApi.Enum("Id", to),
|
3108
3534
|
keep_alive: false
|
3109
3535
|
})]);
|
3110
3536
|
};
|
@@ -6151,7 +6577,7 @@ const SubTokensMiniMetadataExtraSchema = z__default.default.strictObject({
|
|
6151
6577
|
palletId: z__default.default.string()
|
6152
6578
|
});
|
6153
6579
|
|
6154
|
-
const BALANCE_MODULES = [SubNativeBalanceModule, SubAssetsBalanceModule, SubHydrationBalanceModule, SubForeignAssetsBalanceModule, SubPsp22BalanceModule, SubTokensBalanceModule, EvmErc20BalanceModule, EvmUniswapV2BalanceModule, EvmNativeBalanceModule];
|
6580
|
+
const BALANCE_MODULES = [SubNativeBalanceModule, SubAssetsBalanceModule, SubHydrationBalanceModule, SubForeignAssetsBalanceModule, SubPsp22BalanceModule, SubTokensBalanceModule, EvmErc20BalanceModule, EvmUniswapV2BalanceModule, EvmNativeBalanceModule, SolNativeBalanceModule, SolSplBalanceModule];
|
6155
6581
|
|
6156
6582
|
// share requests as all modules will call this at once
|
6157
6583
|
const CACHE$1 = new Map();
|
@@ -6317,6 +6743,9 @@ class BalancesProvider {
|
|
6317
6743
|
balances
|
6318
6744
|
}) => balances)));
|
6319
6745
|
}
|
6746
|
+
getDetectedTokensId$(address) {
|
6747
|
+
return getDetectedTokensIds$(address);
|
6748
|
+
}
|
6320
6749
|
getNetworkBalances$(networkId, addressesByTokenId) {
|
6321
6750
|
const network$ = this.#chaindataProvider.getNetworkById$(networkId);
|
6322
6751
|
const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
|
@@ -6329,10 +6758,25 @@ class BalancesProvider {
|
|
6329
6758
|
{
|
6330
6759
|
return this.getEthereumNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6331
6760
|
}
|
6761
|
+
case "solana":
|
6762
|
+
{
|
6763
|
+
return this.getSolanaNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6764
|
+
}
|
6332
6765
|
case "polkadot":
|
6333
6766
|
{
|
6334
6767
|
return this.getPolkadotNetworkModuleBalances$(networkId, tokensWithAddresses, mod);
|
6335
6768
|
}
|
6769
|
+
default:
|
6770
|
+
{
|
6771
|
+
log.warn("[balances] Unsupported network platform for module", {
|
6772
|
+
networkId,
|
6773
|
+
mod
|
6774
|
+
});
|
6775
|
+
return rxjs.of({
|
6776
|
+
status: "live",
|
6777
|
+
balances: []
|
6778
|
+
});
|
6779
|
+
}
|
6336
6780
|
}
|
6337
6781
|
}));
|
6338
6782
|
}), rxjs.map(results => {
|
@@ -6362,7 +6806,7 @@ class BalancesProvider {
|
|
6362
6806
|
address
|
6363
6807
|
})));
|
6364
6808
|
if (!this.#chainConnectors.substrate) {
|
6365
|
-
log.
|
6809
|
+
log.warn("[balances] no substrate connector or miniMetadata for module", mod.type);
|
6366
6810
|
return rxjs.defer(() => rxjs.of({
|
6367
6811
|
status: "initialising",
|
6368
6812
|
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
@@ -6413,7 +6857,7 @@ class BalancesProvider {
|
|
6413
6857
|
address
|
6414
6858
|
})));
|
6415
6859
|
if (!this.#chainConnectors.evm) {
|
6416
|
-
log.
|
6860
|
+
log.warn("[balances] no ethereum connector for module", mod.type);
|
6417
6861
|
return rxjs.defer(() => rxjs.of({
|
6418
6862
|
status: "initialising",
|
6419
6863
|
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
@@ -6445,6 +6889,56 @@ class BalancesProvider {
|
|
6445
6889
|
})));
|
6446
6890
|
});
|
6447
6891
|
}
|
6892
|
+
getSolanaNetworkModuleBalances$(networkId, tokensWithAddresses, mod) {
|
6893
|
+
return util.getSharedObservable(`BalancesProvider.getSolanaNetworkModuleBalances$`, {
|
6894
|
+
networkId,
|
6895
|
+
mod,
|
6896
|
+
tokensWithAddresses
|
6897
|
+
}, () => {
|
6898
|
+
if (!tokensWithAddresses.length) return rxjs.of({
|
6899
|
+
status: "live",
|
6900
|
+
balances: []
|
6901
|
+
});
|
6902
|
+
const moduleAddressesByTokenId = lodashEs.fromPairs(tokensWithAddresses.map(([token, addresses]) => [token.id, addresses]));
|
6903
|
+
|
6904
|
+
// all balance ids expected in result set
|
6905
|
+
const balanceIds = lodashEs.toPairs(moduleAddressesByTokenId).flatMap(([tokenId, addresses]) => addresses.map(address => getBalanceId({
|
6906
|
+
tokenId,
|
6907
|
+
address
|
6908
|
+
})));
|
6909
|
+
if (!this.#chainConnectors.solana) {
|
6910
|
+
log.warn("[balances] no solana connector for module", mod.type);
|
6911
|
+
return rxjs.defer(() => rxjs.of({
|
6912
|
+
status: "initialising",
|
6913
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6914
|
+
}));
|
6915
|
+
}
|
6916
|
+
const moduleBalances$ = mod.subscribeBalances({
|
6917
|
+
networkId,
|
6918
|
+
tokensWithAddresses,
|
6919
|
+
connector: this.#chainConnectors.solana
|
6920
|
+
}).pipe(rxjs.catchError(() => rxjs.EMPTY),
|
6921
|
+
// don't emit, let provider mark balances stale
|
6922
|
+
rxjs.map(results => ({
|
6923
|
+
status: "live",
|
6924
|
+
// exclude zero balances
|
6925
|
+
balances: results.success.filter(b => new Balance(b).total.planck > 0n)
|
6926
|
+
})), rxjs.tap(results => {
|
6927
|
+
this.updateStorage$(balanceIds, results);
|
6928
|
+
}),
|
6929
|
+
// shareReplay + keepAlive(0) keep the subscription alive while root observable is being unsubscribed+resubscribed, in case any input change
|
6930
|
+
rxjs.shareReplay({
|
6931
|
+
refCount: true,
|
6932
|
+
bufferSize: 1
|
6933
|
+
}), util.keepAlive(0));
|
6934
|
+
|
6935
|
+
// defer the startWith call to start with up to date balances each time the observable is re-subscribed to
|
6936
|
+
return rxjs.defer(() => moduleBalances$.pipe(rxjs.startWith({
|
6937
|
+
status: "initialising",
|
6938
|
+
balances: this.getStoredBalances(moduleAddressesByTokenId)
|
6939
|
+
})));
|
6940
|
+
});
|
6941
|
+
}
|
6448
6942
|
updateStorage$(balanceIds, balancesResult) {
|
6449
6943
|
if (balancesResult.status !== "live") return;
|
6450
6944
|
const storage = this.#storage.getValue();
|
@@ -6550,22 +7044,46 @@ class BalancesProvider {
|
|
6550
7044
|
return lodashEs.fromPairs(lodashEs.toPairs(addressesByTokenId).map(([tokenId, addresses]) => {
|
6551
7045
|
const networkId = chaindataProvider.parseTokenId(tokenId).networkId;
|
6552
7046
|
const network = networksById[networkId];
|
6553
|
-
return [tokenId, lodashEs.uniq(addresses.map(
|
7047
|
+
return [tokenId, lodashEs.uniq(addresses.map(crypto.normalizeAddress)).filter(address => network && isAddressCompatibleWithNetwork(network, address))];
|
6554
7048
|
}).filter(([, addresses]) => addresses.length > 0));
|
6555
7049
|
}));
|
6556
7050
|
}
|
6557
7051
|
}
|
6558
|
-
const
|
7052
|
+
const isAccountPlatformCompatibleWithNetwork = (network, platform) => {
|
6559
7053
|
switch (network.platform) {
|
6560
7054
|
case "ethereum":
|
6561
|
-
return
|
7055
|
+
return platform === "ethereum";
|
7056
|
+
case "solana":
|
7057
|
+
return platform === "solana";
|
6562
7058
|
case "polkadot":
|
6563
|
-
|
7059
|
+
{
|
7060
|
+
switch (network.account) {
|
7061
|
+
case "secp256k1":
|
7062
|
+
return platform === "ethereum";
|
7063
|
+
case "*25519":
|
7064
|
+
return platform === "polkadot";
|
7065
|
+
default:
|
7066
|
+
throw new Error(`Unsupported polkadot network account type ${network.account}`);
|
7067
|
+
}
|
7068
|
+
}
|
6564
7069
|
default:
|
6565
7070
|
log.warn("Unsupported network platform", network);
|
6566
7071
|
throw new Error("Unsupported network platform");
|
6567
7072
|
}
|
6568
7073
|
};
|
7074
|
+
|
7075
|
+
/**
|
7076
|
+
* If this is the address of an account, use isAccountCompatibleWithChain instead.
|
7077
|
+
* Otherwise it could lead to a loss of funds
|
7078
|
+
* @param chain
|
7079
|
+
* @param address
|
7080
|
+
* @returns
|
7081
|
+
*/
|
7082
|
+
const isAddressCompatibleWithNetwork = (network, address) => {
|
7083
|
+
// TODO try with return true to check if wallet filters correctly upfront
|
7084
|
+
const accountPlatform = crypto.getAccountPlatformFromAddress(address);
|
7085
|
+
return isAccountPlatformCompatibleWithNetwork(network, accountPlatform);
|
7086
|
+
};
|
6569
7087
|
const sortByBalanceId = (a, b) => getBalanceId(a).localeCompare(getBalanceId(b));
|
6570
7088
|
const sortByMiniMetadataId = (a, b) => a.id.localeCompare(b.id);
|
6571
7089
|
|
@@ -6592,6 +7110,10 @@ exports.PlanckSumBalancesFormatter = PlanckSumBalancesFormatter;
|
|
6592
7110
|
exports.SCALE_FACTOR = SCALE_FACTOR;
|
6593
7111
|
exports.SUBTENSOR_MIN_STAKE_AMOUNT_PLANK = SUBTENSOR_MIN_STAKE_AMOUNT_PLANK;
|
6594
7112
|
exports.SUBTENSOR_ROOT_NETUID = SUBTENSOR_ROOT_NETUID;
|
7113
|
+
exports.SolNativeBalanceModule = SolNativeBalanceModule;
|
7114
|
+
exports.SolNativeTokenConfigSchema = SolNativeTokenConfigSchema;
|
7115
|
+
exports.SolSplBalanceModule = SolSplBalanceModule;
|
7116
|
+
exports.SolSplTokenConfigSchema = SolSplTokenConfigSchema;
|
6595
7117
|
exports.SubAssetsBalanceModule = SubAssetsBalanceModule;
|
6596
7118
|
exports.SubAssetsTokenConfigSchema = SubAssetsTokenConfigSchema;
|
6597
7119
|
exports.SubForeignAssetsBalanceModule = SubForeignAssetsBalanceModule;
|