use alloy::primitives::{
aliases::{I24, U160},
U256,
};
use anyhow::{Context, Result};
use uniswap_sdk_core::{
entities::FractionBase,
prelude::{Currency, Percent, Price},
utils::FromBig,
};
use uniswap_v3_sdk::prelude::max_liquidity_for_amounts;
use waterpump_evm_amm_math::tick_math::get_sqrt_ratio_at_tick;
use crate::common::{
mint_amounts_utils::calculate_mint_amounts, slippage_utils::ratios_after_slippage,
};
fn i24_to_i32(tick: I24) -> i32 {
let bytes = tick.to_le_bytes::<3>();
let sign_byte = if bytes[2] & 0x80 != 0 { 0xFF } else { 0x00 };
i32::from_le_bytes([bytes[0], bytes[1], bytes[2], sign_byte])
}
fn encode_sqrt_ratio_x96_from_price(price: &Price<Currency, Currency>) -> U160 {
use waterpump_evm_amm_math::encode_sqrt_ratio::encode_sqrt_ratio_x96;
let fraction = price.as_fraction();
let num = U256::from_big_int(fraction.numerator);
let den = U256::from_big_int(fraction.denominator);
let result = encode_sqrt_ratio_x96(num, den).expect("encode_sqrt_ratio_x96 failed");
U160::from(result)
}
pub struct SlippageContext {
pub sqrt_ratio_x96_current: U160,
pub sqrt_ratio_x96_lower: U160,
pub sqrt_ratio_x96_upper: U160,
}
pub fn calculate_slippage_context(
token0_price: &Price<Currency, Currency>,
slippage_tolerance: &Percent,
) -> Result<SlippageContext> {
let sqrt_ratio_x96_current = encode_sqrt_ratio_x96_from_price(token0_price);
let (sqrt_ratio_x96_lower, sqrt_ratio_x96_upper) =
ratios_after_slippage(token0_price, slippage_tolerance)
.context("Failed to calculate ratios after slippage")?;
Ok(SlippageContext { sqrt_ratio_x96_current, sqrt_ratio_x96_lower, sqrt_ratio_x96_upper })
}
pub struct AddLiquidityAmounts {
pub amount0_desired: U256,
pub amount1_desired: U256,
pub amount0_min: U256,
pub amount1_min: U256,
pub liquidity: u128,
}
pub fn calculate_position_amounts(
tick_lower: I24,
tick_upper: I24,
amount0_desired: U256,
amount1_desired: U256,
slippage_ctx: &SlippageContext,
) -> Result<AddLiquidityAmounts> {
let sqrt_ratio_x96_tick_lower = U160::from(
get_sqrt_ratio_at_tick(i24_to_i32(tick_lower))
.context("Failed to get sqrt ratio at tick_lower")?,
);
let sqrt_ratio_x96_tick_upper = U160::from(
get_sqrt_ratio_at_tick(i24_to_i32(tick_upper))
.context("Failed to get sqrt ratio at tick_upper")?,
);
let liquidity_big = max_liquidity_for_amounts(
slippage_ctx.sqrt_ratio_x96_current,
sqrt_ratio_x96_tick_lower,
sqrt_ratio_x96_tick_upper,
amount0_desired,
amount1_desired,
false, );
let liquidity_u256 = U256::from_big_uint(liquidity_big);
if liquidity_u256 > U256::from(u128::MAX) {
return Err(anyhow::anyhow!("Liquidity overflow: value too large for u128"));
}
let liquidity = liquidity_u256.to::<u128>();
let mint_amounts_upper = calculate_mint_amounts(
tick_lower,
tick_upper,
liquidity,
slippage_ctx.sqrt_ratio_x96_upper,
)
.context("Failed to calculate mint amounts at upper price")?;
let mint_amounts_lower = calculate_mint_amounts(
tick_lower,
tick_upper,
liquidity,
slippage_ctx.sqrt_ratio_x96_lower,
)
.context("Failed to calculate mint amounts at lower price")?;
Ok(AddLiquidityAmounts {
amount0_desired,
amount1_desired,
amount0_min: mint_amounts_upper.amount0,
amount1_min: mint_amounts_lower.amount1,
liquidity,
})
}