@0xsquid/react-hooks 8.4.1-beta-interactive-to-amount.0 → 8.4.1-beta-tempo.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/constants.d.ts +3 -0
- package/dist/core/queries/queries-keys.d.ts +1 -1
- package/dist/core/types/config.d.ts +0 -1
- package/dist/core/types/route.d.ts +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/store/useSquidStore.d.ts +1 -2
- package/dist/hooks/swap/useSwap.d.ts +2 -4
- package/dist/hooks/tokens/useSourceChainGasToken.d.ts +11 -0
- package/dist/hooks/transaction/send/useEstimateSendTransactionGas.d.ts +6 -2
- package/dist/hooks/transaction/useEstimate.d.ts +5 -11
- package/dist/hooks/transaction/useGetRoute.d.ts +0 -1
- package/dist/hooks/transaction/useTempoFeeCheck.d.ts +11 -0
- package/dist/{index-DnkELFs9.js → index-B4aeecpP.js} +1319 -1203
- package/dist/index-B4aeecpP.js.map +1 -0
- package/dist/{index-Bg7CS2Uo.js → index-CaI-xWCW.js} +1316 -1200
- package/dist/index-CaI-xWCW.js.map +1 -0
- package/dist/{index.es-tWag56u3.js → index.es-BXf9jwuI.js} +3 -3
- package/dist/{index.es-tWag56u3.js.map → index.es-BXf9jwuI.js.map} +1 -1
- package/dist/{index.es-BQGUcysL.js → index.es-DAfqL2H0.js} +3 -3
- package/dist/{index.es-BQGUcysL.js.map → index.es-DAfqL2H0.js.map} +1 -1
- package/dist/index.esm.js +2 -2
- package/dist/index.js +3 -3
- package/dist/{secretService-Bs5SPC6i.js → secretService-CIYxEkTN.js} +3 -3
- package/dist/{secretService-Bs5SPC6i.js.map → secretService-CIYxEkTN.js.map} +1 -1
- package/dist/{secretService-BXleHutG.js → secretService-Cld4gYfG.js} +3 -3
- package/dist/{secretService-BXleHutG.js.map → secretService-Cld4gYfG.js.map} +1 -1
- package/dist/services/internal/assetsService.d.ts +2 -2
- package/dist/services/internal/estimateService.d.ts +13 -27
- package/dist/services/internal/tempoService.d.ts +82 -0
- package/dist/{stellarService.client-li6iEHAu.js → stellarService.client-COeQeah_.js} +3 -3
- package/dist/{stellarService.client-li6iEHAu.js.map → stellarService.client-COeQeah_.js.map} +1 -1
- package/dist/{stellarService.client-TTYw1-aB.js → stellarService.client-ocLzRIB4.js} +3 -3
- package/dist/{stellarService.client-TTYw1-aB.js.map → stellarService.client-ocLzRIB4.js.map} +1 -1
- package/dist/tests/networkGasToken.test.d.ts +1 -0
- package/dist/tests/tempoService.test.d.ts +1 -0
- package/package.json +1 -1
- package/dist/hooks/user/useUserParams.d.ts +0 -4
- package/dist/index-Bg7CS2Uo.js.map +0 -1
- package/dist/index-DnkELFs9.js.map +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');
|
|
31
30
|
var stargate = require('@cosmjs/stargate');
|
|
32
31
|
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,6 +145,9 @@ const CHAIN_IDS = {
|
|
|
145
145
|
SONEIUM: "1868",
|
|
146
146
|
PEAQ: "3338",
|
|
147
147
|
HEDERA: "295",
|
|
148
|
+
MANTRA: "5888",
|
|
149
|
+
CITREA: "4114",
|
|
150
|
+
TEMPO: "4217",
|
|
148
151
|
// others
|
|
149
152
|
BITCOIN: "bitcoin",
|
|
150
153
|
SOLANA: "solana-mainnet-beta",
|
|
@@ -22306,17 +22309,15 @@ const keys = () => ({
|
|
|
22306
22309
|
// ============
|
|
22307
22310
|
// Transactions
|
|
22308
22311
|
// ============
|
|
22309
|
-
transaction: (fromChainId, toChainId, toTokenAddress, fromTokenAddress,
|
|
22312
|
+
transaction: (fromChainId, toChainId, toTokenAddress, fromTokenAddress, price, slippage, sourceUserAddress, degenMode, destinationAddress, fallbackAddress, quoteOnly, fromChainType, preHook, postHook, overrideGasRefundAddress) => [
|
|
22310
22313
|
...keys().transactions(),
|
|
22311
22314
|
exports.QueryKeys.Transaction,
|
|
22312
22315
|
fromChainId,
|
|
22313
22316
|
toChainId,
|
|
22314
22317
|
toTokenAddress,
|
|
22315
22318
|
fromTokenAddress,
|
|
22316
|
-
|
|
22317
|
-
toAmount,
|
|
22319
|
+
price,
|
|
22318
22320
|
slippage,
|
|
22319
|
-
getGasOnDestination,
|
|
22320
22321
|
sourceUserAddress,
|
|
22321
22322
|
degenMode,
|
|
22322
22323
|
destinationAddress,
|
|
@@ -22477,7 +22478,6 @@ const getConfigWithDefaults = (config) => {
|
|
|
22477
22478
|
integratorId: get$2(config, "integratorId", defaultConfigValues.integratorId),
|
|
22478
22479
|
slippage: get$2(config, "slippage", defaultConfigValues.slippage),
|
|
22479
22480
|
collectFees: get$2(config, "collectFees", defaultConfigValues.collectFees),
|
|
22480
|
-
enableGetGasOnDestination: get$2(config, "enableGetGasOnDestination", defaultConfigValues.enableGetGasOnDestination),
|
|
22481
22481
|
apiUrl: get$2(config, "apiUrl", defaultConfigValues.apiUrl),
|
|
22482
22482
|
priceImpactWarnings: get$2(config, "priceImpactWarnings", defaultConfigValues.priceImpactWarnings),
|
|
22483
22483
|
initialAssets: get$2(config, "initialAssets", defaultConfigValues.initialAssets),
|
|
@@ -22967,8 +22967,8 @@ const sortAllTokens = (tokenA, tokenB) => {
|
|
|
22967
22967
|
return 0;
|
|
22968
22968
|
};
|
|
22969
22969
|
const findToken = (tokens, chainId, address) => tokens.find((t) => t.chainId === chainId && t.address === address);
|
|
22970
|
-
const findNativeToken = (tokens, chain) => tokens.find((t) => t.symbol.toUpperCase() === chain
|
|
22971
|
-
t.chainId == chain
|
|
22970
|
+
const findNativeToken = (tokens, chain) => tokens.find((t) => t.symbol.toUpperCase() === chain.nativeCurrency.symbol.toUpperCase() &&
|
|
22971
|
+
t.chainId == chain.chainId);
|
|
22972
22972
|
const normalizeIbcAddress = (address) => {
|
|
22973
22973
|
if (!address.toLowerCase().startsWith("ibc/")) {
|
|
22974
22974
|
return address;
|
|
@@ -23179,7 +23179,7 @@ const filterViewableTokens = (tokens, config, direction) => {
|
|
|
23179
23179
|
};
|
|
23180
23180
|
const getSecretNetworkBalances = async (chainData, cosmosAddress, squidTokens, keplrTypeWallet) => {
|
|
23181
23181
|
const squidSecretTokens = squidTokens.filter((t) => t.chainId === CHAIN_IDS.SECRET);
|
|
23182
|
-
const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-
|
|
23182
|
+
const { fetchAllSecretBalances } = await Promise.resolve().then(function () { return require('./secretService-Cld4gYfG.js'); });
|
|
23183
23183
|
return fetchAllSecretBalances(chainData, cosmosAddress, squidSecretTokens, keplrTypeWallet);
|
|
23184
23184
|
};
|
|
23185
23185
|
function getTokenAssetsKey(token) {
|
|
@@ -24870,7 +24870,7 @@ class OnrampService {
|
|
|
24870
24870
|
});
|
|
24871
24871
|
return data;
|
|
24872
24872
|
}
|
|
24873
|
-
async getConfiguration({ chains, tokens, }) {
|
|
24873
|
+
async getConfiguration({ chains: _, tokens, }) {
|
|
24874
24874
|
const { data } = await axios.get(`${this.baseUrl}/config`);
|
|
24875
24875
|
// Filter supportedCryptos to only include tokens that match our provided tokens
|
|
24876
24876
|
const filteredCryptos = data.supportedCryptos.filter((supportedCrypto) => tokens.some((token) => token.address.toLowerCase() ===
|
|
@@ -25036,7 +25036,10 @@ const useConfigStore = zustand.create(() => ({
|
|
|
25036
25036
|
isInitialized: false,
|
|
25037
25037
|
}));
|
|
25038
25038
|
const useTransactionStore = zustand.create((set, get) => ({
|
|
25039
|
+
fromPrice: undefined,
|
|
25040
|
+
txLocalId: undefined,
|
|
25039
25041
|
transactions: {},
|
|
25042
|
+
currentTransaction: undefined,
|
|
25040
25043
|
setTransactionState(txId, tx) {
|
|
25041
25044
|
if (!txId)
|
|
25042
25045
|
return;
|
|
@@ -26492,7 +26495,7 @@ function useStellarWallets() {
|
|
|
26492
26495
|
try {
|
|
26493
26496
|
const { allowAllModules: initializeAllModules } = await import('@creit.tech/stellar-wallets-kit');
|
|
26494
26497
|
const { LedgerModule } = await import('@creit.tech/stellar-wallets-kit/modules/ledger.module');
|
|
26495
|
-
const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-
|
|
26498
|
+
const { formatStellarWallet } = await Promise.resolve().then(function () { return require('./stellarService.client-COeQeah_.js'); });
|
|
26496
26499
|
const modules = [...initializeAllModules(), new LedgerModule()];
|
|
26497
26500
|
const promises = modules.map(async (module) => {
|
|
26498
26501
|
const isAvailable = await module.isAvailable();
|
|
@@ -27391,8 +27394,7 @@ const useMultiChainWallet = (chain) => {
|
|
|
27391
27394
|
|
|
27392
27395
|
const useSwap = () => {
|
|
27393
27396
|
const { initialAssets, defaultTokensPerChain, disabledChains, availableChains, } = useConfigStore((state) => state.config);
|
|
27394
|
-
const
|
|
27395
|
-
const toAmount = useTransactionStore((state) => state.toAmount);
|
|
27397
|
+
const fromPrice = useTransactionStore((state) => state.fromPrice);
|
|
27396
27398
|
const { swapRoute } = useSwapRoutePersistStore();
|
|
27397
27399
|
const { tokens } = useSquidTokens();
|
|
27398
27400
|
const queryClient = reactQuery.useQueryClient();
|
|
@@ -27450,17 +27452,8 @@ const useSwap = () => {
|
|
|
27450
27452
|
}),
|
|
27451
27453
|
};
|
|
27452
27454
|
}, [destAddressData, destAddressEnsData.data, toChain?.chainType]);
|
|
27453
|
-
const
|
|
27454
|
-
useTransactionStore.setState({
|
|
27455
|
-
fromAmount: amount || undefined,
|
|
27456
|
-
toAmount: undefined,
|
|
27457
|
-
});
|
|
27458
|
-
}, []);
|
|
27459
|
-
const toAmountChanged = React.useCallback((amount) => {
|
|
27460
|
-
useTransactionStore.setState({
|
|
27461
|
-
toAmount: amount || undefined,
|
|
27462
|
-
fromAmount: undefined,
|
|
27463
|
-
});
|
|
27455
|
+
const fromPriceChanged = React.useCallback((price) => {
|
|
27456
|
+
useTransactionStore.setState({ fromPrice: price || undefined });
|
|
27464
27457
|
}, []);
|
|
27465
27458
|
/**
|
|
27466
27459
|
* When user changes something from the SwapView
|
|
@@ -27601,10 +27594,8 @@ const useSwap = () => {
|
|
|
27601
27594
|
tokenItems,
|
|
27602
27595
|
onSwapChange,
|
|
27603
27596
|
invertSwaps,
|
|
27604
|
-
|
|
27605
|
-
|
|
27606
|
-
fromAmountChanged,
|
|
27607
|
-
toAmountChanged,
|
|
27597
|
+
fromPrice,
|
|
27598
|
+
fromPriceChanged,
|
|
27608
27599
|
toToken,
|
|
27609
27600
|
fromToken,
|
|
27610
27601
|
fromChain,
|
|
@@ -27811,579 +27802,309 @@ function useTrackSearchEmpty({ searchQuery, resultsLength, context, debounceMs =
|
|
|
27811
27802
|
}, [context, debounceMs, resultsLength, searchQuery]);
|
|
27812
27803
|
}
|
|
27813
27804
|
|
|
27814
|
-
|
|
27815
|
-
|
|
27816
|
-
|
|
27817
|
-
|
|
27818
|
-
name: "associate",
|
|
27819
|
-
outputs: [
|
|
27820
|
-
{
|
|
27821
|
-
internalType: "uint256",
|
|
27822
|
-
name: "responseCode",
|
|
27823
|
-
type: "uint256"
|
|
27824
|
-
}
|
|
27825
|
-
],
|
|
27826
|
-
stateMutability: "nonpayable",
|
|
27827
|
-
type: "function"
|
|
27828
|
-
}
|
|
27829
|
-
];
|
|
27830
|
-
|
|
27831
|
-
/**
|
|
27832
|
-
* Client for interacting with the Hedera Mirrornode API.
|
|
27833
|
-
*
|
|
27834
|
-
* @docs https://mainnet.mirrornode.hedera.com/api/v1/docs
|
|
27835
|
-
*/
|
|
27836
|
-
class HederaApiClient {
|
|
27837
|
-
apiUrl;
|
|
27838
|
-
constructor(apiUrl) {
|
|
27839
|
-
this.apiUrl = apiUrl;
|
|
27840
|
-
}
|
|
27841
|
-
async isTokenAssociated({ address, token, }) {
|
|
27842
|
-
const accountInfo = await this.getAccountInfo(address);
|
|
27843
|
-
// Unlimited auto associations
|
|
27844
|
-
if (accountInfo.max_automatic_token_associations === -1) {
|
|
27845
|
-
return true;
|
|
27846
|
-
}
|
|
27847
|
-
// If there's no unlimited auto-associations, we need to check if the token is already associated.
|
|
27848
|
-
const { tokens: accountTokens } = await this.getAccountTokens(address);
|
|
27849
|
-
const tokenId = convertEvmAddressToHederaAccountId(token.address);
|
|
27850
|
-
if (accountTokens.some((t) => t.token_id === tokenId)) {
|
|
27851
|
-
// Token is already associated
|
|
27852
|
-
return true;
|
|
27853
|
-
}
|
|
27854
|
-
// Finally, if not auto-associated, check if there is an available auto-association slot
|
|
27855
|
-
const autoAssociatedTokens = accountTokens.filter((t) => t.automatic_association);
|
|
27856
|
-
const remainingAutoAssociations = accountInfo.max_automatic_token_associations -
|
|
27857
|
-
autoAssociatedTokens.length;
|
|
27858
|
-
return remainingAutoAssociations > 0;
|
|
27805
|
+
class StellarRpcClient {
|
|
27806
|
+
server;
|
|
27807
|
+
constructor(rpcUrl) {
|
|
27808
|
+
this.server = new stellarSdk.rpc.Server(rpcUrl);
|
|
27859
27809
|
}
|
|
27860
|
-
|
|
27861
|
-
|
|
27862
|
-
|
|
27863
|
-
|
|
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}`);
|
|
27864
27821
|
}
|
|
27865
|
-
|
|
27866
|
-
|
|
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}`);
|
|
27867
27840
|
}
|
|
27868
|
-
if (
|
|
27869
|
-
|
|
27841
|
+
if ("result" in simulateTxResponse && simulateTxResponse.result != null) {
|
|
27842
|
+
const native = stellarSdk.scValToNative(simulateTxResponse.result.retval);
|
|
27843
|
+
return native.toString();
|
|
27870
27844
|
}
|
|
27871
|
-
|
|
27845
|
+
throw new Error("Failed to fetch balance");
|
|
27872
27846
|
}
|
|
27873
|
-
async
|
|
27874
|
-
const
|
|
27875
|
-
|
|
27876
|
-
|
|
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
|
+
});
|
|
27864
|
+
}
|
|
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}`);
|
|
27877
27884
|
}
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27885
|
+
}
|
|
27886
|
+
async isAccountActivated(address) {
|
|
27887
|
+
try {
|
|
27888
|
+
await this.getAccount(address);
|
|
27889
|
+
return true;
|
|
27881
27890
|
}
|
|
27882
|
-
|
|
27883
|
-
|
|
27891
|
+
catch (error) {
|
|
27892
|
+
if (error?.message && error.message.includes("Account not found")) {
|
|
27893
|
+
return false;
|
|
27894
|
+
}
|
|
27895
|
+
throw error;
|
|
27884
27896
|
}
|
|
27885
|
-
return data;
|
|
27886
27897
|
}
|
|
27887
|
-
async
|
|
27888
|
-
|
|
27889
|
-
|
|
27890
|
-
|
|
27891
|
-
|
|
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");
|
|
27892
27905
|
}
|
|
27893
|
-
return
|
|
27906
|
+
return transactionResponse;
|
|
27907
|
+
}
|
|
27908
|
+
async prepareTransaction(transaction) {
|
|
27909
|
+
return this.server.prepareTransaction(transaction);
|
|
27894
27910
|
}
|
|
27895
27911
|
}
|
|
27896
27912
|
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
|
|
27904
|
-
|
|
27905
|
-
|
|
27906
|
-
|
|
27907
|
-
|
|
27908
|
-
|
|
27909
|
-
|
|
27910
|
-
|
|
27911
|
-
|
|
27912
|
-
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
|
|
27925
|
-
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
|
|
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 };
|
|
27929
27968
|
}
|
|
27930
|
-
|
|
27931
|
-
|
|
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) {
|
|
27932
27979
|
try {
|
|
27933
|
-
const
|
|
27934
|
-
|
|
27935
|
-
|
|
27936
|
-
|
|
27937
|
-
|
|
27938
|
-
|
|
27939
|
-
|
|
27940
|
-
|
|
27941
|
-
|
|
27942
|
-
|
|
27943
|
-
|
|
27944
|
-
|
|
27945
|
-
|
|
27946
|
-
}
|
|
27947
|
-
const accounts = (await provider.enable()).map((x) => viem.getAddress(x));
|
|
27948
|
-
const currentChainId = await this.getChainId();
|
|
27949
|
-
if (displayUri) {
|
|
27950
|
-
provider.removeListener("display_uri", displayUri);
|
|
27951
|
-
displayUri = undefined;
|
|
27952
|
-
}
|
|
27953
|
-
if (connect) {
|
|
27954
|
-
provider.removeListener("connect", connect);
|
|
27955
|
-
connect = undefined;
|
|
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;
|
|
27956
27993
|
}
|
|
27957
|
-
|
|
27958
|
-
|
|
27959
|
-
|
|
27994
|
+
const status = response.meta?.TransactionResult;
|
|
27995
|
+
if (status === XrplTxStatus.SUCCESS) {
|
|
27996
|
+
return status;
|
|
27960
27997
|
}
|
|
27961
|
-
|
|
27962
|
-
|
|
27963
|
-
provider.on("session_delete", sessionDelete);
|
|
27998
|
+
else {
|
|
27999
|
+
throw new Error(`Transaction failed with status: ${status}`);
|
|
27964
28000
|
}
|
|
27965
|
-
return { accounts, chainId: currentChainId };
|
|
27966
28001
|
}
|
|
27967
28002
|
catch (error) {
|
|
27968
|
-
|
|
27969
|
-
|
|
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;
|
|
27970
28011
|
}
|
|
27971
28012
|
throw error;
|
|
27972
28013
|
}
|
|
27973
|
-
}
|
|
27974
|
-
async disconnect() {
|
|
27975
|
-
const provider = await this.getProvider();
|
|
27976
|
-
try {
|
|
27977
|
-
await provider?.disconnect();
|
|
27978
|
-
}
|
|
27979
|
-
catch (error) {
|
|
27980
|
-
if (!/No matching key/i.test(error.message))
|
|
27981
|
-
throw error;
|
|
27982
|
-
}
|
|
27983
|
-
finally {
|
|
27984
|
-
if (disconnect) {
|
|
27985
|
-
provider?.removeListener("disconnect", disconnect);
|
|
27986
|
-
disconnect = undefined;
|
|
27987
|
-
}
|
|
27988
|
-
if (!connect) {
|
|
27989
|
-
connect = this.onConnect.bind(this);
|
|
27990
|
-
provider?.on("connect", connect);
|
|
27991
|
-
}
|
|
27992
|
-
if (sessionDelete) {
|
|
27993
|
-
provider?.removeListener("session_delete", sessionDelete);
|
|
27994
|
-
sessionDelete = undefined;
|
|
27995
|
-
}
|
|
27996
|
-
}
|
|
27997
|
-
},
|
|
27998
|
-
async getAccounts() {
|
|
27999
|
-
const provider = await this.getProvider();
|
|
28000
|
-
return provider.accounts.map((x) => viem.getAddress(x));
|
|
28001
|
-
},
|
|
28002
|
-
async getProvider() {
|
|
28003
|
-
async function initProvider() {
|
|
28004
|
-
const optionalChains = config.chains.map((x) => x.id);
|
|
28005
|
-
if (!optionalChains.length)
|
|
28006
|
-
return;
|
|
28007
|
-
const { EthereumProvider } = await Promise.resolve().then(function () { return require('./index.es-BQGUcysL.js'); });
|
|
28008
|
-
const rawProvider = await EthereumProvider.init({
|
|
28009
|
-
...restParameters,
|
|
28010
|
-
disableProviderPing: true,
|
|
28011
|
-
optionalChains,
|
|
28012
|
-
projectId: restParameters.projectId,
|
|
28013
|
-
rpcMap: Object.fromEntries(config.chains.map((chain) => {
|
|
28014
|
-
const [url] = extractRpcUrls({
|
|
28015
|
-
chain,
|
|
28016
|
-
transports: config.transports,
|
|
28017
|
-
});
|
|
28018
|
-
return [chain.id, url];
|
|
28019
|
-
})),
|
|
28020
|
-
showQrModal: false,
|
|
28021
|
-
// We need to specify a custom storage prefix to avoid conflicts with Wagmi's walletConnect instance
|
|
28022
|
-
// https://docs.reown.com/walletkit/web/usage#core-instance-sharing
|
|
28023
|
-
customStoragePrefix: "squid-hedera",
|
|
28024
|
-
});
|
|
28025
|
-
const proxiedProvider = new Proxy(rawProvider, {
|
|
28026
|
-
get(target, prop, receiver) {
|
|
28027
|
-
if (prop === "request") {
|
|
28028
|
-
return async (args) => {
|
|
28029
|
-
const signingMethods = [
|
|
28030
|
-
"eth_sendTransaction",
|
|
28031
|
-
"eth_signTransaction",
|
|
28032
|
-
];
|
|
28033
|
-
if (signingMethods.includes(args.method)) {
|
|
28034
|
-
try {
|
|
28035
|
-
HederaExtensionHelper.extensionOpen(extension.id);
|
|
28036
|
-
}
|
|
28037
|
-
catch { }
|
|
28038
|
-
}
|
|
28039
|
-
// forward request to original provider
|
|
28040
|
-
return target.request(args);
|
|
28041
|
-
};
|
|
28042
|
-
}
|
|
28043
|
-
// forward all other properties/methods
|
|
28044
|
-
return Reflect.get(target, prop, receiver);
|
|
28045
|
-
},
|
|
28046
|
-
});
|
|
28047
|
-
return proxiedProvider;
|
|
28048
|
-
}
|
|
28049
|
-
if (!provider_) {
|
|
28050
|
-
if (!providerPromise)
|
|
28051
|
-
providerPromise = initProvider();
|
|
28052
|
-
provider_ = await providerPromise;
|
|
28053
|
-
provider_?.events.setMaxListeners(Number.POSITIVE_INFINITY);
|
|
28054
|
-
}
|
|
28055
|
-
return provider_;
|
|
28056
|
-
},
|
|
28057
|
-
async getChainId() {
|
|
28058
|
-
const provider = await this.getProvider();
|
|
28059
|
-
return provider.chainId;
|
|
28060
|
-
},
|
|
28061
|
-
async isAuthorized() {
|
|
28062
|
-
try {
|
|
28063
|
-
const accounts = await this.getAccounts();
|
|
28064
|
-
return accounts.length > 0;
|
|
28065
|
-
}
|
|
28066
|
-
catch {
|
|
28067
|
-
return false;
|
|
28068
|
-
}
|
|
28069
|
-
},
|
|
28070
|
-
onAccountsChanged(accounts) {
|
|
28071
|
-
if (accounts.length === 0)
|
|
28072
|
-
this.onDisconnect();
|
|
28073
|
-
else
|
|
28074
|
-
config.emitter.emit("change", {
|
|
28075
|
-
accounts: accounts.map((x) => viem.getAddress(x)),
|
|
28076
|
-
});
|
|
28077
|
-
},
|
|
28078
|
-
onChainChanged(chain) {
|
|
28079
|
-
const chainId = Number(chain);
|
|
28080
|
-
config.emitter.emit("change", { chainId });
|
|
28081
|
-
},
|
|
28082
|
-
async onConnect(connectInfo) {
|
|
28083
|
-
const chainId = Number(connectInfo.chainId);
|
|
28084
|
-
const accounts = await this.getAccounts();
|
|
28085
|
-
config.emitter.emit("connect", { accounts, chainId });
|
|
28086
|
-
},
|
|
28087
|
-
async onDisconnect() {
|
|
28088
|
-
config.emitter.emit("disconnect");
|
|
28089
|
-
const provider = await this.getProvider();
|
|
28090
|
-
if (disconnect) {
|
|
28091
|
-
provider.removeListener("disconnect", disconnect);
|
|
28092
|
-
disconnect = undefined;
|
|
28093
|
-
}
|
|
28094
|
-
if (sessionDelete) {
|
|
28095
|
-
provider.removeListener("session_delete", sessionDelete);
|
|
28096
|
-
sessionDelete = undefined;
|
|
28097
|
-
}
|
|
28098
|
-
if (!connect) {
|
|
28099
|
-
connect = this.onConnect.bind(this);
|
|
28100
|
-
provider.on("connect", connect);
|
|
28101
|
-
}
|
|
28102
|
-
},
|
|
28103
|
-
onDisplayUri(uri) {
|
|
28104
|
-
config.emitter.emit("message", { type: "display_uri", data: uri });
|
|
28105
|
-
HederaExtensionHelper.extensionConnect(extension.id, uri);
|
|
28106
|
-
},
|
|
28107
|
-
onSessionDelete() {
|
|
28108
|
-
this.onDisconnect();
|
|
28109
|
-
},
|
|
28110
|
-
}));
|
|
28111
|
-
}
|
|
28112
|
-
|
|
28113
|
-
const createWagmiConfig = (squidChains, hederaExtensions) => {
|
|
28114
|
-
const filteredEvmChains = squidChains.filter((chain) => chain.chainType === squidTypes.ChainType.EVM);
|
|
28115
|
-
if (filteredEvmChains.length === 0) {
|
|
28116
|
-
throw new Error("At least one chain is required");
|
|
28014
|
+
}
|
|
28117
28015
|
}
|
|
28118
|
-
|
|
28119
|
-
return
|
|
28120
|
-
|
|
28121
|
-
|
|
28122
|
-
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
|
|
28126
|
-
},
|
|
28127
|
-
rpcUrls: {
|
|
28128
|
-
public: {
|
|
28129
|
-
http: [chain.rpc],
|
|
28130
|
-
},
|
|
28131
|
-
default: {
|
|
28132
|
-
http: [chain.rpc],
|
|
28133
|
-
},
|
|
28016
|
+
async getServerState() {
|
|
28017
|
+
return this.call("server_state", [{}]);
|
|
28018
|
+
}
|
|
28019
|
+
async getAccountInfo(address) {
|
|
28020
|
+
return this.call("account_info", [
|
|
28021
|
+
{
|
|
28022
|
+
account: address,
|
|
28023
|
+
ledger_index: "validated",
|
|
28134
28024
|
},
|
|
28135
|
-
|
|
28136
|
-
});
|
|
28137
|
-
const wcMetadata = {
|
|
28138
|
-
url: SQUID_METADATA.url,
|
|
28139
|
-
name: SQUID_METADATA.name,
|
|
28140
|
-
icons: [SQUID_METADATA.icon],
|
|
28141
|
-
description: SQUID_METADATA.description,
|
|
28142
|
-
};
|
|
28143
|
-
return wagmi.createConfig({
|
|
28144
|
-
chains: wagmiChains,
|
|
28145
|
-
transports: Object.fromEntries(wagmiChains.map((chain) => [
|
|
28146
|
-
chain.id,
|
|
28147
|
-
wagmi.http(chain.rpcUrls.public.http[0] ?? ""),
|
|
28148
|
-
])),
|
|
28149
|
-
connectors: [
|
|
28150
|
-
connectors.injected(),
|
|
28151
|
-
connectors.safe({
|
|
28152
|
-
allowedDomains: [/app.safe.global$/],
|
|
28153
|
-
}),
|
|
28154
|
-
connectors.metaMask({
|
|
28155
|
-
dappMetadata: {
|
|
28156
|
-
name: SQUID_METADATA.name,
|
|
28157
|
-
url: SQUID_METADATA.url,
|
|
28158
|
-
iconUrl: SQUID_METADATA.icon,
|
|
28159
|
-
},
|
|
28160
|
-
}),
|
|
28161
|
-
connectors.coinbaseWallet({
|
|
28162
|
-
appName: SQUID_METADATA.name,
|
|
28163
|
-
appLogoUrl: SQUID_METADATA.icon,
|
|
28164
|
-
}),
|
|
28165
|
-
connectors.walletConnect({
|
|
28166
|
-
projectId: WALLETCONNECT_PROJECT_ID,
|
|
28167
|
-
metadata: wcMetadata,
|
|
28168
|
-
}),
|
|
28169
|
-
...hederaExtensions.map((extension) => hederaWalletConnect({
|
|
28170
|
-
projectId: WALLETCONNECT_PROJECT_ID,
|
|
28171
|
-
metadata: wcMetadata,
|
|
28172
|
-
extension,
|
|
28173
|
-
})),
|
|
28174
|
-
],
|
|
28175
|
-
});
|
|
28176
|
-
};
|
|
28177
|
-
// Taken from wagmi docs
|
|
28178
|
-
// https://wagmi.sh/react/guides/ethers
|
|
28179
|
-
function clientToSigner(client) {
|
|
28180
|
-
const { account, chain, transport } = client;
|
|
28181
|
-
if (!account || !chain || !transport) {
|
|
28182
|
-
return undefined;
|
|
28025
|
+
]);
|
|
28183
28026
|
}
|
|
28184
|
-
const network = {
|
|
28185
|
-
chainId: chain.id,
|
|
28186
|
-
name: chain.name,
|
|
28187
|
-
ensAddress: chain.contracts?.ensRegistry?.address,
|
|
28188
|
-
};
|
|
28189
|
-
const provider = new ethers.BrowserProvider(transport, network);
|
|
28190
|
-
const signer = new ethers.JsonRpcSigner(provider, account.address);
|
|
28191
|
-
return signer;
|
|
28192
|
-
}
|
|
28193
|
-
|
|
28194
|
-
function useEvmSigner({ chainId }) {
|
|
28195
|
-
const { connector } = wagmi.useAccount();
|
|
28196
|
-
const { data: client } = wagmi.useWalletClient({ chainId, connector });
|
|
28197
|
-
const signer = React.useMemo(() => (client ? clientToSigner(client) : undefined), [client]);
|
|
28198
|
-
return { signer };
|
|
28199
|
-
}
|
|
28200
|
-
const useSigner = ({ chain }) => {
|
|
28201
|
-
const evmChainId = chain?.chainType === squidTypes.ChainType.EVM ? Number(chain.chainId) : undefined;
|
|
28202
|
-
// EVM and Cosmos need a different signer for each chain
|
|
28203
|
-
// This is not the case for Solana or Bitcoin as there's only one chain in those ecosystems
|
|
28204
|
-
const { signer: evmSigner } = useEvmSigner({ chainId: evmChainId });
|
|
28205
|
-
const { signer: cosmosSigner } = useCosmosSigner({ chain });
|
|
28206
|
-
const { signer: solanaSigner } = useSolanaContext();
|
|
28207
|
-
const { signer: bitcoinSigner } = useBitcoinContext();
|
|
28208
|
-
const { signer: suiSigner } = useSuiContext();
|
|
28209
|
-
const { signer: xrplSigner } = useXrplContext();
|
|
28210
|
-
const { signer: stellarSigner } = useStellarContext();
|
|
28211
|
-
const isEvmSignerReady = !!evmSigner;
|
|
28212
|
-
const isSolanaSignerReady = !!solanaSigner;
|
|
28213
|
-
const isCosmosSignerReady = !!cosmosSigner;
|
|
28214
|
-
const isBitcoinSignerReady = !!bitcoinSigner;
|
|
28215
|
-
const isSuiSignerReady = !!suiSigner;
|
|
28216
|
-
const isXrplSignerReady = !!xrplSigner;
|
|
28217
|
-
const isStellarSignerReady = !!stellarSigner;
|
|
28218
|
-
const isSignerReady = React.useMemo(() => {
|
|
28219
|
-
if (!chain?.chainType)
|
|
28220
|
-
return false;
|
|
28221
|
-
switch (chain.chainType) {
|
|
28222
|
-
case squidTypes.ChainType.EVM:
|
|
28223
|
-
return isEvmSignerReady;
|
|
28224
|
-
case squidTypes.ChainType.COSMOS:
|
|
28225
|
-
return isCosmosSignerReady;
|
|
28226
|
-
case squidTypes.ChainType.BTC:
|
|
28227
|
-
return isBitcoinSignerReady;
|
|
28228
|
-
case squidTypes.ChainType.SOLANA:
|
|
28229
|
-
return isSolanaSignerReady;
|
|
28230
|
-
case squidTypes.ChainType.SUI:
|
|
28231
|
-
return isSuiSignerReady;
|
|
28232
|
-
case squidTypes.ChainType.XRPL:
|
|
28233
|
-
return isXrplSignerReady;
|
|
28234
|
-
case squidTypes.ChainType.STELLAR:
|
|
28235
|
-
return isStellarSignerReady;
|
|
28236
|
-
}
|
|
28237
|
-
}, [
|
|
28238
|
-
chain?.chainType,
|
|
28239
|
-
isEvmSignerReady,
|
|
28240
|
-
isCosmosSignerReady,
|
|
28241
|
-
isBitcoinSignerReady,
|
|
28242
|
-
isSolanaSignerReady,
|
|
28243
|
-
isSuiSignerReady,
|
|
28244
|
-
isXrplSignerReady,
|
|
28245
|
-
isStellarSignerReady,
|
|
28246
|
-
]);
|
|
28247
|
-
return {
|
|
28248
|
-
isSignerReady,
|
|
28249
|
-
evmSigner,
|
|
28250
|
-
cosmosSigner,
|
|
28251
|
-
bitcoinSigner,
|
|
28252
|
-
solanaSigner,
|
|
28253
|
-
suiSigner,
|
|
28254
|
-
xrplSigner,
|
|
28255
|
-
stellarSigner,
|
|
28256
|
-
};
|
|
28257
|
-
};
|
|
28258
|
-
|
|
28259
|
-
function useHederaTokenAssociations({ address, chain, token }) {
|
|
28260
|
-
const publicClient = wagmi.usePublicClient({
|
|
28261
|
-
chainId: Number(CHAIN_IDS.HEDERA),
|
|
28262
|
-
});
|
|
28263
|
-
const { evmSigner } = useSigner({ chain });
|
|
28264
|
-
const queryClient = reactQuery.useQueryClient();
|
|
28265
28027
|
/**
|
|
28266
|
-
*
|
|
28028
|
+
* Returns the balance of the user in the native XRP token
|
|
28029
|
+
* formatted as a string
|
|
28267
28030
|
*/
|
|
28268
|
-
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
28272
|
-
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
|
|
28277
|
-
|
|
28278
|
-
|
|
28279
|
-
|
|
28280
|
-
|
|
28281
|
-
}
|
|
28282
|
-
const userAddress = parseEvmAddress(address);
|
|
28283
|
-
if (!userAddress) {
|
|
28284
|
-
throw new Error("Invalid user address");
|
|
28285
|
-
}
|
|
28286
|
-
const encodedData = viem.encodeFunctionData({
|
|
28287
|
-
abi: hrc20,
|
|
28288
|
-
functionName: "associate",
|
|
28289
|
-
args: [],
|
|
28290
|
-
});
|
|
28291
|
-
const txRes = await evmSigner.sendTransaction({
|
|
28292
|
-
data: encodedData,
|
|
28293
|
-
to: tokenAddress,
|
|
28294
|
-
chainId: chain.chainId,
|
|
28295
|
-
});
|
|
28296
|
-
const receipt = await txRes.wait();
|
|
28297
|
-
if (receipt?.status !== 1) {
|
|
28298
|
-
throw new Error(`Transaction failed with status: ${receipt?.status}`);
|
|
28299
|
-
}
|
|
28300
|
-
return true;
|
|
28301
|
-
}
|
|
28302
|
-
catch (error) {
|
|
28303
|
-
console.error("Error associating Hedera token:", error);
|
|
28304
|
-
return false;
|
|
28305
|
-
}
|
|
28306
|
-
},
|
|
28307
|
-
async onSuccess() {
|
|
28308
|
-
queryClient.refetchQueries({
|
|
28309
|
-
queryKey: getPrefixKey(exports.QueryKeys.IsHederaTokenAssociated),
|
|
28310
|
-
});
|
|
28311
|
-
},
|
|
28312
|
-
});
|
|
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
|
+
}
|
|
28313
28044
|
/**
|
|
28314
|
-
*
|
|
28315
|
-
*
|
|
28316
|
-
* Hedera requires accounts to associate a token before being able to receive it.
|
|
28317
|
-
*
|
|
28318
|
-
* Accounts which have max. associations set to -1 can receive any token without previous association
|
|
28045
|
+
* Returns the balance of the user in the given issued currency (e.g. RLUSD)
|
|
28046
|
+
* formatted as a string
|
|
28319
28047
|
*/
|
|
28320
|
-
|
|
28321
|
-
|
|
28322
|
-
|
|
28323
|
-
|
|
28324
|
-
|
|
28325
|
-
|
|
28326
|
-
|
|
28327
|
-
|
|
28328
|
-
|
|
28329
|
-
|
|
28330
|
-
|
|
28331
|
-
|
|
28332
|
-
|
|
28333
|
-
|
|
28334
|
-
|
|
28335
|
-
|
|
28336
|
-
|
|
28337
|
-
|
|
28338
|
-
|
|
28339
|
-
|
|
28340
|
-
|
|
28341
|
-
token,
|
|
28342
|
-
});
|
|
28343
|
-
},
|
|
28344
|
-
enabled: !!address && !!publicClient && token?.chainId === CHAIN_IDS.HEDERA,
|
|
28345
|
-
});
|
|
28346
|
-
return {
|
|
28347
|
-
isTokenAssociated,
|
|
28348
|
-
associateToken,
|
|
28349
|
-
};
|
|
28350
|
-
}
|
|
28351
|
-
|
|
28352
|
-
const useKeyboardNavigation = ({ onEscape }) => {
|
|
28353
|
-
const onKeyDown = React.useCallback((event) => {
|
|
28354
|
-
if (event.key === "Escape") {
|
|
28355
|
-
onEscape?.();
|
|
28356
|
-
return;
|
|
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",
|
|
28058
|
+
},
|
|
28059
|
+
body: JSON.stringify({
|
|
28060
|
+
jsonrpc: "2.0",
|
|
28061
|
+
id: 1,
|
|
28062
|
+
method,
|
|
28063
|
+
params,
|
|
28064
|
+
}),
|
|
28065
|
+
});
|
|
28066
|
+
const data = await response.json();
|
|
28067
|
+
if (!data.result) {
|
|
28068
|
+
throw new Error(`Invalid response from RPC (${method})`);
|
|
28357
28069
|
}
|
|
28358
|
-
|
|
28359
|
-
|
|
28360
|
-
|
|
28361
|
-
return
|
|
28362
|
-
|
|
28363
|
-
|
|
28364
|
-
}, [onKeyDown]);
|
|
28365
|
-
};
|
|
28070
|
+
if ("error" in data.result) {
|
|
28071
|
+
throw new Error(`Error from RPC (${method}): ${data.result.error}`);
|
|
28072
|
+
}
|
|
28073
|
+
return data.result;
|
|
28074
|
+
}
|
|
28075
|
+
}
|
|
28366
28076
|
|
|
28367
|
-
const
|
|
28368
|
-
|
|
28369
|
-
const
|
|
28370
|
-
|
|
28371
|
-
|
|
28372
|
-
}
|
|
28373
|
-
const
|
|
28374
|
-
|
|
28375
|
-
|
|
28376
|
-
|
|
28377
|
-
|
|
28378
|
-
|
|
28379
|
-
|
|
28380
|
-
|
|
28381
|
-
|
|
28382
|
-
|
|
28383
|
-
|
|
28384
|
-
|
|
28385
|
-
|
|
28386
|
-
|
|
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;
|
|
28086
|
+
}
|
|
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,
|
|
28101
|
+
});
|
|
28102
|
+
case squidTypes.ChainType.XRPL:
|
|
28103
|
+
return new XrplRpcClient(chain.rpc);
|
|
28104
|
+
case squidTypes.ChainType.STELLAR:
|
|
28105
|
+
return new StellarRpcClient(chain.rpc);
|
|
28106
|
+
}
|
|
28107
|
+
}
|
|
28387
28108
|
|
|
28388
28109
|
/**
|
|
28389
28110
|
* The default multicall3 address
|
|
@@ -29399,372 +29120,6 @@ function timeout(ms, promise) {
|
|
|
29399
29120
|
return Promise.race([promise, timeoutPromise]);
|
|
29400
29121
|
}
|
|
29401
29122
|
|
|
29402
|
-
class StellarRpcClient {
|
|
29403
|
-
server;
|
|
29404
|
-
constructor(rpcUrl) {
|
|
29405
|
-
this.server = new stellarSdk.rpc.Server(rpcUrl);
|
|
29406
|
-
}
|
|
29407
|
-
/**
|
|
29408
|
-
* Returns the balance of a Stellar Contract Token. This is different from an Issued Token.
|
|
29409
|
-
*
|
|
29410
|
-
* With Contract Tokens, we need to call the .balance method on the token contract
|
|
29411
|
-
* and simulate the transaction to get the balance.
|
|
29412
|
-
*/
|
|
29413
|
-
async getBalance(userAddress, tokenAddress, chainId) {
|
|
29414
|
-
const account = await this.server.getAccount(userAddress);
|
|
29415
|
-
const network = getStellarNetwork(chainId);
|
|
29416
|
-
if (network == null) {
|
|
29417
|
-
throw new Error(`No Stellar network found for chainId ${chainId}`);
|
|
29418
|
-
}
|
|
29419
|
-
const txBuilder = new stellarSdk.TransactionBuilder(account, {
|
|
29420
|
-
fee: (BigInt(stellarSdk.BASE_FEE) * BigInt(2)).toString(),
|
|
29421
|
-
networkPassphrase: network,
|
|
29422
|
-
});
|
|
29423
|
-
const contract = new stellarSdk.Contract(tokenAddress);
|
|
29424
|
-
const tx = txBuilder
|
|
29425
|
-
.addOperation(contract.call("balance", new stellarSdk.Address(userAddress).toScVal()))
|
|
29426
|
-
.setTimeout(stellarSdk.TimeoutInfinite)
|
|
29427
|
-
.build();
|
|
29428
|
-
const simulateTxResponse = await this.server.simulateTransaction(tx);
|
|
29429
|
-
if ("error" in simulateTxResponse) {
|
|
29430
|
-
const isNoBalanceError = simulateTxResponse.error.includes("trying to get non-existing value for contract instance");
|
|
29431
|
-
// If the error message indicates that the user has no balance just return 0
|
|
29432
|
-
// We don't want to spam with this error as it's pretty common
|
|
29433
|
-
if (isNoBalanceError) {
|
|
29434
|
-
return "0";
|
|
29435
|
-
}
|
|
29436
|
-
throw new Error(`Failed to fetch balance. RPC response: ${simulateTxResponse.error}`);
|
|
29437
|
-
}
|
|
29438
|
-
if ("result" in simulateTxResponse && simulateTxResponse.result != null) {
|
|
29439
|
-
const native = stellarSdk.scValToNative(simulateTxResponse.result.retval);
|
|
29440
|
-
return native.toString();
|
|
29441
|
-
}
|
|
29442
|
-
throw new Error("Failed to fetch balance");
|
|
29443
|
-
}
|
|
29444
|
-
async getAllBalances(userAddress, tokens) {
|
|
29445
|
-
const balancePromises = tokens.map((token) => {
|
|
29446
|
-
return this.getBalance(userAddress, token.address, token.chainId);
|
|
29447
|
-
});
|
|
29448
|
-
const results = await Promise.allSettled(balancePromises);
|
|
29449
|
-
const balances = results.map((result) => {
|
|
29450
|
-
if (result.status === "fulfilled") {
|
|
29451
|
-
return result.value;
|
|
29452
|
-
}
|
|
29453
|
-
return "0";
|
|
29454
|
-
});
|
|
29455
|
-
return balances.map((balance, index) => {
|
|
29456
|
-
return {
|
|
29457
|
-
...tokens[index],
|
|
29458
|
-
balance,
|
|
29459
|
-
};
|
|
29460
|
-
});
|
|
29461
|
-
}
|
|
29462
|
-
/**
|
|
29463
|
-
* Resolves when the transaction is confirmed, or fails after a timeout.
|
|
29464
|
-
*/
|
|
29465
|
-
async waitForTransaction(txHash, { interval = 2_000, timeout = 40_000 } = {}) {
|
|
29466
|
-
const startTime = Date.now();
|
|
29467
|
-
while (true) {
|
|
29468
|
-
const result = await this.server.getTransaction(txHash);
|
|
29469
|
-
if (result.status === stellarSdk.rpc.Api.GetTransactionStatus.NOT_FOUND) {
|
|
29470
|
-
if (Date.now() - startTime > timeout) {
|
|
29471
|
-
throw new Error(`Transaction ${txHash} not found within timeout`);
|
|
29472
|
-
}
|
|
29473
|
-
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
29474
|
-
await new Promise((res) => setTimeout(res, interval));
|
|
29475
|
-
continue;
|
|
29476
|
-
}
|
|
29477
|
-
if (result.status === stellarSdk.rpc.Api.GetTransactionStatus.SUCCESS) {
|
|
29478
|
-
return result.status;
|
|
29479
|
-
}
|
|
29480
|
-
throw new Error(`Transaction failed with status: ${result.status}`);
|
|
29481
|
-
}
|
|
29482
|
-
}
|
|
29483
|
-
async isAccountActivated(address) {
|
|
29484
|
-
try {
|
|
29485
|
-
await this.getAccount(address);
|
|
29486
|
-
return true;
|
|
29487
|
-
}
|
|
29488
|
-
catch (error) {
|
|
29489
|
-
if (error?.message && error.message.includes("Account not found")) {
|
|
29490
|
-
return false;
|
|
29491
|
-
}
|
|
29492
|
-
throw error;
|
|
29493
|
-
}
|
|
29494
|
-
}
|
|
29495
|
-
async getAccount(address) {
|
|
29496
|
-
return this.server.getAccount(address);
|
|
29497
|
-
}
|
|
29498
|
-
async sendTransaction(transaction) {
|
|
29499
|
-
const transactionResponse = await this.server.sendTransaction(transaction);
|
|
29500
|
-
if (transactionResponse.status === "ERROR") {
|
|
29501
|
-
throw new Error("Error sending transaction");
|
|
29502
|
-
}
|
|
29503
|
-
return transactionResponse;
|
|
29504
|
-
}
|
|
29505
|
-
async prepareTransaction(transaction) {
|
|
29506
|
-
return this.server.prepareTransaction(transaction);
|
|
29507
|
-
}
|
|
29508
|
-
}
|
|
29509
|
-
|
|
29510
|
-
class XrplRpcClient {
|
|
29511
|
-
rpcUrl;
|
|
29512
|
-
constructor(rpcUrl) {
|
|
29513
|
-
this.rpcUrl = rpcUrl;
|
|
29514
|
-
}
|
|
29515
|
-
async getBalance(address, tokenAddress) {
|
|
29516
|
-
if (tokenAddress.toLowerCase() === nativeXrplTokenAddress.toLowerCase()) {
|
|
29517
|
-
return this.getNativeBalance(address);
|
|
29518
|
-
}
|
|
29519
|
-
return this.getIssuedCurrencyBalance(address, tokenAddress);
|
|
29520
|
-
}
|
|
29521
|
-
async getAllBalances(address) {
|
|
29522
|
-
const [nativeBalance, trustLineBalances] = await Promise.all([
|
|
29523
|
-
this.getNativeBalance(address),
|
|
29524
|
-
this.getTrustLines(address),
|
|
29525
|
-
]);
|
|
29526
|
-
return [
|
|
29527
|
-
{
|
|
29528
|
-
balance: nativeBalance,
|
|
29529
|
-
address: nativeXrplTokenAddress,
|
|
29530
|
-
},
|
|
29531
|
-
...trustLineBalances.lines.map((line) => ({
|
|
29532
|
-
balance: line.balance,
|
|
29533
|
-
address: `${line.currency}.${line.account}`,
|
|
29534
|
-
})),
|
|
29535
|
-
];
|
|
29536
|
-
}
|
|
29537
|
-
async getTrustLines(address, issuer) {
|
|
29538
|
-
return this.call("account_lines", [
|
|
29539
|
-
{
|
|
29540
|
-
account: address,
|
|
29541
|
-
ledger_index: "validated",
|
|
29542
|
-
peer: issuer,
|
|
29543
|
-
},
|
|
29544
|
-
]);
|
|
29545
|
-
}
|
|
29546
|
-
async getTrustLine(address, issuer, currency) {
|
|
29547
|
-
const response = await this.getTrustLines(address, issuer);
|
|
29548
|
-
const trustLine = response.lines.find((line) => line.currency === currency);
|
|
29549
|
-
return trustLine ?? null;
|
|
29550
|
-
}
|
|
29551
|
-
async accountActivatedInfo(address) {
|
|
29552
|
-
const serverState = await this.getServerState();
|
|
29553
|
-
const reserveBaseBn = BigInt(serverState.state.validated_ledger.reserve_base);
|
|
29554
|
-
try {
|
|
29555
|
-
const accountInfo = await this.getAccountInfo(address);
|
|
29556
|
-
const balanceBn = BigInt(accountInfo.account_data.Balance);
|
|
29557
|
-
return {
|
|
29558
|
-
isActivated: balanceBn >= reserveBaseBn,
|
|
29559
|
-
reserveBaseBn,
|
|
29560
|
-
};
|
|
29561
|
-
}
|
|
29562
|
-
catch (error) {
|
|
29563
|
-
if (error.message?.includes("actNotFound")) {
|
|
29564
|
-
return { isActivated: false, reserveBaseBn };
|
|
29565
|
-
}
|
|
29566
|
-
throw error;
|
|
29567
|
-
}
|
|
29568
|
-
}
|
|
29569
|
-
/**
|
|
29570
|
-
* Waits for a transaction to be validated and returns its final status.
|
|
29571
|
-
* Resolves to 'success' or throws an error with the failed status.
|
|
29572
|
-
*/
|
|
29573
|
-
async waitForTransaction(txHash, { interval = 2_000, timeout = 20_000 } = {}) {
|
|
29574
|
-
const startTime = Date.now();
|
|
29575
|
-
while (true) {
|
|
29576
|
-
try {
|
|
29577
|
-
const response = await this.call("tx", [
|
|
29578
|
-
{
|
|
29579
|
-
transaction: txHash,
|
|
29580
|
-
binary: false,
|
|
29581
|
-
},
|
|
29582
|
-
]);
|
|
29583
|
-
if (!response.validated) {
|
|
29584
|
-
if (Date.now() - startTime > timeout) {
|
|
29585
|
-
throw new Error(`Transaction ${txHash} not validated 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
|
-
const status = response.meta?.TransactionResult;
|
|
29592
|
-
if (status === XrplTxStatus.SUCCESS) {
|
|
29593
|
-
return status;
|
|
29594
|
-
}
|
|
29595
|
-
else {
|
|
29596
|
-
throw new Error(`Transaction failed with status: ${status}`);
|
|
29597
|
-
}
|
|
29598
|
-
}
|
|
29599
|
-
catch (error) {
|
|
29600
|
-
// txnNotFound = still pending or non-existent
|
|
29601
|
-
if (error?.message?.includes("txnNotFound")) {
|
|
29602
|
-
if (Date.now() - startTime > timeout) {
|
|
29603
|
-
throw new Error(`Transaction ${txHash} not found within timeout`);
|
|
29604
|
-
}
|
|
29605
|
-
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
29606
|
-
await new Promise((res) => setTimeout(res, interval));
|
|
29607
|
-
continue;
|
|
29608
|
-
}
|
|
29609
|
-
throw error;
|
|
29610
|
-
}
|
|
29611
|
-
}
|
|
29612
|
-
}
|
|
29613
|
-
async getServerState() {
|
|
29614
|
-
return this.call("server_state", [{}]);
|
|
29615
|
-
}
|
|
29616
|
-
async getAccountInfo(address) {
|
|
29617
|
-
return this.call("account_info", [
|
|
29618
|
-
{
|
|
29619
|
-
account: address,
|
|
29620
|
-
ledger_index: "validated",
|
|
29621
|
-
},
|
|
29622
|
-
]);
|
|
29623
|
-
}
|
|
29624
|
-
/**
|
|
29625
|
-
* Returns the balance of the user in the native XRP token
|
|
29626
|
-
* formatted as a string
|
|
29627
|
-
*/
|
|
29628
|
-
async getNativeBalance(address) {
|
|
29629
|
-
const [accountInfo, serverState] = await Promise.all([
|
|
29630
|
-
this.getAccountInfo(address),
|
|
29631
|
-
this.getServerState(),
|
|
29632
|
-
]);
|
|
29633
|
-
const balance = BigInt(accountInfo.account_data.Balance);
|
|
29634
|
-
const ownerCount = BigInt(accountInfo.account_data.OwnerCount);
|
|
29635
|
-
const reserveBase = BigInt(serverState.state.validated_ledger.reserve_base);
|
|
29636
|
-
const reserveIncrement = BigInt(serverState.state.validated_ledger.reserve_inc);
|
|
29637
|
-
const reserveBalance = reserveBase + ownerCount * reserveIncrement;
|
|
29638
|
-
const spendableBalance = balance - reserveBalance;
|
|
29639
|
-
return formatBNToReadable(spendableBalance, 6);
|
|
29640
|
-
}
|
|
29641
|
-
/**
|
|
29642
|
-
* Returns the balance of the user in the given issued currency (e.g. RLUSD)
|
|
29643
|
-
* formatted as a string
|
|
29644
|
-
*/
|
|
29645
|
-
async getIssuedCurrencyBalance(address, tokenAddress) {
|
|
29646
|
-
const response = await this.getTrustLines(address);
|
|
29647
|
-
const tokenBalance = response.lines.find((line) => `${line.currency}.${line.account}` === tokenAddress);
|
|
29648
|
-
return tokenBalance?.balance || "0";
|
|
29649
|
-
}
|
|
29650
|
-
async call(method, params) {
|
|
29651
|
-
const response = await fetch(this.rpcUrl, {
|
|
29652
|
-
method: "POST",
|
|
29653
|
-
headers: {
|
|
29654
|
-
"Content-Type": "application/json",
|
|
29655
|
-
},
|
|
29656
|
-
body: JSON.stringify({
|
|
29657
|
-
jsonrpc: "2.0",
|
|
29658
|
-
id: 1,
|
|
29659
|
-
method,
|
|
29660
|
-
params,
|
|
29661
|
-
}),
|
|
29662
|
-
});
|
|
29663
|
-
const data = await response.json();
|
|
29664
|
-
if (!data.result) {
|
|
29665
|
-
throw new Error(`Invalid response from RPC (${method})`);
|
|
29666
|
-
}
|
|
29667
|
-
if ("error" in data.result) {
|
|
29668
|
-
throw new Error(`Error from RPC (${method}): ${data.result.error}`);
|
|
29669
|
-
}
|
|
29670
|
-
return data.result;
|
|
29671
|
-
}
|
|
29672
|
-
}
|
|
29673
|
-
|
|
29674
|
-
const clientCache = new Map();
|
|
29675
|
-
async function getClient(chain) {
|
|
29676
|
-
const key = `${chain.chainType}:${chain.chainId}`;
|
|
29677
|
-
if (clientCache.has(key)) {
|
|
29678
|
-
return clientCache.get(key);
|
|
29679
|
-
}
|
|
29680
|
-
const client = await createClient(chain);
|
|
29681
|
-
clientCache.set(key, client);
|
|
29682
|
-
return client;
|
|
29683
|
-
}
|
|
29684
|
-
async function createClient(chain) {
|
|
29685
|
-
switch (chain.chainType) {
|
|
29686
|
-
case squidTypes.ChainType.EVM:
|
|
29687
|
-
return new ethers.JsonRpcProvider(chain.rpc);
|
|
29688
|
-
case squidTypes.ChainType.COSMOS:
|
|
29689
|
-
const rpcUrl = await getWorkingCosmosRpcUrl(chain);
|
|
29690
|
-
return (await stargate.StargateClient.connect(rpcUrl));
|
|
29691
|
-
case squidTypes.ChainType.SOLANA:
|
|
29692
|
-
return new web3_js.Connection(SOLANA_RPC_URL);
|
|
29693
|
-
case squidTypes.ChainType.BTC:
|
|
29694
|
-
return null;
|
|
29695
|
-
case squidTypes.ChainType.SUI:
|
|
29696
|
-
return new client.SuiClient({
|
|
29697
|
-
url: chain.rpc,
|
|
29698
|
-
});
|
|
29699
|
-
case squidTypes.ChainType.XRPL:
|
|
29700
|
-
return new XrplRpcClient(chain.rpc);
|
|
29701
|
-
case squidTypes.ChainType.STELLAR:
|
|
29702
|
-
return new StellarRpcClient(chain.rpc);
|
|
29703
|
-
}
|
|
29704
|
-
}
|
|
29705
|
-
|
|
29706
|
-
class StellarApiClient {
|
|
29707
|
-
apiUrl;
|
|
29708
|
-
constructor(apiUrl) {
|
|
29709
|
-
this.apiUrl = apiUrl;
|
|
29710
|
-
}
|
|
29711
|
-
async getBaseReserve() {
|
|
29712
|
-
const response = await fetch(`${this.apiUrl}/ledgers?order=desc&limit=1`);
|
|
29713
|
-
if (!response.ok) {
|
|
29714
|
-
throw new Error(`Failed to fetch ledgers: ${response.status}`);
|
|
29715
|
-
}
|
|
29716
|
-
const ledgers = await response.json();
|
|
29717
|
-
const latestLedger = ledgers?._embedded.records?.[0];
|
|
29718
|
-
if (latestLedger?.base_reserve_in_stroops == null) {
|
|
29719
|
-
throw new Error("Invalid ledger data");
|
|
29720
|
-
}
|
|
29721
|
-
const baseReserveBn = BigInt(latestLedger.base_reserve_in_stroops);
|
|
29722
|
-
return baseReserveBn;
|
|
29723
|
-
}
|
|
29724
|
-
}
|
|
29725
|
-
|
|
29726
|
-
const DEFAULT_REFETCH_INTERVAL$1 = 20_000;
|
|
29727
|
-
function useStellarAccountActivation({ address, chain, token, }) {
|
|
29728
|
-
/**
|
|
29729
|
-
* Checks if the destination account exists on the Stellar network
|
|
29730
|
-
* Stellar accounts need to have a minimum balance before they can receive payments
|
|
29731
|
-
*/
|
|
29732
|
-
const accountActivatedInfo = reactQuery.useQuery({
|
|
29733
|
-
queryKey: keys().stellarAccountActivatedInfo(address, chain?.chainId, chain?.chainType),
|
|
29734
|
-
queryFn: async () => {
|
|
29735
|
-
if (chain?.chainType !== squidTypes.ChainType.STELLAR ||
|
|
29736
|
-
token?.type !== squidTypes.ChainType.STELLAR) {
|
|
29737
|
-
return null;
|
|
29738
|
-
}
|
|
29739
|
-
if (!address) {
|
|
29740
|
-
throw new Error("Destination address is required");
|
|
29741
|
-
}
|
|
29742
|
-
// TODO: update types
|
|
29743
|
-
const [horizonApiUrl] = chain?.horizonRpcList;
|
|
29744
|
-
if (typeof horizonApiUrl !== "string") {
|
|
29745
|
-
throw new Error("Invalid Horizon API URL");
|
|
29746
|
-
}
|
|
29747
|
-
const stellarApiClient = new StellarApiClient(horizonApiUrl);
|
|
29748
|
-
const reserveBase = await stellarApiClient.getBaseReserve();
|
|
29749
|
-
// Stellar accounts require two base reserves to be activated
|
|
29750
|
-
// https://developers.stellar.org/docs/learn/fundamentals/lumens#minimum-balance
|
|
29751
|
-
const accountReserveBase = reserveBase * BigInt(2);
|
|
29752
|
-
const stellarRpcClient = await getClient(chain);
|
|
29753
|
-
const isActivated = await stellarRpcClient.isAccountActivated(address);
|
|
29754
|
-
return {
|
|
29755
|
-
isActivated,
|
|
29756
|
-
reserveBaseBn: accountReserveBase,
|
|
29757
|
-
};
|
|
29758
|
-
},
|
|
29759
|
-
enabled: !!address && chain?.chainType === squidTypes.ChainType.STELLAR,
|
|
29760
|
-
refetchInterval: DEFAULT_REFETCH_INTERVAL$1,
|
|
29761
|
-
refetchOnWindowFocus: true,
|
|
29762
|
-
});
|
|
29763
|
-
return {
|
|
29764
|
-
accountActivatedInfo,
|
|
29765
|
-
};
|
|
29766
|
-
}
|
|
29767
|
-
|
|
29768
29123
|
const DEFAULT_REFRESH_INTERVAL_MS = 15000;
|
|
29769
29124
|
const useEvmBalance = ({ chain, token, userAddress, enabled = true, refreshIntervalMs = DEFAULT_REFRESH_INTERVAL_MS, }) => {
|
|
29770
29125
|
const { isChainTypeConnected } = useWallet();
|
|
@@ -29959,6 +29314,8 @@ function useNativeTokenForChain(chain) {
|
|
|
29959
29314
|
}
|
|
29960
29315
|
};
|
|
29961
29316
|
const nativeTokenForChainType = React.useMemo(() => {
|
|
29317
|
+
if (!chain)
|
|
29318
|
+
return undefined;
|
|
29962
29319
|
return findNativeToken(getTokensForChainType(), chain);
|
|
29963
29320
|
}, [chain]);
|
|
29964
29321
|
return { nativeToken: nativeTokenForChainType };
|
|
@@ -30116,136 +29473,772 @@ const useStellarNativeBalance = ({ address, chain, }) => {
|
|
|
30116
29473
|
userAddress: address,
|
|
30117
29474
|
enabled: chain?.chainType === squidTypes.ChainType.STELLAR,
|
|
30118
29475
|
});
|
|
30119
|
-
const balance = React.useMemo(() => {
|
|
30120
|
-
if (nativeToken?.decimals && rawBalance) {
|
|
30121
|
-
return {
|
|
30122
|
-
decimals: nativeToken.decimals,
|
|
30123
|
-
value: parseToBigInt(rawBalance, nativeToken.decimals),
|
|
30124
|
-
};
|
|
30125
|
-
}
|
|
30126
|
-
}, [nativeToken?.decimals, rawBalance]);
|
|
30127
|
-
return {
|
|
30128
|
-
balance,
|
|
30129
|
-
isLoading,
|
|
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,
|
|
30130
29937
|
};
|
|
30131
|
-
|
|
30132
|
-
|
|
30133
|
-
|
|
30134
|
-
|
|
30135
|
-
|
|
30136
|
-
|
|
30137
|
-
|
|
30138
|
-
|
|
30139
|
-
|
|
30140
|
-
|
|
30141
|
-
|
|
30142
|
-
|
|
30143
|
-
|
|
30144
|
-
|
|
30145
|
-
|
|
30146
|
-
|
|
30147
|
-
|
|
30148
|
-
|
|
30149
|
-
|
|
30150
|
-
|
|
30151
|
-
|
|
30152
|
-
|
|
30153
|
-
|
|
30154
|
-
|
|
30155
|
-
|
|
30156
|
-
|
|
30157
|
-
|
|
30158
|
-
|
|
30159
|
-
|
|
30160
|
-
|
|
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
|
+
],
|
|
30161
29970
|
});
|
|
30162
|
-
|
|
30163
|
-
|
|
30164
|
-
|
|
30165
|
-
|
|
30166
|
-
|
|
30167
|
-
|
|
30168
|
-
|
|
30169
|
-
|
|
30170
|
-
|
|
30171
|
-
|
|
30172
|
-
|
|
30173
|
-
|
|
30174
|
-
|
|
30175
|
-
|
|
30176
|
-
|
|
30177
|
-
|
|
30178
|
-
|
|
30179
|
-
|
|
30180
|
-
|
|
30181
|
-
|
|
30182
|
-
|
|
30183
|
-
|
|
30184
|
-
|
|
30185
|
-
|
|
30186
|
-
|
|
30187
|
-
|
|
30188
|
-
|
|
30189
|
-
|
|
30190
|
-
|
|
30191
|
-
|
|
30192
|
-
|
|
30193
|
-
}
|
|
30194
|
-
|
|
30195
|
-
|
|
30196
|
-
|
|
30197
|
-
|
|
30198
|
-
|
|
30199
|
-
|
|
30200
|
-
|
|
30201
|
-
|
|
30202
|
-
|
|
30203
|
-
const
|
|
29971
|
+
};
|
|
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,
|
|
29983
|
+
};
|
|
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(() => {
|
|
30204
30014
|
if (!chain?.chainType)
|
|
30205
30015
|
return false;
|
|
30206
30016
|
switch (chain.chainType) {
|
|
30207
30017
|
case squidTypes.ChainType.EVM:
|
|
30208
|
-
return
|
|
30018
|
+
return isEvmSignerReady;
|
|
30209
30019
|
case squidTypes.ChainType.COSMOS:
|
|
30210
|
-
return
|
|
30020
|
+
return isCosmosSignerReady;
|
|
30211
30021
|
case squidTypes.ChainType.BTC:
|
|
30212
|
-
return
|
|
30022
|
+
return isBitcoinSignerReady;
|
|
30213
30023
|
case squidTypes.ChainType.SOLANA:
|
|
30214
|
-
return
|
|
30024
|
+
return isSolanaSignerReady;
|
|
30215
30025
|
case squidTypes.ChainType.SUI:
|
|
30216
|
-
return
|
|
30026
|
+
return isSuiSignerReady;
|
|
30217
30027
|
case squidTypes.ChainType.XRPL:
|
|
30218
|
-
return
|
|
30028
|
+
return isXrplSignerReady;
|
|
30219
30029
|
case squidTypes.ChainType.STELLAR:
|
|
30220
|
-
return
|
|
30030
|
+
return isStellarSignerReady;
|
|
30221
30031
|
}
|
|
30222
30032
|
}, [
|
|
30223
30033
|
chain?.chainType,
|
|
30224
|
-
|
|
30225
|
-
|
|
30226
|
-
|
|
30227
|
-
|
|
30228
|
-
|
|
30229
|
-
|
|
30230
|
-
|
|
30034
|
+
isEvmSignerReady,
|
|
30035
|
+
isCosmosSignerReady,
|
|
30036
|
+
isBitcoinSignerReady,
|
|
30037
|
+
isSolanaSignerReady,
|
|
30038
|
+
isSuiSignerReady,
|
|
30039
|
+
isXrplSignerReady,
|
|
30040
|
+
isStellarSignerReady,
|
|
30231
30041
|
]);
|
|
30232
|
-
return {
|
|
30042
|
+
return {
|
|
30043
|
+
isSignerReady,
|
|
30044
|
+
evmSigner,
|
|
30045
|
+
cosmosSigner,
|
|
30046
|
+
bitcoinSigner,
|
|
30047
|
+
solanaSigner,
|
|
30048
|
+
suiSigner,
|
|
30049
|
+
xrplSigner,
|
|
30050
|
+
stellarSigner,
|
|
30051
|
+
};
|
|
30233
30052
|
};
|
|
30234
30053
|
|
|
30235
|
-
function
|
|
30236
|
-
const
|
|
30237
|
-
|
|
30238
|
-
|
|
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,
|
|
30239
30140
|
});
|
|
30240
|
-
const isHederaAccountActivated = React.useMemo(() => {
|
|
30241
|
-
if (token?.chainId !== CHAIN_IDS.HEDERA)
|
|
30242
|
-
return true;
|
|
30243
|
-
if (token.address.toLowerCase() === nativeEvmTokenAddress.toLowerCase())
|
|
30244
|
-
return true;
|
|
30245
|
-
return (!!destNativeEvmBalance?.value && destNativeEvmBalance.value > BigInt(0));
|
|
30246
|
-
}, [token?.chainId, token?.address, destNativeEvmBalance?.value]);
|
|
30247
30141
|
return {
|
|
30248
|
-
|
|
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
|
+
};
|
|
30181
|
+
};
|
|
30182
|
+
|
|
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,
|
|
30239
|
+
});
|
|
30240
|
+
return {
|
|
30241
|
+
accountActivatedInfo,
|
|
30249
30242
|
};
|
|
30250
30243
|
}
|
|
30251
30244
|
|
|
@@ -30773,6 +30766,161 @@ const useSingleTokenPrice = (tokenData) => {
|
|
|
30773
30766
|
return { tokenPrice, getUSDValue };
|
|
30774
30767
|
};
|
|
30775
30768
|
|
|
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
|
+
|
|
30776
30924
|
const MAX_COINGECKO_QUERY_TOKENS = 100;
|
|
30777
30925
|
const fetchHistoricalData = async (coingeckoId, timeFrame) => {
|
|
30778
30926
|
const now = Math.floor(Date.now() / 1000);
|
|
@@ -30923,24 +31071,20 @@ const calculateTotalNativeFees = ({ expressFeeCost, firstFeeCost, firstGasCost,
|
|
|
30923
31071
|
(sameTokenBetweenFees
|
|
30924
31072
|
? BigInt(firstFeeCost?.amount ?? "0") + BigInt(firstGasCost?.amount ?? "0")
|
|
30925
31073
|
: BigInt(firstGasCost?.amount ?? "0"));
|
|
30926
|
-
const
|
|
30927
|
-
|
|
30928
|
-
|
|
30929
|
-
|
|
30930
|
-
|
|
30931
|
-
|
|
30932
|
-
|
|
30933
|
-
|
|
30934
|
-
|
|
30935
|
-
}
|
|
30936
|
-
|
|
30937
|
-
|
|
30938
|
-
|
|
30939
|
-
|
|
30940
|
-
? formatBNToReadable(minAmount, sourceChainNativeTokenDecimals)
|
|
30941
|
-
: "0";
|
|
30942
|
-
})()
|
|
30943
|
-
: undefined;
|
|
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
|
+
};
|
|
30944
31088
|
/**
|
|
30945
31089
|
* Calculates the estimated duration of a route
|
|
30946
31090
|
*
|
|
@@ -30979,12 +31123,10 @@ const formatEstimatedRouteDuration = ({ estimatedRouteDuration, isSingleChainRou
|
|
|
30979
31123
|
* @returns {Object} An object containing various estimate results and calculations, including token information,
|
|
30980
31124
|
* amounts, fees, gas costs, and other relevant data for the transaction.
|
|
30981
31125
|
*/
|
|
30982
|
-
const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, collectFees, nativeTokenBalanceFromChainWei, }) => {
|
|
31126
|
+
const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, collectFees, nativeTokenBalanceFromChainWei, tempoFeeData, }) => {
|
|
30983
31127
|
const fromToken = findToken(tokens, squidRoute?.params.fromChain, squidRoute?.params.fromToken);
|
|
30984
31128
|
const fromAmount = squidRoute?.estimate?.fromAmount;
|
|
30985
31129
|
const fromAmountFormatted = formatAmount(fromAmount, fromToken?.decimals);
|
|
30986
|
-
const sourceChainNativeToken = findNativeToken(tokens, fromChain);
|
|
30987
|
-
const destChainNativeToken = findNativeToken(tokens, toChain);
|
|
30988
31130
|
const toAmountUSD = squidRoute?.estimate?.toAmountUSD;
|
|
30989
31131
|
const exchangeRate = squidRoute?.estimate.exchangeRate ?? "0";
|
|
30990
31132
|
const toAmountMinUSD = squidRoute?.estimate.toAmountMinUSD ?? "0";
|
|
@@ -31003,10 +31145,6 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31003
31145
|
const expectedGasRefundCostUSD = convertTokenAmountToUSD(formatBNToReadable(expectedGasRefundCost, firstFeeCost?.token.decimals ?? 18), firstFeeCost?.token.usdPrice ?? "0");
|
|
31004
31146
|
const sameTokenBetweenFees = firstFeeCost?.token.address === firstGasCost?.token.address &&
|
|
31005
31147
|
firstFeeCost?.token.chainId === firstGasCost?.token.chainId;
|
|
31006
|
-
const isFromTokenNative =
|
|
31007
|
-
// TODO: temporary fix, currently nativeCurrency.symbol is not always in uppercase
|
|
31008
|
-
fromToken?.symbol.toUpperCase() ===
|
|
31009
|
-
fromChain?.nativeCurrency.symbol.toUpperCase();
|
|
31010
31148
|
const totalNativeFees = calculateTotalNativeFees({
|
|
31011
31149
|
expressFeeCost,
|
|
31012
31150
|
firstFeeCost,
|
|
@@ -31014,19 +31152,32 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31014
31152
|
sameTokenBetweenFees,
|
|
31015
31153
|
});
|
|
31016
31154
|
const totalFeesInNativeTokenPlusRatio = (totalNativeFees * BigInt(110)) / BigInt(100);
|
|
31017
|
-
const
|
|
31018
|
-
|
|
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,
|
|
31019
31168
|
fromAmount,
|
|
31020
|
-
|
|
31021
|
-
totalFeesInNativeTokenPlusRatio,
|
|
31022
|
-
nativeTokenBalanceFromChainWei,
|
|
31169
|
+
networkFeesWei,
|
|
31023
31170
|
});
|
|
31024
|
-
const
|
|
31025
|
-
|
|
31026
|
-
|
|
31027
|
-
|
|
31028
|
-
totalFeesInNativeTokenPlusRatio,
|
|
31171
|
+
const minAmount = calculateMinAmountValueWarnMsg({
|
|
31172
|
+
chainFeeParams,
|
|
31173
|
+
// gasTokenDecimals: chainFeeParams,
|
|
31174
|
+
networkFeesWei,
|
|
31029
31175
|
});
|
|
31176
|
+
const minAmountValueWarnMsg = minAmount
|
|
31177
|
+
? formatBNToReadable(minAmount, chainFeeParams?.fromTokenPaysNetworkFee
|
|
31178
|
+
? fromToken?.decimals
|
|
31179
|
+
: undefined)
|
|
31180
|
+
: undefined;
|
|
31030
31181
|
const isSingleChainRoute = fromChain?.chainId === toChain?.chainId;
|
|
31031
31182
|
const estimatedRouteDuration = formatEstimatedRouteDuration({
|
|
31032
31183
|
estimatedRouteDuration: squidRoute?.estimate?.estimatedRouteDuration,
|
|
@@ -31036,8 +31187,6 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31036
31187
|
fromToken,
|
|
31037
31188
|
fromAmount,
|
|
31038
31189
|
fromAmountFormatted,
|
|
31039
|
-
sourceChainNativeToken,
|
|
31040
|
-
destChainNativeToken,
|
|
31041
31190
|
toAmountUSD,
|
|
31042
31191
|
exchangeRate,
|
|
31043
31192
|
toAmountMinUSD,
|
|
@@ -31052,10 +31201,10 @@ const calculateEstimateResults = ({ squidRoute, tokens, fromChain, toChain, coll
|
|
|
31052
31201
|
expectedGasRefundCost,
|
|
31053
31202
|
expectedGasRefundCostUSD,
|
|
31054
31203
|
sameTokenBetweenFees,
|
|
31055
|
-
|
|
31204
|
+
fromTokenPaysNetworkFee: chainFeeParams?.fromTokenPaysNetworkFee ?? false,
|
|
31056
31205
|
totalNativeFees,
|
|
31057
31206
|
totalFeesInNativeTokenPlusRatio,
|
|
31058
|
-
|
|
31207
|
+
gasBalanceEnough,
|
|
31059
31208
|
minAmountValueWarnMsg,
|
|
31060
31209
|
estimatedRouteDuration,
|
|
31061
31210
|
};
|
|
@@ -31091,28 +31240,6 @@ const calculateTotalWithRefundEstimate = (allFeeCosts, expectedGasRefundCost, ge
|
|
|
31091
31240
|
feeToken: firstFeeCost?.token,
|
|
31092
31241
|
};
|
|
31093
31242
|
};
|
|
31094
|
-
/**
|
|
31095
|
-
* Calculates the proposed gas amount for the destination chain.
|
|
31096
|
-
*
|
|
31097
|
-
* @param destChainNativeToken - The symbol of the native token for the destination chain.
|
|
31098
|
-
* @returns An object containing the proposed gas amount value and the currency symbol.
|
|
31099
|
-
*/
|
|
31100
|
-
const getProposedGasDestinationAmount = (destChainNativeToken) => {
|
|
31101
|
-
const gasAmounts = {
|
|
31102
|
-
GLMR: 5.289,
|
|
31103
|
-
ETH: 0.0009,
|
|
31104
|
-
AVAX: 0.115,
|
|
31105
|
-
BNB: 0.00425,
|
|
31106
|
-
FTM: 4.45,
|
|
31107
|
-
CELO: 3.052,
|
|
31108
|
-
KAVA: 2.339,
|
|
31109
|
-
MATIC: 1.795,
|
|
31110
|
-
};
|
|
31111
|
-
return {
|
|
31112
|
-
value: gasAmounts[destChainNativeToken ?? ""] ?? 0,
|
|
31113
|
-
currency: destChainNativeToken,
|
|
31114
|
-
};
|
|
31115
|
-
};
|
|
31116
31243
|
|
|
31117
31244
|
function useSendTransactionGas({ chain, token, from, }) {
|
|
31118
31245
|
return reactQuery.useQuery({
|
|
@@ -31134,7 +31261,11 @@ function useSendTransactionGas({ chain, token, from, }) {
|
|
|
31134
31261
|
// Some RPC providers require the sender to have enough balance, otherwise estimation reverts
|
|
31135
31262
|
// so we'll try to use the user provided address when possible
|
|
31136
31263
|
const sender = from || dummyAddress;
|
|
31137
|
-
|
|
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) {
|
|
31138
31269
|
const gas = await client.estimateGas({
|
|
31139
31270
|
from: sender,
|
|
31140
31271
|
to: dummyAddress,
|
|
@@ -31154,7 +31285,14 @@ function useSendTransactionGas({ chain, token, from, }) {
|
|
|
31154
31285
|
data,
|
|
31155
31286
|
chainId: chain.chainId,
|
|
31156
31287
|
});
|
|
31157
|
-
|
|
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;
|
|
31158
31296
|
}
|
|
31159
31297
|
case squidTypes.ChainType.COSMOS: {
|
|
31160
31298
|
// TODO: get gas estimation from backend
|
|
@@ -31202,18 +31340,24 @@ function useEstimateSendTransaction({ chain, token, amount, balance, from, }) {
|
|
|
31202
31340
|
from,
|
|
31203
31341
|
});
|
|
31204
31342
|
const { nativeBalance } = useNativeBalance(chain);
|
|
31205
|
-
const
|
|
31206
|
-
|
|
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)
|
|
31207
31354
|
return false;
|
|
31208
|
-
|
|
31209
|
-
|
|
31210
|
-
|
|
31211
|
-
|
|
31212
|
-
|
|
31213
|
-
|
|
31214
|
-
}
|
|
31215
|
-
return estimatedGas <= nativeBalance.value;
|
|
31216
|
-
}, [amount, estimatedGas, nativeBalance?.value, token]);
|
|
31355
|
+
return isGasBalanceEnough({
|
|
31356
|
+
fromAmount: parseToBigInt(amount, token.decimals).toString(),
|
|
31357
|
+
networkFeesWei: estimatedGas,
|
|
31358
|
+
chainFeeParams,
|
|
31359
|
+
});
|
|
31360
|
+
}, [amount, estimatedGas, token, chainFeeParams]);
|
|
31217
31361
|
const isBalanceEnough = React.useMemo(() => {
|
|
31218
31362
|
if (token?.decimals == null || !balance || !amount)
|
|
31219
31363
|
return false;
|
|
@@ -31221,28 +31365,30 @@ function useEstimateSendTransaction({ chain, token, amount, balance, from, }) {
|
|
|
31221
31365
|
parseToBigInt(amount, token.decimals));
|
|
31222
31366
|
}, [amount, balance, token?.decimals]);
|
|
31223
31367
|
const minAmountValueWarnMsg = React.useMemo(() => {
|
|
31224
|
-
if (!token?.address || !
|
|
31368
|
+
if (!token?.address || !estimatedGas)
|
|
31369
|
+
return undefined;
|
|
31370
|
+
if (!chainFeeParams)
|
|
31225
31371
|
return undefined;
|
|
31226
|
-
const
|
|
31227
|
-
|
|
31228
|
-
|
|
31229
|
-
|
|
31230
|
-
|
|
31231
|
-
|
|
31232
|
-
totalFeesInNativeTokenPlusRatio: estimatedGas,
|
|
31372
|
+
const minAmount = calculateMinAmountValueWarnMsg({
|
|
31373
|
+
chainFeeParams,
|
|
31374
|
+
// gasTokenDecimals: chainFeeParams.fromTokenPaysNetworkFee
|
|
31375
|
+
// ? token.decimals
|
|
31376
|
+
// : undefined,
|
|
31377
|
+
networkFeesWei: estimatedGas,
|
|
31233
31378
|
});
|
|
31234
|
-
|
|
31235
|
-
|
|
31236
|
-
|
|
31237
|
-
|
|
31238
|
-
|
|
31239
|
-
token
|
|
31240
|
-
]);
|
|
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]);
|
|
31241
31386
|
return {
|
|
31242
31387
|
estimatedGas,
|
|
31243
31388
|
isBalanceEnough,
|
|
31389
|
+
tokenPaysNetworkFee: chainFeeParams?.fromTokenPaysNetworkFee ?? false,
|
|
31244
31390
|
isLoading,
|
|
31245
|
-
|
|
31391
|
+
isGasBalanceEnough: gasBalanceEnough,
|
|
31246
31392
|
minAmountValueWarnMsg,
|
|
31247
31393
|
};
|
|
31248
31394
|
}
|
|
@@ -32128,7 +32274,7 @@ const useApproval = ({ squidRoute, }) => {
|
|
|
32128
32274
|
const publicClient = wagmi.usePublicClient();
|
|
32129
32275
|
const queryClient = reactQuery.useQueryClient();
|
|
32130
32276
|
const squid = useSquidStore((state) => state.squid);
|
|
32131
|
-
const { fromChain, fromToken,
|
|
32277
|
+
const { fromChain, fromToken, fromPrice, isSameChain } = useSwap();
|
|
32132
32278
|
const { evmSigner } = useSigner({ chain: fromChain });
|
|
32133
32279
|
const { connector: activeConnector } = wagmi.useAccount();
|
|
32134
32280
|
const { getChainType } = useSquidChains();
|
|
@@ -32301,7 +32447,7 @@ const useApproval = ({ squidRoute, }) => {
|
|
|
32301
32447
|
// This is to ensure we're using the latest expiry timestamp
|
|
32302
32448
|
if (squidRoute) {
|
|
32303
32449
|
queryClient.refetchQueries({
|
|
32304
|
-
queryKey: keys().transaction(squidRoute.params.fromChain, squidRoute.params.toChain, squidRoute.params.toToken, squidRoute.params.fromToken,
|
|
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,
|
|
32305
32451
|
// TODO: update types
|
|
32306
32452
|
squidRoute.params?.overrideGasRefundAddress),
|
|
32307
32453
|
});
|
|
@@ -32320,8 +32466,10 @@ const AXELAR_PROVIDER_IMAGE_URL = "https://raw.githubusercontent.com/0xsquid/ass
|
|
|
32320
32466
|
const useEstimate = (squidRoute) => {
|
|
32321
32467
|
const collectFees = useConfigStore((state) => state.config.collectFees);
|
|
32322
32468
|
const { tokens } = useSquidTokens();
|
|
32323
|
-
const { fromChain, toChain } = useSwap();
|
|
32469
|
+
const { fromChain, toChain, fromPrice } = useSwap();
|
|
32324
32470
|
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 });
|
|
32325
32473
|
const estimateResults = React.useMemo(() => calculateEstimateResults({
|
|
32326
32474
|
squidRoute,
|
|
32327
32475
|
tokens,
|
|
@@ -32329,8 +32477,17 @@ const useEstimate = (squidRoute) => {
|
|
|
32329
32477
|
toChain,
|
|
32330
32478
|
collectFees: !!collectFees && collectFees.fee > 0,
|
|
32331
32479
|
nativeTokenBalanceFromChainWei: nativeBalance?.value ?? BigInt("0"),
|
|
32332
|
-
|
|
32333
|
-
|
|
32480
|
+
tempoFeeData: tempoFeeData ?? undefined,
|
|
32481
|
+
}), [
|
|
32482
|
+
squidRoute,
|
|
32483
|
+
tokens,
|
|
32484
|
+
fromChain,
|
|
32485
|
+
toChain,
|
|
32486
|
+
collectFees,
|
|
32487
|
+
nativeBalance,
|
|
32488
|
+
tempoFeeData,
|
|
32489
|
+
]);
|
|
32490
|
+
const fromBalanceFormatted = useMultiChainBalance({
|
|
32334
32491
|
chain: fromChain,
|
|
32335
32492
|
token: estimateResults.fromToken,
|
|
32336
32493
|
userAddress: squidRoute?.params.fromAddress ?? "",
|
|
@@ -32342,7 +32499,6 @@ const useEstimate = (squidRoute) => {
|
|
|
32342
32499
|
estimateResults.expectedGasRefundCost,
|
|
32343
32500
|
getUSDValue,
|
|
32344
32501
|
]);
|
|
32345
|
-
const proposedGasDestinationAmount = React.useMemo(() => getProposedGasDestinationAmount(estimateResults.destChainNativeToken?.symbol), [estimateResults.destChainNativeToken]);
|
|
32346
32502
|
const { feeCostsFormatted, totalFeeCostsUsd } = React.useMemo(() => {
|
|
32347
32503
|
const feeCosts = squidRoute?.estimate.feeCosts ?? [];
|
|
32348
32504
|
const feeCostsRenamed = [];
|
|
@@ -32401,19 +32557,18 @@ const useEstimate = (squidRoute) => {
|
|
|
32401
32557
|
totalFeeCostsUsd: totalFeeCostsUsdFormatted,
|
|
32402
32558
|
};
|
|
32403
32559
|
}, [squidRoute?.estimate.feeCosts]);
|
|
32404
|
-
const slippageFormatted =
|
|
32405
|
-
|
|
32406
|
-
|
|
32407
|
-
|
|
32408
|
-
|
|
32409
|
-
|
|
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]);
|
|
32410
32566
|
return {
|
|
32411
32567
|
...estimateResults,
|
|
32412
|
-
|
|
32568
|
+
fromBalanceFormatted,
|
|
32413
32569
|
slippageFormatted,
|
|
32414
32570
|
totalWithRefundEstimate,
|
|
32415
|
-
|
|
32416
|
-
enoughBalanceToSwap,
|
|
32571
|
+
fromBalanceEnoughToSwap,
|
|
32417
32572
|
feeCostsFormatted,
|
|
32418
32573
|
totalFeeCostsUsd,
|
|
32419
32574
|
};
|
|
@@ -36114,12 +36269,8 @@ const useGetRoute = () => {
|
|
|
36114
36269
|
* These data will be used to trigger the transaction
|
|
36115
36270
|
* @returns {Route} Route data
|
|
36116
36271
|
*/
|
|
36117
|
-
return reactQuery.useMutation(async ({ fromChain, toChain, fromToken, toToken, sourceUserAddress, destinationAddress, fromPrice
|
|
36118
|
-
if (!fromChain ||
|
|
36119
|
-
!toChain ||
|
|
36120
|
-
!fromToken ||
|
|
36121
|
-
!toToken ||
|
|
36122
|
-
(!fromPrice && !toPrice)) {
|
|
36272
|
+
return reactQuery.useMutation(async ({ fromChain, toChain, fromToken, toToken, sourceUserAddress, destinationAddress, fromPrice, bypassGuardrails, quoteOnly, fromChainType, postHook, preHook, overrideGasRefundAddress, }) => {
|
|
36273
|
+
if (!fromChain || !toChain || !fromToken || !toToken || !fromPrice) {
|
|
36123
36274
|
return undefined;
|
|
36124
36275
|
}
|
|
36125
36276
|
// Dispatch requestQuote event
|
|
@@ -36129,7 +36280,6 @@ const useGetRoute = () => {
|
|
|
36129
36280
|
fromToken: fromToken.address,
|
|
36130
36281
|
toToken: toToken.address,
|
|
36131
36282
|
fromAmount: fromPrice,
|
|
36132
|
-
toAmount: toPrice,
|
|
36133
36283
|
fromAddress: sourceUserAddress,
|
|
36134
36284
|
toAddress: destinationAddress,
|
|
36135
36285
|
});
|
|
@@ -36138,7 +36288,6 @@ const useGetRoute = () => {
|
|
|
36138
36288
|
const fromTokenAddress = fromToken.address;
|
|
36139
36289
|
const toTokenAddress = toToken.address;
|
|
36140
36290
|
const fromAmount = parseToBigInt(fromPrice?.toString() ?? "0", fromToken?.decimals).toString();
|
|
36141
|
-
const toAmount = parseToBigInt(toPrice?.toString() ?? "0", toToken?.decimals).toString();
|
|
36142
36291
|
const fromAddress = sourceUserAddress ??
|
|
36143
36292
|
chainTypeToZeroAddressMap[fromChainType ?? squidTypes.ChainType.EVM];
|
|
36144
36293
|
const params = {
|
|
@@ -36146,7 +36295,6 @@ const useGetRoute = () => {
|
|
|
36146
36295
|
fromToken: fromTokenAddress,
|
|
36147
36296
|
fromAddress,
|
|
36148
36297
|
fromAmount,
|
|
36149
|
-
toAmount,
|
|
36150
36298
|
toChain,
|
|
36151
36299
|
toToken: toTokenAddress,
|
|
36152
36300
|
toAddress: destinationAddress ?? "",
|
|
@@ -36168,7 +36316,7 @@ const useGetRoute = () => {
|
|
|
36168
36316
|
});
|
|
36169
36317
|
// Cache the route data
|
|
36170
36318
|
// Useful when the getRoute mutation is called from another hook
|
|
36171
|
-
queryClient.setQueryData(keys().transaction(fromChain, toChain, toToken.address, fromToken.address, fromPrice,
|
|
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);
|
|
36172
36320
|
return route;
|
|
36173
36321
|
});
|
|
36174
36322
|
};
|
|
@@ -36182,7 +36330,7 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
|
|
|
36182
36330
|
const depositRefundAddress = useSwapRoutePersistStore((store) => store.swapRoute?.depositRefundAddress);
|
|
36183
36331
|
const { isAvailableAsPaymentMethod, isEnabled: isDepositAddressEnabled } = useDepositAddress();
|
|
36184
36332
|
const getRouteMutation = useGetRoute();
|
|
36185
|
-
const { fromChain, toChain,
|
|
36333
|
+
const { fromChain, toChain, fromPrice, destinationAddress: { address: destinationAddress } = {}, fromToken, toToken, } = useSwap();
|
|
36186
36334
|
const { connectedAddress: { address: sourceConnectedAddress }, } = useMultiChainWallet(fromChain);
|
|
36187
36335
|
// When the payment method is deposit address, users can specify a refund address on the source chain
|
|
36188
36336
|
// Tokens will be sent to this address in case of swap failure
|
|
@@ -36191,15 +36339,13 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
|
|
|
36191
36339
|
const sourceUserAddress = isDepositAddressEnabled && isAvailableAsPaymentMethod
|
|
36192
36340
|
? depositRefundAddress ?? sourceConnectedAddress
|
|
36193
36341
|
: sourceConnectedAddress;
|
|
36194
|
-
const squidRouteQueryKeys = React.useMemo(() => keys().transaction(fromChain?.chainId, toChain?.chainId, toToken?.address, fromToken?.address,
|
|
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), [
|
|
36195
36343
|
fromChain?.chainId,
|
|
36196
36344
|
toChain?.chainId,
|
|
36197
36345
|
toToken?.address,
|
|
36198
36346
|
fromToken?.address,
|
|
36199
|
-
|
|
36200
|
-
toAmount,
|
|
36347
|
+
fromPrice,
|
|
36201
36348
|
config.slippage,
|
|
36202
|
-
config.enableGetGasOnDestination,
|
|
36203
36349
|
sourceUserAddress,
|
|
36204
36350
|
config.degenMode,
|
|
36205
36351
|
destinationAddress,
|
|
@@ -36213,8 +36359,8 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
|
|
|
36213
36359
|
const queryEnabled = enabled != undefined
|
|
36214
36360
|
? enabled
|
|
36215
36361
|
: squid !== undefined &&
|
|
36216
|
-
|
|
36217
|
-
|
|
36362
|
+
fromPrice !== undefined &&
|
|
36363
|
+
fromPrice !== "0" &&
|
|
36218
36364
|
toChain?.chainId !== undefined &&
|
|
36219
36365
|
toToken?.address !== undefined;
|
|
36220
36366
|
const queryClient = reactQuery.useQueryClient();
|
|
@@ -36232,8 +36378,7 @@ refetchIntervalInBackground = false, refetchInterval = 30000, quoteOnly = true,
|
|
|
36232
36378
|
toToken,
|
|
36233
36379
|
sourceUserAddress,
|
|
36234
36380
|
destinationAddress,
|
|
36235
|
-
fromPrice
|
|
36236
|
-
toPrice: toAmount,
|
|
36381
|
+
fromPrice,
|
|
36237
36382
|
bypassGuardrails: config.degenMode,
|
|
36238
36383
|
quoteOnly,
|
|
36239
36384
|
fromChainType: fromChain?.chainType,
|
|
@@ -36472,35 +36617,6 @@ const useAvatar = (seed = zeroAddress) => {
|
|
|
36472
36617
|
return avatar || "";
|
|
36473
36618
|
};
|
|
36474
36619
|
|
|
36475
|
-
const useUserParams = () => {
|
|
36476
|
-
const enableGetGasOnDestination = useConfigStore((state) => state.config.enableGetGasOnDestination);
|
|
36477
|
-
const { fromToken, toToken, toChain, fromChain } = useSwap();
|
|
36478
|
-
// =============
|
|
36479
|
-
// GAS
|
|
36480
|
-
// =============
|
|
36481
|
-
const getGasOnDestSupportedForThisRoute = React.useMemo(() =>
|
|
36482
|
-
// Not supporting get gas on dest for same chains
|
|
36483
|
-
fromChain?.chainId !== toChain?.chainId &&
|
|
36484
|
-
// If the destination chain is cosmos, we don't support getting gas there
|
|
36485
|
-
toChain?.chainType !== squidTypes.ChainType.COSMOS &&
|
|
36486
|
-
// Not supporting get gas on dest for same tokens (bridge)
|
|
36487
|
-
((fromToken?.subGraphIds?.some((sgi) => !!toToken?.subGraphIds?.includes(sgi)) &&
|
|
36488
|
-
toToken?.subGraphIds?.some((sgi) => !!fromToken?.subGraphIds?.includes(sgi))) ||
|
|
36489
|
-
// Except for uusdc -> uusdc
|
|
36490
|
-
(fromToken?.subGraphIds?.includes("uusdc") &&
|
|
36491
|
-
toToken?.subGraphIds?.includes("uusdc"))), [
|
|
36492
|
-
fromChain?.chainId,
|
|
36493
|
-
fromToken?.subGraphIds,
|
|
36494
|
-
toChain?.chainId,
|
|
36495
|
-
toToken?.subGraphIds,
|
|
36496
|
-
toChain?.chainType,
|
|
36497
|
-
]);
|
|
36498
|
-
return {
|
|
36499
|
-
gasEnabled: enableGetGasOnDestination && getGasOnDestSupportedForThisRoute,
|
|
36500
|
-
getGasOnDestSupportedForThisRoute,
|
|
36501
|
-
};
|
|
36502
|
-
};
|
|
36503
|
-
|
|
36504
36620
|
const useAddToken = (chainToCompare, tokenToCompare) => {
|
|
36505
36621
|
const { chain: currentEvmChain } = wagmi.useAccount();
|
|
36506
36622
|
const { connector } = wagmi.useAccount();
|
|
@@ -37153,6 +37269,7 @@ exports.useSendTransactionStore = useSendTransactionStore;
|
|
|
37153
37269
|
exports.useSigner = useSigner;
|
|
37154
37270
|
exports.useSingleTokenPrice = useSingleTokenPrice;
|
|
37155
37271
|
exports.useSolanaNativeBalance = useSolanaNativeBalance;
|
|
37272
|
+
exports.useSourceChainGasToken = useSourceChainGasToken;
|
|
37156
37273
|
exports.useSquid = useSquid;
|
|
37157
37274
|
exports.useSquidChains = useSquidChains;
|
|
37158
37275
|
exports.useSquidQueryClient = useSquidQueryClient;
|
|
@@ -37168,7 +37285,6 @@ exports.useSwapTransactionStatus = useSwapTransactionStatus;
|
|
|
37168
37285
|
exports.useTokensData = useTokensData;
|
|
37169
37286
|
exports.useTrackSearchEmpty = useTrackSearchEmpty;
|
|
37170
37287
|
exports.useTransactionStore = useTransactionStore;
|
|
37171
|
-
exports.useUserParams = useUserParams;
|
|
37172
37288
|
exports.useWallet = useWallet;
|
|
37173
37289
|
exports.useWalletStore = useWalletStore;
|
|
37174
37290
|
exports.useWallets = useWallets;
|
|
@@ -37177,4 +37293,4 @@ exports.useXrplTrustLine = useXrplTrustLine;
|
|
|
37177
37293
|
exports.waitForReceiptWithRetry = waitForReceiptWithRetry;
|
|
37178
37294
|
exports.walletIconBaseUrl = walletIconBaseUrl;
|
|
37179
37295
|
exports.walletSupportsChainType = walletSupportsChainType;
|
|
37180
|
-
//# sourceMappingURL=index-
|
|
37296
|
+
//# sourceMappingURL=index-CaI-xWCW.js.map
|