ethers_types_rs/
unit.rs

1use std::{fmt::Display, str::FromStr};
2
3use ethabi::ethereum_types::U256;
4use num::{BigInt, BigRational, ToPrimitive};
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, thiserror::Error)]
9pub enum UnitError {
10    #[error("Parse ethereum unit from float string literal failed. {0}")]
11    ParseFloatString(String),
12}
13
14pub trait EthereumUnit {
15    fn decimals() -> usize;
16
17    fn new(v: U256) -> Self;
18
19    fn to_u256(self) -> U256;
20}
21
22macro_rules! def_eth_unit {
23    ($name: ident, $decimals: literal) => {
24        #[derive(Clone, Deserialize, Serialize)]
25        pub struct $name(pub U256);
26
27        impl EthereumUnit for $name {
28            fn decimals() -> usize {
29                $decimals
30            }
31
32            fn new(v: U256) -> Self {
33                Self(v)
34            }
35
36            fn to_u256(self) -> U256 {
37                self.0
38            }
39        }
40
41        impl $name {
42            pub fn unit_to<T: EthereumUnit>(&self) -> T {
43                T::new(self.0.clone())
44            }
45        }
46
47        impl From<u128> for $name {
48            fn from(v: u128) -> Self {
49                $name(U256::from(v))
50            }
51        }
52
53        impl From<u64> for $name {
54            fn from(v: u64) -> Self {
55                $name(U256::from(v))
56            }
57        }
58
59        impl From<u32> for $name {
60            fn from(v: u32) -> Self {
61                $name(U256::from(v))
62            }
63        }
64
65        impl From<u16> for $name {
66            fn from(v: u16) -> Self {
67                $name(U256::from(v))
68            }
69        }
70
71        impl From<u8> for $name {
72            fn from(v: u8) -> Self {
73                $name(U256::from(v))
74            }
75        }
76
77        impl FromStr for $name {
78            type Err = anyhow::Error;
79            fn from_str(s: &str) -> Result<Self, Self::Err> {
80                let v: f64 = s.parse()?;
81
82                let v =
83                    BigRational::from_float(v).ok_or(UnitError::ParseFloatString(s.to_owned()))?;
84
85                let exponent = BigInt::from(10u32).pow($name::decimals() as u32);
86
87                let v = v * exponent;
88
89                log::debug!("{:#x}", v);
90
91                let v = v.floor();
92
93                log::debug!("{:#x}", v);
94
95                let v = v.to_integer();
96
97                log::debug!("{:#x}", v);
98
99                let (_, bytes) = v.to_bytes_be();
100
101                Ok($name(U256::from_big_endian(&bytes)))
102            }
103        }
104
105        impl<'a> TryFrom<&'a str> for $name {
106            type Error = anyhow::Error;
107            fn try_from(value: &'a str) -> Result<Self, Self::Error> {
108                value.parse()
109            }
110        }
111
112        impl From<$name> for String {
113            fn from(v: $name) -> Self {
114                let mut bytes = vec![0; 32];
115
116                v.0.to_big_endian(&mut bytes);
117
118                let v = BigInt::from_bytes_be(num::bigint::Sign::Plus, &bytes);
119
120                let v = BigRational::from_integer(v);
121
122                let v = v / BigInt::from(10u32).pow($name::decimals() as u32);
123
124                format!(
125                    "{}",
126                    v.numer().to_f64().unwrap() / v.denom().to_f64().unwrap()
127                )
128            }
129        }
130
131        impl From<$name> for U256 {
132            fn from(v: $name) -> Self {
133                v.0
134            }
135        }
136
137        impl Display for $name {
138            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139                let s: String = String::from(self.clone());
140
141                write!(f, "{}", s)
142            }
143        }
144    };
145}
146
147def_eth_unit!(Wei, 0);
148def_eth_unit!(Kwei, 3);
149def_eth_unit!(Mwei, 6);
150def_eth_unit!(Gwei, 9);
151def_eth_unit!(Szabo, 12);
152def_eth_unit!(Finney, 15);
153def_eth_unit!(Ether, 18);
154
155#[cfg(test)]
156mod tests {
157
158    use crate::*;
159
160    #[test]
161    fn test_convert() {
162        _ = pretty_env_logger::try_init();
163
164        let v = "1.1".parse::<Gwei>().expect("Parse ether");
165
166        let gwei = serde_json::to_string(&v).expect("");
167
168        let v: Wei = v.unit_to();
169
170        let wei = serde_json::to_string(&v).expect("");
171
172        assert_eq!(gwei, wei);
173
174        log::debug!("{}", gwei);
175
176        // let expected = U256::from_dec_str("1100000000000000000").expect("Parse u256");
177    }
178}