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
101#[cfg(feature = "std")]
102impl std::error::Error for UnitsError {
103 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
104 match self {
105 Self::InvalidUnit(_) => None,
106 Self::ParseSigned(e) => Some(e),
107 }
108 }
109}
110
111impl fmt::Display for UnitsError {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self {
114 Self::InvalidUnit(s) => write!(f, "{s:?} is not a valid unit"),
115 Self::ParseSigned(e) => e.fmt(f),
116 }
117 }
118}
119
120impl From<ruint::ParseError> for UnitsError {
121 fn from(value: ruint::ParseError) -> Self {
122 Self::ParseSigned(value.into())
123 }
124}
125
126impl From<ParseSignedError> for UnitsError {
127 fn from(value: ParseSignedError) -> Self {
128 Self::ParseSigned(value)
129 }
130}
131
132#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
135pub enum ParseUnits {
136 U256(U256),
138 I256(I256),
140}
141
142impl From<ParseUnits> for U256 {
143 #[inline]
144 fn from(value: ParseUnits) -> Self {
145 value.get_absolute()
146 }
147}
148
149impl From<ParseUnits> for I256 {
150 #[inline]
151 fn from(value: ParseUnits) -> Self {
152 value.get_signed()
153 }
154}
155
156impl fmt::Display for ParseUnits {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::U256(val) => val.fmt(f),
160 Self::I256(val) => val.fmt(f),
161 }
162 }
163}
164
165macro_rules! impl_from_integers {
166 ($convert:ident($($t:ty),* $(,)?)) => {$(
167 impl From<$t> for ParseUnits {
168 fn from(value: $t) -> Self {
169 Self::$convert($convert::try_from(value).unwrap())
170 }
171 }
172 )*}
173}
174
175impl_from_integers!(U256(u8, u16, u32, u64, u128, usize, U256));
176impl_from_integers!(I256(i8, i16, i32, i64, i128, isize, I256));
177
178macro_rules! impl_try_into_absolute {
179 ($($t:ty),* $(,)?) => { $(
180 impl TryFrom<ParseUnits> for $t {
181 type Error = <$t as TryFrom<U256>>::Error;
182
183 fn try_from(value: ParseUnits) -> Result<Self, Self::Error> {
184 <$t>::try_from(value.get_absolute())
185 }
186 }
187 )* };
188}
189
190impl_try_into_absolute!(u64, u128);
191
192impl ParseUnits {
193 #[allow(clippy::self_named_constructors)]
197 pub fn parse_units(amount: &str, unit: Unit) -> Result<Self, UnitsError> {
198 let exponent = unit.get() as usize;
199
200 let mut amount = amount.to_string();
201 let negative = amount.starts_with('-');
202 let dec_len = if let Some(di) = amount.find('.') {
203 amount.remove(di);
204 amount[di..].len()
205 } else {
206 0
207 };
208 let amount = amount.as_str();
209
210 if dec_len > exponent {
211 let amount = &amount[..(amount.len() - (dec_len - exponent))];
213 if negative {
214 if amount == "-" {
217 Ok(Self::I256(I256::ZERO))
218 } else {
219 Ok(Self::I256(I256::from_dec_str(amount)?))
220 }
221 } else {
222 Ok(Self::U256(U256::from_str_radix(amount, 10)?))
223 }
224 } else if negative {
225 if amount == "-" {
228 Ok(Self::I256(I256::ZERO))
229 } else {
230 let mut n = I256::from_dec_str(amount)?;
231 n *= I256::try_from(10u8)
232 .unwrap()
233 .checked_pow(U256::from(exponent - dec_len))
234 .ok_or(UnitsError::ParseSigned(ParseSignedError::IntegerOverflow))?;
235 Ok(Self::I256(n))
236 }
237 } else {
238 let mut a_uint = U256::from_str_radix(amount, 10)?;
239 a_uint *= U256::from(10)
240 .checked_pow(U256::from(exponent - dec_len))
241 .ok_or(UnitsError::ParseSigned(ParseSignedError::IntegerOverflow))?;
242 Ok(Self::U256(a_uint))
243 }
244 }
245
246 pub fn format_units(&self, mut unit: Unit) -> String {
250 if self.is_signed() && unit == Unit::MAX {
253 unit = Unit::new(Unit::MAX.get() - 1).unwrap();
254 }
255 let units = unit.get() as usize;
256 let exp10 = unit.wei();
257
258 match *self {
261 Self::U256(amount) => {
262 let integer = amount / exp10;
263 let decimals = (amount % exp10).to_string();
264 format!("{integer}.{decimals:0>units$}")
265 }
266 Self::I256(amount) => {
267 let exp10 = I256::from_raw(exp10);
268 let sign = if amount.is_negative() { "-" } else { "" };
269 let integer = (amount / exp10).twos_complement();
270 let decimals = ((amount % exp10).twos_complement()).to_string();
271 format!("{sign}{integer}.{decimals:0>units$}")
272 }
273 }
274 }
275
276 #[inline]
278 pub const fn is_signed(&self) -> bool {
279 matches!(self, Self::I256(_))
280 }
281
282 #[inline]
284 pub const fn is_unsigned(&self) -> bool {
285 matches!(self, Self::U256(_))
286 }
287
288 #[inline]
290 pub const fn is_negative(&self) -> bool {
291 match self {
292 Self::U256(_) => false,
293 Self::I256(n) => n.is_negative(),
294 }
295 }
296
297 #[inline]
299 pub const fn is_positive(&self) -> bool {
300 match self {
301 Self::U256(_) => true,
302 Self::I256(n) => n.is_positive(),
303 }
304 }
305
306 #[inline]
308 pub fn is_zero(&self) -> bool {
309 match self {
310 Self::U256(n) => n.is_zero(),
311 Self::I256(n) => n.is_zero(),
312 }
313 }
314
315 #[inline]
317 pub const fn get_absolute(self) -> U256 {
318 match self {
319 Self::U256(n) => n,
320 Self::I256(n) => n.into_raw(),
321 }
322 }
323
324 #[inline]
326 pub const fn get_signed(self) -> I256 {
327 match self {
328 Self::U256(n) => I256::from_raw(n),
329 Self::I256(n) => n,
330 }
331 }
332}
333
334#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
336pub struct Unit(u8);
337
338impl fmt::Display for Unit {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 self.get().fmt(f)
341 }
342}
343
344impl TryFrom<u8> for Unit {
345 type Error = UnitsError;
346
347 fn try_from(value: u8) -> Result<Self, Self::Error> {
348 Self::new(value).ok_or_else(|| UnitsError::InvalidUnit(value.to_string()))
349 }
350}
351
352impl TryFrom<String> for Unit {
353 type Error = UnitsError;
354
355 fn try_from(value: String) -> Result<Self, Self::Error> {
356 value.parse()
357 }
358}
359
360impl<'a> TryFrom<&'a String> for Unit {
361 type Error = UnitsError;
362
363 fn try_from(value: &'a String) -> Result<Self, Self::Error> {
364 value.parse()
365 }
366}
367
368impl TryFrom<&str> for Unit {
369 type Error = UnitsError;
370
371 fn try_from(value: &str) -> Result<Self, Self::Error> {
372 value.parse()
373 }
374}
375
376impl core::str::FromStr for Unit {
377 type Err = UnitsError;
378
379 fn from_str(s: &str) -> Result<Self, Self::Err> {
380 if let Ok(unit) = crate::U8::from_str(s) {
381 return Self::new(unit.to()).ok_or_else(|| UnitsError::InvalidUnit(s.to_string()));
382 }
383
384 Ok(match s.to_ascii_lowercase().as_str() {
385 "eth" | "ether" => Self::ETHER,
386 "pwei" | "milli" | "milliether" | "finney" => Self::PWEI,
387 "twei" | "micro" | "microether" | "szabo" => Self::TWEI,
388 "gwei" | "nano" | "nanoether" | "shannon" => Self::GWEI,
389 "mwei" | "pico" | "picoether" | "lovelace" => Self::MWEI,
390 "kwei" | "femto" | "femtoether" | "babbage" => Self::KWEI,
391 "wei" => Self::WEI,
392 _ => return Err(UnitsError::InvalidUnit(s.to_string())),
393 })
394 }
395}
396
397impl Unit {
398 pub const WEI: Self = unsafe { Self::new_unchecked(0) };
400 #[allow(non_upper_case_globals)]
401 #[doc(hidden)]
402 #[deprecated(since = "0.5.0", note = "use `Unit::WEI` instead")]
403 pub const Wei: Self = Self::WEI;
404
405 pub const KWEI: Self = unsafe { Self::new_unchecked(3) };
407 #[allow(non_upper_case_globals)]
408 #[doc(hidden)]
409 #[deprecated(since = "0.5.0", note = "use `Unit::KWEI` instead")]
410 pub const Kwei: Self = Self::KWEI;
411
412 pub const MWEI: Self = unsafe { Self::new_unchecked(6) };
414 #[allow(non_upper_case_globals)]
415 #[doc(hidden)]
416 #[deprecated(since = "0.5.0", note = "use `Unit::MWEI` instead")]
417 pub const Mwei: Self = Self::MWEI;
418
419 pub const GWEI: Self = unsafe { Self::new_unchecked(9) };
421 #[allow(non_upper_case_globals)]
422 #[doc(hidden)]
423 #[deprecated(since = "0.5.0", note = "use `Unit::GWEI` instead")]
424 pub const Gwei: Self = Self::GWEI;
425
426 pub const TWEI: Self = unsafe { Self::new_unchecked(12) };
428 #[allow(non_upper_case_globals)]
429 #[doc(hidden)]
430 #[deprecated(since = "0.5.0", note = "use `Unit::TWEI` instead")]
431 pub const Twei: Self = Self::TWEI;
432
433 pub const PWEI: Self = unsafe { Self::new_unchecked(15) };
435 #[allow(non_upper_case_globals)]
436 #[doc(hidden)]
437 #[deprecated(since = "0.5.0", note = "use `Unit::PWEI` instead")]
438 pub const Pwei: Self = Self::PWEI;
439
440 pub const ETHER: Self = unsafe { Self::new_unchecked(18) };
442 #[allow(non_upper_case_globals)]
443 #[doc(hidden)]
444 #[deprecated(since = "0.5.0", note = "use `Unit::ETHER` instead")]
445 pub const Ether: Self = Self::ETHER;
446
447 pub const MIN: Self = Self::WEI;
449 pub const MAX: Self = unsafe { Self::new_unchecked(77) };
451
452 #[inline]
454 pub const fn new(units: u8) -> Option<Self> {
455 if units <= Self::MAX.get() {
456 Some(unsafe { Self::new_unchecked(units) })
458 } else {
459 None
460 }
461 }
462
463 #[inline]
469 pub const unsafe fn new_unchecked(x: u8) -> Self {
470 Self(x)
471 }
472
473 #[inline]
489 pub fn wei(self) -> U256 {
490 if self.get() <= MAX_U64_EXPONENT {
491 self.wei_const()
492 } else {
493 U256::from(10u8).pow(U256::from(self.get()))
494 }
495 }
496
497 #[inline]
504 pub const fn wei_const(self) -> U256 {
505 if self.get() > MAX_U64_EXPONENT {
506 panic!("overflow")
507 }
508 U256::from_limbs([10u64.pow(self.get() as u32), 0, 0, 0])
509 }
510
511 #[inline]
513 pub const fn get(self) -> u8 {
514 self.0
515 }
516
517 #[doc(hidden)]
518 #[deprecated(since = "0.5.0", note = "use `get` instead")]
519 pub const fn as_num(&self) -> u8 {
520 self.get()
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use super::*;
527
528 #[test]
529 fn unit_values() {
530 assert_eq!(Unit::WEI.get(), 0);
531 assert_eq!(Unit::KWEI.get(), 3);
532 assert_eq!(Unit::MWEI.get(), 6);
533 assert_eq!(Unit::GWEI.get(), 9);
534 assert_eq!(Unit::TWEI.get(), 12);
535 assert_eq!(Unit::PWEI.get(), 15);
536 assert_eq!(Unit::ETHER.get(), 18);
537 assert_eq!(Unit::new(10).unwrap().get(), 10);
538 assert_eq!(Unit::new(20).unwrap().get(), 20);
539 }
540
541 #[test]
542 fn unit_wei() {
543 let assert = |unit: Unit| {
544 let wei = unit.wei();
545 assert_eq!(wei.to::<u128>(), 10u128.pow(unit.get() as u32));
546 assert_eq!(wei, U256::from(10u8).pow(U256::from(unit.get())));
547 };
548 assert(Unit::WEI);
549 assert(Unit::KWEI);
550 assert(Unit::MWEI);
551 assert(Unit::GWEI);
552 assert(Unit::TWEI);
553 assert(Unit::PWEI);
554 assert(Unit::ETHER);
555 assert(Unit::new(10).unwrap());
556 assert(Unit::new(20).unwrap());
557 }
558
559 #[test]
560 fn parse() {
561 assert_eq!(Unit::try_from("wei").unwrap(), Unit::WEI);
562 assert_eq!(Unit::try_from("kwei").unwrap(), Unit::KWEI);
563 assert_eq!(Unit::try_from("mwei").unwrap(), Unit::MWEI);
564 assert_eq!(Unit::try_from("gwei").unwrap(), Unit::GWEI);
565 assert_eq!(Unit::try_from("twei").unwrap(), Unit::TWEI);
566 assert_eq!(Unit::try_from("pwei").unwrap(), Unit::PWEI);
567 assert_eq!(Unit::try_from("ether").unwrap(), Unit::ETHER);
568 }
569
570 #[test]
571 fn wei_in_ether() {
572 assert_eq!(Unit::ETHER.wei(), U256::from(1e18 as u64));
573 }
574
575 #[test]
576 fn test_format_ether_unsigned() {
577 let eth = format_ether(Unit::ETHER.wei());
578 assert_eq!(eth.parse::<f64>().unwrap() as u64, 1);
579
580 let eth = format_ether(1395633240123456000_u128);
581 assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
582
583 let eth = format_ether(U256::from_str_radix("1395633240123456000", 10).unwrap());
584 assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
585
586 let eth = format_ether(U256::from_str_radix("1395633240123456789", 10).unwrap());
587 assert_eq!(eth, "1.395633240123456789");
588
589 let eth = format_ether(U256::from_str_radix("1005633240123456789", 10).unwrap());
590 assert_eq!(eth, "1.005633240123456789");
591
592 let eth = format_ether(u16::MAX);
593 assert_eq!(eth, "0.000000000000065535");
594
595 let eth = format_ether(u32::MAX);
597 assert_eq!(eth, "0.000000004294967295");
598
599 let eth = format_ether(u64::MAX);
601 assert_eq!(eth, "18.446744073709551615");
602 }
603
604 #[test]
605 fn test_format_ether_signed() {
606 let eth = format_ether(I256::from_dec_str("-1395633240123456000").unwrap());
607 assert_eq!(eth.parse::<f64>().unwrap(), -1.395633240123456);
608
609 let eth = format_ether(I256::from_dec_str("-1395633240123456789").unwrap());
610 assert_eq!(eth, "-1.395633240123456789");
611
612 let eth = format_ether(I256::from_dec_str("1005633240123456789").unwrap());
613 assert_eq!(eth, "1.005633240123456789");
614
615 let eth = format_ether(i8::MIN);
616 assert_eq!(eth, "-0.000000000000000128");
617
618 let eth = format_ether(i8::MAX);
619 assert_eq!(eth, "0.000000000000000127");
620
621 let eth = format_ether(i16::MIN);
622 assert_eq!(eth, "-0.000000000000032768");
623
624 let eth = format_ether(i32::MIN);
626 assert_eq!(eth, "-0.000000002147483648");
627
628 let eth = format_ether(i64::MIN);
630 assert_eq!(eth, "-9.223372036854775808");
631 }
632
633 #[test]
634 fn test_format_units_unsigned() {
635 let gwei_in_ether = format_units(Unit::ETHER.wei(), 9).unwrap();
636 assert_eq!(gwei_in_ether.parse::<f64>().unwrap() as u64, 1e9 as u64);
637
638 let eth = format_units(Unit::ETHER.wei(), "ether").unwrap();
639 assert_eq!(eth.parse::<f64>().unwrap() as u64, 1);
640
641 let eth = format_units(1395633240123456000_u128, "ether").unwrap();
642 assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
643
644 let eth = format_units(U256::from_str_radix("1395633240123456000", 10).unwrap(), "ether")
645 .unwrap();
646 assert_eq!(eth.parse::<f64>().unwrap(), 1.395633240123456);
647
648 let eth = format_units(U256::from_str_radix("1395633240123456789", 10).unwrap(), "ether")
649 .unwrap();
650 assert_eq!(eth, "1.395633240123456789");
651
652 let eth = format_units(U256::from_str_radix("1005633240123456789", 10).unwrap(), "ether")
653 .unwrap();
654 assert_eq!(eth, "1.005633240123456789");
655
656 let eth = format_units(u8::MAX, 4).unwrap();
657 assert_eq!(eth, "0.0255");
658
659 let eth = format_units(u16::MAX, "ether").unwrap();
660 assert_eq!(eth, "0.000000000000065535");
661
662 let eth = format_units(u32::MAX, 18).unwrap();
664 assert_eq!(eth, "0.000000004294967295");
665
666 let eth = format_units(u64::MAX, "gwei").unwrap();
668 assert_eq!(eth, "18446744073.709551615");
669
670 let eth = format_units(u128::MAX, 36).unwrap();
671 assert_eq!(eth, "340.282366920938463463374607431768211455");
672
673 let eth = format_units(U256::MAX, 77).unwrap();
674 assert_eq!(
675 eth,
676 "1.15792089237316195423570985008687907853269984665640564039457584007913129639935"
677 );
678
679 let _err = format_units(U256::MAX, 78).unwrap_err();
680 let _err = format_units(U256::MAX, 79).unwrap_err();
681 }
682
683 #[test]
684 fn test_format_units_signed() {
685 let eth =
686 format_units(I256::from_dec_str("-1395633240123456000").unwrap(), "ether").unwrap();
687 assert_eq!(eth.parse::<f64>().unwrap(), -1.395633240123456);
688
689 let eth =
690 format_units(I256::from_dec_str("-1395633240123456789").unwrap(), "ether").unwrap();
691 assert_eq!(eth, "-1.395633240123456789");
692
693 let eth =
694 format_units(I256::from_dec_str("1005633240123456789").unwrap(), "ether").unwrap();
695 assert_eq!(eth, "1.005633240123456789");
696
697 let eth = format_units(i8::MIN, 4).unwrap();
698 assert_eq!(eth, "-0.0128");
699 assert_eq!(eth.parse::<f64>().unwrap(), -0.0128_f64);
700
701 let eth = format_units(i8::MAX, 4).unwrap();
702 assert_eq!(eth, "0.0127");
703 assert_eq!(eth.parse::<f64>().unwrap(), 0.0127);
704
705 let eth = format_units(i16::MIN, "ether").unwrap();
706 assert_eq!(eth, "-0.000000000000032768");
707
708 let eth = format_units(i32::MIN, 18).unwrap();
710 assert_eq!(eth, "-0.000000002147483648");
711
712 let eth = format_units(i64::MIN, "gwei").unwrap();
714 assert_eq!(eth, "-9223372036.854775808");
715
716 let eth = format_units(i128::MIN, 36).unwrap();
717 assert_eq!(eth, "-170.141183460469231731687303715884105728");
718
719 let eth = format_units(I256::MIN, 76).unwrap();
720 let min = "-5.7896044618658097711785492504343953926634992332820282019728792003956564819968";
721 assert_eq!(eth, min);
722 let eth = format_units(I256::MIN, 77).unwrap();
724 assert_eq!(eth, min);
725
726 let _err = format_units(I256::MIN, 78).unwrap_err();
727 let _err = format_units(I256::MIN, 79).unwrap_err();
728 }
729
730 #[test]
731 fn parse_large_units() {
732 let decimals = 27u8;
733 let val = "10.55";
734
735 let n: U256 = parse_units(val, decimals).unwrap().into();
736 assert_eq!(n.to_string(), "10550000000000000000000000000");
737 }
738
739 #[test]
740 fn test_parse_units() {
741 let gwei: U256 = parse_units("1.5", 9).unwrap().into();
742 assert_eq!(gwei, U256::from(15e8 as u64));
743
744 let token: U256 = parse_units("1163.56926418", 8).unwrap().into();
745 assert_eq!(token, U256::from(116356926418u64));
746
747 let eth_dec_float: U256 = parse_units("1.39563324", "ether").unwrap().into();
748 assert_eq!(eth_dec_float, U256::from_str_radix("1395633240000000000", 10).unwrap());
749
750 let eth_dec_string: U256 = parse_units("1.39563324", "ether").unwrap().into();
751 assert_eq!(eth_dec_string, U256::from_str_radix("1395633240000000000", 10).unwrap());
752
753 let eth: U256 = parse_units("1", "ether").unwrap().into();
754 assert_eq!(eth, Unit::ETHER.wei());
755
756 let val: U256 = parse_units("2.3", "ether").unwrap().into();
757 assert_eq!(val, U256::from_str_radix("2300000000000000000", 10).unwrap());
758
759 let n: U256 = parse_units(".2", 2).unwrap().into();
760 assert_eq!(n, U256::from(20), "leading dot");
761
762 let n: U256 = parse_units("333.21", 2).unwrap().into();
763 assert_eq!(n, U256::from(33321), "trailing dot");
764
765 let n: U256 = parse_units("98766", 16).unwrap().into();
766 assert_eq!(n, U256::from_str_radix("987660000000000000000", 10).unwrap(), "no dot");
767
768 let n: U256 = parse_units("3_3_0", 3).unwrap().into();
769 assert_eq!(n, U256::from(330000), "underscore");
770
771 let n: U256 = parse_units("330", 0).unwrap().into();
772 assert_eq!(n, U256::from(330), "zero decimals");
773
774 let n: U256 = parse_units(".1234", 3).unwrap().into();
775 assert_eq!(n, U256::from(123), "truncate too many decimals");
776
777 assert!(parse_units("1", 80).is_err(), "overflow");
778
779 let two_e30 = U256::from(2) * U256::from_limbs([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]);
780 let n: U256 = parse_units("2", 30).unwrap().into();
781 assert_eq!(n, two_e30, "2e30");
782
783 let n: U256 = parse_units(".33_319_2", 0).unwrap().into();
784 assert_eq!(n, U256::ZERO, "mix");
785
786 let n: U256 = parse_units("", 3).unwrap().into();
787 assert_eq!(n, U256::ZERO, "empty");
788 }
789
790 #[test]
791 fn test_signed_parse_units() {
792 let gwei: I256 = parse_units("-1.5", 9).unwrap().into();
793 assert_eq!(gwei.as_i64(), -15e8 as i64);
794
795 let token: I256 = parse_units("-1163.56926418", 8).unwrap().into();
796 assert_eq!(token.as_i64(), -116356926418);
797
798 let eth_dec_float: I256 = parse_units("-1.39563324", "ether").unwrap().into();
799 assert_eq!(eth_dec_float, I256::from_dec_str("-1395633240000000000").unwrap());
800
801 let eth_dec_string: I256 = parse_units("-1.39563324", "ether").unwrap().into();
802 assert_eq!(eth_dec_string, I256::from_dec_str("-1395633240000000000").unwrap());
803
804 let eth: I256 = parse_units("-1", "ether").unwrap().into();
805 assert_eq!(eth, I256::from_raw(Unit::ETHER.wei()) * I256::MINUS_ONE);
806
807 let val: I256 = parse_units("-2.3", "ether").unwrap().into();
808 assert_eq!(val, I256::from_dec_str("-2300000000000000000").unwrap());
809
810 let n: I256 = parse_units("-.2", 2).unwrap().into();
811 assert_eq!(n, I256::try_from(-20).unwrap(), "leading dot");
812
813 let n: I256 = parse_units("-333.21", 2).unwrap().into();
814 assert_eq!(n, I256::try_from(-33321).unwrap(), "trailing dot");
815
816 let n: I256 = parse_units("-98766", 16).unwrap().into();
817 assert_eq!(n, I256::from_dec_str("-987660000000000000000").unwrap(), "no dot");
818
819 let n: I256 = parse_units("-3_3_0", 3).unwrap().into();
820 assert_eq!(n, I256::try_from(-330000).unwrap(), "underscore");
821
822 let n: I256 = parse_units("-330", 0).unwrap().into();
823 assert_eq!(n, I256::try_from(-330).unwrap(), "zero decimals");
824
825 let n: I256 = parse_units("-.1234", 3).unwrap().into();
826 assert_eq!(n, I256::try_from(-123).unwrap(), "truncate too many decimals");
827
828 assert!(parse_units("-1", 80).is_err(), "overflow");
829
830 let two_e30 = I256::try_from(-2).unwrap()
831 * I256::from_raw(U256::from_limbs([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]));
832 let n: I256 = parse_units("-2", 30).unwrap().into();
833 assert_eq!(n, two_e30, "-2e30");
834
835 let n: I256 = parse_units("-.33_319_2", 0).unwrap().into();
836 assert_eq!(n, I256::ZERO, "mix");
837
838 let n: I256 = parse_units("-", 3).unwrap().into();
839 assert_eq!(n, I256::ZERO, "empty");
840 }
841}