alloy_primitives/utils/
units.rs

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