anvil_zksync_types/
numbers.rs

1use zksync_types::U256;
2
3#[derive(Clone, Debug, Eq, PartialEq)]
4pub enum Sign {
5    NonNegative,
6    Negative,
7}
8impl std::fmt::Display for Sign {
9    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10        f.write_str(match self {
11            Sign::NonNegative => "+",
12            Sign::Negative => "-",
13        })
14    }
15}
16
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct SignedU256 {
19    pub sign: Sign,
20    pub inner: U256,
21}
22impl std::fmt::Display for SignedU256 {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        let Self { sign, inner } = self;
25        if *sign == Sign::Negative {
26            write!(f, "-")?;
27        }
28        write!(f, "{inner}")
29    }
30}
31
32impl<T> From<T> for SignedU256
33where
34    T: Into<U256>,
35{
36    fn from(val: T) -> Self {
37        SignedU256 {
38            sign: Sign::NonNegative,
39            inner: val.into(),
40        }
41    }
42}
43
44impl From<SignedU256> for NumberExponentialRepr {
45    /// Formats a U256 number to string, adding an exponential notation _hint_ if it
46    /// is larger than `10_000`, with a precision of `4` figures, and trimming the
47    /// trailing zeros.
48    fn from(val: SignedU256) -> Self {
49        to_exp_notation(val, 4, true)
50    }
51}
52
53pub struct NumberExponentialRepr {
54    pub value: SignedU256,
55    pub mantissa: String,
56    pub exponent: usize,
57}
58
59//////////////////////////////////////////////////////////////////////////////////////
60// Attribution: Function `to_exp_notation`                                         //
61// is adapted from the `foundry-common-fmt` crate.                                 //
62//                                                                                  //
63// Full credit goes to its authors. See the original implementation here:           //
64// https://github.com/foundry-rs/foundry/blob/master/crates/common/fmt/src/exp.rs.  //
65//                                                                                  //
66// Note: These methods are used under the terms of the original project's license.  //
67//////////////////////////////////////////////////////////////////////////////////////
68
69/// Returns the number expressed as a string in exponential notation
70/// with the given precision (number of significant figures),
71/// optionally removing trailing zeros from the mantissa.
72fn to_exp_notation(
73    value: SignedU256,
74    precision: usize,
75    trim_end_zeros: bool,
76) -> NumberExponentialRepr {
77    let stringified = value.inner.to_string();
78    let exponent = stringified.len() - 1;
79    let mut mantissa = stringified.chars().take(precision).collect::<String>();
80
81    // optionally remove trailing zeros
82    if trim_end_zeros {
83        mantissa = mantissa.trim_end_matches('0').to_string();
84    }
85
86    // Place a decimal point only if needed
87    // e.g. 1234 -> 1.234e3 (needed)
88    //      5 -> 5 (not needed)
89    if mantissa.len() > 1 {
90        mantissa.insert(1, '.');
91    }
92    NumberExponentialRepr {
93        value,
94        mantissa,
95        exponent,
96    }
97}
98
99impl std::fmt::Display for NumberExponentialRepr {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        let NumberExponentialRepr {
102            value: SignedU256 { sign, .. },
103            mantissa,
104            exponent,
105        } = self;
106        let sign = if sign == &Sign::Negative { "-" } else { "" };
107        f.write_fmt(format_args!("{sign}{mantissa}e{exponent}"))?;
108        Ok(())
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use crate::numbers::{to_exp_notation, SignedU256};
115
116    #[test]
117    fn test_format_to_exponential_notation() {
118        let value = 1234124124u64;
119
120        let formatted = to_exp_notation(SignedU256::from(value), 4, false);
121        assert_eq!(formatted.to_string(), "1.234e9");
122
123        let formatted = to_exp_notation(SignedU256::from(value), 3, false);
124        assert_eq!(formatted.to_string(), "1.23e9");
125
126        let value = 10000000u64;
127
128        let formatted = to_exp_notation(SignedU256::from(value), 4, false);
129        assert_eq!(formatted.to_string(), "1.000e7");
130
131        let formatted = to_exp_notation(SignedU256::from(value), 3, true);
132        assert_eq!(formatted.to_string(), "1e7");
133    }
134}