stratus/eth/primitives/
wei.rs

1use std::str::FromStr;
2
3use alloy_primitives::U256;
4use anyhow::Context;
5use display_json::DebugAsJson;
6use fake::Dummy;
7use fake::Faker;
8use sqlx::types::BigDecimal;
9
10/// Native token amount in wei.
11#[derive(DebugAsJson, derive_more::Display, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, derive_more::Sub, serde::Serialize, serde::Deserialize)]
12pub struct Wei(pub U256);
13
14impl Wei {
15    pub const ZERO: Wei = Wei(U256::ZERO);
16    pub const ONE: Wei = Wei(U256::ONE);
17    pub const TEST_BALANCE: Wei = Wei(U256::from_limbs([u64::MAX, 0, 0, 0]));
18
19    /// Converts a hexadecimal string to Wei
20    ///
21    /// # Arguments
22    ///
23    /// * `hex` - A hexadecimal string, with or without the "0x" prefix
24    ///
25    /// # Returns
26    ///
27    /// A Result containing the Wei value or an error
28    ///
29    /// # Examples
30    ///
31    /// ```
32    /// use stratus::eth::primitives::Wei;
33    ///
34    /// let wei = Wei::from_hex_str("1a").unwrap();
35    /// assert_eq!(wei, Wei::from(26u64));
36    ///
37    /// let wei = Wei::from_hex_str("0x1a").unwrap();
38    /// assert_eq!(wei, Wei::from(26u64));
39    /// ```
40    pub fn from_hex_str(hex: &str) -> Result<Self, anyhow::Error> {
41        let hex = hex.trim_start_matches("0x");
42        let u256 = U256::from_str_radix(hex, 16)?;
43        Ok(Self(u256))
44    }
45
46    /// Alias for from_hex_str for backward compatibility
47    #[deprecated(since = "0.20.1", note = "Use from_hex_str instead")]
48    pub fn from_str_hex(hex: &str) -> Result<Self, anyhow::Error> {
49        Self::from_hex_str(hex)
50    }
51}
52
53impl Dummy<Faker> for Wei {
54    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
55        rng.next_u64().into()
56    }
57}
58
59// -----------------------------------------------------------------------------
60// Conversions: Other -> Self
61// -----------------------------------------------------------------------------
62
63impl From<u8> for Wei {
64    fn from(value: u8) -> Self {
65        Self(U256::from(value))
66    }
67}
68
69impl From<u16> for Wei {
70    fn from(value: u16) -> Self {
71        Self(U256::from(value))
72    }
73}
74
75impl From<u32> for Wei {
76    fn from(value: u32) -> Self {
77        Self(U256::from(value))
78    }
79}
80
81impl From<u64> for Wei {
82    fn from(value: u64) -> Self {
83        Self(U256::from(value))
84    }
85}
86
87impl From<u128> for Wei {
88    fn from(value: u128) -> Self {
89        Self(U256::from(value))
90    }
91}
92
93impl From<U256> for Wei {
94    fn from(value: U256) -> Self {
95        Self(value)
96    }
97}
98
99impl From<usize> for Wei {
100    fn from(value: usize) -> Self {
101        Self(U256::from(value))
102    }
103}
104
105impl From<i32> for Wei {
106    fn from(value: i32) -> Self {
107        Self(U256::from(value as u32))
108    }
109}
110
111impl From<[u64; 4]> for Wei {
112    fn from(value: [u64; 4]) -> Self {
113        Self(U256::from_limbs(value))
114    }
115}
116
117impl TryFrom<BigDecimal> for Wei {
118    type Error = anyhow::Error;
119
120    fn try_from(value: BigDecimal) -> Result<Self, Self::Error> {
121        let value_str = value.to_string();
122        Ok(Wei(U256::from_str_radix(&value_str, 10)?))
123    }
124}
125
126// -----------------------------------------------------------------------------
127// Conversions: Self -> Other
128// -----------------------------------------------------------------------------
129
130impl TryFrom<Wei> for u128 {
131    type Error = anyhow::Error;
132
133    fn try_from(value: Wei) -> Result<Self, Self::Error> {
134        u128::try_from(value.0).context("wei conversion failed")
135    }
136}
137
138impl From<Wei> for U256 {
139    fn from(value: Wei) -> Self {
140        value.0
141    }
142}
143
144impl TryFrom<Wei> for BigDecimal {
145    type Error = anyhow::Error;
146    fn try_from(value: Wei) -> Result<Self, Self::Error> {
147        // HACK: If we could import BigInt or BigUint we could convert the bytes directly.
148        Ok(BigDecimal::from_str(&value.0.to_string())?)
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    // #[test]
157    // fn big_decimal_to_nonce_conversion() {
158    //     // Test with a simple value
159    //     let big_decimal = BigDecimal::new(1.into(), -4);
160    //     let nonce: Wei = big_decimal.clone().try_into().unwrap();
161    //     let expected = nonce.0.as_u64();
162    //     assert_eq!(10000, expected);
163    // }
164
165    #[test]
166    fn test_from_hex_str() {
167        // Test with a simple value without 0x prefix
168        let wei = Wei::from_hex_str("1a").unwrap();
169        assert_eq!(wei, Wei::from(26u64));
170
171        // Test with 0x prefix
172        let wei = Wei::from_hex_str("0x1a").unwrap();
173        assert_eq!(wei, Wei::from(26u64));
174
175        // Test with a larger value
176        let wei = Wei::from_hex_str("0xffff").unwrap();
177        assert_eq!(wei, Wei::from(65535u64));
178    }
179}