diff --git a/src/components/swap-form.tsx b/src/components/swap-form.tsx index 07c88d5..565696b 100644 --- a/src/components/swap-form.tsx +++ b/src/components/swap-form.tsx @@ -6,7 +6,7 @@ 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'; @@ -18,6 +18,7 @@ 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(null); @@ -405,10 +406,18 @@ export function SwapForm() { )} - {/* Uniswap Quote */} - {fromAmount && ( - - )} + {/*/!* Uniswap Quote - Hidden (under construction) *!/*/} + {/*{false && fromAmount && selectedFromToken && selectedToToken && (*/} + {/* */} + {/*)}*/} {/* Gas Estimate, Slippage, and Fees */} {isConnected && fromAmount && toAmount && ( diff --git a/src/components/uniswap-quote.tsx b/src/components/uniswap-quote.tsx index 7236b2b..4141684 100644 --- a/src/components/uniswap-quote.tsx +++ b/src/components/uniswap-quote.tsx @@ -1,38 +1,70 @@ 'use client'; import { useState, useEffect } from 'react'; - -// ETH and SHIB addresses -const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; -const SHIB_ADDRESS = '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE'; +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 { - eth: number; - shib: number; + [key: string]: number; } -export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { +export default function UniswapQuote({ + amountIn, + tokenInAddress, + tokenOutAddress, + tokenInDecimals, + tokenOutDecimals, + tokenOutSymbol, + chainId +}: UniswapQuoteProps) { const [quote, setQuote] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [prices, setPrices] = useState(null); - // Fetch token prices from CoinGecko + // 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 { - const response = await fetch( - 'https://api.coingecko.com/api/v3/simple/price?ids=ethereum,shiba-inu&vs_currencies=usd' - ); - const data = await response.json(); + // 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({ - eth: data.ethereum.usd, - shib: data['shiba-inu'].usd + tokenIn: tokenInPrice, + tokenOut: tokenOutPrice }); + + console.log('Token prices:', { tokenInPrice, tokenOutPrice, tokenInData, tokenOutData }); } catch (err) { console.error('Failed to fetch prices:', err); } @@ -42,7 +74,7 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { // Refresh prices every 30 seconds const interval = setInterval(fetchPrices, 30000); return () => clearInterval(interval); - }, []); + }, [tokenInAddress, tokenOutAddress]); const getQuote = async () => { if (!amountIn || parseFloat(amountIn) <= 0) { @@ -54,8 +86,8 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { setError(''); try { - // Convert ETH amount to wei - const amountInWei = (parseFloat(amountIn) * 1e18).toString(); + // 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', @@ -64,11 +96,11 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { 'Origin': 'https://app.uniswap.org' }, body: JSON.stringify({ - amount: amountInWei, - tokenIn: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH - tokenInChainId: 1, - tokenOut: SHIB_ADDRESS, - tokenOutChainId: 1, + amount: amountInSmallestUnit, + tokenIn: tokenInAddress, + tokenInChainId: chainId, + tokenOut: tokenOutAddress, + tokenOutChainId: chainId, type: 'EXACT_INPUT', configs: [ { @@ -92,8 +124,11 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { } }; - const formatSHIB = (amount: string) => { - return (parseFloat(amount) / 1e18).toLocaleString('en-US', { maximumFractionDigits: 0 }); + const formatTokenAmount = (amount: string) => { + const formatted = formatUnits(BigInt(amount), tokenOutDecimals); + return parseFloat(formatted).toLocaleString('en-US', { + maximumFractionDigits: tokenOutDecimals >= 18 ? 0 : 6 + }); }; const getQuoteAmount = () => { @@ -103,10 +138,10 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { }; const calculateCostBreakdown = () => { - if (!quote || !prices) return null; + if (!quote || !prices || !prices.tokenIn) return null; - const ethAmount = parseFloat(amountIn); - const tradeValueUSD = ethAmount * prices.eth; + const tokenAmount = parseFloat(amountIn); + const tradeValueUSD = tokenAmount * prices.tokenIn; // Access nested quote object const quoteData = quote.quote || quote; @@ -114,21 +149,15 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { // 1. Gas Cost const gasCostUSD = parseFloat(quoteData.gasUseEstimateUSD || '0'); - // 2. Price Impact - const priceImpactPercent = parseFloat(quoteData.priceImpact || '0'); - const priceImpactUSD = (priceImpactPercent / 100) * tradeValueUSD; + // 2. Uniswap UX Fee (0.25%) + const uniswapFeePercent = 0.25; + const uniswapFeeUSD = (uniswapFeePercent / 100) * tradeValueUSD; - // 3. Trading Fees (estimate 0.3% for typical Uniswap pools) - const tradingFeePercent = 0.3; - const tradingFeeUSD = (tradingFeePercent / 100) * tradeValueUSD; - - const totalCostUSD = gasCostUSD + priceImpactUSD + tradingFeeUSD; + const totalCostUSD = gasCostUSD + uniswapFeeUSD; console.log('Cost breakdown calc:', { gasCostUSD, - priceImpactPercent, - priceImpactUSD, - tradingFeeUSD, + uniswapFeeUSD, totalCostUSD, tradeValueUSD, quoteData @@ -136,10 +165,8 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { return { gasCostUSD, - priceImpactPercent, - priceImpactUSD, - tradingFeePercent, - tradingFeeUSD, + uniswapFeePercent, + uniswapFeeUSD, totalCostUSD, tradeValueUSD }; @@ -166,9 +193,9 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { {quote && costBreakdown && (
-
You Get (SHIB)
+
You Get ({tokenOutSymbol})
- {formatSHIB(getQuoteAmount())} + {formatTokenAmount(getQuoteAmount())}
@@ -186,29 +213,16 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
- {/* 2. Price Impact */} + {/* 2. Uniswap UX Fee */}
- 2. Price Impact + 2. Uniswap UX Fee - {costBreakdown.priceImpactPercent.toFixed(2)}% + {costBreakdown.uniswapFeePercent}%
- Cost: ~${costBreakdown.priceImpactUSD.toFixed(2)} USD -
-
- - {/* 3. Trading Fees */} -
-
- 3. Trading Fees (DEX Fees) - - {costBreakdown.tradingFeePercent}% - -
-
- ${costBreakdown.tradingFeeUSD.toFixed(2)} USD + ${costBreakdown.uniswapFeeUSD.toFixed(2)} USD