Compare commits
2 Commits
4523195b78
...
d4e41821a6
| Author | SHA1 | Date | |
|---|---|---|---|
| d4e41821a6 | |||
| d07ff55c13 |
@@ -173,7 +173,7 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
}, [stakeAmount, selectedToken, mode]);
|
}, [stakeAmount, selectedToken, mode]);
|
||||||
|
|
||||||
// Fetch swap mint amounts (for stake mode)
|
// Fetch swap mint amounts (for stake mode)
|
||||||
const { swapMintAmounts, loading: swapMintLoading } = useSwapMintAmounts(
|
const { swapMintAmounts, loading: swapMintLoading, error: swapMintError } = useSwapMintAmounts(
|
||||||
mode === 'stake' ? selectedPool?.address : undefined,
|
mode === 'stake' ? selectedPool?.address : undefined,
|
||||||
mode === 'stake' ? inputTokenIndex : undefined,
|
mode === 'stake' ? inputTokenIndex : undefined,
|
||||||
mode === 'stake' ? maxAmountIn : undefined,
|
mode === 'stake' ? maxAmountIn : undefined,
|
||||||
@@ -181,7 +181,7 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Fetch burn swap amounts (for unstake mode, only when not redeeming all)
|
// Fetch burn swap amounts (for unstake mode, only when not redeeming all)
|
||||||
const { burnSwapAmounts, loading: burnSwapLoading } = useBurnSwapAmounts(
|
const { burnSwapAmounts, loading: burnSwapLoading, error: burnSwapError } = useBurnSwapAmounts(
|
||||||
mode === 'unstake' && !redeemAll ? selectedPool?.address : undefined,
|
mode === 'unstake' && !redeemAll ? selectedPool?.address : undefined,
|
||||||
mode === 'unstake' && !redeemAll ? maxAmountIn : undefined,
|
mode === 'unstake' && !redeemAll ? maxAmountIn : undefined,
|
||||||
mode === 'unstake' && !redeemAll ? inputTokenIndex : undefined,
|
mode === 'unstake' && !redeemAll ? inputTokenIndex : undefined,
|
||||||
@@ -726,8 +726,27 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Error messages for output zero */}
|
||||||
|
{mode === 'stake' && swapMintError && stakeAmount && (
|
||||||
|
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||||
|
<p className="text-sm text-destructive font-medium">⚠️ Cannot Process Stake</p>
|
||||||
|
<p className="text-xs text-destructive/80 mt-1">
|
||||||
|
{swapMintError}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{mode === 'unstake' && !redeemAll && burnSwapError && stakeAmount && (
|
||||||
|
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||||
|
<p className="text-sm text-destructive font-medium">⚠️ Cannot Process Unstake</p>
|
||||||
|
<p className="text-xs text-destructive/80 mt-1">
|
||||||
|
{burnSwapError}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Slippage warnings - consolidated for both stake and unstake modes */}
|
{/* Slippage warnings - consolidated for both stake and unstake modes */}
|
||||||
{mode === 'stake' && slippageExceedsLimit && swapMintAmounts?.calculatedSlippage !== undefined && (
|
{mode === 'stake' && !swapMintError && slippageExceedsLimit && swapMintAmounts?.calculatedSlippage !== undefined && (
|
||||||
<SlippageWarning
|
<SlippageWarning
|
||||||
slippage={swapMintAmounts.calculatedSlippage}
|
slippage={swapMintAmounts.calculatedSlippage}
|
||||||
action="stake"
|
action="stake"
|
||||||
@@ -735,7 +754,7 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{mode === 'stake' && !slippageExceedsLimit && slippageIsHigh && swapMintAmounts?.calculatedSlippage !== undefined && (
|
{mode === 'stake' && !swapMintError && !slippageExceedsLimit && slippageIsHigh && swapMintAmounts?.calculatedSlippage !== undefined && (
|
||||||
<SlippageWarning
|
<SlippageWarning
|
||||||
slippage={swapMintAmounts.calculatedSlippage}
|
slippage={swapMintAmounts.calculatedSlippage}
|
||||||
action="stake"
|
action="stake"
|
||||||
@@ -743,7 +762,7 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{mode === 'unstake' && !redeemAll && unstakeSlippageExceedsLimit && burnSwapAmounts?.calculatedSlippage !== undefined && (
|
{mode === 'unstake' && !redeemAll && !burnSwapError && unstakeSlippageExceedsLimit && burnSwapAmounts?.calculatedSlippage !== undefined && (
|
||||||
<SlippageWarning
|
<SlippageWarning
|
||||||
slippage={burnSwapAmounts.calculatedSlippage}
|
slippage={burnSwapAmounts.calculatedSlippage}
|
||||||
action="unstake"
|
action="unstake"
|
||||||
@@ -751,7 +770,7 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{mode === 'unstake' && !redeemAll && !unstakeSlippageExceedsLimit && slippageIsHigh && burnSwapAmounts?.calculatedSlippage !== undefined && (
|
{mode === 'unstake' && !redeemAll && !burnSwapError && !unstakeSlippageExceedsLimit && slippageIsHigh && burnSwapAmounts?.calculatedSlippage !== undefined && (
|
||||||
<SlippageWarning
|
<SlippageWarning
|
||||||
slippage={burnSwapAmounts.calculatedSlippage}
|
slippage={burnSwapAmounts.calculatedSlippage}
|
||||||
action="unstake"
|
action="unstake"
|
||||||
@@ -807,10 +826,10 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
|||||||
!selectedPool ||
|
!selectedPool ||
|
||||||
isAmountExceedingBalance ||
|
isAmountExceedingBalance ||
|
||||||
(mode === 'stake'
|
(mode === 'stake'
|
||||||
? (!selectedToken || isSwapMinting || slippageExceedsLimit)
|
? (!selectedToken || isSwapMinting || slippageExceedsLimit || !!swapMintError)
|
||||||
: (redeemAll
|
: (redeemAll
|
||||||
? isBurning
|
? isBurning
|
||||||
: (!selectedToken || inputTokenIndex === undefined || isBurnSwapping || unstakeSlippageExceedsLimit)))
|
: (!selectedToken || inputTokenIndex === undefined || isBurnSwapping || unstakeSlippageExceedsLimit || !!burnSwapError)))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{!isConnected
|
{!isConnected
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ export function SwapForm() {
|
|||||||
const swapResult = swapAmounts[0]; // Get the first (and should be only) result
|
const swapResult = swapAmounts[0]; // Get the first (and should be only) result
|
||||||
const formattedAmount = formatUnits(swapResult.amountOut, selectedToToken.decimals);
|
const formattedAmount = formatUnits(swapResult.amountOut, selectedToToken.decimals);
|
||||||
setToAmount(formattedAmount);
|
setToAmount(formattedAmount);
|
||||||
|
} else {
|
||||||
|
setToAmount('');
|
||||||
}
|
}
|
||||||
}, [swapAmounts, selectedToToken, hasInsufficientBalance]);
|
}, [swapAmounts, selectedToToken, hasInsufficientBalance]);
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ export interface SwapRoute {
|
|||||||
poolAddress: `0x${string}`;
|
poolAddress: `0x${string}`;
|
||||||
inputTokenIndex: number;
|
inputTokenIndex: number;
|
||||||
outputTokenIndex: number;
|
outputTokenIndex: number;
|
||||||
|
inputTokenDecimal: number;
|
||||||
|
outputTokenDecimal: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AvailableToken {
|
export interface AvailableToken {
|
||||||
@@ -234,11 +236,29 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
functionName: 'symbol',
|
functionName: 'symbol',
|
||||||
}).catch(() => null);
|
}).catch(() => null);
|
||||||
|
|
||||||
// Skip tokens with the same symbol as the selected token
|
const inputTokenDecimal = await publicClient.readContract({
|
||||||
|
address: tokenAddress,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'decimals',
|
||||||
|
}).catch(() => null);
|
||||||
|
|
||||||
|
const outputTokenDecimal = await publicClient.readContract({
|
||||||
|
address: outputTokenAddress,
|
||||||
|
abi: ERC20ABI,
|
||||||
|
functionName: 'decimals',
|
||||||
|
}).catch(() => null);
|
||||||
|
|
||||||
|
// Skip tokens with the same symbol as the selected token
|
||||||
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip tokens if decimals failed to load
|
||||||
|
if (inputTokenDecimal === null || outputTokenDecimal === null) {
|
||||||
|
console.error(`Failed to load decimals for token ${outputTokenAddress} or ${tokenAddress}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Create or update the available token entry
|
// Create or update the available token entry
|
||||||
const tokenKey = outputTokenAddress.toLowerCase();
|
const tokenKey = outputTokenAddress.toLowerCase();
|
||||||
if (!tokenRoutesMap.has(tokenKey)) {
|
if (!tokenRoutesMap.has(tokenKey)) {
|
||||||
@@ -254,6 +274,8 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
|||||||
poolAddress,
|
poolAddress,
|
||||||
inputTokenIndex,
|
inputTokenIndex,
|
||||||
outputTokenIndex,
|
outputTokenIndex,
|
||||||
|
inputTokenDecimal,
|
||||||
|
outputTokenDecimal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -505,6 +527,8 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
|||||||
|
|
||||||
// Calculate TVL (approximate by getting first token balance and multiplying by 3)
|
// Calculate TVL (approximate by getting first token balance and multiplying by 3)
|
||||||
const tokenBalance = Number(balance) / Math.pow(10, decimals);
|
const tokenBalance = Number(balance) / Math.pow(10, decimals);
|
||||||
|
|
||||||
|
console.log('tokenBalance', tokenBalance);
|
||||||
const approximateTVL = tokenBalance * 3;
|
const approximateTVL = tokenBalance * 3;
|
||||||
tvlStr = formatTVL(approximateTVL);
|
tvlStr = formatTVL(approximateTVL);
|
||||||
}
|
}
|
||||||
@@ -632,7 +656,6 @@ export function useSwapMintAmounts(
|
|||||||
|
|
||||||
const basePrice = Number(poolPriceInt128) / (2 ** 64);
|
const basePrice = Number(poolPriceInt128) / (2 ** 64);
|
||||||
poolPrice = 1 / (basePrice * Math.pow(10, 18 - inputTokenDecimals));
|
poolPrice = 1 / (basePrice * Math.pow(10, 18 - inputTokenDecimals));
|
||||||
|
|
||||||
// Calculate slippage
|
// Calculate slippage
|
||||||
const decimalsMultiplier = Math.pow(10, inputTokenDecimals);
|
const decimalsMultiplier = Math.pow(10, inputTokenDecimals);
|
||||||
const lpMinted = Number(result[1]) / Math.pow(10, 18);
|
const lpMinted = Number(result[1]) / Math.pow(10, 18);
|
||||||
@@ -641,7 +664,6 @@ export function useSwapMintAmounts(
|
|||||||
|
|
||||||
const swapPrice = lpMinted / (amountIn - fee);
|
const swapPrice = lpMinted / (amountIn - fee);
|
||||||
calculatedSlippage = ((poolPrice - swapPrice) / poolPrice) * 100;
|
calculatedSlippage = ((poolPrice - swapPrice) / poolPrice) * 100;
|
||||||
|
|
||||||
} catch (priceErr) {
|
} catch (priceErr) {
|
||||||
console.error('Error fetching poolPrice or calculating slippage:', priceErr);
|
console.error('Error fetching poolPrice or calculating slippage:', priceErr);
|
||||||
}
|
}
|
||||||
@@ -654,7 +676,8 @@ export function useSwapMintAmounts(
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error calling swapMintAmounts:', err);
|
console.error('Error calling swapMintAmounts:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to fetch swap mint amounts');
|
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch swap mint amounts';
|
||||||
|
setError(errorMessage);
|
||||||
setSwapMintAmounts(null);
|
setSwapMintAmounts(null);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -763,7 +786,8 @@ export function useBurnSwapAmounts(
|
|||||||
setBurnSwapAmounts(parsedAmounts);
|
setBurnSwapAmounts(parsedAmounts);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error calling burnSwapAmounts:', err);
|
console.error('Error calling burnSwapAmounts:', err);
|
||||||
setError(err instanceof Error ? err.message : 'Failed to fetch burn swap amounts');
|
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch burn swap amounts';
|
||||||
|
setError(errorMessage);
|
||||||
setBurnSwapAmounts(null);
|
setBurnSwapAmounts(null);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -21,13 +21,12 @@ const Q96 = 1n << 96n;
|
|||||||
*/
|
*/
|
||||||
export function calculateSlippage(
|
export function calculateSlippage(
|
||||||
marketPrice: number,
|
marketPrice: number,
|
||||||
swapOutputAmount: bigint,
|
swapOutputAmount: number,
|
||||||
swapInputAmount: bigint,
|
swapInputAmount: number,
|
||||||
swapFee: bigint
|
swapFee: number
|
||||||
): number {
|
): number {
|
||||||
// Calculate actual swap price with decimal correction
|
// Calculate actual swap price with decimal correction
|
||||||
const swapPrice = Number(swapOutputAmount) / (Number(swapInputAmount) - Number(swapFee));
|
const swapPrice = swapOutputAmount / ((swapInputAmount) - (swapFee));
|
||||||
|
|
||||||
// Calculate slippage percentage: ((swapPrice - marketPrice) / marketPrice) * 100
|
// Calculate slippage percentage: ((swapPrice - marketPrice) / marketPrice) * 100
|
||||||
const slippage = ((marketPrice - swapPrice) / marketPrice) * 100;
|
const slippage = ((marketPrice - swapPrice) / marketPrice) * 100;
|
||||||
|
|
||||||
@@ -194,11 +193,18 @@ export function useSwapAmounts(
|
|||||||
args: [route.poolAddress, BigInt(route.inputTokenIndex), BigInt(route.outputTokenIndex)],
|
args: [route.poolAddress, BigInt(route.inputTokenIndex), BigInt(route.outputTokenIndex)],
|
||||||
}) as bigint;
|
}) as bigint;
|
||||||
|
|
||||||
// Convert Q64 format to decimal (price = priceValue / 2^64)
|
|
||||||
const marketPrice = Number(priceInt128) / 2 ** 64;
|
|
||||||
|
|
||||||
|
// Convert Q128 format to decimal (price = priceValue / 2^128)
|
||||||
|
// Then apply decimal conversion: 10^18 / 10^outputTokenDecimals
|
||||||
|
const priceQ128 = Number(priceInt128) / 2 ** 128;
|
||||||
|
const decimalAdjustment = 10 ** Math.abs(route.outputTokenDecimal - route.inputTokenDecimal);
|
||||||
|
const marketPrice = priceQ128 / decimalAdjustment;
|
||||||
|
const swapOutputAmountDecimal = Number(swapOutputAmount) / 10 ** route.outputTokenDecimal;
|
||||||
|
const swapInputAmountDecimal = Number(swapInputAmount) / 10 ** route.inputTokenDecimal;
|
||||||
|
const swapFeeDecimal = Number(inFee) / 10 ** route.inputTokenDecimal;
|
||||||
// Calculate slippage using the reusable function
|
// Calculate slippage using the reusable function
|
||||||
calculatedSlippage = calculateSlippage(marketPrice, swapOutputAmount, swapInputAmount, inFee);
|
|
||||||
|
calculatedSlippage = calculateSlippage(marketPrice, swapOutputAmountDecimal, swapInputAmountDecimal, swapFeeDecimal);
|
||||||
console.log('calculatedSlippage', calculatedSlippage)
|
console.log('calculatedSlippage', calculatedSlippage)
|
||||||
} catch (slippageErr) {
|
} catch (slippageErr) {
|
||||||
console.error(`Error calculating slippage for ${token.symbol}:`, slippageErr);
|
console.error(`Error calculating slippage for ${token.symbol}:`, slippageErr);
|
||||||
|
|||||||
Reference in New Issue
Block a user