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>
|
</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 */}
|
{/* Gas Estimate, Slippage, and Fees */}
|
||||||
{isConnected && fromAmount && toAmount && (
|
{isConnected && fromAmount && toAmount && (
|
||||||
<div className="px-4 py-2 bg-muted/30 rounded-lg space-y-2">
|
<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 { useState, useEffect, useCallback } from 'react';
|
||||||
import { usePublicClient, useWalletClient } from 'wagmi';
|
import { usePublicClient, useWalletClient } from 'wagmi';
|
||||||
import { decodeEventLog } from 'viem';
|
import { decodeEventLog, parseUnits } from 'viem';
|
||||||
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
|
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';
|
import type { AvailableToken } from './usePartyPlanner';
|
||||||
|
|
||||||
// Q96 constant for price calculations
|
// Q96 constant for price calculations
|
||||||
@@ -20,6 +21,7 @@ export interface SwapAmountResult {
|
|||||||
kappa: bigint;
|
kappa: bigint;
|
||||||
inputTokenIndex: number;
|
inputTokenIndex: number;
|
||||||
outputTokenIndex: number;
|
outputTokenIndex: number;
|
||||||
|
calculatedSlippage?: number; // Percentage, e.g. 5.5 means 5.5%
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,8 +97,7 @@ export function useSwapAmounts(
|
|||||||
try {
|
try {
|
||||||
setLoading(true);
|
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
|
// Calculate limit price based on slippage tolerance
|
||||||
// limitPrice in Q96 format = Q96 * (100 + slippage%) / 100
|
// limitPrice in Q96 format = Q96 * (100 + slippage%) / 100
|
||||||
// This represents the maximum acceptable price ratio (1 + slippage%)
|
// This represents the maximum acceptable price ratio (1 + slippage%)
|
||||||
@@ -111,6 +112,8 @@ export function useSwapAmounts(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const results: SwapAmountResult[] = [];
|
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
|
// Calculate swap amounts for ALL routes of each token
|
||||||
for (const token of availableTokens) {
|
for (const token of availableTokens) {
|
||||||
@@ -129,7 +132,7 @@ export function useSwapAmounts(
|
|||||||
args: [
|
args: [
|
||||||
BigInt(route.inputTokenIndex),
|
BigInt(route.inputTokenIndex),
|
||||||
BigInt(route.outputTokenIndex),
|
BigInt(route.outputTokenIndex),
|
||||||
amountInWei,
|
amountInTokenUnits,
|
||||||
limitPrice,
|
limitPrice,
|
||||||
],
|
],
|
||||||
}) as readonly [bigint, bigint, bigint];
|
}) as readonly [bigint, bigint, bigint];
|
||||||
@@ -143,6 +146,45 @@ export function useSwapAmounts(
|
|||||||
functionName: 'kappa',
|
functionName: 'kappa',
|
||||||
}) as bigint;
|
}) 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({
|
routeResults.push({
|
||||||
tokenAddress: token.address,
|
tokenAddress: token.address,
|
||||||
tokenSymbol: token.symbol,
|
tokenSymbol: token.symbol,
|
||||||
@@ -153,6 +195,7 @@ export function useSwapAmounts(
|
|||||||
kappa,
|
kappa,
|
||||||
inputTokenIndex: route.inputTokenIndex,
|
inputTokenIndex: route.inputTokenIndex,
|
||||||
outputTokenIndex: route.outputTokenIndex,
|
outputTokenIndex: route.outputTokenIndex,
|
||||||
|
calculatedSlippage,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Error calculating swap for ${token.symbol} via ${route.poolAddress}:`, err);
|
console.error(`Error calculating swap for ${token.symbol} via ${route.poolAddress}:`, err);
|
||||||
|
|||||||
Reference in New Issue
Block a user