@0xsquid/react-hooks 8.4.1-beta-tempo.0 → 8.5.0
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/core/constants.d.ts +0 -3
- package/dist/core/queries/queries-keys.d.ts +1 -1
- package/dist/core/types/config.d.ts +1 -0
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/transaction/send/useEstimateSendTransactionGas.d.ts +2 -6
- package/dist/hooks/transaction/useAllTransactionsStatus.d.ts +1 -0
- package/dist/hooks/transaction/useEstimate.d.ts +11 -5
- package/dist/hooks/user/useUserParams.d.ts +4 -0
- package/dist/{index-CaI-xWCW.js → index-5cyMUZhY.js} +1205 -1349
- package/dist/index-5cyMUZhY.js.map +1 -0
- package/dist/{index-B4aeecpP.js → index-BGVXRZI6.js} +1208 -1351
- package/dist/index-BGVXRZI6.js.map +1 -0
- package/dist/{index.es-BXf9jwuI.js → index.es-BfdAGErV.js} +3 -3
- package/dist/{index.es-BXf9jwuI.js.map → index.es-BfdAGErV.js.map} +1 -1
- package/dist/{index.es-DAfqL2H0.js → index.es-CeHwkxPw.js} +3 -3
- package/dist/{index.es-DAfqL2H0.js.map → index.es-CeHwkxPw.js.map} +1 -1
- package/dist/index.esm.js +2 -2
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/{secretService-CIYxEkTN.js → secretService-BMYOBXhv.js} +3 -3
- package/dist/{secretService-CIYxEkTN.js.map → secretService-BMYOBXhv.js.map} +1 -1
- package/dist/{secretService-Cld4gYfG.js → secretService-D_d3CFdp.js} +3 -3
- package/dist/{secretService-Cld4gYfG.js.map → secretService-D_d3CFdp.js.map} +1 -1
- package/dist/services/internal/assetsService.d.ts +2 -2
- package/dist/services/internal/estimateService.d.ts +27 -13
- package/dist/services/internal/transactionService.d.ts +0 -7
- package/dist/{stellarService.client-COeQeah_.js → stellarService.client-CIkvwxPo.js} +3 -3
- package/dist/{stellarService.client-COeQeah_.js.map → stellarService.client-CIkvwxPo.js.map} +1 -1
- package/dist/{stellarService.client-ocLzRIB4.js → stellarService.client-DOrCdvCd.js} +3 -3
- package/dist/{stellarService.client-ocLzRIB4.js.map → stellarService.client-DOrCdvCd.js.map} +1 -1
- package/package.json +3 -3
- package/dist/hooks/tokens/useSourceChainGasToken.d.ts +0 -11
- package/dist/hooks/transaction/useTempoFeeCheck.d.ts +0 -11
- package/dist/index-B4aeecpP.js.map +0 -1
- package/dist/index-CaI-xWCW.js.map +0 -1
- package/dist/services/internal/tempoService.d.ts +0 -82
- package/dist/tests/networkGasToken.test.d.ts +0 -1
- package/dist/tests/tempoService.test.d.ts +0 -1
|
@@ -27,9 +27,9 @@ var wagmi = require('wagmi');
|
|
|
27
27
|
var SafeAppsSDK = require('@safe-global/safe-apps-sdk');
|
|
28
28
|
var core$1 = require('@wallet-standard/core');
|
|
29
29
|
var slushWallet = require('@mysten/slush-wallet');
|
|
30
|
+
var connectors = require('wagmi/connectors');
|
|
30
31
|
var stargate = require('@cosmjs/stargate');
|
|
31
32
|
var client = require('@mysten/sui/client');
|
|
32
|
-
var connectors = require('wagmi/connectors');
|
|
33
33
|
var protoSigning = require('@cosmjs/proto-signing');
|
|
34
34
|
var transactions = require('@mysten/sui/transactions');
|
|
35
35
|
var cosmwasmStargate = require('@cosmjs/cosmwasm-stargate');
|
|
@@ -145,9 +145,6 @@ const CHAIN_IDS = {
|
|
|
145
145
|
SONEIUM: "1868",
|
|
146
146
|
PEAQ: "3338",
|
|
147
147
|
HEDERA: "295",
|
|
148
|
-
MANTRA: "5888",
|
|
149
|
-
CITREA: "4114",
|
|
150
|
-
TEMPO: "4217",
|
|
151
148
|
// others
|
|
152
149
|
BITCOIN: "bitcoin",
|
|
153
150
|
SOLANA: "solana-mainnet-beta",
|
|
@@ -21140,17 +21137,9 @@ function isOnChainTxData(squidData) {
|
|
|
21140
21137
|
return [
|
|
21141
21138
|
squidTypes.SquidDataType.OnChainExecution,
|
|
21142
21139
|
squidTypes.SquidDataType.DepositAddressWithSignature,
|
|
21140
|
+
squidTypes.SquidDataType.OnChainExecutionWithSignature,
|
|
21143
21141
|
].includes(squidData.type);
|
|
21144
21142
|
}
|
|
21145
|
-
/**
|
|
21146
|
-
* Checks if a route is of type deposit-with-signature
|
|
21147
|
-
*
|
|
21148
|
-
* deposit-with-signature routes are on-chain routes
|
|
21149
|
-
* that require a signature from the user to execute
|
|
21150
|
-
*/
|
|
21151
|
-
function isDepositWithSignatureTxData(squidData) {
|
|
21152
|
-
return squidData.type === squidTypes.SquidDataType.DepositAddressWithSignature;
|
|
21153
|
-
}
|
|
21154
21143
|
function getHistoryTransactionId(tx) {
|
|
21155
21144
|
switch (tx.txType) {
|
|
21156
21145
|
case exports.HistoryTxType.SWAP:
|
|
@@ -22309,7 +22298,7 @@ const keys = () => ({
|
|
|
22309
22298
|
// ============
|
|
22310
22299
|
// Transactions
|
|
22311
22300
|
// ============
|
|
22312
|
-
transaction: (fromChainId, toChainId, toTokenAddress, fromTokenAddress, price, slippage, sourceUserAddress, degenMode, destinationAddress, fallbackAddress, quoteOnly, fromChainType, preHook, postHook, overrideGasRefundAddress) => [
|
|
22301
|
+
transaction: (fromChainId, toChainId, toTokenAddress, fromTokenAddress, price, slippage, getGasOnDestination, sourceUserAddress, degenMode, destinationAddress, fallbackAddress, quoteOnly, fromChainType, preHook, postHook, overrideGasRefundAddress) => [
|
|
22313
22302
|
...keys().transactions(),
|
|
22314
22303
|
exports.QueryKeys.Transaction,
|
|
22315
22304
|
fromChainId,
|
|
@@ -22318,6 +22307,7 @@ const keys = () => ({
|
|
|
22318
22307
|
fromTokenAddress,
|
|
22319
22308
|
price,
|
|
22320
22309
|
slippage,
|
|
22310
|
+
getGasOnDestination,
|
|
22321
22311
|
sourceUserAddress,
|
|
22322
22312
|
degenMode,
|
|
22323
22313
|
destinationAddress,
|
|
@@ -22478,6 +22468,7 @@ const getConfigWithDefaults = (config) => {
|
|
|
22478
22468
|
integratorId: get$2(config, "integratorId", defaultConfigValues.integratorId),
|
|
22479
22469
|
slippage: get$2(config, "slippage", defaultConfigValues.slippage),
|
|
22480
22470
|
collectFees: get$2(config, "collectFees", defaultConfigValues.collectFees),
|
|
22471
|
+
enableGetGasOnDestination: get$2(config, "enableGetGasOnDestination", defaultConfigValues.enableGetGasOnDestination),
|
|
22481
22472
|
apiUrl: get$2(config, "apiUrl", defaultConfigValues.apiUrl),
|
|
22482
22473
|
priceImpactWarnings: get$2(config, "priceImpactWarnings", defaultConfigValues.priceImpactWarnings),
|
|
22483
22474
|
initialAssets: get$2(config, "initialAssets", defaultConfigValues.initialAssets),
|
|
@@ -22967,8 +22958,8 @@ const sortAllTokens = (tokenA, tokenB) => {
|
|
|
22967
22958
|
return 0;
|
|
22968
22959
|
};
|
|
22969
22960
|
const findToken = (tokens, chainId, address) => tokens.find((t) => t.chainId === chainId && t.address === address);
|
|
22970
|
-
const findNativeToken = (tokens, chain) => tokens.find((t) => t.symbol.toUpperCase() === chain
|
|
22971
|
-
t.chainId == chain
|
|
22961
|
+
const findNativeToken = (tokens, chain) => tokens.find((t) => t.symbol.toUpperCase() === chain?.nativeCurrency.symbol.toUpperCase() &&
|
|
22962
|
+
t.chainId == chain?.chainId);
|
|
22972
22963
|
const normalizeIbcAddress = (address) => {
|
|
22973
22964
|
if (!address.toLowerCase().startsWith("ibc/")) {
|
|
22974
22965
|
return address;
|
|
@@ -23179,7 +23170,7 @@ const filterViewableTokens = (tokens, config, direction) => {
|
|
|
23179
23170
|
};
|
|
23180
23171
|
const getSecretNetworkBalances = async (chainData, cosmosAddress, squidTokens, keplrTypeWallet) => {
|
|
23181
23172
|
const squidSecretTokens = squidTokens.filter((t) => t.chainId === CHAIN_IDS.SECRET);
|
|
23182
|
-
const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-
|
|
23173
|
+
const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-D_d3CFdp.js'); });
|
|
23183
23174
|
return fetchAllSecretBalances(chainData, cosmosAddress, squidSecretTokens, keplrTypeWallet);
|
|
23184
23175
|
};
|
|
23185
23176
|
function getTokenAssetsKey(token) {
|
|
@@ -24870,7 +24861,7 @@ class OnrampService {
|
|
|
24870
24861
|
});
|
|
24871
24862
|
return data;
|
|
24872
24863
|
}
|
|
24873
|
-
async getConfiguration({ chains
|
|
24864
|
+
async getConfiguration({ chains, tokens, }) {
|
|
24874
24865
|
const { data } = await axios.get(`${this.baseUrl}/config`);
|
|
24875
24866
|
// Filter supportedCryptos to only include tokens that match our provided tokens
|
|
24876
24867
|
const filteredCryptos = data.supportedCryptos.filter((supportedCrypto) => tokens.some((token) => token.address.toLowerCase() ===
|
|
@@ -26495,7 +26486,7 @@ function useStellarWallets() {
|
|
|
26495
26486
|
try {
|
|
26496
26487
|
const { allowAllModules: initializeAllModules } = await import('@creit.tech/stellar-wallets-kit');
|
|
26497
26488
|
const { LedgerModule } = await import('@creit.tech/stellar-wallets-kit/modules/ledger.module');
|
|
26498
|
-
const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-
|
|
26489
|
+
const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-CIkvwxPo.js'); });
|
|
26499
26490
|
const modules = [...initializeAllModules(), new LedgerModule()];
|
|
26500
26491
|
const promises = modules.map(async (module) => {
|
|
26501
26492
|
const isAvailable = await module.isAvailable();
|
|
@@ -27802,310 +27793,580 @@ function useTrackSearchEmpty({ searchQuery, resultsLength, context, debounceMs =
|
|
|
27802
27793
|
}, [context, debounceMs, resultsLength, searchQuery]);
|
|
27803
27794
|
}
|
|
27804
27795
|
|
|
27805
|
-
|
|
27806
|
-
|
|
27807
|
-
|
|
27808
|
-
|
|
27796
|
+
var hrc20 = [
|
|
27797
|
+
{
|
|
27798
|
+
inputs: [
|
|
27799
|
+
],
|
|
27800
|
+
name: "associate",
|
|
27801
|
+
outputs: [
|
|
27802
|
+
{
|
|
27803
|
+
internalType: "uint256",
|
|
27804
|
+
name: "responseCode",
|
|
27805
|
+
type: "uint256"
|
|
27806
|
+
}
|
|
27807
|
+
],
|
|
27808
|
+
stateMutability: "nonpayable",
|
|
27809
|
+
type: "function"
|
|
27810
|
+
}
|
|
27811
|
+
];
|
|
27812
|
+
|
|
27813
|
+
/**
|
|
27814
|
+
* Client for interacting with the Hedera Mirrornode API.
|
|
27815
|
+
*
|
|
27816
|
+
* @docs https://mainnet.mirrornode.hedera.com/api/v1/docs
|
|
27817
|
+
*/
|
|
27818
|
+
class HederaApiClient {
|
|
27819
|
+
apiUrl;
|
|
27820
|
+
constructor(apiUrl) {
|
|
27821
|
+
this.apiUrl = apiUrl;
|
|
27809
27822
|
}
|
|
27810
|
-
|
|
27811
|
-
|
|
27812
|
-
|
|
27813
|
-
|
|
27814
|
-
|
|
27815
|
-
*/
|
|
27816
|
-
async getBalance(userAddress, tokenAddress, chainId) {
|
|
27817
|
-
const account = await this.server.getAccount(userAddress);
|
|
27818
|
-
const network = getStellarNetwork(chainId);
|
|
27819
|
-
if (network == null) {
|
|
27820
|
-
throw new Error(`No Stellar network found for chainId ${chainId}`);
|
|
27821
|
-
}
|
|
27822
|
-
const txBuilder = new stellarSdk.TransactionBuilder(account, {
|
|
27823
|
-
fee: (BigInt(stellarSdk.BASE_FEE) * BigInt(2)).toString(),
|
|
27824
|
-
networkPassphrase: network,
|
|
27825
|
-
});
|
|
27826
|
-
const contract = new stellarSdk.Contract(tokenAddress);
|
|
27827
|
-
const tx = txBuilder
|
|
27828
|
-
.addOperation(contract.call("balance", new stellarSdk.Address(userAddress).toScVal()))
|
|
27829
|
-
.setTimeout(stellarSdk.TimeoutInfinite)
|
|
27830
|
-
.build();
|
|
27831
|
-
const simulateTxResponse = await this.server.simulateTransaction(tx);
|
|
27832
|
-
if ("error" in simulateTxResponse) {
|
|
27833
|
-
const isNoBalanceError = simulateTxResponse.error.includes("trying to get non-existing value for contract instance");
|
|
27834
|
-
// If the error message indicates that the user has no balance just return 0
|
|
27835
|
-
// We don't want to spam with this error as it's pretty common
|
|
27836
|
-
if (isNoBalanceError) {
|
|
27837
|
-
return "0";
|
|
27838
|
-
}
|
|
27839
|
-
throw new Error(`Failed to fetch balance. RPC response: ${simulateTxResponse.error}`);
|
|
27823
|
+
async isTokenAssociated({ address, token, }) {
|
|
27824
|
+
const accountInfo = await this.getAccountInfo(address);
|
|
27825
|
+
// Unlimited auto associations
|
|
27826
|
+
if (accountInfo.max_automatic_token_associations === -1) {
|
|
27827
|
+
return true;
|
|
27840
27828
|
}
|
|
27841
|
-
|
|
27842
|
-
|
|
27843
|
-
|
|
27829
|
+
// If there's no unlimited auto-associations, we need to check if the token is already associated.
|
|
27830
|
+
const { tokens: accountTokens } = await this.getAccountTokens(address);
|
|
27831
|
+
const tokenId = convertEvmAddressToHederaAccountId(token.address);
|
|
27832
|
+
if (accountTokens.some((t) => t.token_id === tokenId)) {
|
|
27833
|
+
// Token is already associated
|
|
27834
|
+
return true;
|
|
27844
27835
|
}
|
|
27845
|
-
|
|
27846
|
-
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
});
|
|
27851
|
-
const results = await Promise.allSettled(balancePromises);
|
|
27852
|
-
const balances = results.map((result) => {
|
|
27853
|
-
if (result.status === "fulfilled") {
|
|
27854
|
-
return result.value;
|
|
27855
|
-
}
|
|
27856
|
-
return "0";
|
|
27857
|
-
});
|
|
27858
|
-
return balances.map((balance, index) => {
|
|
27859
|
-
return {
|
|
27860
|
-
...tokens[index],
|
|
27861
|
-
balance,
|
|
27862
|
-
};
|
|
27863
|
-
});
|
|
27836
|
+
// Finally, if not auto-associated, check if there is an available auto-association slot
|
|
27837
|
+
const autoAssociatedTokens = accountTokens.filter((t) => t.automatic_association);
|
|
27838
|
+
const remainingAutoAssociations = accountInfo.max_automatic_token_associations -
|
|
27839
|
+
autoAssociatedTokens.length;
|
|
27840
|
+
return remainingAutoAssociations > 0;
|
|
27864
27841
|
}
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27869
|
-
const startTime = Date.now();
|
|
27870
|
-
while (true) {
|
|
27871
|
-
const result = await this.server.getTransaction(txHash);
|
|
27872
|
-
if (result.status === stellarSdk.rpc.Api.GetTransactionStatus.NOT_FOUND) {
|
|
27873
|
-
if (Date.now() - startTime > timeout) {
|
|
27874
|
-
throw new Error(`Transaction ${txHash} not found within timeout`);
|
|
27875
|
-
}
|
|
27876
|
-
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
27877
|
-
await new Promise((res) => setTimeout(res, interval));
|
|
27878
|
-
continue;
|
|
27879
|
-
}
|
|
27880
|
-
if (result.status === stellarSdk.rpc.Api.GetTransactionStatus.SUCCESS) {
|
|
27881
|
-
return result.status;
|
|
27882
|
-
}
|
|
27883
|
-
throw new Error(`Transaction failed with status: ${result.status}`);
|
|
27842
|
+
async getAccountInfo(address) {
|
|
27843
|
+
const data = await this.fetch(`accounts/${address}`);
|
|
27844
|
+
if (typeof data.max_automatic_token_associations !== "number") {
|
|
27845
|
+
throw new Error("Invalid max_automatic_token_associations type, expected number");
|
|
27884
27846
|
}
|
|
27885
|
-
|
|
27886
|
-
|
|
27887
|
-
try {
|
|
27888
|
-
await this.getAccount(address);
|
|
27889
|
-
return true;
|
|
27847
|
+
if (typeof data.balance.balance !== "number") {
|
|
27848
|
+
throw new Error("Invalid balance type, expected number");
|
|
27890
27849
|
}
|
|
27891
|
-
|
|
27892
|
-
|
|
27893
|
-
return false;
|
|
27894
|
-
}
|
|
27895
|
-
throw error;
|
|
27850
|
+
if (!Array.isArray(data.balance.tokens)) {
|
|
27851
|
+
throw new Error("Invalid tokens type, expected array");
|
|
27896
27852
|
}
|
|
27853
|
+
return data;
|
|
27897
27854
|
}
|
|
27898
|
-
async
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
|
|
27902
|
-
const transactionResponse = await this.server.sendTransaction(transaction);
|
|
27903
|
-
if (transactionResponse.status === "ERROR") {
|
|
27904
|
-
throw new Error("Error sending transaction");
|
|
27855
|
+
async getAccountTokens(address) {
|
|
27856
|
+
const data = await this.fetch(`accounts/${address}/tokens`);
|
|
27857
|
+
if (!Array.isArray(data.tokens)) {
|
|
27858
|
+
throw new Error("Invalid tokens type, expected array");
|
|
27905
27859
|
}
|
|
27906
|
-
|
|
27860
|
+
const firstToken = data.tokens[0];
|
|
27861
|
+
if (typeof firstToken?.automatic_association !== "boolean") {
|
|
27862
|
+
throw new Error("Invalid automatic_association type, expected boolean");
|
|
27863
|
+
}
|
|
27864
|
+
if (typeof firstToken?.token_id !== "string") {
|
|
27865
|
+
throw new Error("Invalid token_id type, expected string");
|
|
27866
|
+
}
|
|
27867
|
+
return data;
|
|
27907
27868
|
}
|
|
27908
|
-
async
|
|
27909
|
-
|
|
27869
|
+
async fetch(path) {
|
|
27870
|
+
const url = new URL(path, this.apiUrl);
|
|
27871
|
+
const response = await fetch(url);
|
|
27872
|
+
if (!response.ok) {
|
|
27873
|
+
throw new Error(`Hedera API error: ${response.status}`);
|
|
27874
|
+
}
|
|
27875
|
+
return response.json();
|
|
27910
27876
|
}
|
|
27911
27877
|
}
|
|
27912
27878
|
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
|
|
27925
|
-
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
|
|
27929
|
-
|
|
27930
|
-
|
|
27931
|
-
|
|
27932
|
-
|
|
27933
|
-
|
|
27934
|
-
|
|
27935
|
-
|
|
27936
|
-
|
|
27937
|
-
|
|
27938
|
-
|
|
27939
|
-
|
|
27940
|
-
|
|
27941
|
-
return this.call("account_lines", [
|
|
27942
|
-
{
|
|
27943
|
-
account: address,
|
|
27944
|
-
ledger_index: "validated",
|
|
27945
|
-
peer: issuer,
|
|
27946
|
-
},
|
|
27947
|
-
]);
|
|
27948
|
-
}
|
|
27949
|
-
async getTrustLine(address, issuer, currency) {
|
|
27950
|
-
const response = await this.getTrustLines(address, issuer);
|
|
27951
|
-
const trustLine = response.lines.find((line) => line.currency === currency);
|
|
27952
|
-
return trustLine ?? null;
|
|
27953
|
-
}
|
|
27954
|
-
async accountActivatedInfo(address) {
|
|
27955
|
-
const serverState = await this.getServerState();
|
|
27956
|
-
const reserveBaseBn = BigInt(serverState.state.validated_ledger.reserve_base);
|
|
27957
|
-
try {
|
|
27958
|
-
const accountInfo = await this.getAccountInfo(address);
|
|
27959
|
-
const balanceBn = BigInt(accountInfo.account_data.Balance);
|
|
27960
|
-
return {
|
|
27961
|
-
isActivated: balanceBn >= reserveBaseBn,
|
|
27962
|
-
reserveBaseBn,
|
|
27963
|
-
};
|
|
27964
|
-
}
|
|
27965
|
-
catch (error) {
|
|
27966
|
-
if (error.message?.includes("actNotFound")) {
|
|
27967
|
-
return { isActivated: false, reserveBaseBn };
|
|
27879
|
+
hederaWalletConnect.type = "hederaWalletConnect";
|
|
27880
|
+
/**
|
|
27881
|
+
* Wagmi connector to interact with the Hedera EVM network via the WalletConnect protocol.
|
|
27882
|
+
*
|
|
27883
|
+
* The connector removes the need for a WalletConnect modal, and instead
|
|
27884
|
+
* communicates with the provided `extension` by opening the extension popup
|
|
27885
|
+
* for user interaction upon request.
|
|
27886
|
+
*/
|
|
27887
|
+
function hederaWalletConnect(parameters) {
|
|
27888
|
+
const { extension, ...restParameters } = parameters;
|
|
27889
|
+
let provider_;
|
|
27890
|
+
let providerPromise;
|
|
27891
|
+
let connect;
|
|
27892
|
+
let displayUri;
|
|
27893
|
+
let sessionDelete;
|
|
27894
|
+
let disconnect;
|
|
27895
|
+
return createConnector((config) => ({
|
|
27896
|
+
id: `hedera-wc-${extension.id}`,
|
|
27897
|
+
name: extension.name,
|
|
27898
|
+
icon: extension.icon,
|
|
27899
|
+
type: hederaWalletConnect.type,
|
|
27900
|
+
async setup() {
|
|
27901
|
+
const provider = await this.getProvider().catch(() => null);
|
|
27902
|
+
if (!provider)
|
|
27903
|
+
return;
|
|
27904
|
+
if (!connect) {
|
|
27905
|
+
connect = this.onConnect.bind(this);
|
|
27906
|
+
provider.on("connect", connect);
|
|
27968
27907
|
}
|
|
27969
|
-
|
|
27970
|
-
|
|
27971
|
-
|
|
27972
|
-
|
|
27973
|
-
|
|
27974
|
-
|
|
27975
|
-
*/
|
|
27976
|
-
async waitForTransaction(txHash, { interval = 2_000, timeout = 20_000 } = {}) {
|
|
27977
|
-
const startTime = Date.now();
|
|
27978
|
-
while (true) {
|
|
27908
|
+
if (!sessionDelete) {
|
|
27909
|
+
sessionDelete = this.onSessionDelete.bind(this);
|
|
27910
|
+
provider.on("session_delete", sessionDelete);
|
|
27911
|
+
}
|
|
27912
|
+
},
|
|
27913
|
+
async connect(params = {}) {
|
|
27979
27914
|
try {
|
|
27980
|
-
const
|
|
27981
|
-
|
|
27982
|
-
|
|
27983
|
-
|
|
27984
|
-
|
|
27985
|
-
|
|
27986
|
-
if (!response.validated) {
|
|
27987
|
-
if (Date.now() - startTime > timeout) {
|
|
27988
|
-
throw new Error(`Transaction ${txHash} not validated within timeout`);
|
|
27989
|
-
}
|
|
27990
|
-
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
27991
|
-
await new Promise((res) => setTimeout(res, interval));
|
|
27992
|
-
continue;
|
|
27915
|
+
const provider = await this.getProvider();
|
|
27916
|
+
if (!provider)
|
|
27917
|
+
throw new ProviderNotFoundError();
|
|
27918
|
+
if (!displayUri) {
|
|
27919
|
+
displayUri = this.onDisplayUri;
|
|
27920
|
+
provider.on("display_uri", displayUri);
|
|
27993
27921
|
}
|
|
27994
|
-
|
|
27995
|
-
|
|
27996
|
-
|
|
27922
|
+
if (!provider.session) {
|
|
27923
|
+
await provider.connect({
|
|
27924
|
+
...("pairingTopic" in params
|
|
27925
|
+
? { pairingTopic: params.pairingTopic }
|
|
27926
|
+
: {}),
|
|
27927
|
+
});
|
|
27997
27928
|
}
|
|
27998
|
-
|
|
27999
|
-
|
|
27929
|
+
const accounts = (await provider.enable()).map((x) => viem.getAddress(x));
|
|
27930
|
+
const currentChainId = await this.getChainId();
|
|
27931
|
+
if (displayUri) {
|
|
27932
|
+
provider.removeListener("display_uri", displayUri);
|
|
27933
|
+
displayUri = undefined;
|
|
27934
|
+
}
|
|
27935
|
+
if (connect) {
|
|
27936
|
+
provider.removeListener("connect", connect);
|
|
27937
|
+
connect = undefined;
|
|
27938
|
+
}
|
|
27939
|
+
if (!disconnect) {
|
|
27940
|
+
disconnect = this.onDisconnect.bind(this);
|
|
27941
|
+
provider.on("disconnect", disconnect);
|
|
27942
|
+
}
|
|
27943
|
+
if (!sessionDelete) {
|
|
27944
|
+
sessionDelete = this.onSessionDelete.bind(this);
|
|
27945
|
+
provider.on("session_delete", sessionDelete);
|
|
28000
27946
|
}
|
|
27947
|
+
return { accounts, chainId: currentChainId };
|
|
28001
27948
|
}
|
|
28002
27949
|
catch (error) {
|
|
28003
|
-
|
|
28004
|
-
|
|
28005
|
-
if (Date.now() - startTime > timeout) {
|
|
28006
|
-
throw new Error(`Transaction ${txHash} not found within timeout`);
|
|
28007
|
-
}
|
|
28008
|
-
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
28009
|
-
await new Promise((res) => setTimeout(res, interval));
|
|
28010
|
-
continue;
|
|
27950
|
+
if (/(user rejected|connection request reset)/i.test(error?.message)) {
|
|
27951
|
+
throw new viem.UserRejectedRequestError(error);
|
|
28011
27952
|
}
|
|
28012
27953
|
throw error;
|
|
28013
27954
|
}
|
|
28014
|
-
}
|
|
28015
|
-
|
|
28016
|
-
|
|
28017
|
-
|
|
27955
|
+
},
|
|
27956
|
+
async disconnect() {
|
|
27957
|
+
const provider = await this.getProvider();
|
|
27958
|
+
try {
|
|
27959
|
+
await provider?.disconnect();
|
|
27960
|
+
}
|
|
27961
|
+
catch (error) {
|
|
27962
|
+
if (!/No matching key/i.test(error.message))
|
|
27963
|
+
throw error;
|
|
27964
|
+
}
|
|
27965
|
+
finally {
|
|
27966
|
+
if (disconnect) {
|
|
27967
|
+
provider?.removeListener("disconnect", disconnect);
|
|
27968
|
+
disconnect = undefined;
|
|
27969
|
+
}
|
|
27970
|
+
if (!connect) {
|
|
27971
|
+
connect = this.onConnect.bind(this);
|
|
27972
|
+
provider?.on("connect", connect);
|
|
27973
|
+
}
|
|
27974
|
+
if (sessionDelete) {
|
|
27975
|
+
provider?.removeListener("session_delete", sessionDelete);
|
|
27976
|
+
sessionDelete = undefined;
|
|
27977
|
+
}
|
|
27978
|
+
}
|
|
27979
|
+
},
|
|
27980
|
+
async getAccounts() {
|
|
27981
|
+
const provider = await this.getProvider();
|
|
27982
|
+
return provider.accounts.map((x) => viem.getAddress(x));
|
|
27983
|
+
},
|
|
27984
|
+
async getProvider() {
|
|
27985
|
+
async function initProvider() {
|
|
27986
|
+
const optionalChains = config.chains.map((x) => x.id);
|
|
27987
|
+
if (!optionalChains.length)
|
|
27988
|
+
return;
|
|
27989
|
+
const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-CeHwkxPw.js'); });
|
|
27990
|
+
const rawProvider = await EthereumProvider.init({
|
|
27991
|
+
...restParameters,
|
|
27992
|
+
disableProviderPing: true,
|
|
27993
|
+
optionalChains,
|
|
27994
|
+
projectId: restParameters.projectId,
|
|
27995
|
+
rpcMap: Object.fromEntries(config.chains.map((chain) => {
|
|
27996
|
+
const [url] = extractRpcUrls({
|
|
27997
|
+
chain,
|
|
27998
|
+
transports: config.transports,
|
|
27999
|
+
});
|
|
28000
|
+
return [chain.id, url];
|
|
28001
|
+
})),
|
|
28002
|
+
showQrModal: false,
|
|
28003
|
+
// We need to specify a custom storage prefix to avoid conflicts with Wagmi's walletConnect instance
|
|
28004
|
+
// https://docs.reown.com/walletkit/web/usage#core-instance-sharing
|
|
28005
|
+
customStoragePrefix: "squid-hedera",
|
|
28006
|
+
});
|
|
28007
|
+
const proxiedProvider = new Proxy(rawProvider, {
|
|
28008
|
+
get(target, prop, receiver) {
|
|
28009
|
+
if (prop === "request") {
|
|
28010
|
+
return async (args) => {
|
|
28011
|
+
const signingMethods = [
|
|
28012
|
+
"eth_sendTransaction",
|
|
28013
|
+
"eth_signTransaction",
|
|
28014
|
+
];
|
|
28015
|
+
if (signingMethods.includes(args.method)) {
|
|
28016
|
+
try {
|
|
28017
|
+
HederaExtensionHelper.extensionOpen(extension.id);
|
|
28018
|
+
}
|
|
28019
|
+
catch { }
|
|
28020
|
+
}
|
|
28021
|
+
// forward request to original provider
|
|
28022
|
+
return target.request(args);
|
|
28023
|
+
};
|
|
28024
|
+
}
|
|
28025
|
+
// forward all other properties/methods
|
|
28026
|
+
return Reflect.get(target, prop, receiver);
|
|
28027
|
+
},
|
|
28028
|
+
});
|
|
28029
|
+
return proxiedProvider;
|
|
28030
|
+
}
|
|
28031
|
+
if (!provider_) {
|
|
28032
|
+
if (!providerPromise)
|
|
28033
|
+
providerPromise = initProvider();
|
|
28034
|
+
provider_ = await providerPromise;
|
|
28035
|
+
provider_?.events.setMaxListeners(Number.POSITIVE_INFINITY);
|
|
28036
|
+
}
|
|
28037
|
+
return provider_;
|
|
28038
|
+
},
|
|
28039
|
+
async getChainId() {
|
|
28040
|
+
const provider = await this.getProvider();
|
|
28041
|
+
return provider.chainId;
|
|
28042
|
+
},
|
|
28043
|
+
async isAuthorized() {
|
|
28044
|
+
try {
|
|
28045
|
+
const accounts = await this.getAccounts();
|
|
28046
|
+
return accounts.length > 0;
|
|
28047
|
+
}
|
|
28048
|
+
catch {
|
|
28049
|
+
return false;
|
|
28050
|
+
}
|
|
28051
|
+
},
|
|
28052
|
+
onAccountsChanged(accounts) {
|
|
28053
|
+
if (accounts.length === 0)
|
|
28054
|
+
this.onDisconnect();
|
|
28055
|
+
else
|
|
28056
|
+
config.emitter.emit("change", {
|
|
28057
|
+
accounts: accounts.map((x) => viem.getAddress(x)),
|
|
28058
|
+
});
|
|
28059
|
+
},
|
|
28060
|
+
onChainChanged(chain) {
|
|
28061
|
+
const chainId = Number(chain);
|
|
28062
|
+
config.emitter.emit("change", { chainId });
|
|
28063
|
+
},
|
|
28064
|
+
async onConnect(connectInfo) {
|
|
28065
|
+
const chainId = Number(connectInfo.chainId);
|
|
28066
|
+
const accounts = await this.getAccounts();
|
|
28067
|
+
config.emitter.emit("connect", { accounts, chainId });
|
|
28068
|
+
},
|
|
28069
|
+
async onDisconnect() {
|
|
28070
|
+
config.emitter.emit("disconnect");
|
|
28071
|
+
const provider = await this.getProvider();
|
|
28072
|
+
if (disconnect) {
|
|
28073
|
+
provider.removeListener("disconnect", disconnect);
|
|
28074
|
+
disconnect = undefined;
|
|
28075
|
+
}
|
|
28076
|
+
if (sessionDelete) {
|
|
28077
|
+
provider.removeListener("session_delete", sessionDelete);
|
|
28078
|
+
sessionDelete = undefined;
|
|
28079
|
+
}
|
|
28080
|
+
if (!connect) {
|
|
28081
|
+
connect = this.onConnect.bind(this);
|
|
28082
|
+
provider.on("connect", connect);
|
|
28083
|
+
}
|
|
28084
|
+
},
|
|
28085
|
+
onDisplayUri(uri) {
|
|
28086
|
+
config.emitter.emit("message", { type: "display_uri", data: uri });
|
|
28087
|
+
HederaExtensionHelper.extensionConnect(extension.id, uri);
|
|
28088
|
+
},
|
|
28089
|
+
onSessionDelete() {
|
|
28090
|
+
this.onDisconnect();
|
|
28091
|
+
},
|
|
28092
|
+
}));
|
|
28093
|
+
}
|
|
28094
|
+
|
|
28095
|
+
const createWagmiConfig = (squidChains, hederaExtensions) => {
|
|
28096
|
+
const filteredEvmChains = squidChains.filter((chain) => chain.chainType === squidTypes.ChainType.EVM);
|
|
28097
|
+
if (filteredEvmChains.length === 0) {
|
|
28098
|
+
throw new Error("At least one chain is required");
|
|
28018
28099
|
}
|
|
28019
|
-
|
|
28020
|
-
return
|
|
28021
|
-
|
|
28022
|
-
|
|
28023
|
-
|
|
28100
|
+
const wagmiChains = filteredEvmChains.map((chain) => {
|
|
28101
|
+
return viem.defineChain({
|
|
28102
|
+
id: Number(chain.chainId),
|
|
28103
|
+
name: chain.networkName,
|
|
28104
|
+
nativeCurrency: {
|
|
28105
|
+
name: chain.nativeCurrency.name,
|
|
28106
|
+
symbol: chain.nativeCurrency.symbol,
|
|
28107
|
+
decimals: chain.nativeCurrency.decimals,
|
|
28024
28108
|
},
|
|
28025
|
-
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28032
|
-
const [accountInfo, serverState] = await Promise.all([
|
|
28033
|
-
this.getAccountInfo(address),
|
|
28034
|
-
this.getServerState(),
|
|
28035
|
-
]);
|
|
28036
|
-
const balance = BigInt(accountInfo.account_data.Balance);
|
|
28037
|
-
const ownerCount = BigInt(accountInfo.account_data.OwnerCount);
|
|
28038
|
-
const reserveBase = BigInt(serverState.state.validated_ledger.reserve_base);
|
|
28039
|
-
const reserveIncrement = BigInt(serverState.state.validated_ledger.reserve_inc);
|
|
28040
|
-
const reserveBalance = reserveBase + ownerCount * reserveIncrement;
|
|
28041
|
-
const spendableBalance = balance - reserveBalance;
|
|
28042
|
-
return formatBNToReadable(spendableBalance, 6);
|
|
28043
|
-
}
|
|
28044
|
-
/**
|
|
28045
|
-
* Returns the balance of the user in the given issued currency (e.g. RLUSD)
|
|
28046
|
-
* formatted as a string
|
|
28047
|
-
*/
|
|
28048
|
-
async getIssuedCurrencyBalance(address, tokenAddress) {
|
|
28049
|
-
const response = await this.getTrustLines(address);
|
|
28050
|
-
const tokenBalance = response.lines.find((line) => `${line.currency}.${line.account}` === tokenAddress);
|
|
28051
|
-
return tokenBalance?.balance || "0";
|
|
28052
|
-
}
|
|
28053
|
-
async call(method, params) {
|
|
28054
|
-
const response = await fetch(this.rpcUrl, {
|
|
28055
|
-
method: "POST",
|
|
28056
|
-
headers: {
|
|
28057
|
-
"Content-Type": "application/json",
|
|
28109
|
+
rpcUrls: {
|
|
28110
|
+
public: {
|
|
28111
|
+
http: [chain.rpc],
|
|
28112
|
+
},
|
|
28113
|
+
default: {
|
|
28114
|
+
http: [chain.rpc],
|
|
28115
|
+
},
|
|
28058
28116
|
},
|
|
28059
|
-
body: JSON.stringify({
|
|
28060
|
-
jsonrpc: "2.0",
|
|
28061
|
-
id: 1,
|
|
28062
|
-
method,
|
|
28063
|
-
params,
|
|
28064
|
-
}),
|
|
28065
28117
|
});
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
|
|
28069
|
-
|
|
28070
|
-
|
|
28071
|
-
|
|
28072
|
-
|
|
28073
|
-
|
|
28118
|
+
});
|
|
28119
|
+
const wcMetadata = {
|
|
28120
|
+
url: SQUID_METADATA.url,
|
|
28121
|
+
name: SQUID_METADATA.name,
|
|
28122
|
+
icons: [SQUID_METADATA.icon],
|
|
28123
|
+
description: SQUID_METADATA.description,
|
|
28124
|
+
};
|
|
28125
|
+
return wagmi.createConfig({
|
|
28126
|
+
chains: wagmiChains,
|
|
28127
|
+
transports: Object.fromEntries(wagmiChains.map((chain) => [
|
|
28128
|
+
chain.id,
|
|
28129
|
+
wagmi.http(chain.rpcUrls.public.http[0] ?? ""),
|
|
28130
|
+
])),
|
|
28131
|
+
connectors: [
|
|
28132
|
+
connectors.injected(),
|
|
28133
|
+
connectors.safe({
|
|
28134
|
+
allowedDomains: [/app.safe.global$/],
|
|
28135
|
+
}),
|
|
28136
|
+
connectors.metaMask({
|
|
28137
|
+
dappMetadata: {
|
|
28138
|
+
name: SQUID_METADATA.name,
|
|
28139
|
+
url: SQUID_METADATA.url,
|
|
28140
|
+
iconUrl: SQUID_METADATA.icon,
|
|
28141
|
+
},
|
|
28142
|
+
}),
|
|
28143
|
+
connectors.coinbaseWallet({
|
|
28144
|
+
appName: SQUID_METADATA.name,
|
|
28145
|
+
appLogoUrl: SQUID_METADATA.icon,
|
|
28146
|
+
}),
|
|
28147
|
+
connectors.walletConnect({
|
|
28148
|
+
projectId: WALLETCONNECT_PROJECT_ID,
|
|
28149
|
+
metadata: wcMetadata,
|
|
28150
|
+
}),
|
|
28151
|
+
...hederaExtensions.map((extension) => hederaWalletConnect({
|
|
28152
|
+
projectId: WALLETCONNECT_PROJECT_ID,
|
|
28153
|
+
metadata: wcMetadata,
|
|
28154
|
+
extension,
|
|
28155
|
+
})),
|
|
28156
|
+
],
|
|
28157
|
+
});
|
|
28158
|
+
};
|
|
28159
|
+
// Taken from wagmi docs
|
|
28160
|
+
// https://wagmi.sh/react/guides/ethers
|
|
28161
|
+
function clientToSigner(client) {
|
|
28162
|
+
const { account, chain, transport } = client;
|
|
28163
|
+
if (!account || !chain || !transport) {
|
|
28164
|
+
return undefined;
|
|
28074
28165
|
}
|
|
28166
|
+
const network = {
|
|
28167
|
+
chainId: chain.id,
|
|
28168
|
+
name: chain.name,
|
|
28169
|
+
ensAddress: chain.contracts?.ensRegistry?.address,
|
|
28170
|
+
};
|
|
28171
|
+
const provider = new ethers.BrowserProvider(transport, network);
|
|
28172
|
+
const signer = new ethers.JsonRpcSigner(provider, account.address);
|
|
28173
|
+
return signer;
|
|
28075
28174
|
}
|
|
28076
28175
|
|
|
28077
|
-
|
|
28078
|
-
|
|
28079
|
-
const
|
|
28080
|
-
|
|
28081
|
-
|
|
28082
|
-
}
|
|
28083
|
-
const client = await createClient(chain);
|
|
28084
|
-
clientCache.set(key, client);
|
|
28085
|
-
return client;
|
|
28176
|
+
function useEvmSigner({ chainId }) {
|
|
28177
|
+
const { connector } = wagmi.useAccount();
|
|
28178
|
+
const { data: client } = wagmi.useWalletClient({ chainId, connector });
|
|
28179
|
+
const signer = React.useMemo(() => (client ? clientToSigner(client) : undefined), [client]);
|
|
28180
|
+
return { signer };
|
|
28086
28181
|
}
|
|
28087
|
-
|
|
28088
|
-
|
|
28089
|
-
|
|
28090
|
-
|
|
28091
|
-
|
|
28092
|
-
|
|
28093
|
-
|
|
28094
|
-
|
|
28095
|
-
|
|
28096
|
-
|
|
28097
|
-
|
|
28098
|
-
|
|
28099
|
-
|
|
28100
|
-
|
|
28182
|
+
const useSigner = ({ chain }) => {
|
|
28183
|
+
const evmChainId = chain?.chainType === squidTypes.ChainType.EVM ? Number(chain.chainId) : undefined;
|
|
28184
|
+
// EVM and Cosmos need a different signer for each chain
|
|
28185
|
+
// This is not the case for Solana or Bitcoin as there's only one chain in those ecosystems
|
|
28186
|
+
const { signer: evmSigner } = useEvmSigner({ chainId: evmChainId });
|
|
28187
|
+
const { signer: cosmosSigner } = useCosmosSigner({ chain });
|
|
28188
|
+
const { signer: solanaSigner } = useSolanaContext();
|
|
28189
|
+
const { signer: bitcoinSigner } = useBitcoinContext();
|
|
28190
|
+
const { signer: suiSigner } = useSuiContext();
|
|
28191
|
+
const { signer: xrplSigner } = useXrplContext();
|
|
28192
|
+
const { signer: stellarSigner } = useStellarContext();
|
|
28193
|
+
const isEvmSignerReady = !!evmSigner;
|
|
28194
|
+
const isSolanaSignerReady = !!solanaSigner;
|
|
28195
|
+
const isCosmosSignerReady = !!cosmosSigner;
|
|
28196
|
+
const isBitcoinSignerReady = !!bitcoinSigner;
|
|
28197
|
+
const isSuiSignerReady = !!suiSigner;
|
|
28198
|
+
const isXrplSignerReady = !!xrplSigner;
|
|
28199
|
+
const isStellarSignerReady = !!stellarSigner;
|
|
28200
|
+
const isSignerReady = React.useMemo(() => {
|
|
28201
|
+
if (!chain?.chainType)
|
|
28202
|
+
return false;
|
|
28203
|
+
switch (chain.chainType) {
|
|
28204
|
+
case squidTypes.ChainType.EVM:
|
|
28205
|
+
return isEvmSignerReady;
|
|
28206
|
+
case squidTypes.ChainType.COSMOS:
|
|
28207
|
+
return isCosmosSignerReady;
|
|
28208
|
+
case squidTypes.ChainType.BTC:
|
|
28209
|
+
return isBitcoinSignerReady;
|
|
28210
|
+
case squidTypes.ChainType.SOLANA:
|
|
28211
|
+
return isSolanaSignerReady;
|
|
28212
|
+
case squidTypes.ChainType.SUI:
|
|
28213
|
+
return isSuiSignerReady;
|
|
28214
|
+
case squidTypes.ChainType.XRPL:
|
|
28215
|
+
return isXrplSignerReady;
|
|
28216
|
+
case squidTypes.ChainType.STELLAR:
|
|
28217
|
+
return isStellarSignerReady;
|
|
28218
|
+
}
|
|
28219
|
+
}, [
|
|
28220
|
+
chain?.chainType,
|
|
28221
|
+
isEvmSignerReady,
|
|
28222
|
+
isCosmosSignerReady,
|
|
28223
|
+
isBitcoinSignerReady,
|
|
28224
|
+
isSolanaSignerReady,
|
|
28225
|
+
isSuiSignerReady,
|
|
28226
|
+
isXrplSignerReady,
|
|
28227
|
+
isStellarSignerReady,
|
|
28228
|
+
]);
|
|
28229
|
+
return {
|
|
28230
|
+
isSignerReady,
|
|
28231
|
+
evmSigner,
|
|
28232
|
+
cosmosSigner,
|
|
28233
|
+
bitcoinSigner,
|
|
28234
|
+
solanaSigner,
|
|
28235
|
+
suiSigner,
|
|
28236
|
+
xrplSigner,
|
|
28237
|
+
stellarSigner,
|
|
28238
|
+
};
|
|
28239
|
+
};
|
|
28240
|
+
|
|
28241
|
+
function useHederaTokenAssociations({ address, chain, token }) {
|
|
28242
|
+
const publicClient = wagmi.usePublicClient({
|
|
28243
|
+
chainId: Number(CHAIN_IDS.HEDERA),
|
|
28244
|
+
});
|
|
28245
|
+
const { evmSigner } = useSigner({ chain });
|
|
28246
|
+
const queryClient = reactQuery.useQueryClient();
|
|
28247
|
+
/**
|
|
28248
|
+
* Creates a token association transaction where the destination account authorizes to receive the given token
|
|
28249
|
+
*/
|
|
28250
|
+
const associateToken = reactQuery.useMutation({
|
|
28251
|
+
mutationFn: async () => {
|
|
28252
|
+
try {
|
|
28253
|
+
if (!evmSigner) {
|
|
28254
|
+
throw new Error("EVM signer not found");
|
|
28255
|
+
}
|
|
28256
|
+
if (chain?.chainId !== CHAIN_IDS.HEDERA ||
|
|
28257
|
+
token?.chainId !== CHAIN_IDS.HEDERA) {
|
|
28258
|
+
throw new Error("Chain and token to associate must be on Hedera");
|
|
28259
|
+
}
|
|
28260
|
+
const tokenAddress = parseEvmAddress(token.address);
|
|
28261
|
+
if (!tokenAddress) {
|
|
28262
|
+
throw new Error("Invalid token address");
|
|
28263
|
+
}
|
|
28264
|
+
const userAddress = parseEvmAddress(address);
|
|
28265
|
+
if (!userAddress) {
|
|
28266
|
+
throw new Error("Invalid user address");
|
|
28267
|
+
}
|
|
28268
|
+
const encodedData = viem.encodeFunctionData({
|
|
28269
|
+
abi: hrc20,
|
|
28270
|
+
functionName: "associate",
|
|
28271
|
+
args: [],
|
|
28272
|
+
});
|
|
28273
|
+
const txRes = await evmSigner.sendTransaction({
|
|
28274
|
+
data: encodedData,
|
|
28275
|
+
to: tokenAddress,
|
|
28276
|
+
chainId: chain.chainId,
|
|
28277
|
+
});
|
|
28278
|
+
const receipt = await txRes.wait();
|
|
28279
|
+
if (receipt?.status !== 1) {
|
|
28280
|
+
throw new Error(`Transaction failed with status: ${receipt?.status}`);
|
|
28281
|
+
}
|
|
28282
|
+
return true;
|
|
28283
|
+
}
|
|
28284
|
+
catch (error) {
|
|
28285
|
+
console.error("Error associating Hedera token:", error);
|
|
28286
|
+
return false;
|
|
28287
|
+
}
|
|
28288
|
+
},
|
|
28289
|
+
async onSuccess() {
|
|
28290
|
+
queryClient.refetchQueries({
|
|
28291
|
+
queryKey: getPrefixKey(exports.QueryKeys.IsHederaTokenAssociated),
|
|
28101
28292
|
});
|
|
28102
|
-
|
|
28103
|
-
|
|
28104
|
-
|
|
28105
|
-
|
|
28106
|
-
|
|
28293
|
+
},
|
|
28294
|
+
});
|
|
28295
|
+
/**
|
|
28296
|
+
* Checks if the destination account has associated the given token.
|
|
28297
|
+
*
|
|
28298
|
+
* Hedera requires accounts to associate a token before being able to receive it.
|
|
28299
|
+
*
|
|
28300
|
+
* Accounts which have max. associations set to -1 can receive any token without previous association
|
|
28301
|
+
*/
|
|
28302
|
+
const isTokenAssociated = reactQuery.useQuery({
|
|
28303
|
+
queryKey: keys().isHederaTokenAssociated(address, token?.chainId, token?.type, token?.address),
|
|
28304
|
+
queryFn: async () => {
|
|
28305
|
+
if (token?.chainId !== CHAIN_IDS.HEDERA) {
|
|
28306
|
+
return true;
|
|
28307
|
+
}
|
|
28308
|
+
// The native HBAR token doesn't need an association
|
|
28309
|
+
if (token.address.toLowerCase() === nativeEvmTokenAddress.toLowerCase()) {
|
|
28310
|
+
return true;
|
|
28311
|
+
}
|
|
28312
|
+
if (!chain || !address || !publicClient) {
|
|
28313
|
+
throw new Error("Missing required parameters");
|
|
28314
|
+
}
|
|
28315
|
+
// TODO: update types
|
|
28316
|
+
const apiUrl = chain?.chainConfig?.hedera?.mirrorNodeUrl;
|
|
28317
|
+
if (!apiUrl) {
|
|
28318
|
+
throw new Error("Missing Hedera mirror node URL in chain config");
|
|
28319
|
+
}
|
|
28320
|
+
const hederaApiClient = new HederaApiClient(apiUrl);
|
|
28321
|
+
return hederaApiClient.isTokenAssociated({
|
|
28322
|
+
address,
|
|
28323
|
+
token,
|
|
28324
|
+
});
|
|
28325
|
+
},
|
|
28326
|
+
enabled: !!address && !!publicClient && token?.chainId === CHAIN_IDS.HEDERA,
|
|
28327
|
+
});
|
|
28328
|
+
return {
|
|
28329
|
+
isTokenAssociated,
|
|
28330
|
+
associateToken,
|
|
28331
|
+
};
|
|
28107
28332
|
}
|
|
28108
28333
|
|
|
28334
|
+
const useKeyboardNavigation = ({ onEscape }) => {
|
|
28335
|
+
const onKeyDown = React.useCallback((event) => {
|
|
28336
|
+
if (event.key === "Escape") {
|
|
28337
|
+
onEscape?.();
|
|
28338
|
+
return;
|
|
28339
|
+
}
|
|
28340
|
+
}, [onEscape]);
|
|
28341
|
+
React.useEffect(() => {
|
|
28342
|
+
document.addEventListener("keydown", onKeyDown, false);
|
|
28343
|
+
return () => {
|
|
28344
|
+
document.removeEventListener("keydown", onKeyDown, false);
|
|
28345
|
+
};
|
|
28346
|
+
}, [onKeyDown]);
|
|
28347
|
+
};
|
|
28348
|
+
|
|
28349
|
+
const useSquidQueryClient = () => {
|
|
28350
|
+
const queryClient = reactQuery.useQueryClient();
|
|
28351
|
+
const invalidateQueries = (key) => {
|
|
28352
|
+
const prefixKey = getPrefixKey(key);
|
|
28353
|
+
queryClient.invalidateQueries(prefixKey);
|
|
28354
|
+
};
|
|
28355
|
+
const refetchQueries = (key) => {
|
|
28356
|
+
const prefixKey = getPrefixKey(key);
|
|
28357
|
+
queryClient.refetchQueries(prefixKey);
|
|
28358
|
+
};
|
|
28359
|
+
const invalidateAndRefetchQueries = (key) => {
|
|
28360
|
+
invalidateQueries(key);
|
|
28361
|
+
refetchQueries(key);
|
|
28362
|
+
};
|
|
28363
|
+
return {
|
|
28364
|
+
invalidateQueries,
|
|
28365
|
+
refetchQueries,
|
|
28366
|
+
invalidateAndRefetchQueries,
|
|
28367
|
+
};
|
|
28368
|
+
};
|
|
28369
|
+
|
|
28109
28370
|
/**
|
|
28110
28371
|
* The default multicall3 address
|
|
28111
28372
|
* available on most EVM chains
|
|
@@ -29120,6 +29381,372 @@ function timeout(ms, promise) {
|
|
|
29120
29381
|
return Promise.race([promise, timeoutPromise]);
|
|
29121
29382
|
}
|
|
29122
29383
|
|
|
29384
|
+
class StellarRpcClient {
|
|
29385
|
+
server;
|
|
29386
|
+
constructor(rpcUrl) {
|
|
29387
|
+
this.server = new stellarSdk.rpc.Server(rpcUrl);
|
|
29388
|
+
}
|
|
29389
|
+
/**
|
|
29390
|
+
* Returns the balance of a Stellar Contract Token. This is different from an Issued Token.
|
|
29391
|
+
*
|
|
29392
|
+
* With Contract Tokens, we need to call the .balance method on the token contract
|
|
29393
|
+
* and simulate the transaction to get the balance.
|
|
29394
|
+
*/
|
|
29395
|
+
async getBalance(userAddress, tokenAddress, chainId) {
|
|
29396
|
+
const account = await this.server.getAccount(userAddress);
|
|
29397
|
+
const network = getStellarNetwork(chainId);
|
|
29398
|
+
if (network == null) {
|
|
29399
|
+
throw new Error(`No Stellar network found for chainId ${chainId}`);
|
|
29400
|
+
}
|
|
29401
|
+
const txBuilder = new stellarSdk.TransactionBuilder(account, {
|
|
29402
|
+
fee: (BigInt(stellarSdk.BASE_FEE) * BigInt(2)).toString(),
|
|
29403
|
+
networkPassphrase: network,
|
|
29404
|
+
});
|
|
29405
|
+
const contract = new stellarSdk.Contract(tokenAddress);
|
|
29406
|
+
const tx = txBuilder
|
|
29407
|
+
.addOperation(contract.call("balance", new stellarSdk.Address(userAddress).toScVal()))
|
|
29408
|
+
.setTimeout(stellarSdk.TimeoutInfinite)
|
|
29409
|
+
.build();
|
|
29410
|
+
const simulateTxResponse = await this.server.simulateTransaction(tx);
|
|
29411
|
+
if ("error" in simulateTxResponse) {
|
|
29412
|
+
const isNoBalanceError = simulateTxResponse.error.includes("trying to get non-existing value for contract instance");
|
|
29413
|
+
// If the error message indicates that the user has no balance just return 0
|
|
29414
|
+
// We don't want to spam with this error as it's pretty common
|
|
29415
|
+
if (isNoBalanceError) {
|
|
29416
|
+
return "0";
|
|
29417
|
+
}
|
|
29418
|
+
throw new Error(`Failed to fetch balance. RPC response: ${simulateTxResponse.error}`);
|
|
29419
|
+
}
|
|
29420
|
+
if ("result" in simulateTxResponse && simulateTxResponse.result != null) {
|
|
29421
|
+
const native = stellarSdk.scValToNative(simulateTxResponse.result.retval);
|
|
29422
|
+
return native.toString();
|
|
29423
|
+
}
|
|
29424
|
+
throw new Error("Failed to fetch balance");
|
|
29425
|
+
}
|
|
29426
|
+
async getAllBalances(userAddress, tokens) {
|
|
29427
|
+
const balancePromises = tokens.map((token) => {
|
|
29428
|
+
return this.getBalance(userAddress, token.address, token.chainId);
|
|
29429
|
+
});
|
|
29430
|
+
const results = await Promise.allSettled(balancePromises);
|
|
29431
|
+
const balances = results.map((result) => {
|
|
29432
|
+
if (result.status === "fulfilled") {
|
|
29433
|
+
return result.value;
|
|
29434
|
+
}
|
|
29435
|
+
return "0";
|
|
29436
|
+
});
|
|
29437
|
+
return balances.map((balance, index) => {
|
|
29438
|
+
return {
|
|
29439
|
+
...tokens[index],
|
|
29440
|
+
balance,
|
|
29441
|
+
};
|
|
29442
|
+
});
|
|
29443
|
+
}
|
|
29444
|
+
/**
|
|
29445
|
+
* Resolves when the transaction is confirmed, or fails after a timeout.
|
|
29446
|
+
*/
|
|
29447
|
+
async waitForTransaction(txHash, { interval = 2_000, timeout = 40_000 } = {}) {
|
|
29448
|
+
const startTime = Date.now();
|
|
29449
|
+
while (true) {
|
|
29450
|
+
const result = await this.server.getTransaction(txHash);
|
|
29451
|
+
if (result.status === stellarSdk.rpc.Api.GetTransactionStatus.NOT_FOUND) {
|
|
29452
|
+
if (Date.now() - startTime > timeout) {
|
|
29453
|
+
throw new Error(`Transaction ${txHash} not found within timeout`);
|
|
29454
|
+
}
|
|
29455
|
+
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
29456
|
+
await new Promise((res) => setTimeout(res, interval));
|
|
29457
|
+
continue;
|
|
29458
|
+
}
|
|
29459
|
+
if (result.status === stellarSdk.rpc.Api.GetTransactionStatus.SUCCESS) {
|
|
29460
|
+
return result.status;
|
|
29461
|
+
}
|
|
29462
|
+
throw new Error(`Transaction failed with status: ${result.status}`);
|
|
29463
|
+
}
|
|
29464
|
+
}
|
|
29465
|
+
async isAccountActivated(address) {
|
|
29466
|
+
try {
|
|
29467
|
+
await this.getAccount(address);
|
|
29468
|
+
return true;
|
|
29469
|
+
}
|
|
29470
|
+
catch (error) {
|
|
29471
|
+
if (error?.message && error.message.includes("Account not found")) {
|
|
29472
|
+
return false;
|
|
29473
|
+
}
|
|
29474
|
+
throw error;
|
|
29475
|
+
}
|
|
29476
|
+
}
|
|
29477
|
+
async getAccount(address) {
|
|
29478
|
+
return this.server.getAccount(address);
|
|
29479
|
+
}
|
|
29480
|
+
async sendTransaction(transaction) {
|
|
29481
|
+
const transactionResponse = await this.server.sendTransaction(transaction);
|
|
29482
|
+
if (transactionResponse.status === "ERROR") {
|
|
29483
|
+
throw new Error("Error sending transaction");
|
|
29484
|
+
}
|
|
29485
|
+
return transactionResponse;
|
|
29486
|
+
}
|
|
29487
|
+
async prepareTransaction(transaction) {
|
|
29488
|
+
return this.server.prepareTransaction(transaction);
|
|
29489
|
+
}
|
|
29490
|
+
}
|
|
29491
|
+
|
|
29492
|
+
class XrplRpcClient {
|
|
29493
|
+
rpcUrl;
|
|
29494
|
+
constructor(rpcUrl) {
|
|
29495
|
+
this.rpcUrl = rpcUrl;
|
|
29496
|
+
}
|
|
29497
|
+
async getBalance(address, tokenAddress) {
|
|
29498
|
+
if (tokenAddress.toLowerCase() === nativeXrplTokenAddress.toLowerCase()) {
|
|
29499
|
+
return this.getNativeBalance(address);
|
|
29500
|
+
}
|
|
29501
|
+
return this.getIssuedCurrencyBalance(address, tokenAddress);
|
|
29502
|
+
}
|
|
29503
|
+
async getAllBalances(address) {
|
|
29504
|
+
const [nativeBalance, trustLineBalances] = await Promise.all([
|
|
29505
|
+
this.getNativeBalance(address),
|
|
29506
|
+
this.getTrustLines(address),
|
|
29507
|
+
]);
|
|
29508
|
+
return [
|
|
29509
|
+
{
|
|
29510
|
+
balance: nativeBalance,
|
|
29511
|
+
address: nativeXrplTokenAddress,
|
|
29512
|
+
},
|
|
29513
|
+
...trustLineBalances.lines.map((line) => ({
|
|
29514
|
+
balance: line.balance,
|
|
29515
|
+
address: `${line.currency}.${line.account}`,
|
|
29516
|
+
})),
|
|
29517
|
+
];
|
|
29518
|
+
}
|
|
29519
|
+
async getTrustLines(address, issuer) {
|
|
29520
|
+
return this.call("account_lines", [
|
|
29521
|
+
{
|
|
29522
|
+
account: address,
|
|
29523
|
+
ledger_index: "validated",
|
|
29524
|
+
peer: issuer,
|
|
29525
|
+
},
|
|
29526
|
+
]);
|
|
29527
|
+
}
|
|
29528
|
+
async getTrustLine(address, issuer, currency) {
|
|
29529
|
+
const response = await this.getTrustLines(address, issuer);
|
|
29530
|
+
const trustLine = response.lines.find((line) => line.currency === currency);
|
|
29531
|
+
return trustLine ?? null;
|
|
29532
|
+
}
|
|
29533
|
+
async accountActivatedInfo(address) {
|
|
29534
|
+
const serverState = await this.getServerState();
|
|
29535
|
+
const reserveBaseBn = BigInt(serverState.state.validated_ledger.reserve_base);
|
|
29536
|
+
try {
|
|
29537
|
+
const accountInfo = await this.getAccountInfo(address);
|
|
29538
|
+
const balanceBn = BigInt(accountInfo.account_data.Balance);
|
|
29539
|
+
return {
|
|
29540
|
+
isActivated: balanceBn >= reserveBaseBn,
|
|
29541
|
+
reserveBaseBn,
|
|
29542
|
+
};
|
|
29543
|
+
}
|
|
29544
|
+
catch (error) {
|
|
29545
|
+
if (error.message?.includes("actNotFound")) {
|
|
29546
|
+
return { isActivated: false, reserveBaseBn };
|
|
29547
|
+
}
|
|
29548
|
+
throw error;
|
|
29549
|
+
}
|
|
29550
|
+
}
|
|
29551
|
+
/**
|
|
29552
|
+
* Waits for a transaction to be validated and returns its final status.
|
|
29553
|
+
* Resolves to 'success' or throws an error with the failed status.
|
|
29554
|
+
*/
|
|
29555
|
+
async waitForTransaction(txHash, { interval = 2_000, timeout = 20_000 } = {}) {
|
|
29556
|
+
const startTime = Date.now();
|
|
29557
|
+
while (true) {
|
|
29558
|
+
try {
|
|
29559
|
+
const response = await this.call("tx", [
|
|
29560
|
+
{
|
|
29561
|
+
transaction: txHash,
|
|
29562
|
+
binary: false,
|
|
29563
|
+
},
|
|
29564
|
+
]);
|
|
29565
|
+
if (!response.validated) {
|
|
29566
|
+
if (Date.now() - startTime > timeout) {
|
|
29567
|
+
throw new Error(`Transaction ${txHash} not validated within timeout`);
|
|
29568
|
+
}
|
|
29569
|
+
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
29570
|
+
await new Promise((res) => setTimeout(res, interval));
|
|
29571
|
+
continue;
|
|
29572
|
+
}
|
|
29573
|
+
const status = response.meta?.TransactionResult;
|
|
29574
|
+
if (status === XrplTxStatus.SUCCESS) {
|
|
29575
|
+
return status;
|
|
29576
|
+
}
|
|
29577
|
+
else {
|
|
29578
|
+
throw new Error(`Transaction failed with status: ${status}`);
|
|
29579
|
+
}
|
|
29580
|
+
}
|
|
29581
|
+
catch (error) {
|
|
29582
|
+
// txnNotFound = still pending or non-existent
|
|
29583
|
+
if (error?.message?.includes("txnNotFound")) {
|
|
29584
|
+
if (Date.now() - startTime > timeout) {
|
|
29585
|
+
throw new Error(`Transaction ${txHash} not found within timeout`);
|
|
29586
|
+
}
|
|
29587
|
+
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
29588
|
+
await new Promise((res) => setTimeout(res, interval));
|
|
29589
|
+
continue;
|
|
29590
|
+
}
|
|
29591
|
+
throw error;
|
|
29592
|
+
}
|
|
29593
|
+
}
|
|
29594
|
+
}
|
|
29595
|
+
async getServerState() {
|
|
29596
|
+
return this.call("server_state", [{}]);
|
|
29597
|
+
}
|
|
29598
|
+
async getAccountInfo(address) {
|
|
29599
|
+
return this.call("account_info", [
|
|
29600
|
+
{
|
|
29601
|
+
account: address,
|
|
29602
|
+
ledger_index: "validated",
|
|
29603
|
+
},
|
|
29604
|
+
]);
|
|
29605
|
+
}
|
|
29606
|
+
/**
|
|
29607
|
+
* Returns the balance of the user in the native XRP token
|
|
29608
|
+
* formatted as a string
|
|
29609
|
+
*/
|
|
29610
|
+
async getNativeBalance(address) {
|
|
29611
|
+
const [accountInfo, serverState] = await Promise.all([
|
|
29612
|
+
this.getAccountInfo(address),
|
|
29613
|
+
this.getServerState(),
|
|
29614
|
+
]);
|
|
29615
|
+
const balance = BigInt(accountInfo.account_data.Balance);
|
|
29616
|
+
const ownerCount = BigInt(accountInfo.account_data.OwnerCount);
|
|
29617
|
+
const reserveBase = BigInt(serverState.state.validated_ledger.reserve_base);
|
|
29618
|
+
const reserveIncrement = BigInt(serverState.state.validated_ledger.reserve_inc);
|
|
29619
|
+
const reserveBalance = reserveBase + ownerCount * reserveIncrement;
|
|
29620
|
+
const spendableBalance = balance - reserveBalance;
|
|
29621
|
+
return formatBNToReadable(spendableBalance, 6);
|
|
29622
|
+
}
|
|
29623
|
+
/**
|
|
29624
|
+
* Returns the balance of the user in the given issued currency (e.g. RLUSD)
|
|
29625
|
+
* formatted as a string
|
|
29626
|
+
*/
|
|
29627
|
+
async getIssuedCurrencyBalance(address, tokenAddress) {
|
|
29628
|
+
const response = await this.getTrustLines(address);
|
|
29629
|
+
const tokenBalance = response.lines.find((line) => `${line.currency}.${line.account}` === tokenAddress);
|
|
29630
|
+
return tokenBalance?.balance || "0";
|
|
29631
|
+
}
|
|
29632
|
+
async call(method, params) {
|
|
29633
|
+
const response = await fetch(this.rpcUrl, {
|
|
29634
|
+
method: "POST",
|
|
29635
|
+
headers: {
|
|
29636
|
+
"Content-Type": "application/json",
|
|
29637
|
+
},
|
|
29638
|
+
body: JSON.stringify({
|
|
29639
|
+
jsonrpc: "2.0",
|
|
29640
|
+
id: 1,
|
|
29641
|
+
method,
|
|
29642
|
+
params,
|
|
29643
|
+
}),
|
|
29644
|
+
});
|
|
29645
|
+
const data = await response.json();
|
|
29646
|
+
if (!data.result) {
|
|
29647
|
+
throw new Error(`Invalid response from RPC (${method})`);
|
|
29648
|
+
}
|
|
29649
|
+
if ("error" in data.result) {
|
|
29650
|
+
throw new Error(`Error from RPC (${method}): ${data.result.error}`);
|
|
29651
|
+
}
|
|
29652
|
+
return data.result;
|
|
29653
|
+
}
|
|
29654
|
+
}
|
|
29655
|
+
|
|
29656
|
+
const clientCache = new Map();
|
|
29657
|
+
async function getClient(chain) {
|
|
29658
|
+
const key = `${chain.chainType}:${chain.chainId}`;
|
|
29659
|
+
if (clientCache.has(key)) {
|
|
29660
|
+
return clientCache.get(key);
|
|
29661
|
+
}
|
|
29662
|
+
const client = await createClient(chain);
|
|
29663
|
+
clientCache.set(key, client);
|
|
29664
|
+
return client;
|
|
29665
|
+
}
|
|
29666
|
+
async function createClient(chain) {
|
|
29667
|
+
switch (chain.chainType) {
|
|
29668
|
+
case squidTypes.ChainType.EVM:
|
|
29669
|
+
return new ethers.JsonRpcProvider(chain.rpc);
|
|
29670
|
+
case squidTypes.ChainType.COSMOS:
|
|
29671
|
+
const rpcUrl = await getWorkingCosmosRpcUrl(chain);
|
|
29672
|
+
return (await stargate.StargateClient.connect(rpcUrl));
|
|
29673
|
+
case squidTypes.ChainType.SOLANA:
|
|
29674
|
+
return new web3_js.Connection(SOLANA_RPC_URL);
|
|
29675
|
+
case squidTypes.ChainType.BTC:
|
|
29676
|
+
return null;
|
|
29677
|
+
case squidTypes.ChainType.SUI:
|
|
29678
|
+
return new client.SuiClient({
|
|
29679
|
+
url: chain.rpc,
|
|
29680
|
+
});
|
|
29681
|
+
case squidTypes.ChainType.XRPL:
|
|
29682
|
+
return new XrplRpcClient(chain.rpc);
|
|
29683
|
+
case squidTypes.ChainType.STELLAR:
|
|
29684
|
+
return new StellarRpcClient(chain.rpc);
|
|
29685
|
+
}
|
|
29686
|
+
}
|
|
29687
|
+
|
|
29688
|
+
class StellarApiClient {
|
|
29689
|
+
apiUrl;
|
|
29690
|
+
constructor(apiUrl) {
|
|
29691
|
+
this.apiUrl = apiUrl;
|
|
29692
|
+
}
|
|
29693
|
+
async getBaseReserve() {
|
|
29694
|
+
const response = await fetch(`${this.apiUrl}/ledgers?order=desc&limit=1`);
|
|
29695
|
+
if (!response.ok) {
|
|
29696
|
+
throw new Error(`Failed to fetch ledgers: ${response.status}`);
|
|
29697
|
+
}
|
|
29698
|
+
const ledgers = await response.json();
|
|
29699
|
+
const latestLedger = ledgers?._embedded.records?.[0];
|
|
29700
|
+
if (latestLedger?.base_reserve_in_stroops == null) {
|
|
29701
|
+
throw new Error("Invalid ledger data");
|
|
29702
|
+
}
|
|
29703
|
+
const baseReserveBn = BigInt(latestLedger.base_reserve_in_stroops);
|
|
29704
|
+
return baseReserveBn;
|
|
29705
|
+
}
|
|
29706
|
+
}
|
|
29707
|
+
|
|
29708
|
+
const DEFAULT_REFETCH_INTERVAL$1 = 20_000;
|
|
29709
|
+
function useStellarAccountActivation({ address, chain, token, }) {
|
|
29710
|
+
/**
|
|
29711
|
+
* Checks if the destination account exists on the Stellar network
|
|
29712
|
+
* Stellar accounts need to have a minimum balance before they can receive payments
|
|
29713
|
+
*/
|
|
29714
|
+
const accountActivatedInfo = reactQuery.useQuery({
|
|
29715
|
+
queryKey: keys().stellarAccountActivatedInfo(address, chain?.chainId, chain?.chainType),
|
|
29716
|
+
queryFn: async () => {
|
|
29717
|
+
if (chain?.chainType !== squidTypes.ChainType.STELLAR ||
|
|
29718
|
+
token?.type !== squidTypes.ChainType.STELLAR) {
|
|
29719
|
+
return null;
|
|
29720
|
+
}
|
|
29721
|
+
if (!address) {
|
|
29722
|
+
throw new Error("Destination address is required");
|
|
29723
|
+
}
|
|
29724
|
+
// TODO: update types
|
|
29725
|
+
const [horizonApiUrl] = chain?.horizonRpcList;
|
|
29726
|
+
if (typeof horizonApiUrl !== "string") {
|
|
29727
|
+
throw new Error("Invalid Horizon API URL");
|
|
29728
|
+
}
|
|
29729
|
+
const stellarApiClient = new StellarApiClient(horizonApiUrl);
|
|
29730
|
+
const reserveBase = await stellarApiClient.getBaseReserve();
|
|
29731
|
+
// Stellar accounts require two base reserves to be activated
|
|
29732
|
+
// https://developers.stellar.org/docs/learn/fundamentals/lumens#minimum-balance
|
|
29733
|
+
const accountReserveBase = reserveBase * BigInt(2);
|
|
29734
|
+
const stellarRpcClient = await getClient(chain);
|
|
29735
|
+
const isActivated = await stellarRpcClient.isAccountActivated(address);
|
|
29736
|
+
return {
|
|
29737
|
+
isActivated,
|
|
29738
|
+
reserveBaseBn: accountReserveBase,
|
|
29739
|
+
};
|
|
29740
|
+
},
|
|
29741
|
+
enabled: !!address && chain?.chainType === squidTypes.ChainType.STELLAR,
|
|
29742
|
+
refetchInterval: DEFAULT_REFETCH_INTERVAL$1,
|
|
29743
|
+
refetchOnWindowFocus: true,
|
|
29744
|
+
});
|
|
29745
|
+
return {
|
|
29746
|
+
accountActivatedInfo,
|
|
29747
|
+
};
|
|
29748
|
+
}
|
|
29749
|
+
|
|
29123
29750
|
const DEFAULT_REFRESH_INTERVAL_MS = 15000;
|
|
29124
29751
|
const useEvmBalance = ({ chain, token, userAddress, enabled = true, refreshIntervalMs = DEFAULT_REFRESH_INTERVAL_MS, }) => {
|
|
29125
29752
|
const { isChainTypeConnected } = useWallet();
|
|
@@ -29314,8 +29941,6 @@ function useNativeTokenForChain(chain) {
|
|
|
29314
29941
|
}
|
|
29315
29942
|
};
|
|
29316
29943
|
const nativeTokenForChainType = React.useMemo(() => {
|
|
29317
|
-
if (!chain)
|
|
29318
|
-
return undefined;
|
|
29319
29944
|
return findNativeToken(getTokensForChainType(), chain);
|
|
29320
29945
|
}, [chain]);
|
|
29321
29946
|
return { nativeToken: nativeTokenForChainType };
|
|
@@ -29431,814 +30056,178 @@ const useSuiNativeBalance = ({ address, chain, }) => {
|
|
|
29431
30056
|
token: nativeToken,
|
|
29432
30057
|
userAddress: address,
|
|
29433
30058
|
});
|
|
29434
|
-
const balance = React.useMemo(() => {
|
|
29435
|
-
if (nativeToken?.decimals && rawBalance) {
|
|
29436
|
-
return {
|
|
29437
|
-
decimals: nativeToken.decimals,
|
|
29438
|
-
value: parseToBigInt(rawBalance, nativeToken.decimals),
|
|
29439
|
-
};
|
|
29440
|
-
}
|
|
29441
|
-
}, [nativeToken?.decimals, rawBalance]);
|
|
29442
|
-
return {
|
|
29443
|
-
balance,
|
|
29444
|
-
isLoading,
|
|
29445
|
-
};
|
|
29446
|
-
};
|
|
29447
|
-
const useXrplNativeBalance = ({ address, chain, }) => {
|
|
29448
|
-
const { nativeToken } = useNativeTokenForChain(chain);
|
|
29449
|
-
const { balance: rawBalance, isLoading } = useXrplBalance({
|
|
29450
|
-
chain,
|
|
29451
|
-
token: nativeToken,
|
|
29452
|
-
userAddress: address,
|
|
29453
|
-
enabled: chain?.chainType === squidTypes.ChainType.XRPL,
|
|
29454
|
-
});
|
|
29455
|
-
const balance = React.useMemo(() => {
|
|
29456
|
-
if (nativeToken?.decimals && rawBalance) {
|
|
29457
|
-
return {
|
|
29458
|
-
decimals: nativeToken.decimals,
|
|
29459
|
-
value: parseToBigInt(rawBalance, nativeToken.decimals),
|
|
29460
|
-
};
|
|
29461
|
-
}
|
|
29462
|
-
}, [nativeToken?.decimals, rawBalance]);
|
|
29463
|
-
return {
|
|
29464
|
-
balance,
|
|
29465
|
-
isLoading,
|
|
29466
|
-
};
|
|
29467
|
-
};
|
|
29468
|
-
const useStellarNativeBalance = ({ address, chain, }) => {
|
|
29469
|
-
const { nativeToken } = useNativeTokenForChain(chain);
|
|
29470
|
-
const { balance: rawBalance, isLoading } = useStellarBalance({
|
|
29471
|
-
chain,
|
|
29472
|
-
token: nativeToken,
|
|
29473
|
-
userAddress: address,
|
|
29474
|
-
enabled: chain?.chainType === squidTypes.ChainType.STELLAR,
|
|
29475
|
-
});
|
|
29476
|
-
const balance = React.useMemo(() => {
|
|
29477
|
-
if (nativeToken?.decimals && rawBalance) {
|
|
29478
|
-
return {
|
|
29479
|
-
decimals: nativeToken.decimals,
|
|
29480
|
-
value: parseToBigInt(rawBalance, nativeToken.decimals),
|
|
29481
|
-
};
|
|
29482
|
-
}
|
|
29483
|
-
}, [nativeToken?.decimals, rawBalance]);
|
|
29484
|
-
return {
|
|
29485
|
-
balance,
|
|
29486
|
-
isLoading,
|
|
29487
|
-
};
|
|
29488
|
-
};
|
|
29489
|
-
const useNativeBalance = (chain) => {
|
|
29490
|
-
const { connectedAddresses } = useWallet();
|
|
29491
|
-
const { data: cosmosAddressForChain } = useCosmosForChain(chain);
|
|
29492
|
-
// Cosmos is a special case because the address changes on every chain
|
|
29493
|
-
// so we can't use the default cosmos connected address
|
|
29494
|
-
const { balance: nativeCosmosBalance, isLoading: isCosmosLoading } = useCosmosNativeBalance({
|
|
29495
|
-
address: cosmosAddressForChain,
|
|
29496
|
-
chain,
|
|
29497
|
-
});
|
|
29498
|
-
const { balance: nativeEvmBalance, isLoading: isEvmLoading } = useEvmNativeBalance({ address: connectedAddresses[squidTypes.ChainType.EVM], chain });
|
|
29499
|
-
const { balance: nativeBitcoinBalance, isLoading: isBitcoinLoading } = useBitcoinNativeBalance({
|
|
29500
|
-
address: connectedAddresses[squidTypes.ChainType.BTC],
|
|
29501
|
-
chain,
|
|
29502
|
-
});
|
|
29503
|
-
const { balance: nativeSolanaBalance, isLoading: isSolanaLoading } = useSolanaNativeBalance({
|
|
29504
|
-
address: connectedAddresses[squidTypes.ChainType.SOLANA],
|
|
29505
|
-
chain,
|
|
29506
|
-
});
|
|
29507
|
-
const { balance: nativeSuiBalance, isLoading: isSuiLoading } = useSuiNativeBalance({
|
|
29508
|
-
address: connectedAddresses[squidTypes.ChainType.SUI],
|
|
29509
|
-
chain,
|
|
29510
|
-
});
|
|
29511
|
-
const { balance: nativeXrplBalance, isLoading: isXrpLoading } = useXrplNativeBalance({
|
|
29512
|
-
address: connectedAddresses[squidTypes.ChainType.XRPL],
|
|
29513
|
-
chain,
|
|
29514
|
-
});
|
|
29515
|
-
const { balance: nativeStellarBalance, isLoading: isStellarLoading } = useStellarNativeBalance({
|
|
29516
|
-
address: connectedAddresses[squidTypes.ChainType.STELLAR],
|
|
29517
|
-
chain,
|
|
29518
|
-
});
|
|
29519
|
-
const { nativeBalance, nativeBalanceFormatted } = React.useMemo(() => {
|
|
29520
|
-
let balance;
|
|
29521
|
-
switch (chain?.chainType) {
|
|
29522
|
-
case squidTypes.ChainType.EVM:
|
|
29523
|
-
balance = nativeEvmBalance;
|
|
29524
|
-
break;
|
|
29525
|
-
case squidTypes.ChainType.COSMOS:
|
|
29526
|
-
balance = nativeCosmosBalance;
|
|
29527
|
-
break;
|
|
29528
|
-
case squidTypes.ChainType.BTC:
|
|
29529
|
-
balance = nativeBitcoinBalance;
|
|
29530
|
-
break;
|
|
29531
|
-
case squidTypes.ChainType.SOLANA:
|
|
29532
|
-
balance = nativeSolanaBalance;
|
|
29533
|
-
break;
|
|
29534
|
-
case squidTypes.ChainType.SUI:
|
|
29535
|
-
balance = nativeSuiBalance;
|
|
29536
|
-
break;
|
|
29537
|
-
case squidTypes.ChainType.XRPL:
|
|
29538
|
-
balance = nativeXrplBalance;
|
|
29539
|
-
break;
|
|
29540
|
-
case squidTypes.ChainType.STELLAR:
|
|
29541
|
-
balance = nativeStellarBalance;
|
|
29542
|
-
}
|
|
29543
|
-
const balanceFormatted = !!balance
|
|
29544
|
-
? formatBNToReadable(balance.value, balance.decimals)
|
|
29545
|
-
: undefined;
|
|
29546
|
-
return {
|
|
29547
|
-
nativeBalance: balance,
|
|
29548
|
-
nativeBalanceFormatted: balanceFormatted,
|
|
29549
|
-
};
|
|
29550
|
-
}, [
|
|
29551
|
-
chain?.chainType,
|
|
29552
|
-
nativeEvmBalance,
|
|
29553
|
-
nativeCosmosBalance,
|
|
29554
|
-
nativeBitcoinBalance,
|
|
29555
|
-
nativeSolanaBalance,
|
|
29556
|
-
nativeSuiBalance,
|
|
29557
|
-
nativeXrplBalance,
|
|
29558
|
-
nativeStellarBalance,
|
|
29559
|
-
]);
|
|
29560
|
-
const isLoading = React.useMemo(() => {
|
|
29561
|
-
if (!chain?.chainType)
|
|
29562
|
-
return false;
|
|
29563
|
-
switch (chain.chainType) {
|
|
29564
|
-
case squidTypes.ChainType.EVM:
|
|
29565
|
-
return isEvmLoading;
|
|
29566
|
-
case squidTypes.ChainType.COSMOS:
|
|
29567
|
-
return isCosmosLoading;
|
|
29568
|
-
case squidTypes.ChainType.BTC:
|
|
29569
|
-
return isBitcoinLoading;
|
|
29570
|
-
case squidTypes.ChainType.SOLANA:
|
|
29571
|
-
return isSolanaLoading;
|
|
29572
|
-
case squidTypes.ChainType.SUI:
|
|
29573
|
-
return isSuiLoading;
|
|
29574
|
-
case squidTypes.ChainType.XRPL:
|
|
29575
|
-
return isXrpLoading;
|
|
29576
|
-
case squidTypes.ChainType.STELLAR:
|
|
29577
|
-
return isStellarLoading;
|
|
29578
|
-
}
|
|
29579
|
-
}, [
|
|
29580
|
-
chain?.chainType,
|
|
29581
|
-
isEvmLoading,
|
|
29582
|
-
isCosmosLoading,
|
|
29583
|
-
isBitcoinLoading,
|
|
29584
|
-
isSolanaLoading,
|
|
29585
|
-
isSuiLoading,
|
|
29586
|
-
isXrpLoading,
|
|
29587
|
-
isStellarLoading,
|
|
29588
|
-
]);
|
|
29589
|
-
return { nativeBalance, nativeBalanceFormatted, isLoading };
|
|
29590
|
-
};
|
|
29591
|
-
|
|
29592
|
-
function useHederaAccountActivation({ address, chain, token }) {
|
|
29593
|
-
const { balance: destNativeEvmBalance } = useEvmNativeBalance({
|
|
29594
|
-
chain,
|
|
29595
|
-
address,
|
|
29596
|
-
});
|
|
29597
|
-
const isHederaAccountActivated = React.useMemo(() => {
|
|
29598
|
-
if (token?.chainId !== CHAIN_IDS.HEDERA)
|
|
29599
|
-
return true;
|
|
29600
|
-
if (token.address.toLowerCase() === nativeEvmTokenAddress.toLowerCase())
|
|
29601
|
-
return true;
|
|
29602
|
-
return (!!destNativeEvmBalance?.value && destNativeEvmBalance.value > BigInt(0));
|
|
29603
|
-
}, [token?.chainId, token?.address, destNativeEvmBalance?.value]);
|
|
29604
|
-
return {
|
|
29605
|
-
isHederaAccountActivated,
|
|
29606
|
-
};
|
|
29607
|
-
}
|
|
29608
|
-
|
|
29609
|
-
var hrc20 = [
|
|
29610
|
-
{
|
|
29611
|
-
inputs: [
|
|
29612
|
-
],
|
|
29613
|
-
name: "associate",
|
|
29614
|
-
outputs: [
|
|
29615
|
-
{
|
|
29616
|
-
internalType: "uint256",
|
|
29617
|
-
name: "responseCode",
|
|
29618
|
-
type: "uint256"
|
|
29619
|
-
}
|
|
29620
|
-
],
|
|
29621
|
-
stateMutability: "nonpayable",
|
|
29622
|
-
type: "function"
|
|
29623
|
-
}
|
|
29624
|
-
];
|
|
29625
|
-
|
|
29626
|
-
/**
|
|
29627
|
-
* Client for interacting with the Hedera Mirrornode API.
|
|
29628
|
-
*
|
|
29629
|
-
* @docs https://mainnet.mirrornode.hedera.com/api/v1/docs
|
|
29630
|
-
*/
|
|
29631
|
-
class HederaApiClient {
|
|
29632
|
-
apiUrl;
|
|
29633
|
-
constructor(apiUrl) {
|
|
29634
|
-
this.apiUrl = apiUrl;
|
|
29635
|
-
}
|
|
29636
|
-
async isTokenAssociated({ address, token, }) {
|
|
29637
|
-
const accountInfo = await this.getAccountInfo(address);
|
|
29638
|
-
// Unlimited auto associations
|
|
29639
|
-
if (accountInfo.max_automatic_token_associations === -1) {
|
|
29640
|
-
return true;
|
|
29641
|
-
}
|
|
29642
|
-
// If there's no unlimited auto-associations, we need to check if the token is already associated.
|
|
29643
|
-
const { tokens: accountTokens } = await this.getAccountTokens(address);
|
|
29644
|
-
const tokenId = convertEvmAddressToHederaAccountId(token.address);
|
|
29645
|
-
if (accountTokens.some((t) => t.token_id === tokenId)) {
|
|
29646
|
-
// Token is already associated
|
|
29647
|
-
return true;
|
|
29648
|
-
}
|
|
29649
|
-
// Finally, if not auto-associated, check if there is an available auto-association slot
|
|
29650
|
-
const autoAssociatedTokens = accountTokens.filter((t) => t.automatic_association);
|
|
29651
|
-
const remainingAutoAssociations = accountInfo.max_automatic_token_associations -
|
|
29652
|
-
autoAssociatedTokens.length;
|
|
29653
|
-
return remainingAutoAssociations > 0;
|
|
29654
|
-
}
|
|
29655
|
-
async getAccountInfo(address) {
|
|
29656
|
-
const data = await this.fetch(`accounts/${address}`);
|
|
29657
|
-
if (typeof data.max_automatic_token_associations !== "number") {
|
|
29658
|
-
throw new Error("Invalid max_automatic_token_associations type, expected number");
|
|
29659
|
-
}
|
|
29660
|
-
if (typeof data.balance.balance !== "number") {
|
|
29661
|
-
throw new Error("Invalid balance type, expected number");
|
|
29662
|
-
}
|
|
29663
|
-
if (!Array.isArray(data.balance.tokens)) {
|
|
29664
|
-
throw new Error("Invalid tokens type, expected array");
|
|
29665
|
-
}
|
|
29666
|
-
return data;
|
|
29667
|
-
}
|
|
29668
|
-
async getAccountTokens(address) {
|
|
29669
|
-
const data = await this.fetch(`accounts/${address}/tokens`);
|
|
29670
|
-
if (!Array.isArray(data.tokens)) {
|
|
29671
|
-
throw new Error("Invalid tokens type, expected array");
|
|
29672
|
-
}
|
|
29673
|
-
const firstToken = data.tokens[0];
|
|
29674
|
-
if (typeof firstToken?.automatic_association !== "boolean") {
|
|
29675
|
-
throw new Error("Invalid automatic_association type, expected boolean");
|
|
29676
|
-
}
|
|
29677
|
-
if (typeof firstToken?.token_id !== "string") {
|
|
29678
|
-
throw new Error("Invalid token_id type, expected string");
|
|
29679
|
-
}
|
|
29680
|
-
return data;
|
|
29681
|
-
}
|
|
29682
|
-
async fetch(path) {
|
|
29683
|
-
const url = new URL(path, this.apiUrl);
|
|
29684
|
-
const response = await fetch(url);
|
|
29685
|
-
if (!response.ok) {
|
|
29686
|
-
throw new Error(`Hedera API error: ${response.status}`);
|
|
29687
|
-
}
|
|
29688
|
-
return response.json();
|
|
29689
|
-
}
|
|
29690
|
-
}
|
|
29691
|
-
|
|
29692
|
-
hederaWalletConnect.type = "hederaWalletConnect";
|
|
29693
|
-
/**
|
|
29694
|
-
* Wagmi connector to interact with the Hedera EVM network via the WalletConnect protocol.
|
|
29695
|
-
*
|
|
29696
|
-
* The connector removes the need for a WalletConnect modal, and instead
|
|
29697
|
-
* communicates with the provided `extension` by opening the extension popup
|
|
29698
|
-
* for user interaction upon request.
|
|
29699
|
-
*/
|
|
29700
|
-
function hederaWalletConnect(parameters) {
|
|
29701
|
-
const { extension, ...restParameters } = parameters;
|
|
29702
|
-
let provider_;
|
|
29703
|
-
let providerPromise;
|
|
29704
|
-
let connect;
|
|
29705
|
-
let displayUri;
|
|
29706
|
-
let sessionDelete;
|
|
29707
|
-
let disconnect;
|
|
29708
|
-
return createConnector((config) => ({
|
|
29709
|
-
id: `hedera-wc-${extension.id}`,
|
|
29710
|
-
name: extension.name,
|
|
29711
|
-
icon: extension.icon,
|
|
29712
|
-
type: hederaWalletConnect.type,
|
|
29713
|
-
async setup() {
|
|
29714
|
-
const provider = await this.getProvider().catch(() => null);
|
|
29715
|
-
if (!provider)
|
|
29716
|
-
return;
|
|
29717
|
-
if (!connect) {
|
|
29718
|
-
connect = this.onConnect.bind(this);
|
|
29719
|
-
provider.on("connect", connect);
|
|
29720
|
-
}
|
|
29721
|
-
if (!sessionDelete) {
|
|
29722
|
-
sessionDelete = this.onSessionDelete.bind(this);
|
|
29723
|
-
provider.on("session_delete", sessionDelete);
|
|
29724
|
-
}
|
|
29725
|
-
},
|
|
29726
|
-
async connect(params = {}) {
|
|
29727
|
-
try {
|
|
29728
|
-
const provider = await this.getProvider();
|
|
29729
|
-
if (!provider)
|
|
29730
|
-
throw new ProviderNotFoundError();
|
|
29731
|
-
if (!displayUri) {
|
|
29732
|
-
displayUri = this.onDisplayUri;
|
|
29733
|
-
provider.on("display_uri", displayUri);
|
|
29734
|
-
}
|
|
29735
|
-
if (!provider.session) {
|
|
29736
|
-
await provider.connect({
|
|
29737
|
-
...("pairingTopic" in params
|
|
29738
|
-
? { pairingTopic: params.pairingTopic }
|
|
29739
|
-
: {}),
|
|
29740
|
-
});
|
|
29741
|
-
}
|
|
29742
|
-
const accounts = (await provider.enable()).map((x) => viem.getAddress(x));
|
|
29743
|
-
const currentChainId = await this.getChainId();
|
|
29744
|
-
if (displayUri) {
|
|
29745
|
-
provider.removeListener("display_uri", displayUri);
|
|
29746
|
-
displayUri = undefined;
|
|
29747
|
-
}
|
|
29748
|
-
if (connect) {
|
|
29749
|
-
provider.removeListener("connect", connect);
|
|
29750
|
-
connect = undefined;
|
|
29751
|
-
}
|
|
29752
|
-
if (!disconnect) {
|
|
29753
|
-
disconnect = this.onDisconnect.bind(this);
|
|
29754
|
-
provider.on("disconnect", disconnect);
|
|
29755
|
-
}
|
|
29756
|
-
if (!sessionDelete) {
|
|
29757
|
-
sessionDelete = this.onSessionDelete.bind(this);
|
|
29758
|
-
provider.on("session_delete", sessionDelete);
|
|
29759
|
-
}
|
|
29760
|
-
return { accounts, chainId: currentChainId };
|
|
29761
|
-
}
|
|
29762
|
-
catch (error) {
|
|
29763
|
-
if (/(user rejected|connection request reset)/i.test(error?.message)) {
|
|
29764
|
-
throw new viem.UserRejectedRequestError(error);
|
|
29765
|
-
}
|
|
29766
|
-
throw error;
|
|
29767
|
-
}
|
|
29768
|
-
},
|
|
29769
|
-
async disconnect() {
|
|
29770
|
-
const provider = await this.getProvider();
|
|
29771
|
-
try {
|
|
29772
|
-
await provider?.disconnect();
|
|
29773
|
-
}
|
|
29774
|
-
catch (error) {
|
|
29775
|
-
if (!/No matching key/i.test(error.message))
|
|
29776
|
-
throw error;
|
|
29777
|
-
}
|
|
29778
|
-
finally {
|
|
29779
|
-
if (disconnect) {
|
|
29780
|
-
provider?.removeListener("disconnect", disconnect);
|
|
29781
|
-
disconnect = undefined;
|
|
29782
|
-
}
|
|
29783
|
-
if (!connect) {
|
|
29784
|
-
connect = this.onConnect.bind(this);
|
|
29785
|
-
provider?.on("connect", connect);
|
|
29786
|
-
}
|
|
29787
|
-
if (sessionDelete) {
|
|
29788
|
-
provider?.removeListener("session_delete", sessionDelete);
|
|
29789
|
-
sessionDelete = undefined;
|
|
29790
|
-
}
|
|
29791
|
-
}
|
|
29792
|
-
},
|
|
29793
|
-
async getAccounts() {
|
|
29794
|
-
const provider = await this.getProvider();
|
|
29795
|
-
return provider.accounts.map((x) => viem.getAddress(x));
|
|
29796
|
-
},
|
|
29797
|
-
async getProvider() {
|
|
29798
|
-
async function initProvider() {
|
|
29799
|
-
const optionalChains = config.chains.map((x) => x.id);
|
|
29800
|
-
if (!optionalChains.length)
|
|
29801
|
-
return;
|
|
29802
|
-
const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-DAfqL2H0.js'); });
|
|
29803
|
-
const rawProvider = await EthereumProvider.init({
|
|
29804
|
-
...restParameters,
|
|
29805
|
-
disableProviderPing: true,
|
|
29806
|
-
optionalChains,
|
|
29807
|
-
projectId: restParameters.projectId,
|
|
29808
|
-
rpcMap: Object.fromEntries(config.chains.map((chain) => {
|
|
29809
|
-
const [url] = extractRpcUrls({
|
|
29810
|
-
chain,
|
|
29811
|
-
transports: config.transports,
|
|
29812
|
-
});
|
|
29813
|
-
return [chain.id, url];
|
|
29814
|
-
})),
|
|
29815
|
-
showQrModal: false,
|
|
29816
|
-
// We need to specify a custom storage prefix to avoid conflicts with Wagmi's walletConnect instance
|
|
29817
|
-
// https://docs.reown.com/walletkit/web/usage#core-instance-sharing
|
|
29818
|
-
customStoragePrefix: "squid-hedera",
|
|
29819
|
-
});
|
|
29820
|
-
const proxiedProvider = new Proxy(rawProvider, {
|
|
29821
|
-
get(target, prop, receiver) {
|
|
29822
|
-
if (prop === "request") {
|
|
29823
|
-
return async (args) => {
|
|
29824
|
-
const signingMethods = [
|
|
29825
|
-
"eth_sendTransaction",
|
|
29826
|
-
"eth_signTransaction",
|
|
29827
|
-
];
|
|
29828
|
-
if (signingMethods.includes(args.method)) {
|
|
29829
|
-
try {
|
|
29830
|
-
HederaExtensionHelper.extensionOpen(extension.id);
|
|
29831
|
-
}
|
|
29832
|
-
catch { }
|
|
29833
|
-
}
|
|
29834
|
-
// forward request to original provider
|
|
29835
|
-
return target.request(args);
|
|
29836
|
-
};
|
|
29837
|
-
}
|
|
29838
|
-
// forward all other properties/methods
|
|
29839
|
-
return Reflect.get(target, prop, receiver);
|
|
29840
|
-
},
|
|
29841
|
-
});
|
|
29842
|
-
return proxiedProvider;
|
|
29843
|
-
}
|
|
29844
|
-
if (!provider_) {
|
|
29845
|
-
if (!providerPromise)
|
|
29846
|
-
providerPromise = initProvider();
|
|
29847
|
-
provider_ = await providerPromise;
|
|
29848
|
-
provider_?.events.setMaxListeners(Number.POSITIVE_INFINITY);
|
|
29849
|
-
}
|
|
29850
|
-
return provider_;
|
|
29851
|
-
},
|
|
29852
|
-
async getChainId() {
|
|
29853
|
-
const provider = await this.getProvider();
|
|
29854
|
-
return provider.chainId;
|
|
29855
|
-
},
|
|
29856
|
-
async isAuthorized() {
|
|
29857
|
-
try {
|
|
29858
|
-
const accounts = await this.getAccounts();
|
|
29859
|
-
return accounts.length > 0;
|
|
29860
|
-
}
|
|
29861
|
-
catch {
|
|
29862
|
-
return false;
|
|
29863
|
-
}
|
|
29864
|
-
},
|
|
29865
|
-
onAccountsChanged(accounts) {
|
|
29866
|
-
if (accounts.length === 0)
|
|
29867
|
-
this.onDisconnect();
|
|
29868
|
-
else
|
|
29869
|
-
config.emitter.emit("change", {
|
|
29870
|
-
accounts: accounts.map((x) => viem.getAddress(x)),
|
|
29871
|
-
});
|
|
29872
|
-
},
|
|
29873
|
-
onChainChanged(chain) {
|
|
29874
|
-
const chainId = Number(chain);
|
|
29875
|
-
config.emitter.emit("change", { chainId });
|
|
29876
|
-
},
|
|
29877
|
-
async onConnect(connectInfo) {
|
|
29878
|
-
const chainId = Number(connectInfo.chainId);
|
|
29879
|
-
const accounts = await this.getAccounts();
|
|
29880
|
-
config.emitter.emit("connect", { accounts, chainId });
|
|
29881
|
-
},
|
|
29882
|
-
async onDisconnect() {
|
|
29883
|
-
config.emitter.emit("disconnect");
|
|
29884
|
-
const provider = await this.getProvider();
|
|
29885
|
-
if (disconnect) {
|
|
29886
|
-
provider.removeListener("disconnect", disconnect);
|
|
29887
|
-
disconnect = undefined;
|
|
29888
|
-
}
|
|
29889
|
-
if (sessionDelete) {
|
|
29890
|
-
provider.removeListener("session_delete", sessionDelete);
|
|
29891
|
-
sessionDelete = undefined;
|
|
29892
|
-
}
|
|
29893
|
-
if (!connect) {
|
|
29894
|
-
connect = this.onConnect.bind(this);
|
|
29895
|
-
provider.on("connect", connect);
|
|
29896
|
-
}
|
|
29897
|
-
},
|
|
29898
|
-
onDisplayUri(uri) {
|
|
29899
|
-
config.emitter.emit("message", { type: "display_uri", data: uri });
|
|
29900
|
-
HederaExtensionHelper.extensionConnect(extension.id, uri);
|
|
29901
|
-
},
|
|
29902
|
-
onSessionDelete() {
|
|
29903
|
-
this.onDisconnect();
|
|
29904
|
-
},
|
|
29905
|
-
}));
|
|
29906
|
-
}
|
|
29907
|
-
|
|
29908
|
-
const createWagmiConfig = (squidChains, hederaExtensions) => {
|
|
29909
|
-
const filteredEvmChains = squidChains.filter((chain) => chain.chainType === squidTypes.ChainType.EVM);
|
|
29910
|
-
if (filteredEvmChains.length === 0) {
|
|
29911
|
-
throw new Error("At least one chain is required");
|
|
29912
|
-
}
|
|
29913
|
-
const wagmiChains = filteredEvmChains.map((chain) => {
|
|
29914
|
-
return viem.defineChain({
|
|
29915
|
-
id: Number(chain.chainId),
|
|
29916
|
-
name: chain.networkName,
|
|
29917
|
-
nativeCurrency: {
|
|
29918
|
-
name: chain.nativeCurrency.name,
|
|
29919
|
-
symbol: chain.nativeCurrency.symbol,
|
|
29920
|
-
decimals: chain.nativeCurrency.decimals,
|
|
29921
|
-
},
|
|
29922
|
-
rpcUrls: {
|
|
29923
|
-
public: {
|
|
29924
|
-
http: [chain.rpc],
|
|
29925
|
-
},
|
|
29926
|
-
default: {
|
|
29927
|
-
http: [chain.rpc],
|
|
29928
|
-
},
|
|
29929
|
-
},
|
|
29930
|
-
});
|
|
29931
|
-
});
|
|
29932
|
-
const wcMetadata = {
|
|
29933
|
-
url: SQUID_METADATA.url,
|
|
29934
|
-
name: SQUID_METADATA.name,
|
|
29935
|
-
icons: [SQUID_METADATA.icon],
|
|
29936
|
-
description: SQUID_METADATA.description,
|
|
29937
|
-
};
|
|
29938
|
-
return wagmi.createConfig({
|
|
29939
|
-
chains: wagmiChains,
|
|
29940
|
-
transports: Object.fromEntries(wagmiChains.map((chain) => [
|
|
29941
|
-
chain.id,
|
|
29942
|
-
wagmi.http(chain.rpcUrls.public.http[0] ?? ""),
|
|
29943
|
-
])),
|
|
29944
|
-
connectors: [
|
|
29945
|
-
connectors.injected(),
|
|
29946
|
-
connectors.safe({
|
|
29947
|
-
allowedDomains: [/app.safe.global$/],
|
|
29948
|
-
}),
|
|
29949
|
-
connectors.metaMask({
|
|
29950
|
-
dappMetadata: {
|
|
29951
|
-
name: SQUID_METADATA.name,
|
|
29952
|
-
url: SQUID_METADATA.url,
|
|
29953
|
-
iconUrl: SQUID_METADATA.icon,
|
|
29954
|
-
},
|
|
29955
|
-
}),
|
|
29956
|
-
connectors.coinbaseWallet({
|
|
29957
|
-
appName: SQUID_METADATA.name,
|
|
29958
|
-
appLogoUrl: SQUID_METADATA.icon,
|
|
29959
|
-
}),
|
|
29960
|
-
connectors.walletConnect({
|
|
29961
|
-
projectId: WALLETCONNECT_PROJECT_ID,
|
|
29962
|
-
metadata: wcMetadata,
|
|
29963
|
-
}),
|
|
29964
|
-
...hederaExtensions.map((extension) => hederaWalletConnect({
|
|
29965
|
-
projectId: WALLETCONNECT_PROJECT_ID,
|
|
29966
|
-
metadata: wcMetadata,
|
|
29967
|
-
extension,
|
|
29968
|
-
})),
|
|
29969
|
-
],
|
|
30059
|
+
const balance = React.useMemo(() => {
|
|
30060
|
+
if (nativeToken?.decimals && rawBalance) {
|
|
30061
|
+
return {
|
|
30062
|
+
decimals: nativeToken.decimals,
|
|
30063
|
+
value: parseToBigInt(rawBalance, nativeToken.decimals),
|
|
30064
|
+
};
|
|
30065
|
+
}
|
|
30066
|
+
}, [nativeToken?.decimals, rawBalance]);
|
|
30067
|
+
return {
|
|
30068
|
+
balance,
|
|
30069
|
+
isLoading,
|
|
30070
|
+
};
|
|
30071
|
+
};
|
|
30072
|
+
const useXrplNativeBalance = ({ address, chain, }) => {
|
|
30073
|
+
const { nativeToken } = useNativeTokenForChain(chain);
|
|
30074
|
+
const { balance: rawBalance, isLoading } = useXrplBalance({
|
|
30075
|
+
chain,
|
|
30076
|
+
token: nativeToken,
|
|
30077
|
+
userAddress: address,
|
|
30078
|
+
enabled: chain?.chainType === squidTypes.ChainType.XRPL,
|
|
29970
30079
|
});
|
|
30080
|
+
const balance = React.useMemo(() => {
|
|
30081
|
+
if (nativeToken?.decimals && rawBalance) {
|
|
30082
|
+
return {
|
|
30083
|
+
decimals: nativeToken.decimals,
|
|
30084
|
+
value: parseToBigInt(rawBalance, nativeToken.decimals),
|
|
30085
|
+
};
|
|
30086
|
+
}
|
|
30087
|
+
}, [nativeToken?.decimals, rawBalance]);
|
|
30088
|
+
return {
|
|
30089
|
+
balance,
|
|
30090
|
+
isLoading,
|
|
30091
|
+
};
|
|
29971
30092
|
};
|
|
29972
|
-
|
|
29973
|
-
|
|
29974
|
-
|
|
29975
|
-
|
|
29976
|
-
|
|
29977
|
-
|
|
29978
|
-
|
|
29979
|
-
|
|
29980
|
-
|
|
29981
|
-
|
|
29982
|
-
|
|
30093
|
+
const useStellarNativeBalance = ({ address, chain, }) => {
|
|
30094
|
+
const { nativeToken } = useNativeTokenForChain(chain);
|
|
30095
|
+
const { balance: rawBalance, isLoading } = useStellarBalance({
|
|
30096
|
+
chain,
|
|
30097
|
+
token: nativeToken,
|
|
30098
|
+
userAddress: address,
|
|
30099
|
+
enabled: chain?.chainType === squidTypes.ChainType.STELLAR,
|
|
30100
|
+
});
|
|
30101
|
+
const balance = React.useMemo(() => {
|
|
30102
|
+
if (nativeToken?.decimals && rawBalance) {
|
|
30103
|
+
return {
|
|
30104
|
+
decimals: nativeToken.decimals,
|
|
30105
|
+
value: parseToBigInt(rawBalance, nativeToken.decimals),
|
|
30106
|
+
};
|
|
30107
|
+
}
|
|
30108
|
+
}, [nativeToken?.decimals, rawBalance]);
|
|
30109
|
+
return {
|
|
30110
|
+
balance,
|
|
30111
|
+
isLoading,
|
|
29983
30112
|
};
|
|
29984
|
-
|
|
29985
|
-
|
|
29986
|
-
|
|
29987
|
-
}
|
|
29988
|
-
|
|
29989
|
-
|
|
29990
|
-
const {
|
|
29991
|
-
|
|
29992
|
-
|
|
29993
|
-
|
|
29994
|
-
}
|
|
29995
|
-
const
|
|
29996
|
-
|
|
29997
|
-
|
|
29998
|
-
|
|
29999
|
-
const {
|
|
30000
|
-
|
|
30001
|
-
|
|
30002
|
-
|
|
30003
|
-
const {
|
|
30004
|
-
|
|
30005
|
-
|
|
30006
|
-
|
|
30007
|
-
const
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
const
|
|
30012
|
-
|
|
30013
|
-
|
|
30113
|
+
};
|
|
30114
|
+
const useNativeBalance = (chain) => {
|
|
30115
|
+
const { connectedAddresses } = useWallet();
|
|
30116
|
+
const { data: cosmosAddressForChain } = useCosmosForChain(chain);
|
|
30117
|
+
// Cosmos is a special case because the address changes on every chain
|
|
30118
|
+
// so we can't use the default cosmos connected address
|
|
30119
|
+
const { balance: nativeCosmosBalance, isLoading: isCosmosLoading } = useCosmosNativeBalance({
|
|
30120
|
+
address: cosmosAddressForChain,
|
|
30121
|
+
chain,
|
|
30122
|
+
});
|
|
30123
|
+
const { balance: nativeEvmBalance, isLoading: isEvmLoading } = useEvmNativeBalance({ address: connectedAddresses[squidTypes.ChainType.EVM], chain });
|
|
30124
|
+
const { balance: nativeBitcoinBalance, isLoading: isBitcoinLoading } = useBitcoinNativeBalance({
|
|
30125
|
+
address: connectedAddresses[squidTypes.ChainType.BTC],
|
|
30126
|
+
chain,
|
|
30127
|
+
});
|
|
30128
|
+
const { balance: nativeSolanaBalance, isLoading: isSolanaLoading } = useSolanaNativeBalance({
|
|
30129
|
+
address: connectedAddresses[squidTypes.ChainType.SOLANA],
|
|
30130
|
+
chain,
|
|
30131
|
+
});
|
|
30132
|
+
const { balance: nativeSuiBalance, isLoading: isSuiLoading } = useSuiNativeBalance({
|
|
30133
|
+
address: connectedAddresses[squidTypes.ChainType.SUI],
|
|
30134
|
+
chain,
|
|
30135
|
+
});
|
|
30136
|
+
const { balance: nativeXrplBalance, isLoading: isXrpLoading } = useXrplNativeBalance({
|
|
30137
|
+
address: connectedAddresses[squidTypes.ChainType.XRPL],
|
|
30138
|
+
chain,
|
|
30139
|
+
});
|
|
30140
|
+
const { balance: nativeStellarBalance, isLoading: isStellarLoading } = useStellarNativeBalance({
|
|
30141
|
+
address: connectedAddresses[squidTypes.ChainType.STELLAR],
|
|
30142
|
+
chain,
|
|
30143
|
+
});
|
|
30144
|
+
const { nativeBalance, nativeBalanceFormatted } = React.useMemo(() => {
|
|
30145
|
+
let balance;
|
|
30146
|
+
switch (chain?.chainType) {
|
|
30147
|
+
case squidTypes.ChainType.EVM:
|
|
30148
|
+
balance = nativeEvmBalance;
|
|
30149
|
+
break;
|
|
30150
|
+
case squidTypes.ChainType.COSMOS:
|
|
30151
|
+
balance = nativeCosmosBalance;
|
|
30152
|
+
break;
|
|
30153
|
+
case squidTypes.ChainType.BTC:
|
|
30154
|
+
balance = nativeBitcoinBalance;
|
|
30155
|
+
break;
|
|
30156
|
+
case squidTypes.ChainType.SOLANA:
|
|
30157
|
+
balance = nativeSolanaBalance;
|
|
30158
|
+
break;
|
|
30159
|
+
case squidTypes.ChainType.SUI:
|
|
30160
|
+
balance = nativeSuiBalance;
|
|
30161
|
+
break;
|
|
30162
|
+
case squidTypes.ChainType.XRPL:
|
|
30163
|
+
balance = nativeXrplBalance;
|
|
30164
|
+
break;
|
|
30165
|
+
case squidTypes.ChainType.STELLAR:
|
|
30166
|
+
balance = nativeStellarBalance;
|
|
30167
|
+
}
|
|
30168
|
+
const balanceFormatted = !!balance
|
|
30169
|
+
? formatBNToReadable(balance.value, balance.decimals)
|
|
30170
|
+
: undefined;
|
|
30171
|
+
return {
|
|
30172
|
+
nativeBalance: balance,
|
|
30173
|
+
nativeBalanceFormatted: balanceFormatted,
|
|
30174
|
+
};
|
|
30175
|
+
}, [
|
|
30176
|
+
chain?.chainType,
|
|
30177
|
+
nativeEvmBalance,
|
|
30178
|
+
nativeCosmosBalance,
|
|
30179
|
+
nativeBitcoinBalance,
|
|
30180
|
+
nativeSolanaBalance,
|
|
30181
|
+
nativeSuiBalance,
|
|
30182
|
+
nativeXrplBalance,
|
|
30183
|
+
nativeStellarBalance,
|
|
30184
|
+
]);
|
|
30185
|
+
const isLoading = React.useMemo(() => {
|
|
30014
30186
|
if (!chain?.chainType)
|
|
30015
30187
|
return false;
|
|
30016
30188
|
switch (chain.chainType) {
|
|
30017
30189
|
case squidTypes.ChainType.EVM:
|
|
30018
|
-
return
|
|
30190
|
+
return isEvmLoading;
|
|
30019
30191
|
case squidTypes.ChainType.COSMOS:
|
|
30020
|
-
return
|
|
30192
|
+
return isCosmosLoading;
|
|
30021
30193
|
case squidTypes.ChainType.BTC:
|
|
30022
|
-
return
|
|
30194
|
+
return isBitcoinLoading;
|
|
30023
30195
|
case squidTypes.ChainType.SOLANA:
|
|
30024
|
-
return
|
|
30196
|
+
return isSolanaLoading;
|
|
30025
30197
|
case squidTypes.ChainType.SUI:
|
|
30026
|
-
return
|
|
30198
|
+
return isSuiLoading;
|
|
30027
30199
|
case squidTypes.ChainType.XRPL:
|
|
30028
|
-
return
|
|
30200
|
+
return isXrpLoading;
|
|
30029
30201
|
case squidTypes.ChainType.STELLAR:
|
|
30030
|
-
return
|
|
30202
|
+
return isStellarLoading;
|
|
30031
30203
|
}
|
|
30032
30204
|
}, [
|
|
30033
30205
|
chain?.chainType,
|
|
30034
|
-
|
|
30035
|
-
|
|
30036
|
-
|
|
30037
|
-
|
|
30038
|
-
|
|
30039
|
-
|
|
30040
|
-
|
|
30206
|
+
isEvmLoading,
|
|
30207
|
+
isCosmosLoading,
|
|
30208
|
+
isBitcoinLoading,
|
|
30209
|
+
isSolanaLoading,
|
|
30210
|
+
isSuiLoading,
|
|
30211
|
+
isXrpLoading,
|
|
30212
|
+
isStellarLoading,
|
|
30041
30213
|
]);
|
|
30042
|
-
return {
|
|
30043
|
-
isSignerReady,
|
|
30044
|
-
evmSigner,
|
|
30045
|
-
cosmosSigner,
|
|
30046
|
-
bitcoinSigner,
|
|
30047
|
-
solanaSigner,
|
|
30048
|
-
suiSigner,
|
|
30049
|
-
xrplSigner,
|
|
30050
|
-
stellarSigner,
|
|
30051
|
-
};
|
|
30052
|
-
};
|
|
30053
|
-
|
|
30054
|
-
function useHederaTokenAssociations({ address, chain, token }) {
|
|
30055
|
-
const publicClient = wagmi.usePublicClient({
|
|
30056
|
-
chainId: Number(CHAIN_IDS.HEDERA),
|
|
30057
|
-
});
|
|
30058
|
-
const { evmSigner } = useSigner({ chain });
|
|
30059
|
-
const queryClient = reactQuery.useQueryClient();
|
|
30060
|
-
/**
|
|
30061
|
-
* Creates a token association transaction where the destination account authorizes to receive the given token
|
|
30062
|
-
*/
|
|
30063
|
-
const associateToken = reactQuery.useMutation({
|
|
30064
|
-
mutationFn: async () => {
|
|
30065
|
-
try {
|
|
30066
|
-
if (!evmSigner) {
|
|
30067
|
-
throw new Error("EVM signer not found");
|
|
30068
|
-
}
|
|
30069
|
-
if (chain?.chainId !== CHAIN_IDS.HEDERA ||
|
|
30070
|
-
token?.chainId !== CHAIN_IDS.HEDERA) {
|
|
30071
|
-
throw new Error("Chain and token to associate must be on Hedera");
|
|
30072
|
-
}
|
|
30073
|
-
const tokenAddress = parseEvmAddress(token.address);
|
|
30074
|
-
if (!tokenAddress) {
|
|
30075
|
-
throw new Error("Invalid token address");
|
|
30076
|
-
}
|
|
30077
|
-
const userAddress = parseEvmAddress(address);
|
|
30078
|
-
if (!userAddress) {
|
|
30079
|
-
throw new Error("Invalid user address");
|
|
30080
|
-
}
|
|
30081
|
-
const encodedData = viem.encodeFunctionData({
|
|
30082
|
-
abi: hrc20,
|
|
30083
|
-
functionName: "associate",
|
|
30084
|
-
args: [],
|
|
30085
|
-
});
|
|
30086
|
-
const txRes = await evmSigner.sendTransaction({
|
|
30087
|
-
data: encodedData,
|
|
30088
|
-
to: tokenAddress,
|
|
30089
|
-
chainId: chain.chainId,
|
|
30090
|
-
});
|
|
30091
|
-
const receipt = await txRes.wait();
|
|
30092
|
-
if (receipt?.status !== 1) {
|
|
30093
|
-
throw new Error(`Transaction failed with status: ${receipt?.status}`);
|
|
30094
|
-
}
|
|
30095
|
-
return true;
|
|
30096
|
-
}
|
|
30097
|
-
catch (error) {
|
|
30098
|
-
console.error("Error associating Hedera token:", error);
|
|
30099
|
-
return false;
|
|
30100
|
-
}
|
|
30101
|
-
},
|
|
30102
|
-
async onSuccess() {
|
|
30103
|
-
queryClient.refetchQueries({
|
|
30104
|
-
queryKey: getPrefixKey(exports.QueryKeys.IsHederaTokenAssociated),
|
|
30105
|
-
});
|
|
30106
|
-
},
|
|
30107
|
-
});
|
|
30108
|
-
/**
|
|
30109
|
-
* Checks if the destination account has associated the given token.
|
|
30110
|
-
*
|
|
30111
|
-
* Hedera requires accounts to associate a token before being able to receive it.
|
|
30112
|
-
*
|
|
30113
|
-
* Accounts which have max. associations set to -1 can receive any token without previous association
|
|
30114
|
-
*/
|
|
30115
|
-
const isTokenAssociated = reactQuery.useQuery({
|
|
30116
|
-
queryKey: keys().isHederaTokenAssociated(address, token?.chainId, token?.type, token?.address),
|
|
30117
|
-
queryFn: async () => {
|
|
30118
|
-
if (token?.chainId !== CHAIN_IDS.HEDERA) {
|
|
30119
|
-
return true;
|
|
30120
|
-
}
|
|
30121
|
-
// The native HBAR token doesn't need an association
|
|
30122
|
-
if (token.address.toLowerCase() === nativeEvmTokenAddress.toLowerCase()) {
|
|
30123
|
-
return true;
|
|
30124
|
-
}
|
|
30125
|
-
if (!chain || !address || !publicClient) {
|
|
30126
|
-
throw new Error("Missing required parameters");
|
|
30127
|
-
}
|
|
30128
|
-
// TODO: update types
|
|
30129
|
-
const apiUrl = chain?.chainConfig?.hedera?.mirrorNodeUrl;
|
|
30130
|
-
if (!apiUrl) {
|
|
30131
|
-
throw new Error("Missing Hedera mirror node URL in chain config");
|
|
30132
|
-
}
|
|
30133
|
-
const hederaApiClient = new HederaApiClient(apiUrl);
|
|
30134
|
-
return hederaApiClient.isTokenAssociated({
|
|
30135
|
-
address,
|
|
30136
|
-
token,
|
|
30137
|
-
});
|
|
30138
|
-
},
|
|
30139
|
-
enabled: !!address && !!publicClient && token?.chainId === CHAIN_IDS.HEDERA,
|
|
30140
|
-
});
|
|
30141
|
-
return {
|
|
30142
|
-
isTokenAssociated,
|
|
30143
|
-
associateToken,
|
|
30144
|
-
};
|
|
30145
|
-
}
|
|
30146
|
-
|
|
30147
|
-
const useKeyboardNavigation = ({ onEscape }) => {
|
|
30148
|
-
const onKeyDown = React.useCallback((event) => {
|
|
30149
|
-
if (event.key === "Escape") {
|
|
30150
|
-
onEscape?.();
|
|
30151
|
-
return;
|
|
30152
|
-
}
|
|
30153
|
-
}, [onEscape]);
|
|
30154
|
-
React.useEffect(() => {
|
|
30155
|
-
document.addEventListener("keydown", onKeyDown, false);
|
|
30156
|
-
return () => {
|
|
30157
|
-
document.removeEventListener("keydown", onKeyDown, false);
|
|
30158
|
-
};
|
|
30159
|
-
}, [onKeyDown]);
|
|
30160
|
-
};
|
|
30161
|
-
|
|
30162
|
-
const useSquidQueryClient = () => {
|
|
30163
|
-
const queryClient = reactQuery.useQueryClient();
|
|
30164
|
-
const invalidateQueries = (key) => {
|
|
30165
|
-
const prefixKey = getPrefixKey(key);
|
|
30166
|
-
queryClient.invalidateQueries(prefixKey);
|
|
30167
|
-
};
|
|
30168
|
-
const refetchQueries = (key) => {
|
|
30169
|
-
const prefixKey = getPrefixKey(key);
|
|
30170
|
-
queryClient.refetchQueries(prefixKey);
|
|
30171
|
-
};
|
|
30172
|
-
const invalidateAndRefetchQueries = (key) => {
|
|
30173
|
-
invalidateQueries(key);
|
|
30174
|
-
refetchQueries(key);
|
|
30175
|
-
};
|
|
30176
|
-
return {
|
|
30177
|
-
invalidateQueries,
|
|
30178
|
-
refetchQueries,
|
|
30179
|
-
invalidateAndRefetchQueries,
|
|
30180
|
-
};
|
|
30214
|
+
return { nativeBalance, nativeBalanceFormatted, isLoading };
|
|
30181
30215
|
};
|
|
30182
30216
|
|
|
30183
|
-
|
|
30184
|
-
|
|
30185
|
-
|
|
30186
|
-
|
|
30187
|
-
}
|
|
30188
|
-
async getBaseReserve() {
|
|
30189
|
-
const response = await fetch(`${this.apiUrl}/ledgers?order=desc&limit=1`);
|
|
30190
|
-
if (!response.ok) {
|
|
30191
|
-
throw new Error(`Failed to fetch ledgers: ${response.status}`);
|
|
30192
|
-
}
|
|
30193
|
-
const ledgers = await response.json();
|
|
30194
|
-
const latestLedger = ledgers?._embedded.records?.[0];
|
|
30195
|
-
if (latestLedger?.base_reserve_in_stroops == null) {
|
|
30196
|
-
throw new Error("Invalid ledger data");
|
|
30197
|
-
}
|
|
30198
|
-
const baseReserveBn = BigInt(latestLedger.base_reserve_in_stroops);
|
|
30199
|
-
return baseReserveBn;
|
|
30200
|
-
}
|
|
30201
|
-
}
|
|
30202
|
-
|
|
30203
|
-
const DEFAULT_REFETCH_INTERVAL$1 = 20_000;
|
|
30204
|
-
function useStellarAccountActivation({ address, chain, token, }) {
|
|
30205
|
-
/**
|
|
30206
|
-
* Checks if the destination account exists on the Stellar network
|
|
30207
|
-
* Stellar accounts need to have a minimum balance before they can receive payments
|
|
30208
|
-
*/
|
|
30209
|
-
const accountActivatedInfo = reactQuery.useQuery({
|
|
30210
|
-
queryKey: keys().stellarAccountActivatedInfo(address, chain?.chainId, chain?.chainType),
|
|
30211
|
-
queryFn: async () => {
|
|
30212
|
-
if (chain?.chainType !== squidTypes.ChainType.STELLAR ||
|
|
30213
|
-
token?.type !== squidTypes.ChainType.STELLAR) {
|
|
30214
|
-
return null;
|
|
30215
|
-
}
|
|
30216
|
-
if (!address) {
|
|
30217
|
-
throw new Error("Destination address is required");
|
|
30218
|
-
}
|
|
30219
|
-
// TODO: update types
|
|
30220
|
-
const [horizonApiUrl] = chain?.horizonRpcList;
|
|
30221
|
-
if (typeof horizonApiUrl !== "string") {
|
|
30222
|
-
throw new Error("Invalid Horizon API URL");
|
|
30223
|
-
}
|
|
30224
|
-
const stellarApiClient = new StellarApiClient(horizonApiUrl);
|
|
30225
|
-
const reserveBase = await stellarApiClient.getBaseReserve();
|
|
30226
|
-
// Stellar accounts require two base reserves to be activated
|
|
30227
|
-
// https://developers.stellar.org/docs/learn/fundamentals/lumens#minimum-balance
|
|
30228
|
-
const accountReserveBase = reserveBase * BigInt(2);
|
|
30229
|
-
const stellarRpcClient = await getClient(chain);
|
|
30230
|
-
const isActivated = await stellarRpcClient.isAccountActivated(address);
|
|
30231
|
-
return {
|
|
30232
|
-
isActivated,
|
|
30233
|
-
reserveBaseBn: accountReserveBase,
|
|
30234
|
-
};
|
|
30235
|
-
},
|
|
30236
|
-
enabled: !!address && chain?.chainType === squidTypes.ChainType.STELLAR,
|
|
30237
|
-
refetchInterval: DEFAULT_REFETCH_INTERVAL$1,
|
|
30238
|
-
refetchOnWindowFocus: true,
|
|
30217
|
+
function useHederaAccountActivation({ address, chain, token }) {
|
|
30218
|
+
const { balance: destNativeEvmBalance } = useEvmNativeBalance({
|
|
30219
|
+
chain,
|
|
30220
|
+
address,
|
|
30239
30221
|
});
|
|
30222
|
+
const isHederaAccountActivated = React.useMemo(() => {
|
|
30223
|
+
if (token?.chainId !== CHAIN_IDS.HEDERA)
|
|
30224
|
+
return true;
|
|
30225
|
+
if (token.address.toLowerCase() === nativeEvmTokenAddress.toLowerCase())
|
|
30226
|
+
return true;
|
|
30227
|
+
return (!!destNativeEvmBalance?.value && destNativeEvmBalance.value > BigInt(0));
|
|
30228
|
+
}, [token?.chainId, token?.address, destNativeEvmBalance?.value]);
|
|
30240
30229
|
return {
|
|
30241
|
-
|
|
30230
|
+
isHederaAccountActivated,
|
|
30242
30231
|
};
|
|
30243
30232
|
}
|
|
30244
30233
|
|
|
@@ -30766,161 +30755,6 @@ const useSingleTokenPrice = (tokenData) => {
|
|
|
30766
30755
|
return { tokenPrice, getUSDValue };
|
|
30767
30756
|
};
|
|
30768
30757
|
|
|
30769
|
-
const TEMPO_FEE_MANAGER_ADDRESS = "0xfeec000000000000000000000000000000000000";
|
|
30770
|
-
const feeManagerAbi = [
|
|
30771
|
-
{
|
|
30772
|
-
name: "userTokens",
|
|
30773
|
-
type: "function",
|
|
30774
|
-
stateMutability: "view",
|
|
30775
|
-
inputs: [{ type: "address", name: "user" }],
|
|
30776
|
-
outputs: [{ type: "address" }],
|
|
30777
|
-
},
|
|
30778
|
-
];
|
|
30779
|
-
const isTempoChain = (chainId) => chainId === CHAIN_IDS.TEMPO;
|
|
30780
|
-
/**
|
|
30781
|
-
* Convert a fee amount from virtual USD (18 decimals, attodollars)
|
|
30782
|
-
* to 6-decimal TIP-20 stablecoin units (microdollars).
|
|
30783
|
-
* All TIP-20 tokens on Tempo have 6 decimals.
|
|
30784
|
-
*/
|
|
30785
|
-
const convertTempoFeeToStablecoinUnits = (feeIn18Dec) => feeIn18Dec / BigInt(10 ** 12);
|
|
30786
|
-
/**
|
|
30787
|
-
* Pure function that derives TempoFeeData from on-chain query results.
|
|
30788
|
-
* Extracted from useTempoFeeCheck so the branching logic is unit-testable
|
|
30789
|
-
* without mocking React hooks.
|
|
30790
|
-
*/
|
|
30791
|
-
function resolveTempoFeeData({ hasAccountFeePreference, accountGasTokenAddress, accountGasTokenBalance, fromTokenAddress, fromTokenBalance, }) {
|
|
30792
|
-
if (hasAccountFeePreference && accountGasTokenAddress) {
|
|
30793
|
-
const gasTokenIsFromToken = !!fromTokenAddress &&
|
|
30794
|
-
accountGasTokenAddress.toLowerCase() === fromTokenAddress.toLowerCase();
|
|
30795
|
-
return {
|
|
30796
|
-
gasTokenBalance: accountGasTokenBalance ?? BigInt(0),
|
|
30797
|
-
gasTokenIsFromToken,
|
|
30798
|
-
gasTokenAddress: accountGasTokenAddress,
|
|
30799
|
-
};
|
|
30800
|
-
}
|
|
30801
|
-
if (fromTokenAddress != null && fromTokenBalance != null) {
|
|
30802
|
-
return {
|
|
30803
|
-
gasTokenBalance: fromTokenBalance,
|
|
30804
|
-
gasTokenIsFromToken: true,
|
|
30805
|
-
gasTokenAddress: fromTokenAddress,
|
|
30806
|
-
};
|
|
30807
|
-
}
|
|
30808
|
-
return null;
|
|
30809
|
-
}
|
|
30810
|
-
/**
|
|
30811
|
-
* Resolves the token that pays network gas fees for a given chain and transaction context.
|
|
30812
|
-
* For Tempo chains, this is the dynamically resolved gas token (from tempoFeeData).
|
|
30813
|
-
* For all other chains, this is the static native gas token.
|
|
30814
|
-
*/
|
|
30815
|
-
function resolveGasToken({ nativeToken, chain, tempoFeeData, evmTokens, }) {
|
|
30816
|
-
if (!chain)
|
|
30817
|
-
return undefined;
|
|
30818
|
-
if (isTempoChain(chain.chainId)) {
|
|
30819
|
-
if (!tempoFeeData)
|
|
30820
|
-
return undefined;
|
|
30821
|
-
return evmTokens.find((t) => t.chainId === chain.chainId &&
|
|
30822
|
-
t.address.toLowerCase() === tempoFeeData.gasTokenAddress.toLowerCase());
|
|
30823
|
-
}
|
|
30824
|
-
return nativeToken;
|
|
30825
|
-
}
|
|
30826
|
-
/**
|
|
30827
|
-
* Resolves chain-agnostic fee parameters from chain-specific inputs.
|
|
30828
|
-
* This is the single place that knows about Tempo vs. native-token fee semantics.
|
|
30829
|
-
*
|
|
30830
|
-
* Safe defaults when tempoFeeData is missing on a Tempo chain: balance=0 and
|
|
30831
|
-
* fromTokenPaysNetworkFee=false, so the UI blocks rather than misleads.
|
|
30832
|
-
*/
|
|
30833
|
-
function resolveChainFeeParams({ fromChainId, tempoFeeData, isFromTokenNative, nativeBalanceWei, }) {
|
|
30834
|
-
if (isTempoChain(fromChainId)) {
|
|
30835
|
-
if (!tempoFeeData) {
|
|
30836
|
-
return null;
|
|
30837
|
-
}
|
|
30838
|
-
return {
|
|
30839
|
-
fromTokenPaysNetworkFee: tempoFeeData?.gasTokenIsFromToken ?? false,
|
|
30840
|
-
gasTokenBalanceWei: tempoFeeData?.gasTokenBalance ?? BigInt(0),
|
|
30841
|
-
normalizeFee: convertTempoFeeToStablecoinUnits,
|
|
30842
|
-
};
|
|
30843
|
-
}
|
|
30844
|
-
return {
|
|
30845
|
-
fromTokenPaysNetworkFee: isFromTokenNative,
|
|
30846
|
-
gasTokenBalanceWei: nativeBalanceWei,
|
|
30847
|
-
normalizeFee: (fee) => fee,
|
|
30848
|
-
};
|
|
30849
|
-
}
|
|
30850
|
-
|
|
30851
|
-
const BALANCE_QUERY_STALE_TIME = 10_000;
|
|
30852
|
-
/**
|
|
30853
|
-
* Returns raw on-chain gas token data for Tempo chains, or null when the source
|
|
30854
|
-
* chain is not Tempo.
|
|
30855
|
-
*/
|
|
30856
|
-
const useTempoFeeCheck = ({ fromChain, fromToken, }) => {
|
|
30857
|
-
const { connectedAddresses } = useWallet();
|
|
30858
|
-
const evmAddress = parseEvmAddress(connectedAddresses[squidTypes.ChainType.EVM]);
|
|
30859
|
-
const isTempo = isTempoChain(fromChain?.chainId);
|
|
30860
|
-
const chainId = Number(fromChain?.chainId);
|
|
30861
|
-
// Read account-level gas token preference from FeeManager precompile
|
|
30862
|
-
// See docs: https://docs.tempo.xyz/protocol/fees/spec-fee#account-level
|
|
30863
|
-
const { data: accountGasTokenAddress } = wagmi.useReadContract({
|
|
30864
|
-
address: TEMPO_FEE_MANAGER_ADDRESS,
|
|
30865
|
-
abi: feeManagerAbi,
|
|
30866
|
-
functionName: "userTokens",
|
|
30867
|
-
args: evmAddress ? [evmAddress] : undefined,
|
|
30868
|
-
chainId,
|
|
30869
|
-
query: {
|
|
30870
|
-
enabled: isTempo && !!evmAddress,
|
|
30871
|
-
staleTime: 30_000,
|
|
30872
|
-
},
|
|
30873
|
-
});
|
|
30874
|
-
const hasAccountFeePreference = !!accountGasTokenAddress &&
|
|
30875
|
-
// By default, the zero address is returned when the user has no preference set
|
|
30876
|
-
viem.zeroAddress.toLowerCase() !== accountGasTokenAddress.toLowerCase();
|
|
30877
|
-
// Fetch balance of the account-preferred gas token (if set)
|
|
30878
|
-
const { data: accountGasTokenBalance } = wagmi.useBalance({
|
|
30879
|
-
address: evmAddress,
|
|
30880
|
-
token: hasAccountFeePreference ? accountGasTokenAddress : undefined,
|
|
30881
|
-
chainId,
|
|
30882
|
-
query: {
|
|
30883
|
-
enabled: isTempo && hasAccountFeePreference && !!evmAddress,
|
|
30884
|
-
staleTime: BALANCE_QUERY_STALE_TIME,
|
|
30885
|
-
},
|
|
30886
|
-
});
|
|
30887
|
-
const fromTokenAddress = parseEvmAddress(fromToken?.address);
|
|
30888
|
-
const { data: fromTokenBalance } = wagmi.useBalance({
|
|
30889
|
-
address: evmAddress,
|
|
30890
|
-
token: fromTokenAddress,
|
|
30891
|
-
chainId,
|
|
30892
|
-
query: {
|
|
30893
|
-
enabled: isTempo && !!evmAddress,
|
|
30894
|
-
staleTime: BALANCE_QUERY_STALE_TIME,
|
|
30895
|
-
},
|
|
30896
|
-
});
|
|
30897
|
-
if (!isTempo)
|
|
30898
|
-
return null;
|
|
30899
|
-
return resolveTempoFeeData({
|
|
30900
|
-
hasAccountFeePreference,
|
|
30901
|
-
accountGasTokenAddress,
|
|
30902
|
-
accountGasTokenBalance: accountGasTokenBalance?.value,
|
|
30903
|
-
fromTokenAddress,
|
|
30904
|
-
fromTokenBalance: fromTokenBalance?.value,
|
|
30905
|
-
});
|
|
30906
|
-
};
|
|
30907
|
-
|
|
30908
|
-
/**
|
|
30909
|
-
* Resolves the token that pays network gas fees for a given chain
|
|
30910
|
-
*/
|
|
30911
|
-
function useSourceChainGasToken({ fromChain, fromToken, }) {
|
|
30912
|
-
const { nativeToken } = useNativeTokenForChain(fromChain);
|
|
30913
|
-
const { evmTokens } = useSquidTokens();
|
|
30914
|
-
const tempoFeeData = useTempoFeeCheck({ fromChain, fromToken });
|
|
30915
|
-
const gasToken = React.useMemo(() => resolveGasToken({
|
|
30916
|
-
nativeToken,
|
|
30917
|
-
chain: fromChain,
|
|
30918
|
-
tempoFeeData,
|
|
30919
|
-
evmTokens,
|
|
30920
|
-
}), [nativeToken, fromChain, tempoFeeData, evmTokens]);
|
|
30921
|
-
return { gasToken };
|
|
30922
|
-
}
|
|
30923
|
-
|
|
30924
30758
|
const MAX_COINGECKO_QUERY_TOKENS = 100;
|
|
30925
30759
|
const fetchHistoricalData = async (coingeckoId, timeFrame) => {
|
|
30926
30760
|
const now = Math.floor(Date.now() / 1000);
|
|
@@ -31071,20 +30905,24 @@ const calculateTotalNativeFees = ({ expressFeeCost, firstFeeCost, firstGasCost,
|
|
|
31071
30905
|
(sameTokenBetweenFees
|
|
31072
30906
|
? BigInt(firstFeeCost?.amount ?? "0") + BigInt(firstGasCost?.amount ?? "0")
|
|
31073
30907
|
: BigInt(firstGasCost?.amount ?? "0"));
|
|
31074
|
-
const
|
|
31075
|
-
|
|
31076
|
-
|
|
31077
|
-
|
|
31078
|
-
|
|
31079
|
-
|
|
31080
|
-
|
|
31081
|
-
|
|
31082
|
-
|
|
31083
|
-
|
|
31084
|
-
|
|
31085
|
-
|
|
31086
|
-
|
|
31087
|
-
|
|
30908
|
+
const isFromBalanceEnoughToSwap = ({ isFromTokenNative, fromAmount, totalFeesInNativeTokenPlusRatio, nativeTokenBalanceFromChainWei, }) => {
|
|
30909
|
+
const fromAmountBigInt = BigInt(fromAmount ?? "0");
|
|
30910
|
+
const totalFeesInNativeTokenPlusRatioBigInt = totalFeesInNativeTokenPlusRatio;
|
|
30911
|
+
const nativeTokenBalanceFromChainWeiBigInt = nativeTokenBalanceFromChainWei;
|
|
30912
|
+
return isFromTokenNative
|
|
30913
|
+
? fromAmountBigInt + totalFeesInNativeTokenPlusRatioBigInt <=
|
|
30914
|
+
nativeTokenBalanceFromChainWeiBigInt
|
|
30915
|
+
: totalFeesInNativeTokenPlusRatioBigInt <=
|
|
30916
|
+
nativeTokenBalanceFromChainWeiBigInt;
|
|
30917
|
+
};
|
|
30918
|
+
const calculateMinAmountValueWarnMsg = ({ isFromTokenNative, nativeTokenBalanceFromChainWei, sourceChainNativeTokenDecimals, totalFeesInNativeTokenPlusRatio, }) => isFromTokenNative
|
|
30919
|
+
? (() => {
|
|
30920
|
+
const minAmount = nativeTokenBalanceFromChainWei - totalFeesInNativeTokenPlusRatio;
|
|
30921
|
+
return minAmount > BigInt(0)
|
|
30922
|
+
? formatBNToReadable(minAmount, sourceChainNativeTokenDecimals)
|
|
30923
|
+
: "0";
|
|
30924
|
+
})()
|
|
30925
|
+
: undefined;
|
|
31088
30926
|
/**
|
|
31089
30927
|
* Calculates the estimated duration of a route
|
|
31090
30928
|
*
|
|
@@ -31123,10 +30961,12 @@ const formatEstimatedRouteDuration = ({ estimatedRouteDuration, isSingleChainRou
|
|
|
31123
30961
|
* @returns {Object} An object containing various estimate results and calculations, including token information,
|
|
31124
30962
|
* amounts, fees, gas costs, and other relevant data for the transaction.
|
|
31125
30963
|
*/
|
|
31126
|
-
const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, collectFees, nativeTokenBalanceFromChainWei,
|
|
30964
|
+
const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, collectFees, nativeTokenBalanceFromChainWei, }) => {
|
|
31127
30965
|
const fromToken = findToken(tokens, squidRoute?.params.fromChain, squidRoute?.params.fromToken);
|
|
31128
30966
|
const fromAmount = squidRoute?.estimate?.fromAmount;
|
|
31129
30967
|
const fromAmountFormatted = formatAmount(fromAmount, fromToken?.decimals);
|
|
30968
|
+
const sourceChainNativeToken = findNativeToken(tokens, fromChain);
|
|
30969
|
+
const destChainNativeToken = findNativeToken(tokens, toChain);
|
|
31130
30970
|
const toAmountUSD = squidRoute?.estimate?.toAmountUSD;
|
|
31131
30971
|
const exchangeRate = squidRoute?.estimate.exchangeRate ?? "0";
|
|
31132
30972
|
const toAmountMinUSD = squidRoute?.estimate.toAmountMinUSD ?? "0";
|
|
@@ -31145,6 +30985,10 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31145
30985
|
const expectedGasRefundCostUSD = convertTokenAmountToUSD(formatBNToReadable(expectedGasRefundCost, firstFeeCost?.token.decimals ?? 18), firstFeeCost?.token.usdPrice ?? "0");
|
|
31146
30986
|
const sameTokenBetweenFees = firstFeeCost?.token.address === firstGasCost?.token.address &&
|
|
31147
30987
|
firstFeeCost?.token.chainId === firstGasCost?.token.chainId;
|
|
30988
|
+
const isFromTokenNative =
|
|
30989
|
+
// TODO: temporary fix, currently nativeCurrency.symbol is not always in uppercase
|
|
30990
|
+
fromToken?.symbol.toUpperCase() ===
|
|
30991
|
+
fromChain?.nativeCurrency.symbol.toUpperCase();
|
|
31148
30992
|
const totalNativeFees = calculateTotalNativeFees({
|
|
31149
30993
|
expressFeeCost,
|
|
31150
30994
|
firstFeeCost,
|
|
@@ -31152,32 +30996,19 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31152
30996
|
sameTokenBetweenFees,
|
|
31153
30997
|
});
|
|
31154
30998
|
const totalFeesInNativeTokenPlusRatio = (totalNativeFees * BigInt(110)) / BigInt(100);
|
|
31155
|
-
const
|
|
31156
|
-
|
|
31157
|
-
fromToken?.symbol.toUpperCase() ===
|
|
31158
|
-
fromChain?.nativeCurrency.symbol.toUpperCase();
|
|
31159
|
-
const chainFeeParams = resolveChainFeeParams({
|
|
31160
|
-
tempoFeeData,
|
|
31161
|
-
isFromTokenNative: isNativeTokenFromChain,
|
|
31162
|
-
nativeBalanceWei: nativeTokenBalanceFromChainWei,
|
|
31163
|
-
fromChainId: fromChain?.chainId,
|
|
31164
|
-
});
|
|
31165
|
-
const networkFeesWei = chainFeeParams?.normalizeFee(totalFeesInNativeTokenPlusRatio) ?? BigInt(0);
|
|
31166
|
-
const gasBalanceEnough = isGasBalanceEnough({
|
|
31167
|
-
chainFeeParams,
|
|
30999
|
+
const fromBalanceEnoughToSwap = isFromBalanceEnoughToSwap({
|
|
31000
|
+
isFromTokenNative,
|
|
31168
31001
|
fromAmount,
|
|
31169
|
-
|
|
31002
|
+
fromTokenDecimals: fromToken?.decimals,
|
|
31003
|
+
totalFeesInNativeTokenPlusRatio,
|
|
31004
|
+
nativeTokenBalanceFromChainWei,
|
|
31170
31005
|
});
|
|
31171
|
-
const
|
|
31172
|
-
|
|
31173
|
-
|
|
31174
|
-
|
|
31006
|
+
const minAmountValueWarnMsg = calculateMinAmountValueWarnMsg({
|
|
31007
|
+
isFromTokenNative,
|
|
31008
|
+
nativeTokenBalanceFromChainWei,
|
|
31009
|
+
sourceChainNativeTokenDecimals: sourceChainNativeToken?.decimals,
|
|
31010
|
+
totalFeesInNativeTokenPlusRatio,
|
|
31175
31011
|
});
|
|
31176
|
-
const minAmountValueWarnMsg = minAmount
|
|
31177
|
-
? formatBNToReadable(minAmount, chainFeeParams?.fromTokenPaysNetworkFee
|
|
31178
|
-
? fromToken?.decimals
|
|
31179
|
-
: undefined)
|
|
31180
|
-
: undefined;
|
|
31181
31012
|
const isSingleChainRoute = fromChain?.chainId === toChain?.chainId;
|
|
31182
31013
|
const estimatedRouteDuration = formatEstimatedRouteDuration({
|
|
31183
31014
|
estimatedRouteDuration: squidRoute?.estimate?.estimatedRouteDuration,
|
|
@@ -31187,6 +31018,8 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31187
31018
|
fromToken,
|
|
31188
31019
|
fromAmount,
|
|
31189
31020
|
fromAmountFormatted,
|
|
31021
|
+
sourceChainNativeToken,
|
|
31022
|
+
destChainNativeToken,
|
|
31190
31023
|
toAmountUSD,
|
|
31191
31024
|
exchangeRate,
|
|
31192
31025
|
toAmountMinUSD,
|
|
@@ -31201,10 +31034,10 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31201
31034
|
expectedGasRefundCost,
|
|
31202
31035
|
expectedGasRefundCostUSD,
|
|
31203
31036
|
sameTokenBetweenFees,
|
|
31204
|
-
|
|
31037
|
+
isFromTokenNative,
|
|
31205
31038
|
totalNativeFees,
|
|
31206
31039
|
totalFeesInNativeTokenPlusRatio,
|
|
31207
|
-
|
|
31040
|
+
fromBalanceEnoughToSwap,
|
|
31208
31041
|
minAmountValueWarnMsg,
|
|
31209
31042
|
estimatedRouteDuration,
|
|
31210
31043
|
};
|
|
@@ -31240,6 +31073,28 @@ const calculateTotalWithRefundEstimate = (allFeeCosts, expectedGasRefundCost, ge
|
|
|
31240
31073
|
feeToken: firstFeeCost?.token,
|
|
31241
31074
|
};
|
|
31242
31075
|
};
|
|
31076
|
+
/**
|
|
31077
|
+
* Calculates the proposed gas amount for the destination chain.
|
|
31078
|
+
*
|
|
31079
|
+
* @param destChainNativeToken - The symbol of the native token for the destination chain.
|
|
31080
|
+
* @returns An object containing the proposed gas amount value and the currency symbol.
|
|
31081
|
+
*/
|
|
31082
|
+
const getProposedGasDestinationAmount = (destChainNativeToken) => {
|
|
31083
|
+
const gasAmounts = {
|
|
31084
|
+
GLMR: 5.289,
|
|
31085
|
+
ETH: 0.0009,
|
|
31086
|
+
AVAX: 0.115,
|
|
31087
|
+
BNB: 0.00425,
|
|
31088
|
+
FTM: 4.45,
|
|
31089
|
+
CELO: 3.052,
|
|
31090
|
+
KAVA: 2.339,
|
|
31091
|
+
MATIC: 1.795,
|
|
31092
|
+
};
|
|
31093
|
+
return {
|
|
31094
|
+
value: gasAmounts[destChainNativeToken ?? ""] ?? 0,
|
|
31095
|
+
currency: destChainNativeToken,
|
|
31096
|
+
};
|
|
31097
|
+
};
|
|
31243
31098
|
|
|
31244
31099
|
function useSendTransactionGas({ chain, token, from, }) {
|
|
31245
31100
|
return reactQuery.useQuery({
|
|
@@ -31261,11 +31116,7 @@ function useSendTransactionGas({ chain, token, from, }) {
|
|
|
31261
31116
|
// Some RPC providers require the sender to have enough balance, otherwise estimation reverts
|
|
31262
31117
|
// so we'll try to use the user provided address when possible
|
|
31263
31118
|
const sender = from || dummyAddress;
|
|
31264
|
-
|
|
31265
|
-
// We can simulate an ERC20 transfer instead
|
|
31266
|
-
const supportsNativeTransfers = chain.chainId !== CHAIN_IDS.TEMPO;
|
|
31267
|
-
const isNativeToken = token.address.toLowerCase() === nativeEvmTokenAddress.toLowerCase();
|
|
31268
|
-
if (isNativeToken && supportsNativeTransfers) {
|
|
31119
|
+
if (token.address.toLowerCase() === nativeEvmTokenAddress.toLowerCase()) {
|
|
31269
31120
|
const gas = await client.estimateGas({
|
|
31270
31121
|
from: sender,
|
|
31271
31122
|
to: dummyAddress,
|
|
@@ -31285,14 +31136,7 @@ function useSendTransactionGas({ chain, token, from, }) {
|
|
|
31285
31136
|
data,
|
|
31286
31137
|
chainId: chain.chainId,
|
|
31287
31138
|
});
|
|
31288
|
-
|
|
31289
|
-
// Tempo fees are denominated in virtual USD (18 dec, 1e-18 USD units).
|
|
31290
|
-
// Convert to 6-dec stablecoin units so all callers receive a
|
|
31291
|
-
// consistent unit without needing to know about the chain.
|
|
31292
|
-
if (chain.chainId === CHAIN_IDS.TEMPO) {
|
|
31293
|
-
return convertTempoFeeToStablecoinUnits(feeWei);
|
|
31294
|
-
}
|
|
31295
|
-
return feeWei;
|
|
31139
|
+
return gas * maxFeePerGas;
|
|
31296
31140
|
}
|
|
31297
31141
|
case squidTypes.ChainType.COSMOS: {
|
|
31298
31142
|
// TODO: get gas estimation from backend
|
|
@@ -31340,24 +31184,18 @@ function useEstimateSendTransaction({ chain, token, amount, balance, from, }) {
|
|
|
31340
31184
|
from,
|
|
31341
31185
|
});
|
|
31342
31186
|
const { nativeBalance } = useNativeBalance(chain);
|
|
31343
|
-
const
|
|
31344
|
-
|
|
31345
|
-
fromChainId: chain?.chainId,
|
|
31346
|
-
tempoFeeData,
|
|
31347
|
-
isFromTokenNative: token != null &&
|
|
31348
|
-
token.address.toLowerCase() ===
|
|
31349
|
-
chainTypeToNativeTokenAddressMap[token.type]?.toLowerCase(),
|
|
31350
|
-
nativeBalanceWei: nativeBalance?.value ?? BigInt(0),
|
|
31351
|
-
});
|
|
31352
|
-
const gasBalanceEnough = React.useMemo(() => {
|
|
31353
|
-
if (!amount || !token?.decimals)
|
|
31187
|
+
const isNativeBalanceEnoughToPayGasFees = React.useMemo(() => {
|
|
31188
|
+
if (!nativeBalance?.value || !amount || !token?.decimals) {
|
|
31354
31189
|
return false;
|
|
31355
|
-
|
|
31356
|
-
|
|
31357
|
-
|
|
31358
|
-
|
|
31359
|
-
|
|
31360
|
-
|
|
31190
|
+
}
|
|
31191
|
+
const isTokenNative = token.address.toLowerCase() ===
|
|
31192
|
+
chainTypeToNativeTokenAddressMap[token.type].toLowerCase();
|
|
31193
|
+
if (isTokenNative) {
|
|
31194
|
+
return (parseToBigInt(amount, token.decimals) + estimatedGas <=
|
|
31195
|
+
nativeBalance.value);
|
|
31196
|
+
}
|
|
31197
|
+
return estimatedGas <= nativeBalance.value;
|
|
31198
|
+
}, [amount, estimatedGas, nativeBalance?.value, token]);
|
|
31361
31199
|
const isBalanceEnough = React.useMemo(() => {
|
|
31362
31200
|
if (token?.decimals == null || !balance || !amount)
|
|
31363
31201
|
return false;
|
|
@@ -31365,30 +31203,28 @@ function useEstimateSendTransaction({ chain, token, amount, balance, from, }) {
|
|
|
31365
31203
|
parseToBigInt(amount, token.decimals));
|
|
31366
31204
|
}, [amount, balance, token?.decimals]);
|
|
31367
31205
|
const minAmountValueWarnMsg = React.useMemo(() => {
|
|
31368
|
-
if (!token?.address || !estimatedGas)
|
|
31369
|
-
return undefined;
|
|
31370
|
-
if (!chainFeeParams)
|
|
31206
|
+
if (!token?.address || !nativeBalance?.value || !estimatedGas)
|
|
31371
31207
|
return undefined;
|
|
31372
|
-
const
|
|
31373
|
-
|
|
31374
|
-
|
|
31375
|
-
|
|
31376
|
-
|
|
31377
|
-
|
|
31208
|
+
const isFromTokenNative = token.address.toLowerCase() ===
|
|
31209
|
+
chainTypeToNativeTokenAddressMap[token.type].toLowerCase();
|
|
31210
|
+
return calculateMinAmountValueWarnMsg({
|
|
31211
|
+
isFromTokenNative,
|
|
31212
|
+
nativeTokenBalanceFromChainWei: nativeBalance.value,
|
|
31213
|
+
sourceChainNativeTokenDecimals: nativeBalance.decimals,
|
|
31214
|
+
totalFeesInNativeTokenPlusRatio: estimatedGas,
|
|
31378
31215
|
});
|
|
31379
|
-
|
|
31380
|
-
|
|
31381
|
-
|
|
31382
|
-
|
|
31383
|
-
|
|
31384
|
-
|
|
31385
|
-
|
|
31216
|
+
}, [
|
|
31217
|
+
estimatedGas,
|
|
31218
|
+
nativeBalance?.decimals,
|
|
31219
|
+
nativeBalance?.value,
|
|
31220
|
+
token?.address,
|
|
31221
|
+
token?.type,
|
|
31222
|
+
]);
|
|
31386
31223
|
return {
|
|
31387
31224
|
estimatedGas,
|
|
31388
31225
|
isBalanceEnough,
|
|
31389
|
-
tokenPaysNetworkFee: chainFeeParams?.fromTokenPaysNetworkFee ?? false,
|
|
31390
31226
|
isLoading,
|
|
31391
|
-
|
|
31227
|
+
isNativeBalanceEnoughToPayGasFees,
|
|
31392
31228
|
minAmountValueWarnMsg,
|
|
31393
31229
|
};
|
|
31394
31230
|
}
|
|
@@ -32447,7 +32283,7 @@ const useApproval = ({ squidRoute, }) => {
|
|
|
32447
32283
|
// This is to ensure we're using the latest expiry timestamp
|
|
32448
32284
|
if (squidRoute) {
|
|
32449
32285
|
queryClient.refetchQueries({
|
|
32450
|
-
queryKey: keys().transaction(squidRoute.params.fromChain, squidRoute.params.toChain, squidRoute.params.toToken, squidRoute.params.fromToken, fromPrice, squidRoute.params.slippage, squidRoute.params.fromAddress, squidRoute.params.bypassGuardrails, squidRoute.params.toAddress, squidRoute.params.fallbackAddresses?.[0]?.address, squidRoute.params.quoteOnly, getChainType(squidRoute.params.fromChain), squidRoute.params.preHook, squidRoute.params.postHook,
|
|
32286
|
+
queryKey: keys().transaction(squidRoute.params.fromChain, squidRoute.params.toChain, squidRoute.params.toToken, squidRoute.params.fromToken, fromPrice, squidRoute.params.slippage, squidRoute.params.receiveGasOnDestination, squidRoute.params.fromAddress, squidRoute.params.bypassGuardrails, squidRoute.params.toAddress, squidRoute.params.fallbackAddresses?.[0]?.address, squidRoute.params.quoteOnly, getChainType(squidRoute.params.fromChain), squidRoute.params.preHook, squidRoute.params.postHook,
|
|
32451
32287
|
// TODO: update types
|
|
32452
32288
|
squidRoute.params?.overrideGasRefundAddress),
|
|
32453
32289
|
});
|
|
@@ -32466,10 +32302,8 @@ const AXELAR_PROVIDER_IMAGE_URL = "https://raw.githubusercontent.com/0xsquid/ass
|
|
|
32466
32302
|
const useEstimate = (squidRoute) => {
|
|
32467
32303
|
const collectFees = useConfigStore((state) => state.config.collectFees);
|
|
32468
32304
|
const { tokens } = useSquidTokens();
|
|
32469
|
-
const { fromChain, toChain
|
|
32305
|
+
const { fromChain, toChain } = useSwap();
|
|
32470
32306
|
const { nativeBalance } = useNativeBalance(fromChain);
|
|
32471
|
-
const fromToken = React.useMemo(() => findToken(tokens, squidRoute?.params.fromChain, squidRoute?.params.fromToken), [tokens, squidRoute?.params.fromChain, squidRoute?.params.fromToken]);
|
|
32472
|
-
const tempoFeeData = useTempoFeeCheck({ fromChain, fromToken });
|
|
32473
32307
|
const estimateResults = React.useMemo(() => calculateEstimateResults({
|
|
32474
32308
|
squidRoute,
|
|
32475
32309
|
tokens,
|
|
@@ -32477,17 +32311,8 @@ const useEstimate = (squidRoute) => {
|
|
|
32477
32311
|
toChain,
|
|
32478
32312
|
collectFees: !!collectFees && collectFees.fee > 0,
|
|
32479
32313
|
nativeTokenBalanceFromChainWei: nativeBalance?.value ?? BigInt("0"),
|
|
32480
|
-
|
|
32481
|
-
|
|
32482
|
-
squidRoute,
|
|
32483
|
-
tokens,
|
|
32484
|
-
fromChain,
|
|
32485
|
-
toChain,
|
|
32486
|
-
collectFees,
|
|
32487
|
-
nativeBalance,
|
|
32488
|
-
tempoFeeData,
|
|
32489
|
-
]);
|
|
32490
|
-
const fromBalanceFormatted = useMultiChainBalance({
|
|
32314
|
+
}), [squidRoute, tokens, fromChain, toChain, collectFees, nativeBalance]);
|
|
32315
|
+
const balanceFormatted = useMultiChainBalance({
|
|
32491
32316
|
chain: fromChain,
|
|
32492
32317
|
token: estimateResults.fromToken,
|
|
32493
32318
|
userAddress: squidRoute?.params.fromAddress ?? "",
|
|
@@ -32499,6 +32324,7 @@ const useEstimate = (squidRoute) => {
|
|
|
32499
32324
|
estimateResults.expectedGasRefundCost,
|
|
32500
32325
|
getUSDValue,
|
|
32501
32326
|
]);
|
|
32327
|
+
const proposedGasDestinationAmount = React.useMemo(() => getProposedGasDestinationAmount(estimateResults.destChainNativeToken?.symbol), [estimateResults.destChainNativeToken]);
|
|
32502
32328
|
const { feeCostsFormatted, totalFeeCostsUsd } = React.useMemo(() => {
|
|
32503
32329
|
const feeCosts = squidRoute?.estimate.feeCosts ?? [];
|
|
32504
32330
|
const feeCostsRenamed = [];
|
|
@@ -32557,18 +32383,19 @@ const useEstimate = (squidRoute) => {
|
|
|
32557
32383
|
totalFeeCostsUsd: totalFeeCostsUsdFormatted,
|
|
32558
32384
|
};
|
|
32559
32385
|
}, [squidRoute?.estimate.feeCosts]);
|
|
32560
|
-
const slippageFormatted =
|
|
32561
|
-
|
|
32562
|
-
|
|
32563
|
-
|
|
32564
|
-
|
|
32565
|
-
|
|
32386
|
+
const slippageFormatted =
|
|
32387
|
+
// TODO: update types
|
|
32388
|
+
Number(squidRoute?.estimate?.aggregateSlippage ?? 0).toFixed(2) +
|
|
32389
|
+
"%";
|
|
32390
|
+
const enoughBalanceToSwap = +balanceFormatted >= 0 &&
|
|
32391
|
+
+balanceFormatted > +estimateResults.fromAmountFormatted;
|
|
32566
32392
|
return {
|
|
32567
32393
|
...estimateResults,
|
|
32568
|
-
|
|
32394
|
+
balanceFormatted,
|
|
32569
32395
|
slippageFormatted,
|
|
32570
32396
|
totalWithRefundEstimate,
|
|
32571
|
-
|
|
32397
|
+
proposedGasDestinationAmount,
|
|
32398
|
+
enoughBalanceToSwap,
|
|
32572
32399
|
feeCostsFormatted,
|
|
32573
32400
|
totalFeeCostsUsd,
|
|
32574
32401
|
};
|
|
@@ -36316,7 +36143,7 @@ const useGetRoute = () => {
|
|
|
36316
36143
|
});
|
|
36317
36144
|
// Cache the route data
|
|
36318
36145
|
// Useful when the getRoute mutation is called from another hook
|
|
36319
|
-
queryClient.setQueryData(keys().transaction(fromChain, toChain, toToken.address, fromToken.address, fromPrice, config.slippage, sourceUserAddress, config.degenMode, destinationAddress, swapRoute?.fallbackAddress, quoteOnly, fromChainType, config.preHook, config.postHook, overrideGasRefundAddress), route);
|
|
36146
|
+
queryClient.setQueryData(keys().transaction(fromChain, toChain, toToken.address, fromToken.address, fromPrice, config.slippage, config.enableGetGasOnDestination, sourceUserAddress, config.degenMode, destinationAddress, swapRoute?.fallbackAddress, quoteOnly, fromChainType, config.preHook, config.postHook, overrideGasRefundAddress), route);
|
|
36320
36147
|
return route;
|
|
36321
36148
|
});
|
|
36322
36149
|
};
|
|
@@ -36339,13 +36166,14 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
|
|
|
36339
36166
|
const sourceUserAddress = isDepositAddressEnabled && isAvailableAsPaymentMethod
|
|
36340
36167
|
? depositRefundAddress ?? sourceConnectedAddress
|
|
36341
36168
|
: sourceConnectedAddress;
|
|
36342
|
-
const squidRouteQueryKeys = React.useMemo(() => keys().transaction(fromChain?.chainId, toChain?.chainId, toToken?.address, fromToken?.address, fromPrice, config.slippage, sourceUserAddress, config.degenMode, destinationAddress, fallbackAddress, quoteOnly, fromChain?.chainType, config.preHook, config.postHook, config.overrideGasRefundAddress), [
|
|
36169
|
+
const squidRouteQueryKeys = React.useMemo(() => keys().transaction(fromChain?.chainId, toChain?.chainId, toToken?.address, fromToken?.address, fromPrice, config.slippage, config.enableGetGasOnDestination, sourceUserAddress, config.degenMode, destinationAddress, fallbackAddress, quoteOnly, fromChain?.chainType, config.preHook, config.postHook, config.overrideGasRefundAddress), [
|
|
36343
36170
|
fromChain?.chainId,
|
|
36344
36171
|
toChain?.chainId,
|
|
36345
36172
|
toToken?.address,
|
|
36346
36173
|
fromToken?.address,
|
|
36347
36174
|
fromPrice,
|
|
36348
36175
|
config.slippage,
|
|
36176
|
+
config.enableGetGasOnDestination,
|
|
36349
36177
|
sourceUserAddress,
|
|
36350
36178
|
config.degenMode,
|
|
36351
36179
|
destinationAddress,
|
|
@@ -36617,6 +36445,35 @@ const useAvatar = (seed = zeroAddress) => {
|
|
|
36617
36445
|
return avatar || "";
|
|
36618
36446
|
};
|
|
36619
36447
|
|
|
36448
|
+
const useUserParams = () => {
|
|
36449
|
+
const enableGetGasOnDestination = useConfigStore((state) => state.config.enableGetGasOnDestination);
|
|
36450
|
+
const { fromToken, toToken, toChain, fromChain } = useSwap();
|
|
36451
|
+
// =============
|
|
36452
|
+
// GAS
|
|
36453
|
+
// =============
|
|
36454
|
+
const getGasOnDestSupportedForThisRoute = React.useMemo(() =>
|
|
36455
|
+
// Not supporting get gas on dest for same chains
|
|
36456
|
+
fromChain?.chainId !== toChain?.chainId &&
|
|
36457
|
+
// If the destination chain is cosmos, we don't support getting gas there
|
|
36458
|
+
toChain?.chainType !== squidTypes.ChainType.COSMOS &&
|
|
36459
|
+
// Not supporting get gas on dest for same tokens (bridge)
|
|
36460
|
+
((fromToken?.subGraphIds?.some((sgi) => !!toToken?.subGraphIds?.includes(sgi)) &&
|
|
36461
|
+
toToken?.subGraphIds?.some((sgi) => !!fromToken?.subGraphIds?.includes(sgi))) ||
|
|
36462
|
+
// Except for uusdc -> uusdc
|
|
36463
|
+
(fromToken?.subGraphIds?.includes("uusdc") &&
|
|
36464
|
+
toToken?.subGraphIds?.includes("uusdc"))), [
|
|
36465
|
+
fromChain?.chainId,
|
|
36466
|
+
fromToken?.subGraphIds,
|
|
36467
|
+
toChain?.chainId,
|
|
36468
|
+
toToken?.subGraphIds,
|
|
36469
|
+
toChain?.chainType,
|
|
36470
|
+
]);
|
|
36471
|
+
return {
|
|
36472
|
+
gasEnabled: enableGetGasOnDestination && getGasOnDestSupportedForThisRoute,
|
|
36473
|
+
getGasOnDestSupportedForThisRoute,
|
|
36474
|
+
};
|
|
36475
|
+
};
|
|
36476
|
+
|
|
36620
36477
|
const useAddToken = (chainToCompare, tokenToCompare) => {
|
|
36621
36478
|
const { chain: currentEvmChain } = wagmi.useAccount();
|
|
36622
36479
|
const { connector } = wagmi.useAccount();
|
|
@@ -37158,7 +37015,6 @@ exports.isChainflipBridgeTransaction = isChainflipBridgeTransaction;
|
|
|
37158
37015
|
exports.isCoralBridgeAction = isCoralBridgeAction;
|
|
37159
37016
|
exports.isCosmosAddressValid = isCosmosAddressValid;
|
|
37160
37017
|
exports.isDepositRoute = isDepositRoute;
|
|
37161
|
-
exports.isDepositWithSignatureTxData = isDepositWithSignatureTxData;
|
|
37162
37018
|
exports.isEmptyObject = isEmptyObject;
|
|
37163
37019
|
exports.isEvmChainNotSupportedError = isEvmChainNotSupportedError;
|
|
37164
37020
|
exports.isEvmosChain = isEvmosChain;
|
|
@@ -37269,7 +37125,6 @@ exports.useSendTransactionStore = useSendTransactionStore;
|
|
|
37269
37125
|
exports.useSigner = useSigner;
|
|
37270
37126
|
exports.useSingleTokenPrice = useSingleTokenPrice;
|
|
37271
37127
|
exports.useSolanaNativeBalance = useSolanaNativeBalance;
|
|
37272
|
-
exports.useSourceChainGasToken = useSourceChainGasToken;
|
|
37273
37128
|
exports.useSquid = useSquid;
|
|
37274
37129
|
exports.useSquidChains = useSquidChains;
|
|
37275
37130
|
exports.useSquidQueryClient = useSquidQueryClient;
|
|
@@ -37285,6 +37140,7 @@ exports.useSwapTransactionStatus = useSwapTransactionStatus;
|
|
|
37285
37140
|
exports.useTokensData = useTokensData;
|
|
37286
37141
|
exports.useTrackSearchEmpty = useTrackSearchEmpty;
|
|
37287
37142
|
exports.useTransactionStore = useTransactionStore;
|
|
37143
|
+
exports.useUserParams = useUserParams;
|
|
37288
37144
|
exports.useWallet = useWallet;
|
|
37289
37145
|
exports.useWalletStore = useWalletStore;
|
|
37290
37146
|
exports.useWallets = useWallets;
|
|
@@ -37293,4 +37149,4 @@ exports.useXrplTrustLine = useXrplTrustLine;
|
|
|
37293
37149
|
exports.waitForReceiptWithRetry = waitForReceiptWithRetry;
|
|
37294
37150
|
exports.walletIconBaseUrl = walletIconBaseUrl;
|
|
37295
37151
|
exports.walletSupportsChainType = walletSupportsChainType;
|
|
37296
|
-
//# sourceMappingURL=index-
|
|
37152
|
+
//# sourceMappingURL=index-5cyMUZhY.js.map
|