using viem for the uniswap quote and hiding the component since its not ready
This commit is contained in:
@@ -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 && (
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user