@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.
Files changed (38) hide show
  1. package/dist/core/constants.d.ts +0 -3
  2. package/dist/core/queries/queries-keys.d.ts +1 -1
  3. package/dist/core/types/config.d.ts +1 -0
  4. package/dist/hooks/index.d.ts +2 -2
  5. package/dist/hooks/transaction/send/useEstimateSendTransactionGas.d.ts +2 -6
  6. package/dist/hooks/transaction/useAllTransactionsStatus.d.ts +1 -0
  7. package/dist/hooks/transaction/useEstimate.d.ts +11 -5
  8. package/dist/hooks/user/useUserParams.d.ts +4 -0
  9. package/dist/{index-CaI-xWCW.js → index-5cyMUZhY.js} +1205 -1349
  10. package/dist/index-5cyMUZhY.js.map +1 -0
  11. package/dist/{index-B4aeecpP.js → index-BGVXRZI6.js} +1208 -1351
  12. package/dist/index-BGVXRZI6.js.map +1 -0
  13. package/dist/{index.es-BXf9jwuI.js → index.es-BfdAGErV.js} +3 -3
  14. package/dist/{index.es-BXf9jwuI.js.map → index.es-BfdAGErV.js.map} +1 -1
  15. package/dist/{index.es-DAfqL2H0.js → index.es-CeHwkxPw.js} +3 -3
  16. package/dist/{index.es-DAfqL2H0.js.map → index.es-CeHwkxPw.js.map} +1 -1
  17. package/dist/index.esm.js +2 -2
  18. package/dist/index.js +3 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/{secretService-CIYxEkTN.js → secretService-BMYOBXhv.js} +3 -3
  21. package/dist/{secretService-CIYxEkTN.js.map → secretService-BMYOBXhv.js.map} +1 -1
  22. package/dist/{secretService-Cld4gYfG.js → secretService-D_d3CFdp.js} +3 -3
  23. package/dist/{secretService-Cld4gYfG.js.map → secretService-D_d3CFdp.js.map} +1 -1
  24. package/dist/services/internal/assetsService.d.ts +2 -2
  25. package/dist/services/internal/estimateService.d.ts +27 -13
  26. package/dist/services/internal/transactionService.d.ts +0 -7
  27. package/dist/{stellarService.client-COeQeah_.js → stellarService.client-CIkvwxPo.js} +3 -3
  28. package/dist/{stellarService.client-COeQeah_.js.map → stellarService.client-CIkvwxPo.js.map} +1 -1
  29. package/dist/{stellarService.client-ocLzRIB4.js → stellarService.client-DOrCdvCd.js} +3 -3
  30. package/dist/{stellarService.client-ocLzRIB4.js.map → stellarService.client-DOrCdvCd.js.map} +1 -1
  31. package/package.json +3 -3
  32. package/dist/hooks/tokens/useSourceChainGasToken.d.ts +0 -11
  33. package/dist/hooks/transaction/useTempoFeeCheck.d.ts +0 -11
  34. package/dist/index-B4aeecpP.js.map +0 -1
  35. package/dist/index-CaI-xWCW.js.map +0 -1
  36. package/dist/services/internal/tempoService.d.ts +0 -82
  37. package/dist/tests/networkGasToken.test.d.ts +0 -1
  38. 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.nativeCurrency.symbol.toUpperCase() &&
