Compare commits
4 Commits
1c2e267136
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d207ef6dca | |||
| 0e5921255e | |||
| 1ac26aeec0 | |||
| a2a036818d |
@@ -6,17 +6,19 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowDownUp, ChevronDown, Settings, CheckCircle, XCircle, Loader2 } from 'lucide-react';
|
||||
import { useAccount } from 'wagmi';
|
||||
import { useAccount, useChainId } from 'wagmi';
|
||||
import { useTokenDetails, useGetPoolsByToken, type TokenDetails } from '@/hooks/usePartyPlanner';
|
||||
import { useSwapAmounts, useSwap, selectBestSwapRoute, type ActualSwapAmounts } from '@/hooks/usePartyPool';
|
||||
import { formatUnits, parseUnits } from 'viem';
|
||||
import { SwapReviewModal } from './swap-review-modal';
|
||||
import UniswapQuote from './uniswap-quote';
|
||||
|
||||
type TransactionStatus = 'idle' | 'pending' | 'success' | 'error';
|
||||
|
||||
export function SwapForm() {
|
||||
const { t } = useTranslation();
|
||||
const { isConnected, address } = useAccount();
|
||||
const chainId = useChainId();
|
||||
const [fromAmount, setFromAmount] = useState('');
|
||||
const [toAmount, setToAmount] = useState('');
|
||||
const [selectedFromToken, setSelectedFromToken] = useState<TokenDetails | null>(null);
|
||||
@@ -404,6 +406,19 @@ export function SwapForm() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/*/!* Uniswap Quote - Hidden (under construction) *!/*/}
|
||||
{/*{false && fromAmount && selectedFromToken && selectedToToken && (*/}
|
||||
{/* <UniswapQuote*/}
|
||||
{/* amountIn={fromAmount}*/}
|
||||
{/* tokenInAddress={selectedFromToken.address}*/}
|
||||
{/* tokenOutAddress={selectedToToken.address}*/}
|
||||
{/* tokenInDecimals={selectedFromToken.decimals}*/}
|
||||
{/* tokenOutDecimals={selectedToToken.decimals}*/}
|
||||
{/* tokenOutSymbol={selectedToToken.symbol}*/}
|
||||
{/* chainId={chainId || 1}*/}
|
||||
{/* />*/}
|
||||
{/*)}*/}
|
||||
|
||||
{/* Gas Estimate, Slippage, and Fees */}
|
||||
{isConnected && fromAmount && toAmount && (
|
||||
<div className="px-4 py-2 bg-muted/30 rounded-lg space-y-2">
|
||||
|
||||
248
src/components/uniswap-quote.tsx
Normal file
248
src/components/uniswap-quote.tsx
Normal file
@@ -0,0 +1,248 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { parseUnits, formatUnits } from 'viem';
|
||||
|
||||
interface UniswapQuoteProps {
|
||||
amountIn: string;
|
||||
tokenInAddress: string | null;
|
||||
tokenOutAddress: string | null;
|
||||
tokenInDecimals: number;
|
||||
tokenOutDecimals: number;
|
||||
tokenOutSymbol: string;
|
||||
chainId: number;
|
||||
}
|
||||
|
||||
interface TokenPrices {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
export default function UniswapQuote({
|
||||
amountIn,
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
tokenInDecimals,
|
||||
tokenOutDecimals,
|
||||
tokenOutSymbol,
|
||||
chainId
|
||||
}: UniswapQuoteProps) {
|
||||
const [quote, setQuote] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [prices, setPrices] = useState<TokenPrices | null>(null);
|
||||
|
||||
// Only show on mainnet
|
||||
if (chainId !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't fetch quote if tokens aren't selected
|
||||
if (!tokenInAddress || !tokenOutAddress) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fetch token prices from CoinGecko using contract addresses
|
||||
useEffect(() => {
|
||||
if (!tokenInAddress || !tokenOutAddress) return;
|
||||
|
||||
const fetchPrices = async () => {
|
||||
try {
|
||||
// Fetch both token prices separately using the correct endpoint
|
||||
const [tokenInResponse, tokenOutResponse] = await Promise.all([
|
||||
fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenInAddress}&vs_currencies=usd`),
|
||||
fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenOutAddress}&vs_currencies=usd`)
|
||||
]);
|
||||
|
||||
const tokenInData = await tokenInResponse.json();
|
||||
const tokenOutData = await tokenOutResponse.json();
|
||||
|
||||
const tokenInPrice = tokenInData[tokenInAddress.toLowerCase()]?.usd || 0;
|
||||
const tokenOutPrice = tokenOutData[tokenOutAddress.toLowerCase()]?.usd || 0;
|
||||
|
||||
setPrices({
|
||||
tokenIn: tokenInPrice,
|
||||
tokenOut: tokenOutPrice
|
||||
});
|
||||
|
||||
console.log('Token prices:', { tokenInPrice, tokenOutPrice, tokenInData, tokenOutData });
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch prices:', err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPrices();
|
||||
// Refresh prices every 30 seconds
|
||||
const interval = setInterval(fetchPrices, 30000);
|
||||
return () => clearInterval(interval);
|
||||
}, [tokenInAddress, tokenOutAddress]);
|
||||
|
||||
const getQuote = async () => {
|
||||
if (!amountIn || parseFloat(amountIn) <= 0) {
|
||||
setError('Please enter a valid amount');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
// Convert amount to smallest unit based on token decimals using viem
|
||||
const amountInSmallestUnit = parseUnits(amountIn, tokenInDecimals).toString();
|
||||
|
||||
const response = await fetch('https://api.uniswap.org/v2/quote', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': 'https://app.uniswap.org'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
amount: amountInSmallestUnit,
|
||||
tokenIn: tokenInAddress,
|
||||
tokenInChainId: chainId,
|
||||
tokenOut: tokenOutAddress,
|
||||
tokenOutChainId: chainId,
|
||||
type: 'EXACT_INPUT',
|
||||
configs: [
|
||||
{
|
||||
protocols: ['V2', 'V3', 'V4'],
|
||||
enableUniversalRouter: true,
|
||||
routingType: 'CLASSIC'
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch quote');
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Uniswap Quote:', data);
|
||||
setQuote(data);
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formatTokenAmount = (amount: string) => {
|
||||
const formatted = formatUnits(BigInt(amount), tokenOutDecimals);
|
||||
return parseFloat(formatted).toLocaleString('en-US', {
|
||||
maximumFractionDigits: tokenOutDecimals >= 18 ? 0 : 6
|
||||
});
|
||||
};
|
||||
|
||||
const getQuoteAmount = () => {
|
||||
if (!quote) return '0';
|
||||
// Handle nested quote structure
|
||||
return quote.quote?.quote || quote.quote || '0';
|
||||
};
|
||||
|
||||
const calculateCostBreakdown = () => {
|
||||
if (!quote || !prices || !prices.tokenIn) return null;
|
||||
|
||||
const tokenAmount = parseFloat(amountIn);
|
||||
const tradeValueUSD = tokenAmount * prices.tokenIn;
|
||||
|
||||
// Access nested quote object
|
||||
const quoteData = quote.quote || quote;
|
||||
|
||||
// 1. Gas Cost
|
||||
const gasCostUSD = parseFloat(quoteData.gasUseEstimateUSD || '0');
|
||||
|
||||
// 2. Uniswap UX Fee (0.25%)
|
||||
const uniswapFeePercent = 0.25;
|
||||
const uniswapFeeUSD = (uniswapFeePercent / 100) * tradeValueUSD;
|
||||
|
||||
const totalCostUSD = gasCostUSD + uniswapFeeUSD;
|
||||
|
||||
console.log('Cost breakdown calc:', {
|
||||
gasCostUSD,
|
||||
uniswapFeeUSD,
|
||||
totalCostUSD,
|
||||
tradeValueUSD,
|
||||
quoteData
|
||||
});
|
||||
|
||||
return {
|
||||
gasCostUSD,
|
||||
uniswapFeePercent,
|
||||
uniswapFeeUSD,
|
||||
totalCostUSD,
|
||||
tradeValueUSD
|
||||
};
|
||||
};
|
||||
|
||||
const costBreakdown = calculateCostBreakdown();
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-md p-4">
|
||||
<button
|
||||
onClick={getQuote}
|
||||
disabled={loading}
|
||||
className="w-full bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white font-semibold py-2 px-4 rounded"
|
||||
>
|
||||
{loading ? 'Getting Quote...' : 'Get Quote'}
|
||||
</button>
|
||||
|
||||
{error && (
|
||||
<div className="mt-3 p-3 bg-red-50 text-red-700 rounded text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{quote && costBreakdown && (
|
||||
<div className="mt-4 space-y-3">
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="text-sm text-gray-600">You Get ({tokenOutSymbol})</div>
|
||||
<div className="text-xl font-bold">
|
||||
{formatTokenAmount(getQuoteAmount())}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Total Costs Breakdown */}
|
||||
<div className="p-4 bg-blue-50 rounded-lg space-y-3">
|
||||
<h3 className="font-semibold text-lg text-gray-800">Total Costs Breakdown</h3>
|
||||
|
||||
{/* 1. Gas Cost */}
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm font-medium text-gray-700">1. Gas Cost (Network Fee)</span>
|
||||
<span className="text-sm font-bold text-gray-900">
|
||||
${costBreakdown.gasCostUSD.toFixed(2)} USD
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2. Uniswap UX Fee */}
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm font-medium text-gray-700">2. Uniswap UX Fee</span>
|
||||
<span className="text-sm font-bold text-gray-900">
|
||||
{costBreakdown.uniswapFeePercent}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 pl-4">
|
||||
${costBreakdown.uniswapFeeUSD.toFixed(2)} USD
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Total */}
|
||||
<div className="pt-2 border-t border-gray-300">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm font-bold text-gray-800">Total Estimated Cost</span>
|
||||
<span className="text-base font-bold text-red-600">
|
||||
${costBreakdown.totalCostUSD.toFixed(2)} USD
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trade Value Reference */}
|
||||
<div className="text-xs text-gray-500 text-center pt-1">
|
||||
Trade Value: ${costBreakdown.tradeValueUSD.toFixed(2)} USD
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -198,88 +198,131 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
||||
return;
|
||||
}
|
||||
|
||||
// First, fetch all tokens from all working pools
|
||||
const poolTokensContracts = workingPools.map(poolAddress => ({
|
||||
address: poolAddress,
|
||||
abi: IPartyPoolABI,
|
||||
functionName: 'allTokens',
|
||||
}));
|
||||
|
||||
const poolTokensResults = await publicClient.multicall({
|
||||
contracts: poolTokensContracts as any,
|
||||
allowFailure: true,
|
||||
});
|
||||
|
||||
// Build a flat list of all unique token addresses we need to query
|
||||
const uniqueTokenAddresses = new Set<`0x${string}`>();
|
||||
uniqueTokenAddresses.add(tokenAddress); // Add input token
|
||||
|
||||
poolTokensResults.forEach((result) => {
|
||||
if (result.status === 'success') {
|
||||
const tokens = result.result as readonly `0x${string}`[];
|
||||
tokens.forEach(token => uniqueTokenAddresses.add(token));
|
||||
}
|
||||
});
|
||||
|
||||
const tokenAddressesArray = Array.from(uniqueTokenAddresses);
|
||||
|
||||
// Build multicall for all token symbols and decimals
|
||||
const tokenDataContracts = tokenAddressesArray.flatMap(addr => [
|
||||
{
|
||||
address: addr,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
},
|
||||
{
|
||||
address: addr,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
},
|
||||
]);
|
||||
|
||||
const tokenDataResults = await publicClient.multicall({
|
||||
contracts: tokenDataContracts as any,
|
||||
allowFailure: true,
|
||||
});
|
||||
|
||||
// Parse token data into a map
|
||||
const tokenDataMap = new Map<string, { symbol: string | null; decimals: number | null }>();
|
||||
for (let i = 0; i < tokenAddressesArray.length; i++) {
|
||||
const symbolResult = tokenDataResults[i * 2];
|
||||
const decimalsResult = tokenDataResults[i * 2 + 1];
|
||||
tokenDataMap.set(tokenAddressesArray[i].toLowerCase(), {
|
||||
symbol: symbolResult.status === 'success' ? (symbolResult.result as string) : null,
|
||||
decimals: decimalsResult.status === 'success' ? Number(decimalsResult.result) : null,
|
||||
});
|
||||
}
|
||||
|
||||
// Map to store available tokens with their swap routes
|
||||
const tokenRoutesMap = new Map<string, AvailableToken>();
|
||||
|
||||
// For each working pool, fetch all tokens and track indices
|
||||
for (const poolAddress of workingPools) {
|
||||
try {
|
||||
const tokensInPool = await publicClient.readContract({
|
||||
address: poolAddress,
|
||||
abi: IPartyPoolABI,
|
||||
functionName: 'allTokens',
|
||||
}) as readonly `0x${string}`[];
|
||||
// For each working pool, process tokens
|
||||
for (let poolIdx = 0; poolIdx < workingPools.length; poolIdx++) {
|
||||
const poolAddress = workingPools[poolIdx];
|
||||
const poolTokensResult = poolTokensResults[poolIdx];
|
||||
|
||||
// Find the input token index in this pool
|
||||
const inputTokenIndex = tokensInPool.findIndex(
|
||||
(token) => token.toLowerCase() === tokenAddress.toLowerCase()
|
||||
);
|
||||
if (poolTokensResult.status !== 'success') {
|
||||
console.error('Failed to fetch tokens for pool', poolAddress);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inputTokenIndex === -1) {
|
||||
console.error('Input token not found in pool', poolAddress);
|
||||
const tokensInPool = poolTokensResult.result as readonly `0x${string}`[];
|
||||
|
||||
// Find the input token index in this pool
|
||||
const inputTokenIndex = tokensInPool.findIndex(
|
||||
(token) => token.toLowerCase() === tokenAddress.toLowerCase()
|
||||
);
|
||||
|
||||
if (inputTokenIndex === -1) {
|
||||
console.error('Input token not found in pool', poolAddress);
|
||||
continue;
|
||||
}
|
||||
|
||||
const inputTokenData = tokenDataMap.get(tokenAddress.toLowerCase());
|
||||
const inputTokenDecimal = inputTokenData?.decimals ?? null;
|
||||
|
||||
// Process each token in the pool
|
||||
for (let outputTokenIndex = 0; outputTokenIndex < tokensInPool.length; outputTokenIndex++) {
|
||||
const outputTokenAddress = tokensInPool[outputTokenIndex];
|
||||
|
||||
// Skip if it's the same as the input token
|
||||
if (outputTokenIndex === inputTokenIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process each token in the pool
|
||||
for (let outputTokenIndex = 0; outputTokenIndex < tokensInPool.length; outputTokenIndex++) {
|
||||
const outputTokenAddress = tokensInPool[outputTokenIndex];
|
||||
const outputTokenData = tokenDataMap.get(outputTokenAddress.toLowerCase());
|
||||
const outputTokenSymbol = outputTokenData?.symbol ?? null;
|
||||
const outputTokenDecimal = outputTokenData?.decimals ?? null;
|
||||
|
||||
// Skip if it's the same as the input token
|
||||
if (outputTokenIndex === inputTokenIndex) {
|
||||
continue;
|
||||
}
|
||||
// Skip tokens with the same symbol as the selected token
|
||||
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the symbol of this token
|
||||
const outputTokenSymbol = await publicClient.readContract({
|
||||
// Skip tokens if decimals failed to load
|
||||
if (inputTokenDecimal === null || outputTokenDecimal === null) {
|
||||
console.error(`Failed to load decimals for token ${outputTokenAddress} or ${tokenAddress}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create or update the available token entry
|
||||
const tokenKey = outputTokenAddress.toLowerCase();
|
||||
if (!tokenRoutesMap.has(tokenKey)) {
|
||||
tokenRoutesMap.set(tokenKey, {
|
||||
address: outputTokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
}).catch(() => null);
|
||||
|
||||
const inputTokenDecimal = await publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
}).catch(() => null);
|
||||
|
||||
const outputTokenDecimal = await publicClient.readContract({
|
||||
address: outputTokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
}).catch(() => null);
|
||||
|
||||
// Skip tokens with the same symbol as the selected token
|
||||
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip tokens if decimals failed to load
|
||||
if (inputTokenDecimal === null || outputTokenDecimal === null) {
|
||||
console.error(`Failed to load decimals for token ${outputTokenAddress} or ${tokenAddress}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create or update the available token entry
|
||||
const tokenKey = outputTokenAddress.toLowerCase();
|
||||
if (!tokenRoutesMap.has(tokenKey)) {
|
||||
tokenRoutesMap.set(tokenKey, {
|
||||
address: outputTokenAddress,
|
||||
symbol: outputTokenSymbol,
|
||||
swapRoutes: [],
|
||||
});
|
||||
}
|
||||
|
||||
// Add this swap route
|
||||
tokenRoutesMap.get(tokenKey)!.swapRoutes.push({
|
||||
poolAddress,
|
||||
inputTokenIndex,
|
||||
outputTokenIndex,
|
||||
inputTokenDecimal,
|
||||
outputTokenDecimal,
|
||||
symbol: outputTokenSymbol,
|
||||
swapRoutes: [],
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching tokens from pool', poolAddress, err);
|
||||
|
||||
// Add this swap route
|
||||
tokenRoutesMap.get(tokenKey)!.swapRoutes.push({
|
||||
poolAddress,
|
||||
inputTokenIndex,
|
||||
outputTokenIndex,
|
||||
inputTokenDecimal,
|
||||
outputTokenDecimal,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,55 +369,54 @@ export function useTokenDetails(userAddress: `0x${string}` | undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build multicall contracts array - 4 calls per token (name, symbol, decimals, balanceOf)
|
||||
const contracts = tokens.flatMap((tokenAddress) => [
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'name',
|
||||
},
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
},
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
},
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [userAddress],
|
||||
},
|
||||
]);
|
||||
|
||||
// Execute multicall
|
||||
const results = await publicClient.multicall({
|
||||
contracts: contracts as any,
|
||||
allowFailure: true,
|
||||
});
|
||||
|
||||
// Parse results
|
||||
const details: TokenDetails[] = [];
|
||||
|
||||
// Make individual calls for each token
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const tokenAddress = tokens[i];
|
||||
try {
|
||||
const [name, symbol, decimals, balance] = await Promise.all([
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'name',
|
||||
}).catch(() => 'Unknown'),
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
}).catch(() => '???'),
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
}).catch(() => 18),
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [userAddress],
|
||||
}).catch(() => BigInt(0)),
|
||||
]);
|
||||
const baseIndex = i * 4;
|
||||
const nameResult = results[baseIndex];
|
||||
const symbolResult = results[baseIndex + 1];
|
||||
const decimalsResult = results[baseIndex + 2];
|
||||
const balanceResult = results[baseIndex + 3];
|
||||
|
||||
details.push({
|
||||
address: tokenAddress,
|
||||
name: name as string,
|
||||
symbol: symbol as string,
|
||||
decimals: Number(decimals),
|
||||
balance: balance as bigint,
|
||||
index: i,
|
||||
});
|
||||
} catch (err) {
|
||||
// Add token with fallback values if individual call fails
|
||||
details.push({
|
||||
address: tokenAddress,
|
||||
name: 'Unknown',
|
||||
symbol: '???',
|
||||
decimals: 18,
|
||||
balance: BigInt(0),
|
||||
index: i,
|
||||
});
|
||||
}
|
||||
details.push({
|
||||
address: tokens[i],
|
||||
name: nameResult.status === 'success' ? (nameResult.result as string) : 'Unknown',
|
||||
symbol: symbolResult.status === 'success' ? (symbolResult.result as string) : '???',
|
||||
decimals: decimalsResult.status === 'success' ? Number(decimalsResult.result) : 18,
|
||||
balance: balanceResult.status === 'success' ? (balanceResult.result as bigint) : BigInt(0),
|
||||
index: i,
|
||||
});
|
||||
}
|
||||
|
||||
setTokenDetails(details);
|
||||
@@ -525,11 +567,9 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
||||
priceStr = `$${finalPrice.toFixed(4)}`;
|
||||
}
|
||||
|
||||
// Calculate TVL (approximate by getting first token balance and multiplying by 3)
|
||||
// Calculate TVL (approximate by getting first token balance and multiplying by number of tokens)
|
||||
const tokenBalance = Number(balance) / Math.pow(10, decimals);
|
||||
|
||||
console.log('tokenBalance', tokenBalance);
|
||||
const approximateTVL = tokenBalance * 3;
|
||||
const approximateTVL = tokenBalance * tokens.length;
|
||||
tvlStr = formatTVL(approximateTVL);
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user