From e2198c9b31fd2001011fe17b6865acbee42b2d15 Mon Sep 17 00:00:00 2001 From: surbhi Date: Wed, 15 Oct 2025 13:36:04 -0400 Subject: [PATCH] adding token from pool logic and updating ABIs to support new smart contracts --- src/app/liquidity-party.json | 4 +- src/components/swap-form.tsx | 52 +++++++----- src/contracts/IPartyPlannerABI.ts | 2 +- src/contracts/IPartyPoolABI.ts | 87 ++++++++++++++++++-- src/hooks/usePartyPlanner.ts | 128 +++++++++++++++++++++++++++++- 5 files changed, 245 insertions(+), 28 deletions(-) diff --git a/src/app/liquidity-party.json b/src/app/liquidity-party.json index 4e427c0..ce32b17 100644 --- a/src/app/liquidity-party.json +++ b/src/app/liquidity-party.json @@ -1,6 +1,6 @@ { "31337": { - "IPartyPlanner": "0xB35D3C9b9f2Fd72FAAb282E8Dd56da31FAA30E3d", - "IPartyPoolViewer": "0x238213078DbD09f2D15F4c14c02300FA1b2A81BB" + "IPartyPlanner": "0x536F14E49e1Bb927003E83aDEBF295F3682ff121", + "IPartyPoolViewer": "0xd85BdcdaE4db1FAEB8eF93331525FE68D7C8B3f0" } } \ No newline at end of file diff --git a/src/components/swap-form.tsx b/src/components/swap-form.tsx index 5596941..779e89f 100644 --- a/src/components/swap-form.tsx +++ b/src/components/swap-form.tsx @@ -7,7 +7,7 @@ import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { ArrowDownUp, ChevronDown } from 'lucide-react'; import { useAccount } from 'wagmi'; -import { useTokenDetails, type TokenDetails } from '@/hooks/usePartyPlanner'; +import { useTokenDetails, useGetPoolsByToken, type TokenDetails } from '@/hooks/usePartyPlanner'; import { formatUnits } from 'viem'; export function SwapForm() { @@ -28,6 +28,9 @@ export function SwapForm() { // Use the custom hook to get all token details with a single batched RPC call const { tokenDetails, loading, error } = useTokenDetails(address); + // Get available tokens for the selected "from" token + const { availableTokens } = useGetPoolsByToken(selectedFromToken?.address); + // Trigger the hook to fetch data when address is available useEffect(() => { if (tokenDetails) { @@ -165,12 +168,14 @@ export function SwapForm() { value={toAmount} onChange={(e) => setToAmount(e.target.value)} className="text-2xl h-16" + disabled={!selectedFromToken} />
{isToDropdownOpen && (
- {tokenDetails && tokenDetails.length > 0 ? ( - tokenDetails.map((token) => ( - - )) + {availableTokens && availableTokens.length > 0 && tokenDetails ? ( + // Filter tokenDetails to only show tokens in availableTokens + tokenDetails + .filter((token) => + availableTokens.some((availToken) => + availToken.toLowerCase() === token.address.toLowerCase() + ) + ) + .map((token) => ( + + )) + ) : selectedFromToken ? ( +
+ {loading ? 'Loading available tokens...' : 'No tokens available for swap'} +
) : (
- {loading ? 'Loading tokens...' : 'No tokens available'} + Select a token in "You Pay" first
)}
diff --git a/src/contracts/IPartyPlannerABI.ts b/src/contracts/IPartyPlannerABI.ts index 22619a5..0d33dc2 100644 --- a/src/contracts/IPartyPlannerABI.ts +++ b/src/contracts/IPartyPlannerABI.ts @@ -317,7 +317,7 @@ const IPartyPlannerABI = [ }, { "type": "function", - "name": "swapMintImpl", + "name": "swapImpl", "inputs": [], "outputs": [ { diff --git a/src/contracts/IPartyPoolABI.ts b/src/contracts/IPartyPoolABI.ts index b4c4dbd..fe19721 100644 --- a/src/contracts/IPartyPoolABI.ts +++ b/src/contracts/IPartyPoolABI.ts @@ -147,6 +147,11 @@ const IPartyPoolABI = [ "name": "deadline", "type": "uint256", "internalType": "uint256" + }, + { + "name": "unwrap", + "type": "bool", + "internalType": "bool" } ], "outputs": [ @@ -186,6 +191,11 @@ const IPartyPoolABI = [ "name": "deadline", "type": "uint256", "internalType": "uint256" + }, + { + "name": "unwrap", + "type": "bool", + "internalType": "bool" } ], "outputs": [ @@ -318,7 +328,7 @@ const IPartyPoolABI = [ "internalType": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -365,7 +375,7 @@ const IPartyPoolABI = [ "internalType": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -457,6 +467,11 @@ const IPartyPoolABI = [ "name": "deadline", "type": "uint256", "internalType": "uint256" + }, + { + "name": "unwrap", + "type": "bool", + "internalType": "bool" } ], "outputs": [ @@ -476,7 +491,51 @@ const IPartyPoolABI = [ "internalType": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "payable" + }, + { + "type": "function", + "name": "swapAmounts", + "inputs": [ + { + "name": "inputTokenIndex", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "outputTokenIndex", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxAmountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "limitPrice", + "type": "int128", + "internalType": "int128" + } + ], + "outputs": [ + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "fee", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" }, { "type": "function", @@ -528,7 +587,7 @@ const IPartyPoolABI = [ "internalType": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -563,6 +622,11 @@ const IPartyPoolABI = [ "name": "deadline", "type": "uint256", "internalType": "uint256" + }, + { + "name": "unwrap", + "type": "bool", + "internalType": "bool" } ], "outputs": [ @@ -582,7 +646,7 @@ const IPartyPoolABI = [ "internalType": "uint256" } ], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -663,6 +727,19 @@ const IPartyPoolABI = [ ], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "wrapperToken", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IWETH9" + } + ], + "stateMutability": "view" + }, { "type": "event", "name": "Approval", diff --git a/src/hooks/usePartyPlanner.ts b/src/hooks/usePartyPlanner.ts index 342f510..83f51b4 100644 --- a/src/hooks/usePartyPlanner.ts +++ b/src/hooks/usePartyPlanner.ts @@ -4,6 +4,7 @@ import { useState, useEffect } from 'react'; import { usePublicClient } from 'wagmi'; import chainInfo from '@/app/liquidity-party.json'; import IPartyPlannerABI from '@/contracts/IPartyPlannerABI'; +import IPartyPoolABI from '@/contracts/IPartyPoolABI'; import { ERC20ABI } from '@/contracts/ERC20ABI'; export function useGetAllTokens(offset: number = 0, limit: number = 100) { @@ -73,7 +74,127 @@ export interface TokenDetails { name: string; symbol: string; decimals: number; - balance: bigint;P + balance: bigint; + index: number; +} + +export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offset: number = 0, limit: number = 100) { + const publicClient = usePublicClient(); + const [mounted, setMounted] = useState(false); + const [availableTokens, setAvailableTokens] = useState<`0x${string}`[] | null>(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // Handle hydration for Next.js static export + useEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + if (!mounted || !tokenAddress) { + setLoading(false); + return; + } + + const fetchPoolsFromTokens = 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()]?.IPartyPlanner; + + if (!address) { + setError('IPartyPlanner contract not found for current chain'); + setAvailableTokens([]); + return; + } + + // Call getPoolsByToken function + const poolsResult = await publicClient.readContract({ + address: address as `0x${string}`, + abi: IPartyPlannerABI, + functionName: 'getPoolsByToken', + args: [tokenAddress, BigInt(offset), BigInt(limit)], + }); + + console.log('Pools for token', tokenAddress, ':', poolsResult); + + // Get the symbol of the originally selected token + const selectedTokenSymbol = await publicClient.readContract({ + address: tokenAddress, + abi: ERC20ABI, + functionName: 'symbol', + }).catch(() => null); + + if (!selectedTokenSymbol) { + setAvailableTokens([]); + return; + } + + // For each pool, fetch all tokens in that pool + const allTokensInPools: `0x${string}`[] = []; + for (const poolAddress of poolsResult) { + try { + const tokensInPool = await publicClient.readContract({ + address: poolAddress, + abi: IPartyPoolABI, + functionName: 'allTokens', + }) as readonly `0x${string}`[]; + + // Add all tokens from this pool + allTokensInPools.push(...tokensInPool); + } catch (err) { + console.error('Error fetching tokens from pool', poolAddress, err); + } + } + + // Remove duplicates by address + const uniqueTokenAddresses = Array.from(new Set(allTokensInPools)); + + // Fetch symbols for all tokens and filter out those matching the selected token's symbol + const filteredTokens: `0x${string}`[] = []; + for (const token of uniqueTokenAddresses) { + try { + const tokenSymbol = await publicClient.readContract({ + address: token, + abi: ERC20ABI, + functionName: 'symbol', + }).catch(() => null); + + // Only include tokens with different symbols + if (tokenSymbol && tokenSymbol !== selectedTokenSymbol) { + filteredTokens.push(token); + } + } catch (err) { + console.error('Error fetching symbol for token', token, err); + } + } + + console.log('Available tokens to swap to (excluding', selectedTokenSymbol, '):', filteredTokens); + setAvailableTokens(filteredTokens); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to fetch pools and tokens'); + } finally { + setLoading(false); + } + }; + + fetchPoolsFromTokens(); + }, [publicClient, mounted, tokenAddress, offset, limit]); + + return { + availableTokens, + loading, + error, + isReady: mounted, + }; } export function useTokenDetails(userAddress: `0x${string}` | undefined) { @@ -102,7 +223,8 @@ export function useTokenDetails(userAddress: `0x${string}` | undefined) { const details: TokenDetails[] = []; // Make individual calls for each token - for (const tokenAddress of tokens) { + for (let i = 0; i < tokens.length; i++) { + const tokenAddress = tokens[i]; try { const [name, symbol, decimals, balance] = await Promise.all([ publicClient.readContract({ @@ -134,6 +256,7 @@ export function useTokenDetails(userAddress: `0x${string}` | undefined) { symbol: symbol as string, decimals: Number(decimals), balance: balance as bigint, + index: i, }); } catch (err) { // Add token with fallback values if individual call fails @@ -143,6 +266,7 @@ export function useTokenDetails(userAddress: `0x${string}` | undefined) { symbol: '???', decimals: 18, balance: BigInt(0), + index: i, }); } }