22971
- t.chainId == chain.chainId);
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-Cld4gYfG.js'); });
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: _, tokens, }) {
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-COeQeah_.js'); });
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
- class StellarRpcClient {
27806
- server;
27807
- constructor(rpcUrl) {
27808
- this.server = new stellarSdk.rpc.Server(rpcUrl);
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
- * Returns the balance of a Stellar Contract Token. This is different from an Issued Token.
27812
- *
27813
- * With Contract Tokens, we need to call the .balance method on the token contract
27814
- * and simulate the transaction to get the balance.
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
- if ("result" in simulateTxResponse && simulateTxResponse.result != null) {
27842
- const native = stellarSdk.scValToNative(simulateTxResponse.result.retval);
27843
- return native.toString();
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
- throw new Error("Failed to fetch balance");
27846
- }
27847
- async getAllBalances(userAddress, tokens) {
27848
- const balancePromises = tokens.map((token) => {
27849
- return this.getBalance(userAddress, token.address, token.chainId);
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
- * Resolves when the transaction is confirmed, or fails after a timeout.
27867
- */
27868
- async waitForTransaction(txHash, { interval = 2_000, timeout = 40_000 } = {}) {
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
- async isAccountActivated(address) {
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
- catch (error) {
27892
- if (error?.message && error.message.includes("Account not found")) {
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 getAccount(address) {
27899
- return this.server.getAccount(address);
27900
- }
27901
- async sendTransaction(transaction) {
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
- return transactionResponse;
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 prepareTransaction(transaction) {
27909
- return this.server.prepareTransaction(transaction);
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
- class XrplRpcClient {
27914
- rpcUrl;
27915
- constructor(rpcUrl) {
27916
- this.rpcUrl = rpcUrl;
27917
- }
27918
- async getBalance(address, tokenAddress) {
27919
- if (tokenAddress.toLowerCase() === nativeXrplTokenAddress.toLowerCase()) {
27920
- return this.getNativeBalance(address);
27921
- }
27922
- return this.getIssuedCurrencyBalance(address, tokenAddress);
27923
- }
27924
- async getAllBalances(address) {
27925
- const [nativeBalance, trustLineBalances] = await Promise.all([
27926
- this.getNativeBalance(address),
27927
- this.getTrustLines(address),
27928
- ]);
27929
- return [
27930
- {
27931
- balance: nativeBalance,
27932
- address: nativeXrplTokenAddress,
27933
- },
27934
- ...trustLineBalances.lines.map((line) => ({
27935
- balance: line.balance,
27936
- address: `${line.currency}.${line.account}`,
27937
- })),
27938
- ];
27939
- }
27940
- async getTrustLines(address, issuer) {
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
- throw error;
27970
- }
27971
- }
27972
- /**
27973
- * Waits for a transaction to be validated and returns its final status.
27974
- * Resolves to 'success' or throws an error with the failed status.
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 response = await this.call("tx", [
27981
- {
27982
- transaction: txHash,
27983
- binary: false,
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
- const status = response.meta?.TransactionResult;
27995
- if (status === XrplTxStatus.SUCCESS) {
27996
- return status;
27922
+ if (!provider.session) {
27923
+ await provider.connect({
27924
+ ...("pairingTopic" in params
27925
+ ? { pairingTopic: params.pairingTopic }
27926
+ : {}),
27927
+ });
27997
27928
  }
27998
- else {
27999
- throw new Error(`Transaction failed with status: ${status}`);
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
- // txnNotFound = still pending or non-existent
28004
- if (error?.message?.includes("txnNotFound")) {
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
- async getServerState() {
28017
- return this.call("server_state", [{}]);
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
- async getAccountInfo(address) {
28020
- return this.call("account_info", [
28021
- {
28022
- account: address,
28023
- ledger_index: "validated",
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
- * Returns the balance of the user in the native XRP token
28029
- * formatted as a string
28030
- */
28031
- async getNativeBalance(address) {
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
- const data = await response.json();
28067
- if (!data.result) {
28068
- throw new Error(`Invalid response from RPC (${method})`);
28069
- }
28070
- if ("error" in data.result) {
28071
- throw new Error(`Error from RPC (${method}): ${data.result.error}`);
28072
- }
28073
- return data.result;
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
- const clientCache = new Map();
28078
- async function getClient(chain) {
28079
- const key = `${chain.chainType}:${chain.chainId}`;
28080
- if (clientCache.has(key)) {
28081
- return clientCache.get(key);
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
- async function createClient(chain) {
28088
- switch (chain.chainType) {
28089
- case squidTypes.ChainType.EVM:
28090
- return new ethers.JsonRpcProvider(chain.rpc);
28091
- case squidTypes.ChainType.COSMOS:
28092
- const rpcUrl = await getWorkingCosmosRpcUrl(chain);
28093
- return (await stargate.StargateClient.connect(rpcUrl));
28094
- case squidTypes.ChainType.SOLANA:
28095
- return new web3_js.Connection(SOLANA_RPC_URL);
28096
- case squidTypes.ChainType.BTC:
28097
- return null;
28098
- case squidTypes.ChainType.SUI:
28099
- return new client.SuiClient({
28100
- url: chain.rpc,
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
- case squidTypes.ChainType.XRPL:
28103
- return new XrplRpcClient(chain.rpc);
28104
- case squidTypes.ChainType.STELLAR:
28105
- return new StellarRpcClient(chain.rpc);
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
- // Taken from wagmi docs
29973
- // https://wagmi.sh/react/guides/ethers
29974
- function clientToSigner(client) {
29975
- const { account, chain, transport } = client;
29976
- if (!account || !chain || !transport) {
29977
- return undefined;
29978
- }
29979
- const network = {
29980
- chainId: chain.id,
29981
- name: chain.name,
29982
- ensAddress: chain.contracts?.ensRegistry?.address,
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
- const provider = new ethers.BrowserProvider(transport, network);
29985
- const signer = new ethers.JsonRpcSigner(provider, account.address);
29986
- return signer;
29987
- }
29988
-
29989
- function useEvmSigner({ chainId }) {
29990
- const { connector } = wagmi.useAccount();
29991
- const { data: client } = wagmi.useWalletClient({ chainId, connector });
29992
- const signer = React.useMemo(() => (client ? clientToSigner(client) : undefined), [client]);
29993
- return { signer };
29994
- }
29995
- const useSigner = ({ chain }) => {
29996
- const evmChainId = chain?.chainType === squidTypes.ChainType.EVM ? Number(chain.chainId) : undefined;
29997
- // EVM and Cosmos need a different signer for each chain
29998
- // This is not the case for Solana or Bitcoin as there's only one chain in those ecosystems
29999
- const { signer: evmSigner } = useEvmSigner({ chainId: evmChainId });
30000
- const { signer: cosmosSigner } = useCosmosSigner({ chain });
30001
- const { signer: solanaSigner } = useSolanaContext();
30002
- const { signer: bitcoinSigner } = useBitcoinContext();
30003
- const { signer: suiSigner } = useSuiContext();
30004
- const { signer: xrplSigner } = useXrplContext();
30005
- const { signer: stellarSigner } = useStellarContext();
30006
- const isEvmSignerReady = !!evmSigner;
30007
- const isSolanaSignerReady = !!solanaSigner;
30008
- const isCosmosSignerReady = !!cosmosSigner;
30009
- const isBitcoinSignerReady = !!bitcoinSigner;
30010
- const isSuiSignerReady = !!suiSigner;
30011
- const isXrplSignerReady = !!xrplSigner;
30012
- const isStellarSignerReady = !!stellarSigner;
30013
- const isSignerReady = React.useMemo(() => {
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 isEvmSignerReady;
30190
+ return isEvmLoading;
30019
30191
  case squidTypes.ChainType.COSMOS:
30020
- return isCosmosSignerReady;
30192
+ return isCosmosLoading;
30021
30193
  case squidTypes.ChainType.BTC:
30022
- return isBitcoinSignerReady;
30194
+ return isBitcoinLoading;
30023
30195
  case squidTypes.ChainType.SOLANA:
30024
- return isSolanaSignerReady;
30196
+ return isSolanaLoading;
30025
30197
  case squidTypes.ChainType.SUI:
30026
- return isSuiSignerReady;
30198
+ return isSuiLoading;
30027
30199
  case squidTypes.ChainType.XRPL:
30028
- return isXrplSignerReady;
30200
+ return isXrpLoading;
30029
30201
  case squidTypes.ChainType.STELLAR:
30030
- return isStellarSignerReady;
30202
+ return isStellarLoading;
30031
30203
  }
30032
30204
  }, [
30033
30205
  chain?.chainType,
30034
- isEvmSignerReady,
30035
- isCosmosSignerReady,
30036
- isBitcoinSignerReady,
30037
- isSolanaSignerReady,
30038
- isSuiSignerReady,
30039
- isXrplSignerReady,
30040
- isStellarSignerReady,
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
- class StellarApiClient {
30184
- apiUrl;
30185
- constructor(apiUrl) {
30186
- this.apiUrl = apiUrl;
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
- accountActivatedInfo,
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 isGasBalanceEnough = ({ fromAmount, networkFeesWei, chainFeeParams, }) => {
31075
- if (!chainFeeParams)
31076
- return false;
31077
- if (chainFeeParams.fromTokenPaysNetworkFee) {
31078
- return (BigInt(fromAmount ?? "0") + networkFeesWei <=
31079
- chainFeeParams.gasTokenBalanceWei);
31080
- }
31081
- return networkFeesWei <= chainFeeParams.gasTokenBalanceWei;
31082
- };
31083
- const calculateMinAmountValueWarnMsg = ({ networkFeesWei, chainFeeParams, }) => {
31084
- if (!chainFeeParams?.fromTokenPaysNetworkFee)
31085
- return undefined;
31086
- return chainFeeParams.gasTokenBalanceWei - networkFeesWei;
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, tempoFeeData, }) => {
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 isNativeTokenFromChain =
31156
- // TODO: nativeCurrency.symbol is not always uppercase
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
- networkFeesWei,
31002
+ fromTokenDecimals: fromToken?.decimals,
31003
+ totalFeesInNativeTokenPlusRatio,
31004
+ nativeTokenBalanceFromChainWei,
31170
31005
  });
31171
- const minAmount = calculateMinAmountValueWarnMsg({
31172
- chainFeeParams,
31173
- // gasTokenDecimals: chainFeeParams,
31174
- networkFeesWei,
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
- fromTokenPaysNetworkFee: chainFeeParams?.fromTokenPaysNetworkFee ?? false,
31037
+ isFromTokenNative,
31205
31038
  totalNativeFees,
31206
31039
  totalFeesInNativeTokenPlusRatio,
31207
- gasBalanceEnough,
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
- // Tempo has no native token, so `value` transfers are rejected by the EVM.
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
- const feeWei = gas * maxFeePerGas;
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 tempoFeeData = useTempoFeeCheck({ fromChain: chain, fromToken: token });
31344
- const chainFeeParams = resolveChainFeeParams({
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
- return isGasBalanceEnough({
31356
- fromAmount: parseToBigInt(amount, token.decimals).toString(),
31357
- networkFeesWei: estimatedGas,
31358
- chainFeeParams,
31359
- });
31360
- }, [amount, estimatedGas, token, chainFeeParams]);
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 minAmount = calculateMinAmountValueWarnMsg({
31373
- chainFeeParams,
31374
- // gasTokenDecimals: chainFeeParams.fromTokenPaysNetworkFee
31375
- // ? token.decimals
31376
- // : undefined,
31377
- networkFeesWei: estimatedGas,
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
- // return minAmount > BigInt(0)
31380
- // ? formatBNToReadable(minAmount, gasTokenDecimals)
31381
- // : "0";
31382
- if (!minAmount)
31383
- return undefined;
31384
- return formatBNToReadable(minAmount, chainFeeParams.fromTokenPaysNetworkFee ? token.decimals : undefined);
31385
- }, [estimatedGas, token, chainFeeParams]);
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
- isGasBalanceEnough: gasBalanceEnough,
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, fromPrice } = useSwap();
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
- tempoFeeData: tempoFeeData ?? undefined,
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 = Number(squidRoute?.estimate?.aggregateSlippage ?? 0).toFixed(2) + "%";
32561
- const fromBalanceEnoughToSwap = React.useMemo(() => {
32562
- const fromBalanceNum = Number(fromBalanceFormatted ?? 0);
32563
- const fromPriceNum = Number(fromPrice ?? 0);
32564
- return fromBalanceNum >= fromPriceNum;
32565
- }, [fromBalanceFormatted, fromPrice]);
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
- fromBalanceFormatted,
32394
+ balanceFormatted,
32569
32395
  slippageFormatted,
32570
32396
  totalWithRefundEstimate,
32571
- fromBalanceEnoughToSwap,
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-CaI-xWCW.js.map
37152
+ //# sourceMappingURL=index-5cyMUZhY.js.map