alloy_primitives/utils/
units.rs

1use crate::{ParseSignedError, I256, U256};
2use alloc::string::{String, ToString};
3use core::fmt;
4
5const MAX_U64_EXPONENT: u8 = 19;
6
7/// Converts the input to a U256 and converts from Ether to Wei.
8///
9/// # Examples
10///
11/// ```
12/// use alloy_primitives::{
13///     utils::{parse_ether, Unit},
14///     U256,
15/// };
16///
17/// let eth = Unit::ETHER.wei();
18/// assert_eq!(parse_ether("1").unwrap(), eth);
19/// ```
20pub fn parse_ether(eth: &str) -> Result<U256, UnitsError> {
21    ParseUnits::parse_units(eth, Unit::ETHER).map(Into::into)
22}
23
24/// Parses a decimal number and multiplies it with 10^units.
25///
26/// # Examples
27///
28/// ```
29/// use alloy_primitives::{utils::parse_units, U256};
30///
31/// let amount_in_eth = U256::from_str_radix("15230001000000000000", 10).unwrap();
32/// let amount_in_gwei = U256::from_str_radix("15230001000", 10).unwrap();
33/// let amount_in_wei = U256::from_str_radix("15230001000", 10).unwrap();
34/// assert_eq!(amount_in_eth, parse_units("15.230001000000000000", "ether").unwrap().into());
35/// assert_eq!(amount_in_gwei, parse_units("15.230001000000000000", "gwei").unwrap().into());
36/// assert_eq!(amount_in_wei, parse_units("15230001000", "wei").unwrap().into());
37/// ```
38///
39/// Example of trying to parse decimal WEI, which should fail, as WEI is the smallest
40/// ETH denominator. 1 ETH = 10^18 WEI.
41///
42/// ```should_panic
43/// use alloy_primitives::{utils::parse_units, U256};
44/// let amount_in_wei = U256::from_str_radix("15230001000", 10).unwrap();
45/// assert_eq!(amount_in_wei, parse_units("15.230001000000000000", "wei").unwrap().into());
46/// ```
47pub fn parse_units<K, E>(amount: &str, units: K) -> Result<ParseUnits, UnitsError>
48where
49    K: TryInto<Unit, Error = E>,
50    UnitsError: From<E>,
51{
52    ParseUnits::parse_units(amount, units.try_into()?)
53}
54
55/// Formats the given number of Wei as an Ether amount.
56///
57/// # Examples
58///
59/// ```
60/// use alloy_primitives::{utils::format_ether, U256};
61///
62/// let eth = format_ether(1395633240123456000_u128);
63/// assert_eq!(format_ether(1395633240123456000_u128), "1.395633240123456000");
64/// ```
65pub fn format_ether<T: Into<ParseUnits>>(amount: T) -> String {
66    amount.into().format_units(Unit::ETHER)
67}
68
69/// Formats the given number of Wei as the given unit.
70///
71/// # Examples
72///
73/// ```
74/// use alloy_primitives::{utils::format_units, U256};
75///
76/// let eth = U256::from_str_radix("1395633240123456000", 10).unwrap();
77/// assert_eq!(format_units(eth, "eth").unwrap(), "1.395633240123456000");
78///
79/// assert_eq!(format_units(i64::MIN, "gwei").unwrap(), "-9223372036.854775808");
80///
81/// assert_eq!(format_units(i128::MIN, 36).unwrap(), "-170.141183460469231731687303715884105728");
82/// ```
83pub fn format_units<T, K, E>(amount: T, units: K) -> Result<String, UnitsError>
84where
85    T: Into<ParseUnits>,
86    K: TryInto<Unit, Error = E>,
87    UnitsError: From<E>,
88{
89    units.try_into().map(|units| amount.into().format_units(units)).map_err(UnitsError::from)
90}
91
92/// Error type for [`Unit`]-related operations.
93#[derive(Debug)]
94pub enum UnitsError {
95    /// The provided units are not recognized.
96    InvalidUnit(String),
97    /// Overflow when parsing a signed number.
98    ParseSigned(ParseSignedError),
99}
100
101impl core::error::Error for UnitsError {
102    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
103        match self {
104            Self::InvalidUnit(_) => None,
105            Self::ParseSigned(e) => Some(e),
106        }
107    }
108}
109
110impl fmt::Display for UnitsError {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        match self {
113            Self::InvalidUnit(s) => write!(f, "{s:?} is not a valid unit"),
114            Self::ParseSigned(e) => e.fmt(f),
115        }
116    }
117}
118
119impl From<ruint::ParseError> for UnitsError {
120    fn from(value: ruint::ParseError) -> Self {
121        Self::ParseSigned(value.into())
122    }
123}
124
125impl From<ParseSignedError> for UnitsError {
126    fn from(value: ParseSignedError) -> Self {
127        Self::ParseSigned(value)
128    }
129}
130
131/// This enum holds the numeric types that a possible to be returned by `parse_units` and
132/// that are taken by `format_units`.
133#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
134pub enum ParseUnits {
135    /// Unsigned 256-bit integer.
136    U256(U256),
137    /// Signed 256-bit integer.
138    I256(I256),
139}
140
141impl From<ParseUnits> for U256 {
142    #[inline]
143    fn from(value: ParseUnits) -> Self {
144        value.get_absolute()
145    }
146}
147
148impl From<ParseUnits> for I256 {
149    #[inline]
150    fn from(value: ParseUnits) -> Self {
151        value.get_signed()
152    }
153}
154
155impl fmt::Display for ParseUnits {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        match self {
158            Self::U256(val) => val.fmt(f),
159            Self::I256(val) => val.fmt(f),
160        }
161    }
162}
163
164macro_rules! impl_from_integers {
165    ($convert:ident($($t:ty),* $(,)?)) => {$(
166        impl From<$t> for ParseUnits {
167            fn from(value: $t) -> Self {
168                Self::$convert($convert::try_from(value).unwrap())
169            }
170        }
171    )*}
172}
173
174impl_from_integers!(U256(u8, u16, u32, u64, u128, usize, U256));
175impl_from_integers!(I256(i8, i16, i32, i64, i128, isize, I256));
176
177macro_rules! impl_try_into_absolute {
178    ($($t:ty),* $(,)?) => { $(
179        impl TryFrom<ParseUnits> for $t {
180            type Error = <$t as TryFrom<U256>>::Error;
181
182            fn try_from(value: ParseUnits) -> Result<Self, Self::Error> {
183                <$t>::try_from(value.get_absolute())
184            }
185        }
186    )* };
187}
188
189impl_try_into_absolute!(u64, u128);
190
191/// Decimal separator for number formatting
192#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
193pub enum DecimalSeparator {
194    /// Use comma as decimal separator
195    Comma,
196    /// Use period as decimal separator
197    #[default]
198    Period,
199}
200
201impl DecimalSeparator {
202    /// Returns the character used as decimal separator
203    #[inline]
204    pub const fn separator(&self) -> char {
205        match self {
206            Self::Comma => ',',
207            Self::Period => '.',
208        }
209    }
210}
211
212impl ParseUnits {
213    /// Parses a decimal number and multiplies it with 10^units.
214    ///
215    /// See [`parse_units`] for more information.
216    #[allow(clippy::self_named_constructors)]
217    pub fn parse_units(amount: &str, unit: Unit) -> Result<Self, UnitsError> {
218        let exponent = unit.get() as usize;
219
220        let mut amount = amount.to_string();
221        let negative = amount.starts_with('-');
222        let dec_len = if let Some(di) = amount.find('.') {
223            amount.remove(di);
224            amount[di..].len()
225        } else {
226            0
227        };
228        let amount = amount.as_str();
229
230        if dec_len > exponent {
231            // Truncate the decimal part if it is longer than the exponent
232            let amount = &amount[..(amount.len() - (dec_len - exponent))];
233            if negative {
234                // Edge case: We have removed the entire number and only the negative sign is left.
235                //            Return 0 as a I256 given the input was signed.
236                if amount == "-" {
237                    Ok(Self::I256(I256::ZERO))
238                } else {
239                    Ok(Self::I256(I256::from_dec_str(amount)?))
240                }
241            } else {
242                Ok(Self::U256(U256::from_str_radix(amount, 10)?))
243            }
244        } else if negative {
245            // Edge case: Only a negative sign was given, return 0 as a I256 given the input was
246            // signed.
247            if amount == "-" {
248                Ok(Self::I256(I256::ZERO))
249            } else {
250                let mut n = I256::from_dec_str(amount)?;
251                n *= I256::try_from(10u8)
252                    .unwrap()
253                    .checked_pow(U256::from(exponent - dec_len))
254                    .ok_or(UnitsError::ParseSigned(ParseSignedError::IntegerOverflow))?;
255                Ok(Self::I256(n))
256            }
257        } else {
258            let mut a_uint = U256::from_str_radix(amount, 10)?;
259            a_uint *= U256::from(10)
260                .checked_pow(U256::from(exponent - dec_len))
261                .ok_or(UnitsError::ParseSigned(ParseSignedError::IntegerOverflow))?;
262            Ok(Self::U256(a_uint))
263        }
264    }
265
266    /// Formats the given number of Wei as the given unit.
267    pub fn format_units_with(&self, mut unit: Unit, separator: DecimalSeparator) -> String {
268        // Edge case: If the number is signed and the unit is the largest possible unit, we need to
269        //            subtract 1 from the unit to avoid overflow.
270        if self.is_signed() && unit == Unit::MAX {
271            unit = Unit::new(Unit::MAX.get() - 1).unwrap();
272        }
273        let units = unit.get() as usize;
274        let exp10 = unit.wei();
275
276        // TODO: `decimals` are formatted twice because U256 does not support alignment
277        // (`:0>width`).
278        match *self {
279            Self::U256(amount) => {
280                let integer = amount / exp10;
281                let decimals = (amount % exp10).to_string();
282                format!("{integer}{}{decimals:0>units$}", separator.separator())
283            }
284            Self::I256(amount) => {
285                let exp10 = I256::from_raw(exp10);
286                let sign = if amount.is_negative() { "-" } else { "" };
287                let integer = (amount / exp10).twos_complement();
288                let decimals = ((amount % exp10).twos_complement()).to_string();
289                format!("{sign}{integer}{}{decimals:0>units$}", separator.separator())
290            }
291        }
292    }
293
294    /// Formats the given number of Wei as the given unit.
295    ///
296    /// See [`format_units`] for more information.
297    pub fn format_units(&self, unit: Unit) -> String {
298        self.format_units_with(unit, DecimalSeparator::Period)
299    }
300
301    /// Returns `true` if the number is signed.
302    #[inline]
303    pub const fn is_signed(&self) -> bool {
304        matches!(self, Self::I256(_))
305    }
306
307    /// Returns `true` if the number is unsigned.
308    #[inline]
309    pub const fn is_unsigned(&self) -> bool {
310        matches!(self, Self::U256(_))
311    }
312
313    /// Returns `true` if the number is negative.
314    #[inline]
315    pub const fn is_negative(&self) -> bool {
316        match self {
317            Self::U256(_) => false,
318            Self::I256(n) => n.is_negative(),
319        }
320    }
321
322    /// Returns `true` if the number is positive.
323    #[inline]
324    pub const fn is_positive(&self) -> bool {
325        match self {
326            Self::U256(_) => true,
327            Self::I256(n) => n.is_positive(),
328        }
329    }
330
331    /// Returns `true` if the number is zero.
332    #[inline]
333    pub fn is_zero(&self) -> bool {
334        match self {
335            Self::U256(n) => n.is_zero(),
336            Self::I256(n) => n.is_zero(),
337        }
338    }
339
340    /// Returns the absolute value of the number.
341    #[inline]
342    pub const fn get_absolute(self) -> U256 {
343        match self {
344            Self::U256(n) => n,
345            Self::I256(n) => n.into_raw(),
346        }
347    }
348
349    /// Returns the signed value of the number.
350    #[inline]
351    pub const fn get_signed(self) -> I256 {
352        match self {
353            Self::U256(n) => I256::from_raw(n),
354            Self::I256(n) => n,
355        }
356    }
357}
358
359/// Ethereum unit. Always less than [`77`](Unit::MAX).
360#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
361pub struct Unit(u8);
362
363impl fmt::Display for Unit {
364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        self.get().fmt(f)
366    }
367}
368
369impl TryFrom<u8> for Unit {
370    type Error = UnitsError;
371
372    fn try_from(value: u8) -> Result<Self, Self::Error> {
373        Self::new(value).ok_or_else(|| UnitsError::InvalidUnit(value.to_string()))
374    }
375}
376
377impl TryFrom<String> for Unit {
378    type Error = UnitsError;
379
380    fn try_from(value: String) -> Result<Self, Self::Error> {
381        value.parse()
382    }
383}
384
385impl<'a> TryFrom<&'a String> for Unit {
386    type Error = UnitsError;
387
388    fn try_from(value: &'a String) -> Result<Self, Self::Error> {
389        value.parse()
390    }
391}
392
393impl TryFrom<&str> for Unit {
394    type Error = UnitsError;
395
396    fn try_from(value: &str) -> Result<Self, Self::Error> {
397        value.parse()
398    }
399}
400
401impl core::str::FromStr for Unit {
402    type Err = UnitsError;
403
404    fn from_str(s: &str) -> Result<Self, Self::Err> {
405        if let Ok(unit) = crate::U8::from_str(s) {
406            return Self::new(unit.to()).ok_or_else(|| UnitsError::InvalidUnit(s.to_string()));
407        }
408
409        Ok(match s.to_ascii_lowercase().as_str() {
410            "eth" | "ether" => Self::ETHER,
411            "pwei" | "milli" | "milliether" | "finney" => Self::PWEI,
412            "twei" | "micro" | "microether" | "szabo" => Self::TWEI,
413            "gwei" | "nano" | "nanoether" | "shannon" => Self::GWEI,
414            "mwei" | "pico" | "picoether" | "lovelace" => Self::MWEI,
415            "kwei" | "femto" | "femtoether" | "babbage" => Self::KWEI,
416            "wei" => Self::WEI,
417            _ => return Err(UnitsError::InvalidUnit(s.to_string())),
418        })
419    }
420}
421
422impl Unit {
423    /// Wei is equivalent to 1 wei.
424    pub const WEI: Self = unsafe { Self::new_unchecked(0) };
425    #[allow(non_upper_case_globals)]
426    #[doc(hidden)]
427    #[deprecated(since = "0.5.0", note = "use `Unit::WEI` instead")]
428    pub const Wei: Self = Self::WEI;
429
430    /// Kwei is equivalent to 1e3 wei.
431    pub const KWEI: Self = unsafe { Self::new_unchecked(3) };
432    #[allow(non_upper_case_globals)]
433    #[doc(hidden)]
434    #[deprecated(since = "0.5.0", note = "use `Unit::KWEI` instead")]
435    pub const Kwei: Self = Self::KWEI;
436
437    /// Mwei is equivalent to 1e6 wei.
438    pub const MWEI: Self = unsafe { Self::new_unchecked(6) };
439    #[allow(non_upper_case_globals)]
440    #[doc(hidden)]
441    #[deprecated(since = "0.5.0", note = "use `Unit::MWEI` instead")]
442    pub const Mwei: Self = Self::MWEI;
443
444    /// Gwei is equivalent to 1e9 wei.
445    pub const GWEI: Self = unsafe { Self::new_unchecked(9) };
446    #[allow(non_upper_case_globals)]
447    #[doc(hidden)]
448    #[deprecated(since = "0.5.0", note = "use `Unit::GWEI` instead")]
449    pub const Gwei: Self = Self::GWEI;
450
451    /// Twei is equivalent to 1e12 wei.
452    pub const TWEI: Self = unsafe { Self::new_unchecked(12) };
453    #[allow(non_upper_case_globals)]
454    #[doc(hidden)]
455    #[deprecated(since = "0.5.0", note = "use `Unit::TWEI` instead")]
456    pub const Twei: Self = Self::TWEI;
457
458    /// Pwei is equivalent to 1e15 wei.
459    pub const PWEI: Self = unsafe { Self::new_unchecked(15) };
460    #[allow(non_upper_case_globals)]
461    #[doc(hidden)]
462    #[deprecated(since = "0.5.0", note = "use `Unit::PWEI` instead")]
463    pub const Pwei: Self = Self::PWEI;
464
465    /// Ether is equivalent to 1e18 wei.
466    pub const ETHER: Self = unsafe { Self::new_unchecked(18) };
467    #[allow(non_upper_case_globals)]
468    #[doc(hidden)]
469    #[deprecated(since = "0.5.0", note = "use `Unit::ETHER` instead")]
470    pub const Ether: Self = Self::ETHER;
471
472    /// The smallest unit.
473    pub const MIN: Self = Self::WEI;
474    /// The largest unit.
475    pub const MAX: Self = unsafe { Self::new_unchecked(77) };
476
477    /// Creates a new `Unit` instance, checking for overflow.
478    #[inline]
479    pub const fn new(units: u8) -> Option<Self> {
480        if units <= Self::MAX.get() {
481            // SAFETY: `units` is contained in the valid range.
482            Some(unsafe { Self::new_unchecked(units) })
483        } else {
484            None
485        }
486    }
487
488    /// Creates a new `Unit` instance.
489    ///
490    /// # Safety
491    ///
492    /// `x` must be less than [`Unit::MAX`].
493    #[inline]
494    pub const unsafe fn new_unchecked(x: u8) -> Self {
495        Self(x)
496    }
497
498    /// Returns `10^self`, which is the number of Wei in this unit.
499    ///
500    /// # Examples
501    ///
502    /// ```
503    /// use alloy_primitives::{utils::Unit, U256};
504    ///
505    /// assert_eq!(U256::from(1u128), Unit::WEI.wei());
506    /// assert_eq!(U256::from(1_000u128), Unit::KWEI.wei());
507    /// assert_eq!(U256::from(1_000_000u128), Unit::MWEI.wei());
508    /// assert_eq!(U256::from(1_000_000_000u128), Unit::GWEI.wei());
509    /// assert_eq!(U256::from(1_000_000_000_000u128), Unit::TWEI.wei());
510    /// assert_eq!(U256::from(1_000_000_000_000_000u128), Unit::PWEI.wei());
511    /// assert_eq!(U256::from(1_000_000_000_000_000_000u128), Unit::ETHER.wei());
512    /// ```
513    #[inline]
514    pub fn wei(self) -> U256 {
515        if self.get() <= MAX_U64_EXPONENT {
516            self.wei_const()
517        } else {
518            U256::from(10u8).pow(U256::from(self.get()))
519        }
520    }
521
522    /// Returns `10^self`, which is the number of Wei in this unit.
523    ///
524    /// # Panics
525    ///
526    /// Panics if `10^self` would overflow a `u64` (`self > 19`). If this can happen, use
527    /// [`wei`](Self::wei) instead.
528    #[inline]
529    pub const fn wei_const(self) -> U256 {
530        if self.get() > MAX_U64_EXPONENT {
531            panic!("overflow")
532        }
533        U256::from_limbs([10u64.pow(self.get() as u32), 0, 0, 0])
534    }
535
536    /// Returns the numeric value of the unit.
537    #[inline]
538    pub const fn get(self) -> u8 {
539        self.0
540    }
541
542    #[doc(hidden)]
543    #[deprecated(since = "0.5.0", note = "use `get` instead")]
544    pub const fn as_num(&self) -> u8 {
545        self.get()
546    }
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    #[test]
554    fn unit_values() {
555        assert_eq!(Unit::WEI.get(), 0);
556        assert_eq!(Unit::KWEI.get(), 3);
557        assert_eq!(Unit::MWEI.get(), 6);
558        assert_eq!(Unit::GWEI.get(), 9);
559        assert_eq!(Unit::TWEI.get(), 12);
560        assert_eq!(Unit::PWEI.get(), 15);
561        assert_eq!(Unit::ETHER.get(), 18);
562        assert_eq!(Unit::new(10).unwrap().get(), 10);
563        assert_eq!(Unit::new(20).unwrap().get(), 20);
564    }
565
566    #[test]
567    fn unit_wei() {
568        let assert = |unit: Unit| {
569            let wei = unit.wei();
570            assert_eq!(wei.to::<u128>(), 10u128.pow(unit.get() as u32));
571            assert_eq!(wei, U256::from(10u8).pow(U256::from(unit.get())));
572        };
573        assert(Unit::WEI);
574        assert(Unit::KWEI);
575        assert(Unit::MWEI);
576        assert(Unit::GWEI);
577        assert(Unit::TWEI);
578        assert(Unit::PWEI);
579        assert(Unit::ETHER);
580        assert(Unit::new(10).unwrap());
581        assert(Unit::new(20).unwrap());
582    }
583
584    #[test]
585    fn parse() {
586        assert_eq!(Unit::try_from("wei").unwrap(), Unit::WEI);
587        assert_eq!(Unit::try_from("kwei").unwrap(), Unit::KWEI);
588        assert_eq!(Unit::try_from("mwei").unwrap(), Unit::MWEI);
589        assert_eq!(Unit::try_from("gwei").unwrap(), Unit::GWEI);
590        assert_eq!(Unit::try_from("twei").unwrap(), Unit::TWEI);
591        assert_eq!(Unit::try_from("pwei").unwrap(), Unit::PWEI);
592        assert_eq!(Unit::try_from("ether").unwrap(), Unit::ETHER);
593    }
594
595    #[test]
596    fn wei_in_ether() {
597        assert_eq!(Unit::ETHER.wei(), U256::from(1e18 as u64));
598    }
599
600    #[test]
601    fn test_format_ether_unsigned() {
602        let eth = format_ether(Unit::ETHER.wei());
603        assert_eq!(eth.parse::<f64>().unwrap() as u64, 1);
604
605        let eth = format_ether(1395633240123456000_u128);
606        assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
607
608        let eth = format_ether(U256::from_str_radix("1395633240123456000", 10).unwrap());
609        assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
610
611        let eth = format_ether(U256::from_str_radix("1395633240123456789", 10).unwrap());
612        assert_eq!(eth, "1.395633240123456789");
613
614        let eth = format_ether(U256::from_str_radix("1005633240123456789", 10).unwrap());
615        assert_eq!(eth, "1.005633240123456789");
616
617        let eth = format_ether(u16::MAX);
618        assert_eq!(eth, "0.000000000000065535");
619
620        // Note: This covers usize on 32 bit systems.
621        let eth = format_ether(u32::MAX);
622        assert_eq!(eth, "0.000000004294967295");
623
624        // Note: This covers usize on 64 bit systems.
625        let eth = format_ether(u64::MAX);
626        assert_eq!(eth, "18.446744073709551615");
627    }
628
629    #[test]
630    fn test_format_ether_signed() {
631        let eth = format_ether(I256::from_dec_str("-1395633240123456000").unwrap());
632        assert_eq!(eth.parse::<f64>().unwrap(), -1.395633240123456);
633
634        let eth = format_ether(I256::from_dec_str("-1395633240123456789").unwrap());
635        assert_eq!(eth, "-1.395633240123456789");
636
637        let eth = format_ether(I256::from_dec_str("1005633240123456789").unwrap());
638        assert_eq!(eth, "1.005633240123456789");
639
640        let eth = format_ether(i8::MIN);
641        assert_eq!(eth, "-0.000000000000000128");
642
643        let eth = format_ether(i8::MAX);
644        assert_eq!(eth, "0.000000000000000127");
645
646        let eth = format_ether(i16::MIN);
647        assert_eq!(eth, "-0.000000000000032768");
648
649        // Note: This covers isize on 32 bit systems.
650        let eth = format_ether(i32::MIN);
651        assert_eq!(eth, "-0.000000002147483648");
652
653        // Note: This covers isize on 64 bit systems.
654        let eth = format_ether(i64::MIN);
655        assert_eq!(eth, "-9.223372036854775808");
656    }
657
658    #[test]
659    fn test_format_units_unsigned() {
660        let gwei_in_ether = format_units(Unit::ETHER.wei(), 9).unwrap();
661        assert_eq!(gwei_in_ether.parse::<f64>().unwrap() as u64, 1e9 as u64);
662
663        let eth = format_units(Unit::ETHER.wei(), "ether").unwrap();
664        assert_eq!(eth.parse::<f64>().unwrap() as u64, 1);
665
666        let eth = format_units(1395633240123456000_u128, "ether").unwrap();
667        assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
668
669        let eth = format_units(U256::from_str_radix("1395633240123456000", 10).unwrap(), "ether")
670            .unwrap();
671        assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
672
673        let eth = format_units(U256::from_str_radix("1395633240123456789", 10).unwrap(), "ether")
674            .unwrap();
675        assert_eq!(eth, "1.395633240123456789");
676
677        let eth = format_units(U256::from_str_radix("1005633240123456789", 10).unwrap(), "ether")
678            .unwrap();
679        assert_eq!(eth, "1.005633240123456789");
680
681        let eth = format_units(u8::MAX, 4).unwrap();
682        assert_eq!(eth, "0.0255");
683
684        let eth = format_units(u16::MAX, "ether").unwrap();
685        assert_eq!(eth, "0.000000000000065535");
686
687        // Note: This covers usize on 32 bit systems.
688        let eth = format_units(u32::MAX, 18).unwrap();
689        assert_eq!(eth, "0.000000004294967295");
690
691        // Note: This covers usize on 64 bit systems.
692        let eth = format_units(u64::MAX, "gwei").unwrap();
693        assert_eq!(eth, "18446744073.709551615");
694
695        let eth = format_units(u128::MAX, 36).unwrap();
696        assert_eq!(eth, "340.282366920938463463374607431768211455");
697
698        let eth = format_units(U256::MAX, 77).unwrap();
699        assert_eq!(
700            eth,
701            "1.15792089237316195423570985008687907853269984665640564039457584007913129639935"
702        );
703
704        let _err = format_units(U256::MAX, 78).unwrap_err();
705        let _err = format_units(U256::MAX, 79).unwrap_err();
706    }
707
708    #[test]
709    fn test_format_units_signed() {
710        let eth =
711            format_units(I256::from_dec_str("-1395633240123456000").unwrap(), "ether").unwrap();
712        assert_eq!(eth.parse::<f64>().unwrap(), -1.395633240123456);
713
714        let eth =
715            format_units(I256::from_dec_str("-1395633240123456789").unwrap(), "ether").unwrap();
716        assert_eq!(eth, "-1.395633240123456789");
717
718        let eth =
719            format_units(I256::from_dec_str("1005633240123456789").unwrap(), "ether").unwrap();
720        assert_eq!(eth, "1.005633240123456789");
721
722        let eth = format_units(i8::MIN, 4).unwrap();
723        assert_eq!(eth, "-0.0128");
724        assert_eq!(eth.parse::<f64>().unwrap(), -0.0128_f64);
725
726        let eth = format_units(i8::MAX, 4).unwrap();
727        assert_eq!(eth, "0.0127");
728        assert_eq!(eth.parse::<f64>().unwrap(), 0.0127);
729
730        let eth = format_units(i16::MIN, "ether").unwrap();
731        assert_eq!(eth, "-0.000000000000032768");
732
733        // Note: This covers isize on 32 bit systems.
734        let eth = format_units(i32::MIN, 18).unwrap();
735        assert_eq!(eth, "-0.000000002147483648");
736
737        // Note: This covers isize on 64 bit systems.
738        let eth = format_units(i64::MIN, "gwei").unwrap();
739        assert_eq!(eth, "-9223372036.854775808");
740
741        let eth = format_units(i128::MIN, 36).unwrap();
742        assert_eq!(eth, "-170.141183460469231731687303715884105728");
743
744        let eth = format_units(I256::MIN, 76).unwrap();
745        let min = "-5.7896044618658097711785492504343953926634992332820282019728792003956564819968";
746        assert_eq!(eth, min);
747        // doesn't error
748        let eth = format_units(I256::MIN, 77).unwrap();
749        assert_eq!(eth, min);
750
751        let _err = format_units(I256::MIN, 78).unwrap_err();
752        let _err = format_units(I256::MIN, 79).unwrap_err();
753    }
754
755    #[test]
756    fn parse_large_units() {
757        let decimals = 27u8;
758        let val = "10.55";
759
760        let n: U256 = parse_units(val, decimals).unwrap().into();
761        assert_eq!(n.to_string(), "10550000000000000000000000000");
762    }
763
764    #[test]
765    fn test_parse_units() {
766        let gwei: U256 = parse_units("1.5", 9).unwrap().into();
767        assert_eq!(gwei, U256::from(15e8 as u64));
768
769        let token: U256 = parse_units("1163.56926418", 8).unwrap().into();
770        assert_eq!(token, U256::from(116356926418u64));
771
772        let eth_dec_float: U256 = parse_units("1.39563324", "ether").unwrap().into();
773        assert_eq!(eth_dec_float, U256::from_str_radix("1395633240000000000", 10).unwrap());
774
775        let eth_dec_string: U256 = parse_units("1.39563324", "ether").unwrap().into();
776        assert_eq!(eth_dec_string, U256::from_str_radix("1395633240000000000", 10).unwrap());
777
778        let eth: U256 = parse_units("1", "ether").unwrap().into();
779        assert_eq!(eth, Unit::ETHER.wei());
780
781        let val: U256 = parse_units("2.3", "ether").unwrap().into();
782        assert_eq!(val, U256::from_str_radix("2300000000000000000", 10).unwrap());
783
784        let n: U256 = parse_units(".2", 2).unwrap().into();
785        assert_eq!(n, U256::from(20), "leading dot");
786
787        let n: U256 = parse_units("333.21", 2).unwrap().into();
788        assert_eq!(n, U256::from(33321), "trailing dot");
789
790        let n: U256 = parse_units("98766", 16).unwrap().into();
791        assert_eq!(n, U256::from_str_radix("987660000000000000000", 10).unwrap(), "no dot");
792
793        let n: U256 = parse_units("3_3_0", 3).unwrap().into();
794        assert_eq!(n, U256::from(330000), "underscore");
795
796        let n: U256 = parse_units("330", 0).unwrap().into();
797        assert_eq!(n, U256::from(330), "zero decimals");
798
799        let n: U256 = parse_units(".1234", 3).unwrap().into();
800        assert_eq!(n, U256::from(123), "truncate too many decimals");
801
802        assert!(parse_units("1", 80).is_err(), "overflow");
803
804        let two_e30 = U256::from(2) * U256::from_limbs([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]);
805        let n: U256 = parse_units("2", 30).unwrap().into();
806        assert_eq!(n, two_e30, "2e30");
807
808        let n: U256 = parse_units(".33_319_2", 0).unwrap().into();
809        assert_eq!(n, U256::ZERO, "mix");
810
811        let n: U256 = parse_units("", 3).unwrap().into();
812        assert_eq!(n, U256::ZERO, "empty");
813    }
814
815    #[test]
816    fn test_signed_parse_units() {
817        let gwei: I256 = parse_units("-1.5", 9).unwrap().into();
818        assert_eq!(gwei.as_i64(), -15e8 as i64);
819
820        let token: I256 = parse_units("-1163.56926418", 8).unwrap().into();
821        assert_eq!(token.as_i64(), -116356926418);
822
823        let eth_dec_float: I256 = parse_units("-1.39563324", "ether").unwrap().into();
824        assert_eq!(eth_dec_float, I256::from_dec_str("-1395633240000000000").unwrap());
825
826        let eth_dec_string: I256 = parse_units("-1.39563324", "ether").unwrap().into();
827        assert_eq!(eth_dec_string, I256::from_dec_str("-1395633240000000000").unwrap());
828
829        let eth: I256 = parse_units("-1", "ether").unwrap().into();
830        assert_eq!(eth, I256::from_raw(Unit::ETHER.wei()) * I256::MINUS_ONE);
831
832        let val: I256 = parse_units("-2.3", "ether").unwrap().into();
833        assert_eq!(val, I256::from_dec_str("-2300000000000000000").unwrap());
834
835        let n: I256 = parse_units("-.2", 2).unwrap().into();
836        assert_eq!(n, I256::try_from(-20).unwrap(), "leading dot");
837
838        let n: I256 = parse_units("-333.21", 2).unwrap().into();
839        assert_eq!(n, I256::try_from(-33321).unwrap(), "trailing dot");
840
841        let n: I256 = parse_units("-98766", 16).unwrap().into();
842        assert_eq!(n, I256::from_dec_str("-987660000000000000000").unwrap(), "no dot");
843
844        let n: I256 = parse_units("-3_3_0", 3).unwrap().into();
845        assert_eq!(n, I256::try_from(-330000).unwrap(), "underscore");
846
847        let n: I256 = parse_units("-330", 0).unwrap().into();
848        assert_eq!(n, I256::try_from(-330).unwrap(), "zero decimals");
849
850        let n: I256 = parse_units("-.1234", 3).unwrap().into();
851        assert_eq!(n, I256::try_from(-123).unwrap(), "truncate too many decimals");
852
853        assert!(parse_units("-1", 80).is_err(), "overflow");
854
855        let two_e30 = I256::try_from(-2).unwrap()
856            * I256::from_raw(U256::from_limbs([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]));
857        let n: I256 = parse_units("-2", 30).unwrap().into();
858        assert_eq!(n, two_e30, "-2e30");
859
860        let n: I256 = parse_units("-.33_319_2", 0).unwrap().into();
861        assert_eq!(n, I256::ZERO, "mix");
862
863        let n: I256 = parse_units("-", 3).unwrap().into();
864        assert_eq!(n, I256::ZERO, "empty");
865    }
866}