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 35ab2fa..9d8714d 100644 --- a/packages/neutron-std/src/serde/mod.rs +++ b/packages/neutron-std/src/serde/mod.rs @@ -14,8 +14,8 @@ use std::{ struct StringOrNumberVisitor { p: PhantomData, } -// The Visitor helps deserialize a number, both from its numerical JSON representation and from a string. -// For example, for the struct: +// The Visitor helps deserialize a number, both from its numerical JSON representation and from a string. +// For example, for the struct: // ```rust // struct Foo { // pub bar: i32; @@ -205,11 +205,11 @@ pub mod as_option_base64_encoded_string { } -// NumberOrString is a helper enum that helps us determine which -// JSON numeric representation we are working with. If it's a string, -// we will get the value `NumberOrString::String("-11")`. -// If we are dealing with a numeric representation, -// we will get `NumberOrString::Number(-11i64)`. +// NumberOrString is a helper enum that helps us determine which +// JSON numeric representation we are working with. If it's a string, +// we will get the value `NumberOrString::String("-11")`. +// If we are dealing with a numeric representation, +// we will get `NumberOrString::Number(-11i64)`. // Then, using pattern matching, we can select the appropriate algorithm to work with the data. #[derive(Deserialize, Debug, PartialEq)] #[serde(untagged)] @@ -228,3 +228,54 @@ fn parse_test() { let res = serde_json_wasm::from_str::>(str).unwrap(); assert_eq!(res, NumberOrString::String("-11".to_string())); } + +pub mod as_option_prec_dec { + + use crate::util::precdec::PrecDec; + use serde::{de, Deserialize, Deserializer, Serialize, 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) => p.serialize(serializer), + + None => serializer.serialize_none(), + } + } +} + +pub mod as_prec_dec { + use crate::util::precdec::PrecDec; + use serde::{de, Deserialize, Deserializer, Serialize, 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, + { + value.serialize(serializer) + } +} diff --git a/packages/neutron-std/src/types/neutron/dex/mod.rs b/packages/neutron-std/src/types/neutron/dex/mod.rs index b117d8e..f85577d 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 { @@ -2141,3 +2177,42 @@ 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 test_marshall_placelimitorder(){ + let price_opt = Some(PrecDec::from_str("1.1").unwrap()); + let lo = MsgPlaceLimitOrder{ + creator: "test".to_string(), + receiver: "test".to_string(), + token_in: "TokenA".to_string(), + token_out: "TokenB".to_string(), + 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 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/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/util/forward_ref.rs b/packages/neutron-std/src/util/forward_ref.rs new file mode 100644 index 0000000..d639b65 --- /dev/null +++ b/packages/neutron-std/src/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/util/mod.rs b/packages/neutron-std/src/util/mod.rs new file mode 100644 index 0000000..bcaca70 --- /dev/null +++ b/packages/neutron-std/src/util/mod.rs @@ -0,0 +1,2 @@ +mod forward_ref; +pub mod precdec; diff --git a/packages/neutron-std/src/util/precdec.rs b/packages/neutron-std/src/util/precdec.rs new file mode 100644 index 0000000..03378a7 --- /dev/null +++ b/packages/neutron-std/src/util/precdec.rs @@ -0,0 +1,2349 @@ +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 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) +#[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("PrecDec range exceeded")] +pub struct PrecDecRangeExceeded; + +#[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; + /// 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 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 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 PrecDec + #[inline] + pub const fn one() -> Self { + Self(Self::DECIMAL_FRACTIONAL) + } + + /// Create a 0.0 PrecDec + #[inline] + pub const fn zero() -> Self { + Self(Uint256::zero()) + } + + /// Convert x% into PrecDec + /// + /// ## Examples + /// + /// ``` + /// # use std::str::FromStr; + /// const HALF: PrecDec = PrecDec::percent(50); + /// + /// assert_eq!(HALF, PrecDec::from_str("0.5").unwrap()); + /// ``` + 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; + /// const HALF: PrecDec = PrecDec::permille(500); + /// + /// assert_eq!(HALF, PrecDec::from_str("0.5").unwrap()); + /// ``` + + 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 PrecDec + /// + /// ## Examples + /// + /// ``` + /// # use std::str::FromStr; + /// const TWO_BPS: PrecDec = PrecDec::bps(2); + /// const HALF: PrecDec = PrecDec::bps(5000); + /// + /// assert_eq!(TWO_BPS, PrecDec::from_str("0.0002").unwrap()); + /// assert_eq!(HALF, PrecDec::from_str("0.5").unwrap()); + /// ``` + + 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 27 decimal places. So the input 123 and 2 will create + /// the decimal 1.23. + /// + /// + /// ## Examples + /// + /// ``` + /// # use cosmwasm_std::{Uint128}; + /// let a = PrecDec::from_atomics(Uint128::new(1234), 3).unwrap(); + /// assert_eq!(a.to_string(), "1.234"); + /// + /// let a = PrecDec::from_atomics(1234u128, 0).unwrap(); + /// assert_eq!(a.to_string(), "1234"); + /// + /// 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 { + 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 <= 27 + Self( + atomics + .checked_mul(factor) + .map_err(|_| PrecDecRangeExceeded)?, + ) + } + 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 PrecDec + 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 PrecDec + 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::{Uint128}; + /// # use core::str::FromStr; + /// // Value with whole and fractional part + /// 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 = PrecDec::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 `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" + 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 PrecDec. + /// + /// 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(u256::MAX) * 10^13` 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 = Uint256::from(10u128).pow(precision * 2 + 1); + 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()) + }) + } + + #[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::{Uint128}; + /// + /// let d = PrecDec::from_str("12.345").unwrap(); + /// assert_eq!(d.to_uint_floor(), Uint128::new(12)); + /// + /// let d = PrecDec::from_str("12.999").unwrap(); + /// assert_eq!(d.to_uint_floor(), Uint128::new(12)); + /// + /// 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"] + 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::{Uint128}; + /// + /// let d = PrecDec::from_str("12.345").unwrap(); + /// assert_eq!(d.to_uint_ceil(), Uint128::new(13)); + /// + /// let d = PrecDec::from_str("12.999").unwrap(); + /// assert_eq!(d.to_uint_ceil(), Uint128::new(13)); + /// + /// 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"] + 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) + } + } + + #[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() + } + + #[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)) + } +} + +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 + .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`. + Some(PrecDec(decimal_fractional_squared / self.0)) + } + } +} + +impl TryFrom for PrecDec { + type Error = PrecDecRangeExceeded; + + fn try_from(value: Decimal256) -> Result { + Self::from_atomics(value.atomics(), Decimal256::DECIMAL_PLACES) + } +} + +impl TryFrom for PrecDec { + type Error = PrecDecRangeExceeded; + + fn try_from(value: SignedDecimal) -> Result { + let atomics: Uint256 = value + .atomics() + .try_into() + .map_err(|_| PrecDecRangeExceeded)?; + Self::from_atomics(atomics, SignedDecimal::DECIMAL_PLACES) + } +} + +impl TryFrom for PrecDec { + type Error = PrecDecRangeExceeded; + + fn try_from(value: SignedDecimal256) -> Result { + let atomics: Uint256 = value + .atomics() + .try_into() + .map_err(|_| PrecDecRangeExceeded)?; + Self::from_atomics(atomics, SignedDecimal::DECIMAL_PLACES) + } +} + +impl TryFrom for PrecDec { + type Error = PrecDecRangeExceeded; + + #[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 PrecDec + /// 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, "PrecDec({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 { + // 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() + + 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, + { + let atomics_str = &self.atomics().to_string(); + + serializer.serialize_str(&atomics_str) + } +} + +/// Deserializes as a base64 string +impl<'de> Deserialize<'de> for PrecDec { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(PrecDecVisitor) + } +} + +struct PrecDecVisitor; + +impl<'de> de::Visitor<'de> for PrecDecVisitor { + 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}"))), + } + } +} + +// 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(); + buf.put_slice(value.as_bytes()); + } + + fn merge_field( + &mut self, + tag: u32, + wire_type: WireType, + buf: &mut B, + ctx: prost::encoding::DecodeContext, + ) -> Result<(), DecodeError> + where + B: prost::bytes::Buf, + { + 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 { + self.to_prec_dec_string().as_bytes().len() + } + + fn clear(&mut self) { + *self = PrecDec::zero(); + } +} + +#[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("340282366920938463463.374607431768211455").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::from_str("170141183460469231731.687303715884105727").unwrap() + ); + 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.414213562373095048801688724").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.456322602596803227841789792").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); + } + } +} 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 {