ethers_primitives 0.2.2

Primitive types for ethereum, including Rlp/ABI encode/decode support
Documentation
use std::{fmt::Display, str::FromStr};

use crate::U256;
use num::{BigInt, BigRational, ToPrimitive};

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, thiserror::Error)]
pub enum UnitError {
    #[error("Parse ethereum unit from float string literal failed. {0}")]
    ParseFloatString(String),
}

pub trait EthereumUnit {
    fn decimals() -> usize;

    fn new(v: U256) -> Self;

    fn to_u256(self) -> U256;
}

macro_rules! def_eth_unit {
    ($name: ident, $decimals: literal) => {
        #[derive(Clone, Deserialize, Serialize)]
        pub struct $name(pub U256);

        impl EthereumUnit for $name {
            fn decimals() -> usize {
                $decimals
            }

            fn new(v: U256) -> Self {
                Self(v)
            }

            fn to_u256(self) -> U256 {
                self.0
            }
        }

        impl $name {
            pub fn unit_to<T: EthereumUnit>(&self) -> T {
                T::new(self.0.clone())
            }
        }

        impl From<u128> for $name {
            fn from(v: u128) -> Self {
                $name(U256::from(v))
            }
        }

        impl From<u64> for $name {
            fn from(v: u64) -> Self {
                $name(U256::from(v))
            }
        }

        impl From<u32> for $name {
            fn from(v: u32) -> Self {
                $name(U256::from(v))
            }
        }

        impl From<u16> for $name {
            fn from(v: u16) -> Self {
                $name(U256::from(v))
            }
        }

        impl From<u8> for $name {
            fn from(v: u8) -> Self {
                $name(U256::from(v))
            }
        }

        impl FromStr for $name {
            type Err = anyhow::Error;
            fn from_str(s: &str) -> Result<Self, Self::Err> {
                let v: f64 = s.trim_end_matches(stringify!($name)).parse()?;

                let v =
                    BigRational::from_float(v).ok_or(UnitError::ParseFloatString(s.to_owned()))?;

                let exponent = BigInt::from(10u32).pow($name::decimals() as u32);

                let v = v * exponent;

                log::debug!("{:#x}", v);

                let v = v.floor();

                log::debug!("{:#x}", v);

                let v = v.to_integer().to_biguint().unwrap();

                Ok($name(U256::new(v)?))
            }
        }

        impl<'a> TryFrom<&'a str> for $name {
            type Error = anyhow::Error;
            fn try_from(value: &'a str) -> Result<Self, Self::Error> {
                value.parse()
            }
        }

        impl From<$name> for String {
            fn from(v: $name) -> Self {
                let bytes = v.0 .0;

                let v = BigInt::from_bytes_be(num::bigint::Sign::Plus, &bytes);

                let v = BigRational::from_integer(v);

                let v = v / BigInt::from(10u32).pow($name::decimals() as u32);

                format!(
                    "{}",
                    v.numer().to_f64().unwrap() / v.denom().to_f64().unwrap()
                )
            }
        }

        impl From<$name> for U256 {
            fn from(v: $name) -> Self {
                v.0
            }
        }

        impl Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                let s: String = String::from(self.clone());

                write!(f, "{} {}", s, stringify!($name))
            }
        }
    };
}

def_eth_unit!(Wei, 0);
def_eth_unit!(Kwei, 3);
def_eth_unit!(Mwei, 6);
def_eth_unit!(Gwei, 9);
def_eth_unit!(Szabo, 12);
def_eth_unit!(Finney, 15);
def_eth_unit!(Ether, 18);

#[cfg(test)]
mod tests {

    use crate::*;

    #[test]
    fn test_convert() {
        _ = pretty_env_logger::try_init();

        let v = "1.1".parse::<Gwei>().expect("Parse ether");

        let gwei = serde_json::to_string(&v).expect("");

        let v: Wei = v.unit_to();

        let wei = serde_json::to_string(&v).expect("");

        assert_eq!(gwei, wei);

        log::debug!("{}", gwei);
    }
}