sol-trade-sdk 5.0.2

A high-performance Rust SDK for Solana DEX trading.
Documentation
/// Calculate transaction fee based on amount and fee basis points
///
/// # Parameters
/// * `amount` - Transaction amount
/// * `fee_basis_points` - Fee basis points, 1 basis point = 0.01%
///
/// # Examples
/// * fee_basis_points = 1   -> 0.01% fee
/// * fee_basis_points = 10  -> 0.1% fee
/// * fee_basis_points = 25  -> 0.25% fee (common exchange rate)
/// * fee_basis_points = 100 -> 1% fee
#[inline(always)]
pub const fn compute_fee(amount: u128, fee_basis_points: u128) -> u128 {
    let whole = match (amount / 10_000).checked_mul(fee_basis_points) {
        Some(value) => value,
        None => return u128::MAX,
    };
    let remainder_product = match (amount % 10_000).checked_mul(fee_basis_points) {
        Some(value) => value,
        None => return u128::MAX,
    };
    match whole.checked_add(ceil_div(remainder_product, 10_000)) {
        Some(value) => value,
        None => u128::MAX,
    }
}

/// Ceiling division implementation
/// Ceiling division that ensures results are not lost due to integer division precision
///
/// # Parameters
/// * `a` - Dividend
/// * `b` - Divisor
///
/// # Returns
/// Returns the ceiling result of a/b
#[inline(always)]
pub const fn ceil_div(a: u128, b: u128) -> u128 {
    let quotient = a / b;
    if a % b == 0 {
        quotient
    } else {
        quotient + 1
    }
}

/// Maximum slippage in basis points (99.99% = 9999 bps)
/// This prevents the wrap amount from doubling when slippage is 100%
pub const MAX_SLIPPAGE_BASIS_POINTS: u64 = 9999;

/// Clamp slippage so 100% (10000 bps) cannot zero min-out, underflow, or divide by zero.
#[inline(always)]
pub const fn clamp_slippage_basis_points(basis_points: u64) -> u64 {
    if basis_points > MAX_SLIPPAGE_BASIS_POINTS {
        MAX_SLIPPAGE_BASIS_POINTS
    } else {
        basis_points
    }
}

/// `u128` variant for Bonk helpers that take slippage as `u128`.
#[inline(always)]
pub const fn clamp_slippage_basis_points_u128(basis_points: u128) -> u128 {
    if basis_points > MAX_SLIPPAGE_BASIS_POINTS as u128 {
        MAX_SLIPPAGE_BASIS_POINTS as u128
    } else {
        basis_points
    }
}

/// Calculate buy amount with slippage protection
/// Add slippage percentage to the amount to ensure successful purchase
///
/// # Parameters
/// * `amount` - Original transaction amount
/// * `basis_points` - Slippage basis points, 1 basis point = 0.01%
///
/// # Examples
/// * basis_points = 1   -> 0.01% slippage
/// * basis_points = 10  -> 0.1% slippage
/// * basis_points = 100 -> 1% slippage
/// * basis_points = 500 -> 5% slippage
///
/// # Note
/// Basis points are clamped to MAX_SLIPPAGE_BASIS_POINTS (9999 = 99.99%)
/// to prevent the amount from doubling when basis_points = 10000.
#[inline(always)]
pub const fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 {
    let bps = clamp_slippage_basis_points(basis_points);
    let result = amount as u128 + (amount as u128 * bps as u128 / 10_000);
    if result > u64::MAX as u128 {
        u64::MAX
    } else {
        result as u64
    }
}

/// Calculate sell amount with slippage protection
/// Subtract slippage percentage from the amount to ensure successful sale
///
/// # Parameters
/// * `amount` - Original transaction amount
/// * `basis_points` - Slippage basis points, 1 basis point = 0.01%
///
/// # Examples
/// * basis_points = 1   -> 0.01% slippage
/// * basis_points = 10  -> 0.1% slippage
/// * basis_points = 100 -> 1% slippage
/// * basis_points = 500 -> 5% slippage
#[inline(always)]
pub const fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 {
    if amount == 0 {
        return 0;
    }
    let bps = clamp_slippage_basis_points(basis_points);
    amount - (amount as u128 * bps as u128 / 10_000) as u64
}

/// Calculate a protocol min-out using `floor(amount * (1 - slippage))`.
#[inline(always)]
pub const fn calculate_min_amount_out(amount: u64, basis_points: u64) -> u64 {
    let bps = clamp_slippage_basis_points(basis_points);
    (amount as u128 * (10_000 - bps) as u128 / 10_000) as u64
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ceil_div_handles_u128_max_without_addition_overflow() {
        assert_eq!(ceil_div(u128::MAX, u128::MAX), 1);
        assert_eq!(ceil_div(u128::MAX, 2), u128::MAX / 2 + 1);
    }

    #[test]
    fn compute_fee_handles_large_amounts_without_multiplication_overflow() {
        assert_eq!(compute_fee(u128::MAX, 1), ceil_div(u128::MAX, 10_000));
        assert_eq!(compute_fee(u128::MAX, 10_000), u128::MAX);
    }

    #[test]
    fn slippage_helpers_are_deterministic_at_numeric_boundaries() {
        assert_eq!(calculate_with_slippage_buy(u64::MAX, 100), u64::MAX);
        assert_eq!(calculate_with_slippage_sell(u64::MAX, 100), 18_262_276_632_972_456_099);
        assert_eq!(calculate_with_slippage_sell(10_000, u64::MAX), 1);
        assert_eq!(calculate_with_slippage_sell(1, u64::MAX), 1);
    }

    #[test]
    fn min_amount_out_uses_protocol_floor_rounding() {
        assert_eq!(calculate_min_amount_out(101, 100), 99);
        assert_eq!(calculate_min_amount_out(10_000, u64::MAX), 1);
        assert_eq!(calculate_min_amount_out(1, u64::MAX), 0);
        assert_eq!(calculate_min_amount_out(u64::MAX, 0), u64::MAX);
    }

    #[test]
    fn clamp_slippage_basis_points_caps_at_max() {
        assert_eq!(clamp_slippage_basis_points(0), 0);
        assert_eq!(clamp_slippage_basis_points(100), 100);
        assert_eq!(
            clamp_slippage_basis_points(MAX_SLIPPAGE_BASIS_POINTS),
            MAX_SLIPPAGE_BASIS_POINTS
        );
        assert_eq!(clamp_slippage_basis_points(10_000), MAX_SLIPPAGE_BASIS_POINTS);
        assert_eq!(clamp_slippage_basis_points(u64::MAX), MAX_SLIPPAGE_BASIS_POINTS);

        assert_eq!(clamp_slippage_basis_points_u128(0), 0);
        assert_eq!(clamp_slippage_basis_points_u128(100), 100);
        assert_eq!(clamp_slippage_basis_points_u128(10_000), MAX_SLIPPAGE_BASIS_POINTS as u128);
        assert_eq!(clamp_slippage_basis_points_u128(u128::MAX), MAX_SLIPPAGE_BASIS_POINTS as u128);
    }
}