From 239b947802126c13229d7752eef1d65619768e99 Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Thu, 14 Nov 2024 21:52:49 -0500 Subject: [PATCH 1/9] add prec dec impl --- packages/neutron-std/src/types/neutron/mod.rs | 1 + .../src/types/neutron/util/forward_ref.rs | 84 + .../neutron-std/src/types/neutron/util/mod.rs | 2 + .../src/types/neutron/util/precdec.rs | 2243 +++++++++++++++++ 4 files changed, 2330 insertions(+) create mode 100644 packages/neutron-std/src/types/neutron/util/forward_ref.rs create mode 100644 packages/neutron-std/src/types/neutron/util/mod.rs create mode 100644 packages/neutron-std/src/types/neutron/util/precdec.rs diff --git a/packages/neutron-std/src/types/neutron/mod.rs b/packages/neutron-std/src/types/neutron/mod.rs index eb206e7..c6486c3 100644 --- a/packages/neutron-std/src/types/neutron/mod.rs +++ b/packages/neutron-std/src/types/neutron/mod.rs @@ -8,3 +8,4 @@ pub mod ibcratelimit; pub mod interchainqueries; pub mod interchaintxs; pub mod transfer; +pub mod util; diff --git a/packages/neutron-std/src/types/neutron/util/forward_ref.rs b/packages/neutron-std/src/types/neutron/util/forward_ref.rs new file mode 100644 index 0000000..d639b65 --- /dev/null +++ b/packages/neutron-std/src/types/neutron/util/forward_ref.rs @@ -0,0 +1,84 @@ +/// # ⚠ THIS IS AN INTERNAL IMPLEMENTATION DETAIL. DO NOT USE. +/// +/// Given an implementation of `T == U`, implements: +/// - `&T == U` +/// - `T == &U` +/// +/// We don't need to add `&T == &U` here because this is implemented automatically. +#[doc(hidden)] +#[macro_export] +macro_rules! forward_ref_partial_eq { + ($t:ty, $u:ty) => { + // `&T == U` + impl<'a> PartialEq<$u> for &'a $t { + #[inline] + fn eq(&self, rhs: &$u) -> bool { + **self == *rhs // Implement via T == U + } + } + + // `T == &U` + impl PartialEq<&$u> for $t { + #[inline] + fn eq(&self, rhs: &&$u) -> bool { + *self == **rhs // Implement via T == U + } + } + }; +} + +/// implements binary operators "&T op U", "T op &U", "&T op &U" +/// based on "T op U" where T and U are expected to be `Copy`able +/// +/// Copied from `libcore` +macro_rules! forward_ref_binop { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + impl<'a> $imp<$u> for &'a $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + #[track_caller] + fn $method(self, other: $u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, other) + } + } + + impl $imp<&$u> for $t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + #[track_caller] + fn $method(self, other: &$u) -> <$t as $imp<$u>>::Output { + $imp::$method(self, *other) + } + } + + impl $imp<&$u> for &$t { + type Output = <$t as $imp<$u>>::Output; + + #[inline] + #[track_caller] + fn $method(self, other: &$u) -> <$t as $imp<$u>>::Output { + $imp::$method(*self, *other) + } + } + }; +} + +/// implements "T op= &U", based on "T op= U" +/// where U is expected to be `Copy`able +/// +/// Copied from `libcore` +macro_rules! forward_ref_op_assign { + (impl $imp:ident, $method:ident for $t:ty, $u:ty) => { + impl $imp<&$u> for $t { + #[inline] + #[track_caller] + fn $method(&mut self, other: &$u) { + $imp::$method(self, *other); + } + } + }; +} + +pub(crate) use {forward_ref_binop, forward_ref_op_assign, forward_ref_partial_eq}; diff --git a/packages/neutron-std/src/types/neutron/util/mod.rs b/packages/neutron-std/src/types/neutron/util/mod.rs new file mode 100644 index 0000000..3d97001 --- /dev/null +++ b/packages/neutron-std/src/types/neutron/util/mod.rs @@ -0,0 +1,2 @@ +pub mod precdec; +mod forward_ref; diff --git a/packages/neutron-std/src/types/neutron/util/precdec.rs b/packages/neutron-std/src/types/neutron/util/precdec.rs new file mode 100644 index 0000000..bdfa62a --- /dev/null +++ b/packages/neutron-std/src/types/neutron/util/precdec.rs @@ -0,0 +1,2243 @@ +use core::cmp::Ordering; +use core::fmt::{self, Write}; +use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign}; +use core::str::FromStr; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; + +use cosmwasm_std::{ + CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, + OverflowOperation, RoundUpOverflowError, StdError, +}; +use crate::types::neutron::util::forward_ref::{forward_ref_binop, forward_ref_op_assign, forward_ref_partial_eq}; +use cosmwasm_std::{Decimal256, SignedDecimal, SignedDecimal256}; + +use cosmwasm_std::{Fraction, Isqrt, Uint256, Uint512}; + + + + +/// A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0 +/// +/// The greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18) +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, schemars::JsonSchema)] +pub struct PrecDec(#[schemars(with = "String")] Uint256); + +forward_ref_partial_eq!(PrecDec, PrecDec); + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[error("Decimal range exceeded")] +pub struct DecimalRangeExceeded; + +impl PrecDec { + const DECIMAL_FRACTIONAL: Uint256 = Uint256::from_uint128(1_000_000_000_000_000_000_000_000_000u128); // 1*10**27 + // const DECIMAL_FRACTIONAL_SQUARED: Uint256 = Uint256::from_uint128(1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000u128); // (1*10**18)**2 = 1*10**54 + + + + /// The number of decimal places. Since decimal types are fixed-point rather than + /// floating-point, this is a constant. + pub const DECIMAL_PLACES: u32 = 27; // This needs to be an even number. // TODO: is it ok ? + /// The largest value that can be represented by this decimal type. + pub const MAX: Self = Self(Uint256::MAX); + /// The smallest value that can be represented by this decimal type. + pub const MIN: Self = Self(Uint256::MIN); + + /// Creates a Decimal(value) + /// This is equivalent to `Decimal::from_atomics(value, 18)` but usable in a const context. + pub const fn new(value: Uint256) -> Self { + Self(value) + } + + /// Creates a Decimal(Uint128(value)) + /// This is equivalent to `Decimal::from_atomics(value, 18)` but usable in a const context. + pub const fn raw(value: u128) -> Self { + Self(Uint256::from_u128(value)) + } + + /// Create a 1.0 Decimal + #[inline] + pub const fn one() -> Self { + Self(Self::DECIMAL_FRACTIONAL) + } + + /// Create a 0.0 Decimal + #[inline] + pub const fn zero() -> Self { + Self(Uint256::zero()) + } + + /// Convert x% into Decimal + /// + /// ## Examples + /// + /// ``` + /// # use std::str::FromStr; + /// # use cosmwasm_std::Decimal; + /// const HALF: Decimal = Decimal::percent(50); + /// + /// assert_eq!(HALF, Decimal::from_str("0.5").unwrap()); + /// ``` + // TODO: fix me + // pub const fn percent(x: u64) -> Self { + // // multiplication does not overflow since `u64::MAX` * 10**16 is well in u128 range + // let atomics = (x as u128) * 10_000_000_000_000_000; + // Self(Uint128::new(atomics)) + // } + + /// Convert permille (x/1000) into Decimal + /// + /// ## Examples + /// + /// ``` + /// # use std::str::FromStr; + /// # use cosmwasm_std::Decimal; + /// const HALF: Decimal = Decimal::permille(500); + /// + /// assert_eq!(HALF, Decimal::from_str("0.5").unwrap()); + /// ``` + + // TODO: fix me + // pub const fn permille(x: u64) -> Self { + // // multiplication does not overflow since `u64::MAX` * 10**15 is well in u128 range + // let atomics = (x as u128) * 1_000_000_000_000_000; + // Self(Uint128::new(atomics)) + // } + + /// Convert basis points (x/10000) into Decimal + /// + /// ## Examples + /// + /// ``` + /// # use std::str::FromStr; + /// # use cosmwasm_std::Decimal; + /// const TWO_BPS: Decimal = Decimal::bps(2); + /// const HALF: Decimal = Decimal::bps(5000); + /// + /// assert_eq!(TWO_BPS, Decimal::from_str("0.0002").unwrap()); + /// assert_eq!(HALF, Decimal::from_str("0.5").unwrap()); + /// ``` + + // TODO: fix me + // pub const fn bps(x: u64) -> Self { + // // multiplication does not overflow since `u64::MAX` * 10**14 is well in u128 range + // let atomics = (x as u128) * 100_000_000_000_000; + // Self(Uint128::new(atomics)) + // } + + /// Creates a decimal from a number of atomic units and the number + /// of decimal places. The inputs will be converted internally to form + /// a decimal with 18 decimal places. So the input 123 and 2 will create + /// the decimal 1.23. + /// + /// Using 18 decimal places is slightly more efficient than other values + /// as no internal conversion is necessary. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{Decimal, Uint128}; + /// let a = Decimal::from_atomics(Uint128::new(1234), 3).unwrap(); + /// assert_eq!(a.to_string(), "1.234"); + /// + /// let a = Decimal::from_atomics(1234u128, 0).unwrap(); + /// assert_eq!(a.to_string(), "1234"); + /// + /// let a = Decimal::from_atomics(1u64, 18).unwrap(); + /// assert_eq!(a.to_string(), "0.000000000000000001"); + /// ``` + pub fn from_atomics( + atomics: impl Into, + decimal_places: u32, + ) -> Result { + let atomics = atomics.into(); + const TEN: Uint256 = Uint256::from_u128(10); + Ok(match decimal_places.cmp(&Self::DECIMAL_PLACES) { + Ordering::Less => { + let digits = (Self::DECIMAL_PLACES) - decimal_places; // No overflow because decimal_places < DECIMAL_PLACES + let factor = TEN.checked_pow(digits).unwrap(); // Safe because digits <= 17 //TODO: Is still ok? + Self( + atomics + .checked_mul(factor) + .map_err(|_| DecimalRangeExceeded)?, + ) + } + Ordering::Equal => Self(atomics), + Ordering::Greater => { + let digits = decimal_places - (Self::DECIMAL_PLACES); // No overflow because decimal_places > DECIMAL_PLACES + if let Ok(factor) = TEN.checked_pow(digits) { + Self(atomics.checked_div(factor).unwrap()) // Safe because factor cannot be zero + } else { + // In this case `factor` exceeds the Uint128 range. + // Any Uint128 `x` divided by `factor` with `factor > Uint128::MAX` is 0. + // Try e.g. Python3: `(2**128-1) // 2**128` + Self(Uint256::zero()) + } + } + }) + } + + /// Returns the ratio (numerator / denominator) as a Decimal + pub fn from_ratio(numerator: impl Into, denominator: impl Into) -> Self { + match PrecDec::checked_from_ratio(numerator, denominator) { + Ok(value) => value, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => panic!("Multiplication overflow"), + } + } + + /// Returns the ratio (numerator / denominator) as a Decimal + pub fn checked_from_ratio( + numerator: impl Into, + denominator: impl Into, + ) -> Result { + let numerator: Uint256 = numerator.into(); + let denominator: Uint256 = denominator.into(); + match numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator) { + Ok(ratio) => { + // numerator * DECIMAL_FRACTIONAL / denominator + Ok(PrecDec(ratio)) + } + Err(CheckedMultiplyRatioError::Overflow) => Err(CheckedFromRatioError::Overflow), + Err(CheckedMultiplyRatioError::DivideByZero) => { + Err(CheckedFromRatioError::DivideByZero) + } + } + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// A decimal is an integer of atomic units plus a number that specifies the + /// position of the decimal dot. So any decimal can be expressed as two numbers. + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{Decimal, Uint128}; + /// # use core::str::FromStr; + /// // Value with whole and fractional part + /// let a = Decimal::from_str("1.234").unwrap(); + /// assert_eq!(a.decimal_places(), 18); + /// assert_eq!(a.atomics(), Uint128::new(1234000000000000000)); + /// + /// // Smallest possible value + /// let b = Decimal::from_str("0.000000000000000001").unwrap(); + /// assert_eq!(b.decimal_places(), 18); + /// assert_eq!(b.atomics(), Uint128::new(1)); + /// ``` + #[must_use] + #[inline] + pub const fn atomics(&self) -> Uint256 { + self.0 + } + + /// The number of decimal places. This is a constant value for now + /// but this could potentially change as the type evolves. + /// + /// See also [`Decimal::atomics()`]. + #[must_use] + #[inline] + pub const fn decimal_places(&self) -> u32 { + Self::DECIMAL_PLACES + } + + /// Rounds value down after decimal places. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn floor(&self) -> Self { + Self((self.0 / Self::DECIMAL_FRACTIONAL) * Self::DECIMAL_FRACTIONAL) + } + + /// Rounds value up after decimal places. Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn ceil(&self) -> Self { + match self.checked_ceil() { + Ok(value) => value, + Err(_) => panic!("attempt to ceil with overflow"), + } + } + + /// Rounds value up after decimal places. Returns OverflowError on overflow. + pub fn checked_ceil(&self) -> Result { + let floor = self.floor(); + if floor == self { + Ok(floor) + } else { + floor + .checked_add(PrecDec::one()) + .map_err(|_| RoundUpOverflowError) + } + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Add)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Sub)) + } + + //TODO: fix me, overflow check doesn't work + /// Multiplies one `Decimal` by another, returning an `OverflowError` if an overflow occurred. + pub fn checked_mul(self, other: Self) -> Result { + let result_as_uint256 = self.numerator().full_mul(other.numerator()) + / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL) ; // from_uint128 is a const method and should be "free" + result_as_uint256 + .try_into() + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Mul)) + } + + /// Raises a value to the power of `exp`, panics if an overflow occurred. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => panic!("Multiplication overflow"), + } + } + + /// Raises a value to the power of `exp`, returning an `OverflowError` if an overflow occurred. + pub fn checked_pow(self, exp: u32) -> Result { + // This uses the exponentiation by squaring algorithm: + // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Basic_method + + fn inner(mut x: PrecDec, mut n: u32) -> Result { + if n == 0 { + return Ok(PrecDec::one()); + } + + let mut y = PrecDec::one(); + + while n > 1 { + if n % 2 == 0 { + x = x.checked_mul(x)?; + n /= 2; + } else { + y = x.checked_mul(y)?; + x = x.checked_mul(x)?; + n = (n - 1) / 2; + } + } + + Ok(x * y) + } + + inner(self, exp).map_err(|_| OverflowError::new(OverflowOperation::Pow)) + } + + pub fn checked_div(self, other: Self) -> Result { + PrecDec::checked_from_ratio(self.numerator(), other.numerator()) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError) + } + + /// Returns the approximate square root as a Decimal. + /// + /// This should not overflow or panic. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn sqrt(&self) -> Self { + // Algorithm described in https://hackmd.io/@webmaster128/SJThlukj_ + // We start with the highest precision possible and lower it until + // there's no overflow. + // + // TODO: This could be made more efficient once log10 is in: + // https://github.com/rust-lang/rust/issues/70887 + // The max precision is something like `9 - log10(self.0) / 2`. + (0..=Self::DECIMAL_PLACES / 2) + .rev() + .find_map(|i| self.sqrt_with_precision(i)) + // The last step (i = 0) is guaranteed to succeed because `isqrt(u128::MAX) * 10^9` does not overflow + .unwrap() + } + + /// Lower precision means more aggressive rounding, but less risk of overflow. + /// Precision *must* be a number between 0 and 9 (inclusive). + /// + /// Returns `None` if the internal multiplication overflows. + #[must_use = "this returns the result of the operation, without modifying the original"] + fn sqrt_with_precision(&self, precision: u32) -> Option { + let inner_mul = 100u128.pow(precision); + self.0.checked_mul(inner_mul.into()).ok().map(|inner| { + let outer_mul = 10u128.pow(Self::DECIMAL_PLACES / 2 - precision); + PrecDec(inner.isqrt().checked_mul(Uint256::from(outer_mul)).unwrap()) + }) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn abs_diff(self, other: Self) -> Self { + Self(self.0.abs_diff(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + match self.checked_add(other) { + Ok(value) => value, + Err(_) => Self::MAX, + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + match self.checked_sub(other) { + Ok(value) => value, + Err(_) => Self::zero(), + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + match self.checked_mul(other) { + Ok(value) => value, + Err(_) => Self::MAX, + } + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => Self::MAX, + } + } + + /// Converts this decimal to an unsigned integer by truncating + /// the fractional part, e.g. 22.5 becomes 22. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{Decimal, Uint128}; + /// + /// let d = Decimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_uint_floor(), Uint128::new(12)); + /// + /// let d = Decimal::from_str("12.999").unwrap(); + /// assert_eq!(d.to_uint_floor(), Uint128::new(12)); + /// + /// let d = Decimal::from_str("75.0").unwrap(); + /// assert_eq!(d.to_uint_floor(), Uint128::new(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_uint_floor(self) -> Uint256 { + self.0 / Self::DECIMAL_FRACTIONAL + } + + /// Converts this decimal to an unsigned integer by rounting up + /// to the next integer, e.g. 22.3 becomes 23. + /// + /// ## Examples + /// + /// ``` + /// use core::str::FromStr; + /// use cosmwasm_std::{Decimal, Uint128}; + /// + /// let d = Decimal::from_str("12.345").unwrap(); + /// assert_eq!(d.to_uint_ceil(), Uint128::new(13)); + /// + /// let d = Decimal::from_str("12.999").unwrap(); + /// assert_eq!(d.to_uint_ceil(), Uint128::new(13)); + /// + /// let d = Decimal::from_str("75.0").unwrap(); + /// assert_eq!(d.to_uint_ceil(), Uint128::new(75)); + /// ``` + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_uint_ceil(self) -> Uint256 { + // Using `q = 1 + ((x - 1) / y); // if x != 0` with unsigned integers x, y, q + // from https://stackoverflow.com/a/2745086/2013738. We know `x + y` CAN overflow. + let x = self.0; + let y = Self::DECIMAL_FRACTIONAL; + if x.is_zero() { + Uint256::zero() + } else { + Uint256::one() + ((x - Uint256::one()) / y) + } + } +} + +impl Fraction for PrecDec { + #[inline] + fn numerator(&self) -> Uint256 { + self.0 + } + + #[inline] + fn denominator(&self) -> Uint256 { + Self::DECIMAL_FRACTIONAL + } + + /// Returns the multiplicative inverse `1/d` for decimal `d`. + /// + /// If `d` is zero, none is returned. + fn inv(&self) -> Option { + if self.is_zero() { + None + } else { + let decimal_fractional_squared = Self::DECIMAL_FRACTIONAL.Mul(Self::DECIMAL_FRACTIONAL); + // Let self be p/q with p = self.0 and q = DECIMAL_FRACTIONAL. + // Now we calculate the inverse a/b = q/p such that b = DECIMAL_FRACTIONAL. Then + // `a = DECIMAL_FRACTIONAL*DECIMAL_FRACTIONAL / self.0`. + Some(PrecDec(decimal_fractional_squared / self.0)) + } + } +} + +impl TryFrom for PrecDec { + type Error = DecimalRangeExceeded; + + fn try_from(value: Decimal256) -> Result { + value + .atomics() + .try_into() + .map(PrecDec) + .map_err(|_| DecimalRangeExceeded) + } +} + +impl TryFrom for PrecDec { + type Error = DecimalRangeExceeded; + + fn try_from(value: SignedDecimal) -> Result { + value + .atomics() + .try_into() + .map(PrecDec) + .map_err(|_| DecimalRangeExceeded) + } +} + +impl TryFrom for PrecDec { + type Error = DecimalRangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + value + .atomics() + .try_into() + .map(PrecDec) + .map_err(|_| DecimalRangeExceeded) + } +} + +impl TryFrom for PrecDec { + type Error = DecimalRangeExceeded; + + #[inline] + fn try_from(value: Uint256) -> Result { + Self::from_atomics(value, 0) + } +} + +impl FromStr for PrecDec { + type Err = StdError; + + /// Converts the decimal string to a Decimal + /// Possible inputs: "1.23", "1", "000012", "1.123000000" + /// Disallowed: "", ".23" + /// + /// This never performs any kind of rounding. + /// More than DECIMAL_PLACES fractional digits, even zeros, result in an error. + fn from_str(input: &str) -> Result { + let mut parts_iter = input.split('.'); + + let whole_part = parts_iter.next().unwrap(); // split always returns at least one element + let whole = whole_part + .parse::() + .map_err(|_| StdError::generic_err("Error parsing whole"))?; + let mut atomics = whole + .checked_mul(Self::DECIMAL_FRACTIONAL) + .map_err(|_| StdError::generic_err("Value too big"))?; + + if let Some(fractional_part) = parts_iter.next() { + let fractional = fractional_part + .parse::() + .map_err(|_| StdError::generic_err("Error parsing fractional"))?; + let exp = (Self::DECIMAL_PLACES.checked_sub(fractional_part.len() as u32)).ok_or_else( + || { + StdError::generic_err(format!( + "Cannot parse more than {} fractional digits", + Self::DECIMAL_PLACES + )) + }, + )?; + debug_assert!(exp <= Self::DECIMAL_PLACES); + let fractional_factor = Uint256::from(10u128.pow(exp)); + atomics = atomics + .checked_add( + // The inner multiplication can't overflow because + // fractional < 10^DECIMAL_PLACES && fractional_factor <= 10^DECIMAL_PLACES + fractional.checked_mul(fractional_factor).unwrap(), + ) + .map_err(|_| StdError::generic_err("Value too big"))?; + } + + if parts_iter.next().is_some() { + return Err(StdError::generic_err("Unexpected number of dots")); + } + + Ok(PrecDec(atomics)) + } +} + +impl fmt::Display for PrecDec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let whole = (self.0) / Self::DECIMAL_FRACTIONAL; + let fractional = (self.0).checked_rem(Self::DECIMAL_FRACTIONAL).unwrap(); + + if fractional.is_zero() { + write!(f, "{whole}") + } else { + let fractional_string = format!( + "{:0>padding$}", + fractional, + padding = Self::DECIMAL_PLACES as usize + ); + f.write_str(&whole.to_string())?; + f.write_char('.')?; + f.write_str(fractional_string.trim_end_matches('0'))?; + Ok(()) + } + } +} + +impl fmt::Debug for PrecDec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Decimal({self})") + } +} + +impl Add for PrecDec { + type Output = Self; + + fn add(self, other: Self) -> Self { + PrecDec(self.0 + other.0) + } +} +forward_ref_binop!(impl Add, add for PrecDec, PrecDec); + +impl AddAssign for PrecDec { + fn add_assign(&mut self, rhs: PrecDec) { + *self = *self + rhs; + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for PrecDec, PrecDec); + +impl Sub for PrecDec { + type Output = Self; + + fn sub(self, other: Self) -> Self { + PrecDec(self.0 - other.0) + } +} +forward_ref_binop!(impl Sub, sub for PrecDec, PrecDec); + +impl SubAssign for PrecDec { + fn sub_assign(&mut self, rhs: PrecDec) { + *self = *self - rhs; + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for PrecDec, PrecDec); + +impl Mul for PrecDec { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn mul(self, other: Self) -> Self { + // Decimals are fractions. We can multiply two decimals a and b + // via + // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) + // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() + + let result_as_uint256 = self.numerator().full_mul(other.numerator()) + / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL); // from_uint128 is a const method and should be "free" // TODO: fix me, this can still overflow + match result_as_uint256.try_into() { + Ok(result) => Self(result), + Err(_) => panic!("attempt to multiply with overflow"), + } + } +} +forward_ref_binop!(impl Mul, mul for PrecDec, PrecDec); + +impl MulAssign for PrecDec { + fn mul_assign(&mut self, rhs: PrecDec) { + *self = *self * rhs; + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for PrecDec, PrecDec); + +impl Div for PrecDec { + type Output = Self; + + fn div(self, other: Self) -> Self { + match PrecDec::checked_from_ratio(self.numerator(), other.numerator()) { + Ok(ratio) => ratio, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Division failed - denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => { + panic!("Division failed - multiplication overflow") + } + } + } +} +forward_ref_binop!(impl Div, div for PrecDec, PrecDec); + +impl DivAssign for PrecDec { + fn div_assign(&mut self, rhs: PrecDec) { + *self = *self / rhs; + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for PrecDec, PrecDec); + +impl Div for PrecDec { + type Output = Self; + + fn div(self, rhs: Uint256) -> Self::Output { + PrecDec(self.0 / rhs) + } +} + +impl DivAssign for PrecDec { + fn div_assign(&mut self, rhs: Uint256) { + self.0 /= rhs; + } +} + +impl Rem for PrecDec { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for PrecDec, PrecDec); + +impl RemAssign for PrecDec { + fn rem_assign(&mut self, rhs: PrecDec) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for PrecDec, PrecDec); + +impl core::iter::Sum for PrecDec +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +/// Serializes as a decimal string +impl Serialize for PrecDec { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +/// Deserializes as a base64 string +impl<'de> Deserialize<'de> for PrecDec { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(DecimalVisitor) + } +} + +struct DecimalVisitor; + +impl<'de> de::Visitor<'de> for DecimalVisitor { + type Value = PrecDec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded decimal") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + match PrecDec::from_str(v) { + Ok(d) => Ok(d), + Err(e) => Err(E::custom(format_args!("Error parsing decimal '{v}': {e}"))), + } + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; + +// fn dec(input: &str) -> PrecDec { +// PrecDec::from_str(input).unwrap() +// } + +// #[test] +// fn decimal_new() { +// let expected = Uint256::from(300u128); +// assert_eq!(PrecDec::new(expected).0, expected); +// } + +// #[test] +// fn decimal_raw() { +// let value = 300u128; +// assert_eq!(PrecDec::raw(value).0.u128(), value); +// } + +// #[test] +// fn decimal_one() { +// let value = PrecDec::one(); +// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL); +// } + +// #[test] +// fn decimal_zero() { +// let value = PrecDec::zero(); +// assert!(value.0.is_zero()); +// } + +// #[test] +// fn decimal_percent() { +// let value = PrecDec::percent(50); +// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(2u8)); +// } + +// #[test] +// fn decimal_permille() { +// let value = PrecDec::permille(125); +// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(8u8)); +// } + +// #[test] +// fn decimal_bps() { +// let value = PrecDec::bps(125); +// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(80u8)); +// } + +// #[test] +// fn decimal_from_decimal256_works() { +// let too_big = Decimal256::new(Uint256::from(Uint256::MAX) + Uint256::one()); +// assert_eq!(PrecDec::try_from(too_big), Err(DecimalRangeExceeded)); + +// let just_right = Decimal256::new(Uint256::from(Uint256::MAX)); +// assert_eq!(PrecDec::try_from(just_right), Ok(PrecDec::MAX)); + +// assert_eq!(PrecDec::try_from(Decimal256::zero()), Ok(PrecDec::zero())); +// assert_eq!(PrecDec::try_from(Decimal256::one()), Ok(PrecDec::one())); +// assert_eq!( +// PrecDec::try_from(Decimal256::percent(50)), +// Ok(PrecDec::percent(50)) +// ); +// } + +// #[test] +// fn decimal_try_from_integer() { +// let int = Uint256::new(0xDEADBEEF); +// let decimal = PrecDec::try_from(int).unwrap(); +// assert_eq!(int.to_string(), decimal.to_string()); +// } + +// #[test] +// fn decimal_try_from_signed_works() { +// assert_eq!( +// PrecDec::try_from(SignedDecimal::MAX).unwrap(), +// PrecDec::raw(SignedDecimal::MAX.atomics().i128() as u128) +// ); +// assert_eq!( +// PrecDec::try_from(SignedDecimal::zero()).unwrap(), +// PrecDec::zero() +// ); +// assert_eq!( +// PrecDec::try_from(SignedDecimal::one()).unwrap(), +// PrecDec::one() +// ); +// assert_eq!( +// PrecDec::try_from(SignedDecimal::percent(50)).unwrap(), +// PrecDec::percent(50) +// ); +// assert_eq!( +// PrecDec::try_from(SignedDecimal::negative_one()), +// Err(DecimalRangeExceeded) +// ); +// assert_eq!( +// PrecDec::try_from(SignedDecimal::MIN), +// Err(DecimalRangeExceeded) +// ); +// } + +// #[test] +// fn decimal_from_atomics_works() { +// let one = PrecDec::one(); +// let two = one + one; + +// assert_eq!(PrecDec::from_atomics(1u128, 0).unwrap(), one); +// assert_eq!(PrecDec::from_atomics(10u128, 1).unwrap(), one); +// assert_eq!(PrecDec::from_atomics(100u128, 2).unwrap(), one); +// assert_eq!(PrecDec::from_atomics(1000u128, 3).unwrap(), one); +// assert_eq!( +// PrecDec::from_atomics(1000000000000000000u128, 18).unwrap(), +// one +// ); +// assert_eq!( +// PrecDec::from_atomics(10000000000000000000u128, 19).unwrap(), +// one +// ); +// assert_eq!( +// PrecDec::from_atomics(100000000000000000000u128, 20).unwrap(), +// one +// ); + +// assert_eq!(PrecDec::from_atomics(2u128, 0).unwrap(), two); +// assert_eq!(PrecDec::from_atomics(20u128, 1).unwrap(), two); +// assert_eq!(PrecDec::from_atomics(200u128, 2).unwrap(), two); +// assert_eq!(PrecDec::from_atomics(2000u128, 3).unwrap(), two); +// assert_eq!( +// PrecDec::from_atomics(2000000000000000000u128, 18).unwrap(), +// two +// ); +// assert_eq!( +// PrecDec::from_atomics(20000000000000000000u128, 19).unwrap(), +// two +// ); +// assert_eq!( +// PrecDec::from_atomics(200000000000000000000u128, 20).unwrap(), +// two +// ); + +// // Cuts decimal digits (20 provided but only 18 can be stored) +// assert_eq!( +// PrecDec::from_atomics(4321u128, 20).unwrap(), +// PrecDec::from_str("0.000000000000000043").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(6789u128, 20).unwrap(), +// PrecDec::from_str("0.000000000000000067").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(u128::MAX, 38).unwrap(), +// PrecDec::from_str("3.402823669209384634").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(u128::MAX, 39).unwrap(), +// PrecDec::from_str("0.340282366920938463").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(u128::MAX, 45).unwrap(), +// PrecDec::from_str("0.000000340282366920").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(u128::MAX, 51).unwrap(), +// PrecDec::from_str("0.000000000000340282").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(u128::MAX, 56).unwrap(), +// PrecDec::from_str("0.000000000000000003").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(u128::MAX, 57).unwrap(), +// PrecDec::from_str("0.000000000000000000").unwrap() +// ); +// assert_eq!( +// PrecDec::from_atomics(u128::MAX, u32::MAX).unwrap(), +// PrecDec::from_str("0.000000000000000000").unwrap() +// ); + +// // Can be used with max value +// let max = PrecDec::MAX; +// assert_eq!( +// PrecDec::from_atomics(max.atomics(), max.decimal_places()).unwrap(), +// max +// ); + +// // Overflow is only possible with digits < 18 +// let result = PrecDec::from_atomics(u128::MAX, 17); +// assert_eq!(result.unwrap_err(), DecimalRangeExceeded); +// } + +// #[test] +// fn decimal_from_ratio_works() { +// // 1.0 +// assert_eq!(PrecDec::from_ratio(1u128, 1u128), PrecDec::one()); +// assert_eq!(PrecDec::from_ratio(53u128, 53u128), PrecDec::one()); +// assert_eq!(PrecDec::from_ratio(125u128, 125u128), PrecDec::one()); + +// // 1.5 +// assert_eq!(PrecDec::from_ratio(3u128, 2u128), PrecDec::percent(150)); +// assert_eq!(PrecDec::from_ratio(150u128, 100u128), PrecDec::percent(150)); +// assert_eq!(PrecDec::from_ratio(333u128, 222u128), PrecDec::percent(150)); + +// // 0.125 +// assert_eq!(PrecDec::from_ratio(1u64, 8u64), PrecDec::permille(125)); +// assert_eq!(PrecDec::from_ratio(125u64, 1000u64), PrecDec::permille(125)); + +// // 1/3 (result floored) +// assert_eq!( +// PrecDec::from_ratio(1u64, 3u64), +// PrecDec(Uint256::from(333_333_333_333_333_333u128)) +// ); + +// // 2/3 (result floored) +// assert_eq!( +// PrecDec::from_ratio(2u64, 3u64), +// PrecDec(Uint256::from(666_666_666_666_666_666u128)) +// ); + +// // large inputs +// assert_eq!(PrecDec::from_ratio(0u128, u128::MAX), PrecDec::zero()); +// assert_eq!(PrecDec::from_ratio(u128::MAX, u128::MAX), PrecDec::one()); +// // 340282366920938463463 is the largest integer <= Decimal::MAX +// assert_eq!( +// PrecDec::from_ratio(340282366920938463463u128, 1u128), +// PrecDec::from_str("340282366920938463463").unwrap() +// ); +// } + +// #[test] +// #[should_panic(expected = "Denominator must not be zero")] +// fn decimal_from_ratio_panics_for_zero_denominator() { +// PrecDec::from_ratio(1u128, 0u128); +// } + +// #[test] +// #[should_panic(expected = "Multiplication overflow")] +// fn decimal_from_ratio_panics_for_mul_overflow() { +// PrecDec::from_ratio(u128::MAX, 1u128); +// } + +// #[test] +// fn decimal_checked_from_ratio_does_not_panic() { +// assert_eq!( +// PrecDec::checked_from_ratio(1u128, 0u128), +// Err(CheckedFromRatioError::DivideByZero) +// ); + +// assert_eq!( +// PrecDec::checked_from_ratio(u128::MAX, 1u128), +// Err(CheckedFromRatioError::Overflow) +// ); +// } + +// #[test] +// fn decimal_implements_fraction() { +// let fraction = PrecDec::from_str("1234.567").unwrap(); +// assert_eq!( +// fraction.numerator(), +// Uint256::from(1_234_567_000_000_000_000_000u128) +// ); +// assert_eq!( +// fraction.denominator(), +// Uint256::from(1_000_000_000_000_000_000u128) +// ); +// } + +// #[test] +// fn decimal_from_str_works() { +// // Integers +// assert_eq!(PrecDec::from_str("0").unwrap(), PrecDec::percent(0)); +// assert_eq!(PrecDec::from_str("1").unwrap(), PrecDec::percent(100)); +// assert_eq!(PrecDec::from_str("5").unwrap(), PrecDec::percent(500)); +// assert_eq!(PrecDec::from_str("42").unwrap(), PrecDec::percent(4200)); +// assert_eq!(PrecDec::from_str("000").unwrap(), PrecDec::percent(0)); +// assert_eq!(PrecDec::from_str("001").unwrap(), PrecDec::percent(100)); +// assert_eq!(PrecDec::from_str("005").unwrap(), PrecDec::percent(500)); +// assert_eq!(PrecDec::from_str("0042").unwrap(), PrecDec::percent(4200)); + +// // Decimals +// assert_eq!(PrecDec::from_str("1.0").unwrap(), PrecDec::percent(100)); +// assert_eq!(PrecDec::from_str("1.5").unwrap(), PrecDec::percent(150)); +// assert_eq!(PrecDec::from_str("0.5").unwrap(), PrecDec::percent(50)); +// assert_eq!(PrecDec::from_str("0.123").unwrap(), PrecDec::permille(123)); + +// assert_eq!(PrecDec::from_str("40.00").unwrap(), PrecDec::percent(4000)); +// assert_eq!(PrecDec::from_str("04.00").unwrap(), PrecDec::percent(400)); +// assert_eq!(PrecDec::from_str("00.40").unwrap(), PrecDec::percent(40)); +// assert_eq!(PrecDec::from_str("00.04").unwrap(), PrecDec::percent(4)); + +// // Can handle DECIMAL_PLACES fractional digits +// assert_eq!( +// PrecDec::from_str("7.123456789012345678").unwrap(), +// PrecDec(Uint256::from(7123456789012345678u128)) +// ); +// assert_eq!( +// PrecDec::from_str("7.999999999999999999").unwrap(), +// PrecDec(Uint256::from(7999999999999999999u128)) +// ); + +// // Works for documented max value +// assert_eq!( +// PrecDec::from_str("340282366920938463463.374607431768211455").unwrap(), +// PrecDec::MAX +// ); +// } + +// #[test] +// fn decimal_from_str_errors_for_broken_whole_part() { +// match PrecDec::from_str("").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), +// e => panic!("Unexpected error: {e:?}"), +// } + +// match PrecDec::from_str(" ").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), +// e => panic!("Unexpected error: {e:?}"), +// } + +// match PrecDec::from_str("-1").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), +// e => panic!("Unexpected error: {e:?}"), +// } +// } + +// #[test] +// fn decimal_from_str_errors_for_broken_fractional_part() { +// match PrecDec::from_str("1.").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), +// e => panic!("Unexpected error: {e:?}"), +// } + +// match PrecDec::from_str("1. ").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), +// e => panic!("Unexpected error: {e:?}"), +// } + +// match PrecDec::from_str("1.e").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), +// e => panic!("Unexpected error: {e:?}"), +// } + +// match PrecDec::from_str("1.2e3").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), +// e => panic!("Unexpected error: {e:?}"), +// } +// } + +// #[test] +// fn decimal_from_str_errors_for_more_than_18_fractional_digits() { +// match PrecDec::from_str("7.1234567890123456789").unwrap_err() { +// StdError::GenericErr { msg, .. } => { +// assert_eq!(msg, "Cannot parse more than 18 fractional digits",) +// } +// e => panic!("Unexpected error: {e:?}"), +// } + +// // No special rules for trailing zeros. This could be changed but adds gas cost for the happy path. +// match PrecDec::from_str("7.1230000000000000000").unwrap_err() { +// StdError::GenericErr { msg, .. } => { +// assert_eq!(msg, "Cannot parse more than 18 fractional digits") +// } +// e => panic!("Unexpected error: {e:?}"), +// } +// } + +// #[test] +// fn decimal_from_str_errors_for_invalid_number_of_dots() { +// match PrecDec::from_str("1.2.3").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Unexpected number of dots"), +// e => panic!("Unexpected error: {e:?}"), +// } + +// match PrecDec::from_str("1.2.3.4").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Unexpected number of dots"), +// e => panic!("Unexpected error: {e:?}"), +// } +// } + +// #[test] +// fn decimal_from_str_errors_for_more_than_max_value() { +// // Integer +// match PrecDec::from_str("340282366920938463464").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), +// e => panic!("Unexpected error: {e:?}"), +// } + +// // Decimal +// match PrecDec::from_str("340282366920938463464.0").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), +// e => panic!("Unexpected error: {e:?}"), +// } +// match PrecDec::from_str("340282366920938463463.374607431768211456").unwrap_err() { +// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), +// e => panic!("Unexpected error: {e:?}"), +// } +// } + +// #[test] +// fn decimal_atomics_works() { +// let zero = PrecDec::zero(); +// let one = PrecDec::one(); +// let half = PrecDec::percent(50); +// let two = PrecDec::percent(200); +// let max = PrecDec::MAX; + +// assert_eq!(zero.atomics(), Uint256::new(0)); +// assert_eq!(one.atomics(), Uint256::new(1000000000000000000)); +// assert_eq!(half.atomics(), Uint256::new(500000000000000000)); +// assert_eq!(two.atomics(), Uint256::new(2000000000000000000)); +// assert_eq!(max.atomics(), Uint256::MAX); +// } + +// #[test] +// fn decimal_decimal_places_works() { +// let zero = PrecDec::zero(); +// let one = PrecDec::one(); +// let half = PrecDec::percent(50); +// let two = PrecDec::percent(200); +// let max = PrecDec::MAX; + +// assert_eq!(zero.decimal_places(), 18); +// assert_eq!(one.decimal_places(), 18); +// assert_eq!(half.decimal_places(), 18); +// assert_eq!(two.decimal_places(), 18); +// assert_eq!(max.decimal_places(), 18); +// } + +// #[test] +// fn decimal_is_zero_works() { +// assert!(PrecDec::zero().is_zero()); +// assert!(PrecDec::percent(0).is_zero()); +// assert!(PrecDec::permille(0).is_zero()); + +// assert!(!PrecDec::one().is_zero()); +// assert!(!PrecDec::percent(123).is_zero()); +// assert!(!PrecDec::permille(1234).is_zero()); +// } + +// #[test] +// fn decimal_inv_works() { +// // d = 0 +// assert_eq!(PrecDec::zero().inv(), None); + +// // d == 1 +// assert_eq!(PrecDec::one().inv(), Some(PrecDec::one())); + +// // d > 1 exact +// assert_eq!( +// PrecDec::from_str("2").unwrap().inv(), +// Some(PrecDec::from_str("0.5").unwrap()) +// ); +// assert_eq!( +// PrecDec::from_str("20").unwrap().inv(), +// Some(PrecDec::from_str("0.05").unwrap()) +// ); +// assert_eq!( +// PrecDec::from_str("200").unwrap().inv(), +// Some(PrecDec::from_str("0.005").unwrap()) +// ); +// assert_eq!( +// PrecDec::from_str("2000").unwrap().inv(), +// Some(PrecDec::from_str("0.0005").unwrap()) +// ); + +// // d > 1 rounded +// assert_eq!( +// PrecDec::from_str("3").unwrap().inv(), +// Some(PrecDec::from_str("0.333333333333333333").unwrap()) +// ); +// assert_eq!( +// PrecDec::from_str("6").unwrap().inv(), +// Some(PrecDec::from_str("0.166666666666666666").unwrap()) +// ); + +// // d < 1 exact +// assert_eq!( +// PrecDec::from_str("0.5").unwrap().inv(), +// Some(PrecDec::from_str("2").unwrap()) +// ); +// assert_eq!( +// PrecDec::from_str("0.05").unwrap().inv(), +// Some(PrecDec::from_str("20").unwrap()) +// ); +// assert_eq!( +// PrecDec::from_str("0.005").unwrap().inv(), +// Some(PrecDec::from_str("200").unwrap()) +// ); +// assert_eq!( +// PrecDec::from_str("0.0005").unwrap().inv(), +// Some(PrecDec::from_str("2000").unwrap()) +// ); +// } + +// #[test] +// #[allow(clippy::op_ref)] +// fn decimal_add_works() { +// let value = PrecDec::one() + PrecDec::percent(50); // 1.5 +// assert_eq!( +// value.0, +// PrecDec::DECIMAL_FRACTIONAL * Uint256::from(3u8) / Uint256::from(2u8) +// ); + +// assert_eq!( +// PrecDec::percent(5) + PrecDec::percent(4), +// PrecDec::percent(9) +// ); +// assert_eq!(PrecDec::percent(5) + PrecDec::zero(), PrecDec::percent(5)); +// assert_eq!(PrecDec::zero() + PrecDec::zero(), PrecDec::zero()); + +// // works for refs +// let a = PrecDec::percent(15); +// let b = PrecDec::percent(25); +// let expected = PrecDec::percent(40); +// assert_eq!(a + b, expected); +// assert_eq!(&a + b, expected); +// assert_eq!(a + &b, expected); +// assert_eq!(&a + &b, expected); +// } + +// #[test] +// #[should_panic(expected = "attempt to add with overflow")] +// fn decimal_add_overflow_panics() { +// let _value = PrecDec::MAX + PrecDec::percent(50); +// } + +// #[test] +// fn decimal_add_assign_works() { +// let mut a = PrecDec::percent(30); +// a += PrecDec::percent(20); +// assert_eq!(a, PrecDec::percent(50)); + +// // works for refs +// let mut a = PrecDec::percent(15); +// let b = PrecDec::percent(3); +// let expected = PrecDec::percent(18); +// a += &b; +// assert_eq!(a, expected); +// } + +// #[test] +// #[allow(clippy::op_ref)] +// fn decimal_sub_works() { +// let value = PrecDec::one() - PrecDec::percent(50); // 0.5 +// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(2u8)); + +// assert_eq!( +// PrecDec::percent(9) - PrecDec::percent(4), +// PrecDec::percent(5) +// ); +// assert_eq!(PrecDec::percent(16) - PrecDec::zero(), PrecDec::percent(16)); +// assert_eq!(PrecDec::percent(16) - PrecDec::percent(16), PrecDec::zero()); +// assert_eq!(PrecDec::zero() - PrecDec::zero(), PrecDec::zero()); + +// // works for refs +// let a = PrecDec::percent(13); +// let b = PrecDec::percent(6); +// let expected = PrecDec::percent(7); +// assert_eq!(a - b, expected); +// assert_eq!(&a - b, expected); +// assert_eq!(a - &b, expected); +// assert_eq!(&a - &b, expected); +// } + +// #[test] +// #[should_panic(expected = "attempt to subtract with overflow")] +// fn decimal_sub_overflow_panics() { +// let _value = PrecDec::zero() - PrecDec::percent(50); +// } + +// #[test] +// fn decimal_sub_assign_works() { +// let mut a = PrecDec::percent(20); +// a -= PrecDec::percent(2); +// assert_eq!(a, PrecDec::percent(18)); + +// // works for refs +// let mut a = PrecDec::percent(33); +// let b = PrecDec::percent(13); +// let expected = PrecDec::percent(20); +// a -= &b; +// assert_eq!(a, expected); +// } + +// #[test] +// #[allow(clippy::op_ref)] +// fn decimal_implements_mul() { +// let one = PrecDec::one(); +// let two = one + one; +// let half = PrecDec::percent(50); + +// // 1*x and x*1 +// assert_eq!(one * PrecDec::percent(0), PrecDec::percent(0)); +// assert_eq!(one * PrecDec::percent(1), PrecDec::percent(1)); +// assert_eq!(one * PrecDec::percent(10), PrecDec::percent(10)); +// assert_eq!(one * PrecDec::percent(100), PrecDec::percent(100)); +// assert_eq!(one * PrecDec::percent(1000), PrecDec::percent(1000)); +// assert_eq!(one * PrecDec::MAX, PrecDec::MAX); +// assert_eq!(PrecDec::percent(0) * one, PrecDec::percent(0)); +// assert_eq!(PrecDec::percent(1) * one, PrecDec::percent(1)); +// assert_eq!(PrecDec::percent(10) * one, PrecDec::percent(10)); +// assert_eq!(PrecDec::percent(100) * one, PrecDec::percent(100)); +// assert_eq!(PrecDec::percent(1000) * one, PrecDec::percent(1000)); +// assert_eq!(PrecDec::MAX * one, PrecDec::MAX); + +// // double +// assert_eq!(two * PrecDec::percent(0), PrecDec::percent(0)); +// assert_eq!(two * PrecDec::percent(1), PrecDec::percent(2)); +// assert_eq!(two * PrecDec::percent(10), PrecDec::percent(20)); +// assert_eq!(two * PrecDec::percent(100), PrecDec::percent(200)); +// assert_eq!(two * PrecDec::percent(1000), PrecDec::percent(2000)); +// assert_eq!(PrecDec::percent(0) * two, PrecDec::percent(0)); +// assert_eq!(PrecDec::percent(1) * two, PrecDec::percent(2)); +// assert_eq!(PrecDec::percent(10) * two, PrecDec::percent(20)); +// assert_eq!(PrecDec::percent(100) * two, PrecDec::percent(200)); +// assert_eq!(PrecDec::percent(1000) * two, PrecDec::percent(2000)); + +// // half +// assert_eq!(half * PrecDec::percent(0), PrecDec::percent(0)); +// assert_eq!(half * PrecDec::percent(1), PrecDec::permille(5)); +// assert_eq!(half * PrecDec::percent(10), PrecDec::percent(5)); +// assert_eq!(half * PrecDec::percent(100), PrecDec::percent(50)); +// assert_eq!(half * PrecDec::percent(1000), PrecDec::percent(500)); +// assert_eq!(PrecDec::percent(0) * half, PrecDec::percent(0)); +// assert_eq!(PrecDec::percent(1) * half, PrecDec::permille(5)); +// assert_eq!(PrecDec::percent(10) * half, PrecDec::percent(5)); +// assert_eq!(PrecDec::percent(100) * half, PrecDec::percent(50)); +// assert_eq!(PrecDec::percent(1000) * half, PrecDec::percent(500)); + +// // Move left +// let a = dec("123.127726548762582"); +// assert_eq!(a * dec("1"), dec("123.127726548762582")); +// assert_eq!(a * dec("10"), dec("1231.27726548762582")); +// assert_eq!(a * dec("100"), dec("12312.7726548762582")); +// assert_eq!(a * dec("1000"), dec("123127.726548762582")); +// assert_eq!(a * dec("1000000"), dec("123127726.548762582")); +// assert_eq!(a * dec("1000000000"), dec("123127726548.762582")); +// assert_eq!(a * dec("1000000000000"), dec("123127726548762.582")); +// assert_eq!(a * dec("1000000000000000"), dec("123127726548762582")); +// assert_eq!(a * dec("1000000000000000000"), dec("123127726548762582000")); +// assert_eq!(dec("1") * a, dec("123.127726548762582")); +// assert_eq!(dec("10") * a, dec("1231.27726548762582")); +// assert_eq!(dec("100") * a, dec("12312.7726548762582")); +// assert_eq!(dec("1000") * a, dec("123127.726548762582")); +// assert_eq!(dec("1000000") * a, dec("123127726.548762582")); +// assert_eq!(dec("1000000000") * a, dec("123127726548.762582")); +// assert_eq!(dec("1000000000000") * a, dec("123127726548762.582")); +// assert_eq!(dec("1000000000000000") * a, dec("123127726548762582")); +// assert_eq!(dec("1000000000000000000") * a, dec("123127726548762582000")); + +// // Move right +// let max = PrecDec::MAX; +// assert_eq!( +// max * dec("1.0"), +// dec("340282366920938463463.374607431768211455") +// ); +// assert_eq!( +// max * dec("0.1"), +// dec("34028236692093846346.337460743176821145") +// ); +// assert_eq!( +// max * dec("0.01"), +// dec("3402823669209384634.633746074317682114") +// ); +// assert_eq!( +// max * dec("0.001"), +// dec("340282366920938463.463374607431768211") +// ); +// assert_eq!( +// max * dec("0.000001"), +// dec("340282366920938.463463374607431768") +// ); +// assert_eq!( +// max * dec("0.000000001"), +// dec("340282366920.938463463374607431") +// ); +// assert_eq!( +// max * dec("0.000000000001"), +// dec("340282366.920938463463374607") +// ); +// assert_eq!( +// max * dec("0.000000000000001"), +// dec("340282.366920938463463374") +// ); +// assert_eq!( +// max * dec("0.000000000000000001"), +// dec("340.282366920938463463") +// ); + +// // works for refs +// let a = PrecDec::percent(20); +// let b = PrecDec::percent(30); +// let expected = PrecDec::percent(6); +// assert_eq!(a * b, expected); +// assert_eq!(&a * b, expected); +// assert_eq!(a * &b, expected); +// assert_eq!(&a * &b, expected); +// } + +// #[test] +// fn decimal_mul_assign_works() { +// let mut a = PrecDec::percent(15); +// a *= PrecDec::percent(60); +// assert_eq!(a, PrecDec::percent(9)); + +// // works for refs +// let mut a = PrecDec::percent(50); +// let b = PrecDec::percent(20); +// a *= &b; +// assert_eq!(a, PrecDec::percent(10)); +// } + +// #[test] +// #[should_panic(expected = "attempt to multiply with overflow")] +// fn decimal_mul_overflow_panics() { +// let _value = PrecDec::MAX * PrecDec::percent(101); +// } + +// #[test] +// fn decimal_checked_mul() { +// let test_data = [ +// (PrecDec::zero(), PrecDec::zero()), +// (PrecDec::zero(), PrecDec::one()), +// (PrecDec::one(), PrecDec::zero()), +// (PrecDec::percent(10), PrecDec::zero()), +// (PrecDec::percent(10), PrecDec::percent(5)), +// (PrecDec::MAX, PrecDec::one()), +// (PrecDec::MAX / Uint256::new(2), PrecDec::percent(200)), +// (PrecDec::permille(6), PrecDec::permille(13)), +// ]; + +// // The regular core::ops::Mul is our source of truth for these tests. +// for (x, y) in test_data.into_iter() { +// assert_eq!(x * y, x.checked_mul(y).unwrap()); +// } +// } + +// #[test] +// fn decimal_checked_mul_overflow() { +// assert_eq!( +// PrecDec::MAX.checked_mul(PrecDec::percent(200)), +// Err(OverflowError::new(OverflowOperation::Mul)) +// ); +// } + +// #[test] +// #[allow(clippy::op_ref)] +// fn decimal_implements_div() { +// let one = PrecDec::one(); +// let two = one + one; +// let half = PrecDec::percent(50); + +// // 1/x and x/1 +// assert_eq!(one / PrecDec::percent(1), PrecDec::percent(10_000)); +// assert_eq!(one / PrecDec::percent(10), PrecDec::percent(1_000)); +// assert_eq!(one / PrecDec::percent(100), PrecDec::percent(100)); +// assert_eq!(one / PrecDec::percent(1000), PrecDec::percent(10)); +// assert_eq!(PrecDec::percent(0) / one, PrecDec::percent(0)); +// assert_eq!(PrecDec::percent(1) / one, PrecDec::percent(1)); +// assert_eq!(PrecDec::percent(10) / one, PrecDec::percent(10)); +// assert_eq!(PrecDec::percent(100) / one, PrecDec::percent(100)); +// assert_eq!(PrecDec::percent(1000) / one, PrecDec::percent(1000)); + +// // double +// assert_eq!(two / PrecDec::percent(1), PrecDec::percent(20_000)); +// assert_eq!(two / PrecDec::percent(10), PrecDec::percent(2_000)); +// assert_eq!(two / PrecDec::percent(100), PrecDec::percent(200)); +// assert_eq!(two / PrecDec::percent(1000), PrecDec::percent(20)); +// assert_eq!(PrecDec::percent(0) / two, PrecDec::percent(0)); +// assert_eq!(PrecDec::percent(1) / two, dec("0.005")); +// assert_eq!(PrecDec::percent(10) / two, PrecDec::percent(5)); +// assert_eq!(PrecDec::percent(100) / two, PrecDec::percent(50)); +// assert_eq!(PrecDec::percent(1000) / two, PrecDec::percent(500)); + +// // half +// assert_eq!(half / PrecDec::percent(1), PrecDec::percent(5_000)); +// assert_eq!(half / PrecDec::percent(10), PrecDec::percent(500)); +// assert_eq!(half / PrecDec::percent(100), PrecDec::percent(50)); +// assert_eq!(half / PrecDec::percent(1000), PrecDec::percent(5)); +// assert_eq!(PrecDec::percent(0) / half, PrecDec::percent(0)); +// assert_eq!(PrecDec::percent(1) / half, PrecDec::percent(2)); +// assert_eq!(PrecDec::percent(10) / half, PrecDec::percent(20)); +// assert_eq!(PrecDec::percent(100) / half, PrecDec::percent(200)); +// assert_eq!(PrecDec::percent(1000) / half, PrecDec::percent(2000)); + +// // Move right +// let a = dec("123127726548762582"); +// assert_eq!(a / dec("1"), dec("123127726548762582")); +// assert_eq!(a / dec("10"), dec("12312772654876258.2")); +// assert_eq!(a / dec("100"), dec("1231277265487625.82")); +// assert_eq!(a / dec("1000"), dec("123127726548762.582")); +// assert_eq!(a / dec("1000000"), dec("123127726548.762582")); +// assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); +// assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); +// assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); +// assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); +// assert_eq!(dec("1") / a, dec("0.000000000000000008")); +// assert_eq!(dec("10") / a, dec("0.000000000000000081")); +// assert_eq!(dec("100") / a, dec("0.000000000000000812")); +// assert_eq!(dec("1000") / a, dec("0.000000000000008121")); +// assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); +// assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); +// assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); +// assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); +// assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); + +// // Move left +// let a = dec("0.123127726548762582"); +// assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); +// assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); +// assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); +// assert_eq!(a / dec("0.001"), dec("123.127726548762582")); +// assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); +// assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); +// assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); +// assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); +// assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + +// assert_eq!( +// PrecDec::percent(15) / PrecDec::percent(60), +// PrecDec::percent(25) +// ); + +// // works for refs +// let a = PrecDec::percent(100); +// let b = PrecDec::percent(20); +// let expected = PrecDec::percent(500); +// assert_eq!(a / b, expected); +// assert_eq!(&a / b, expected); +// assert_eq!(a / &b, expected); +// assert_eq!(&a / &b, expected); +// } + +// #[test] +// fn decimal_div_assign_works() { +// let mut a = PrecDec::percent(15); +// a /= PrecDec::percent(20); +// assert_eq!(a, PrecDec::percent(75)); + +// // works for refs +// let mut a = PrecDec::percent(50); +// let b = PrecDec::percent(20); +// a /= &b; +// assert_eq!(a, PrecDec::percent(250)); +// } + +// #[test] +// #[should_panic(expected = "Division failed - multiplication overflow")] +// fn decimal_div_overflow_panics() { +// let _value = PrecDec::MAX / PrecDec::percent(10); +// } + +// #[test] +// #[should_panic(expected = "Division failed - denominator must not be zero")] +// fn decimal_div_by_zero_panics() { +// let _value = PrecDec::one() / PrecDec::zero(); +// } + +// #[test] +// fn decimal_uint128_division() { +// // a/b +// let left = PrecDec::percent(150); // 1.5 +// let right = Uint256::new(3); +// assert_eq!(left / right, PrecDec::percent(50)); + +// // 0/a +// let left = PrecDec::zero(); +// let right = Uint256::new(300); +// assert_eq!(left / right, PrecDec::zero()); +// } + +// #[test] +// #[should_panic(expected = "attempt to divide by zero")] +// fn decimal_uint128_divide_by_zero() { +// let left = PrecDec::percent(150); // 1.5 +// let right = Uint256::new(0); +// let _result = left / right; +// } + +// #[test] +// fn decimal_uint128_div_assign() { +// // a/b +// let mut dec = PrecDec::percent(150); // 1.5 +// dec /= Uint256::new(3); +// assert_eq!(dec, PrecDec::percent(50)); + +// // 0/a +// let mut dec = PrecDec::zero(); +// dec /= Uint256::new(300); +// assert_eq!(dec, PrecDec::zero()); +// } + +// #[test] +// #[should_panic(expected = "attempt to divide by zero")] +// fn decimal_uint128_div_assign_by_zero() { +// // a/0 +// let mut dec = PrecDec::percent(50); +// dec /= Uint256::new(0); +// } + +// #[test] +// fn decimal_uint128_sqrt() { +// assert_eq!(PrecDec::percent(900).sqrt(), PrecDec::percent(300)); + +// assert!(PrecDec::percent(316) < PrecDec::percent(1000).sqrt()); +// assert!(PrecDec::percent(1000).sqrt() < PrecDec::percent(317)); +// } + +// /// sqrt(2) is an irrational number, i.e. all 18 decimal places should be used. +// #[test] +// fn decimal_uint128_sqrt_is_precise() { +// assert_eq!( +// PrecDec::from_str("2").unwrap().sqrt(), +// PrecDec::from_str("1.414213562373095048").unwrap() // https://www.wolframalpha.com/input/?i=sqrt%282%29 +// ); +// } + +// #[test] +// fn decimal_uint128_sqrt_does_not_overflow() { +// assert_eq!( +// PrecDec::from_str("400").unwrap().sqrt(), +// PrecDec::from_str("20").unwrap() +// ); +// } + +// #[test] +// fn decimal_uint128_sqrt_intermediate_precision_used() { +// assert_eq!( +// PrecDec::from_str("400001").unwrap().sqrt(), +// // The last two digits (27) are truncated below due to the algorithm +// // we use. Larger numbers will cause less precision. +// // https://www.wolframalpha.com/input/?i=sqrt%28400001%29 +// PrecDec::from_str("632.456322602596803200").unwrap() +// ); +// } + +// #[test] +// fn decimal_checked_pow() { +// for exp in 0..10 { +// assert_eq!(PrecDec::one().checked_pow(exp).unwrap(), PrecDec::one()); +// } + +// // This case is mathematically undefined but we ensure consistency with Rust standard types +// // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 +// assert_eq!(PrecDec::zero().checked_pow(0).unwrap(), PrecDec::one()); + +// for exp in 1..10 { +// assert_eq!(PrecDec::zero().checked_pow(exp).unwrap(), PrecDec::zero()); +// } + +// for num in &[ +// PrecDec::percent(50), +// PrecDec::percent(99), +// PrecDec::percent(200), +// ] { +// assert_eq!(num.checked_pow(0).unwrap(), PrecDec::one()) +// } + +// assert_eq!( +// PrecDec::percent(20).checked_pow(2).unwrap(), +// PrecDec::percent(4) +// ); + +// assert_eq!( +// PrecDec::percent(20).checked_pow(3).unwrap(), +// PrecDec::permille(8) +// ); + +// assert_eq!( +// PrecDec::percent(200).checked_pow(4).unwrap(), +// PrecDec::percent(1600) +// ); + +// assert_eq!( +// PrecDec::percent(200).checked_pow(4).unwrap(), +// PrecDec::percent(1600) +// ); + +// assert_eq!( +// PrecDec::percent(700).checked_pow(5).unwrap(), +// PrecDec::percent(1680700) +// ); + +// assert_eq!( +// PrecDec::percent(700).checked_pow(8).unwrap(), +// PrecDec::percent(576480100) +// ); + +// assert_eq!( +// PrecDec::percent(700).checked_pow(10).unwrap(), +// PrecDec::percent(28247524900) +// ); + +// assert_eq!( +// PrecDec::percent(120).checked_pow(123).unwrap(), +// PrecDec(5486473221892422150877397607u128.into()) +// ); + +// assert_eq!( +// PrecDec::percent(10).checked_pow(2).unwrap(), +// PrecDec(10000000000000000u128.into()) +// ); + +// assert_eq!( +// PrecDec::percent(10).checked_pow(18).unwrap(), +// PrecDec(1u128.into()) +// ); +// } + +// #[test] +// fn decimal_checked_pow_overflow() { +// assert_eq!( +// PrecDec::MAX.checked_pow(2), +// Err(OverflowError::new(OverflowOperation::Pow)) +// ); +// } + +// #[test] +// fn decimal_to_string() { +// // Integers +// assert_eq!(PrecDec::zero().to_string(), "0"); +// assert_eq!(PrecDec::one().to_string(), "1"); +// assert_eq!(PrecDec::percent(500).to_string(), "5"); + +// // Decimals +// assert_eq!(PrecDec::percent(125).to_string(), "1.25"); +// assert_eq!(PrecDec::percent(42638).to_string(), "426.38"); +// assert_eq!(PrecDec::percent(3).to_string(), "0.03"); +// assert_eq!(PrecDec::permille(987).to_string(), "0.987"); + +// assert_eq!( +// PrecDec(Uint256::from(1u128)).to_string(), +// "0.000000000000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(10u128)).to_string(), +// "0.00000000000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(100u128)).to_string(), +// "0.0000000000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(1000u128)).to_string(), +// "0.000000000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(10000u128)).to_string(), +// "0.00000000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(100000u128)).to_string(), +// "0.0000000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(1000000u128)).to_string(), +// "0.000000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(10000000u128)).to_string(), +// "0.00000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(100000000u128)).to_string(), +// "0.0000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(1000000000u128)).to_string(), +// "0.000000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(10000000000u128)).to_string(), +// "0.00000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(100000000000u128)).to_string(), +// "0.0000001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(10000000000000u128)).to_string(), +// "0.00001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(100000000000000u128)).to_string(), +// "0.0001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(1000000000000000u128)).to_string(), +// "0.001" +// ); +// assert_eq!( +// PrecDec(Uint256::from(10000000000000000u128)).to_string(), +// "0.01" +// ); +// assert_eq!( +// PrecDec(Uint256::from(100000000000000000u128)).to_string(), +// "0.1" +// ); +// } + +// #[test] +// fn decimal_iter_sum() { +// let items = vec![ +// PrecDec::zero(), +// PrecDec(Uint256::from(2u128)), +// PrecDec(Uint256::from(2u128)), +// ]; +// assert_eq!(items.iter().sum::(), PrecDec(Uint256::from(4u128))); +// assert_eq!( +// items.into_iter().sum::(), +// PrecDec(Uint256::from(4u128)) +// ); + +// let empty: Vec = vec![]; +// assert_eq!(PrecDec::zero(), empty.iter().sum::()); +// } + +// #[test] +// fn decimal_serialize() { +// assert_eq!(serde_json::to_vec(&PrecDec::zero()).unwrap(), br#""0""#); +// assert_eq!(serde_json::to_vec(&PrecDec::one()).unwrap(), br#""1""#); +// assert_eq!( +// serde_json::to_vec(&PrecDec::percent(8)).unwrap(), +// br#""0.08""# +// ); +// assert_eq!( +// serde_json::to_vec(&PrecDec::percent(87)).unwrap(), +// br#""0.87""# +// ); +// assert_eq!( +// serde_json::to_vec(&PrecDec::percent(876)).unwrap(), +// br#""8.76""# +// ); +// assert_eq!( +// serde_json::to_vec(&PrecDec::percent(8765)).unwrap(), +// br#""87.65""# +// ); +// } + +// #[test] +// fn decimal_deserialize() { +// assert_eq!( +// serde_json::from_slice::(br#""0""#).unwrap(), +// PrecDec::zero() +// ); +// assert_eq!( +// serde_json::from_slice::(br#""1""#).unwrap(), +// PrecDec::one() +// ); +// assert_eq!( +// serde_json::from_slice::(br#""000""#).unwrap(), +// PrecDec::zero() +// ); +// assert_eq!( +// serde_json::from_slice::(br#""001""#).unwrap(), +// PrecDec::one() +// ); + +// assert_eq!( +// serde_json::from_slice::(br#""0.08""#).unwrap(), +// PrecDec::percent(8) +// ); +// assert_eq!( +// serde_json::from_slice::(br#""0.87""#).unwrap(), +// PrecDec::percent(87) +// ); +// assert_eq!( +// serde_json::from_slice::(br#""8.76""#).unwrap(), +// PrecDec::percent(876) +// ); +// assert_eq!( +// serde_json::from_slice::(br#""87.65""#).unwrap(), +// PrecDec::percent(8765) +// ); +// } + +// #[test] +// fn decimal_abs_diff_works() { +// let a = PrecDec::percent(285); +// let b = PrecDec::percent(200); +// let expected = PrecDec::percent(85); +// assert_eq!(a.abs_diff(b), expected); +// assert_eq!(b.abs_diff(a), expected); +// } + +// #[test] +// #[allow(clippy::op_ref)] +// fn decimal_rem_works() { +// // 4.02 % 1.11 = 0.69 +// assert_eq!( +// PrecDec::percent(402) % PrecDec::percent(111), +// PrecDec::percent(69) +// ); + +// // 15.25 % 4 = 3.25 +// assert_eq!( +// PrecDec::percent(1525) % PrecDec::percent(400), +// PrecDec::percent(325) +// ); + +// let a = PrecDec::percent(318); +// let b = PrecDec::percent(317); +// let expected = PrecDec::percent(1); +// assert_eq!(a % b, expected); +// assert_eq!(a % &b, expected); +// assert_eq!(&a % b, expected); +// assert_eq!(&a % &b, expected); +// } + +// #[test] +// fn decimal_rem_assign_works() { +// let mut a = PrecDec::percent(17673); +// a %= PrecDec::percent(2362); +// assert_eq!(a, PrecDec::percent(1139)); // 176.73 % 23.62 = 11.39 + +// let mut a = PrecDec::percent(4262); +// let b = PrecDec::percent(1270); +// a %= &b; +// assert_eq!(a, PrecDec::percent(452)); // 42.62 % 12.7 = 4.52 +// } + +// #[test] +// #[should_panic(expected = "divisor of zero")] +// fn decimal_rem_panics_for_zero() { +// let _ = PrecDec::percent(777) % PrecDec::zero(); +// } + +// #[test] +// fn decimal_checked_methods() { +// // checked add +// assert_eq!( +// PrecDec::percent(402) +// .checked_add(PrecDec::percent(111)) +// .unwrap(), +// PrecDec::percent(513) +// ); +// assert!(matches!( +// PrecDec::MAX.checked_add(PrecDec::percent(1)), +// Err(OverflowError { .. }) +// )); + +// // checked sub +// assert_eq!( +// PrecDec::percent(1111) +// .checked_sub(PrecDec::percent(111)) +// .unwrap(), +// PrecDec::percent(1000) +// ); +// assert!(matches!( +// PrecDec::zero().checked_sub(PrecDec::percent(1)), +// Err(OverflowError { .. }) +// )); + +// // checked div +// assert_eq!( +// PrecDec::percent(30) +// .checked_div(PrecDec::percent(200)) +// .unwrap(), +// PrecDec::percent(15) +// ); +// assert_eq!( +// PrecDec::percent(88) +// .checked_div(PrecDec::percent(20)) +// .unwrap(), +// PrecDec::percent(440) +// ); +// assert!(matches!( +// PrecDec::MAX.checked_div(PrecDec::zero()), +// Err(CheckedFromRatioError::DivideByZero {}) +// )); +// assert!(matches!( +// PrecDec::MAX.checked_div(PrecDec::percent(1)), +// Err(CheckedFromRatioError::Overflow {}) +// )); + +// // checked rem +// assert_eq!( +// PrecDec::percent(402) +// .checked_rem(PrecDec::percent(111)) +// .unwrap(), +// PrecDec::percent(69) +// ); +// assert_eq!( +// PrecDec::percent(1525) +// .checked_rem(PrecDec::percent(400)) +// .unwrap(), +// PrecDec::percent(325) +// ); +// assert!(matches!( +// PrecDec::MAX.checked_rem(PrecDec::zero()), +// Err(DivideByZeroError { .. }) +// )); +// } + +// #[test] +// fn decimal_pow_works() { +// assert_eq!(PrecDec::percent(200).pow(2), PrecDec::percent(400)); +// assert_eq!(PrecDec::percent(200).pow(10), PrecDec::percent(102400)); +// } + +// #[test] +// #[should_panic] +// fn decimal_pow_overflow_panics() { +// _ = PrecDec::MAX.pow(2u32); +// } + +// #[test] +// fn decimal_saturating_works() { +// assert_eq!( +// PrecDec::percent(200).saturating_add(PrecDec::percent(200)), +// PrecDec::percent(400) +// ); +// assert_eq!( +// PrecDec::MAX.saturating_add(PrecDec::percent(200)), +// PrecDec::MAX +// ); +// assert_eq!( +// PrecDec::percent(200).saturating_sub(PrecDec::percent(100)), +// PrecDec::percent(100) +// ); +// assert_eq!( +// PrecDec::zero().saturating_sub(PrecDec::percent(200)), +// PrecDec::zero() +// ); +// assert_eq!( +// PrecDec::percent(200).saturating_mul(PrecDec::percent(50)), +// PrecDec::percent(100) +// ); +// assert_eq!( +// PrecDec::MAX.saturating_mul(PrecDec::percent(200)), +// PrecDec::MAX +// ); +// assert_eq!( +// PrecDec::percent(400).saturating_pow(2u32), +// PrecDec::percent(1600) +// ); +// assert_eq!(PrecDec::MAX.saturating_pow(2u32), PrecDec::MAX); +// } + +// #[test] +// fn decimal_rounding() { +// assert_eq!(PrecDec::one().floor(), PrecDec::one()); +// assert_eq!(PrecDec::percent(150).floor(), PrecDec::one()); +// assert_eq!(PrecDec::percent(199).floor(), PrecDec::one()); +// assert_eq!(PrecDec::percent(200).floor(), PrecDec::percent(200)); +// assert_eq!(PrecDec::percent(99).floor(), PrecDec::zero()); + +// assert_eq!(PrecDec::one().ceil(), PrecDec::one()); +// assert_eq!(PrecDec::percent(150).ceil(), PrecDec::percent(200)); +// assert_eq!(PrecDec::percent(199).ceil(), PrecDec::percent(200)); +// assert_eq!(PrecDec::percent(99).ceil(), PrecDec::one()); +// assert_eq!(PrecDec(Uint256::from(1u128)).ceil(), PrecDec::one()); +// } + +// #[test] +// #[should_panic(expected = "attempt to ceil with overflow")] +// fn decimal_ceil_panics() { +// let _ = PrecDec::MAX.ceil(); +// } + +// #[test] +// fn decimal_checked_ceil() { +// assert_eq!( +// PrecDec::percent(199).checked_ceil(), +// Ok(PrecDec::percent(200)) +// ); +// assert!(matches!( +// PrecDec::MAX.checked_ceil(), +// Err(RoundUpOverflowError { .. }) +// )); +// } + +// #[test] +// fn decimal_to_uint_floor_works() { +// let d = PrecDec::from_str("12.000000000000000001").unwrap(); +// assert_eq!(d.to_uint_floor(), Uint256::new(12)); +// let d = PrecDec::from_str("12.345").unwrap(); +// assert_eq!(d.to_uint_floor(), Uint256::new(12)); +// let d = PrecDec::from_str("12.999").unwrap(); +// assert_eq!(d.to_uint_floor(), Uint256::new(12)); +// let d = PrecDec::from_str("0.98451384").unwrap(); +// assert_eq!(d.to_uint_floor(), Uint256::new(0)); + +// let d = PrecDec::from_str("75.0").unwrap(); +// assert_eq!(d.to_uint_floor(), Uint256::new(75)); +// let d = PrecDec::from_str("0.0").unwrap(); +// assert_eq!(d.to_uint_floor(), Uint256::new(0)); + +// let d = PrecDec::MAX; +// assert_eq!(d.to_uint_floor(), Uint256::new(340282366920938463463)); + +// // Does the same as the old workaround `Uint256::one() * my_decimal`. +// // This block can be deleted as part of https://github.com/CosmWasm/cosmwasm/issues/1485. +// let tests = vec![ +// (PrecDec::from_str("12.345").unwrap(), 12u128), +// (PrecDec::from_str("0.98451384").unwrap(), 0u128), +// (PrecDec::from_str("178.0").unwrap(), 178u128), +// (PrecDec::MIN, 0u128), +// (PrecDec::MAX, u128::MAX / PrecDec::DECIMAL_FRACTIONAL.u128()), +// ]; +// for (my_decimal, expected) in tests.into_iter() { +// assert_eq!(my_decimal.to_uint_floor(), Uint256::new(expected)); +// } +// } + +// #[test] +// fn decimal_to_uint_ceil_works() { +// let d = PrecDec::from_str("12.000000000000000001").unwrap(); +// assert_eq!(d.to_uint_ceil(), Uint256::new(13)); +// let d = PrecDec::from_str("12.345").unwrap(); +// assert_eq!(d.to_uint_ceil(), Uint256::new(13)); +// let d = PrecDec::from_str("12.999").unwrap(); +// assert_eq!(d.to_uint_ceil(), Uint256::new(13)); + +// let d = PrecDec::from_str("75.0").unwrap(); +// assert_eq!(d.to_uint_ceil(), Uint256::new(75)); +// let d = PrecDec::from_str("0.0").unwrap(); +// assert_eq!(d.to_uint_ceil(), Uint256::new(0)); + +// let d = PrecDec::MAX; +// assert_eq!(d.to_uint_ceil(), Uint256::new(340282366920938463464)); +// } + +// #[test] +// fn decimal_partial_eq() { +// let test_cases = [ +// ("1", "1", true), +// ("0.5", "0.5", true), +// ("0.5", "0.51", false), +// ("0", "0.00000", true), +// ] +// .into_iter() +// .map(|(lhs, rhs, expected)| (dec(lhs), dec(rhs), expected)); + +// #[allow(clippy::op_ref)] +// for (lhs, rhs, expected) in test_cases { +// assert_eq!(lhs == rhs, expected); +// assert_eq!(&lhs == rhs, expected); +// assert_eq!(lhs == &rhs, expected); +// assert_eq!(&lhs == &rhs, expected); +// } +// } + +// #[test] +// fn decimal_implements_debug() { +// let decimal = PrecDec::from_str("123.45").unwrap(); +// assert_eq!(format!("{decimal:?}"), "Decimal(123.45)"); + +// let test_cases = ["5", "5.01", "42", "0", "2"]; +// for s in test_cases { +// let decimal = PrecDec::from_str(s).unwrap(); +// let expected = format!("Decimal({s})"); +// assert_eq!(format!("{decimal:?}"), expected); +// } +// } +// } From 0256dde8174e43720be19009d03dc9fee0833214 Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Fri, 29 Nov 2024 19:34:02 -0800 Subject: [PATCH 2/9] fix tests --- .../src/types/neutron/util/precdec.rs | 3079 ++++++++--------- 1 file changed, 1532 insertions(+), 1547 deletions(-) diff --git a/packages/neutron-std/src/types/neutron/util/precdec.rs b/packages/neutron-std/src/types/neutron/util/precdec.rs index bdfa62a..a465508 100644 --- a/packages/neutron-std/src/types/neutron/util/precdec.rs +++ b/packages/neutron-std/src/types/neutron/util/precdec.rs @@ -16,20 +16,20 @@ use cosmwasm_std::{Fraction, Isqrt, Uint256, Uint512}; -/// A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0 +/// A fixed-point decimal value with 27 fractional digits, i.e. Precdec(1_000_000_000_000_000_000) == 1.0 /// -/// The greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18) +/// The greatest possible value that can be represented is 115792089237316195423570985008687907853269984665640.564039457584007913129639935 (which is (2^256 - 1) / 10^27) #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, schemars::JsonSchema)] pub struct PrecDec(#[schemars(with = "String")] Uint256); forward_ref_partial_eq!(PrecDec, PrecDec); #[derive(Debug, PartialEq, Eq, thiserror::Error)] -#[error("Decimal range exceeded")] -pub struct DecimalRangeExceeded; +#[error("PrecDec range exceeded")] +pub struct PrecDecRangeExceeded; impl PrecDec { - const DECIMAL_FRACTIONAL: Uint256 = Uint256::from_uint128(1_000_000_000_000_000_000_000_000_000u128); // 1*10**27 + const DECIMAL_FRACTIONAL: Uint256 = Uint256::from_u128(1_000_000_000_000_000_000_000_000_000u128); // 10**27 // const DECIMAL_FRACTIONAL_SQUARED: Uint256 = Uint256::from_uint128(1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000u128); // (1*10**18)**2 = 1*10**54 @@ -42,123 +42,115 @@ impl PrecDec { /// The smallest value that can be represented by this decimal type. pub const MIN: Self = Self(Uint256::MIN); - /// Creates a Decimal(value) - /// This is equivalent to `Decimal::from_atomics(value, 18)` but usable in a const context. + /// Creates a PrecDec(value) + /// This is equivalent to `PrecDec::from_atomics(value, 27)` but usable in a const context. pub const fn new(value: Uint256) -> Self { Self(value) } - /// Creates a Decimal(Uint128(value)) - /// This is equivalent to `Decimal::from_atomics(value, 18)` but usable in a const context. + /// Creates a PrecDec(Uint128(value)) + /// This is equivalent to `PrecDec::from_atomics(value, 27)` but usable in a const context. pub const fn raw(value: u128) -> Self { Self(Uint256::from_u128(value)) } - /// Create a 1.0 Decimal + /// Create a 1.0 PrecDec #[inline] pub const fn one() -> Self { Self(Self::DECIMAL_FRACTIONAL) } - /// Create a 0.0 Decimal + /// Create a 0.0 PrecDec #[inline] pub const fn zero() -> Self { Self(Uint256::zero()) } - /// Convert x% into Decimal + /// Convert x% into PrecDec /// /// ## Examples /// /// ``` /// # use std::str::FromStr; - /// # use cosmwasm_std::Decimal; - /// const HALF: Decimal = Decimal::percent(50); + /// const HALF: PrecDec = PrecDec::percent(50); /// - /// assert_eq!(HALF, Decimal::from_str("0.5").unwrap()); + /// assert_eq!(HALF, PrecDec::from_str("0.5").unwrap()); /// ``` - // TODO: fix me - // pub const fn percent(x: u64) -> Self { - // // multiplication does not overflow since `u64::MAX` * 10**16 is well in u128 range - // let atomics = (x as u128) * 10_000_000_000_000_000; - // Self(Uint128::new(atomics)) - // } - - /// Convert permille (x/1000) into Decimal + pub const fn percent(x: u32) -> Self { + // multiplication does not overflow since `u32::MAX` * 10**25 is well in u128 range + let atomics = (x as u128) * 10_000_000_000_000_000_000_000_000; + Self(Uint256::from_u128(atomics)) + } + + /// Convert permille (x/1000) into PrecDec /// /// ## Examples /// /// ``` /// # use std::str::FromStr; - /// # use cosmwasm_std::Decimal; - /// const HALF: Decimal = Decimal::permille(500); + /// const HALF: PrecDec = PrecDec::permille(500); /// - /// assert_eq!(HALF, Decimal::from_str("0.5").unwrap()); + /// assert_eq!(HALF, PrecDec::from_str("0.5").unwrap()); /// ``` - // TODO: fix me - // pub const fn permille(x: u64) -> Self { - // // multiplication does not overflow since `u64::MAX` * 10**15 is well in u128 range - // let atomics = (x as u128) * 1_000_000_000_000_000; - // Self(Uint128::new(atomics)) - // } + pub const fn permille(x: u32) -> Self { + // multiplication does not overflow since `u32::MAX` * 10**24 is well in u128 range + let atomics = (x as u128) * 1_000_000_000_000_000_000_000_000; + Self(Uint256::from_u128(atomics)) + } - /// Convert basis points (x/10000) into Decimal + /// Convert basis points (x/10000) into PrecDec /// /// ## Examples /// /// ``` /// # use std::str::FromStr; - /// # use cosmwasm_std::Decimal; - /// const TWO_BPS: Decimal = Decimal::bps(2); - /// const HALF: Decimal = Decimal::bps(5000); + /// const TWO_BPS: PrecDec = PrecDec::bps(2); + /// const HALF: PrecDec = PrecDec::bps(5000); /// - /// assert_eq!(TWO_BPS, Decimal::from_str("0.0002").unwrap()); - /// assert_eq!(HALF, Decimal::from_str("0.5").unwrap()); + /// assert_eq!(TWO_BPS, PrecDec::from_str("0.0002").unwrap()); + /// assert_eq!(HALF, PrecDec::from_str("0.5").unwrap()); /// ``` - // TODO: fix me - // pub const fn bps(x: u64) -> Self { - // // multiplication does not overflow since `u64::MAX` * 10**14 is well in u128 range - // let atomics = (x as u128) * 100_000_000_000_000; - // Self(Uint128::new(atomics)) - // } + pub const fn bps(x: u32) -> Self { + // multiplication does not overflow since `u32::MAX` * 10**23 is well in u128 range + let atomics = (x as u128) * 100_000_000_000_000_000_000_000; + Self(Uint256::from_u128(atomics)) + } /// Creates a decimal from a number of atomic units and the number /// of decimal places. The inputs will be converted internally to form - /// a decimal with 18 decimal places. So the input 123 and 2 will create + /// a decimal with 27 decimal places. So the input 123 and 2 will create /// the decimal 1.23. /// - /// Using 18 decimal places is slightly more efficient than other values - /// as no internal conversion is necessary. /// /// ## Examples /// /// ``` - /// # use cosmwasm_std::{Decimal, Uint128}; - /// let a = Decimal::from_atomics(Uint128::new(1234), 3).unwrap(); + /// # use cosmwasm_std::{Uint128}; + /// let a = PrecDec::from_atomics(Uint128::new(1234), 3).unwrap(); /// assert_eq!(a.to_string(), "1.234"); /// - /// let a = Decimal::from_atomics(1234u128, 0).unwrap(); + /// let a = PrecDec::from_atomics(1234u128, 0).unwrap(); /// assert_eq!(a.to_string(), "1234"); /// - /// let a = Decimal::from_atomics(1u64, 18).unwrap(); + /// let a = PrecDec::from_atomics(1u64, 18).unwrap(); /// assert_eq!(a.to_string(), "0.000000000000000001"); /// ``` pub fn from_atomics( atomics: impl Into, decimal_places: u32, - ) -> Result { + ) -> Result { let atomics = atomics.into(); const TEN: Uint256 = Uint256::from_u128(10); Ok(match decimal_places.cmp(&Self::DECIMAL_PLACES) { Ordering::Less => { let digits = (Self::DECIMAL_PLACES) - decimal_places; // No overflow because decimal_places < DECIMAL_PLACES - let factor = TEN.checked_pow(digits).unwrap(); // Safe because digits <= 17 //TODO: Is still ok? + let factor = TEN.checked_pow(digits).unwrap(); // Safe because digits <= 27 Self( atomics .checked_mul(factor) - .map_err(|_| DecimalRangeExceeded)?, + .map_err(|_| PrecDecRangeExceeded)?, ) } Ordering::Equal => Self(atomics), @@ -176,7 +168,7 @@ impl PrecDec { }) } - /// Returns the ratio (numerator / denominator) as a Decimal + /// Returns the ratio (numerator / denominator) as a PrecDec pub fn from_ratio(numerator: impl Into, denominator: impl Into) -> Self { match PrecDec::checked_from_ratio(numerator, denominator) { Ok(value) => value, @@ -187,7 +179,7 @@ impl PrecDec { } } - /// Returns the ratio (numerator / denominator) as a Decimal + /// Returns the ratio (numerator / denominator) as a PrecDec pub fn checked_from_ratio( numerator: impl Into, denominator: impl Into, @@ -217,15 +209,15 @@ impl PrecDec { /// ## Examples /// /// ``` - /// # use cosmwasm_std::{Decimal, Uint128}; + /// # use cosmwasm_std::{Uint128}; /// # use core::str::FromStr; /// // Value with whole and fractional part - /// let a = Decimal::from_str("1.234").unwrap(); + /// let a = PrecDec::from_str("1.234").unwrap(); /// assert_eq!(a.decimal_places(), 18); /// assert_eq!(a.atomics(), Uint128::new(1234000000000000000)); /// /// // Smallest possible value - /// let b = Decimal::from_str("0.000000000000000001").unwrap(); + /// let b = PrecDec::from_str("0.000000000000000001").unwrap(); /// assert_eq!(b.decimal_places(), 18); /// assert_eq!(b.atomics(), Uint128::new(1)); /// ``` @@ -287,7 +279,7 @@ impl PrecDec { } //TODO: fix me, overflow check doesn't work - /// Multiplies one `Decimal` by another, returning an `OverflowError` if an overflow occurred. + /// Multiplies one `PrecDec` by another, returning an `OverflowError` if an overflow occurred. pub fn checked_mul(self, other: Self) -> Result { let result_as_uint256 = self.numerator().full_mul(other.numerator()) / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL) ; // from_uint128 is a const method and should be "free" @@ -346,7 +338,7 @@ impl PrecDec { .map_err(|_| DivideByZeroError) } - /// Returns the approximate square root as a Decimal. + /// Returns the approximate square root as a PrecDec. /// /// This should not overflow or panic. #[must_use = "this returns the result of the operation, without modifying the original"] @@ -358,7 +350,7 @@ impl PrecDec { // TODO: This could be made more efficient once log10 is in: // https://github.com/rust-lang/rust/issues/70887 // The max precision is something like `9 - log10(self.0) / 2`. - (0..=Self::DECIMAL_PLACES / 2) + (0..14) .rev() .find_map(|i| self.sqrt_with_precision(i)) // The last step (i = 0) is guaranteed to succeed because `isqrt(u128::MAX) * 10^9` does not overflow @@ -371,10 +363,10 @@ impl PrecDec { /// Returns `None` if the internal multiplication overflows. #[must_use = "this returns the result of the operation, without modifying the original"] fn sqrt_with_precision(&self, precision: u32) -> Option { - let inner_mul = 100u128.pow(precision); - self.0.checked_mul(inner_mul.into()).ok().map(|inner| { - let outer_mul = 10u128.pow(Self::DECIMAL_PLACES / 2 - precision); - PrecDec(inner.isqrt().checked_mul(Uint256::from(outer_mul)).unwrap()) + let inner_mul = Uint256::from(100u128).pow(precision); + self.0.checked_mul(inner_mul).ok().map(|inner| { + let outer_mul = Uint256::from(10u128).pow(Self::DECIMAL_PLACES / 2 - precision); + Self(inner.isqrt().checked_mul(outer_mul).unwrap()) }) } @@ -422,15 +414,15 @@ impl PrecDec { /// /// ``` /// use core::str::FromStr; - /// use cosmwasm_std::{Decimal, Uint128}; + /// use cosmwasm_std::{Uint128}; /// - /// let d = Decimal::from_str("12.345").unwrap(); + /// let d = PrecDec::from_str("12.345").unwrap(); /// assert_eq!(d.to_uint_floor(), Uint128::new(12)); /// - /// let d = Decimal::from_str("12.999").unwrap(); + /// let d = PrecDec::from_str("12.999").unwrap(); /// assert_eq!(d.to_uint_floor(), Uint128::new(12)); /// - /// let d = Decimal::from_str("75.0").unwrap(); + /// let d = PrecDec::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_floor(), Uint128::new(75)); /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] @@ -445,15 +437,15 @@ impl PrecDec { /// /// ``` /// use core::str::FromStr; - /// use cosmwasm_std::{Decimal, Uint128}; + /// use cosmwasm_std::{Uint128}; /// - /// let d = Decimal::from_str("12.345").unwrap(); + /// let d = PrecDec::from_str("12.345").unwrap(); /// assert_eq!(d.to_uint_ceil(), Uint128::new(13)); /// - /// let d = Decimal::from_str("12.999").unwrap(); + /// let d = PrecDec::from_str("12.999").unwrap(); /// assert_eq!(d.to_uint_ceil(), Uint128::new(13)); /// - /// let d = Decimal::from_str("75.0").unwrap(); + /// let d = PrecDec::from_str("75.0").unwrap(); /// assert_eq!(d.to_uint_ceil(), Uint128::new(75)); /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] @@ -488,7 +480,7 @@ impl Fraction for PrecDec { if self.is_zero() { None } else { - let decimal_fractional_squared = Self::DECIMAL_FRACTIONAL.Mul(Self::DECIMAL_FRACTIONAL); + let decimal_fractional_squared = Self::DECIMAL_FRACTIONAL.checked_mul(Self::DECIMAL_FRACTIONAL).unwrap(); // Let self be p/q with p = self.0 and q = DECIMAL_FRACTIONAL. // Now we calculate the inverse a/b = q/p such that b = DECIMAL_FRACTIONAL. Then // `a = DECIMAL_FRACTIONAL*DECIMAL_FRACTIONAL / self.0`. @@ -498,43 +490,40 @@ impl Fraction for PrecDec { } impl TryFrom for PrecDec { - type Error = DecimalRangeExceeded; + type Error = PrecDecRangeExceeded; fn try_from(value: Decimal256) -> Result { - value - .atomics() - .try_into() - .map(PrecDec) - .map_err(|_| DecimalRangeExceeded) + Self::from_atomics(value.atomics(), Decimal256::DECIMAL_PLACES) } } impl TryFrom for PrecDec { - type Error = DecimalRangeExceeded; + type Error = PrecDecRangeExceeded; fn try_from(value: SignedDecimal) -> Result { value .atomics() .try_into() .map(PrecDec) - .map_err(|_| DecimalRangeExceeded) + .map_err(|_| PrecDecRangeExceeded) + } } impl TryFrom for PrecDec { - type Error = DecimalRangeExceeded; + type Error = PrecDecRangeExceeded; fn try_from(value: SignedDecimal256) -> Result { value .atomics() .try_into() .map(PrecDec) - .map_err(|_| DecimalRangeExceeded) + .map_err(|_| PrecDecRangeExceeded) } } impl TryFrom for PrecDec { - type Error = DecimalRangeExceeded; + type Error = PrecDecRangeExceeded; #[inline] fn try_from(value: Uint256) -> Result { @@ -545,7 +534,7 @@ impl TryFrom for PrecDec { impl FromStr for PrecDec { type Err = StdError; - /// Converts the decimal string to a Decimal + /// Converts the decimal string to a PrecDec /// Possible inputs: "1.23", "1", "000012", "1.123000000" /// Disallowed: "", ".23" /// @@ -616,7 +605,7 @@ impl fmt::Display for PrecDec { impl fmt::Debug for PrecDec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Decimal({self})") + write!(f, "PrecDec({self})") } } @@ -657,7 +646,7 @@ impl Mul for PrecDec { #[allow(clippy::suspicious_arithmetic_impl)] fn mul(self, other: Self) -> Self { - // Decimals are fractions. We can multiply two decimals a and b + // PrecDecs are fractions. We can multiply two decimals a and b // via // (a.numerator() * b.numerator()) / (a.denominator() * b.denominator()) // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() @@ -762,13 +751,13 @@ impl<'de> Deserialize<'de> for PrecDec { where D: Deserializer<'de>, { - deserializer.deserialize_str(DecimalVisitor) + deserializer.deserialize_str(PrecDecVisitor) } } -struct DecimalVisitor; +struct PrecDecVisitor; -impl<'de> de::Visitor<'de> for DecimalVisitor { +impl<'de> de::Visitor<'de> for PrecDecVisitor { type Value = PrecDec; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -786,1458 +775,1454 @@ impl<'de> de::Visitor<'de> for DecimalVisitor { } } -// #[cfg(test)] -// mod tests { -// use super::*; - -// fn dec(input: &str) -> PrecDec { -// PrecDec::from_str(input).unwrap() -// } - -// #[test] -// fn decimal_new() { -// let expected = Uint256::from(300u128); -// assert_eq!(PrecDec::new(expected).0, expected); -// } - -// #[test] -// fn decimal_raw() { -// let value = 300u128; -// assert_eq!(PrecDec::raw(value).0.u128(), value); -// } - -// #[test] -// fn decimal_one() { -// let value = PrecDec::one(); -// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL); -// } - -// #[test] -// fn decimal_zero() { -// let value = PrecDec::zero(); -// assert!(value.0.is_zero()); -// } - -// #[test] -// fn decimal_percent() { -// let value = PrecDec::percent(50); -// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(2u8)); -// } - -// #[test] -// fn decimal_permille() { -// let value = PrecDec::permille(125); -// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(8u8)); -// } - -// #[test] -// fn decimal_bps() { -// let value = PrecDec::bps(125); -// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(80u8)); -// } - -// #[test] -// fn decimal_from_decimal256_works() { -// let too_big = Decimal256::new(Uint256::from(Uint256::MAX) + Uint256::one()); -// assert_eq!(PrecDec::try_from(too_big), Err(DecimalRangeExceeded)); - -// let just_right = Decimal256::new(Uint256::from(Uint256::MAX)); -// assert_eq!(PrecDec::try_from(just_right), Ok(PrecDec::MAX)); - -// assert_eq!(PrecDec::try_from(Decimal256::zero()), Ok(PrecDec::zero())); -// assert_eq!(PrecDec::try_from(Decimal256::one()), Ok(PrecDec::one())); -// assert_eq!( -// PrecDec::try_from(Decimal256::percent(50)), -// Ok(PrecDec::percent(50)) -// ); -// } - -// #[test] -// fn decimal_try_from_integer() { -// let int = Uint256::new(0xDEADBEEF); -// let decimal = PrecDec::try_from(int).unwrap(); -// assert_eq!(int.to_string(), decimal.to_string()); -// } - -// #[test] -// fn decimal_try_from_signed_works() { -// assert_eq!( -// PrecDec::try_from(SignedDecimal::MAX).unwrap(), -// PrecDec::raw(SignedDecimal::MAX.atomics().i128() as u128) -// ); -// assert_eq!( -// PrecDec::try_from(SignedDecimal::zero()).unwrap(), -// PrecDec::zero() -// ); -// assert_eq!( -// PrecDec::try_from(SignedDecimal::one()).unwrap(), -// PrecDec::one() -// ); -// assert_eq!( -// PrecDec::try_from(SignedDecimal::percent(50)).unwrap(), -// PrecDec::percent(50) -// ); -// assert_eq!( -// PrecDec::try_from(SignedDecimal::negative_one()), -// Err(DecimalRangeExceeded) -// ); -// assert_eq!( -// PrecDec::try_from(SignedDecimal::MIN), -// Err(DecimalRangeExceeded) -// ); -// } - -// #[test] -// fn decimal_from_atomics_works() { -// let one = PrecDec::one(); -// let two = one + one; - -// assert_eq!(PrecDec::from_atomics(1u128, 0).unwrap(), one); -// assert_eq!(PrecDec::from_atomics(10u128, 1).unwrap(), one); -// assert_eq!(PrecDec::from_atomics(100u128, 2).unwrap(), one); -// assert_eq!(PrecDec::from_atomics(1000u128, 3).unwrap(), one); -// assert_eq!( -// PrecDec::from_atomics(1000000000000000000u128, 18).unwrap(), -// one -// ); -// assert_eq!( -// PrecDec::from_atomics(10000000000000000000u128, 19).unwrap(), -// one -// ); -// assert_eq!( -// PrecDec::from_atomics(100000000000000000000u128, 20).unwrap(), -// one -// ); - -// assert_eq!(PrecDec::from_atomics(2u128, 0).unwrap(), two); -// assert_eq!(PrecDec::from_atomics(20u128, 1).unwrap(), two); -// assert_eq!(PrecDec::from_atomics(200u128, 2).unwrap(), two); -// assert_eq!(PrecDec::from_atomics(2000u128, 3).unwrap(), two); -// assert_eq!( -// PrecDec::from_atomics(2000000000000000000u128, 18).unwrap(), -// two -// ); -// assert_eq!( -// PrecDec::from_atomics(20000000000000000000u128, 19).unwrap(), -// two -// ); -// assert_eq!( -// PrecDec::from_atomics(200000000000000000000u128, 20).unwrap(), -// two -// ); - -// // Cuts decimal digits (20 provided but only 18 can be stored) -// assert_eq!( -// PrecDec::from_atomics(4321u128, 20).unwrap(), -// PrecDec::from_str("0.000000000000000043").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(6789u128, 20).unwrap(), -// PrecDec::from_str("0.000000000000000067").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(u128::MAX, 38).unwrap(), -// PrecDec::from_str("3.402823669209384634").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(u128::MAX, 39).unwrap(), -// PrecDec::from_str("0.340282366920938463").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(u128::MAX, 45).unwrap(), -// PrecDec::from_str("0.000000340282366920").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(u128::MAX, 51).unwrap(), -// PrecDec::from_str("0.000000000000340282").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(u128::MAX, 56).unwrap(), -// PrecDec::from_str("0.000000000000000003").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(u128::MAX, 57).unwrap(), -// PrecDec::from_str("0.000000000000000000").unwrap() -// ); -// assert_eq!( -// PrecDec::from_atomics(u128::MAX, u32::MAX).unwrap(), -// PrecDec::from_str("0.000000000000000000").unwrap() -// ); - -// // Can be used with max value -// let max = PrecDec::MAX; -// assert_eq!( -// PrecDec::from_atomics(max.atomics(), max.decimal_places()).unwrap(), -// max -// ); - -// // Overflow is only possible with digits < 18 -// let result = PrecDec::from_atomics(u128::MAX, 17); -// assert_eq!(result.unwrap_err(), DecimalRangeExceeded); -// } - -// #[test] -// fn decimal_from_ratio_works() { -// // 1.0 -// assert_eq!(PrecDec::from_ratio(1u128, 1u128), PrecDec::one()); -// assert_eq!(PrecDec::from_ratio(53u128, 53u128), PrecDec::one()); -// assert_eq!(PrecDec::from_ratio(125u128, 125u128), PrecDec::one()); - -// // 1.5 -// assert_eq!(PrecDec::from_ratio(3u128, 2u128), PrecDec::percent(150)); -// assert_eq!(PrecDec::from_ratio(150u128, 100u128), PrecDec::percent(150)); -// assert_eq!(PrecDec::from_ratio(333u128, 222u128), PrecDec::percent(150)); - -// // 0.125 -// assert_eq!(PrecDec::from_ratio(1u64, 8u64), PrecDec::permille(125)); -// assert_eq!(PrecDec::from_ratio(125u64, 1000u64), PrecDec::permille(125)); - -// // 1/3 (result floored) -// assert_eq!( -// PrecDec::from_ratio(1u64, 3u64), -// PrecDec(Uint256::from(333_333_333_333_333_333u128)) -// ); - -// // 2/3 (result floored) -// assert_eq!( -// PrecDec::from_ratio(2u64, 3u64), -// PrecDec(Uint256::from(666_666_666_666_666_666u128)) -// ); - -// // large inputs -// assert_eq!(PrecDec::from_ratio(0u128, u128::MAX), PrecDec::zero()); -// assert_eq!(PrecDec::from_ratio(u128::MAX, u128::MAX), PrecDec::one()); -// // 340282366920938463463 is the largest integer <= Decimal::MAX -// assert_eq!( -// PrecDec::from_ratio(340282366920938463463u128, 1u128), -// PrecDec::from_str("340282366920938463463").unwrap() -// ); -// } - -// #[test] -// #[should_panic(expected = "Denominator must not be zero")] -// fn decimal_from_ratio_panics_for_zero_denominator() { -// PrecDec::from_ratio(1u128, 0u128); -// } - -// #[test] -// #[should_panic(expected = "Multiplication overflow")] -// fn decimal_from_ratio_panics_for_mul_overflow() { -// PrecDec::from_ratio(u128::MAX, 1u128); -// } - -// #[test] -// fn decimal_checked_from_ratio_does_not_panic() { -// assert_eq!( -// PrecDec::checked_from_ratio(1u128, 0u128), -// Err(CheckedFromRatioError::DivideByZero) -// ); - -// assert_eq!( -// PrecDec::checked_from_ratio(u128::MAX, 1u128), -// Err(CheckedFromRatioError::Overflow) -// ); -// } - -// #[test] -// fn decimal_implements_fraction() { -// let fraction = PrecDec::from_str("1234.567").unwrap(); -// assert_eq!( -// fraction.numerator(), -// Uint256::from(1_234_567_000_000_000_000_000u128) -// ); -// assert_eq!( -// fraction.denominator(), -// Uint256::from(1_000_000_000_000_000_000u128) -// ); -// } - -// #[test] -// fn decimal_from_str_works() { -// // Integers -// assert_eq!(PrecDec::from_str("0").unwrap(), PrecDec::percent(0)); -// assert_eq!(PrecDec::from_str("1").unwrap(), PrecDec::percent(100)); -// assert_eq!(PrecDec::from_str("5").unwrap(), PrecDec::percent(500)); -// assert_eq!(PrecDec::from_str("42").unwrap(), PrecDec::percent(4200)); -// assert_eq!(PrecDec::from_str("000").unwrap(), PrecDec::percent(0)); -// assert_eq!(PrecDec::from_str("001").unwrap(), PrecDec::percent(100)); -// assert_eq!(PrecDec::from_str("005").unwrap(), PrecDec::percent(500)); -// assert_eq!(PrecDec::from_str("0042").unwrap(), PrecDec::percent(4200)); - -// // Decimals -// assert_eq!(PrecDec::from_str("1.0").unwrap(), PrecDec::percent(100)); -// assert_eq!(PrecDec::from_str("1.5").unwrap(), PrecDec::percent(150)); -// assert_eq!(PrecDec::from_str("0.5").unwrap(), PrecDec::percent(50)); -// assert_eq!(PrecDec::from_str("0.123").unwrap(), PrecDec::permille(123)); - -// assert_eq!(PrecDec::from_str("40.00").unwrap(), PrecDec::percent(4000)); -// assert_eq!(PrecDec::from_str("04.00").unwrap(), PrecDec::percent(400)); -// assert_eq!(PrecDec::from_str("00.40").unwrap(), PrecDec::percent(40)); -// assert_eq!(PrecDec::from_str("00.04").unwrap(), PrecDec::percent(4)); - -// // Can handle DECIMAL_PLACES fractional digits -// assert_eq!( -// PrecDec::from_str("7.123456789012345678").unwrap(), -// PrecDec(Uint256::from(7123456789012345678u128)) -// ); -// assert_eq!( -// PrecDec::from_str("7.999999999999999999").unwrap(), -// PrecDec(Uint256::from(7999999999999999999u128)) -// ); - -// // Works for documented max value -// assert_eq!( -// PrecDec::from_str("340282366920938463463.374607431768211455").unwrap(), -// PrecDec::MAX -// ); -// } - -// #[test] -// fn decimal_from_str_errors_for_broken_whole_part() { -// match PrecDec::from_str("").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), -// e => panic!("Unexpected error: {e:?}"), -// } - -// match PrecDec::from_str(" ").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), -// e => panic!("Unexpected error: {e:?}"), -// } - -// match PrecDec::from_str("-1").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), -// e => panic!("Unexpected error: {e:?}"), -// } -// } - -// #[test] -// fn decimal_from_str_errors_for_broken_fractional_part() { -// match PrecDec::from_str("1.").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), -// e => panic!("Unexpected error: {e:?}"), -// } - -// match PrecDec::from_str("1. ").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), -// e => panic!("Unexpected error: {e:?}"), -// } - -// match PrecDec::from_str("1.e").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), -// e => panic!("Unexpected error: {e:?}"), -// } - -// match PrecDec::from_str("1.2e3").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), -// e => panic!("Unexpected error: {e:?}"), -// } -// } - -// #[test] -// fn decimal_from_str_errors_for_more_than_18_fractional_digits() { -// match PrecDec::from_str("7.1234567890123456789").unwrap_err() { -// StdError::GenericErr { msg, .. } => { -// assert_eq!(msg, "Cannot parse more than 18 fractional digits",) -// } -// e => panic!("Unexpected error: {e:?}"), -// } - -// // No special rules for trailing zeros. This could be changed but adds gas cost for the happy path. -// match PrecDec::from_str("7.1230000000000000000").unwrap_err() { -// StdError::GenericErr { msg, .. } => { -// assert_eq!(msg, "Cannot parse more than 18 fractional digits") -// } -// e => panic!("Unexpected error: {e:?}"), -// } -// } - -// #[test] -// fn decimal_from_str_errors_for_invalid_number_of_dots() { -// match PrecDec::from_str("1.2.3").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Unexpected number of dots"), -// e => panic!("Unexpected error: {e:?}"), -// } - -// match PrecDec::from_str("1.2.3.4").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Unexpected number of dots"), -// e => panic!("Unexpected error: {e:?}"), -// } -// } - -// #[test] -// fn decimal_from_str_errors_for_more_than_max_value() { -// // Integer -// match PrecDec::from_str("340282366920938463464").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), -// e => panic!("Unexpected error: {e:?}"), -// } - -// // Decimal -// match PrecDec::from_str("340282366920938463464.0").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), -// e => panic!("Unexpected error: {e:?}"), -// } -// match PrecDec::from_str("340282366920938463463.374607431768211456").unwrap_err() { -// StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), -// e => panic!("Unexpected error: {e:?}"), -// } -// } - -// #[test] -// fn decimal_atomics_works() { -// let zero = PrecDec::zero(); -// let one = PrecDec::one(); -// let half = PrecDec::percent(50); -// let two = PrecDec::percent(200); -// let max = PrecDec::MAX; - -// assert_eq!(zero.atomics(), Uint256::new(0)); -// assert_eq!(one.atomics(), Uint256::new(1000000000000000000)); -// assert_eq!(half.atomics(), Uint256::new(500000000000000000)); -// assert_eq!(two.atomics(), Uint256::new(2000000000000000000)); -// assert_eq!(max.atomics(), Uint256::MAX); -// } - -// #[test] -// fn decimal_decimal_places_works() { -// let zero = PrecDec::zero(); -// let one = PrecDec::one(); -// let half = PrecDec::percent(50); -// let two = PrecDec::percent(200); -// let max = PrecDec::MAX; - -// assert_eq!(zero.decimal_places(), 18); -// assert_eq!(one.decimal_places(), 18); -// assert_eq!(half.decimal_places(), 18); -// assert_eq!(two.decimal_places(), 18); -// assert_eq!(max.decimal_places(), 18); -// } - -// #[test] -// fn decimal_is_zero_works() { -// assert!(PrecDec::zero().is_zero()); -// assert!(PrecDec::percent(0).is_zero()); -// assert!(PrecDec::permille(0).is_zero()); - -// assert!(!PrecDec::one().is_zero()); -// assert!(!PrecDec::percent(123).is_zero()); -// assert!(!PrecDec::permille(1234).is_zero()); -// } - -// #[test] -// fn decimal_inv_works() { -// // d = 0 -// assert_eq!(PrecDec::zero().inv(), None); - -// // d == 1 -// assert_eq!(PrecDec::one().inv(), Some(PrecDec::one())); - -// // d > 1 exact -// assert_eq!( -// PrecDec::from_str("2").unwrap().inv(), -// Some(PrecDec::from_str("0.5").unwrap()) -// ); -// assert_eq!( -// PrecDec::from_str("20").unwrap().inv(), -// Some(PrecDec::from_str("0.05").unwrap()) -// ); -// assert_eq!( -// PrecDec::from_str("200").unwrap().inv(), -// Some(PrecDec::from_str("0.005").unwrap()) -// ); -// assert_eq!( -// PrecDec::from_str("2000").unwrap().inv(), -// Some(PrecDec::from_str("0.0005").unwrap()) -// ); - -// // d > 1 rounded -// assert_eq!( -// PrecDec::from_str("3").unwrap().inv(), -// Some(PrecDec::from_str("0.333333333333333333").unwrap()) -// ); -// assert_eq!( -// PrecDec::from_str("6").unwrap().inv(), -// Some(PrecDec::from_str("0.166666666666666666").unwrap()) -// ); - -// // d < 1 exact -// assert_eq!( -// PrecDec::from_str("0.5").unwrap().inv(), -// Some(PrecDec::from_str("2").unwrap()) -// ); -// assert_eq!( -// PrecDec::from_str("0.05").unwrap().inv(), -// Some(PrecDec::from_str("20").unwrap()) -// ); -// assert_eq!( -// PrecDec::from_str("0.005").unwrap().inv(), -// Some(PrecDec::from_str("200").unwrap()) -// ); -// assert_eq!( -// PrecDec::from_str("0.0005").unwrap().inv(), -// Some(PrecDec::from_str("2000").unwrap()) -// ); -// } - -// #[test] -// #[allow(clippy::op_ref)] -// fn decimal_add_works() { -// let value = PrecDec::one() + PrecDec::percent(50); // 1.5 -// assert_eq!( -// value.0, -// PrecDec::DECIMAL_FRACTIONAL * Uint256::from(3u8) / Uint256::from(2u8) -// ); - -// assert_eq!( -// PrecDec::percent(5) + PrecDec::percent(4), -// PrecDec::percent(9) -// ); -// assert_eq!(PrecDec::percent(5) + PrecDec::zero(), PrecDec::percent(5)); -// assert_eq!(PrecDec::zero() + PrecDec::zero(), PrecDec::zero()); - -// // works for refs -// let a = PrecDec::percent(15); -// let b = PrecDec::percent(25); -// let expected = PrecDec::percent(40); -// assert_eq!(a + b, expected); -// assert_eq!(&a + b, expected); -// assert_eq!(a + &b, expected); -// assert_eq!(&a + &b, expected); -// } - -// #[test] -// #[should_panic(expected = "attempt to add with overflow")] -// fn decimal_add_overflow_panics() { -// let _value = PrecDec::MAX + PrecDec::percent(50); -// } - -// #[test] -// fn decimal_add_assign_works() { -// let mut a = PrecDec::percent(30); -// a += PrecDec::percent(20); -// assert_eq!(a, PrecDec::percent(50)); - -// // works for refs -// let mut a = PrecDec::percent(15); -// let b = PrecDec::percent(3); -// let expected = PrecDec::percent(18); -// a += &b; -// assert_eq!(a, expected); -// } - -// #[test] -// #[allow(clippy::op_ref)] -// fn decimal_sub_works() { -// let value = PrecDec::one() - PrecDec::percent(50); // 0.5 -// assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(2u8)); - -// assert_eq!( -// PrecDec::percent(9) - PrecDec::percent(4), -// PrecDec::percent(5) -// ); -// assert_eq!(PrecDec::percent(16) - PrecDec::zero(), PrecDec::percent(16)); -// assert_eq!(PrecDec::percent(16) - PrecDec::percent(16), PrecDec::zero()); -// assert_eq!(PrecDec::zero() - PrecDec::zero(), PrecDec::zero()); - -// // works for refs -// let a = PrecDec::percent(13); -// let b = PrecDec::percent(6); -// let expected = PrecDec::percent(7); -// assert_eq!(a - b, expected); -// assert_eq!(&a - b, expected); -// assert_eq!(a - &b, expected); -// assert_eq!(&a - &b, expected); -// } - -// #[test] -// #[should_panic(expected = "attempt to subtract with overflow")] -// fn decimal_sub_overflow_panics() { -// let _value = PrecDec::zero() - PrecDec::percent(50); -// } - -// #[test] -// fn decimal_sub_assign_works() { -// let mut a = PrecDec::percent(20); -// a -= PrecDec::percent(2); -// assert_eq!(a, PrecDec::percent(18)); - -// // works for refs -// let mut a = PrecDec::percent(33); -// let b = PrecDec::percent(13); -// let expected = PrecDec::percent(20); -// a -= &b; -// assert_eq!(a, expected); -// } - -// #[test] -// #[allow(clippy::op_ref)] -// fn decimal_implements_mul() { -// let one = PrecDec::one(); -// let two = one + one; -// let half = PrecDec::percent(50); - -// // 1*x and x*1 -// assert_eq!(one * PrecDec::percent(0), PrecDec::percent(0)); -// assert_eq!(one * PrecDec::percent(1), PrecDec::percent(1)); -// assert_eq!(one * PrecDec::percent(10), PrecDec::percent(10)); -// assert_eq!(one * PrecDec::percent(100), PrecDec::percent(100)); -// assert_eq!(one * PrecDec::percent(1000), PrecDec::percent(1000)); -// assert_eq!(one * PrecDec::MAX, PrecDec::MAX); -// assert_eq!(PrecDec::percent(0) * one, PrecDec::percent(0)); -// assert_eq!(PrecDec::percent(1) * one, PrecDec::percent(1)); -// assert_eq!(PrecDec::percent(10) * one, PrecDec::percent(10)); -// assert_eq!(PrecDec::percent(100) * one, PrecDec::percent(100)); -// assert_eq!(PrecDec::percent(1000) * one, PrecDec::percent(1000)); -// assert_eq!(PrecDec::MAX * one, PrecDec::MAX); - -// // double -// assert_eq!(two * PrecDec::percent(0), PrecDec::percent(0)); -// assert_eq!(two * PrecDec::percent(1), PrecDec::percent(2)); -// assert_eq!(two * PrecDec::percent(10), PrecDec::percent(20)); -// assert_eq!(two * PrecDec::percent(100), PrecDec::percent(200)); -// assert_eq!(two * PrecDec::percent(1000), PrecDec::percent(2000)); -// assert_eq!(PrecDec::percent(0) * two, PrecDec::percent(0)); -// assert_eq!(PrecDec::percent(1) * two, PrecDec::percent(2)); -// assert_eq!(PrecDec::percent(10) * two, PrecDec::percent(20)); -// assert_eq!(PrecDec::percent(100) * two, PrecDec::percent(200)); -// assert_eq!(PrecDec::percent(1000) * two, PrecDec::percent(2000)); - -// // half -// assert_eq!(half * PrecDec::percent(0), PrecDec::percent(0)); -// assert_eq!(half * PrecDec::percent(1), PrecDec::permille(5)); -// assert_eq!(half * PrecDec::percent(10), PrecDec::percent(5)); -// assert_eq!(half * PrecDec::percent(100), PrecDec::percent(50)); -// assert_eq!(half * PrecDec::percent(1000), PrecDec::percent(500)); -// assert_eq!(PrecDec::percent(0) * half, PrecDec::percent(0)); -// assert_eq!(PrecDec::percent(1) * half, PrecDec::permille(5)); -// assert_eq!(PrecDec::percent(10) * half, PrecDec::percent(5)); -// assert_eq!(PrecDec::percent(100) * half, PrecDec::percent(50)); -// assert_eq!(PrecDec::percent(1000) * half, PrecDec::percent(500)); - -// // Move left -// let a = dec("123.127726548762582"); -// assert_eq!(a * dec("1"), dec("123.127726548762582")); -// assert_eq!(a * dec("10"), dec("1231.27726548762582")); -// assert_eq!(a * dec("100"), dec("12312.7726548762582")); -// assert_eq!(a * dec("1000"), dec("123127.726548762582")); -// assert_eq!(a * dec("1000000"), dec("123127726.548762582")); -// assert_eq!(a * dec("1000000000"), dec("123127726548.762582")); -// assert_eq!(a * dec("1000000000000"), dec("123127726548762.582")); -// assert_eq!(a * dec("1000000000000000"), dec("123127726548762582")); -// assert_eq!(a * dec("1000000000000000000"), dec("123127726548762582000")); -// assert_eq!(dec("1") * a, dec("123.127726548762582")); -// assert_eq!(dec("10") * a, dec("1231.27726548762582")); -// assert_eq!(dec("100") * a, dec("12312.7726548762582")); -// assert_eq!(dec("1000") * a, dec("123127.726548762582")); -// assert_eq!(dec("1000000") * a, dec("123127726.548762582")); -// assert_eq!(dec("1000000000") * a, dec("123127726548.762582")); -// assert_eq!(dec("1000000000000") * a, dec("123127726548762.582")); -// assert_eq!(dec("1000000000000000") * a, dec("123127726548762582")); -// assert_eq!(dec("1000000000000000000") * a, dec("123127726548762582000")); - -// // Move right -// let max = PrecDec::MAX; -// assert_eq!( -// max * dec("1.0"), -// dec("340282366920938463463.374607431768211455") -// ); -// assert_eq!( -// max * dec("0.1"), -// dec("34028236692093846346.337460743176821145") -// ); -// assert_eq!( -// max * dec("0.01"), -// dec("3402823669209384634.633746074317682114") -// ); -// assert_eq!( -// max * dec("0.001"), -// dec("340282366920938463.463374607431768211") -// ); -// assert_eq!( -// max * dec("0.000001"), -// dec("340282366920938.463463374607431768") -// ); -// assert_eq!( -// max * dec("0.000000001"), -// dec("340282366920.938463463374607431") -// ); -// assert_eq!( -// max * dec("0.000000000001"), -// dec("340282366.920938463463374607") -// ); -// assert_eq!( -// max * dec("0.000000000000001"), -// dec("340282.366920938463463374") -// ); -// assert_eq!( -// max * dec("0.000000000000000001"), -// dec("340.282366920938463463") -// ); - -// // works for refs -// let a = PrecDec::percent(20); -// let b = PrecDec::percent(30); -// let expected = PrecDec::percent(6); -// assert_eq!(a * b, expected); -// assert_eq!(&a * b, expected); -// assert_eq!(a * &b, expected); -// assert_eq!(&a * &b, expected); -// } - -// #[test] -// fn decimal_mul_assign_works() { -// let mut a = PrecDec::percent(15); -// a *= PrecDec::percent(60); -// assert_eq!(a, PrecDec::percent(9)); - -// // works for refs -// let mut a = PrecDec::percent(50); -// let b = PrecDec::percent(20); -// a *= &b; -// assert_eq!(a, PrecDec::percent(10)); -// } - -// #[test] -// #[should_panic(expected = "attempt to multiply with overflow")] -// fn decimal_mul_overflow_panics() { -// let _value = PrecDec::MAX * PrecDec::percent(101); -// } - -// #[test] -// fn decimal_checked_mul() { -// let test_data = [ -// (PrecDec::zero(), PrecDec::zero()), -// (PrecDec::zero(), PrecDec::one()), -// (PrecDec::one(), PrecDec::zero()), -// (PrecDec::percent(10), PrecDec::zero()), -// (PrecDec::percent(10), PrecDec::percent(5)), -// (PrecDec::MAX, PrecDec::one()), -// (PrecDec::MAX / Uint256::new(2), PrecDec::percent(200)), -// (PrecDec::permille(6), PrecDec::permille(13)), -// ]; - -// // The regular core::ops::Mul is our source of truth for these tests. -// for (x, y) in test_data.into_iter() { -// assert_eq!(x * y, x.checked_mul(y).unwrap()); -// } -// } - -// #[test] -// fn decimal_checked_mul_overflow() { -// assert_eq!( -// PrecDec::MAX.checked_mul(PrecDec::percent(200)), -// Err(OverflowError::new(OverflowOperation::Mul)) -// ); -// } - -// #[test] -// #[allow(clippy::op_ref)] -// fn decimal_implements_div() { -// let one = PrecDec::one(); -// let two = one + one; -// let half = PrecDec::percent(50); - -// // 1/x and x/1 -// assert_eq!(one / PrecDec::percent(1), PrecDec::percent(10_000)); -// assert_eq!(one / PrecDec::percent(10), PrecDec::percent(1_000)); -// assert_eq!(one / PrecDec::percent(100), PrecDec::percent(100)); -// assert_eq!(one / PrecDec::percent(1000), PrecDec::percent(10)); -// assert_eq!(PrecDec::percent(0) / one, PrecDec::percent(0)); -// assert_eq!(PrecDec::percent(1) / one, PrecDec::percent(1)); -// assert_eq!(PrecDec::percent(10) / one, PrecDec::percent(10)); -// assert_eq!(PrecDec::percent(100) / one, PrecDec::percent(100)); -// assert_eq!(PrecDec::percent(1000) / one, PrecDec::percent(1000)); - -// // double -// assert_eq!(two / PrecDec::percent(1), PrecDec::percent(20_000)); -// assert_eq!(two / PrecDec::percent(10), PrecDec::percent(2_000)); -// assert_eq!(two / PrecDec::percent(100), PrecDec::percent(200)); -// assert_eq!(two / PrecDec::percent(1000), PrecDec::percent(20)); -// assert_eq!(PrecDec::percent(0) / two, PrecDec::percent(0)); -// assert_eq!(PrecDec::percent(1) / two, dec("0.005")); -// assert_eq!(PrecDec::percent(10) / two, PrecDec::percent(5)); -// assert_eq!(PrecDec::percent(100) / two, PrecDec::percent(50)); -// assert_eq!(PrecDec::percent(1000) / two, PrecDec::percent(500)); - -// // half -// assert_eq!(half / PrecDec::percent(1), PrecDec::percent(5_000)); -// assert_eq!(half / PrecDec::percent(10), PrecDec::percent(500)); -// assert_eq!(half / PrecDec::percent(100), PrecDec::percent(50)); -// assert_eq!(half / PrecDec::percent(1000), PrecDec::percent(5)); -// assert_eq!(PrecDec::percent(0) / half, PrecDec::percent(0)); -// assert_eq!(PrecDec::percent(1) / half, PrecDec::percent(2)); -// assert_eq!(PrecDec::percent(10) / half, PrecDec::percent(20)); -// assert_eq!(PrecDec::percent(100) / half, PrecDec::percent(200)); -// assert_eq!(PrecDec::percent(1000) / half, PrecDec::percent(2000)); - -// // Move right -// let a = dec("123127726548762582"); -// assert_eq!(a / dec("1"), dec("123127726548762582")); -// assert_eq!(a / dec("10"), dec("12312772654876258.2")); -// assert_eq!(a / dec("100"), dec("1231277265487625.82")); -// assert_eq!(a / dec("1000"), dec("123127726548762.582")); -// assert_eq!(a / dec("1000000"), dec("123127726548.762582")); -// assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); -// assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); -// assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); -// assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); -// assert_eq!(dec("1") / a, dec("0.000000000000000008")); -// assert_eq!(dec("10") / a, dec("0.000000000000000081")); -// assert_eq!(dec("100") / a, dec("0.000000000000000812")); -// assert_eq!(dec("1000") / a, dec("0.000000000000008121")); -// assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); -// assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); -// assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); -// assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); -// assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); - -// // Move left -// let a = dec("0.123127726548762582"); -// assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); -// assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); -// assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); -// assert_eq!(a / dec("0.001"), dec("123.127726548762582")); -// assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); -// assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); -// assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); -// assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); -// assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); - -// assert_eq!( -// PrecDec::percent(15) / PrecDec::percent(60), -// PrecDec::percent(25) -// ); - -// // works for refs -// let a = PrecDec::percent(100); -// let b = PrecDec::percent(20); -// let expected = PrecDec::percent(500); -// assert_eq!(a / b, expected); -// assert_eq!(&a / b, expected); -// assert_eq!(a / &b, expected); -// assert_eq!(&a / &b, expected); -// } - -// #[test] -// fn decimal_div_assign_works() { -// let mut a = PrecDec::percent(15); -// a /= PrecDec::percent(20); -// assert_eq!(a, PrecDec::percent(75)); - -// // works for refs -// let mut a = PrecDec::percent(50); -// let b = PrecDec::percent(20); -// a /= &b; -// assert_eq!(a, PrecDec::percent(250)); -// } - -// #[test] -// #[should_panic(expected = "Division failed - multiplication overflow")] -// fn decimal_div_overflow_panics() { -// let _value = PrecDec::MAX / PrecDec::percent(10); -// } - -// #[test] -// #[should_panic(expected = "Division failed - denominator must not be zero")] -// fn decimal_div_by_zero_panics() { -// let _value = PrecDec::one() / PrecDec::zero(); -// } - -// #[test] -// fn decimal_uint128_division() { -// // a/b -// let left = PrecDec::percent(150); // 1.5 -// let right = Uint256::new(3); -// assert_eq!(left / right, PrecDec::percent(50)); - -// // 0/a -// let left = PrecDec::zero(); -// let right = Uint256::new(300); -// assert_eq!(left / right, PrecDec::zero()); -// } - -// #[test] -// #[should_panic(expected = "attempt to divide by zero")] -// fn decimal_uint128_divide_by_zero() { -// let left = PrecDec::percent(150); // 1.5 -// let right = Uint256::new(0); -// let _result = left / right; -// } - -// #[test] -// fn decimal_uint128_div_assign() { -// // a/b -// let mut dec = PrecDec::percent(150); // 1.5 -// dec /= Uint256::new(3); -// assert_eq!(dec, PrecDec::percent(50)); - -// // 0/a -// let mut dec = PrecDec::zero(); -// dec /= Uint256::new(300); -// assert_eq!(dec, PrecDec::zero()); -// } - -// #[test] -// #[should_panic(expected = "attempt to divide by zero")] -// fn decimal_uint128_div_assign_by_zero() { -// // a/0 -// let mut dec = PrecDec::percent(50); -// dec /= Uint256::new(0); -// } - -// #[test] -// fn decimal_uint128_sqrt() { -// assert_eq!(PrecDec::percent(900).sqrt(), PrecDec::percent(300)); - -// assert!(PrecDec::percent(316) < PrecDec::percent(1000).sqrt()); -// assert!(PrecDec::percent(1000).sqrt() < PrecDec::percent(317)); -// } - -// /// sqrt(2) is an irrational number, i.e. all 18 decimal places should be used. -// #[test] -// fn decimal_uint128_sqrt_is_precise() { -// assert_eq!( -// PrecDec::from_str("2").unwrap().sqrt(), -// PrecDec::from_str("1.414213562373095048").unwrap() // https://www.wolframalpha.com/input/?i=sqrt%282%29 -// ); -// } - -// #[test] -// fn decimal_uint128_sqrt_does_not_overflow() { -// assert_eq!( -// PrecDec::from_str("400").unwrap().sqrt(), -// PrecDec::from_str("20").unwrap() -// ); -// } - -// #[test] -// fn decimal_uint128_sqrt_intermediate_precision_used() { -// assert_eq!( -// PrecDec::from_str("400001").unwrap().sqrt(), -// // The last two digits (27) are truncated below due to the algorithm -// // we use. Larger numbers will cause less precision. -// // https://www.wolframalpha.com/input/?i=sqrt%28400001%29 -// PrecDec::from_str("632.456322602596803200").unwrap() -// ); -// } - -// #[test] -// fn decimal_checked_pow() { -// for exp in 0..10 { -// assert_eq!(PrecDec::one().checked_pow(exp).unwrap(), PrecDec::one()); -// } - -// // This case is mathematically undefined but we ensure consistency with Rust standard types -// // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 -// assert_eq!(PrecDec::zero().checked_pow(0).unwrap(), PrecDec::one()); - -// for exp in 1..10 { -// assert_eq!(PrecDec::zero().checked_pow(exp).unwrap(), PrecDec::zero()); -// } - -// for num in &[ -// PrecDec::percent(50), -// PrecDec::percent(99), -// PrecDec::percent(200), -// ] { -// assert_eq!(num.checked_pow(0).unwrap(), PrecDec::one()) -// } - -// assert_eq!( -// PrecDec::percent(20).checked_pow(2).unwrap(), -// PrecDec::percent(4) -// ); - -// assert_eq!( -// PrecDec::percent(20).checked_pow(3).unwrap(), -// PrecDec::permille(8) -// ); - -// assert_eq!( -// PrecDec::percent(200).checked_pow(4).unwrap(), -// PrecDec::percent(1600) -// ); - -// assert_eq!( -// PrecDec::percent(200).checked_pow(4).unwrap(), -// PrecDec::percent(1600) -// ); - -// assert_eq!( -// PrecDec::percent(700).checked_pow(5).unwrap(), -// PrecDec::percent(1680700) -// ); - -// assert_eq!( -// PrecDec::percent(700).checked_pow(8).unwrap(), -// PrecDec::percent(576480100) -// ); - -// assert_eq!( -// PrecDec::percent(700).checked_pow(10).unwrap(), -// PrecDec::percent(28247524900) -// ); - -// assert_eq!( -// PrecDec::percent(120).checked_pow(123).unwrap(), -// PrecDec(5486473221892422150877397607u128.into()) -// ); - -// assert_eq!( -// PrecDec::percent(10).checked_pow(2).unwrap(), -// PrecDec(10000000000000000u128.into()) -// ); - -// assert_eq!( -// PrecDec::percent(10).checked_pow(18).unwrap(), -// PrecDec(1u128.into()) -// ); -// } - -// #[test] -// fn decimal_checked_pow_overflow() { -// assert_eq!( -// PrecDec::MAX.checked_pow(2), -// Err(OverflowError::new(OverflowOperation::Pow)) -// ); -// } - -// #[test] -// fn decimal_to_string() { -// // Integers -// assert_eq!(PrecDec::zero().to_string(), "0"); -// assert_eq!(PrecDec::one().to_string(), "1"); -// assert_eq!(PrecDec::percent(500).to_string(), "5"); - -// // Decimals -// assert_eq!(PrecDec::percent(125).to_string(), "1.25"); -// assert_eq!(PrecDec::percent(42638).to_string(), "426.38"); -// assert_eq!(PrecDec::percent(3).to_string(), "0.03"); -// assert_eq!(PrecDec::permille(987).to_string(), "0.987"); - -// assert_eq!( -// PrecDec(Uint256::from(1u128)).to_string(), -// "0.000000000000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(10u128)).to_string(), -// "0.00000000000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(100u128)).to_string(), -// "0.0000000000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(1000u128)).to_string(), -// "0.000000000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(10000u128)).to_string(), -// "0.00000000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(100000u128)).to_string(), -// "0.0000000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(1000000u128)).to_string(), -// "0.000000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(10000000u128)).to_string(), -// "0.00000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(100000000u128)).to_string(), -// "0.0000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(1000000000u128)).to_string(), -// "0.000000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(10000000000u128)).to_string(), -// "0.00000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(100000000000u128)).to_string(), -// "0.0000001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(10000000000000u128)).to_string(), -// "0.00001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(100000000000000u128)).to_string(), -// "0.0001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(1000000000000000u128)).to_string(), -// "0.001" -// ); -// assert_eq!( -// PrecDec(Uint256::from(10000000000000000u128)).to_string(), -// "0.01" -// ); -// assert_eq!( -// PrecDec(Uint256::from(100000000000000000u128)).to_string(), -// "0.1" -// ); -// } - -// #[test] -// fn decimal_iter_sum() { -// let items = vec![ -// PrecDec::zero(), -// PrecDec(Uint256::from(2u128)), -// PrecDec(Uint256::from(2u128)), -// ]; -// assert_eq!(items.iter().sum::(), PrecDec(Uint256::from(4u128))); -// assert_eq!( -// items.into_iter().sum::(), -// PrecDec(Uint256::from(4u128)) -// ); - -// let empty: Vec = vec![]; -// assert_eq!(PrecDec::zero(), empty.iter().sum::()); -// } - -// #[test] -// fn decimal_serialize() { -// assert_eq!(serde_json::to_vec(&PrecDec::zero()).unwrap(), br#""0""#); -// assert_eq!(serde_json::to_vec(&PrecDec::one()).unwrap(), br#""1""#); -// assert_eq!( -// serde_json::to_vec(&PrecDec::percent(8)).unwrap(), -// br#""0.08""# -// ); -// assert_eq!( -// serde_json::to_vec(&PrecDec::percent(87)).unwrap(), -// br#""0.87""# -// ); -// assert_eq!( -// serde_json::to_vec(&PrecDec::percent(876)).unwrap(), -// br#""8.76""# -// ); -// assert_eq!( -// serde_json::to_vec(&PrecDec::percent(8765)).unwrap(), -// br#""87.65""# -// ); -// } - -// #[test] -// fn decimal_deserialize() { -// assert_eq!( -// serde_json::from_slice::(br#""0""#).unwrap(), -// PrecDec::zero() -// ); -// assert_eq!( -// serde_json::from_slice::(br#""1""#).unwrap(), -// PrecDec::one() -// ); -// assert_eq!( -// serde_json::from_slice::(br#""000""#).unwrap(), -// PrecDec::zero() -// ); -// assert_eq!( -// serde_json::from_slice::(br#""001""#).unwrap(), -// PrecDec::one() -// ); - -// assert_eq!( -// serde_json::from_slice::(br#""0.08""#).unwrap(), -// PrecDec::percent(8) -// ); -// assert_eq!( -// serde_json::from_slice::(br#""0.87""#).unwrap(), -// PrecDec::percent(87) -// ); -// assert_eq!( -// serde_json::from_slice::(br#""8.76""#).unwrap(), -// PrecDec::percent(876) -// ); -// assert_eq!( -// serde_json::from_slice::(br#""87.65""#).unwrap(), -// PrecDec::percent(8765) -// ); -// } - -// #[test] -// fn decimal_abs_diff_works() { -// let a = PrecDec::percent(285); -// let b = PrecDec::percent(200); -// let expected = PrecDec::percent(85); -// assert_eq!(a.abs_diff(b), expected); -// assert_eq!(b.abs_diff(a), expected); -// } - -// #[test] -// #[allow(clippy::op_ref)] -// fn decimal_rem_works() { -// // 4.02 % 1.11 = 0.69 -// assert_eq!( -// PrecDec::percent(402) % PrecDec::percent(111), -// PrecDec::percent(69) -// ); - -// // 15.25 % 4 = 3.25 -// assert_eq!( -// PrecDec::percent(1525) % PrecDec::percent(400), -// PrecDec::percent(325) -// ); - -// let a = PrecDec::percent(318); -// let b = PrecDec::percent(317); -// let expected = PrecDec::percent(1); -// assert_eq!(a % b, expected); -// assert_eq!(a % &b, expected); -// assert_eq!(&a % b, expected); -// assert_eq!(&a % &b, expected); -// } - -// #[test] -// fn decimal_rem_assign_works() { -// let mut a = PrecDec::percent(17673); -// a %= PrecDec::percent(2362); -// assert_eq!(a, PrecDec::percent(1139)); // 176.73 % 23.62 = 11.39 - -// let mut a = PrecDec::percent(4262); -// let b = PrecDec::percent(1270); -// a %= &b; -// assert_eq!(a, PrecDec::percent(452)); // 42.62 % 12.7 = 4.52 -// } - -// #[test] -// #[should_panic(expected = "divisor of zero")] -// fn decimal_rem_panics_for_zero() { -// let _ = PrecDec::percent(777) % PrecDec::zero(); -// } - -// #[test] -// fn decimal_checked_methods() { -// // checked add -// assert_eq!( -// PrecDec::percent(402) -// .checked_add(PrecDec::percent(111)) -// .unwrap(), -// PrecDec::percent(513) -// ); -// assert!(matches!( -// PrecDec::MAX.checked_add(PrecDec::percent(1)), -// Err(OverflowError { .. }) -// )); - -// // checked sub -// assert_eq!( -// PrecDec::percent(1111) -// .checked_sub(PrecDec::percent(111)) -// .unwrap(), -// PrecDec::percent(1000) -// ); -// assert!(matches!( -// PrecDec::zero().checked_sub(PrecDec::percent(1)), -// Err(OverflowError { .. }) -// )); - -// // checked div -// assert_eq!( -// PrecDec::percent(30) -// .checked_div(PrecDec::percent(200)) -// .unwrap(), -// PrecDec::percent(15) -// ); -// assert_eq!( -// PrecDec::percent(88) -// .checked_div(PrecDec::percent(20)) -// .unwrap(), -// PrecDec::percent(440) -// ); -// assert!(matches!( -// PrecDec::MAX.checked_div(PrecDec::zero()), -// Err(CheckedFromRatioError::DivideByZero {}) -// )); -// assert!(matches!( -// PrecDec::MAX.checked_div(PrecDec::percent(1)), -// Err(CheckedFromRatioError::Overflow {}) -// )); - -// // checked rem -// assert_eq!( -// PrecDec::percent(402) -// .checked_rem(PrecDec::percent(111)) -// .unwrap(), -// PrecDec::percent(69) -// ); -// assert_eq!( -// PrecDec::percent(1525) -// .checked_rem(PrecDec::percent(400)) -// .unwrap(), -// PrecDec::percent(325) -// ); -// assert!(matches!( -// PrecDec::MAX.checked_rem(PrecDec::zero()), -// Err(DivideByZeroError { .. }) -// )); -// } - -// #[test] -// fn decimal_pow_works() { -// assert_eq!(PrecDec::percent(200).pow(2), PrecDec::percent(400)); -// assert_eq!(PrecDec::percent(200).pow(10), PrecDec::percent(102400)); -// } - -// #[test] -// #[should_panic] -// fn decimal_pow_overflow_panics() { -// _ = PrecDec::MAX.pow(2u32); -// } - -// #[test] -// fn decimal_saturating_works() { -// assert_eq!( -// PrecDec::percent(200).saturating_add(PrecDec::percent(200)), -// PrecDec::percent(400) -// ); -// assert_eq!( -// PrecDec::MAX.saturating_add(PrecDec::percent(200)), -// PrecDec::MAX -// ); -// assert_eq!( -// PrecDec::percent(200).saturating_sub(PrecDec::percent(100)), -// PrecDec::percent(100) -// ); -// assert_eq!( -// PrecDec::zero().saturating_sub(PrecDec::percent(200)), -// PrecDec::zero() -// ); -// assert_eq!( -// PrecDec::percent(200).saturating_mul(PrecDec::percent(50)), -// PrecDec::percent(100) -// ); -// assert_eq!( -// PrecDec::MAX.saturating_mul(PrecDec::percent(200)), -// PrecDec::MAX -// ); -// assert_eq!( -// PrecDec::percent(400).saturating_pow(2u32), -// PrecDec::percent(1600) -// ); -// assert_eq!(PrecDec::MAX.saturating_pow(2u32), PrecDec::MAX); -// } - -// #[test] -// fn decimal_rounding() { -// assert_eq!(PrecDec::one().floor(), PrecDec::one()); -// assert_eq!(PrecDec::percent(150).floor(), PrecDec::one()); -// assert_eq!(PrecDec::percent(199).floor(), PrecDec::one()); -// assert_eq!(PrecDec::percent(200).floor(), PrecDec::percent(200)); -// assert_eq!(PrecDec::percent(99).floor(), PrecDec::zero()); - -// assert_eq!(PrecDec::one().ceil(), PrecDec::one()); -// assert_eq!(PrecDec::percent(150).ceil(), PrecDec::percent(200)); -// assert_eq!(PrecDec::percent(199).ceil(), PrecDec::percent(200)); -// assert_eq!(PrecDec::percent(99).ceil(), PrecDec::one()); -// assert_eq!(PrecDec(Uint256::from(1u128)).ceil(), PrecDec::one()); -// } - -// #[test] -// #[should_panic(expected = "attempt to ceil with overflow")] -// fn decimal_ceil_panics() { -// let _ = PrecDec::MAX.ceil(); -// } - -// #[test] -// fn decimal_checked_ceil() { -// assert_eq!( -// PrecDec::percent(199).checked_ceil(), -// Ok(PrecDec::percent(200)) -// ); -// assert!(matches!( -// PrecDec::MAX.checked_ceil(), -// Err(RoundUpOverflowError { .. }) -// )); -// } - -// #[test] -// fn decimal_to_uint_floor_works() { -// let d = PrecDec::from_str("12.000000000000000001").unwrap(); -// assert_eq!(d.to_uint_floor(), Uint256::new(12)); -// let d = PrecDec::from_str("12.345").unwrap(); -// assert_eq!(d.to_uint_floor(), Uint256::new(12)); -// let d = PrecDec::from_str("12.999").unwrap(); -// assert_eq!(d.to_uint_floor(), Uint256::new(12)); -// let d = PrecDec::from_str("0.98451384").unwrap(); -// assert_eq!(d.to_uint_floor(), Uint256::new(0)); - -// let d = PrecDec::from_str("75.0").unwrap(); -// assert_eq!(d.to_uint_floor(), Uint256::new(75)); -// let d = PrecDec::from_str("0.0").unwrap(); -// assert_eq!(d.to_uint_floor(), Uint256::new(0)); - -// let d = PrecDec::MAX; -// assert_eq!(d.to_uint_floor(), Uint256::new(340282366920938463463)); - -// // Does the same as the old workaround `Uint256::one() * my_decimal`. -// // This block can be deleted as part of https://github.com/CosmWasm/cosmwasm/issues/1485. -// let tests = vec![ -// (PrecDec::from_str("12.345").unwrap(), 12u128), -// (PrecDec::from_str("0.98451384").unwrap(), 0u128), -// (PrecDec::from_str("178.0").unwrap(), 178u128), -// (PrecDec::MIN, 0u128), -// (PrecDec::MAX, u128::MAX / PrecDec::DECIMAL_FRACTIONAL.u128()), -// ]; -// for (my_decimal, expected) in tests.into_iter() { -// assert_eq!(my_decimal.to_uint_floor(), Uint256::new(expected)); -// } -// } - -// #[test] -// fn decimal_to_uint_ceil_works() { -// let d = PrecDec::from_str("12.000000000000000001").unwrap(); -// assert_eq!(d.to_uint_ceil(), Uint256::new(13)); -// let d = PrecDec::from_str("12.345").unwrap(); -// assert_eq!(d.to_uint_ceil(), Uint256::new(13)); -// let d = PrecDec::from_str("12.999").unwrap(); -// assert_eq!(d.to_uint_ceil(), Uint256::new(13)); - -// let d = PrecDec::from_str("75.0").unwrap(); -// assert_eq!(d.to_uint_ceil(), Uint256::new(75)); -// let d = PrecDec::from_str("0.0").unwrap(); -// assert_eq!(d.to_uint_ceil(), Uint256::new(0)); - -// let d = PrecDec::MAX; -// assert_eq!(d.to_uint_ceil(), Uint256::new(340282366920938463464)); -// } - -// #[test] -// fn decimal_partial_eq() { -// let test_cases = [ -// ("1", "1", true), -// ("0.5", "0.5", true), -// ("0.5", "0.51", false), -// ("0", "0.00000", true), -// ] -// .into_iter() -// .map(|(lhs, rhs, expected)| (dec(lhs), dec(rhs), expected)); - -// #[allow(clippy::op_ref)] -// for (lhs, rhs, expected) in test_cases { -// assert_eq!(lhs == rhs, expected); -// assert_eq!(&lhs == rhs, expected); -// assert_eq!(lhs == &rhs, expected); -// assert_eq!(&lhs == &rhs, expected); -// } -// } - -// #[test] -// fn decimal_implements_debug() { -// let decimal = PrecDec::from_str("123.45").unwrap(); -// assert_eq!(format!("{decimal:?}"), "Decimal(123.45)"); - -// let test_cases = ["5", "5.01", "42", "0", "2"]; -// for s in test_cases { -// let decimal = PrecDec::from_str(s).unwrap(); -// let expected = format!("Decimal({s})"); -// assert_eq!(format!("{decimal:?}"), expected); -// } -// } -// } +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::Uint128; + + fn dec(input: &str) -> PrecDec { + PrecDec::from_str(input).unwrap() + } + + #[test] + fn precdec_new() { + let expected = Uint256::from(300u128); + assert_eq!(PrecDec::new(expected).0, expected); + } + + #[test] + fn precdec_raw() { + let value = 300u128; + let expected = Uint256::from(value); + assert_eq!(PrecDec::raw(value).0, expected); + } + + #[test] + fn precdec_one() { + let value = PrecDec::one(); + assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL); + } + + #[test] + fn precdec_zero() { + let value = PrecDec::zero(); + assert!(value.0.is_zero()); + } + + #[test] + fn precdec_percent() { + let value = PrecDec::percent(50); + assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(2u8)); + } + + #[test] + fn precdec_permille() { + let value = PrecDec::permille(125); + assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(8u8)); + } + + #[test] + fn precdec_bps() { + let value = PrecDec::bps(125); + assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(80u8)); + } + + #[test] + fn precdec_from_decimal256_works() { + let val = Decimal256::new(Uint256::from(Uint128::MAX)); + assert_eq!(PrecDec::try_from(val), Ok(PrecDec::from_str("99").unwrap())); + + assert_eq!(PrecDec::try_from(Decimal256::zero()), Ok(PrecDec::zero())); + assert_eq!(PrecDec::try_from(Decimal256::one()), Ok(PrecDec::one())); + assert_eq!( + PrecDec::try_from(Decimal256::percent(50)), + Ok(PrecDec::percent(50)) + ); + } + + #[test] + fn precdec_try_from_integer() { + let int = Uint256::from_u128(999888777666555); + let decimal = PrecDec::try_from(int).unwrap(); + assert_eq!(int.to_string(), decimal.to_string()); + } + + #[test] + fn precdec_try_from_signed_works() { + assert_eq!( + PrecDec::try_from(SignedDecimal::MAX).unwrap(), + PrecDec::raw(SignedDecimal::MAX.atomics().i128() as u128) + ); + assert_eq!( + PrecDec::try_from(SignedDecimal::zero()).unwrap(), + PrecDec::zero() + ); + assert_eq!( + PrecDec::try_from(SignedDecimal::one()).unwrap(), + PrecDec::one() + ); + assert_eq!( + PrecDec::try_from(SignedDecimal::percent(50)).unwrap(), + PrecDec::percent(50) + ); + assert_eq!( + PrecDec::try_from(SignedDecimal::negative_one()), + Err(PrecDecRangeExceeded) + ); + assert_eq!( + PrecDec::try_from(SignedDecimal::MIN), + Err(PrecDecRangeExceeded) + ); + } + + #[test] + fn precdec_from_atomics_works() { + let one = PrecDec::one(); + let two = one + one; + + assert_eq!(PrecDec::from_atomics(1u128, 0).unwrap(), one); + assert_eq!(PrecDec::from_atomics(10u128, 1).unwrap(), one); + assert_eq!(PrecDec::from_atomics(100u128, 2).unwrap(), one); + assert_eq!(PrecDec::from_atomics(1000u128, 3).unwrap(), one); + assert_eq!( + PrecDec::from_atomics(1000000000000000000u128, 18).unwrap(), + one + ); + assert_eq!( + PrecDec::from_atomics(10000000000000000000u128, 19).unwrap(), + one + ); + assert_eq!( + PrecDec::from_atomics(100000000000000000000u128, 20).unwrap(), + one + ); + + assert_eq!(PrecDec::from_atomics(2u128, 0).unwrap(), two); + assert_eq!(PrecDec::from_atomics(20u128, 1).unwrap(), two); + assert_eq!(PrecDec::from_atomics(200u128, 2).unwrap(), two); + assert_eq!(PrecDec::from_atomics(2000u128, 3).unwrap(), two); + assert_eq!( + PrecDec::from_atomics(2000000000000000000u128, 18).unwrap(), + two + ); + assert_eq!( + PrecDec::from_atomics(20000000000000000000u128, 19).unwrap(), + two + ); + assert_eq!( + PrecDec::from_atomics(200000000000000000000u128, 20).unwrap(), + two + ); + + // Cuts decimal digits (29 provided but only 27 can be stored) + assert_eq!( + PrecDec::from_atomics(4321u128, 29).unwrap(), + PrecDec::from_str("0.000000000000000000000000043").unwrap() + ); + assert_eq!( + PrecDec::from_atomics(6789u128, 29).unwrap(), + PrecDec::from_str("0.000000000000000000000000067").unwrap() + ); + assert_eq!( + Decimal256::from_atomics(u128::MAX, 38).unwrap(), + Decimal256::from_str("3.402823669209384634").unwrap() + ); + assert_eq!( + Decimal256::from_atomics(u128::MAX, 39).unwrap(), + Decimal256::from_str("0.340282366920938463").unwrap() + ); + assert_eq!( + Decimal256::from_atomics(u128::MAX, 45).unwrap(), + Decimal256::from_str("0.000000340282366920").unwrap() + ); + assert_eq!( + Decimal256::from_atomics(u128::MAX, 51).unwrap(), + Decimal256::from_str("0.000000000000340282").unwrap() + ); + assert_eq!( + Decimal256::from_atomics(u128::MAX, 56).unwrap(), + Decimal256::from_str("0.000000000000000003").unwrap() + ); + assert_eq!( + Decimal256::from_atomics(u128::MAX, 57).unwrap(), + Decimal256::from_str("0.000000000000000000").unwrap() + ); + assert_eq!( + Decimal256::from_atomics(u128::MAX, u32::MAX).unwrap(), + Decimal256::from_str("0.000000000000000000").unwrap() + ); + + // Can be used with max value + let max = PrecDec::MAX; + assert_eq!( + PrecDec::from_atomics(max.atomics(), max.decimal_places()).unwrap(), + max + ); + + // Overflow is only possible with digits < 26 + let result = PrecDec::from_atomics(Uint256::MAX, 26); + assert_eq!(result.unwrap_err(), PrecDecRangeExceeded); + } + + #[test] + fn precdec_from_ratio_works() { + // 1.0 + assert_eq!(PrecDec::from_ratio(1u128, 1u128), PrecDec::one()); + assert_eq!(PrecDec::from_ratio(53u128, 53u128), PrecDec::one()); + assert_eq!(PrecDec::from_ratio(125u128, 125u128), PrecDec::one()); + + // 1.5 + assert_eq!(PrecDec::from_ratio(3u128, 2u128), PrecDec::percent(150)); + assert_eq!(PrecDec::from_ratio(150u128, 100u128), PrecDec::percent(150)); + assert_eq!(PrecDec::from_ratio(333u128, 222u128), PrecDec::percent(150)); + + // 0.125 + assert_eq!(PrecDec::from_ratio(1u64, 8u64), PrecDec::permille(125)); + assert_eq!(PrecDec::from_ratio(125u64, 1000u64), PrecDec::permille(125)); + + // 1/3 (result floored) + assert_eq!( + PrecDec::from_ratio(1u64, 3u64), + PrecDec(Uint256::from(333_333_333_333_333_333_333_333_333u128)) + ); + + // 2/3 (result floored) + assert_eq!( + PrecDec::from_ratio(2u64, 3u64), + PrecDec(Uint256::from(666_666_666_666_666_666_666_666_666u128)) + ); + + // large inputs + assert_eq!(PrecDec::from_ratio(0u128, u128::MAX), PrecDec::zero()); + assert_eq!(PrecDec::from_ratio(u128::MAX, u128::MAX), PrecDec::one()); + // 340282366920938463463 is the largest integer <= PrecDec::MAX + assert_eq!( + PrecDec::from_ratio(340282366920938463463u128, 1u128), + PrecDec::from_str("340282366920938463463").unwrap() + ); + } + + #[test] + #[should_panic(expected = "Denominator must not be zero")] + fn precdec_from_ratio_panics_for_zero_denominator() { + PrecDec::from_ratio(1u128, 0u128); + } + + #[test] + #[should_panic(expected = "Multiplication overflow")] + fn precdec_from_ratio_panics_for_mul_overflow() { + PrecDec::from_ratio(Uint256::MAX, 1u128); + } + + #[test] + fn precdec_checked_from_ratio_does_not_panic() { + assert_eq!( + PrecDec::checked_from_ratio(1u128, 0u128), + Err(CheckedFromRatioError::DivideByZero) + ); + + assert_eq!( + PrecDec::checked_from_ratio(Uint256::MAX, 1u128), + Err(CheckedFromRatioError::Overflow) + ); + } + + #[test] + fn precdec_implements_fraction() { + let fraction = PrecDec::from_str("1234.567").unwrap(); + assert_eq!( + fraction.numerator(), + Uint256::from(1_234_567_000_000_000_000_000_000_000_000u128) + ); + assert_eq!( + fraction.denominator(), + Uint256::from(1_000_000_000_000_000_000_000_000_000u128) + ); + } + + #[test] + fn precdec_from_str_works() { + // Integers + assert_eq!(PrecDec::from_str("0").unwrap(), PrecDec::percent(0)); + assert_eq!(PrecDec::from_str("1").unwrap(), PrecDec::percent(100)); + assert_eq!(PrecDec::from_str("5").unwrap(), PrecDec::percent(500)); + assert_eq!(PrecDec::from_str("42").unwrap(), PrecDec::percent(4200)); + assert_eq!(PrecDec::from_str("000").unwrap(), PrecDec::percent(0)); + assert_eq!(PrecDec::from_str("001").unwrap(), PrecDec::percent(100)); + assert_eq!(PrecDec::from_str("005").unwrap(), PrecDec::percent(500)); + assert_eq!(PrecDec::from_str("0042").unwrap(), PrecDec::percent(4200)); + + // Decimals + assert_eq!(PrecDec::from_str("1.0").unwrap(), PrecDec::percent(100)); + assert_eq!(PrecDec::from_str("1.5").unwrap(), PrecDec::percent(150)); + assert_eq!(PrecDec::from_str("0.5").unwrap(), PrecDec::percent(50)); + assert_eq!(PrecDec::from_str("0.123").unwrap(), PrecDec::permille(123)); + + assert_eq!(PrecDec::from_str("40.00").unwrap(), PrecDec::percent(4000)); + assert_eq!(PrecDec::from_str("04.00").unwrap(), PrecDec::percent(400)); + assert_eq!(PrecDec::from_str("00.40").unwrap(), PrecDec::percent(40)); + assert_eq!(PrecDec::from_str("00.04").unwrap(), PrecDec::percent(4)); + + // Can handle DECIMAL_PLACES fractional digits + assert_eq!( + PrecDec::from_str("7.123456789012345678912345678").unwrap(), + PrecDec(Uint256::from(7123456789012345678912345678u128)) + ); + assert_eq!( + PrecDec::from_str("7.999999999999999999999999999").unwrap(), + PrecDec(Uint256::from(7999999999999999999999999999u128)) + ); + + // Works for documented max value + assert_eq!( + + PrecDec::from_str("115792089237316195423570985008687907853269984665640.564039457584007913129639935").unwrap(), + PrecDec::MAX + ); + } + + #[test] + fn precdec_from_str_errors_for_broken_whole_part() { + match PrecDec::from_str("").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), + e => panic!("Unexpected error: {e:?}"), + } + + match PrecDec::from_str(" ").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), + e => panic!("Unexpected error: {e:?}"), + } + + match PrecDec::from_str("-1").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing whole"), + e => panic!("Unexpected error: {e:?}"), + } + } + + #[test] + fn precdec_from_str_errors_for_broken_fractional_part() { + match PrecDec::from_str("1.").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), + e => panic!("Unexpected error: {e:?}"), + } + + match PrecDec::from_str("1. ").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), + e => panic!("Unexpected error: {e:?}"), + } + + match PrecDec::from_str("1.e").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), + e => panic!("Unexpected error: {e:?}"), + } + + match PrecDec::from_str("1.2e3").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Error parsing fractional"), + e => panic!("Unexpected error: {e:?}"), + } + } + + #[test] + fn precdec_from_str_errors_for_more_than_27_fractional_digits() { + match PrecDec::from_str("7.1234567890123456789123456789").unwrap_err() { + StdError::GenericErr { msg, .. } => { + assert_eq!(msg, "Cannot parse more than 27 fractional digits",) + } + e => panic!("Unexpected error: {e:?}"), + } + + // No special rules for trailing zeros. This could be changed but adds gas cost for the happy path. + match PrecDec::from_str("7.1230000000000000000000000000").unwrap_err() { + StdError::GenericErr { msg, .. } => { + assert_eq!(msg, "Cannot parse more than 27 fractional digits") + } + e => panic!("Unexpected error: {e:?}"), + } + } + + #[test] + fn precdec_from_str_errors_for_invalid_number_of_dots() { + match PrecDec::from_str("1.2.3").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Unexpected number of dots"), + e => panic!("Unexpected error: {e:?}"), + } + + match PrecDec::from_str("1.2.3.4").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Unexpected number of dots"), + e => panic!("Unexpected error: {e:?}"), + } + } + + #[test] + fn precdec_from_str_errors_for_more_than_max_value() { + // Integer + match PrecDec::from_str("34028236692099999999999999999999999999999999999999999938463464").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), + e => panic!("Unexpected error: {e:?}"), + } + + // Decimal + match PrecDec::from_str("115792089237316195423570985008687907853269984665640564039458.0").unwrap_err() { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), + e => panic!("Unexpected error: {e:?}"), + } + match Decimal256::from_str( + "115792089237316195423570985008687907853269984665640564039457.584007913129639936", + ) + .unwrap_err() + { + StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), + e => panic!("Unexpected error: {e:?}"), + } + + } + + #[test] + fn precdec_atomics_works() { + let zero = PrecDec::zero(); + let one = PrecDec::one(); + let half = PrecDec::percent(50); + let two = PrecDec::percent(200); + let max = PrecDec::MAX; + + assert_eq!(zero.atomics(), Uint256::from_u128(0)); + assert_eq!(one.atomics(), Uint256::from_u128(1000000000000000000000000000)); + assert_eq!(half.atomics(), Uint256::from_u128(500000000000000000000000000)); + assert_eq!(two.atomics(), Uint256::from_u128(2000000000000000000000000000)); + assert_eq!(max.atomics(), Uint256::MAX); + } + + #[test] + fn precdec_decimal_places_works() { + let zero = PrecDec::zero(); + let one = PrecDec::one(); + let half = PrecDec::percent(50); + let two = PrecDec::percent(200); + let max = PrecDec::MAX; + + assert_eq!(zero.decimal_places(), 27); + assert_eq!(one.decimal_places(), 27); + assert_eq!(half.decimal_places(), 27); + assert_eq!(two.decimal_places(), 27); + assert_eq!(max.decimal_places(), 27); + } + + #[test] + fn precdec_is_zero_works() { + assert!(PrecDec::zero().is_zero()); + assert!(PrecDec::percent(0).is_zero()); + assert!(PrecDec::permille(0).is_zero()); + + assert!(!PrecDec::one().is_zero()); + assert!(!PrecDec::percent(123).is_zero()); + assert!(!PrecDec::permille(1234).is_zero()); + } + + #[test] + fn precdec_inv_works() { + // d = 0 + assert_eq!(PrecDec::zero().inv(), None); + + // d == 1 + assert_eq!(PrecDec::one().inv(), Some(PrecDec::one())); + + // d > 1 exact + assert_eq!( + PrecDec::from_str("2").unwrap().inv(), + Some(PrecDec::from_str("0.5").unwrap()) + ); + assert_eq!( + PrecDec::from_str("20").unwrap().inv(), + Some(PrecDec::from_str("0.05").unwrap()) + ); + assert_eq!( + PrecDec::from_str("200").unwrap().inv(), + Some(PrecDec::from_str("0.005").unwrap()) + ); + assert_eq!( + PrecDec::from_str("2000").unwrap().inv(), + Some(PrecDec::from_str("0.0005").unwrap()) + ); + + // d > 1 rounded + assert_eq!( + PrecDec::from_str("3").unwrap().inv(), + Some(PrecDec::from_str("0.333333333333333333333333333").unwrap()) + ); + assert_eq!( + PrecDec::from_str("6").unwrap().inv(), + Some(PrecDec::from_str("0.166666666666666666666666666").unwrap()) + ); + + // d < 1 exact + assert_eq!( + PrecDec::from_str("0.5").unwrap().inv(), + Some(PrecDec::from_str("2").unwrap()) + ); + assert_eq!( + PrecDec::from_str("0.05").unwrap().inv(), + Some(PrecDec::from_str("20").unwrap()) + ); + assert_eq!( + PrecDec::from_str("0.005").unwrap().inv(), + Some(PrecDec::from_str("200").unwrap()) + ); + assert_eq!( + PrecDec::from_str("0.0005").unwrap().inv(), + Some(PrecDec::from_str("2000").unwrap()) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn precdec_add_works() { + let value = PrecDec::one() + PrecDec::percent(50); // 1.5 + assert_eq!( + value.0, + PrecDec::DECIMAL_FRACTIONAL * Uint256::from(3u8) / Uint256::from(2u8) + ); + + assert_eq!( + PrecDec::percent(5) + PrecDec::percent(4), + PrecDec::percent(9) + ); + assert_eq!(PrecDec::percent(5) + PrecDec::zero(), PrecDec::percent(5)); + assert_eq!(PrecDec::zero() + PrecDec::zero(), PrecDec::zero()); + + // works for refs + let a = PrecDec::percent(15); + let b = PrecDec::percent(25); + let expected = PrecDec::percent(40); + assert_eq!(a + b, expected); + assert_eq!(&a + b, expected); + assert_eq!(a + &b, expected); + assert_eq!(&a + &b, expected); + } + + #[test] + #[should_panic(expected = "attempt to add with overflow")] + fn precdec_add_overflow_panics() { + let _value = PrecDec::MAX + PrecDec::percent(50); + } + + #[test] + fn precdec_add_assign_works() { + let mut a = PrecDec::percent(30); + a += PrecDec::percent(20); + assert_eq!(a, PrecDec::percent(50)); + + // works for refs + let mut a = PrecDec::percent(15); + let b = PrecDec::percent(3); + let expected = PrecDec::percent(18); + a += &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn precdec_sub_works() { + let value = PrecDec::one() - PrecDec::percent(50); // 0.5 + assert_eq!(value.0, PrecDec::DECIMAL_FRACTIONAL / Uint256::from(2u8)); + + assert_eq!( + PrecDec::percent(9) - PrecDec::percent(4), + PrecDec::percent(5) + ); + assert_eq!(PrecDec::percent(16) - PrecDec::zero(), PrecDec::percent(16)); + assert_eq!(PrecDec::percent(16) - PrecDec::percent(16), PrecDec::zero()); + assert_eq!(PrecDec::zero() - PrecDec::zero(), PrecDec::zero()); + + // works for refs + let a = PrecDec::percent(13); + let b = PrecDec::percent(6); + let expected = PrecDec::percent(7); + assert_eq!(a - b, expected); + assert_eq!(&a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn precdec_sub_overflow_panics() { + let _value = PrecDec::zero() - PrecDec::percent(50); + } + + #[test] + fn precdec_sub_assign_works() { + let mut a = PrecDec::percent(20); + a -= PrecDec::percent(2); + assert_eq!(a, PrecDec::percent(18)); + + // works for refs + let mut a = PrecDec::percent(33); + let b = PrecDec::percent(13); + let expected = PrecDec::percent(20); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn precdec_implements_mul() { + let one = PrecDec::one(); + let two = one + one; + let half = PrecDec::percent(50); + + // 1*x and x*1 + assert_eq!(one * PrecDec::percent(0), PrecDec::percent(0)); + assert_eq!(one * PrecDec::percent(1), PrecDec::percent(1)); + assert_eq!(one * PrecDec::percent(10), PrecDec::percent(10)); + assert_eq!(one * PrecDec::percent(100), PrecDec::percent(100)); + assert_eq!(one * PrecDec::percent(1000), PrecDec::percent(1000)); + assert_eq!(one * PrecDec::MAX, PrecDec::MAX); + assert_eq!(PrecDec::percent(0) * one, PrecDec::percent(0)); + assert_eq!(PrecDec::percent(1) * one, PrecDec::percent(1)); + assert_eq!(PrecDec::percent(10) * one, PrecDec::percent(10)); + assert_eq!(PrecDec::percent(100) * one, PrecDec::percent(100)); + assert_eq!(PrecDec::percent(1000) * one, PrecDec::percent(1000)); + assert_eq!(PrecDec::MAX * one, PrecDec::MAX); + + // double + assert_eq!(two * PrecDec::percent(0), PrecDec::percent(0)); + assert_eq!(two * PrecDec::percent(1), PrecDec::percent(2)); + assert_eq!(two * PrecDec::percent(10), PrecDec::percent(20)); + assert_eq!(two * PrecDec::percent(100), PrecDec::percent(200)); + assert_eq!(two * PrecDec::percent(1000), PrecDec::percent(2000)); + assert_eq!(PrecDec::percent(0) * two, PrecDec::percent(0)); + assert_eq!(PrecDec::percent(1) * two, PrecDec::percent(2)); + assert_eq!(PrecDec::percent(10) * two, PrecDec::percent(20)); + assert_eq!(PrecDec::percent(100) * two, PrecDec::percent(200)); + assert_eq!(PrecDec::percent(1000) * two, PrecDec::percent(2000)); + + // half + assert_eq!(half * PrecDec::percent(0), PrecDec::percent(0)); + assert_eq!(half * PrecDec::percent(1), PrecDec::permille(5)); + assert_eq!(half * PrecDec::percent(10), PrecDec::percent(5)); + assert_eq!(half * PrecDec::percent(100), PrecDec::percent(50)); + assert_eq!(half * PrecDec::percent(1000), PrecDec::percent(500)); + assert_eq!(PrecDec::percent(0) * half, PrecDec::percent(0)); + assert_eq!(PrecDec::percent(1) * half, PrecDec::permille(5)); + assert_eq!(PrecDec::percent(10) * half, PrecDec::percent(5)); + assert_eq!(PrecDec::percent(100) * half, PrecDec::percent(50)); + assert_eq!(PrecDec::percent(1000) * half, PrecDec::percent(500)); + + // Move left + let a = dec("123.127726548762582"); + assert_eq!(a * dec("1"), dec("123.127726548762582")); + assert_eq!(a * dec("10"), dec("1231.27726548762582")); + assert_eq!(a * dec("100"), dec("12312.7726548762582")); + assert_eq!(a * dec("1000"), dec("123127.726548762582")); + assert_eq!(a * dec("1000000"), dec("123127726.548762582")); + assert_eq!(a * dec("1000000000"), dec("123127726548.762582")); + assert_eq!(a * dec("1000000000000"), dec("123127726548762.582")); + assert_eq!(a * dec("1000000000000000"), dec("123127726548762582")); + assert_eq!(a * dec("1000000000000000000"), dec("123127726548762582000")); + assert_eq!(dec("1") * a, dec("123.127726548762582")); + assert_eq!(dec("10") * a, dec("1231.27726548762582")); + assert_eq!(dec("100") * a, dec("12312.7726548762582")); + assert_eq!(dec("1000") * a, dec("123127.726548762582")); + assert_eq!(dec("1000000") * a, dec("123127726.548762582")); + assert_eq!(dec("1000000000") * a, dec("123127726548.762582")); + assert_eq!(dec("1000000000000") * a, dec("123127726548762.582")); + assert_eq!(dec("1000000000000000") * a, dec("123127726548762582")); + assert_eq!(dec("1000000000000000000") * a, dec("123127726548762582000")); + + // Move right + let max = PrecDec::MAX; + assert_eq!( + max * dec("1.0"), + dec("115792089237316195423570985008687907853269984665640.564039457584007913129639935") + ); + assert_eq!( + max * dec("0.1"), + dec("11579208923731619542357098500868790785326998466564.056403945758400791312963993") + ); + assert_eq!( + max * dec("0.01"), + dec("1157920892373161954235709850086879078532699846656.405640394575840079131296399") + ); + assert_eq!( + max * dec("0.001"), + dec("115792089237316195423570985008687907853269984665.640564039457584007913129639") + ); + assert_eq!( + max * dec("0.000001"), + dec("115792089237316195423570985008687907853269984.665640564039457584007913129") + ); + assert_eq!( + max * dec("0.000000001"), + dec("115792089237316195423570985008687907853269.984665640564039457584007913") + ); + assert_eq!( + max * dec("0.000000000001"), + dec("115792089237316195423570985008687907853.269984665640564039457584007") + ); + assert_eq!( + max * dec("0.000000000000001"), + dec("115792089237316195423570985008687907.853269984665640564039457584") + ); + assert_eq!( + max * dec("0.000000000000000001"), + dec("115792089237316195423570985008687.907853269984665640564039457") + ); + + // works for refs + let a = PrecDec::percent(20); + let b = PrecDec::percent(30); + let expected = PrecDec::percent(6); + assert_eq!(a * b, expected); + assert_eq!(&a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn precdec_mul_assign_works() { + let mut a = PrecDec::percent(15); + a *= PrecDec::percent(60); + assert_eq!(a, PrecDec::percent(9)); + + // works for refs + let mut a = PrecDec::percent(50); + let b = PrecDec::percent(20); + a *= &b; + assert_eq!(a, PrecDec::percent(10)); + } + + #[test] + #[should_panic(expected = "attempt to multiply with overflow")] + fn precdec_mul_overflow_panics() { + let _value = PrecDec::MAX * PrecDec::percent(101); + } + + #[test] + fn precdec_checked_mul() { + let test_data = [ + (PrecDec::zero(), PrecDec::zero()), + (PrecDec::zero(), PrecDec::one()), + (PrecDec::one(), PrecDec::zero()), + (PrecDec::percent(10), PrecDec::zero()), + (PrecDec::percent(10), PrecDec::percent(5)), + (PrecDec::MAX, PrecDec::one()), + (PrecDec::MAX / Uint256::from_u128(2), PrecDec::percent(200)), + (PrecDec::permille(6), PrecDec::permille(13)), + ]; + + // The regular core::ops::Mul is our source of truth for these tests. + for (x, y) in test_data.into_iter() { + assert_eq!(x * y, x.checked_mul(y).unwrap()); + } + } + + #[test] + fn precdec_checked_mul_overflow() { + assert_eq!( + PrecDec::MAX.checked_mul(PrecDec::percent(200)), + Err(OverflowError::new(OverflowOperation::Mul)) + ); + } + + #[test] + #[allow(clippy::op_ref)] + fn precdec_implements_div() { + let one = PrecDec::one(); + let two = one + one; + let half = PrecDec::percent(50); + + // 1/x and x/1 + assert_eq!(one / PrecDec::percent(1), PrecDec::percent(10_000)); + assert_eq!(one / PrecDec::percent(10), PrecDec::percent(1_000)); + assert_eq!(one / PrecDec::percent(100), PrecDec::percent(100)); + assert_eq!(one / PrecDec::percent(1000), PrecDec::percent(10)); + assert_eq!(PrecDec::percent(0) / one, PrecDec::percent(0)); + assert_eq!(PrecDec::percent(1) / one, PrecDec::percent(1)); + assert_eq!(PrecDec::percent(10) / one, PrecDec::percent(10)); + assert_eq!(PrecDec::percent(100) / one, PrecDec::percent(100)); + assert_eq!(PrecDec::percent(1000) / one, PrecDec::percent(1000)); + + // double + assert_eq!(two / PrecDec::percent(1), PrecDec::percent(20_000)); + assert_eq!(two / PrecDec::percent(10), PrecDec::percent(2_000)); + assert_eq!(two / PrecDec::percent(100), PrecDec::percent(200)); + assert_eq!(two / PrecDec::percent(1000), PrecDec::percent(20)); + assert_eq!(PrecDec::percent(0) / two, PrecDec::percent(0)); + assert_eq!(PrecDec::percent(1) / two, dec("0.005")); + assert_eq!(PrecDec::percent(10) / two, PrecDec::percent(5)); + assert_eq!(PrecDec::percent(100) / two, PrecDec::percent(50)); + assert_eq!(PrecDec::percent(1000) / two, PrecDec::percent(500)); + + // half + assert_eq!(half / PrecDec::percent(1), PrecDec::percent(5_000)); + assert_eq!(half / PrecDec::percent(10), PrecDec::percent(500)); + assert_eq!(half / PrecDec::percent(100), PrecDec::percent(50)); + assert_eq!(half / PrecDec::percent(1000), PrecDec::percent(5)); + assert_eq!(PrecDec::percent(0) / half, PrecDec::percent(0)); + assert_eq!(PrecDec::percent(1) / half, PrecDec::percent(2)); + assert_eq!(PrecDec::percent(10) / half, PrecDec::percent(20)); + assert_eq!(PrecDec::percent(100) / half, PrecDec::percent(200)); + assert_eq!(PrecDec::percent(1000) / half, PrecDec::percent(2000)); + + // Move right + let a = dec("123127726548762582"); + assert_eq!(a / dec("1"), dec("123127726548762582")); + assert_eq!(a / dec("10"), dec("12312772654876258.2")); + assert_eq!(a / dec("100"), dec("1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); + assert_eq!(dec("1") / a, dec("0.00000000000000000812164756")); + assert_eq!(dec("10") / a, dec("0.000000000000000081216475608")); + assert_eq!(dec("100") / a, dec("0.000000000000000812164756086")); + assert_eq!(dec("1000") / a, dec("0.000000000000008121647560868")); + assert_eq!(dec("1000000") / a, dec("0.000000000008121647560868164")); + assert_eq!(dec("1000000000") / a, dec("0.000000008121647560868164773")); + assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868164773903")); + assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164773903026")); + assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773903026009")); + + // Move left + let a = dec("0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + + assert_eq!( + PrecDec::percent(15) / PrecDec::percent(60), + PrecDec::percent(25) + ); + + // works for refs + let a = PrecDec::percent(100); + let b = PrecDec::percent(20); + let expected = PrecDec::percent(500); + assert_eq!(a / b, expected); + assert_eq!(&a / b, expected); + assert_eq!(a / &b, expected); + assert_eq!(&a / &b, expected); + } + + #[test] + fn precdec_div_assign_works() { + let mut a = PrecDec::percent(15); + a /= PrecDec::percent(20); + assert_eq!(a, PrecDec::percent(75)); + + // works for refs + let mut a = PrecDec::percent(50); + let b = PrecDec::percent(20); + a /= &b; + assert_eq!(a, PrecDec::percent(250)); + } + + #[test] + #[should_panic(expected = "Division failed - multiplication overflow")] + fn precdec_div_overflow_panics() { + let _value = PrecDec::MAX / PrecDec::percent(10); + } + + #[test] + #[should_panic(expected = "Division failed - denominator must not be zero")] + fn precdec_div_by_zero_panics() { + let _value = PrecDec::one() / PrecDec::zero(); + } + + #[test] + fn precdec_uint128_division() { + // a/b + let left = PrecDec::percent(150); // 1.5 + let right = Uint256::from_u128(3); + assert_eq!(left / right, PrecDec::percent(50)); + + // 0/a + let left = PrecDec::zero(); + let right = Uint256::from_u128(300); + assert_eq!(left / right, PrecDec::zero()); + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn precdec_uint128_divide_by_zero() { + let left = PrecDec::percent(150); // 1.5 + let right = Uint256::from_u128(0); + let _result = left / right; + } + + #[test] + fn precdec_uint128_div_assign() { + // a/b + let mut dec = PrecDec::percent(150); // 1.5 + dec /= Uint256::from_u128(3); + assert_eq!(dec, PrecDec::percent(50)); + + // 0/a + let mut dec = PrecDec::zero(); + dec /= Uint256::from_u128(300); + assert_eq!(dec, PrecDec::zero()); + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn precdec_uint128_div_assign_by_zero() { + // a/0 + let mut dec = PrecDec::percent(50); + dec /= Uint256::from_u128(0); + } + + #[test] + fn precdec_uint128_sqrt() { + assert_eq!(PrecDec::percent(900).sqrt(), PrecDec::percent(300)); + + assert!(PrecDec::percent(316) < PrecDec::percent(1000).sqrt()); + assert!(PrecDec::percent(1000).sqrt() < PrecDec::percent(317)); + } + + /// sqrt(2) is an irrational number, i.e. all 27 decimal places should be used. + #[test] + fn precdec_uint128_sqrt_is_precise() { + assert_eq!( + PrecDec::from_str("2").unwrap().sqrt(), + PrecDec::from_str("1.414213562373095048").unwrap() // https://www.wolframalpha.com/input/?i=sqrt%282%29 + ); + } + + #[test] + fn precdec_uint128_sqrt_does_not_overflow() { + assert_eq!( + PrecDec::from_str("400").unwrap().sqrt(), + PrecDec::from_str("20").unwrap() + ); + } + + #[test] + fn precdec_uint128_sqrt_intermediate_precision_used() { + assert_eq!( + PrecDec::from_str("400001").unwrap().sqrt(), + // The last two digits (27) are truncated below due to the algorithm + // we use. Larger numbers will cause less precision. + // https://www.wolframalpha.com/input/?i=sqrt%28400001%29 + PrecDec::from_str("632.456322602596803200").unwrap() + ); + } + + #[test] + fn precdec_checked_pow() { + for exp in 0..10 { + assert_eq!(PrecDec::one().checked_pow(exp).unwrap(), PrecDec::one()); + } + + // This case is mathematically undefined but we ensure consistency with Rust standard types + // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=20df6716048e77087acd40194b233494 + assert_eq!(PrecDec::zero().checked_pow(0).unwrap(), PrecDec::one()); + + for exp in 1..10 { + assert_eq!(PrecDec::zero().checked_pow(exp).unwrap(), PrecDec::zero()); + } + + for num in &[ + PrecDec::percent(50), + PrecDec::percent(99), + PrecDec::percent(200), + ] { + assert_eq!(num.checked_pow(0).unwrap(), PrecDec::one()) + } + + assert_eq!( + PrecDec::percent(20).checked_pow(2).unwrap(), + PrecDec::percent(4) + ); + + assert_eq!( + PrecDec::percent(20).checked_pow(3).unwrap(), + PrecDec::permille(8) + ); + + assert_eq!( + PrecDec::percent(200).checked_pow(4).unwrap(), + PrecDec::percent(1600) + ); + + assert_eq!( + PrecDec::percent(200).checked_pow(4).unwrap(), + PrecDec::percent(1600) + ); + + assert_eq!( + PrecDec::percent(700).checked_pow(5).unwrap(), + PrecDec::percent(1680700) + ); + + assert_eq!( + PrecDec::percent(700).checked_pow(8).unwrap(), + PrecDec::percent(576480100) + ); + + + assert_eq!( + PrecDec::percent(10).checked_pow(2).unwrap(), + PrecDec(10000000000000000000000000u128.into()) + ); + + assert_eq!( + PrecDec::percent(10).checked_pow(27).unwrap(), + PrecDec(1u128.into()) + ); + } + + #[test] + fn precdec_checked_pow_overflow() { + assert_eq!( + PrecDec::MAX.checked_pow(2), + Err(OverflowError::new(OverflowOperation::Pow)) + ); + } + + #[test] + fn precdec_to_string() { + // Integers + assert_eq!(PrecDec::zero().to_string(), "0"); + assert_eq!(PrecDec::one().to_string(), "1"); + assert_eq!(PrecDec::percent(500).to_string(), "5"); + + // Decimals + assert_eq!(PrecDec::percent(125).to_string(), "1.25"); + assert_eq!(PrecDec::percent(42638).to_string(), "426.38"); + assert_eq!(PrecDec::percent(3).to_string(), "0.03"); + assert_eq!(PrecDec::permille(987).to_string(), "0.987"); + + assert_eq!( + PrecDec(Uint256::from(1u128)).to_string(), + "0.000000000000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(10u128)).to_string(), + "0.00000000000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(100u128)).to_string(), + "0.0000000000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(1000u128)).to_string(), + "0.000000000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(10000u128)).to_string(), + "0.00000000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(100000u128)).to_string(), + "0.0000000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(1000000u128)).to_string(), + "0.000000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(10000000u128)).to_string(), + "0.00000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(100000000u128)).to_string(), + "0.0000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(1000000000u128)).to_string(), + "0.000000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(10000000000u128)).to_string(), + "0.00000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(100000000000u128)).to_string(), + "0.0000000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(10000000000000u128)).to_string(), + "0.00000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(100000000000000u128)).to_string(), + "0.0000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(1000000000000000u128)).to_string(), + "0.000000000001" + ); + assert_eq!( + PrecDec(Uint256::from(10000000000000000u128)).to_string(), + "0.00000000001" + ); + assert_eq!( + PrecDec(Uint256::from(100000000000000000u128)).to_string(), + "0.0000000001" + ); + } + + #[test] + fn precdec_iter_sum() { + let items = vec![ + PrecDec::zero(), + PrecDec(Uint256::from(2u128)), + PrecDec(Uint256::from(2u128)), + ]; + assert_eq!(items.iter().sum::(), PrecDec(Uint256::from(4u128))); + assert_eq!( + items.into_iter().sum::(), + PrecDec(Uint256::from(4u128)) + ); + + let empty: Vec = vec![]; + assert_eq!(PrecDec::zero(), empty.iter().sum::()); + } + + #[test] + fn precdec_serialize() { + assert_eq!(serde_json::to_vec(&PrecDec::zero()).unwrap(), br#""0""#); + assert_eq!(serde_json::to_vec(&PrecDec::one()).unwrap(), br#""1""#); + assert_eq!( + serde_json::to_vec(&PrecDec::percent(8)).unwrap(), + br#""0.08""# + ); + assert_eq!( + serde_json::to_vec(&PrecDec::percent(87)).unwrap(), + br#""0.87""# + ); + assert_eq!( + serde_json::to_vec(&PrecDec::percent(876)).unwrap(), + br#""8.76""# + ); + assert_eq!( + serde_json::to_vec(&PrecDec::percent(8765)).unwrap(), + br#""87.65""# + ); + } + + #[test] + fn precdec_deserialize() { + assert_eq!( + serde_json::from_slice::(br#""0""#).unwrap(), + PrecDec::zero() + ); + assert_eq!( + serde_json::from_slice::(br#""1""#).unwrap(), + PrecDec::one() + ); + assert_eq!( + serde_json::from_slice::(br#""000""#).unwrap(), + PrecDec::zero() + ); + assert_eq!( + serde_json::from_slice::(br#""001""#).unwrap(), + PrecDec::one() + ); + + assert_eq!( + serde_json::from_slice::(br#""0.08""#).unwrap(), + PrecDec::percent(8) + ); + assert_eq!( + serde_json::from_slice::(br#""0.87""#).unwrap(), + PrecDec::percent(87) + ); + assert_eq!( + serde_json::from_slice::(br#""8.76""#).unwrap(), + PrecDec::percent(876) + ); + assert_eq!( + serde_json::from_slice::(br#""87.65""#).unwrap(), + PrecDec::percent(8765) + ); + } + + #[test] + fn precdec_abs_diff_works() { + let a = PrecDec::percent(285); + let b = PrecDec::percent(200); + let expected = PrecDec::percent(85); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn precdec_rem_works() { + // 4.02 % 1.11 = 0.69 + assert_eq!( + PrecDec::percent(402) % PrecDec::percent(111), + PrecDec::percent(69) + ); + + // 15.25 % 4 = 3.25 + assert_eq!( + PrecDec::percent(1525) % PrecDec::percent(400), + PrecDec::percent(325) + ); + + let a = PrecDec::percent(318); + let b = PrecDec::percent(317); + let expected = PrecDec::percent(1); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + fn precdec_rem_assign_works() { + let mut a = PrecDec::percent(17673); + a %= PrecDec::percent(2362); + assert_eq!(a, PrecDec::percent(1139)); // 176.73 % 23.62 = 11.39 + + let mut a = PrecDec::percent(4262); + let b = PrecDec::percent(1270); + a %= &b; + assert_eq!(a, PrecDec::percent(452)); // 42.62 % 12.7 = 4.52 + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn precdec_rem_panics_for_zero() { + let _ = PrecDec::percent(777) % PrecDec::zero(); + } + + #[test] + fn precdec_checked_methods() { + // checked add + assert_eq!( + PrecDec::percent(402) + .checked_add(PrecDec::percent(111)) + .unwrap(), + PrecDec::percent(513) + ); + assert!(matches!( + PrecDec::MAX.checked_add(PrecDec::percent(1)), + Err(OverflowError { .. }) + )); + + // checked sub + assert_eq!( + PrecDec::percent(1111) + .checked_sub(PrecDec::percent(111)) + .unwrap(), + PrecDec::percent(1000) + ); + assert!(matches!( + PrecDec::zero().checked_sub(PrecDec::percent(1)), + Err(OverflowError { .. }) + )); + + // checked div + assert_eq!( + PrecDec::percent(30) + .checked_div(PrecDec::percent(200)) + .unwrap(), + PrecDec::percent(15) + ); + assert_eq!( + PrecDec::percent(88) + .checked_div(PrecDec::percent(20)) + .unwrap(), + PrecDec::percent(440) + ); + assert!(matches!( + PrecDec::MAX.checked_div(PrecDec::zero()), + Err(CheckedFromRatioError::DivideByZero {}) + )); + assert!(matches!( + PrecDec::MAX.checked_div(PrecDec::percent(1)), + Err(CheckedFromRatioError::Overflow {}) + )); + + // checked rem + assert_eq!( + PrecDec::percent(402) + .checked_rem(PrecDec::percent(111)) + .unwrap(), + PrecDec::percent(69) + ); + assert_eq!( + PrecDec::percent(1525) + .checked_rem(PrecDec::percent(400)) + .unwrap(), + PrecDec::percent(325) + ); + assert!(matches!( + PrecDec::MAX.checked_rem(PrecDec::zero()), + Err(DivideByZeroError { .. }) + )); + } + + #[test] + fn precdec_pow_works() { + assert_eq!(PrecDec::percent(200).pow(2), PrecDec::percent(400)); + assert_eq!(PrecDec::percent(200).pow(10), PrecDec::percent(102400)); + } + + #[test] + #[should_panic] + fn precdec_pow_overflow_panics() { + _ = PrecDec::MAX.pow(2u32); + } + + #[test] + fn precdec_saturating_works() { + assert_eq!( + PrecDec::percent(200).saturating_add(PrecDec::percent(200)), + PrecDec::percent(400) + ); + assert_eq!( + PrecDec::MAX.saturating_add(PrecDec::percent(200)), + PrecDec::MAX + ); + assert_eq!( + PrecDec::percent(200).saturating_sub(PrecDec::percent(100)), + PrecDec::percent(100) + ); + assert_eq!( + PrecDec::zero().saturating_sub(PrecDec::percent(200)), + PrecDec::zero() + ); + assert_eq!( + PrecDec::percent(200).saturating_mul(PrecDec::percent(50)), + PrecDec::percent(100) + ); + assert_eq!( + PrecDec::MAX.saturating_mul(PrecDec::percent(200)), + PrecDec::MAX + ); + assert_eq!( + PrecDec::percent(400).saturating_pow(2u32), + PrecDec::percent(1600) + ); + assert_eq!(PrecDec::MAX.saturating_pow(2u32), PrecDec::MAX); + } + + #[test] + fn precdec_rounding() { + assert_eq!(PrecDec::one().floor(), PrecDec::one()); + assert_eq!(PrecDec::percent(150).floor(), PrecDec::one()); + assert_eq!(PrecDec::percent(199).floor(), PrecDec::one()); + assert_eq!(PrecDec::percent(200).floor(), PrecDec::percent(200)); + assert_eq!(PrecDec::percent(99).floor(), PrecDec::zero()); + + assert_eq!(PrecDec::one().ceil(), PrecDec::one()); + assert_eq!(PrecDec::percent(150).ceil(), PrecDec::percent(200)); + assert_eq!(PrecDec::percent(199).ceil(), PrecDec::percent(200)); + assert_eq!(PrecDec::percent(99).ceil(), PrecDec::one()); + assert_eq!(PrecDec(Uint256::from(1u128)).ceil(), PrecDec::one()); + } + + #[test] + #[should_panic(expected = "attempt to ceil with overflow")] + fn precdec_ceil_panics() { + let _ = PrecDec::MAX.ceil(); + } + + #[test] + fn precdec_checked_ceil() { + assert_eq!( + PrecDec::percent(199).checked_ceil(), + Ok(PrecDec::percent(200)) + ); + assert!(matches!( + PrecDec::MAX.checked_ceil(), + Err(RoundUpOverflowError { .. }) + )); + } + + #[test] + fn precdec_to_uint_floor_works() { + let d = PrecDec::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_uint_floor(), Uint256::from_u128(12)); + let d = PrecDec::from_str("12.345").unwrap(); + assert_eq!(d.to_uint_floor(), Uint256::from_u128(12)); + let d = PrecDec::from_str("12.999").unwrap(); + assert_eq!(d.to_uint_floor(), Uint256::from_u128(12)); + let d = PrecDec::from_str("0.98451384").unwrap(); + assert_eq!(d.to_uint_floor(), Uint256::from_u128(0)); + + let d = PrecDec::from_str("75.0").unwrap(); + assert_eq!(d.to_uint_floor(), Uint256::from_u128(75)); + let d = PrecDec::from_str("0.0").unwrap(); + assert_eq!(d.to_uint_floor(), Uint256::from_u128(0)); + + let d = PrecDec::MAX; + assert_eq!(d.to_uint_floor(), Uint256::from_str("115792089237316195423570985008687907853269984665640").unwrap()); + + // Does the same as the old workaround `Uint256::one() * my_decimal`. + // This block can be deleted as part of https://github.com/CosmWasm/cosmwasm/issues/1485. + let tests = vec![ + (PrecDec::from_str("12.345").unwrap(), Uint256::from(12u128)), + (PrecDec::from_str("0.98451384").unwrap(), Uint256::from(0u128)), + (PrecDec::from_str("178.0").unwrap(), Uint256::from(178u128)), + (PrecDec::MIN, Uint256::from(0u128)), + (PrecDec::MAX, Uint256::MAX / PrecDec::DECIMAL_FRACTIONAL), + ]; + for (my_decimal, expected) in tests.into_iter() { + assert_eq!(my_decimal.to_uint_floor(), expected); + } + } + + #[test] + fn precdec_to_uint_ceil_works() { + let d = PrecDec::from_str("12.000000000000000001").unwrap(); + assert_eq!(d.to_uint_ceil(), Uint256::from_u128(13)); + let d = PrecDec::from_str("12.345").unwrap(); + assert_eq!(d.to_uint_ceil(), Uint256::from_u128(13)); + let d = PrecDec::from_str("12.999").unwrap(); + assert_eq!(d.to_uint_ceil(), Uint256::from_u128(13)); + + let d = PrecDec::from_str("75.0").unwrap(); + assert_eq!(d.to_uint_ceil(), Uint256::from_u128(75)); + let d = PrecDec::from_str("0.0").unwrap(); + assert_eq!(d.to_uint_ceil(), Uint256::from_u128(0)); + + let d = PrecDec::MAX; + assert_eq!(d.to_uint_ceil(), Uint256::from_str("115792089237316195423570985008687907853269984665641").unwrap()); + } + + #[test] + fn precdec_partial_eq() { + let test_cases = [ + ("1", "1", true), + ("0.5", "0.5", true), + ("0.5", "0.51", false), + ("0", "0.00000", true), + ] + .into_iter() + .map(|(lhs, rhs, expected)| (dec(lhs), dec(rhs), expected)); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } + + #[test] + fn precdec_implements_debug() { + let decimal = PrecDec::from_str("123.45").unwrap(); + assert_eq!(format!("{decimal:?}"), "PrecDec(123.45)"); + + let test_cases = ["5", "5.01", "42", "0", "2"]; + for s in test_cases { + let decimal = PrecDec::from_str(s).unwrap(); + let expected = format!("PrecDec({s})"); + assert_eq!(format!("{decimal:?}"), expected); + } + } +} From d8b357fbe484c089c7ed5acfc608d81b33e8fce2 Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Wed, 11 Dec 2024 02:40:41 -0500 Subject: [PATCH 3/9] fix all tests --- .../src/types/neutron/util/precdec.rs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/neutron-std/src/types/neutron/util/precdec.rs b/packages/neutron-std/src/types/neutron/util/precdec.rs index a465508..3f4e96c 100644 --- a/packages/neutron-std/src/types/neutron/util/precdec.rs +++ b/packages/neutron-std/src/types/neutron/util/precdec.rs @@ -350,10 +350,10 @@ impl PrecDec { // TODO: This could be made more efficient once log10 is in: // https://github.com/rust-lang/rust/issues/70887 // The max precision is something like `9 - log10(self.0) / 2`. - (0..14) + (0..=Self::DECIMAL_PLACES / 2) .rev() .find_map(|i| self.sqrt_with_precision(i)) - // The last step (i = 0) is guaranteed to succeed because `isqrt(u128::MAX) * 10^9` does not overflow + // The last step (i = 0) is guaranteed to succeed because `isqrt(u256::MAX) * 10^13` does not overflow .unwrap() } @@ -363,8 +363,9 @@ impl PrecDec { /// Returns `None` if the internal multiplication overflows. #[must_use = "this returns the result of the operation, without modifying the original"] fn sqrt_with_precision(&self, precision: u32) -> Option { - let inner_mul = Uint256::from(100u128).pow(precision); + let inner_mul = Uint256::from(10u128).pow(precision * 2 + 1); self.0.checked_mul(inner_mul).ok().map(|inner| { + let sq = inner.isqrt(); let outer_mul = Uint256::from(10u128).pow(Self::DECIMAL_PLACES / 2 - precision); Self(inner.isqrt().checked_mul(outer_mul).unwrap()) }) @@ -501,12 +502,11 @@ impl TryFrom for PrecDec { type Error = PrecDecRangeExceeded; fn try_from(value: SignedDecimal) -> Result { - value + let atomics: Uint256 = value .atomics() .try_into() - .map(PrecDec) - .map_err(|_| PrecDecRangeExceeded) - + .map_err(|_| PrecDecRangeExceeded)?; + Self::from_atomics(atomics, SignedDecimal::DECIMAL_PLACES) } } @@ -514,11 +514,11 @@ impl TryFrom for PrecDec { type Error = PrecDecRangeExceeded; fn try_from(value: SignedDecimal256) -> Result { - value + let atomics: Uint256 = value .atomics() .try_into() - .map(PrecDec) - .map_err(|_| PrecDecRangeExceeded) + .map_err(|_| PrecDecRangeExceeded)?; + Self::from_atomics(atomics, SignedDecimal::DECIMAL_PLACES) } } @@ -830,7 +830,7 @@ mod tests { #[test] fn precdec_from_decimal256_works() { let val = Decimal256::new(Uint256::from(Uint128::MAX)); - assert_eq!(PrecDec::try_from(val), Ok(PrecDec::from_str("99").unwrap())); + assert_eq!(PrecDec::try_from(val), Ok(PrecDec::from_str("340282366920938463463.374607431768211455").unwrap())); assert_eq!(PrecDec::try_from(Decimal256::zero()), Ok(PrecDec::zero())); assert_eq!(PrecDec::try_from(Decimal256::one()), Ok(PrecDec::one())); @@ -851,8 +851,7 @@ mod tests { fn precdec_try_from_signed_works() { assert_eq!( PrecDec::try_from(SignedDecimal::MAX).unwrap(), - PrecDec::raw(SignedDecimal::MAX.atomics().i128() as u128) - ); + PrecDec::from_str("170141183460469231731.687303715884105727").unwrap()); assert_eq!( PrecDec::try_from(SignedDecimal::zero()).unwrap(), PrecDec::zero() @@ -1692,7 +1691,7 @@ mod tests { fn precdec_uint128_sqrt_is_precise() { assert_eq!( PrecDec::from_str("2").unwrap().sqrt(), - PrecDec::from_str("1.414213562373095048").unwrap() // https://www.wolframalpha.com/input/?i=sqrt%282%29 + PrecDec::from_str("1.414213562373095048801688724").unwrap() // https://www.wolframalpha.com/input/?i=sqrt%282%29 ); } @@ -1711,7 +1710,7 @@ mod tests { // The last two digits (27) are truncated below due to the algorithm // we use. Larger numbers will cause less precision. // https://www.wolframalpha.com/input/?i=sqrt%28400001%29 - PrecDec::from_str("632.456322602596803200").unwrap() + PrecDec::from_str("632.456322602596803227841789792").unwrap() ); } From bfe745f7a7080f681dfef97d053932897cbe0f6e Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Wed, 11 Dec 2024 02:58:12 -0500 Subject: [PATCH 4/9] add to_prec_dec_string impl --- packages/neutron-std/src/types/neutron/util/precdec.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/neutron-std/src/types/neutron/util/precdec.rs b/packages/neutron-std/src/types/neutron/util/precdec.rs index 3f4e96c..d962487 100644 --- a/packages/neutron-std/src/types/neutron/util/precdec.rs +++ b/packages/neutron-std/src/types/neutron/util/precdec.rs @@ -461,6 +461,11 @@ impl PrecDec { Uint256::one() + ((x - Uint256::one()) / y) } } + + #[must_use = "converts a PrecDec to a string that can be passed and parsed by Neutron core"] + pub fn to_prec_dec_string(self) -> String { + self.atomics().to_string() + } } impl Fraction for PrecDec { From 444856a023b7ea27f0422b94f4c214852e55fe24 Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Thu, 19 Dec 2024 19:44:11 -0800 Subject: [PATCH 5/9] fix generation for PrecDec Fields --- packages/neutron-std/src/lib.rs | 2 + packages/neutron-std/src/serde/mod.rs | 50 ++++++ .../neutron-std/src/types/neutron/dex/mod.rs | 74 +++++--- .../neutron-std/src/types/neutron/dex/v2.rs | 8 +- packages/neutron-std/src/types/neutron/mod.rs | 1 - .../{types/neutron => }/util/forward_ref.rs | 0 .../src/{types/neutron => }/util/mod.rs | 2 +- .../src/{types/neutron => }/util/precdec.rs | 160 ++++++++++++++---- proto-build/src/transform.rs | 1 + proto-build/src/transformers.rs | 122 +++++++++++++ 10 files changed, 363 insertions(+), 57 deletions(-) rename packages/neutron-std/src/{types/neutron => }/util/forward_ref.rs (100%) rename packages/neutron-std/src/{types/neutron => }/util/mod.rs (100%) rename packages/neutron-std/src/{types/neutron => }/util/precdec.rs (94%) diff --git a/packages/neutron-std/src/lib.rs b/packages/neutron-std/src/lib.rs index 457be9b..9c7f301 100644 --- a/packages/neutron-std/src/lib.rs +++ b/packages/neutron-std/src/lib.rs @@ -13,3 +13,5 @@ pub mod shim; pub mod types; pub use shim::{cosmwasm_to_proto_coins, try_proto_to_cosmwasm_coins}; + +pub mod util; diff --git a/packages/neutron-std/src/serde/mod.rs b/packages/neutron-std/src/serde/mod.rs index 6bcde31..8fd657e 100644 --- a/packages/neutron-std/src/serde/mod.rs +++ b/packages/neutron-std/src/serde/mod.rs @@ -135,3 +135,53 @@ pub mod as_option_base64_encoded_string { } } } + +pub mod as_option_prec_dec { + + use crate::util::precdec::PrecDec; + use serde::{de, Deserialize, Deserializer, Serializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let encoded_prec_dec_str: Option = Option::deserialize(deserializer)?; + match encoded_prec_dec_str { + Some(s) => PrecDec::from_prec_dec_str(s.as_str()) + .map(|p| Some(p)) + .map_err(de::Error::custom), + None => Ok(None), + } + } + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + { + match value { + Some(p) => serializer.serialize_str(&p.to_prec_dec_string()), + None => serializer.serialize_none(), + } + } +} + +pub mod as_prec_dec { + use crate::util::precdec::PrecDec; + use serde::{de, Deserialize, Deserializer, Serializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let encoded_prec_dec_str: String = String::deserialize(deserializer)?; + + PrecDec::from_prec_dec_str(&encoded_prec_dec_str.to_owned()).map_err(de::Error::custom) + } + + pub fn serialize(value: &PrecDec, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&value.to_prec_dec_string()) + } +} diff --git a/packages/neutron-std/src/types/neutron/dex/mod.rs b/packages/neutron-std/src/types/neutron/dex/mod.rs index b117d8e..2481184 100644 --- a/packages/neutron-std/src/types/neutron/dex/mod.rs +++ b/packages/neutron-std/src/types/neutron/dex/mod.rs @@ -84,16 +84,28 @@ pub struct PoolReserves { pub reserves_maker_denom: ::prost::alloc::string::String, /// DEPRECATED: price_taker_to_maker will be removed in future release, `maker_price` should always be used. #[deprecated] - #[prost(string, tag = "3")] - pub price_taker_to_maker: ::prost::alloc::string::String, + #[prost(message, required, tag = "3")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub price_taker_to_maker: crate::util::precdec::PrecDec, /// DEPRECATED: price_opposite_taker_maker was an internal implementation detail and will be removed in a future release. /// It is being kept strictly for backwards compatibility. The actual field value is unused. #[deprecated] - #[prost(string, tag = "4")] - pub price_opposite_taker_to_maker: ::prost::alloc::string::String, + #[prost(message, required, tag = "4")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub price_opposite_taker_to_maker: crate::util::precdec::PrecDec, /// This is the price of the PoolReserves denominated in the opposite token. (ie. 1 TokenA with a maker_price of 10 is worth 10 TokenB ) - #[prost(string, tag = "5")] - pub maker_price: ::prost::alloc::string::String, + #[prost(message, required, tag = "5")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub maker_price: crate::util::precdec::PrecDec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive( @@ -224,11 +236,19 @@ pub struct LimitOrderTranche { pub expiration_time: ::core::option::Option, /// DEPRECATED: price_taker_to_maker will be removed in future release, `maker_price` should always be used. #[deprecated] - #[prost(string, tag = "7")] - pub price_taker_to_maker: ::prost::alloc::string::String, + #[prost(message, required, tag = "7")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub price_taker_to_maker: crate::util::precdec::PrecDec, /// This is the price of the LimitOrder denominated in the opposite token. (ie. 1 TokenA with a maker_price of 10 is worth 10 TokenB ) - #[prost(string, tag = "8")] - pub maker_price: ::prost::alloc::string::String, + #[prost(message, required, tag = "8")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub maker_price: crate::util::precdec::PrecDec, } /// Params defines the parameters for the module. #[allow(clippy::derive_partial_eq_without_eq)] @@ -467,15 +487,23 @@ pub struct MsgPlaceLimitOrder { #[prost(string, tag = "10")] #[prost(optional)] pub max_amount_out: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, tag = "11")] + #[prost(message, tag = "11")] + #[serde( + serialize_with = "crate::serde::as_option_prec_dec::serialize", + deserialize_with = "crate::serde::as_option_prec_dec::deserialize" + )] #[prost(optional)] - pub limit_sell_price: ::core::option::Option<::prost::alloc::string::String>, + pub limit_sell_price: ::core::option::Option, /// min_average_sell_price is an optional parameter that sets a required minimum average price for the entire trade. /// if the min_average_sell_price is not met the trade will fail. /// If min_average_sell_price is omitted limit_sell_price will be used instead - #[prost(string, tag = "12")] + #[prost(message, tag = "12")] + #[serde( + serialize_with = "crate::serde::as_option_prec_dec::serialize", + deserialize_with = "crate::serde::as_option_prec_dec::deserialize" + )] #[prost(optional)] - pub min_average_sell_price: ::core::option::Option<::prost::alloc::string::String>, + pub min_average_sell_price: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive( @@ -618,8 +646,12 @@ pub struct MsgMultiHopSwap { pub routes: ::prost::alloc::vec::Vec, #[prost(string, tag = "4")] pub amount_in: ::prost::alloc::string::String, - #[prost(string, tag = "5")] - pub exit_limit_price: ::prost::alloc::string::String, + #[prost(message, required, tag = "5")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub exit_limit_price: crate::util::precdec::PrecDec, /// If pickBestRoute == true then all routes are run and the route with the /// best price is chosen otherwise, the first succesful route is used. #[prost(bool, tag = "6")] @@ -1425,8 +1457,12 @@ pub struct QueryEstimateMultiHopSwapRequest { pub routes: ::prost::alloc::vec::Vec, #[prost(string, tag = "4")] pub amount_in: ::prost::alloc::string::String, - #[prost(string, tag = "5")] - pub exit_limit_price: ::prost::alloc::string::String, + #[prost(message, required, tag = "5")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub exit_limit_price: crate::util::precdec::PrecDec, /// If pickBestRoute == true then all routes are run and the route with the /// best price is chosen otherwise, the first succesful route is used. #[prost(bool, tag = "6")] @@ -2036,7 +2072,7 @@ impl<'a, Q: cosmwasm_std::CustomQuery> DexQuerier<'a, Q> { receiver: ::prost::alloc::string::String, routes: ::prost::alloc::vec::Vec, amount_in: ::prost::alloc::string::String, - exit_limit_price: ::prost::alloc::string::String, + exit_limit_price: crate::util::precdec::PrecDec, pick_best_route: bool, ) -> Result { QueryEstimateMultiHopSwapRequest { diff --git a/packages/neutron-std/src/types/neutron/dex/v2.rs b/packages/neutron-std/src/types/neutron/dex/v2.rs index 9c2b53b..59f09c1 100644 --- a/packages/neutron-std/src/types/neutron/dex/v2.rs +++ b/packages/neutron-std/src/types/neutron/dex/v2.rs @@ -19,6 +19,10 @@ pub struct Params { deserialize_with = "crate::serde::as_str_vec::deserialize" )] pub fee_tiers: ::prost::alloc::vec::Vec, - #[prost(string, tag = "2")] - pub max_true_taker_spread: ::prost::alloc::string::String, + #[prost(message, required, tag = "2")] + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + pub max_true_taker_spread: crate::util::precdec::PrecDec, } diff --git a/packages/neutron-std/src/types/neutron/mod.rs b/packages/neutron-std/src/types/neutron/mod.rs index c6486c3..eb206e7 100644 --- a/packages/neutron-std/src/types/neutron/mod.rs +++ b/packages/neutron-std/src/types/neutron/mod.rs @@ -8,4 +8,3 @@ pub mod ibcratelimit; pub mod interchainqueries; pub mod interchaintxs; pub mod transfer; -pub mod util; diff --git a/packages/neutron-std/src/types/neutron/util/forward_ref.rs b/packages/neutron-std/src/util/forward_ref.rs similarity index 100% rename from packages/neutron-std/src/types/neutron/util/forward_ref.rs rename to packages/neutron-std/src/util/forward_ref.rs diff --git a/packages/neutron-std/src/types/neutron/util/mod.rs b/packages/neutron-std/src/util/mod.rs similarity index 100% rename from packages/neutron-std/src/types/neutron/util/mod.rs rename to packages/neutron-std/src/util/mod.rs index 3d97001..bcaca70 100644 --- a/packages/neutron-std/src/types/neutron/util/mod.rs +++ b/packages/neutron-std/src/util/mod.rs @@ -1,2 +1,2 @@ -pub mod precdec; mod forward_ref; +pub mod precdec; diff --git a/packages/neutron-std/src/types/neutron/util/precdec.rs b/packages/neutron-std/src/util/precdec.rs similarity index 94% rename from packages/neutron-std/src/types/neutron/util/precdec.rs rename to packages/neutron-std/src/util/precdec.rs index d962487..cc50e04 100644 --- a/packages/neutron-std/src/types/neutron/util/precdec.rs +++ b/packages/neutron-std/src/util/precdec.rs @@ -2,20 +2,21 @@ use core::cmp::Ordering; use core::fmt::{self, Write}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign}; use core::str::FromStr; +use prost::encoding::{skip_field, WireType}; +use prost::{DecodeError, Message}; use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use crate::util::forward_ref::{ + forward_ref_binop, forward_ref_op_assign, forward_ref_partial_eq, +}; use cosmwasm_std::{ CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, OverflowOperation, RoundUpOverflowError, StdError, }; -use crate::types::neutron::util::forward_ref::{forward_ref_binop, forward_ref_op_assign, forward_ref_partial_eq}; use cosmwasm_std::{Decimal256, SignedDecimal, SignedDecimal256}; use cosmwasm_std::{Fraction, Isqrt, Uint256, Uint512}; - - - /// A fixed-point decimal value with 27 fractional digits, i.e. Precdec(1_000_000_000_000_000_000) == 1.0 /// /// The greatest possible value that can be represented is 115792089237316195423570985008687907853269984665640.564039457584007913129639935 (which is (2^256 - 1) / 10^27) @@ -28,15 +29,20 @@ forward_ref_partial_eq!(PrecDec, PrecDec); #[error("PrecDec range exceeded")] pub struct PrecDecRangeExceeded; -impl PrecDec { - const DECIMAL_FRACTIONAL: Uint256 = Uint256::from_u128(1_000_000_000_000_000_000_000_000_000u128); // 10**27 - // const DECIMAL_FRACTIONAL_SQUARED: Uint256 = Uint256::from_uint128(1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000u128); // (1*10**18)**2 = 1*10**54 - +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[error("Cannot parse PrecDec String from {input}")] +pub struct ParsePrecDecError { + input: String, +} +impl PrecDec { + const DECIMAL_FRACTIONAL: Uint256 = + Uint256::from_u128(1_000_000_000_000_000_000_000_000_000u128); // 10**27 + // const DECIMAL_FRACTIONAL_SQUARED: Uint256 = Uint256::from_uint128(1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000u128); // (1*10**18)**2 = 1*10**54 /// The number of decimal places. Since decimal types are fixed-point rather than /// floating-point, this is a constant. - pub const DECIMAL_PLACES: u32 = 27; // This needs to be an even number. // TODO: is it ok ? + pub const DECIMAL_PLACES: u32 = 27; /// The largest value that can be represented by this decimal type. pub const MAX: Self = Self(Uint256::MAX); /// The smallest value that can be represented by this decimal type. @@ -282,7 +288,7 @@ impl PrecDec { /// Multiplies one `PrecDec` by another, returning an `OverflowError` if an overflow occurred. pub fn checked_mul(self, other: Self) -> Result { let result_as_uint256 = self.numerator().full_mul(other.numerator()) - / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL) ; // from_uint128 is a const method and should be "free" + / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL); // from_uint128 is a const method and should be "free" result_as_uint256 .try_into() .map(Self) @@ -365,7 +371,6 @@ impl PrecDec { fn sqrt_with_precision(&self, precision: u32) -> Option { let inner_mul = Uint256::from(10u128).pow(precision * 2 + 1); self.0.checked_mul(inner_mul).ok().map(|inner| { - let sq = inner.isqrt(); let outer_mul = Uint256::from(10u128).pow(Self::DECIMAL_PLACES / 2 - precision); Self(inner.isqrt().checked_mul(outer_mul).unwrap()) }) @@ -464,7 +469,16 @@ impl PrecDec { #[must_use = "converts a PrecDec to a string that can be passed and parsed by Neutron core"] pub fn to_prec_dec_string(self) -> String { - self.atomics().to_string() + self.atomics().to_string() + } + + #[must_use = "returns a PrecDec from an atomics strings"] + pub fn from_prec_dec_str(input: &str) -> Result { + let val = input.parse::().map_err(|_| ParsePrecDecError { + input: input.to_string(), + })?; + + Ok(PrecDec(val)) } } @@ -486,7 +500,9 @@ impl Fraction for PrecDec { if self.is_zero() { None } else { - let decimal_fractional_squared = Self::DECIMAL_FRACTIONAL.checked_mul(Self::DECIMAL_FRACTIONAL).unwrap(); + let decimal_fractional_squared = Self::DECIMAL_FRACTIONAL + .checked_mul(Self::DECIMAL_FRACTIONAL) + .unwrap(); // Let self be p/q with p = self.0 and q = DECIMAL_FRACTIONAL. // Now we calculate the inverse a/b = q/p such that b = DECIMAL_FRACTIONAL. Then // `a = DECIMAL_FRACTIONAL*DECIMAL_FRACTIONAL / self.0`. @@ -507,7 +523,7 @@ impl TryFrom for PrecDec { type Error = PrecDecRangeExceeded; fn try_from(value: SignedDecimal) -> Result { - let atomics: Uint256 = value + let atomics: Uint256 = value .atomics() .try_into() .map_err(|_| PrecDecRangeExceeded)?; @@ -519,7 +535,7 @@ impl TryFrom for PrecDec { type Error = PrecDecRangeExceeded; fn try_from(value: SignedDecimal256) -> Result { - let atomics: Uint256 = value + let atomics: Uint256 = value .atomics() .try_into() .map_err(|_| PrecDecRangeExceeded)?; @@ -657,7 +673,7 @@ impl Mul for PrecDec { // = (a.numerator() * b.numerator()) / a.denominator() / b.denominator() let result_as_uint256 = self.numerator().full_mul(other.numerator()) - / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL); // from_uint128 is a const method and should be "free" // TODO: fix me, this can still overflow + / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL); // from_uint128 is a const method and should be "free" // TODO: fix me, this can still overflow match result_as_uint256.try_into() { Ok(result) => Self(result), Err(_) => panic!("attempt to multiply with overflow"), @@ -780,6 +796,47 @@ impl<'de> de::Visitor<'de> for PrecDecVisitor { } } +// Implement `prost::Message` for `PrecDec` +impl Message for PrecDec { + fn encode_raw(&self, buf: &mut B) + where + B: prost::bytes::BufMut, + { + let value = self.to_prec_dec_string(); + prost::encoding::string::encode(1, &value, buf); + } + + fn merge_field( + &mut self, + tag: u32, + wire_type: WireType, + buf: &mut B, + ctx: prost::encoding::DecodeContext, + ) -> Result<(), DecodeError> + where + B: prost::bytes::Buf, + { + if tag == 1 { + let mut value = String::new(); + prost::encoding::string::merge(wire_type, &mut value, buf, ctx)?; + *self = + PrecDec::from_prec_dec_str(&value).map_err(|e| DecodeError::new(e.to_string()))?; + Ok(()) + } else { + skip_field(wire_type, tag, buf, ctx) + } + } + + fn encoded_len(&self) -> usize { + let value = self.to_prec_dec_string(); + prost::encoding::string::encoded_len(1, &value) + } + + fn clear(&mut self) { + *self = PrecDec(Uint256::zero()); + } +} + #[cfg(test)] mod tests { use super::*; @@ -835,7 +892,10 @@ mod tests { #[test] fn precdec_from_decimal256_works() { let val = Decimal256::new(Uint256::from(Uint128::MAX)); - assert_eq!(PrecDec::try_from(val), Ok(PrecDec::from_str("340282366920938463463.374607431768211455").unwrap())); + assert_eq!( + PrecDec::try_from(val), + Ok(PrecDec::from_str("340282366920938463463.374607431768211455").unwrap()) + ); assert_eq!(PrecDec::try_from(Decimal256::zero()), Ok(PrecDec::zero())); assert_eq!(PrecDec::try_from(Decimal256::one()), Ok(PrecDec::one())); @@ -856,7 +916,8 @@ mod tests { fn precdec_try_from_signed_works() { assert_eq!( PrecDec::try_from(SignedDecimal::MAX).unwrap(), - PrecDec::from_str("170141183460469231731.687303715884105727").unwrap()); + PrecDec::from_str("170141183460469231731.687303715884105727").unwrap() + ); assert_eq!( PrecDec::try_from(SignedDecimal::zero()).unwrap(), PrecDec::zero() @@ -1079,8 +1140,10 @@ mod tests { // Works for documented max value assert_eq!( - - PrecDec::from_str("115792089237316195423570985008687907853269984665640.564039457584007913129639935").unwrap(), + PrecDec::from_str( + "115792089237316195423570985008687907853269984665640.564039457584007913129639935" + ) + .unwrap(), PrecDec::MAX ); } @@ -1160,25 +1223,28 @@ mod tests { #[test] fn precdec_from_str_errors_for_more_than_max_value() { // Integer - match PrecDec::from_str("34028236692099999999999999999999999999999999999999999938463464").unwrap_err() { + match PrecDec::from_str("34028236692099999999999999999999999999999999999999999938463464") + .unwrap_err() + { StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), e => panic!("Unexpected error: {e:?}"), } // Decimal - match PrecDec::from_str("115792089237316195423570985008687907853269984665640564039458.0").unwrap_err() { + match PrecDec::from_str("115792089237316195423570985008687907853269984665640564039458.0") + .unwrap_err() + { StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), e => panic!("Unexpected error: {e:?}"), } match Decimal256::from_str( "115792089237316195423570985008687907853269984665640564039457.584007913129639936", ) - .unwrap_err() + .unwrap_err() { StdError::GenericErr { msg, .. } => assert_eq!(msg, "Value too big"), e => panic!("Unexpected error: {e:?}"), } - } #[test] @@ -1190,9 +1256,18 @@ mod tests { let max = PrecDec::MAX; assert_eq!(zero.atomics(), Uint256::from_u128(0)); - assert_eq!(one.atomics(), Uint256::from_u128(1000000000000000000000000000)); - assert_eq!(half.atomics(), Uint256::from_u128(500000000000000000000000000)); - assert_eq!(two.atomics(), Uint256::from_u128(2000000000000000000000000000)); + assert_eq!( + one.atomics(), + Uint256::from_u128(1000000000000000000000000000) + ); + assert_eq!( + half.atomics(), + Uint256::from_u128(500000000000000000000000000) + ); + assert_eq!( + two.atomics(), + Uint256::from_u128(2000000000000000000000000000) + ); assert_eq!(max.atomics(), Uint256::MAX); } @@ -1585,9 +1660,18 @@ mod tests { assert_eq!(dec("1000") / a, dec("0.000000000000008121647560868")); assert_eq!(dec("1000000") / a, dec("0.000000000008121647560868164")); assert_eq!(dec("1000000000") / a, dec("0.000000008121647560868164773")); - assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868164773903")); - assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164773903026")); - assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773903026009")); + assert_eq!( + dec("1000000000000") / a, + dec("0.000008121647560868164773903") + ); + assert_eq!( + dec("1000000000000000") / a, + dec("0.008121647560868164773903026") + ); + assert_eq!( + dec("1000000000000000000") / a, + dec("8.121647560868164773903026009") + ); // Move left let a = dec("0.123127726548762582"); @@ -1771,7 +1855,6 @@ mod tests { PrecDec::percent(576480100) ); - assert_eq!( PrecDec::percent(10).checked_pow(2).unwrap(), PrecDec(10000000000000000000000000u128.into()) @@ -2163,13 +2246,19 @@ mod tests { assert_eq!(d.to_uint_floor(), Uint256::from_u128(0)); let d = PrecDec::MAX; - assert_eq!(d.to_uint_floor(), Uint256::from_str("115792089237316195423570985008687907853269984665640").unwrap()); + assert_eq!( + d.to_uint_floor(), + Uint256::from_str("115792089237316195423570985008687907853269984665640").unwrap() + ); // Does the same as the old workaround `Uint256::one() * my_decimal`. // This block can be deleted as part of https://github.com/CosmWasm/cosmwasm/issues/1485. let tests = vec![ (PrecDec::from_str("12.345").unwrap(), Uint256::from(12u128)), - (PrecDec::from_str("0.98451384").unwrap(), Uint256::from(0u128)), + ( + PrecDec::from_str("0.98451384").unwrap(), + Uint256::from(0u128), + ), (PrecDec::from_str("178.0").unwrap(), Uint256::from(178u128)), (PrecDec::MIN, Uint256::from(0u128)), (PrecDec::MAX, Uint256::MAX / PrecDec::DECIMAL_FRACTIONAL), @@ -2194,7 +2283,10 @@ mod tests { assert_eq!(d.to_uint_ceil(), Uint256::from_u128(0)); let d = PrecDec::MAX; - assert_eq!(d.to_uint_ceil(), Uint256::from_str("115792089237316195423570985008687907853269984665641").unwrap()); + assert_eq!( + d.to_uint_ceil(), + Uint256::from_str("115792089237316195423570985008687907853269984665641").unwrap() + ); } #[test] diff --git a/proto-build/src/transform.rs b/proto-build/src/transform.rs index 2104d67..49aa77a 100644 --- a/proto-build/src/transform.rs +++ b/proto-build/src/transform.rs @@ -154,6 +154,7 @@ fn transform_items( let s = transformers::add_derive_eq_struct(&s); let s = transformers::append_attrs_struct(src, &s, descriptor); let s = transformers::serde_alias_id_with_uppercased(s); + let s = transformers::use_precdec_type(s, descriptor); let s = transformers::respect_gogoproto_nullable(s, descriptor); // A hack to make Pagination::next_key optional. // Remove if [this PR](https://github.com/cosmos/cosmos-sdk/pull/20246) is merged and released diff --git a/proto-build/src/transformers.rs b/proto-build/src/transformers.rs index 7820bc9..a8e5af0 100644 --- a/proto-build/src/transformers.rs +++ b/proto-build/src/transformers.rs @@ -41,6 +41,9 @@ pub const REPLACEMENTS: &[(&str, &str)] = &[ // https://github.com/cosmos/gogoproto/blob/28b2facaa30178e137477bcc756a72a7a3c84b6b/gogoproto/gogo.proto#L130 const GOGOPROTO_NULLABLE_EXTENSION_FIELD_NUMBER: u32 = 65001; +// 65003 the number of the `customtype` field +const GOGOPROTO_CUSTOM_TYPE_FIELD_NUMBER: u32 = 65003; + pub fn add_derive_eq(mut attr: Attribute) -> Attribute { // find derive attribute if attr.path.is_ident("derive") { @@ -452,6 +455,125 @@ pub fn respect_gogoproto_nullable( s } +fn get_custom_type(field: protobuf::descriptor::FieldDescriptorProto) -> Option { + let sf = field + .options + .special_fields + .unknown_fields() + .get(GOGOPROTO_CUSTOM_TYPE_FIELD_NUMBER); + + if let Some(custom_type) = sf { + return match custom_type { + protobuf::UnknownValueRef::LengthDelimited(bytes) => { + match String::from_utf8(bytes.to_vec()) { + Ok(decoded_str) => Some(decoded_str), + Err(_) => None, + } + } + _ => None, + }; + } + None +} + +fn is_field_precdec( + message_name: &str, + field_name: &str, + descriptor: &protobuf::descriptor::FileDescriptorSet, +) -> bool { + let prec_dec_re = + Regex::new(r"^github\.com\/neutron-org\/neutron\/v\d+\/utils\/math\.PrecDec").unwrap(); + + for file in descriptor.file.iter() { + for message in file.clone().message_type { + // not a message we are searching for + if message.name() != message_name { + continue; + } + + for field in message.clone().field { + // not a field we are searching for + if field.name() != field_name { + continue; + } + + // check if field is a math.PrecDec + if let Some(custom_type) = get_custom_type(field) { + if prec_dec_re.is_match(&custom_type) { + return true; + } + } + } + } + } + + false +} + +pub fn use_precdec_type( + mut s: ItemStruct, + descriptor: &protobuf::descriptor::FileDescriptorSet, +) -> ItemStruct { + // iterating over the fields in a message + if let Fields::Named(ref mut fields_named) = s.fields { + for field in fields_named.named.iter_mut() { + if let Some(ident) = &field.ident { + // if a underlying field is PrecDec + if is_field_precdec(&s.ident.to_string(), &ident.to_string(), descriptor) { + let is_nullable = is_field_gogoproto_nullable(&s.ident.to_string(), &ident.to_string(), descriptor); + + // add custom PrecDec serializer + let from_str: syn::Attribute = if is_nullable { + parse_quote! { + #[serde( + serialize_with = "crate::serde::as_option_prec_dec::serialize", + deserialize_with = "crate::serde::as_option_prec_dec::deserialize" + )] + } + } else { + parse_quote! { + #[serde( + serialize_with = "crate::serde::as_prec_dec::serialize", + deserialize_with = "crate::serde::as_prec_dec::deserialize" + )] + } + }; + field.attrs.append(&mut vec![from_str]); + // set PrecDec type + field.ty = parse_quote!( + crate::util::precdec::PrecDec + ); + + // update the prost type to `message` + for attr in field.attrs.iter_mut() { + if let Ok(syn::Meta::List(meta_list)) = attr.parse_meta() { + for nested in &meta_list.nested { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) = + nested + { + // Check if the key is `tag` + if name_value.path.is_ident("tag") { + // Extract the value as a string literal + if let syn::Lit::Str(wire_number) = &name_value.lit { + // Prost expects message to be Option<> unless explicitly marked as required + let required_tag = if is_nullable {quote! {} }else {quote! {required,}}; + // replace with the updated attr + *attr = parse_quote! { + #[prost(message, #required_tag tag = #wire_number)] + }; + } + } + } + } + } + } + } + } + } + } + s +} + pub fn make_next_key_optional(mut s: ItemStruct) -> ItemStruct { if s.ident == "PageResponse" { if let Fields::Named(ref mut fields_named) = s.fields { From fc0f18342c970d71f9c582622262a5d263cb4e09 Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Fri, 20 Dec 2024 14:31:51 -0800 Subject: [PATCH 6/9] save test --- .../neutron-std/src/types/neutron/dex/mod.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/neutron-std/src/types/neutron/dex/mod.rs b/packages/neutron-std/src/types/neutron/dex/mod.rs index 2481184..e87c480 100644 --- a/packages/neutron-std/src/types/neutron/dex/mod.rs +++ b/packages/neutron-std/src/types/neutron/dex/mod.rs @@ -2177,3 +2177,34 @@ impl<'a, Q: cosmwasm_std::CustomQuery> DexQuerier<'a, Q> { QuerySimulateMultiHopSwapRequest { msg }.query(self.querier) } } + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use prost::Message; + + use crate::util::precdec::PrecDec; + + use super::*; + #[test] + fn marshall_placelimitorder(){ + let lo = MsgPlaceLimitOrder{ + creator: "test".to_string(), + receiver: "test".to_string(), + tick_index_in_to_out: 0, + limit_sell_price: Some(PrecDec::from_str("1.1").unwrap()), + token_in: "TokenA".to_string(), + token_out: "TokenB".to_string(), + min_average_sell_price: None, + amount_in: "100".to_string(), + order_type: 0, + expiration_time: None, + max_amount_out: None, + + }; + let mut buf = Vec::new(); + lo.encode(&mut buf).unwrap(); + print!("{:?}", buf) + } +} From ef8ba0b2009f8e7f950f106b8aac9881bf7cdb77 Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Mon, 30 Dec 2024 19:02:42 -0700 Subject: [PATCH 7/9] better serialization --- packages/neutron-std/src/serde/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/neutron-std/src/serde/mod.rs b/packages/neutron-std/src/serde/mod.rs index 646774c..43ade1b 100644 --- a/packages/neutron-std/src/serde/mod.rs +++ b/packages/neutron-std/src/serde/mod.rs @@ -232,7 +232,7 @@ fn parse_test() { pub mod as_option_prec_dec { use crate::util::precdec::PrecDec; - use serde::{de, Deserialize, Deserializer, Serializer}; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where @@ -252,7 +252,7 @@ pub mod as_option_prec_dec { S: Serializer, { match value { - Some(p) => serializer.serialize_str(&p.to_prec_dec_string()), + Some(p) => p.atomics().serialize(serializer), None => serializer.serialize_none(), } } @@ -260,7 +260,7 @@ pub mod as_option_prec_dec { pub mod as_prec_dec { use crate::util::precdec::PrecDec; - use serde::{de, Deserialize, Deserializer, Serializer}; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; pub fn deserialize<'de, D>(deserializer: D) -> Result where @@ -275,6 +275,6 @@ pub mod as_prec_dec { where S: Serializer, { - serializer.serialize_str(&value.to_prec_dec_string()) + value.atomics().serialize(serializer) } } From d4aeab98f98cc1d57974c8d75dd35191ed3b96b6 Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Fri, 3 Jan 2025 15:56:14 -0800 Subject: [PATCH 8/9] change prec dec serialization --- packages/neutron-std/src/serde/mod.rs | 5 +++-- packages/neutron-std/src/util/precdec.rs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/neutron-std/src/serde/mod.rs b/packages/neutron-std/src/serde/mod.rs index 43ade1b..9d8714d 100644 --- a/packages/neutron-std/src/serde/mod.rs +++ b/packages/neutron-std/src/serde/mod.rs @@ -252,7 +252,8 @@ pub mod as_option_prec_dec { S: Serializer, { match value { - Some(p) => p.atomics().serialize(serializer), + Some(p) => p.serialize(serializer), + None => serializer.serialize_none(), } } @@ -275,6 +276,6 @@ pub mod as_prec_dec { where S: Serializer, { - value.atomics().serialize(serializer) + value.serialize(serializer) } } diff --git a/packages/neutron-std/src/util/precdec.rs b/packages/neutron-std/src/util/precdec.rs index cc50e04..8a78867 100644 --- a/packages/neutron-std/src/util/precdec.rs +++ b/packages/neutron-std/src/util/precdec.rs @@ -762,7 +762,9 @@ impl Serialize for PrecDec { where S: ser::Serializer, { - serializer.serialize_str(&self.to_string()) + let atomics_str = &self.atomics().to_string(); + + serializer.serialize_str(&atomics_str) } } From f4913092130d661b7beaafc5a496ebfb39c7940e Mon Sep 17 00:00:00 2001 From: Julian Compagni Portis Date: Tue, 7 Jan 2025 16:38:01 -0700 Subject: [PATCH 9/9] save --- .../neutron-std/src/types/neutron/dex/mod.rs | 28 +++++++---- packages/neutron-std/src/util/precdec.rs | 47 ++++++++++++++----- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/packages/neutron-std/src/types/neutron/dex/mod.rs b/packages/neutron-std/src/types/neutron/dex/mod.rs index e87c480..f85577d 100644 --- a/packages/neutron-std/src/types/neutron/dex/mod.rs +++ b/packages/neutron-std/src/types/neutron/dex/mod.rs @@ -2187,24 +2187,32 @@ mod tests { use crate::util::precdec::PrecDec; use super::*; + #[test] - fn marshall_placelimitorder(){ + fn test_marshall_placelimitorder(){ + let price_opt = Some(PrecDec::from_str("1.1").unwrap()); let lo = MsgPlaceLimitOrder{ creator: "test".to_string(), receiver: "test".to_string(), - tick_index_in_to_out: 0, - limit_sell_price: Some(PrecDec::from_str("1.1").unwrap()), token_in: "TokenA".to_string(), token_out: "TokenB".to_string(), - min_average_sell_price: None, - amount_in: "100".to_string(), - order_type: 0, + tick_index_in_to_out: 99, + amount_in: "1".to_string(), + order_type: 4, expiration_time: None, + limit_sell_price: price_opt, max_amount_out: None, - + min_average_sell_price: None, }; - let mut buf = Vec::new(); - lo.encode(&mut buf).unwrap(); - print!("{:?}", buf) + let mut prost_buf = Vec::new(); + lo.encode(&mut prost_buf).unwrap(); + + assert_eq!(vec![10, 4, 116, 101, 115, 116, 18, 4, 116, 101, 115, 116, 26, 6, 84, 111, 107, 101, 110, 65, 34, 6, 84, 111, 107, 101, 110, 66, 40, 99, 58, 1, 49, 64, 4, 90, 28, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48], prost_buf); + + + let decoded: MsgPlaceLimitOrder = MsgPlaceLimitOrder::decode(&prost_buf[..]).unwrap(); + print!("decoded: {:?}", decoded); + + } } diff --git a/packages/neutron-std/src/util/precdec.rs b/packages/neutron-std/src/util/precdec.rs index 8a78867..03378a7 100644 --- a/packages/neutron-std/src/util/precdec.rs +++ b/packages/neutron-std/src/util/precdec.rs @@ -805,7 +805,7 @@ impl Message for PrecDec { B: prost::bytes::BufMut, { let value = self.to_prec_dec_string(); - prost::encoding::string::encode(1, &value, buf); + buf.put_slice(value.as_bytes()); } fn merge_field( @@ -818,24 +818,47 @@ impl Message for PrecDec { where B: prost::bytes::Buf, { - if tag == 1 { - let mut value = String::new(); - prost::encoding::string::merge(wire_type, &mut value, buf, ctx)?; - *self = - PrecDec::from_prec_dec_str(&value).map_err(|e| DecodeError::new(e.to_string()))?; - Ok(()) - } else { - skip_field(wire_type, tag, buf, ctx) + print!("here"); + // Ensure the correct tag + if tag != 1 { + return prost::encoding::skip_field(wire_type, tag, buf, ctx); + } + + // Ensure the correct wire type + if wire_type != WireType::LengthDelimited { + return Err(DecodeError::new(format!( + "Expected WireType::LengthDelimited, got {:?}", + wire_type + ))); + } + + // Read the entire buffer as the field value (bypass length prefix) + let mut value_bytes = Vec::new(); + while buf.has_remaining() { + value_bytes.push(buf.get_u8()); } + + // Convert the raw bytes to a string + let value = String::from_utf8(value_bytes) + .map_err(|_| DecodeError::new("Invalid UTF-8 in PrecDec string"))?; + + // Parse the PrecDec from the string + *self = PrecDec::from_prec_dec_str(&value).map_err(|e| { + DecodeError::new(format!( + "Failed to parse PrecDec from string: {}", + e.to_string() + )) + })?; + + Ok(()) } fn encoded_len(&self) -> usize { - let value = self.to_prec_dec_string(); - prost::encoding::string::encoded_len(1, &value) + self.to_prec_dec_string().as_bytes().len() } fn clear(&mut self) { - *self = PrecDec(Uint256::zero()); + *self = PrecDec::zero(); } }