Add Decimal support to ethers::utils::parse_units (#463)

* implemnet parse_units with dec support

* Add doc tests

* add decimal support to parse_ethers()

* add unit tests
This commit is contained in:
Odysseas Lamtzidis 2021-09-21 00:02:45 +03:00 committed by GitHub
parent a92fe13677
commit 22367988fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 40 additions and 10 deletions

View File

@ -35,7 +35,6 @@ pub use rlp;
use crate::types::{Address, Bytes, U256};
use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey};
use std::convert::TryInto;
use std::ops::Neg;
use thiserror::Error;
@ -85,22 +84,41 @@ pub fn format_units<T: Into<U256>, K: Into<Units>>(amount: T, units: K) -> U256
/// assert_eq!(eth, parse_ether(1u8).unwrap());
/// assert_eq!(eth, parse_ether(1usize).unwrap());
/// assert_eq!(eth, parse_ether("1").unwrap());
pub fn parse_ether<S>(eth: S) -> Result<U256, S::Error>
/// ```
pub fn parse_ether<S>(eth: S) -> Result<U256, Box<dyn std::error::Error>>
where
S: TryInto<U256>,
S: ToString,
{
Ok(eth.try_into()? * WEI_IN_ETHER)
parse_units(eth, "ether")
}
/// Multiplies the provided amount with 10^{units} provided.
pub fn parse_units<S, K>(amount: S, units: K) -> Result<U256, S::Error>
///
/// ```
/// use ethers_core::{types::U256, utils::parse_units};
/// let amount_in_eth = U256::from_dec_str("15230001000000000000").unwrap();
/// let amount_in_gwei = U256::from_dec_str("15230001000").unwrap();
/// let amount_in_wei = U256::from_dec_str("15230001000").unwrap();
/// assert_eq!(amount_in_eth, parse_units("15.230001000000000000", "ether").unwrap());
/// assert_eq!(amount_in_gwei, parse_units("15.230001000000000000", "gwei").unwrap());
/// assert_eq!(amount_in_wei, parse_units("15230001000", "wei").unwrap());
/// ```
/// Example of trying to parse decimal WEI, which should fail, as WEI is the smallest
/// ETH denominator. 1 ETH = 10^18 WEI.
/// ```should_panic
/// use ethers_core::{types::U256, utils::parse_units};
/// let amount_in_wei = U256::from_dec_str("15230001000").unwrap();
/// assert_eq!(amount_in_wei, parse_units("15.230001000000000000", "wei").unwrap());
/// ```
pub fn parse_units<K, S>(amount: S, units: K) -> Result<U256, Box<dyn std::error::Error>>
where
S: TryInto<U256>,
S: ToString,
K: Into<Units>,
{
Ok(amount.try_into()? * 10u64.pow(units.into().as_num()))
let float_n: f64 = amount.to_string().parse::<f64>()? * 10u64.pow(units.into().as_num()) as f64;
let u256_n: U256 = U256::from_dec_str(&float_n.to_string())?;
Ok(u256_n)
}
/// The address for an Ethereum contract is deterministically computed from the
/// address of its creator (sender) and how many transactions the creator has
/// sent (nonce). The sender and nonce are RLP encoded and then hashed with Keccak-256.
@ -382,8 +400,20 @@ mod tests {
#[test]
fn test_parse_units() {
let gwei = parse_units(1, 9).unwrap();
assert_eq!(gwei.as_u64(), 1e9 as u64);
let gwei = parse_units(1.5, 9).unwrap();
assert_eq!(gwei.as_u64(), 15e8 as u64);
let eth_dec_float = parse_units(1.39563324, "ether").unwrap();
assert_eq!(
eth_dec_float,
U256::from_dec_str("1395633240000000000").unwrap()
);
let eth_dec_string = parse_units("1.39563324", "ether").unwrap();
assert_eq!(
eth_dec_string,
U256::from_dec_str("1395633240000000000").unwrap()
);
let eth = parse_units(1, "ether").unwrap();
assert_eq!(eth, WEI_IN_ETHER);