stratus/eth/primitives/
wei.rs

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