adding unstake basket page
This commit is contained in:
11
src/app/unstake-basket/page.tsx
Normal file
11
src/app/unstake-basket/page.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { UnstakeBasketForm } from '@/components/unstake-basket-form';
|
||||||
|
|
||||||
|
export default function UnstakeBasketPage() {
|
||||||
|
return (
|
||||||
|
<div className="w-full max-w-2xl mx-auto">
|
||||||
|
<UnstakeBasketForm />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ export function Header() {
|
|||||||
{ href: '/', label: 'Swap' },
|
{ href: '/', label: 'Swap' },
|
||||||
{ href: '/stake', label: 'Stake' },
|
{ href: '/stake', label: 'Stake' },
|
||||||
{ href: '/unstake', label: 'Unstake' },
|
{ href: '/unstake', label: 'Unstake' },
|
||||||
|
{ href: '/unstake-basket', label: 'Unstake Basket' },
|
||||||
{ href: '/about', label: 'About' },
|
{ href: '/about', label: 'About' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
379
src/components/unstake-basket-form.tsx
Normal file
379
src/components/unstake-basket-form.tsx
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { CheckCircle, XCircle, Loader2, Trash2 } from 'lucide-react';
|
||||||
|
import { useAccount, usePublicClient } from 'wagmi';
|
||||||
|
import { useGetAllPools, useLPTokenBalance, type PoolDetails } from '@/hooks/usePartyPlanner';
|
||||||
|
import { useBurn, type ActualBurnAmounts } from '@/hooks/usePartyPool';
|
||||||
|
import { formatUnits, parseUnits } from 'viem';
|
||||||
|
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
|
||||||
|
import { ERC20ABI } from '@/contracts/ERC20ABI';
|
||||||
|
|
||||||
|
type TransactionStatus = 'idle' | 'pending' | 'success' | 'error';
|
||||||
|
|
||||||
|
interface PoolWithBalance extends PoolDetails {
|
||||||
|
lpBalance: bigint;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TokenInfo {
|
||||||
|
address: `0x${string}`;
|
||||||
|
symbol: string;
|
||||||
|
decimals: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UnstakeBasketForm() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { isConnected, address } = useAccount();
|
||||||
|
const publicClient = usePublicClient();
|
||||||
|
const [selectedPools, setSelectedPools] = useState<Set<string>>(new Set());
|
||||||
|
const [poolsWithBalances, setPoolsWithBalances] = useState<PoolWithBalance[]>([]);
|
||||||
|
const [transactionStatus, setTransactionStatus] = useState<TransactionStatus>('idle');
|
||||||
|
const [transactionError, setTransactionError] = useState<string | null>(null);
|
||||||
|
const [actualBurnResults, setActualBurnResults] = useState<{[key: string]: ActualBurnAmounts}>({});
|
||||||
|
const [poolTokens, setPoolTokens] = useState<{[key: string]: TokenInfo[]}>({});
|
||||||
|
|
||||||
|
// Fetch all pools using the hook
|
||||||
|
const { poolDetails, loading: poolsLoading } = useGetAllPools();
|
||||||
|
|
||||||
|
// Initialize burn hook
|
||||||
|
const { executeBurn, isBurning } = useBurn();
|
||||||
|
|
||||||
|
// Fetch LP balances for all pools
|
||||||
|
useEffect(() => {
|
||||||
|
if (!poolDetails || !address || !publicClient) return;
|
||||||
|
|
||||||
|
const fetchBalances = async () => {
|
||||||
|
const poolsWithBal: PoolWithBalance[] = [];
|
||||||
|
|
||||||
|
for (const pool of poolDetails) {
|
||||||
|
try {
|
||||||
|
const balance = await publicClient.readContract({
|
||||||
|
address: pool.address,
|
||||||
|
abi: IPartyPoolABI,
|
||||||
|
functionName: 'balanceOf',
|
||||||
|
args: [address],
|
||||||
|
}) as bigint;
|
||||||
|
|
||||||
|
if (balance > 0n) {
|
||||||
|
poolsWithBal.push({
|
||||||
|
...pool,
|
||||||
|
lpBalance: balance,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error fetching balance for pool ${pool.address}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setPoolsWithBalances(poolsWithBal);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchBalances();
|
||||||
|
}, [poolDetails, address, publicClient]);
|
||||||
|
|
||||||
|
// Fetch token details for selected pools
|
||||||
|
useEffect(() => {
|
||||||
|
if (!publicClient || selectedPools.size === 0) return;
|
||||||
|
|
||||||
|
const fetchTokenDetails = async () => {
|
||||||
|
const tokenInfoMap: {[key: string]: TokenInfo[]} = {};
|
||||||
|
|
||||||
|
for (const poolAddress of Array.from(selectedPools)) {
|
||||||
|
const pool = poolsWithBalances.find(p => p.address === poolAddress);
|
||||||
|
if (!pool) continue;
|
||||||
|
|
||||||
|
const tokenInfos: TokenInfo[] = [];
|
||||||
|
|
||||||
|
for (const tokenAddress of pool.tokens) {
|
||||||
|
try {
|
||||||
|
const [symbol, decimals] = await Promise.all([
|
||||||
|
publicClient.readContract({
|
||||||
|
address: tokenAddress as `0x${string}`,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'symbol',
|
||||||
|
}) as Promise<string>,
|
||||||
|
publicClient.readContract({
|
||||||
|
address: tokenAddress as `0x${string}`,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'decimals',
|
||||||
|
}) as Promise<number>,
|
||||||
|
]);
|
||||||
|
|
||||||
|
tokenInfos.push({
|
||||||
|
address: tokenAddress as `0x${string}`,
|
||||||
|
symbol,
|
||||||
|
decimals,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error fetching token details for ${tokenAddress}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenInfoMap[poolAddress] = tokenInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPoolTokens(tokenInfoMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTokenDetails();
|
||||||
|
}, [publicClient, selectedPools, poolsWithBalances]);
|
||||||
|
|
||||||
|
const togglePoolSelection = (poolAddress: string) => {
|
||||||
|
const newSelected = new Set(selectedPools);
|
||||||
|
if (newSelected.has(poolAddress)) {
|
||||||
|
newSelected.delete(poolAddress);
|
||||||
|
} else {
|
||||||
|
newSelected.add(poolAddress);
|
||||||
|
}
|
||||||
|
setSelectedPools(newSelected);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBurnAll = async () => {
|
||||||
|
if (selectedPools.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTransactionStatus('pending');
|
||||||
|
setTransactionError(null);
|
||||||
|
setActualBurnResults({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const results: {[key: string]: ActualBurnAmounts} = {};
|
||||||
|
|
||||||
|
// Execute burn for each selected pool
|
||||||
|
for (const poolAddress of Array.from(selectedPools)) {
|
||||||
|
const pool = poolsWithBalances.find(p => p.address === poolAddress);
|
||||||
|
if (!pool) continue;
|
||||||
|
|
||||||
|
console.log(`Burning LP tokens for pool ${pool.symbol}...`);
|
||||||
|
|
||||||
|
// Execute the burn transaction
|
||||||
|
const result = await executeBurn(
|
||||||
|
pool.address,
|
||||||
|
pool.lpBalance,
|
||||||
|
false // unwrap = false by default
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store actual burn amounts if available
|
||||||
|
if (result?.actualBurnAmounts) {
|
||||||
|
results[poolAddress] = result.actualBurnAmounts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setActualBurnResults(results);
|
||||||
|
setTransactionStatus('success');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Burn failed:', err);
|
||||||
|
setTransactionError(err instanceof Error ? err.message : 'Transaction failed');
|
||||||
|
setTransactionStatus('error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
if (transactionStatus === 'success') {
|
||||||
|
// Clear selections and refresh
|
||||||
|
setSelectedPools(new Set());
|
||||||
|
// Refresh pool balances
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
setTransactionStatus('idle');
|
||||||
|
setTransactionError(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="w-full max-w-2xl mx-auto relative">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Unstake Basket</CardTitle>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Select pools to burn all LP tokens and receive all underlying tokens proportionally
|
||||||
|
</p>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{/* Pool Selection List */}
|
||||||
|
{poolsLoading ? (
|
||||||
|
<div className="flex justify-center items-center py-8">
|
||||||
|
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||||
|
</div>
|
||||||
|
) : poolsWithBalances.length === 0 ? (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
<p>No pools with LP token balance found.</p>
|
||||||
|
<p className="text-sm">Stake some tokens first to use this feature.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{poolsWithBalances.map((pool) => (
|
||||||
|
<div
|
||||||
|
key={pool.address}
|
||||||
|
className={`p-4 border rounded-lg cursor-pointer transition-colors ${
|
||||||
|
selectedPools.has(pool.address)
|
||||||
|
? 'border-primary bg-primary/5'
|
||||||
|
: 'border-border hover:border-primary/50'
|
||||||
|
}`}
|
||||||
|
onClick={() => togglePoolSelection(pool.address)}
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-start">
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedPools.has(pool.address)}
|
||||||
|
onChange={() => {}}
|
||||||
|
className="w-4 h-4"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<div className="font-medium">{pool.symbol}</div>
|
||||||
|
<div className="text-xs text-muted-foreground">{pool.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<div className="text-sm font-medium">
|
||||||
|
{formatUnits(pool.lpBalance, 18)} LP
|
||||||
|
</div>
|
||||||
|
{poolTokens[pool.address] && (
|
||||||
|
<div className="text-xs text-muted-foreground mt-1">
|
||||||
|
{poolTokens[pool.address].length} tokens
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{selectedPools.has(pool.address) && poolTokens[pool.address] && (
|
||||||
|
<div className="mt-3 pt-3 border-t border-border">
|
||||||
|
<div className="text-xs text-muted-foreground mb-2">You will receive:</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{poolTokens[pool.address].map((token) => (
|
||||||
|
<div key={token.address} className="text-xs flex justify-between">
|
||||||
|
<span>{token.symbol}</span>
|
||||||
|
<span className="text-muted-foreground">
|
||||||
|
(proportional to pool composition)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Burn Button */}
|
||||||
|
<Button
|
||||||
|
className="w-full h-14 text-lg"
|
||||||
|
onClick={handleBurnAll}
|
||||||
|
disabled={!isConnected || selectedPools.size === 0 || isBurning}
|
||||||
|
>
|
||||||
|
{!isConnected
|
||||||
|
? 'Connect Wallet'
|
||||||
|
: isBurning
|
||||||
|
? 'Burning...'
|
||||||
|
: `Burn ${selectedPools.size} Pool${selectedPools.size !== 1 ? 's' : ''}`}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{selectedPools.size > 0 && (
|
||||||
|
<div className="text-xs text-center text-muted-foreground">
|
||||||
|
This will burn all LP tokens from {selectedPools.size} selected pool{selectedPools.size !== 1 ? 's' : ''}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
{/* Transaction Modal Overlay */}
|
||||||
|
{transactionStatus !== 'idle' && (
|
||||||
|
<div className="absolute inset-0 bg-background/80 backdrop-blur-sm flex items-center justify-center z-50 rounded-lg">
|
||||||
|
<div className="bg-card border rounded-lg p-8 max-w-md w-full mx-4 shadow-lg max-h-[80vh] overflow-y-auto">
|
||||||
|
{transactionStatus === 'pending' && (
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<Loader2 className="h-16 w-16 animate-spin text-primary" />
|
||||||
|
<h3 className="text-xl font-semibold text-center">
|
||||||
|
Burning LP Tokens
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-muted-foreground text-center">
|
||||||
|
Burning LP tokens from {selectedPools.size} pool{selectedPools.size !== 1 ? 's' : ''}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground text-center">
|
||||||
|
Please confirm the transactions in your wallet
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{transactionStatus === 'success' && (
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<CheckCircle className="h-16 w-16 text-green-500" />
|
||||||
|
<h3 className="text-xl font-semibold text-center">
|
||||||
|
Burn Confirmed!
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div className="w-full space-y-4">
|
||||||
|
{Object.entries(actualBurnResults).map(([poolAddress, burnAmounts]) => {
|
||||||
|
const pool = poolsWithBalances.find(p => p.address === poolAddress);
|
||||||
|
const tokens = poolTokens[poolAddress];
|
||||||
|
|
||||||
|
if (!pool || !tokens) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={poolAddress} className="border rounded-lg p-4">
|
||||||
|
<div className="font-medium mb-2">{pool.symbol}</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex justify-between text-sm">
|
||||||
|
<span className="text-muted-foreground">LP Burned:</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{formatUnits(burnAmounts.lpBurned, 18)} {pool.symbol}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-muted-foreground mb-1">Tokens Received:</div>
|
||||||
|
{burnAmounts.withdrawAmounts.map((amount, index) => {
|
||||||
|
const token = tokens[index];
|
||||||
|
if (!token) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index} className="flex justify-between text-sm pl-2">
|
||||||
|
<span className="text-muted-foreground">{token.symbol}:</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{formatUnits(amount, token.decimals)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={handleCloseModal}
|
||||||
|
className="w-full mt-4"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{transactionStatus === 'error' && (
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<XCircle className="h-16 w-16 text-destructive" />
|
||||||
|
<h3 className="text-xl font-semibold text-center">
|
||||||
|
Burn Failed
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-muted-foreground text-center break-words">
|
||||||
|
{transactionError || 'Transaction failed'}
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
onClick={handleCloseModal}
|
||||||
|
variant="outline"
|
||||||
|
className="w-full mt-4"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -630,7 +630,6 @@ export function useBurnSwap() {
|
|||||||
deadline: deadline.toString(),
|
deadline: deadline.toString(),
|
||||||
unwrap,
|
unwrap,
|
||||||
});
|
});
|
||||||
|
|
||||||
// STEP 3: Execute the burnSwap transaction
|
// STEP 3: Execute the burnSwap transaction
|
||||||
const hash = await walletClient.writeContract({
|
const hash = await walletClient.writeContract({
|
||||||
address: poolAddress,
|
address: poolAddress,
|
||||||
@@ -710,3 +709,161 @@ export function useBurnSwap() {
|
|||||||
burnSwapError,
|
burnSwapError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ActualBurnAmounts {
|
||||||
|
lpBurned: bigint;
|
||||||
|
withdrawAmounts: bigint[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useBurn() {
|
||||||
|
const publicClient = usePublicClient();
|
||||||
|
const { data: walletClient } = useWalletClient();
|
||||||
|
const [isBurning, setIsBurning] = useState(false);
|
||||||
|
const [burnHash, setBurnHash] = useState<`0x${string}` | null>(null);
|
||||||
|
const [burnError, setBurnError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const executeBurn = async (
|
||||||
|
poolAddress: `0x${string}`,
|
||||||
|
lpAmount: bigint,
|
||||||
|
unwrap: boolean = false
|
||||||
|
) => {
|
||||||
|
if (!walletClient || !publicClient) {
|
||||||
|
setBurnError('Wallet not connected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsBurning(true);
|
||||||
|
setBurnError(null);
|
||||||
|
setBurnHash(null);
|
||||||
|
|
||||||
|
const userAddress = walletClient.account.address;
|
||||||
|
|
||||||
|
// STEP 1: Approve the pool to spend the LP tokens
|
||||||
|
console.log('🔐 Approving LP token spend for burn...');
|
||||||
|
console.log('LP token (pool) to approve:', poolAddress);
|
||||||
|
console.log('Spender (pool):', poolAddress);
|
||||||
|
console.log('Amount:', lpAmount.toString());
|
||||||
|
|
||||||
|
const approvalHash = await walletClient.writeContract({
|
||||||
|
address: poolAddress,
|
||||||
|
abi: [
|
||||||
|
{
|
||||||
|
name: 'approve',
|
||||||
|
type: 'function',
|
||||||
|
stateMutability: 'nonpayable',
|
||||||
|
inputs: [
|
||||||
|
{ name: 'spender', type: 'address' },
|
||||||
|
{ name: 'amount', type: 'uint256' }
|
||||||
|
],
|
||||||
|
outputs: [{ name: '', type: 'bool' }]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
functionName: 'approve',
|
||||||
|
args: [poolAddress, lpAmount],
|
||||||
|
account: userAddress,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ Approval transaction submitted:', approvalHash);
|
||||||
|
await publicClient.waitForTransactionReceipt({ hash: approvalHash });
|
||||||
|
console.log('✅ Approval confirmed');
|
||||||
|
|
||||||
|
// STEP 2: Calculate deadline (20 minutes from now)
|
||||||
|
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200); // 20 minutes = 1200 seconds
|
||||||
|
|
||||||
|
console.log('🚀 Executing burn with params:', {
|
||||||
|
pool: poolAddress,
|
||||||
|
payer: userAddress,
|
||||||
|
receiver: userAddress,
|
||||||
|
lpAmount: lpAmount.toString(),
|
||||||
|
deadline: deadline.toString(),
|
||||||
|
unwrap,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log details for cast call
|
||||||
|
console.log('\n=== CAST CALL DETAILS FOR BURN ===');
|
||||||
|
console.log('Contract Address:', poolAddress);
|
||||||
|
console.log('Function: burn(address,address,uint256,uint256,bool)');
|
||||||
|
console.log('Parameters:');
|
||||||
|
console.log(' - payer (address):', userAddress);
|
||||||
|
console.log(' - receiver (address):', userAddress);
|
||||||
|
console.log(' - lpAmount (uint256):', lpAmount.toString());
|
||||||
|
console.log(' - deadline (uint256):', deadline.toString());
|
||||||
|
console.log(' - unwrap (bool):', unwrap);
|
||||||
|
console.log('\nCast command:');
|
||||||
|
console.log(`cast call ${poolAddress} "burn(address,address,uint256,uint256,bool)" ${userAddress} ${userAddress} ${lpAmount.toString()} ${deadline.toString()} ${unwrap} --from ${userAddress}`);
|
||||||
|
console.log('=====================================\n');
|
||||||
|
|
||||||
|
// STEP 3: Execute the burn transaction
|
||||||
|
const hash = await walletClient.writeContract({
|
||||||
|
address: poolAddress,
|
||||||
|
abi: IPartyPoolABI,
|
||||||
|
functionName: 'burn',
|
||||||
|
args: [
|
||||||
|
userAddress, // payer
|
||||||
|
userAddress, // receiver
|
||||||
|
lpAmount,
|
||||||
|
deadline,
|
||||||
|
unwrap,
|
||||||
|
],
|
||||||
|
account: userAddress,
|
||||||
|
});
|
||||||
|
|
||||||
|
setBurnHash(hash);
|
||||||
|
console.log('✅ Burn transaction submitted:', hash);
|
||||||
|
|
||||||
|
// Wait for transaction confirmation
|
||||||
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
||||||
|
console.log('✅ Burn transaction confirmed:', receipt);
|
||||||
|
|
||||||
|
// Parse the Burn event from the receipt logs
|
||||||
|
let actualBurnAmounts: ActualBurnAmounts | null = null;
|
||||||
|
for (const log of receipt.logs) {
|
||||||
|
try {
|
||||||
|
const decodedLog = decodeEventLog({
|
||||||
|
abi: IPartyPoolABI,
|
||||||
|
data: log.data,
|
||||||
|
topics: log.topics,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (decodedLog.eventName === 'Burn') {
|
||||||
|
const { amounts, lpBurned } = decodedLog.args as {
|
||||||
|
amounts: bigint[];
|
||||||
|
lpBurned: bigint;
|
||||||
|
};
|
||||||
|
|
||||||
|
actualBurnAmounts = {
|
||||||
|
lpBurned,
|
||||||
|
withdrawAmounts: amounts,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📊 Actual burn amounts from event:', {
|
||||||
|
lpBurned: lpBurned.toString(),
|
||||||
|
withdrawAmounts: amounts.map(a => a.toString()),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Skip logs that don't match our ABI
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { receipt, actualBurnAmounts };
|
||||||
|
} catch (err) {
|
||||||
|
const errorMessage = err instanceof Error ? err.message : 'Burn failed';
|
||||||
|
setBurnError(errorMessage);
|
||||||
|
console.error('❌ Burn error:', err);
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
setIsBurning(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
executeBurn,
|
||||||
|
isBurning,
|
||||||
|
burnHash,
|
||||||
|
burnError,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user