diff --git a/src/components/swap-form.tsx b/src/components/swap-form.tsx index 62c538c..62c6242 100644 --- a/src/components/swap-form.tsx +++ b/src/components/swap-form.tsx @@ -370,6 +370,18 @@ export function SwapForm() { )} + {/* High slippage warning - show if calculated slippage exceeds max slippage OR 5% */} + {swapAmounts && swapAmounts.length > 0 && swapAmounts[0].calculatedSlippage !== undefined && ( + Math.abs(swapAmounts[0].calculatedSlippage) > Math.max(currentSlippage, 5) + ) && ( +
+

⚠️ High Slippage Warning

+

+ The estimated slippage for this swap is {Math.abs(swapAmounts[0].calculatedSlippage).toFixed(2)}%. You may lose money due to low liquidity in this pool. +

+
+ )} + {/* Gas Estimate, Slippage, and Fees */} {isConnected && fromAmount && toAmount && (
diff --git a/src/hooks/usePartyPool.ts b/src/hooks/usePartyPool.ts index f494731..1781fe0 100644 --- a/src/hooks/usePartyPool.ts +++ b/src/hooks/usePartyPool.ts @@ -2,9 +2,10 @@ import { useState, useEffect, useCallback } from 'react'; import { usePublicClient, useWalletClient } from 'wagmi'; -import { decodeEventLog } from 'viem'; +import { decodeEventLog, parseUnits } from 'viem'; import IPartyPoolABI from '@/contracts/IPartyPoolABI'; -import chainInfo from '../../../lmsr-amm/liqp-deployments.json'; +import chainInfo from '../contracts/liqp-deployments.json'; +import IPartyInfoABI from '@/contracts/IPartyInfoABI'; import type { AvailableToken } from './usePartyPlanner'; // Q96 constant for price calculations @@ -20,6 +21,7 @@ export interface SwapAmountResult { kappa: bigint; inputTokenIndex: number; outputTokenIndex: number; + calculatedSlippage?: number; // Percentage, e.g. 5.5 means 5.5% } /** @@ -95,8 +97,7 @@ export function useSwapAmounts( try { setLoading(true); - const amountInWei = BigInt(Math.floor(parseFloat(fromAmount) * Math.pow(10, fromTokenDecimals))); - + const amountInTokenUnits = parseUnits(fromAmount, fromTokenDecimals); // Calculate limit price based on slippage tolerance // limitPrice in Q96 format = Q96 * (100 + slippage%) / 100 // This represents the maximum acceptable price ratio (1 + slippage%) @@ -111,6 +112,8 @@ export function useSwapAmounts( }); const results: SwapAmountResult[] = []; + const chainId = await publicClient.getChainId(); + const partyInfoAddress = (chainInfo as any)[chainId]?.v1?.PartyInfo as `0x${string}` | undefined; // Calculate swap amounts for ALL routes of each token for (const token of availableTokens) { @@ -129,7 +132,7 @@ export function useSwapAmounts( args: [ BigInt(route.inputTokenIndex), BigInt(route.outputTokenIndex), - amountInWei, + amountInTokenUnits, limitPrice, ], }) as readonly [bigint, bigint, bigint]; @@ -143,6 +146,45 @@ export function useSwapAmounts( functionName: 'kappa', }) as bigint; + // Calculate slippage for this route + let calculatedSlippage: number | undefined; + if (partyInfoAddress) { + try { + // Get swap amounts with NO LIMIT (0 means no price limit) + const [swapInputAmount, swapOutputAmount, inFee] = await publicClient.readContract({ + address: route.poolAddress, + abi: IPartyPoolABI, + functionName: 'swapAmounts', + args: [ + BigInt(route.inputTokenIndex), + BigInt(route.outputTokenIndex), + amountInTokenUnits, + 0n, // NO LIMIT + ], + }) as readonly [bigint, bigint, bigint]; + + // Get the current market price from PoolInfo + const priceInt128 = await publicClient.readContract({ + address: partyInfoAddress, + abi: IPartyInfoABI, + functionName: 'price', + args: [route.poolAddress, BigInt(route.inputTokenIndex), BigInt(route.outputTokenIndex)], + }) as bigint; + + // Convert Q64 format to decimal (price = priceValue / 2^64) + const price = Number(priceInt128) / 2 ** 64; + + // Calculate actual swap price with decimal correction + const swapPrice = Number(swapOutputAmount) / ((Number(swapInputAmount)) - Number(inFee)); + // Calculate slippage: 1 - (actualPrice / marketPrice) + const slippage = 1 - (swapPrice / price); + calculatedSlippage = slippage * 100; // Convert to percentage + console.log('calculatedSlippage', calculatedSlippage) + } catch (slippageErr) { + console.error(`Error calculating slippage for ${token.symbol}:`, slippageErr); + } + } + routeResults.push({ tokenAddress: token.address, tokenSymbol: token.symbol, @@ -153,6 +195,7 @@ export function useSwapAmounts( kappa, inputTokenIndex: route.inputTokenIndex, outputTokenIndex: route.outputTokenIndex, + calculatedSlippage, }); } catch (err) { console.error(`Error calculating swap for ${token.symbol} via ${route.poolAddress}:`, err);