using viem for the uniswap quote and hiding the component since its not ready

This commit is contained in:
2025-12-10 17:03:13 -04:00
parent a2a036818d
commit 1ac26aeec0
2 changed files with 89 additions and 66 deletions

View File

@@ -6,7 +6,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ArrowDownUp, ChevronDown, Settings, CheckCircle, XCircle, Loader2 } from 'lucide-react'; 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 { useTokenDetails, useGetPoolsByToken, type TokenDetails } from '@/hooks/usePartyPlanner';
import { useSwapAmounts, useSwap, selectBestSwapRoute, type ActualSwapAmounts } from '@/hooks/usePartyPool'; import { useSwapAmounts, useSwap, selectBestSwapRoute, type ActualSwapAmounts } from '@/hooks/usePartyPool';
import { formatUnits, parseUnits } from 'viem'; import { formatUnits, parseUnits } from 'viem';
@@ -18,6 +18,7 @@ type TransactionStatus = 'idle' | 'pending' | 'success' | 'error';
export function SwapForm() { export function SwapForm() {
const { t } = useTranslation(); const { t } = useTranslation();
const { isConnected, address } = useAccount(); const { isConnected, address } = useAccount();
const chainId = useChainId();
const [fromAmount, setFromAmount] = useState(''); const [fromAmount, setFromAmount] = useState('');
const [toAmount, setToAmount] = useState(''); const [toAmount, setToAmount] = useState('');
const [selectedFromToken, setSelectedFromToken] = useState<TokenDetails | null>(null); const [selectedFromToken, setSelectedFromToken] = useState<TokenDetails | null>(null);
@@ -405,10 +406,18 @@ export function SwapForm() {
</div> </div>
)} )}
{/* Uniswap Quote */} {/*/!* Uniswap Quote - Hidden (under construction) *!/*/}
{fromAmount && ( {/*{false && fromAmount && selectedFromToken && selectedToToken && (*/}
<UniswapQuote amountIn={fromAmount} /> {/* <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 */} {/* Gas Estimate, Slippage, and Fees */}
{isConnected && fromAmount && toAmount && ( {isConnected && fromAmount && toAmount && (

View File

@@ -1,38 +1,70 @@
'use client'; 'use client';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { parseUnits, formatUnits } from 'viem';
// ETH and SHIB addresses
const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
const SHIB_ADDRESS = '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE';
interface UniswapQuoteProps { interface UniswapQuoteProps {
amountIn: string; amountIn: string;
tokenInAddress: string | null;
tokenOutAddress: string | null;
tokenInDecimals: number;
tokenOutDecimals: number;
tokenOutSymbol: string;
chainId: number;
} }
interface TokenPrices { interface TokenPrices {
eth: number; [key: string]: number;
shib: number;
} }
export default function UniswapQuote({ amountIn }: UniswapQuoteProps) { export default function UniswapQuote({
amountIn,
tokenInAddress,
tokenOutAddress,
tokenInDecimals,
tokenOutDecimals,
tokenOutSymbol,
chainId
}: UniswapQuoteProps) {
const [quote, setQuote] = useState<any>(null); const [quote, setQuote] = useState<any>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(''); const [error, setError] = useState('');
const [prices, setPrices] = useState<TokenPrices | null>(null); const [prices, setPrices] = useState<TokenPrices | null>(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(() => { useEffect(() => {
if (!tokenInAddress || !tokenOutAddress) return;
const fetchPrices = async () => { const fetchPrices = async () => {
try { try {
const response = await fetch( // Fetch both token prices separately using the correct endpoint
'https://api.coingecko.com/api/v3/simple/price?ids=ethereum,shiba-inu&vs_currencies=usd' const [tokenInResponse, tokenOutResponse] = await Promise.all([
); fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenInAddress}&vs_currencies=usd`),
const data = await response.json(); 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({ setPrices({
eth: data.ethereum.usd, tokenIn: tokenInPrice,
shib: data['shiba-inu'].usd tokenOut: tokenOutPrice
}); });
console.log('Token prices:', { tokenInPrice, tokenOutPrice, tokenInData, tokenOutData });
} catch (err) { } catch (err) {
console.error('Failed to fetch prices:', err); console.error('Failed to fetch prices:', err);
} }
@@ -42,7 +74,7 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
// Refresh prices every 30 seconds // Refresh prices every 30 seconds
const interval = setInterval(fetchPrices, 30000); const interval = setInterval(fetchPrices, 30000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, []); }, [tokenInAddress, tokenOutAddress]);
const getQuote = async () => { const getQuote = async () => {
if (!amountIn || parseFloat(amountIn) <= 0) { if (!amountIn || parseFloat(amountIn) <= 0) {
@@ -54,8 +86,8 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
setError(''); setError('');
try { try {
// Convert ETH amount to wei // Convert amount to smallest unit based on token decimals using viem
const amountInWei = (parseFloat(amountIn) * 1e18).toString(); const amountInSmallestUnit = parseUnits(amountIn, tokenInDecimals).toString();
const response = await fetch('https://api.uniswap.org/v2/quote', { const response = await fetch('https://api.uniswap.org/v2/quote', {
method: 'POST', method: 'POST',
@@ -64,11 +96,11 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
'Origin': 'https://app.uniswap.org' 'Origin': 'https://app.uniswap.org'
}, },
body: JSON.stringify({ body: JSON.stringify({
amount: amountInWei, amount: amountInSmallestUnit,
tokenIn: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH tokenIn: tokenInAddress,
tokenInChainId: 1, tokenInChainId: chainId,
tokenOut: SHIB_ADDRESS, tokenOut: tokenOutAddress,
tokenOutChainId: 1, tokenOutChainId: chainId,
type: 'EXACT_INPUT', type: 'EXACT_INPUT',
configs: [ configs: [
{ {
@@ -92,8 +124,11 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
} }
}; };
const formatSHIB = (amount: string) => { const formatTokenAmount = (amount: string) => {
return (parseFloat(amount) / 1e18).toLocaleString('en-US', { maximumFractionDigits: 0 }); const formatted = formatUnits(BigInt(amount), tokenOutDecimals);
return parseFloat(formatted).toLocaleString('en-US', {
maximumFractionDigits: tokenOutDecimals >= 18 ? 0 : 6
});
}; };
const getQuoteAmount = () => { const getQuoteAmount = () => {
@@ -103,10 +138,10 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
}; };
const calculateCostBreakdown = () => { const calculateCostBreakdown = () => {
if (!quote || !prices) return null; if (!quote || !prices || !prices.tokenIn) return null;
const ethAmount = parseFloat(amountIn); const tokenAmount = parseFloat(amountIn);
const tradeValueUSD = ethAmount * prices.eth; const tradeValueUSD = tokenAmount * prices.tokenIn;
// Access nested quote object // Access nested quote object
const quoteData = quote.quote || quote; const quoteData = quote.quote || quote;
@@ -114,21 +149,15 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
// 1. Gas Cost // 1. Gas Cost
const gasCostUSD = parseFloat(quoteData.gasUseEstimateUSD || '0'); const gasCostUSD = parseFloat(quoteData.gasUseEstimateUSD || '0');
// 2. Price Impact // 2. Uniswap UX Fee (0.25%)
const priceImpactPercent = parseFloat(quoteData.priceImpact || '0'); const uniswapFeePercent = 0.25;
const priceImpactUSD = (priceImpactPercent / 100) * tradeValueUSD; const uniswapFeeUSD = (uniswapFeePercent / 100) * tradeValueUSD;
// 3. Trading Fees (estimate 0.3% for typical Uniswap pools) const totalCostUSD = gasCostUSD + uniswapFeeUSD;
const tradingFeePercent = 0.3;
const tradingFeeUSD = (tradingFeePercent / 100) * tradeValueUSD;
const totalCostUSD = gasCostUSD + priceImpactUSD + tradingFeeUSD;
console.log('Cost breakdown calc:', { console.log('Cost breakdown calc:', {
gasCostUSD, gasCostUSD,
priceImpactPercent, uniswapFeeUSD,
priceImpactUSD,
tradingFeeUSD,
totalCostUSD, totalCostUSD,
tradeValueUSD, tradeValueUSD,
quoteData quoteData
@@ -136,10 +165,8 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
return { return {
gasCostUSD, gasCostUSD,
priceImpactPercent, uniswapFeePercent,
priceImpactUSD, uniswapFeeUSD,
tradingFeePercent,
tradingFeeUSD,
totalCostUSD, totalCostUSD,
tradeValueUSD tradeValueUSD
}; };
@@ -166,9 +193,9 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
{quote && costBreakdown && ( {quote && costBreakdown && (
<div className="mt-4 space-y-3"> <div className="mt-4 space-y-3">
<div className="p-3 bg-gray-50 rounded"> <div className="p-3 bg-gray-50 rounded">
<div className="text-sm text-gray-600">You Get (SHIB)</div> <div className="text-sm text-gray-600">You Get ({tokenOutSymbol})</div>
<div className="text-xl font-bold"> <div className="text-xl font-bold">
{formatSHIB(getQuoteAmount())} {formatTokenAmount(getQuoteAmount())}
</div> </div>
</div> </div>
@@ -186,29 +213,16 @@ export default function UniswapQuote({ amountIn }: UniswapQuoteProps) {
</div> </div>
</div> </div>
{/* 2. Price Impact */} {/* 2. Uniswap UX Fee */}
<div className="space-y-1"> <div className="space-y-1">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="text-sm font-medium text-gray-700">2. Price Impact</span> <span className="text-sm font-medium text-gray-700">2. Uniswap UX Fee</span>
<span className="text-sm font-bold text-gray-900"> <span className="text-sm font-bold text-gray-900">
{costBreakdown.priceImpactPercent.toFixed(2)}% {costBreakdown.uniswapFeePercent}%
</span> </span>
</div> </div>
<div className="text-xs text-gray-600 pl-4"> <div className="text-xs text-gray-600 pl-4">
Cost: ~${costBreakdown.priceImpactUSD.toFixed(2)} USD ${costBreakdown.uniswapFeeUSD.toFixed(2)} USD
</div>
</div>
{/* 3. Trading Fees */}
<div className="space-y-1">
<div className="flex justify-between items-center">
<span className="text-sm font-medium text-gray-700">3. Trading Fees (DEX Fees)</span>
<span className="text-sm font-bold text-gray-900">
{costBreakdown.tradingFeePercent}%
</span>
</div>
<div className="text-xs text-gray-600 pl-4">
${costBreakdown.tradingFeeUSD.toFixed(2)} USD
</div> </div>
</div> </div>