first pass on user alert for slippage (this does not include bug where we calculate neg slippage because of price issue)
This commit is contained in:
@@ -370,6 +370,18 @@ export function SwapForm() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 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)
|
||||
) && (
|
||||
<div className="px-4 py-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
|
||||
<p className="text-sm text-yellow-600 dark:text-yellow-500 font-medium">⚠️ High Slippage Warning</p>
|
||||
<p className="text-xs text-yellow-600/80 dark:text-yellow-500/80 mt-1">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Gas Estimate, Slippage, and Fees */}
|
||||
{isConnected && fromAmount && toAmount && (
|
||||
<div className="px-4 py-2 bg-muted/30 rounded-lg space-y-2">
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user