use super::constants::*; use uint::construct_uint; construct_uint! { pub struct U256(4); } #[derive(Clone, Copy, Debug, PartialEq)] pub enum Rounding { Down, Up, } #[derive(Debug, PartialEq)] pub enum MathError { Overflow, DivisionByZero, } /// Safe version of mul_div that returns Result to handle overflow pub fn try_mul_div( x: u128, y: u128, denominator: u128, rounding: Rounding, ) -> Result { if denominator == 0 { return Err(MathError::DivisionByZero); } let numerator = U256::from(x) * U256::from(y); let denominator = U256::from(denominator); let result = numerator / denominator; let remainder = numerator % denominator; // Check if result fits in u128 if result > U256::from(u128::MAX) { return Err(MathError::Overflow); } let base_result = result.low_u128(); match rounding { Rounding::Down => Ok(base_result), Rounding::Up => { if remainder > U256::zero() { base_result.checked_add(1).ok_or(MathError::Overflow) } else { Ok(base_result) } } } } /// Panicking version of mul_div - use when overflow should halt execution pub fn mul_div(x: u128, y: u128, denominator: u128, rounding: Rounding) -> u128 { try_mul_div(x, y, denominator, rounding) .unwrap_or_else(|err| panic!("Mathematical operation failed: {:?}", err)) } /// Calculates the fee ratio as basis points (10000 = 100%) pub fn calculate_fee_ratio(fee_amount: u128, total_asset_amount: u128) -> u128 { if total_asset_amount == 0 || fee_amount == 0 { return 0; } mul_div( fee_amount, BASIS_POINTS_DIVISOR, total_asset_amount, Rounding::Up, ) } /// Applies a fee ratio to an share price, reducing it proportionally pub fn apply_fee_ratio_to_rate(base_rate: u128, fee_ratio: u128) -> u128 { assert!( fee_ratio <= BASIS_POINTS_DIVISOR, "Fee ratio {} exceeds 100% (10000 basis points)", fee_ratio ); mul_div( base_rate, BASIS_POINTS_DIVISOR - fee_ratio, BASIS_POINTS_DIVISOR, Rounding::Down, ) } /// Calculates the percentage change between two rates in nano percent (100,000,000,000 = 100%) pub fn calculate_price_change_percent(current_rate: u128, previous_rate: u128) -> u64 { if previous_rate == 0 { return 0; } let rate_diff = current_rate.abs_diff(previous_rate); // Calculate nano percent, panic on overflow to prevent silent failures mul_div( rate_diff, NANO_PERCENT_DIVISOR as u128, previous_rate, Rounding::Down, ) .try_into() .expect("Price change percent exceeds u64 range") } /// Safe bounds checking for conversion operations pub fn validate_conversion_bounds(amount: u128, extra_decimals: u8) -> Result<(), MathError> { let decimal_multiplier = 10u128.pow(extra_decimals as u32); // Check if multiplication would overflow if amount > u128::MAX / decimal_multiplier { return Err(MathError::Overflow); } Ok(()) }