waterpump-evm-pool-sdk 0.1.0

EVM pool SDK — viewers, infusers, harvesters, swappers for Uniswap V3/V4, PancakeSwap, Slipstream, Shadow, Algebra
Documentation
use alloy::primitives::{aliases::U160, uint, U256};
use anyhow::Result;
use uniswap_sdk_core::{
    entities::FractionBase,
    prelude::{BigInt, BigUint, Currency, Fraction, Percent, Price, ToBig},
    utils::FromBig,
};
use waterpump_evm_amm_math::{
    encode_sqrt_ratio::encode_sqrt_ratio_x96 as encode_sqrt_ratio_x96_u256,
    tick_math::{MAX_SQRT_RATIO, MIN_SQRT_RATIO},
};

const ONE: U160 = uint!(1_U160);
// Q192 as BigInt: 2^192
const Q192_BIG_INT: BigInt = BigInt::from_bits(BigUint::from_digits([0, 0, 0, 1, 0, 0, 0, 0]));

/// Encode sqrt ratio from BigInt numerator/denominator, returning U160
/// for backward compatibility with callers that expect U160.
fn encode_sqrt_ratio_x96(numerator: BigInt, denominator: BigInt) -> U160 {
    let num_u256 = U256::from_big_int(numerator);
    let den_u256 = U256::from_big_int(denominator);
    let result =
        encode_sqrt_ratio_x96_u256(num_u256, den_u256).expect("encode_sqrt_ratio_x96 failed");
    // Truncate U256 to U160 (sqrt ratios fit in 160 bits)
    U160::from(result)
}

/// Returns the lower and upper sqrt ratios if the price 'slips' up to slippage
/// tolerance percentage
///
/// This function calculates the price bounds after applying slippage tolerance,
/// similar to Position::ratios_after_slippage. It takes the current token0
/// price and applies slippage tolerance to determine the minimum and maximum
/// prices that should be accepted.
///
/// ## Arguments
///
/// * `token0_price`: The current price of token0 in terms of token1
///   (Price<Currency, Currency>)
/// * `slippage_tolerance`: The amount by which the price can 'slip' before the
///   transaction will revert
///
/// ## Returns
///
/// (sqrt_ratio_x96_lower, sqrt_ratio_x96_upper)
pub fn ratios_after_slippage(
    token0_price: &Price<Currency, Currency>,
    slippage_tolerance: &Percent,
) -> Result<(U160, U160)> {
    let one = Percent::new(1, 1);
    let token0_price_fraction = token0_price.as_fraction();

    // Calculate price bounds: lower = price * (1 - slippage), upper = price * (1 +
    // slippage)
    let price_lower = (one.clone() - slippage_tolerance).as_fraction() * &token0_price_fraction;
    let price_upper = token0_price_fraction * ((one + slippage_tolerance).as_fraction());

    // Encode lower price to sqrt ratio
    let min_sqrt_ratio_u160 = U160::from(MIN_SQRT_RATIO);
    let max_sqrt_ratio_u160 = U160::from(MAX_SQRT_RATIO);

    let mut sqrt_ratio_x96_lower =
        encode_sqrt_ratio_x96(price_lower.numerator, price_lower.denominator);

    // Ensure sqrt_ratio_x96_lower is above MIN_SQRT_RATIO
    if sqrt_ratio_x96_lower <= min_sqrt_ratio_u160 {
        sqrt_ratio_x96_lower = min_sqrt_ratio_u160 + ONE;
    }

    // Encode upper price to sqrt ratio
    let sqrt_ratio_x96_upper = {
        // Convert MAX_SQRT_RATIO (U160) to BigInt for calculation
        let max_sqrt_ratio_big = max_sqrt_ratio_u160.to_big_int();
        if price_upper >= Fraction::new(max_sqrt_ratio_big.pow(2), Q192_BIG_INT) {
            max_sqrt_ratio_u160 - ONE
        } else {
            encode_sqrt_ratio_x96(price_upper.numerator, price_upper.denominator)
        }
    };

    Ok((sqrt_ratio_x96_lower, sqrt_ratio_x96_upper))
}