Compare commits
6 Commits
e5bcc632e5
...
1fa6d686f4
| Author | SHA1 | Date | |
|---|---|---|---|
| 1fa6d686f4 | |||
| 2fbebed014 | |||
| fea441b4e7 | |||
| 732dfd7780 | |||
| c69f0a47de | |||
| dab02db690 |
@@ -7,7 +7,7 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ChevronDown, CheckCircle, XCircle, Loader2, ArrowDownUp } from 'lucide-react';
|
import { ChevronDown, CheckCircle, XCircle, Loader2, ArrowDownUp } from 'lucide-react';
|
||||||
import { useAccount, usePublicClient } from 'wagmi';
|
import { useAccount, usePublicClient } from 'wagmi';
|
||||||
import { useGetAllPools, useTokenDetails, useSwapMintAmounts, useBurnSwapAmounts, useLPTokenBalance, type PoolDetails, type TokenDetails, type BurnSwapAmounts } from '@/hooks/usePartyPlanner';
|
import { useGetAllPools, useTokenDetails, useSwapMintAmounts, useBurnSwapAmounts, useLPTokenBalance, useBurnAmounts, type PoolDetails, type TokenDetails, type BurnSwapAmounts } from '@/hooks/usePartyPlanner';
|
||||||
import { useSwapMint, useBurnSwap, useBurn, type ActualSwapMintAmounts, type ActualBurnSwapAmounts, type ActualBurnAmounts } from '@/hooks/usePartyPool';
|
import { useSwapMint, useBurnSwap, useBurn, type ActualSwapMintAmounts, type ActualBurnSwapAmounts, type ActualBurnAmounts } from '@/hooks/usePartyPool';
|
||||||
import { formatUnits, parseUnits } from 'viem';
|
import { formatUnits, parseUnits } from 'viem';
|
||||||
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
|
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
|
||||||
@@ -143,6 +143,12 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
mode === 'unstake' && !redeemAll ? inputTokenIndex : undefined
|
mode === 'unstake' && !redeemAll ? inputTokenIndex : undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Fetch burn amounts (for unstake mode when redeeming all)
|
||||||
|
const { burnAmounts, loading: burnAmountsLoading } = useBurnAmounts(
|
||||||
|
mode === 'unstake' && redeemAll ? selectedPool?.address : undefined,
|
||||||
|
mode === 'unstake' && redeemAll ? maxAmountIn : undefined
|
||||||
|
);
|
||||||
|
|
||||||
// Fetch token details for the selected pool when Redeem All is active
|
// Fetch token details for the selected pool when Redeem All is active
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!publicClient || !selectedPool || mode !== 'unstake' || !redeemAll) {
|
if (!publicClient || !selectedPool || mode !== 'unstake' || !redeemAll) {
|
||||||
@@ -334,9 +340,21 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
setSelectedToken(null);
|
setSelectedToken(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex justify-between items-center w-full">
|
||||||
<span className="font-medium">{pool.symbol}</span>
|
<div className="flex flex-col">
|
||||||
<span className="text-xs text-muted-foreground">{pool.name}</span>
|
<span className="font-medium">{pool.symbol}</span>
|
||||||
|
<span className="text-xs text-muted-foreground">{pool.name}</span>
|
||||||
|
</div>
|
||||||
|
{(pool.price || pool.tvl) && (
|
||||||
|
<div className="flex flex-col items-end">
|
||||||
|
{pool.price && (
|
||||||
|
<span className="text-sm font-medium text-muted-foreground">{pool.price}</span>
|
||||||
|
)}
|
||||||
|
{pool.tvl && (
|
||||||
|
<span className="text-xs text-muted-foreground">TVL: {pool.tvl}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
@@ -384,9 +402,21 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
setSelectedToken(null);
|
setSelectedToken(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex justify-between items-center w-full">
|
||||||
<span className="font-medium">{pool.symbol}</span>
|
<div className="flex flex-col">
|
||||||
<span className="text-xs text-muted-foreground">{pool.name}</span>
|
<span className="font-medium">{pool.symbol}</span>
|
||||||
|
<span className="text-xs text-muted-foreground">{pool.name}</span>
|
||||||
|
</div>
|
||||||
|
{(pool.price || pool.tvl) && (
|
||||||
|
<div className="flex flex-col items-end">
|
||||||
|
{pool.price && (
|
||||||
|
<span className="text-sm font-medium text-muted-foreground">{pool.price}</span>
|
||||||
|
)}
|
||||||
|
{pool.tvl && (
|
||||||
|
<span className="text-xs text-muted-foreground">TVL: {pool.tvl}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
@@ -618,15 +648,18 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Redeem All Tokens Display */}
|
{/* Redeem All Tokens Display */}
|
||||||
{mode === 'unstake' && redeemAll && poolTokens.length > 0 && (
|
{mode === 'unstake' && redeemAll && poolTokens.length > 0 && !isAmountExceedingBalance && (
|
||||||
<div className="px-4 py-3 bg-muted/30 rounded-lg space-y-2">
|
<div className="px-4 py-3 bg-muted/30 rounded-lg space-y-2">
|
||||||
<div className="text-sm font-medium mb-2">You will receive:</div>
|
<div className="text-sm font-medium mb-2">You will receive:</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{poolTokens.map((token) => (
|
{poolTokens.map((token, index) => (
|
||||||
<div key={token.address} className="text-sm flex justify-between">
|
<div key={token.address} className="text-sm flex justify-between">
|
||||||
<span className="text-muted-foreground">{token.symbol}</span>
|
<span className="text-muted-foreground">{token.symbol}:</span>
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="font-medium">
|
||||||
(proportional to pool composition)
|
{burnAmountsLoading ? 'Calculating...' : burnAmounts && burnAmounts[index]
|
||||||
|
? `${Number(formatUnits(burnAmounts[index], token.decimals)).toLocaleString('en-US', { maximumFractionDigits: 6 })}`
|
||||||
|
: '(proportional to pool composition)'
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -709,12 +742,8 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
<span className="font-medium">{formatUnits(actualSwapMintAmounts.lpMinted, 18)} {selectedPool.symbol}</span>
|
<span className="font-medium">{formatUnits(actualSwapMintAmounts.lpMinted, 18)} {selectedPool.symbol}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-muted-foreground">LP Fee:</span>
|
<span className="text-muted-foreground">Fee:</span>
|
||||||
<span className="font-medium">{formatUnits(actualSwapMintAmounts.lpFee, selectedToken.decimals)} {selectedToken.symbol}</span>
|
<span className="font-medium">{formatUnits(actualSwapMintAmounts.lpFee + actualSwapMintAmounts.protocolFee, selectedToken.decimals)} {selectedToken.symbol}</span>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-sm">
|
|
||||||
<span className="text-muted-foreground">Protocol Fee:</span>
|
|
||||||
<span className="font-medium">{formatUnits(actualSwapMintAmounts.protocolFee, selectedToken.decimals)} {selectedToken.symbol}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -783,12 +812,8 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
<span className="font-medium">{formatUnits(actualBurnSwapAmounts.amountOut, selectedToken.decimals)} {selectedToken.symbol}</span>
|
<span className="font-medium">{formatUnits(actualBurnSwapAmounts.amountOut, selectedToken.decimals)} {selectedToken.symbol}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-muted-foreground">LP Fee:</span>
|
<span className="text-muted-foreground">Fee:</span>
|
||||||
<span className="font-medium">{formatUnits(actualBurnSwapAmounts.lpFee, selectedToken.decimals)} {selectedToken.symbol}</span>
|
<span className="font-medium">{formatUnits(actualBurnSwapAmounts.lpFee + actualBurnSwapAmounts.protocolFee, selectedToken.decimals)} {selectedToken.symbol}</span>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-sm">
|
|
||||||
<span className="text-muted-foreground">Protocol Fee:</span>
|
|
||||||
<span className="font-medium">{formatUnits(actualBurnSwapAmounts.protocolFee, selectedToken.decimals)} {selectedToken.symbol}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export function SwapForm() {
|
|||||||
const { tokenDetails, loading } = useTokenDetails(address);
|
const { tokenDetails, loading } = useTokenDetails(address);
|
||||||
|
|
||||||
// Get available tokens for the selected "from" token
|
// Get available tokens for the selected "from" token
|
||||||
const { availableTokens } = useGetPoolsByToken(selectedFromToken?.address);
|
const { availableTokens, error: poolsError } = useGetPoolsByToken(selectedFromToken?.address);
|
||||||
|
|
||||||
// Only calculate swap amounts when both tokens are selected
|
// Only calculate swap amounts when both tokens are selected
|
||||||
// Use useMemo to prevent creating a new array reference on every render
|
// Use useMemo to prevent creating a new array reference on every render
|
||||||
@@ -326,7 +326,7 @@ export function SwapForm() {
|
|||||||
))
|
))
|
||||||
) : selectedFromToken ? (
|
) : selectedFromToken ? (
|
||||||
<div className="px-4 py-3 text-sm text-muted-foreground">
|
<div className="px-4 py-3 text-sm text-muted-foreground">
|
||||||
{loading ? 'Loading available tokens...' : 'No tokens available for swap'}
|
{loading ? 'Loading available tokens...' : poolsError || 'No tokens available for swap'}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="px-4 py-3 text-sm text-muted-foreground">
|
<div className="px-4 py-3 text-sm text-muted-foreground">
|
||||||
@@ -339,6 +339,13 @@ export function SwapForm() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Error message for unsupported tokens */}
|
||||||
|
{poolsError && selectedFromToken && (
|
||||||
|
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||||
|
<p className="text-sm text-destructive">{poolsError}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Gas Estimate, Slippage, and Fees */}
|
{/* Gas Estimate, Slippage, and Fees */}
|
||||||
{isConnected && fromAmount && toAmount && (
|
{isConnected && fromAmount && toAmount && (
|
||||||
<div className="px-4 py-2 bg-muted/30 rounded-lg space-y-2">
|
<div className="px-4 py-2 bg-muted/30 rounded-lg space-y-2">
|
||||||
@@ -377,7 +384,7 @@ export function SwapForm() {
|
|||||||
<Button
|
<Button
|
||||||
className="w-full h-14 text-lg"
|
className="w-full h-14 text-lg"
|
||||||
onClick={() => setIsReviewModalOpen(true)}
|
onClick={() => setIsReviewModalOpen(true)}
|
||||||
disabled={!isConnected || !fromAmount || !toAmount}
|
disabled={!isConnected || !fromAmount || !toAmount || !!poolsError}
|
||||||
>
|
>
|
||||||
{!isConnected
|
{!isConnected
|
||||||
? t('swap.connectWalletToSwap')
|
? t('swap.connectWalletToSwap')
|
||||||
@@ -487,12 +494,8 @@ export function SwapForm() {
|
|||||||
<span className="font-medium">{formatUnits(actualSwapAmounts.amountOut, selectedToToken.decimals)} {selectedToToken.symbol}</span>
|
<span className="font-medium">{formatUnits(actualSwapAmounts.amountOut, selectedToToken.decimals)} {selectedToToken.symbol}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-muted-foreground">LP Fee:</span>
|
<span className="text-muted-foreground">Fee:</span>
|
||||||
<span className="font-medium">{formatUnits(actualSwapAmounts.lpFee, selectedFromToken.decimals)} {selectedFromToken.symbol}</span>
|
<span className="font-medium">{formatUnits(actualSwapAmounts.lpFee + actualSwapAmounts.protocolFee, selectedFromToken.decimals)} {selectedFromToken.symbol}</span>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between text-sm">
|
|
||||||
<span className="text-muted-foreground">Protocol Fee:</span>
|
|
||||||
<span className="font-medium">{formatUnits(actualSwapAmounts.protocolFee, selectedFromToken.decimals)} {selectedFromToken.symbol}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -6,8 +6,22 @@ import chainInfo from '@/contracts/liqp-deployments.json';
|
|||||||
import IPartyPlannerABI from '@/contracts/IPartyPlannerABI';
|
import IPartyPlannerABI from '@/contracts/IPartyPlannerABI';
|
||||||
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
|
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
|
||||||
import IPartyPoolViewerABI from '@/contracts/IPartyPoolViewerABI';
|
import IPartyPoolViewerABI from '@/contracts/IPartyPoolViewerABI';
|
||||||
|
import IPartyInfoABI from '@/contracts/IPartyInfoABI';
|
||||||
import { ERC20ABI } from '@/contracts/ERC20ABI';
|
import { ERC20ABI } from '@/contracts/ERC20ABI';
|
||||||
|
|
||||||
|
// Helper function to format large numbers with K, M, B suffixes
|
||||||
|
function formatTVL(value: number): string {
|
||||||
|
if (value >= 1_000_000_000) {
|
||||||
|
return `$${(value / 1_000_000_000).toFixed(2)}B`;
|
||||||
|
} else if (value >= 1_000_000) {
|
||||||
|
return `$${(value / 1_000_000).toFixed(2)}M`;
|
||||||
|
} else if (value >= 1_000) {
|
||||||
|
return `$${(value / 1_000).toFixed(2)}K`;
|
||||||
|
} else {
|
||||||
|
return `$${value.toFixed(2)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function useGetAllTokens(offset: number = 0, limit: number = 100) {
|
export function useGetAllTokens(offset: number = 0, limit: number = 100) {
|
||||||
const publicClient = usePublicClient();
|
const publicClient = usePublicClient();
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
@@ -120,19 +134,20 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// Get chain ID and contract address
|
// Get chain ID and contract addresses
|
||||||
const chainId = await publicClient.getChainId();
|
const chainId = await publicClient.getChainId();
|
||||||
const address = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyPoolViewer: string } }>)[chainId.toString()]?.v1?.PartyPlanner;
|
const plannerAddress = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyInfo: string } }>)[chainId.toString()]?.v1?.PartyPlanner;
|
||||||
|
const partyInfoAddress = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyInfo: string } }>)[chainId.toString()]?.v1?.PartyInfo;
|
||||||
|
|
||||||
if (!address) {
|
if (!plannerAddress || !partyInfoAddress) {
|
||||||
setError('IPartyPlanner contract not found for current chain');
|
setError('IPartyPlanner or PartyInfo contract not found for current chain');
|
||||||
setAvailableTokens([]);
|
setAvailableTokens([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call getPoolsByToken function
|
// Call getPoolsByToken function
|
||||||
const poolsResult = await publicClient.readContract({
|
const poolsResult = await publicClient.readContract({
|
||||||
address: address as `0x${string}`,
|
address: plannerAddress as `0x${string}`,
|
||||||
abi: IPartyPlannerABI,
|
abi: IPartyPlannerABI,
|
||||||
functionName: 'getPoolsByToken',
|
functionName: 'getPoolsByToken',
|
||||||
args: [tokenAddress, BigInt(offset), BigInt(limit)],
|
args: [tokenAddress, BigInt(offset), BigInt(limit)],
|
||||||
@@ -150,11 +165,41 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter pools to only working ones
|
||||||
|
const workingPools: `0x${string}`[] = [];
|
||||||
|
for (const poolAddress of poolsResult) {
|
||||||
|
try {
|
||||||
|
const isWorking = await publicClient.readContract({
|
||||||
|
address: partyInfoAddress as `0x${string}`,
|
||||||
|
abi: IPartyInfoABI,
|
||||||
|
functionName: 'working',
|
||||||
|
args: [poolAddress],
|
||||||
|
}) as boolean;
|
||||||
|
|
||||||
|
if (isWorking) {
|
||||||
|
workingPools.push(poolAddress);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error checking if pool ${poolAddress} is working:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no working pools found, set error message
|
||||||
|
if (workingPools.length === 0 && poolsResult.length > 0) {
|
||||||
|
setError('This token is no longer supported. All pools containing this token are currently inactive.');
|
||||||
|
setAvailableTokens([]);
|
||||||
|
return;
|
||||||
|
} else if (workingPools.length === 0) {
|
||||||
|
setError('No pools found for this token.');
|
||||||
|
setAvailableTokens([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Map to store available tokens with their swap routes
|
// Map to store available tokens with their swap routes
|
||||||
const tokenRoutesMap = new Map<string, AvailableToken>();
|
const tokenRoutesMap = new Map<string, AvailableToken>();
|
||||||
|
|
||||||
// For each pool, fetch all tokens and track indices
|
// For each working pool, fetch all tokens and track indices
|
||||||
for (const poolAddress of poolsResult) {
|
for (const poolAddress of workingPools) {
|
||||||
try {
|
try {
|
||||||
const tokensInPool = await publicClient.readContract({
|
const tokensInPool = await publicClient.readContract({
|
||||||
address: poolAddress,
|
address: poolAddress,
|
||||||
@@ -332,6 +377,8 @@ export interface PoolDetails {
|
|||||||
name: string;
|
name: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
tokens: readonly `0x${string}`[];
|
tokens: readonly `0x${string}`[];
|
||||||
|
price?: string; // Formatted price string
|
||||||
|
tvl?: string; // Formatted TVL string (e.g., "$1.2M")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
||||||
@@ -360,12 +407,13 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// Get chain ID and contract address
|
// Get chain ID and contract addresses
|
||||||
const chainId = await publicClient.getChainId();
|
const chainId = await publicClient.getChainId();
|
||||||
const address = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyPoolViewer: string } }>)[chainId.toString()]?.v1?.PartyPlanner;
|
const plannerAddress = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyInfo: string } }>)[chainId.toString()]?.v1?.PartyPlanner;
|
||||||
|
const partyInfoAddress = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyInfo: string } }>)[chainId.toString()]?.v1?.PartyInfo;
|
||||||
|
|
||||||
if (!address) {
|
if (!plannerAddress || !partyInfoAddress) {
|
||||||
setError('IPartyPlanner contract not found for current chain');
|
setError('IPartyPlanner or PartyInfo contract not found for current chain');
|
||||||
setPools([]);
|
setPools([]);
|
||||||
setPoolDetails([]);
|
setPoolDetails([]);
|
||||||
return;
|
return;
|
||||||
@@ -373,7 +421,7 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
|||||||
|
|
||||||
// Call getAllPools function
|
// Call getAllPools function
|
||||||
const result = await publicClient.readContract({
|
const result = await publicClient.readContract({
|
||||||
address: address as `0x${string}`,
|
address: plannerAddress as `0x${string}`,
|
||||||
abi: IPartyPlannerABI,
|
abi: IPartyPlannerABI,
|
||||||
functionName: 'getAllPools',
|
functionName: 'getAllPools',
|
||||||
args: [BigInt(offset), BigInt(limit)],
|
args: [BigInt(offset), BigInt(limit)],
|
||||||
@@ -381,11 +429,11 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
|||||||
|
|
||||||
setPools(result);
|
setPools(result);
|
||||||
|
|
||||||
// Fetch details for each pool
|
// Fetch details for each pool and check if it's working
|
||||||
const details: PoolDetails[] = [];
|
const details: PoolDetails[] = [];
|
||||||
for (const poolAddress of result) {
|
for (const poolAddress of result) {
|
||||||
try {
|
try {
|
||||||
const [name, symbol, tokens] = await Promise.all([
|
const [name, symbol, tokens, isWorking] = await Promise.all([
|
||||||
publicClient.readContract({
|
publicClient.readContract({
|
||||||
address: poolAddress,
|
address: poolAddress,
|
||||||
abi: ERC20ABI,
|
abi: ERC20ABI,
|
||||||
@@ -401,23 +449,89 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
|||||||
abi: IPartyPoolABI,
|
abi: IPartyPoolABI,
|
||||||
functionName: 'allTokens',
|
functionName: 'allTokens',
|
||||||
}).catch(() => [] as readonly `0x${string}`[]),
|
}).catch(() => [] as readonly `0x${string}`[]),
|
||||||
|
publicClient.readContract({
|
||||||
|
address: partyInfoAddress as `0x${string}`,
|
||||||
|
abi: IPartyInfoABI,
|
||||||
|
functionName: 'working',
|
||||||
|
args: [poolAddress],
|
||||||
|
}).catch(() => false),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
details.push({
|
// Only add pool if it's working
|
||||||
address: poolAddress,
|
if (isWorking) {
|
||||||
name: name as string,
|
// Fetch pool price (use first token as quote, index 0)
|
||||||
symbol: symbol as string,
|
let priceStr: string | undefined;
|
||||||
tokens: tokens as readonly `0x${string}`[],
|
try {
|
||||||
});
|
const priceRaw = await publicClient.readContract({
|
||||||
|
address: partyInfoAddress as `0x${string}`,
|
||||||
|
abi: IPartyInfoABI,
|
||||||
|
functionName: 'poolPrice',
|
||||||
|
args: [poolAddress, BigInt(0)],
|
||||||
|
});
|
||||||
|
|
||||||
|
const price = BigInt(priceRaw as bigint | number);
|
||||||
|
|
||||||
|
if (price === 0n) {
|
||||||
|
priceStr = undefined;
|
||||||
|
} else {
|
||||||
|
// Convert Q64 format to decimal (price = priceValue / 2^64)
|
||||||
|
const Q64 = 2n ** 64n;
|
||||||
|
const isNegative = price < 0n;
|
||||||
|
const absPrice = isNegative ? -price : price;
|
||||||
|
const priceFloat = Number(absPrice) / Number(Q64);
|
||||||
|
const finalPrice = isNegative ? -priceFloat : priceFloat;
|
||||||
|
|
||||||
|
priceStr = `$${finalPrice.toFixed(4)}`;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error fetching pool price for ${poolAddress}:`, err);
|
||||||
|
priceStr = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate TVL (approximate by getting first token balance and doubling it)
|
||||||
|
let tvlStr: string | undefined;
|
||||||
|
try {
|
||||||
|
if (tokens && tokens.length > 0) {
|
||||||
|
const firstTokenAddress = tokens[0];
|
||||||
|
|
||||||
|
// Get token decimals and balance
|
||||||
|
const [decimals, balance] = await Promise.all([
|
||||||
|
publicClient.readContract({
|
||||||
|
address: firstTokenAddress as `0x${string}`,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'decimals',
|
||||||
|
}) as Promise<number>,
|
||||||
|
publicClient.readContract({
|
||||||
|
address: firstTokenAddress as `0x${string}`,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'balanceOf',
|
||||||
|
args: [poolAddress],
|
||||||
|
}) as Promise<bigint>,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Convert balance to float and double it for total TVL approximation
|
||||||
|
const tokenBalance = Number(balance) / Math.pow(10, decimals);
|
||||||
|
const approximateTVL = tokenBalance * 3;
|
||||||
|
|
||||||
|
tvlStr = formatTVL(approximateTVL);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error fetching TVL for ${poolAddress}:`, err);
|
||||||
|
tvlStr = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
details.push({
|
||||||
|
address: poolAddress,
|
||||||
|
name: name as string,
|
||||||
|
symbol: symbol as string,
|
||||||
|
tokens: tokens as readonly `0x${string}`[],
|
||||||
|
price: priceStr,
|
||||||
|
tvl: tvlStr,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching pool details for', poolAddress, err);
|
console.error('Error fetching pool details for', poolAddress, err);
|
||||||
// Add pool with fallback values
|
// Skip pools that fail to load
|
||||||
details.push({
|
|
||||||
address: poolAddress,
|
|
||||||
name: 'Unknown Pool',
|
|
||||||
symbol: 'POOL',
|
|
||||||
tokens: [],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,4 +802,74 @@ export function useLPTokenBalance(
|
|||||||
error,
|
error,
|
||||||
isReady: mounted,
|
isReady: mounted,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useBurnAmounts(
|
||||||
|
poolAddress: `0x${string}` | undefined,
|
||||||
|
lpTokenAmount: bigint | undefined
|
||||||
|
) {
|
||||||
|
const publicClient = usePublicClient();
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const [burnAmounts, setBurnAmounts] = useState<bigint[] | null>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mounted || !poolAddress || !lpTokenAmount || lpTokenAmount === 0n) {
|
||||||
|
setBurnAmounts(null);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchBurnAmounts = async () => {
|
||||||
|
if (!publicClient) {
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
// Get chain ID and PartyInfo contract address
|
||||||
|
const chainId = await publicClient.getChainId();
|
||||||
|
const partyInfoAddress = (chainInfo as Record<string, { v1: { PartyInfo: string } }>)[chainId.toString()]?.v1?.PartyInfo;
|
||||||
|
|
||||||
|
if (!partyInfoAddress) {
|
||||||
|
setError('PartyInfo contract not found for current chain');
|
||||||
|
setBurnAmounts(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call burnAmounts function
|
||||||
|
const amounts = await publicClient.readContract({
|
||||||
|
address: partyInfoAddress as `0x${string}`,
|
||||||
|
abi: IPartyPoolViewerABI,
|
||||||
|
functionName: 'burnAmounts',
|
||||||
|
args: [poolAddress, lpTokenAmount],
|
||||||
|
}) as bigint[];
|
||||||
|
|
||||||
|
setBurnAmounts(amounts);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching burn amounts:', err);
|
||||||
|
setError(err instanceof Error ? err.message : 'Failed to fetch burn amounts');
|
||||||
|
setBurnAmounts(null);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchBurnAmounts();
|
||||||
|
}, [publicClient, mounted, poolAddress, lpTokenAmount]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
burnAmounts,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
isReady: mounted,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -343,6 +343,7 @@ export function useSwap() {
|
|||||||
functionName: 'swap',
|
functionName: 'swap',
|
||||||
args: [
|
args: [
|
||||||
userAddress, // payer
|
userAddress, // payer
|
||||||
|
'0x00000000', // selector (bytes4(0))
|
||||||
userAddress, // receiver
|
userAddress, // receiver
|
||||||
BigInt(inputTokenIndex),
|
BigInt(inputTokenIndex),
|
||||||
BigInt(outputTokenIndex),
|
BigInt(outputTokenIndex),
|
||||||
|
|||||||
Reference in New Issue
Block a user