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 }
178}