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
92pub 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#[derive(Debug)]
111pub enum UnitsError {
112 InvalidUnit(String),
114 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
151pub enum ParseUnits {
152 U256(U256),
154 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#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
210pub enum DecimalSeparator {
211 Comma,
213 #[default]
215 Period,
216}
217
218impl DecimalSeparator {
219 #[inline]
221 pub const fn separator(&self) -> char {
222 match self {
223 Self::Comma => ',',
224 Self::Period => '.',
225 }
226 }
227}
228
229impl ParseUnits {
230 #[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 let amount = &amount[..(amount.len() - (dec_len - exponent))];
250 if negative {
251 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 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 pub fn format_units_with(&self, mut unit: Unit, separator: DecimalSeparator) -> String {
285 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 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 pub fn format_units(&self, unit: Unit) -> String {
315 self.format_units_with(unit, DecimalSeparator::Period)
316 }
317
318 #[inline]
320 pub const fn is_signed(&self) -> bool {
321 matches!(self, Self::I256(_))
322 }
323
324 #[inline]
326 pub const fn is_unsigned(&self) -> bool {
327 matches!(self, Self::U256(_))
328 }
329
330 #[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 #[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 #[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 #[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 #[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#[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 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 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 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 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 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 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 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 pub const MIN: Self = Self::WEI;
491 pub const MAX: Self = unsafe { Self::new_unchecked(77) };
493
494 #[inline]
496 pub const fn new(units: u8) -> Option<Self> {
497 if units <= Self::MAX.get() {
498 Some(unsafe { Self::new_unchecked(units) })
500 } else {
501 None
502 }
503 }
504
505 #[inline]
511 pub const unsafe fn new_unchecked(x: u8) -> Self {
512 Self(x)
513 }
514
515 #[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 #[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 #[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 let eth = format_ether(u32::MAX);
639 assert_eq!(eth, "0.000000004294967295");
640
641 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 let eth = format_ether(i32::MIN);
668 assert_eq!(eth, "-0.000000002147483648");
669
670 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 let eth = format_units(u32::MAX, 18).unwrap();
706 assert_eq!(eth, "0.000000004294967295");
707
708 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 let eth = format_units(i32::MIN, 18).unwrap();
752 assert_eq!(eth, "-0.000000002147483648");
753
754 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 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}