Files
pwap/web/src/components/PoolDashboard.tsx
T
pezkuwichain 1e2efbac99 fix: correct LP fee from 3% to 0.3%
Runtime LPFee = 3 means 3/1000 = 0.3%, not 3%.
Updated AMM calculations in TokenSwap, SwapInterface, and PoolDashboard.
2026-02-04 21:53:35 +03:00

647 lines
27 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
import { TrendingUp, Droplet, DollarSign, Percent, Info, AlertTriangle, BarChart3, Clock } from 'lucide-react';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { usePezkuwi } from '@/contexts/PezkuwiContext';
import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet';
import { NATIVE_TOKEN_ID } from '@/types/dex';
import { AddLiquidityModal } from '@/components/AddLiquidityModal';
import { RemoveLiquidityModal } from '@/components/RemoveLiquidityModal';
// Helper function to convert asset IDs to user-friendly display names
// Users should only see HEZ, PEZ, USDT - wrapped tokens are backend details
const getDisplayTokenName = (assetId: number): string => {
if (assetId === -1) return 'HEZ'; // Native HEZ from relay chain
if (assetId === ASSET_IDS.WHEZ || assetId === 2) return 'wHEZ';
if (assetId === ASSET_IDS.PEZ || assetId === 1) return 'PEZ';
if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 'USDT';
return getAssetSymbol(assetId); // Fallback for other assets
};
interface PoolData {
asset0: number;
asset1: number;
reserve0: number;
reserve1: number;
lpTokenId: number;
poolAccount: string;
}
interface LPPosition {
lpTokenBalance: number;
share: number; // Percentage of pool
asset0Amount: number;
asset1Amount: number;
}
const PoolDashboard = () => {
// Use Asset Hub API for DEX operations (assetConversion pallet is on Asset Hub)
const { assetHubApi, isAssetHubReady, selectedAccount } = usePezkuwi();
const [poolData, setPoolData] = useState<PoolData | null>(null);
const [lpPosition, setLPPosition] = useState<LPPosition | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [isAddLiquidityModalOpen, setIsAddLiquidityModalOpen] = useState(false);
const [isRemoveLiquidityModalOpen, setIsRemoveLiquidityModalOpen] = useState(false);
// Pool selection state
const [availablePools, setAvailablePools] = useState<Array<[number, number]>>([]);
const [selectedPool, setSelectedPool] = useState<string>('0-1'); // Default: wHEZ/PEZ
// Helper to convert asset ID to XCM Location format (same as CreatePoolModal)
const formatAssetId = (id: number) => {
if (id === NATIVE_TOKEN_ID) {
// Native token from relay chain - XCM location format
return { parents: 1, interior: 'Here' };
}
// Asset on Asset Hub - XCM location format with PalletInstance 50 (assets pallet)
return { parents: 0, interior: { X2: [{ PalletInstance: 50 }, { GeneralIndex: id }] } };
};
// Discover available pools
useEffect(() => {
if (!assetHubApi || !isAssetHubReady) return;
const discoverPools = async () => {
try {
// All possible pool combinations
const possiblePools: Array<[number, number]> = [
// Native HEZ pools
[NATIVE_TOKEN_ID, ASSET_IDS.PEZ], // Native HEZ / PEZ
[NATIVE_TOKEN_ID, ASSET_IDS.WUSDT], // Native HEZ / wUSDT
[NATIVE_TOKEN_ID, ASSET_IDS.WHEZ], // Native HEZ / wHEZ
// wHEZ pools
[ASSET_IDS.WHEZ, ASSET_IDS.PEZ], // wHEZ / PEZ
[ASSET_IDS.WHEZ, ASSET_IDS.WUSDT], // wHEZ / wUSDT
// PEZ pools
[ASSET_IDS.PEZ, ASSET_IDS.WUSDT], // PEZ / wUSDT
];
const existingPools: Array<[number, number]> = [];
for (const [asset0, asset1] of possiblePools) {
try {
// Use XCM Location format for pool queries
const poolKey = [formatAssetId(asset0), formatAssetId(asset1)];
const poolInfo = await assetHubApi.query.assetConversion.pools(poolKey);
if ((poolInfo as { isSome: boolean }).isSome) {
existingPools.push([asset0, asset1]);
if (import.meta.env.DEV) {
console.log(`✅ Found pool: ${asset0}-${asset1}`);
}
}
} catch (err) {
// Skip pools that error out (likely don't exist)
if (import.meta.env.DEV) {
console.log(`❌ Pool ${asset0}-${asset1} not found or error:`, err);
}
}
}
if (import.meta.env.DEV) {
console.log('📊 Total pools found:', existingPools.length, existingPools);
}
setAvailablePools(existingPools);
// Set default pool to first available if current selection doesn't exist
if (existingPools.length > 0) {
const currentPoolKey = selectedPool;
const poolExists = existingPools.some(
([a0, a1]) => `${a0}-${a1}` === currentPoolKey
);
if (!poolExists) {
const [firstAsset0, firstAsset1] = existingPools[0];
setSelectedPool(`${firstAsset0}-${firstAsset1}`);
}
}
} catch (err) {
if (import.meta.env.DEV) console.error('Error discovering pools:', err);
}
};
discoverPools();
}, [assetHubApi, isAssetHubReady, selectedPool]);
// Fetch pool data
useEffect(() => {
if (!assetHubApi || !isAssetHubReady || !selectedPool) return;
const fetchPoolData = async () => {
setIsLoading(true);
setError(null);
try {
// Parse selected pool (e.g., "-1-1" -> [-1, 1] for Native HEZ / PEZ)
const [asset1Str, asset2Str] = selectedPool.split('-').filter(s => s !== '');
const asset1 = selectedPool.startsWith('-') ? -parseInt(asset1Str) : parseInt(asset1Str);
const asset2 = parseInt(asset2Str);
// Use XCM Location format for pool query
const poolKey = [formatAssetId(asset1), formatAssetId(asset2)];
const poolInfo = await assetHubApi.query.assetConversion.pools(poolKey);
if ((poolInfo as { isSome: boolean }).isSome) {
const lpTokenData = (poolInfo as { unwrap: () => { toJSON: () => Record<string, unknown> } }).unwrap().toJSON();
const lpTokenId = lpTokenData.lpToken as number;
// Get decimals for each asset
const getAssetDecimals = (assetId: number): number => {
if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 6; // wUSDT has 6 decimals
return 12; // Native, wHEZ, PEZ have 12 decimals
};
const asset1Decimals = getAssetDecimals(asset1);
const asset2Decimals = getAssetDecimals(asset2);
// Use runtime API to get reserves via price quote
// Query the price for 1 unit to determine if pool has liquidity
let reserve0 = 0;
let reserve1 = 0;
try {
// Use quotePriceExactTokensForTokens to check pool liquidity
const oneUnit1 = BigInt(Math.pow(10, asset1Decimals));
const quote1 = await assetHubApi.call.assetConversionApi.quotePriceExactTokensForTokens(
formatAssetId(asset1),
formatAssetId(asset2),
oneUnit1.toString(),
true // include fee
);
if (quote1 && !(quote1 as { isNone?: boolean }).isNone) {
const outputForOneUnit = Number((quote1 as { unwrap: () => { toString: () => string } }).unwrap().toString());
// Calculate approximate reserves based on price
// This is an approximation - actual reserves would need pool account query
// Price = reserve1 / reserve0, so if we have the price ratio, we can estimate
const price = outputForOneUnit / Math.pow(10, asset2Decimals);
// Try to get LP token total supply to estimate pool size
const lpAssetData = await assetHubApi.query.poolAssets.asset(lpTokenId);
if ((lpAssetData as { isSome: boolean }).isSome) {
const assetInfo = (lpAssetData as { unwrap: () => { toJSON: () => Record<string, unknown> } }).unwrap().toJSON();
const totalLpSupply = Number(assetInfo.supply) / 1e12;
// Estimate reserves: LP supply is approximately sqrt(reserve0 * reserve1)
// With known price ratio, we can solve for individual reserves
// reserve0 * reserve1 = lpSupply^2
// reserve1 / reserve0 = price
// Therefore: reserve0 = lpSupply / sqrt(price), reserve1 = lpSupply * sqrt(price)
if (price > 0 && totalLpSupply > 0) {
const sqrtPrice = Math.sqrt(price);
reserve0 = totalLpSupply / sqrtPrice;
reserve1 = totalLpSupply * sqrtPrice;
}
}
}
} catch (err) {
if (import.meta.env.DEV) console.warn('Could not fetch reserves via runtime API:', err);
}
const poolAccount = 'Pool Account (derived)';
setPoolData({
asset0: asset1,
asset1: asset2,
reserve0,
reserve1,
lpTokenId,
poolAccount,
});
// Get user's LP position if account connected
if (selectedAccount) {
await fetchLPPosition(lpTokenId, reserve0, reserve1);
}
} else {
setError('Pool not found');
}
} catch (err) {
if (import.meta.env.DEV) console.error('Error fetching pool data:', err);
setError(err instanceof Error ? err.message : 'Failed to fetch pool data');
} finally {
setIsLoading(false);
}
};
const fetchLPPosition = async (lpTokenId: number, reserve0: number, reserve1: number) => {
if (!assetHubApi || !selectedAccount) return;
try {
// Query user's LP token balance from poolAssets pallet on Asset Hub
const lpBalance = await assetHubApi.query.poolAssets.account(lpTokenId, selectedAccount.address);
if ((lpBalance as { isSome: boolean }).isSome) {
const lpData = (lpBalance as { unwrap: () => { toJSON: () => Record<string, unknown> } }).unwrap().toJSON();
const userLpBalance = Number(lpData.balance) / 1e12;
// Query total LP supply
const lpAssetData = await assetHubApi.query.poolAssets.asset(lpTokenId);
if ((lpAssetData as { isSome: boolean }).isSome) {
const assetInfo = (lpAssetData as { unwrap: () => { toJSON: () => Record<string, unknown> } }).unwrap().toJSON();
const totalSupply = Number(assetInfo.supply) / 1e12;
// Calculate user's share
const sharePercentage = totalSupply > 0 ? (userLpBalance / totalSupply) * 100 : 0;
// Calculate user's actual token amounts
const asset0Amount = (sharePercentage / 100) * reserve0;
const asset1Amount = (sharePercentage / 100) * reserve1;
setLPPosition({
lpTokenBalance: userLpBalance,
share: sharePercentage,
asset0Amount,
asset1Amount,
});
}
}
} catch (err) {
if (import.meta.env.DEV) console.error('Error fetching LP position:', err);
}
};
fetchPoolData();
// Refresh every 30 seconds
const interval = setInterval(fetchPoolData, 30000);
return () => clearInterval(interval);
}, [assetHubApi, isAssetHubReady, selectedAccount, selectedPool]);
// Calculate metrics
const constantProduct = poolData ? poolData.reserve0 * poolData.reserve1 : 0;
const currentPrice = poolData ? poolData.reserve1 / poolData.reserve0 : 0;
const totalLiquidityUSD = poolData ? poolData.reserve0 * 2 : 0; // Simplified: assumes 1:1 USD peg
// APR calculation (simplified - would need 24h volume data)
const estimateAPR = () => {
if (!poolData) return 0;
// Estimate based on pool size and typical volume
// This is a simplified calculation
// Real APR = (24h fees × 365) / TVL
const dailyVolumeEstimate = totalLiquidityUSD * 0.1; // Assume 10% daily turnover
const dailyFees = dailyVolumeEstimate * 0.003; // 0.3% fee
const annualFees = dailyFees * 365;
const apr = (annualFees / totalLiquidityUSD) * 100;
return apr;
};
// Impermanent loss calculator
const calculateImpermanentLoss = (priceChange: number) => {
// IL formula: 2 * sqrt(price_ratio) / (1 + price_ratio) - 1
const priceRatio = 1 + priceChange / 100;
const il = ((2 * Math.sqrt(priceRatio)) / (1 + priceRatio) - 1) * 100;
return il;
};
if (isLoading && !poolData) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto mb-4"></div>
<p className="text-gray-400">Loading pool data...</p>
</div>
</div>
);
}
if (error) {
return (
<Alert className="bg-red-900/20 border-red-500">
<AlertTriangle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
);
}
if (!poolData) {
return (
<Alert className="bg-yellow-900/20 border-yellow-500">
<Info className="h-4 w-4" />
<AlertDescription>No pool data available</AlertDescription>
</Alert>
);
}
// Get asset symbols for the selected pool (using display names)
const asset0Symbol = poolData ? getDisplayTokenName(poolData.asset0) : '';
const asset1Symbol = poolData ? getDisplayTokenName(poolData.asset1) : '';
return (
<div className="space-y-6">
{/* Pool Selector */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div>
<h3 className="text-sm font-medium text-gray-400 mb-1">Pool Dashboards</h3>
<Select value={selectedPool} onValueChange={setSelectedPool}>
<SelectTrigger className="w-[240px] bg-gray-800/50 border-gray-700">
<SelectValue placeholder="Select pool" />
</SelectTrigger>
<SelectContent>
{availablePools.map(([asset0, asset1]) => {
const symbol0 = getDisplayTokenName(asset0);
const symbol1 = getDisplayTokenName(asset1);
return (
<SelectItem key={`${asset0}-${asset1}`} value={`${asset0}-${asset1}`}>
{symbol0}/{symbol1}
</SelectItem>
);
})}
</SelectContent>
</Select>
</div>
</div>
<Badge variant="outline" className="flex items-center gap-1">
<Clock className="h-3 w-3" />
Live
</Badge>
</div>
{/* Pool Dashboard Title */}
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold text-white flex items-center gap-2">
<Droplet className="h-6 w-6 text-blue-400" />
{asset0Symbol}/{asset1Symbol} Pool Dashboard
</h2>
<p className="text-gray-400 mt-1">Monitor liquidity pool metrics and your position</p>
</div>
</div>
{/* Key Metrics Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{/* Total Liquidity */}
<Card className="p-4 bg-gray-800/50 border-gray-700">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-gray-400">Total Liquidity</p>
<p className="text-2xl font-bold text-white mt-1">
${totalLiquidityUSD.toLocaleString('en-US', { maximumFractionDigits: 0 })}
</p>
<p className="text-xs text-gray-500 mt-1">
{poolData.reserve0.toLocaleString()} {asset0Symbol} + {poolData.reserve1.toLocaleString()} {asset1Symbol}
</p>
</div>
<DollarSign className="h-8 w-8 text-green-400" />
</div>
</Card>
{/* Current Price */}
<Card className="p-4 bg-gray-800/50 border-gray-700">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-gray-400">{asset0Symbol} Price</p>
<p className="text-2xl font-bold text-white mt-1">
${currentPrice.toFixed(4)}
</p>
<p className="text-xs text-gray-500 mt-1">
1 {asset1Symbol} = {(1 / currentPrice).toFixed(4)} {asset0Symbol}
</p>
</div>
<TrendingUp className="h-8 w-8 text-blue-400" />
</div>
</Card>
{/* APR */}
<Card className="p-4 bg-gray-800/50 border-gray-700">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-gray-400">Estimated APR</p>
<p className="text-2xl font-bold text-white mt-1">
{estimateAPR().toFixed(2)}%
</p>
<p className="text-xs text-gray-500 mt-1">
From swap fees
</p>
</div>
<Percent className="h-8 w-8 text-yellow-400" />
</div>
</Card>
{/* Constant Product */}
<Card className="p-4 bg-gray-800/50 border-gray-700">
<div className="flex items-start justify-between">
<div>
<p className="text-sm text-gray-400">Constant (k)</p>
<p className="text-2xl font-bold text-white mt-1">
{(constantProduct / 1e9).toFixed(1)}B
</p>
<p className="text-xs text-gray-500 mt-1">
x × y = k
</p>
</div>
<BarChart3 className="h-8 w-8 text-purple-400" />
</div>
</Card>
</div>
<Tabs defaultValue="reserves" className="w-full">
<TabsList className="grid w-full grid-cols-3 bg-gray-800">
<TabsTrigger value="reserves">Reserves</TabsTrigger>
<TabsTrigger value="position">Your Position</TabsTrigger>
<TabsTrigger value="calculator">IL Calculator</TabsTrigger>
</TabsList>
{/* Reserves Tab */}
<TabsContent value="reserves" className="space-y-4">
<Card className="p-6 bg-gray-800/50 border-gray-700">
<h3 className="text-lg font-semibold text-white mb-4">Pool Reserves</h3>
<div className="space-y-4">
<div className="flex items-center justify-between p-4 bg-gray-900/50 rounded-lg">
<div>
<p className="text-sm text-gray-400">{asset0Symbol} Reserve</p>
<p className="text-2xl font-bold text-white">{poolData.reserve0.toLocaleString('en-US', { maximumFractionDigits: 2 })}</p>
</div>
<Badge variant="outline">Asset 1</Badge>
</div>
<div className="flex items-center justify-between p-4 bg-gray-900/50 rounded-lg">
<div>
<p className="text-sm text-gray-400">{asset1Symbol} Reserve</p>
<p className="text-2xl font-bold text-white">{poolData.reserve1.toLocaleString('en-US', { maximumFractionDigits: 2 })}</p>
</div>
<Badge variant="outline">Asset 2</Badge>
</div>
</div>
<div className="mt-6 p-4 bg-blue-900/20 border border-blue-500/30 rounded-lg">
<div className="flex items-start gap-2">
<Info className="h-4 w-4 text-blue-400 mt-0.5" />
<div className="text-sm text-gray-300">
<p className="font-semibold text-blue-400 mb-1">AMM Formula</p>
<p>Pool maintains constant product: x × y = k</p>
<p className="mt-2 font-mono text-xs">
{poolData.reserve0.toFixed(2)} × {poolData.reserve1.toFixed(2)} = {constantProduct.toLocaleString()}
</p>
</div>
</div>
</div>
</Card>
</TabsContent>
{/* Your Position Tab */}
<TabsContent value="position" className="space-y-4">
<Card className="p-6 bg-gray-800/50 border-gray-700">
<h3 className="text-lg font-semibold text-white mb-4">Your Liquidity Position</h3>
{!selectedAccount ? (
<Alert className="bg-yellow-900/20 border-yellow-500">
<Info className="h-4 w-4" />
<AlertDescription>Connect wallet to view your position</AlertDescription>
</Alert>
) : !lpPosition ? (
<div className="text-center py-8 text-gray-400">
<Droplet className="h-12 w-12 mx-auto mb-2 opacity-50" />
<p>No liquidity position found</p>
<Button
onClick={() => setIsAddLiquidityModalOpen(true)}
className="mt-4 bg-gradient-to-r from-green-600 to-blue-600 hover:from-green-700 hover:to-blue-700"
>
Add Liquidity
</Button>
</div>
) : (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="p-4 bg-gray-900/50 rounded-lg">
<p className="text-sm text-gray-400">LP Tokens</p>
<p className="text-xl font-bold text-white">{lpPosition.lpTokenBalance.toFixed(4)}</p>
</div>
<div className="p-4 bg-gray-900/50 rounded-lg">
<p className="text-sm text-gray-400">Pool Share</p>
<p className="text-xl font-bold text-white">{lpPosition.share.toFixed(4)}%</p>
</div>
</div>
<div className="p-4 bg-gray-900/50 rounded-lg">
<p className="text-sm text-gray-400 mb-2">Your Position Value</p>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-gray-300">{asset0Symbol}:</span>
<span className="text-white font-semibold">{lpPosition.asset0Amount.toFixed(4)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">{asset1Symbol}:</span>
<span className="text-white font-semibold">{lpPosition.asset1Amount.toFixed(4)}</span>
</div>
</div>
</div>
<div className="p-4 bg-green-900/20 border border-green-500/30 rounded-lg">
<p className="text-sm text-gray-400 mb-2">Estimated Earnings (APR {estimateAPR().toFixed(2)}%)</p>
<div className="space-y-1 text-sm">
<div className="flex justify-between">
<span className="text-gray-300">Daily:</span>
<span className="text-green-400">~{((lpPosition.asset0Amount * 2 * estimateAPR()) / 365 / 100).toFixed(4)} {asset0Symbol}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Monthly:</span>
<span className="text-green-400">~{((lpPosition.asset0Amount * 2 * estimateAPR()) / 12 / 100).toFixed(4)} {asset0Symbol}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Yearly:</span>
<span className="text-green-400">~{((lpPosition.asset0Amount * 2 * estimateAPR()) / 100).toFixed(4)} {asset0Symbol}</span>
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-3 pt-2">
<Button
onClick={() => setIsAddLiquidityModalOpen(true)}
className="bg-gradient-to-r from-green-600 to-blue-600 hover:from-green-700 hover:to-blue-700"
>
Add More
</Button>
<Button
onClick={() => setIsRemoveLiquidityModalOpen(true)}
variant="outline"
className="border-red-600 text-red-400 hover:bg-red-900/20"
>
Remove
</Button>
</div>
</div>
)}
</Card>
</TabsContent>
{/* Impermanent Loss Calculator Tab */}
<TabsContent value="calculator" className="space-y-4">
<Card className="p-6 bg-gray-800/50 border-gray-700">
<h3 className="text-lg font-semibold text-white mb-4">Impermanent Loss Calculator</h3>
<div className="space-y-4">
<div className="p-4 bg-gray-900/50 rounded-lg">
<p className="text-sm text-gray-400 mb-3">If {asset0Symbol} price changes by:</p>
<div className="space-y-2">
{[10, 25, 50, 100, 200].map((change) => {
const il = calculateImpermanentLoss(change);
return (
<div key={change} className="flex justify-between items-center py-2 border-b border-gray-700 last:border-0">
<span className="text-gray-300">+{change}%</span>
<Badge
variant="outline"
className={il < -1 ? 'border-red-500 text-red-400' : 'border-yellow-500 text-yellow-400'}
>
{il.toFixed(2)}% Loss
</Badge>
</div>
);
})}
</div>
</div>
<Alert className="bg-orange-900/20 border-orange-500">
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
<p className="font-semibold mb-1">What is Impermanent Loss?</p>
<p className="text-sm text-gray-300">
Impermanent loss occurs when the price ratio of tokens in the pool changes.
The larger the price change, the greater the loss compared to simply holding the tokens.
Fees earned from swaps can offset this loss over time.
</p>
</AlertDescription>
</Alert>
</div>
</Card>
</TabsContent>
</Tabs>
{/* Modals */}
<AddLiquidityModal
isOpen={isAddLiquidityModalOpen}
onClose={() => setIsAddLiquidityModalOpen(false)}
asset0={poolData?.asset0}
asset1={poolData?.asset1}
/>
{lpPosition && poolData && (
<RemoveLiquidityModal
isOpen={isRemoveLiquidityModalOpen}
onClose={() => setIsRemoveLiquidityModalOpen(false)}
lpPosition={lpPosition}
lpTokenId={poolData.lpTokenId}
asset0={poolData.asset0}
asset1={poolData.asset1}
/>
)}
</div>
);
};
export default PoolDashboard;