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