kujira_std/
precision.rs

1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{Decimal, Decimal256, Fraction, Uint128, Uint256};
3
4#[cw_serde]
5pub enum Precision {
6    SignificantFigures(u8),
7    DecimalPlaces(u8),
8}
9
10impl Default for Precision {
11    fn default() -> Self {
12        Self::DecimalPlaces(0)
13    }
14}
15
16impl Precision {
17    pub fn validate<T>(&self, other: &T) -> Option<()>
18    where
19        T: Precise + PartialEq,
20    {
21        if &other.round(self) == other {
22            Some(())
23        } else {
24            None
25        }
26    }
27}
28
29pub trait Precise {
30    fn round(&self, other: &Precision) -> Self;
31}
32
33impl Precise for Decimal {
34    fn round(&self, p: &Precision) -> Self {
35        match p {
36            Precision::SignificantFigures(sf) => {
37                let int = self.numerator();
38                let len = int.to_string().as_str().bytes().len() as u32;
39                let decimals: u32 = len - *sf as u32;
40                let pow = Uint128::from(10u128).pow(decimals);
41                let truncated = Uint128::one().mul_floor(Self::from_ratio(int, pow));
42                Self::from_ratio(truncated * pow, self.denominator())
43            }
44            Precision::DecimalPlaces(dp) => {
45                let pow = Uint128::from(10u128).pow(18 - *dp as u32);
46                let x = Self::from_ratio(self.numerator(), self.denominator() * pow);
47                Self::from_ratio(x.numerator() * pow, x.denominator())
48            }
49        }
50    }
51}
52
53impl Precise for Decimal256 {
54    fn round(&self, p: &Precision) -> Self {
55        match p {
56            Precision::SignificantFigures(sf) => {
57                let int = self.numerator();
58                let len = int.to_string().as_str().bytes().len() as u32;
59                let decimals: u32 = len - *sf as u32;
60                let pow = Uint256::from(10u128).pow(decimals);
61                let truncated = Uint256::one().mul_floor(Self::from_ratio(int, pow));
62                Self::from_ratio(truncated * pow, self.denominator())
63            }
64            Precision::DecimalPlaces(dp) => {
65                let pow = Uint256::from(10u128).pow(18 - *dp as u32);
66                let x = Self::from_ratio(self.numerator(), self.denominator() * pow);
67                Self::from_ratio(x.numerator() * pow, x.denominator())
68            }
69        }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use std::str::FromStr;
77
78    #[test]
79    fn test_significant_figures() {
80        let p = Precision::SignificantFigures(2);
81        assert_eq!(p.validate(&Decimal::from_str("123").unwrap()), None);
82        assert_eq!(p.validate(&Decimal::from_str("12").unwrap()), Some(()));
83        assert_eq!(p.validate(&Decimal::from_str("12.3").unwrap()), None);
84        assert_eq!(p.validate(&Decimal::from_str("1.2").unwrap()), Some(()));
85    }
86
87    #[test]
88    fn test_decimal_places() {
89        let p = Precision::DecimalPlaces(2);
90        assert_eq!(p.validate(&Decimal::from_str("123").unwrap()), Some(()));
91        assert_eq!(p.validate(&Decimal::from_str("1.23").unwrap()), Some(()));
92        assert_eq!(p.validate(&Decimal::from_str("12.343").unwrap()), None);
93        assert_eq!(p.validate(&Decimal::from_str("1.2").unwrap()), Some(()));
94    }
95}