1use crate::{I256, ParseSignedError, 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>
57where
58 K: TryInto<Unit, Error = E>,
59 UnitsError: From<E>,
60{
61 ParseUnits::parse_units(amount, units.try_into()?)
62}
63
64pub fn format_ether<T: Into<ParseUnits>>(amount: T) -> String {
75 amount.into().format_units(Unit::ETHER)
76}
77
78pub 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
101pub 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#[derive(Debug)]
120pub enum UnitsError {
121 InvalidUnit(String),
123 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
160pub enum ParseUnits {
161 U256(U256),
163 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#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
219pub enum DecimalSeparator {
220 Comma,
222 #[default]
224 Period,
225}
226
227impl DecimalSeparator {
228 #[inline]
230 pub const fn separator(&self) -> char {
231 match self {
232 Self::Comma => ',',
233 Self::Period => '.',
234 }
235 }
236}
237
238impl ParseUnits {
239 #[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 let amount = &amount[..(amount.len() - (dec_len - exponent))];
259 if negative {
260 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 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 pub fn format_units_with(&self, mut unit: Unit, separator: DecimalSeparator) -> String {
294 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 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 pub fn format_units(&self, unit: Unit) -> String {
324 self.format_units_with(unit, DecimalSeparator::Period)
325 }
326
327 #[inline]
329 pub const fn is_signed(&self) -> bool {
330 matches!(self, Self::I256(_))
331 }
332
333 #[inline]
335 pub const fn is_unsigned(&self) -> bool {
336 matches!(self, Self::U256(_))
337 }
338
339 #[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 #[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 #[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 #[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 #[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#[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 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 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 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 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 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 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 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 pub const MIN: Self = Self::WEI;
500 pub const MAX: Self = unsafe { Self::new_unchecked(77) };
502
503 #[inline]
505 pub const fn new(units: u8) -> Option<Self> {
506 if units <= Self::MAX.get() {
507 Some(unsafe { Self::new_unchecked(units) })
509 } else {
510 None
511 }
512 }
513
514 #[inline]
520 pub const unsafe fn new_unchecked(x: u8) -> Self {
521 Self(x)
522 }
523
524 #[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 #[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 #[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 let eth = format_ether(u32::MAX);
648 assert_eq!(eth, "0.000000004294967295");
649
650 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 let eth = format_ether(i32::MIN);
677 assert_eq!(eth, "-0.000000002147483648");
678
679 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 let eth = format_units(u32::MAX, 18).unwrap();
715 assert_eq!(eth, "0.000000004294967295");
716
717 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 let eth = format_units(i32::MIN, 18).unwrap();
761 assert_eq!(eth, "-0.000000002147483648");
762
763 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 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}