equanetwork-math 0.0.4

The Equa Network program math library
Documentation
#[cfg(feature = "floats")]
use libm::pow;

#[cfg(feature = "wasm")]
use equanetwork_macros::wasm_expose;

use super::consts::Q64_ONE;
use super::error::{CoreError, AMOUNT_EXCEEDS_MAX_U64, ARITHMETIC_OVERFLOW, DIVISION_BY_ZERO};
use super::U128;

#[cfg(feature = "floats")]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 {
    let power = pow(10f64, decimals as f64);
    amount as f64 / power
}

#[cfg(feature = "floats")]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn ui_amount_to_amount(amount: f64, decimals: u8) -> u64 {
    let power = pow(10f64, decimals as f64);
    (amount * power) as u64
}

/// Convert amount · Q64.64 price → amount (floor or ceil).
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn mul_q64(amount: u64, price: U128, round_up: bool) -> Result<u64, CoreError> {
    let price: u128 = price.into();
    let product = (amount as u128)
        .checked_mul(price)
        .ok_or(ARITHMETIC_OVERFLOW)?;
    let quotient = product >> 64;
    let remainder = product as u64;
    let result = if round_up && remainder > 0 {
        quotient + 1
    } else {
        quotient
    };
    result.try_into().map_err(|_| AMOUNT_EXCEEDS_MAX_U64)
}

/// Convert amount / Q64.64 price → amount (floor or ceil).
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn div_q64(amount: u64, price: U128, round_up: bool) -> Result<u64, CoreError> {
    let price: u128 = price.into();
    if price == 0 {
        return Err(DIVISION_BY_ZERO);
    }
    let numerator = (amount as u128)
        .checked_shl(64)
        .ok_or(ARITHMETIC_OVERFLOW)?;
    let quotient = numerator.checked_div(price).ok_or(ARITHMETIC_OVERFLOW)?;
    let remainder = numerator.checked_rem(price).ok_or(ARITHMETIC_OVERFLOW)?;
    let result = if round_up && remainder > 0 {
        quotient + 1
    } else {
        quotient
    };
    result.try_into().map_err(|_| AMOUNT_EXCEEDS_MAX_U64)
}

/// Identity price (1.0 in Q64.64).
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn one_q64() -> U128 {
    U128::from(Q64_ONE)
}

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

    #[rstest]
    #[case(100, Q64_ONE, false, 100)]
    #[case(100, Q64_ONE * 2, false, 200)]
    fn test_mul_q64(
        #[case] amount: u64,
        #[case] price: u128,
        #[case] round_up: bool,
        #[case] expected: u64,
    ) {
        assert_eq!(
            mul_q64(amount, U128::from(price), round_up).unwrap(),
            expected
        );
    }
}