use ethnum::U256;
#[cfg(feature = "floats")]
use libm::{floor, pow, sqrt};
#[cfg(feature = "wasm")]
use riptide_amm_macros::wasm_expose;
use super::error::{CoreError, AMOUNT_EXCEEDS_MAX_U128, ARITHMETIC_OVERFLOW};
use super::U128;
#[cfg(feature = "floats")]
const Q64_RESOLUTION: f64 = 18446744073709551616.0;
#[cfg(feature = "floats")]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn to_q64_64(price: f64, decimals_a: u8, decimals_b: u8) -> U128 {
let power = pow(10f64, decimals_a as f64 - decimals_b as f64);
#[allow(clippy::useless_conversion)]
(floor((price / power) * Q64_RESOLUTION) as u128).into()
}
#[cfg(feature = "floats")]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn from_q64_64(price: U128, decimals_a: u8, decimals_b: u8) -> f64 {
let power = pow(10f64, decimals_a as f64 - decimals_b as f64);
#[allow(clippy::useless_conversion)]
let q64_64: u128 = price.into();
(q64_64 as f64 / Q64_RESOLUTION) * power
}
#[cfg(feature = "floats")]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn to_sqrt_price(price: f64, decimals_a: u8, decimals_b: u8) -> U128 {
let power = pow(10f64, decimals_a as f64 - decimals_b as f64);
#[allow(clippy::useless_conversion)] (floor(sqrt(price / power) * Q64_RESOLUTION) as u128).into()
}
#[cfg(feature = "floats")]
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn from_sqrt_price(sqrt_price: U128, decimals_a: u8, decimals_b: u8) -> f64 {
let power = pow(10f64, decimals_a as f64 - decimals_b as f64);
#[allow(clippy::useless_conversion)] let sqrt_price: u128 = sqrt_price.into();
pow(sqrt_price as f64 / Q64_RESOLUTION, 2.0) * power
}
#[cfg_attr(feature = "wasm", wasm_expose)]
pub fn invert_price(price: U128) -> Result<U128, CoreError> {
let result: u128 = U256::from(1u128)
.checked_shl(128)
.ok_or(ARITHMETIC_OVERFLOW)?
.checked_div(price.into())
.ok_or(ARITHMETIC_OVERFLOW)?
.try_into()
.map_err(|_| AMOUNT_EXCEEDS_MAX_U128)?;
Ok(U128::from(result))
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[cfg(feature = "floats")]
#[rstest]
#[case(1.0, 9, 9, 1 << 64)]
#[case(1000.0, 9, 6, 1 << 64)]
#[case(0.001, 6, 9, 1 << 64)]
#[case(1.0, 6, 6, 1 << 64)]
#[case(0.0, 6, 6, 0)]
fn test_to_q64_64(
#[case] price: f64,
#[case] decimals_a: u8,
#[case] decimals_b: u8,
#[case] expected_q64_64: u128,
) {
let q64_64 = to_q64_64(price, decimals_a, decimals_b);
assert_eq!(q64_64, U128::from(expected_q64_64));
}
#[cfg(feature = "floats")]
#[rstest]
#[case(1 << 64, 9, 9, 1.0)]
#[case(1 << 64, 9, 6, 1000.0)]
#[case(1 << 64, 6, 9, 0.001)]
#[case(1 << 64, 6, 6, 1.0)]
#[case(0, 6, 6, 0.0)]
fn test_from_q64_64(
#[case] q64_64: u128,
#[case] decimals_a: u8,
#[case] decimals_b: u8,
#[case] expected_price: f64,
) {
#[allow(clippy::useless_conversion)] let price = from_q64_64(q64_64.into(), decimals_a, decimals_b);
assert_eq!(price, expected_price);
}
#[rstest]
#[case(1.0, 9, 9, 1 << 64)]
#[case(1000.0, 9, 6, 1 << 64)]
#[case(0.001, 6, 9, 1 << 64)]
#[case(1.0, 6, 6, 1 << 64)]
#[case(0.0, 6, 6, 0)]
#[cfg(feature = "floats")]
fn test_to_sqrt_price(
#[case] price: f64,
#[case] decimals_a: u8,
#[case] decimals_b: u8,
#[case] expected_sqrt_price: u128,
) {
let sqrt_price = to_sqrt_price(price, decimals_a, decimals_b);
assert_eq!(sqrt_price, U128::from(expected_sqrt_price));
}
#[rstest]
#[case(1 << 64, 9, 9, 1.0)]
#[case(1 << 64, 9, 6, 1000.0)]
#[case(1 << 64, 6, 9, 0.001)]
#[case(1 << 64, 6, 6, 1.0)]
#[case(0, 6, 6, 0.0)]
#[cfg(feature = "floats")]
fn test_from_sqrt_price(
#[case] sqrt_price: u128,
#[case] decimals_a: u8,
#[case] decimals_b: u8,
#[case] expected_price: f64,
) {
#[allow(clippy::useless_conversion)] let price = from_sqrt_price(sqrt_price.into(), decimals_a, decimals_b);
assert_eq!(price, expected_price);
}
#[rstest]
#[case(1 << 64, Ok(1 << 64))]
#[case(2 << 64, Ok((1 << 64) / 2))]
#[case(4 << 64, Ok((1 << 64) / 4))]
#[case((1 << 64) / 2, Ok(2 << 64))]
#[case((1 << 64) / 4, Ok(4 << 64))]
#[case(0, Err(ARITHMETIC_OVERFLOW))]
fn test_invert_price(#[case] price: u128, #[case] expected: Result<u128, CoreError>) {
let result = invert_price(U128::from(price));
#[allow(clippy::useless_conversion)]
let expected = expected.map(U128::from);
assert_eq!(result, expected);
}
}