1use crate::{ParseSignedError, I256, U256};
2use alloc::string::{String, ToString};
3use core::fmt;
4
5const MAX_U64_EXPONENT: u8 = 19;
6
7pub fn parse_ether(eth: &str) -> Result<U256, UnitsError> {
21 ParseUnits::parse_units(eth, Unit::ETHER).map(Into::into)
22}
23
24pub 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
55pub fn format_ether<T: Into<ParseUnits>>(amount: T) -> String {
66 amount.into().format_units(Unit::ETHER)
67}
68
69pub 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#[derive(Debug)]
94pub enum UnitsError {
95 InvalidUnit(String),
97 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
134pub enum ParseUnits {
135 U256(U256),
137 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#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
193pub enum DecimalSeparator {
194 Comma,
196 #[default]
198 Period,
199}
200
201impl DecimalSeparator {
202 #[inline]
204 pub const fn separator(&self) -> char {
205 match self {
206 Self::Comma => ',',
207 Self::Period => '.',
208 }
209 }
210}
211
212impl ParseUnits {
213 #[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 let amount = &amount[..(amount.len() - (dec_len - exponent))];
233 if negative {
234 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 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 pub fn format_units_with(&self, mut unit: Unit, separator: DecimalSeparator) -> String {
268 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 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 pub fn format_units(&self, unit: Unit) -> String {
298 self.format_units_with(unit, DecimalSeparator::Period)
299 }
300
301 #[inline]
303 pub const fn is_signed(&self) -> bool {
304 matches!(self, Self::I256(_))
305 }
306
307 #[inline]
309 pub const fn is_unsigned(&self) -> bool {
310 matches!(self, Self::U256(_))
311 }
312
313 #[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 #[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 #[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 #[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 #[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#[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 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 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 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 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 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 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 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 pub const MIN: Self = Self::WEI;
474 pub const MAX: Self = unsafe { Self::new_unchecked(77) };
476
477 #[inline]
479 pub const fn new(units: u8) -> Option<Self> {
480 if units <= Self::MAX.get() {
481 Some(unsafe { Self::new_unchecked(units) })
483 } else {
484 None
485 }
486 }
487
488 #[inline]
494 pub const unsafe fn new_unchecked(x: u8) -> Self {
495 Self(x)
496 }
497
498 #[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 #[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 #[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 let eth = format_ether(u32::MAX);
622 assert_eq!(eth, "0.000000004294967295");
623
624 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 let eth = format_ether(i32::MIN);
651 assert_eq!(eth, "-0.000000002147483648");
652
653 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 let eth = format_units(u32::MAX, 18).unwrap();
689 assert_eq!(eth, "0.000000004294967295");
690
691 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 let eth = format_units(i32::MIN, 18).unwrap();
735 assert_eq!(eth, "-0.000000002147483648");
736
737 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 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}