diff --git a/src/components/swap-form.tsx b/src/components/swap-form.tsx index a60788f..07c88d5 100644 --- a/src/components/swap-form.tsx +++ b/src/components/swap-form.tsx @@ -11,6 +11,7 @@ import { useTokenDetails, useGetPoolsByToken, type TokenDetails } from '@/hooks/ 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'; @@ -404,6 +405,11 @@ export function SwapForm() { )} + {/* Uniswap Quote */} + {fromAmount && ( + + )} + {/* Gas Estimate, Slippage, and Fees */} {isConnected && fromAmount && toAmount && (
diff --git a/src/components/uniswap-quote.tsx b/src/components/uniswap-quote.tsx new file mode 100644 index 0000000..7236b2b --- /dev/null +++ b/src/components/uniswap-quote.tsx @@ -0,0 +1,234 @@ +'use client'; + +import { useState, useEffect } from 'react'; + +// ETH and SHIB addresses +const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; +const SHIB_ADDRESS = '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE'; + +interface UniswapQuoteProps { + amountIn: string; +} + +interface TokenPrices { + eth: number; + shib: number; +} + +export default function UniswapQuote({ amountIn }: 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 + useEffect(() => { + 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(); + setPrices({ + eth: data.ethereum.usd, + shib: data['shiba-inu'].usd + }); + } catch (err) { + console.error('Failed to fetch prices:', err); + } + }; + + fetchPrices(); + // Refresh prices every 30 seconds + const interval = setInterval(fetchPrices, 30000); + return () => clearInterval(interval); + }, []); + + const getQuote = async () => { + if (!amountIn || parseFloat(amountIn) <= 0) { + setError('Please enter a valid amount'); + return; + } + + setLoading(true); + setError(''); + + try { + // Convert ETH amount to wei + const amountInWei = (parseFloat(amountIn) * 1e18).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: amountInWei, + tokenIn: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH + tokenInChainId: 1, + tokenOut: SHIB_ADDRESS, + tokenOutChainId: 1, + 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 formatSHIB = (amount: string) => { + return (parseFloat(amount) / 1e18).toLocaleString('en-US', { maximumFractionDigits: 0 }); + }; + + const getQuoteAmount = () => { + if (!quote) return '0'; + // Handle nested quote structure + return quote.quote?.quote || quote.quote || '0'; + }; + + const calculateCostBreakdown = () => { + if (!quote || !prices) return null; + + const ethAmount = parseFloat(amountIn); + const tradeValueUSD = ethAmount * prices.eth; + + // Access nested quote object + const quoteData = quote.quote || quote; + + // 1. Gas Cost + const gasCostUSD = parseFloat(quoteData.gasUseEstimateUSD || '0'); + + // 2. Price Impact + const priceImpactPercent = parseFloat(quoteData.priceImpact || '0'); + const priceImpactUSD = (priceImpactPercent / 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; + + console.log('Cost breakdown calc:', { + gasCostUSD, + priceImpactPercent, + priceImpactUSD, + tradingFeeUSD, + totalCostUSD, + tradeValueUSD, + quoteData + }); + + return { + gasCostUSD, + priceImpactPercent, + priceImpactUSD, + tradingFeePercent, + tradingFeeUSD, + totalCostUSD, + tradeValueUSD + }; + }; + + const costBreakdown = calculateCostBreakdown(); + + return ( +
+ + + {error && ( +
+ {error} +
+ )} + + {quote && costBreakdown && ( +
+
+
You Get (SHIB)
+
+ {formatSHIB(getQuoteAmount())} +
+
+ + {/* Total Costs Breakdown */} +
+

Total Costs Breakdown

+ + {/* 1. Gas Cost */} +
+
+ 1. Gas Cost (Network Fee) + + ${costBreakdown.gasCostUSD.toFixed(2)} USD + +
+
+ + {/* 2. Price Impact */} +
+
+ 2. Price Impact + + {costBreakdown.priceImpactPercent.toFixed(2)}% + +
+
+ Cost: ~${costBreakdown.priceImpactUSD.toFixed(2)} USD +
+
+ + {/* 3. Trading Fees */} +
+
+ 3. Trading Fees (DEX Fees) + + {costBreakdown.tradingFeePercent}% + +
+
+ ${costBreakdown.tradingFeeUSD.toFixed(2)} USD +
+
+ + {/* Total */} +
+
+ Total Estimated Cost + + ${costBreakdown.totalCostUSD.toFixed(2)} USD + +
+
+ + {/* Trade Value Reference */} +
+ Trade Value: ${costBreakdown.tradeValueUSD.toFixed(2)} USD +
+
+
+ )} +
+ ); +}