Swap
@@ -428,6 +421,59 @@ export function SwapForm() {
}}
isSwapping={isSwapping}
/>
+
+ {/* Transaction Modal Overlay */}
+ {transactionStatus !== 'idle' && (
+
+
+ {transactionStatus === 'pending' && (
+
+
+
Approving Swap
+
+ Swapping {fromAmount} {selectedFromToken?.symbol} → {toAmount} {selectedToToken?.symbol}
+
+
+ Please confirm the transactions in your wallet
+
+
+ )}
+
+ {transactionStatus === 'success' && (
+
+
+
Swap Confirmed!
+
+ Successfully swapped {fromAmount} {selectedFromToken?.symbol} to {toAmount} {selectedToToken?.symbol}
+
+
+
+ )}
+
+ {transactionStatus === 'error' && (
+
+
+
Swap Failed
+
+ {transactionError || 'Transaction failed'}
+
+
+
+ )}
+
+
+ )}
);
}
diff --git a/src/hooks/usePartyPlanner.ts b/src/hooks/usePartyPlanner.ts
index d9af328..781ddc1 100644
--- a/src/hooks/usePartyPlanner.ts
+++ b/src/hooks/usePartyPlanner.ts
@@ -5,6 +5,7 @@ import { usePublicClient } from 'wagmi';
import chainInfo from '@/contracts/liqp-deployments.json';
import IPartyPlannerABI from '@/contracts/IPartyPlannerABI';
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
+import IPartyPoolViewerABI from '@/contracts/IPartyPoolViewerABI';
import { ERC20ABI } from '@/contracts/ERC20ABI';
export function useGetAllTokens(offset: number = 0, limit: number = 100) {
@@ -324,4 +325,201 @@ export function useTokenDetails(userAddress: `0x${string}` | undefined) {
loading,
error,
};
+}
+
+export interface PoolDetails {
+ address: `0x${string}`;
+ name: string;
+ symbol: string;
+ tokens: readonly `0x${string}`[];
+}
+
+export function useGetAllPools(offset: number = 0, limit: number = 100) {
+ const publicClient = usePublicClient();
+ const [mounted, setMounted] = useState(false);
+ const [pools, setPools] = useState
(null);
+ const [poolDetails, setPoolDetails] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ // Handle hydration for Next.js static export
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ useEffect(() => {
+ if (!mounted) return;
+
+ const fetchPools = async () => {
+ if (!publicClient) {
+ setLoading(false);
+ return;
+ }
+
+ try {
+ setLoading(true);
+ setError(null);
+
+ // Get chain ID and contract address
+ const chainId = await publicClient.getChainId();
+ const address = (chainInfo as Record)[chainId.toString()]?.v1?.PartyPlanner;
+
+ if (!address) {
+ setError('IPartyPlanner contract not found for current chain');
+ setPools([]);
+ setPoolDetails([]);
+ return;
+ }
+
+ // Call getAllPools function
+ const result = await publicClient.readContract({
+ address: address as `0x${string}`,
+ abi: IPartyPlannerABI,
+ functionName: 'getAllPools',
+ args: [BigInt(offset), BigInt(limit)],
+ });
+
+ setPools(result);
+
+ // Fetch details for each pool
+ const details: PoolDetails[] = [];
+ for (const poolAddress of result) {
+ try {
+ const [name, symbol, tokens] = await Promise.all([
+ publicClient.readContract({
+ address: poolAddress,
+ abi: ERC20ABI,
+ functionName: 'name',
+ }).catch(() => 'Unknown Pool'),
+ publicClient.readContract({
+ address: poolAddress,
+ abi: ERC20ABI,
+ functionName: 'symbol',
+ }).catch(() => 'POOL'),
+ publicClient.readContract({
+ address: poolAddress,
+ abi: IPartyPoolABI,
+ functionName: 'allTokens',
+ }).catch(() => [] as readonly `0x${string}`[]),
+ ]);
+
+ details.push({
+ address: poolAddress,
+ name: name as string,
+ symbol: symbol as string,
+ tokens: tokens as readonly `0x${string}`[],
+ });
+ } catch (err) {
+ console.error('Error fetching pool details for', poolAddress, err);
+ // Add pool with fallback values
+ details.push({
+ address: poolAddress,
+ name: 'Unknown Pool',
+ symbol: 'POOL',
+ tokens: [],
+ });
+ }
+ }
+
+ setPoolDetails(details);
+ } catch (err) {
+ console.error('Error calling getAllPools:', err);
+ setError(err instanceof Error ? err.message : 'Failed to fetch pools');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchPools();
+ }, [publicClient, mounted, offset, limit]);
+
+ return {
+ pools,
+ poolDetails,
+ loading,
+ error,
+ isReady: mounted,
+ };
+}
+
+export interface SwapMintAmounts {
+ amountInUsed: bigint;
+ fee: bigint;
+ lpMinted: bigint;
+}
+
+export function useSwapMintAmounts(
+ poolAddress: `0x${string}` | undefined,
+ inputTokenIndex: number | undefined,
+ maxAmountIn: bigint | undefined
+) {
+ const publicClient = usePublicClient();
+ const [mounted, setMounted] = useState(false);
+ const [swapMintAmounts, setSwapMintAmounts] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ // Handle hydration for Next.js static export
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ useEffect(() => {
+ if (!mounted || !poolAddress || inputTokenIndex === undefined || !maxAmountIn || maxAmountIn === BigInt(0)) {
+ setLoading(false);
+ setSwapMintAmounts(null);
+ return;
+ }
+
+ const fetchSwapMintAmounts = async () => {
+ if (!publicClient) {
+ setLoading(false);
+ return;
+ }
+
+ try {
+ setLoading(true);
+ setError(null);
+
+ // Get chain ID and contract address
+ const chainId = await publicClient.getChainId();
+ const viewerAddress = (chainInfo as Record)[chainId.toString()]?.v1?.PartyPoolViewer;
+
+ if (!viewerAddress) {
+ setError('IPartyPoolViewer contract not found for current chain');
+ setSwapMintAmounts(null);
+ return;
+ }
+
+ // Call swapMintAmounts function
+ const result = await publicClient.readContract({
+ address: viewerAddress as `0x${string}`,
+ abi: IPartyPoolViewerABI,
+ functionName: 'swapMintAmounts',
+ args: [poolAddress, BigInt(inputTokenIndex), maxAmountIn],
+ }) as readonly [bigint, bigint, bigint];
+
+ setSwapMintAmounts({
+ amountInUsed: result[0],
+ fee: result[1],
+ lpMinted: result[2],
+ });
+ } catch (err) {
+ console.error('Error calling swapMintAmounts:', err);
+ setError(err instanceof Error ? err.message : 'Failed to fetch swap mint amounts');
+ setSwapMintAmounts(null);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSwapMintAmounts();
+ }, [publicClient, mounted, poolAddress, inputTokenIndex, maxAmountIn]);
+
+ return {
+ swapMintAmounts,
+ loading,
+ error,
+ isReady: mounted,
+ };
}
\ No newline at end of file
diff --git a/src/hooks/usePartyPool.ts b/src/hooks/usePartyPool.ts
index 5eea981..d706099 100644
--- a/src/hooks/usePartyPool.ts
+++ b/src/hooks/usePartyPool.ts
@@ -357,4 +357,109 @@ export function useSwap() {
gasEstimate,
isEstimatingGas,
};
+}
+
+export function useSwapMint() {
+ const publicClient = usePublicClient();
+ const { data: walletClient } = useWalletClient();
+ const [isSwapMinting, setIsSwapMinting] = useState(false);
+ const [swapMintHash, setSwapMintHash] = useState<`0x${string}` | null>(null);
+ const [swapMintError, setSwapMintError] = useState(null);
+
+ const executeSwapMint = async (
+ poolAddress: `0x${string}`,
+ inputTokenAddress: `0x${string}`,
+ inputTokenIndex: number,
+ maxAmountIn: bigint
+ ) => {
+ if (!walletClient || !publicClient) {
+ setSwapMintError('Wallet not connected');
+ return;
+ }
+
+ try {
+ setIsSwapMinting(true);
+ setSwapMintError(null);
+ setSwapMintHash(null);
+
+ const userAddress = walletClient.account.address;
+
+ // STEP 1: Approve the pool to spend the input token
+ console.log('🔐 Approving token spend for swap mint...');
+ console.log('Token to approve:', inputTokenAddress);
+ console.log('Spender (pool):', poolAddress);
+ console.log('Amount:', maxAmountIn.toString());
+
+ const approvalHash = await walletClient.writeContract({
+ address: inputTokenAddress,
+ abi: [
+ {
+ name: 'approve',
+ type: 'function',
+ stateMutability: 'nonpayable',
+ inputs: [
+ { name: 'spender', type: 'address' },
+ { name: 'amount', type: 'uint256' }
+ ],
+ outputs: [{ name: '', type: 'bool' }]
+ }
+ ],
+ functionName: 'approve',
+ args: [poolAddress, maxAmountIn],
+ });
+
+ console.log('✅ Approval transaction submitted:', approvalHash);
+ await publicClient.waitForTransactionReceipt({ hash: approvalHash });
+ console.log('✅ Approval confirmed');
+
+ // STEP 2: Calculate deadline (5 minutes from now)
+ const deadline = BigInt(Math.floor(Date.now() / 1000) + 300); // 5 minutes = 300 seconds
+
+ console.log('🚀 Executing swapMint with params:', {
+ pool: poolAddress,
+ payer: userAddress,
+ receiver: userAddress,
+ inputTokenIndex,
+ maxAmountIn: maxAmountIn.toString(),
+ deadline: deadline.toString(),
+ });
+
+ // STEP 3: Execute the swapMint transaction
+ const hash = await walletClient.writeContract({
+ address: poolAddress,
+ abi: IPartyPoolABI,
+ functionName: 'swapMint',
+ args: [
+ userAddress, // payer
+ userAddress, // receiver
+ BigInt(inputTokenIndex),
+ maxAmountIn,
+ deadline,
+ ],
+ });
+
+ setSwapMintHash(hash);
+ console.log('✅ SwapMint transaction submitted:', hash);
+
+ // Wait for transaction confirmation
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
+ console.log('✅ SwapMint transaction confirmed:', receipt);
+
+ return receipt;
+ } catch (err) {
+ const errorMessage = err instanceof Error ? err.message : 'SwapMint failed';
+ setSwapMintError(errorMessage);
+ console.error('❌ SwapMint error:', err);
+ throw err;
+ } finally {
+ setIsSwapMinting(false);
+ }
+ };
+
+ return {
+ executeSwapMint,
+ isSwapMinting,
+ swapMintHash,
+ swapMintError,
+ };
}
\ No newline at end of file
diff --git a/src/locales/en.json b/src/locales/en.json
index ffe3273..97323ab 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -20,6 +20,14 @@
},
"stake": {
"title": "Stake",
- "comingSoon": "Coming soon..."
+ "comingSoon": "Coming soon...",
+ "selectPool": "Select Pool",
+ "selectToken": "Select Token",
+ "amount": "Amount",
+ "stakeButton": "Stake",
+ "insufficientBalance": "Insufficient balance",
+ "amountUsed": "Amount Used",
+ "fee": "Fee",
+ "lpMinted": "LP Minted"
}
}