I256 parse support (#1863)
* Added support for I256 in format_units. Added two complement support for I256. * Add I256 support into parse_units Co-authored-by: Dave Belvedere <dave@protonmail.com>
This commit is contained in:
parent
74bf6fbb04
commit
a0fb1bf196
|
@ -94,9 +94,11 @@
|
||||||
- [#1632](https://github.com/gakonst/ethers-rs/pull/1632) Re-export `H32` from `ethabi`.
|
- [#1632](https://github.com/gakonst/ethers-rs/pull/1632) Re-export `H32` from `ethabi`.
|
||||||
- [#1634](https://github.com/gakonst/ethers-rs/pull/1634) Derive missing `Clone`, `Copy` and `Debug` impls in ethers-etherscan.
|
- [#1634](https://github.com/gakonst/ethers-rs/pull/1634) Derive missing `Clone`, `Copy` and `Debug` impls in ethers-etherscan.
|
||||||
- Bytes debug format now displays hex literals [#1658](https://github.com/gakonst/ethers-rs/pull/1658)
|
- Bytes debug format now displays hex literals [#1658](https://github.com/gakonst/ethers-rs/pull/1658)
|
||||||
- [#1451](https://github.com/gakonst/ethers-rs/issues/1451) Add Arithemtic Shift Left operation for I256
|
- [#1451](https://github.com/gakonst/ethers-rs/issues/1451) Add Arithmetic Shift Left operation for I256
|
||||||
- [#1860](https://github.com/gakonst/ethers-rs/pull/1860)Update I256 type documentation calling out the inconsistency
|
- [#1860](https://github.com/gakonst/ethers-rs/pull/1860) Update I256 type documentation calling out the inconsistency
|
||||||
between its right shift operator and standard library numeric types.
|
between its right shift operator and standard library numeric types.
|
||||||
|
- [#842](https://github.com/gakonst/ethers-rs/issues/842) Add support for I256 types in `parse_units` and `format_units`.
|
||||||
|
Added `twos_complement` function for I256.
|
||||||
|
|
||||||
## ethers-contract-abigen
|
## ethers-contract-abigen
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
abi::{InvalidOutputType, Token, Tokenizable},
|
abi::{InvalidOutputType, Token, Tokenizable},
|
||||||
types::U256,
|
types::U256,
|
||||||
|
utils::ParseUnits,
|
||||||
};
|
};
|
||||||
use ethabi::ethereum_types::FromDecStrErr;
|
use ethabi::ethereum_types::FromDecStrErr;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -977,13 +978,21 @@ impl I256 {
|
||||||
} else {
|
} else {
|
||||||
let result = self << shift;
|
let result = self << shift;
|
||||||
if result.sign() != self.sign() {
|
if result.sign() != self.sign() {
|
||||||
// Overflow occured
|
// Overflow occurred
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the twos complement of the I256
|
||||||
|
pub fn twos_complement(self) -> U256 {
|
||||||
|
match self.sign() {
|
||||||
|
Sign::Positive => self.into_raw(),
|
||||||
|
Sign::Negative => twos_complement(self.into_raw()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_from {
|
macro_rules! impl_from {
|
||||||
|
@ -1042,6 +1051,15 @@ impl TryFrom<I256> for U256 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ParseUnits> for I256 {
|
||||||
|
fn from(n: ParseUnits) -> Self {
|
||||||
|
match n {
|
||||||
|
ParseUnits::U256(n) => Self::from_raw(n),
|
||||||
|
ParseUnits::I256(n) => n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl str::FromStr for I256 {
|
impl str::FromStr for I256 {
|
||||||
type Err = ParseI256Error;
|
type Err = ParseI256Error;
|
||||||
|
|
||||||
|
@ -1828,4 +1846,37 @@ mod tests {
|
||||||
assert_eq!(I256::from_token(42i32.into_token()).unwrap(), I256::from(42),);
|
assert_eq!(I256::from_token(42i32.into_token()).unwrap(), I256::from(42),);
|
||||||
assert_eq!(I256::from_token(U256::MAX.into_token()).unwrap(), I256::minus_one(),);
|
assert_eq!(I256::from_token(U256::MAX.into_token()).unwrap(), I256::minus_one(),);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn twos_complement() {
|
||||||
|
macro_rules! assert_twos_complement {
|
||||||
|
($signed:ty, $unsigned:ty) => {
|
||||||
|
assert_eq!(
|
||||||
|
I256::from(<$signed>::MAX).twos_complement(),
|
||||||
|
U256::from(<$signed>::MAX)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
I256::from(<$signed>::MIN).twos_complement(),
|
||||||
|
U256::from(<$signed>::MIN.unsigned_abs())
|
||||||
|
);
|
||||||
|
assert_eq!(I256::from(0 as $signed).twos_complement(), U256::from(0 as $signed));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
I256::from(<$unsigned>::MAX).twos_complement(),
|
||||||
|
U256::from(<$unsigned>::MAX)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
I256::from(0 as $unsigned).twos_complement(),
|
||||||
|
U256::from(0 as $unsigned)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_twos_complement!(i8, u8);
|
||||||
|
assert_twos_complement!(i16, u16);
|
||||||
|
assert_twos_complement!(i32, u32);
|
||||||
|
assert_twos_complement!(i64, u64);
|
||||||
|
assert_twos_complement!(i128, u128);
|
||||||
|
assert_twos_complement!(isize, usize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ mod uint8;
|
||||||
pub use uint8::*;
|
pub use uint8::*;
|
||||||
|
|
||||||
mod i256;
|
mod i256;
|
||||||
pub use i256::{Sign, I256};
|
pub use i256::{ParseI256Error, Sign, I256};
|
||||||
|
|
||||||
mod bytes;
|
mod bytes;
|
||||||
pub use self::bytes::{deserialize_bytes, serialize_bytes, Bytes, ParseBytesError};
|
pub use self::bytes::{deserialize_bytes, serialize_bytes, Bytes, ParseBytesError};
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub use rlp;
|
||||||
/// Re-export hex
|
/// Re-export hex
|
||||||
pub use hex;
|
pub use hex;
|
||||||
|
|
||||||
use crate::types::{Address, Bytes, I256, U256};
|
use crate::types::{Address, Bytes, ParseI256Error, I256, U256};
|
||||||
use elliptic_curve::sec1::ToEncodedPoint;
|
use elliptic_curve::sec1::ToEncodedPoint;
|
||||||
use ethabi::ethereum_types::FromDecStrErr;
|
use ethabi::ethereum_types::FromDecStrErr;
|
||||||
use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
|
use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
|
||||||
|
@ -58,6 +58,8 @@ pub enum ConversionError {
|
||||||
FromDecStrError(#[from] FromDecStrErr),
|
FromDecStrError(#[from] FromDecStrErr),
|
||||||
#[error("Overflow parsing string")]
|
#[error("Overflow parsing string")]
|
||||||
ParseOverflow,
|
ParseOverflow,
|
||||||
|
#[error(transparent)]
|
||||||
|
ParseI256Error(#[from] ParseI256Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
|
/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
|
||||||
|
@ -76,6 +78,41 @@ pub const EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER: u64 = 100_000_000_000;
|
||||||
/// under it.
|
/// under it.
|
||||||
pub const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200;
|
pub const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200;
|
||||||
|
|
||||||
|
/// This enum holds the numeric types that a possible to be returned by `parse_units` and
|
||||||
|
/// that are taken by `format_units`.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum ParseUnits {
|
||||||
|
U256(U256),
|
||||||
|
I256(I256),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParseUnits> for U256 {
|
||||||
|
fn from(n: ParseUnits) -> Self {
|
||||||
|
match n {
|
||||||
|
ParseUnits::U256(n) => n,
|
||||||
|
ParseUnits::I256(n) => n.into_raw(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! construct_format_units_from {
|
||||||
|
($( $t:ty[$convert:ident] ),*) => {
|
||||||
|
$(
|
||||||
|
impl From<$t> for ParseUnits {
|
||||||
|
fn from(num: $t) -> Self {
|
||||||
|
Self::$convert(num.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the From<T> code for the given numeric types below.
|
||||||
|
construct_format_units_from! {
|
||||||
|
u8[U256], u16[U256], u32[U256], u64[U256], u128[U256], U256[U256], usize[U256],
|
||||||
|
i8[I256], i16[I256], i32[I256], i64[I256], i128[I256], I256[I256], isize[I256]
|
||||||
|
}
|
||||||
|
|
||||||
/// Format the output for the user which prefer to see values
|
/// Format the output for the user which prefer to see values
|
||||||
/// in ether (instead of wei)
|
/// in ether (instead of wei)
|
||||||
///
|
///
|
||||||
|
@ -97,22 +134,43 @@ pub fn format_ether<T: Into<U256>>(amount: T) -> U256 {
|
||||||
///
|
///
|
||||||
/// let eth = format_units(U256::from_dec_str("1395633240123456789").unwrap(), "ether").unwrap();
|
/// let eth = format_units(U256::from_dec_str("1395633240123456789").unwrap(), "ether").unwrap();
|
||||||
/// assert_eq!(eth, "1.395633240123456789");
|
/// assert_eq!(eth, "1.395633240123456789");
|
||||||
|
///
|
||||||
|
/// let eth = format_units(i64::MIN, "gwei").unwrap();
|
||||||
|
/// assert_eq!(eth, "-9223372036.854775808");
|
||||||
|
///
|
||||||
|
/// let eth = format_units(i128::MIN, 36).unwrap();
|
||||||
|
/// assert_eq!(eth, "-170.141183460469231731687303715884105728");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn format_units<T, K>(amount: T, units: K) -> Result<String, ConversionError>
|
pub fn format_units<T, K>(amount: T, units: K) -> Result<String, ConversionError>
|
||||||
where
|
where
|
||||||
T: Into<U256>,
|
T: Into<ParseUnits>,
|
||||||
K: TryInto<Units, Error = ConversionError>,
|
K: TryInto<Units, Error = ConversionError>,
|
||||||
{
|
{
|
||||||
let units = units.try_into()?;
|
let units = units.try_into()?;
|
||||||
let amount = amount.into();
|
match amount.into() {
|
||||||
let amount_decimals = amount % U256::from(10_u128.pow(units.as_num()));
|
ParseUnits::U256(amount) => {
|
||||||
let amount_integer = amount / U256::from(10_u128.pow(units.as_num()));
|
let amount_decimals = amount % U256::from(10_u128.pow(units.as_num()));
|
||||||
Ok(format!(
|
let amount_integer = amount / U256::from(10_u128.pow(units.as_num()));
|
||||||
"{}.{:0width$}",
|
Ok(format!(
|
||||||
amount_integer,
|
"{}.{:0width$}",
|
||||||
amount_decimals.as_u128(),
|
amount_integer,
|
||||||
width = units.as_num() as usize
|
amount_decimals.as_u128(),
|
||||||
))
|
width = units.as_num() as usize
|
||||||
|
))
|
||||||
|
}
|
||||||
|
ParseUnits::I256(amount) => {
|
||||||
|
let sign = if amount.is_negative() { "-" } else { "" };
|
||||||
|
let amount_decimals = amount % I256::from(10_u128.pow(units.as_num()));
|
||||||
|
let amount_integer = amount / I256::from(10_u128.pow(units.as_num()));
|
||||||
|
Ok(format!(
|
||||||
|
"{}{}.{:0width$}",
|
||||||
|
sign,
|
||||||
|
amount_integer.twos_complement(),
|
||||||
|
amount_decimals.twos_complement().as_u128(),
|
||||||
|
width = units.as_num() as usize
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the input to a U256 and converts from Ether to Wei.
|
/// Converts the input to a U256 and converts from Ether to Wei.
|
||||||
|
@ -129,7 +187,7 @@ pub fn parse_ether<S>(eth: S) -> Result<U256, ConversionError>
|
||||||
where
|
where
|
||||||
S: ToString,
|
S: ToString,
|
||||||
{
|
{
|
||||||
parse_units(eth, "ether")
|
Ok(parse_units(eth, "ether")?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multiplies the provided amount with 10^{units} provided.
|
/// Multiplies the provided amount with 10^{units} provided.
|
||||||
|
@ -139,24 +197,25 @@ where
|
||||||
/// let amount_in_eth = U256::from_dec_str("15230001000000000000").unwrap();
|
/// let amount_in_eth = U256::from_dec_str("15230001000000000000").unwrap();
|
||||||
/// let amount_in_gwei = U256::from_dec_str("15230001000").unwrap();
|
/// let amount_in_gwei = U256::from_dec_str("15230001000").unwrap();
|
||||||
/// let amount_in_wei = 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_eth, parse_units("15.230001000000000000", "ether").unwrap().into());
|
||||||
/// assert_eq!(amount_in_gwei, parse_units("15.230001000000000000", "gwei").unwrap());
|
/// assert_eq!(amount_in_gwei, parse_units("15.230001000000000000", "gwei").unwrap().into());
|
||||||
/// assert_eq!(amount_in_wei, parse_units("15230001000", "wei").unwrap());
|
/// assert_eq!(amount_in_wei, parse_units("15230001000", "wei").unwrap().into());
|
||||||
/// ```
|
/// ```
|
||||||
/// Example of trying to parse decimal WEI, which should fail, as WEI is the smallest
|
/// Example of trying to parse decimal WEI, which should fail, as WEI is the smallest
|
||||||
/// ETH denominator. 1 ETH = 10^18 WEI.
|
/// ETH denominator. 1 ETH = 10^18 WEI.
|
||||||
/// ```should_panic
|
/// ```should_panic
|
||||||
/// use ethers_core::{types::U256, utils::parse_units};
|
/// use ethers_core::{types::U256, utils::parse_units};
|
||||||
/// let amount_in_wei = U256::from_dec_str("15230001000").unwrap();
|
/// let amount_in_wei = U256::from_dec_str("15230001000").unwrap();
|
||||||
/// assert_eq!(amount_in_wei, parse_units("15.230001000000000000", "wei").unwrap());
|
/// assert_eq!(amount_in_wei, parse_units("15.230001000000000000", "wei").unwrap().into());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse_units<K, S>(amount: S, units: K) -> Result<U256, ConversionError>
|
pub fn parse_units<K, S>(amount: S, units: K) -> Result<ParseUnits, ConversionError>
|
||||||
where
|
where
|
||||||
S: ToString,
|
S: ToString,
|
||||||
K: TryInto<Units, Error = ConversionError> + Copy,
|
K: TryInto<Units, Error = ConversionError> + Copy,
|
||||||
{
|
{
|
||||||
let exponent: u32 = units.try_into()?.as_num();
|
let exponent: u32 = units.try_into()?.as_num();
|
||||||
let mut amount_str = amount.to_string().replace('_', "");
|
let mut amount_str = amount.to_string().replace('_', "");
|
||||||
|
let negative = amount_str.chars().next().unwrap_or_default() == '-';
|
||||||
let dec_len = if let Some(di) = amount_str.find('.') {
|
let dec_len = if let Some(di) = amount_str.find('.') {
|
||||||
amount_str.remove(di);
|
amount_str.remove(di);
|
||||||
amount_str[di..].len() as u32
|
amount_str[di..].len() as u32
|
||||||
|
@ -167,16 +226,37 @@ where
|
||||||
if dec_len > exponent {
|
if dec_len > exponent {
|
||||||
// Truncate the decimal part if it is longer than the exponent
|
// Truncate the decimal part if it is longer than the exponent
|
||||||
let amount_str = &amount_str[..(amount_str.len() - (dec_len - exponent) as usize)];
|
let amount_str = &amount_str[..(amount_str.len() - (dec_len - exponent) as usize)];
|
||||||
let a_uint = U256::from_dec_str(amount_str)?;
|
if negative {
|
||||||
Ok(a_uint)
|
// Edge case: We have removed the entire number and only the negative sign is left.
|
||||||
|
// Return 0 as a I256 given the input was signed.
|
||||||
|
if amount_str == "-" {
|
||||||
|
Ok(ParseUnits::I256(I256::zero()))
|
||||||
|
} else {
|
||||||
|
Ok(ParseUnits::I256(I256::from_dec_str(amount_str)?))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(ParseUnits::U256(U256::from_dec_str(amount_str)?))
|
||||||
|
}
|
||||||
|
} else if negative {
|
||||||
|
// Edge case: Only a negative sign was given, return 0 as a I256 given the input was signed.
|
||||||
|
if amount_str == "-" {
|
||||||
|
Ok(ParseUnits::I256(I256::zero()))
|
||||||
|
} else {
|
||||||
|
let mut n = I256::from_dec_str(&amount_str)?;
|
||||||
|
n *= I256::from(10)
|
||||||
|
.checked_pow(exponent - dec_len)
|
||||||
|
.ok_or(ConversionError::ParseOverflow)?;
|
||||||
|
Ok(ParseUnits::I256(n))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut a_uint = U256::from_dec_str(&amount_str)?;
|
let mut a_uint = U256::from_dec_str(&amount_str)?;
|
||||||
a_uint *= U256::from(10)
|
a_uint *= U256::from(10)
|
||||||
.checked_pow(U256::from(exponent - dec_len))
|
.checked_pow(U256::from(exponent - dec_len))
|
||||||
.ok_or(ConversionError::ParseOverflow)?;
|
.ok_or(ConversionError::ParseOverflow)?;
|
||||||
Ok(a_uint)
|
Ok(ParseUnits::U256(a_uint))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The address for an Ethereum contract is deterministically computed from the
|
/// The address for an Ethereum contract is deterministically computed from the
|
||||||
/// address of its creator (sender) and how many transactions the creator has
|
/// 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.
|
/// sent (nonce). The sender and nonce are RLP encoded and then hashed with Keccak-256.
|
||||||
|
@ -432,7 +512,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_units() {
|
fn test_format_units_unsigned() {
|
||||||
let gwei_in_ether = format_units(WEI_IN_ETHER, 9).unwrap();
|
let gwei_in_ether = format_units(WEI_IN_ETHER, 9).unwrap();
|
||||||
assert_eq!(gwei_in_ether.parse::<f64>().unwrap() as u64, 1e9 as u64);
|
assert_eq!(gwei_in_ether.parse::<f64>().unwrap() as u64, 1e9 as u64);
|
||||||
|
|
||||||
|
@ -453,51 +533,173 @@ mod tests {
|
||||||
let eth =
|
let eth =
|
||||||
format_units(U256::from_dec_str("1005633240123456789").unwrap(), "ether").unwrap();
|
format_units(U256::from_dec_str("1005633240123456789").unwrap(), "ether").unwrap();
|
||||||
assert_eq!(eth, "1.005633240123456789");
|
assert_eq!(eth, "1.005633240123456789");
|
||||||
|
|
||||||
|
let eth = format_units(255u8, 4).unwrap();
|
||||||
|
assert_eq!(eth, "0.0255");
|
||||||
|
|
||||||
|
let eth = format_units(u16::MAX, "ether").unwrap();
|
||||||
|
assert_eq!(eth, "0.000000000000065535");
|
||||||
|
|
||||||
|
// Note: This covers usize on 32 bit systems.
|
||||||
|
let eth = format_units(u32::MAX, 18).unwrap();
|
||||||
|
assert_eq!(eth, "0.000000004294967295");
|
||||||
|
|
||||||
|
// Note: This covers usize on 64 bit systems.
|
||||||
|
let eth = format_units(u64::MAX, "gwei").unwrap();
|
||||||
|
assert_eq!(eth, "18446744073.709551615");
|
||||||
|
|
||||||
|
let eth = format_units(u128::MAX, 36).unwrap();
|
||||||
|
assert_eq!(eth, "340.282366920938463463374607431768211455");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_units_signed() {
|
||||||
|
let eth =
|
||||||
|
format_units(I256::from_dec_str("-1395633240123456000").unwrap(), "ether").unwrap();
|
||||||
|
assert_eq!(eth.parse::<f64>().unwrap(), -1.395633240123456);
|
||||||
|
|
||||||
|
let eth =
|
||||||
|
format_units(I256::from_dec_str("-1395633240123456789").unwrap(), "ether").unwrap();
|
||||||
|
assert_eq!(eth, "-1.395633240123456789");
|
||||||
|
|
||||||
|
let eth =
|
||||||
|
format_units(I256::from_dec_str("1005633240123456789").unwrap(), "ether").unwrap();
|
||||||
|
assert_eq!(eth, "1.005633240123456789");
|
||||||
|
|
||||||
|
let eth = format_units(i8::MIN, 4).unwrap();
|
||||||
|
assert_eq!(eth, "-0.0128");
|
||||||
|
assert_eq!(eth.parse::<f64>().unwrap(), -0.0128_f64);
|
||||||
|
|
||||||
|
let eth = format_units(i8::MAX, 4).unwrap();
|
||||||
|
assert_eq!(eth, "0.0127");
|
||||||
|
assert_eq!(eth.parse::<f64>().unwrap(), 0.0127);
|
||||||
|
|
||||||
|
let eth = format_units(i16::MIN, "ether").unwrap();
|
||||||
|
assert_eq!(eth, "-0.000000000000032768");
|
||||||
|
|
||||||
|
// Note: This covers isize on 32 bit systems.
|
||||||
|
let eth = format_units(i32::MIN, 18).unwrap();
|
||||||
|
assert_eq!(eth, "-0.000000002147483648");
|
||||||
|
|
||||||
|
// Note: This covers isize on 64 bit systems.
|
||||||
|
let eth = format_units(i64::MIN, "gwei").unwrap();
|
||||||
|
assert_eq!(eth, "-9223372036.854775808");
|
||||||
|
|
||||||
|
let eth = format_units(i128::MIN, 36).unwrap();
|
||||||
|
assert_eq!(eth, "-170.141183460469231731687303715884105728");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_large_units() {
|
fn parse_large_units() {
|
||||||
let decimals = 27u32;
|
let decimals = 27u32;
|
||||||
let val = "10.55";
|
let val = "10.55";
|
||||||
let unit = parse_units(val, decimals).unwrap();
|
|
||||||
assert_eq!(unit.to_string(), "10550000000000000000000000000");
|
let n: U256 = parse_units(val, decimals).unwrap().into();
|
||||||
|
assert_eq!(n.to_string(), "10550000000000000000000000000");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_units() {
|
fn test_parse_units() {
|
||||||
let gwei = parse_units(1.5, 9).unwrap();
|
let gwei: U256 = parse_units(1.5, 9).unwrap().into();
|
||||||
assert_eq!(gwei.as_u64(), 15e8 as u64);
|
assert_eq!(gwei.as_u64(), 15e8 as u64);
|
||||||
|
|
||||||
let token = parse_units(1163.56926418, 8).unwrap();
|
let token: U256 = parse_units(1163.56926418, 8).unwrap().into();
|
||||||
assert_eq!(token.as_u64(), 116356926418);
|
assert_eq!(token.as_u64(), 116356926418);
|
||||||
|
|
||||||
let eth_dec_float = parse_units(1.39563324, "ether").unwrap();
|
let eth_dec_float: U256 = parse_units(1.39563324, "ether").unwrap().into();
|
||||||
assert_eq!(eth_dec_float, U256::from_dec_str("1395633240000000000").unwrap());
|
assert_eq!(eth_dec_float, U256::from_dec_str("1395633240000000000").unwrap());
|
||||||
|
|
||||||
let eth_dec_string = parse_units("1.39563324", "ether").unwrap();
|
let eth_dec_string: U256 = parse_units("1.39563324", "ether").unwrap().into();
|
||||||
assert_eq!(eth_dec_string, U256::from_dec_str("1395633240000000000").unwrap());
|
assert_eq!(eth_dec_string, U256::from_dec_str("1395633240000000000").unwrap());
|
||||||
|
|
||||||
let eth = parse_units(1, "ether").unwrap();
|
let eth: U256 = parse_units(1, "ether").unwrap().into();
|
||||||
assert_eq!(eth, WEI_IN_ETHER);
|
assert_eq!(eth, WEI_IN_ETHER);
|
||||||
|
|
||||||
let val = parse_units("2.3", "ether").unwrap();
|
let val: U256 = parse_units("2.3", "ether").unwrap().into();
|
||||||
assert_eq!(val, U256::from_dec_str("2300000000000000000").unwrap());
|
assert_eq!(val, U256::from_dec_str("2300000000000000000").unwrap());
|
||||||
|
|
||||||
assert_eq!(parse_units(".2", 2).unwrap(), U256::from(20), "leading dot");
|
let n: U256 = parse_units(".2", 2).unwrap().into();
|
||||||
assert_eq!(parse_units("333.21", 2).unwrap(), U256::from(33321), "trailing dot");
|
assert_eq!(n, U256::from(20), "leading dot");
|
||||||
assert_eq!(
|
|
||||||
parse_units("98766", 16).unwrap(),
|
let n: U256 = parse_units("333.21", 2).unwrap().into();
|
||||||
U256::from_dec_str("987660000000000000000").unwrap(),
|
assert_eq!(n, U256::from(33321), "trailing dot");
|
||||||
"no dot"
|
|
||||||
);
|
let n: U256 = parse_units("98766", 16).unwrap().into();
|
||||||
assert_eq!(parse_units("3_3_0", 3).unwrap(), U256::from(330000), "underscore");
|
assert_eq!(n, U256::from_dec_str("987660000000000000000").unwrap(), "no dot");
|
||||||
assert_eq!(parse_units("330", 0).unwrap(), U256::from(330), "zero decimals");
|
|
||||||
assert_eq!(parse_units(".1234", 3).unwrap(), U256::from(123), "truncate too many decimals");
|
let n: U256 = parse_units("3_3_0", 3).unwrap().into();
|
||||||
|
assert_eq!(n, U256::from(330000), "underscore");
|
||||||
|
|
||||||
|
let n: U256 = parse_units("330", 0).unwrap().into();
|
||||||
|
assert_eq!(n, U256::from(330), "zero decimals");
|
||||||
|
|
||||||
|
let n: U256 = parse_units(".1234", 3).unwrap().into();
|
||||||
|
assert_eq!(n, U256::from(123), "truncate too many decimals");
|
||||||
|
|
||||||
assert!(parse_units("1", 80).is_err(), "overflow");
|
assert!(parse_units("1", 80).is_err(), "overflow");
|
||||||
assert!(parse_units("1", -1).is_err(), "neg units");
|
assert!(parse_units("1", -1).is_err(), "neg units");
|
||||||
|
|
||||||
let two_e30 = U256::from(2) * U256([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]);
|
let two_e30 = U256::from(2) * U256([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]);
|
||||||
assert_eq!(parse_units("2", 30).unwrap(), two_e30, "2e30");
|
let n: U256 = parse_units("2", 30).unwrap().into();
|
||||||
assert_eq!(parse_units(".33_319_2", 0).unwrap(), U256::zero(), "mix");
|
assert_eq!(n, two_e30, "2e30");
|
||||||
|
|
||||||
|
let n: U256 = parse_units(".33_319_2", 0).unwrap().into();
|
||||||
|
assert_eq!(n, U256::zero(), "mix");
|
||||||
|
|
||||||
|
let n: U256 = parse_units("", 3).unwrap().into();
|
||||||
|
assert_eq!(n, U256::zero(), "empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_signed_parse_units() {
|
||||||
|
let gwei: I256 = parse_units(-1.5, 9).unwrap().into();
|
||||||
|
assert_eq!(gwei.as_i64(), -15e8 as i64);
|
||||||
|
|
||||||
|
let token: I256 = parse_units(-1163.56926418, 8).unwrap().into();
|
||||||
|
assert_eq!(token.as_i64(), -116356926418);
|
||||||
|
|
||||||
|
let eth_dec_float: I256 = parse_units(-1.39563324, "ether").unwrap().into();
|
||||||
|
assert_eq!(eth_dec_float, I256::from_dec_str("-1395633240000000000").unwrap());
|
||||||
|
|
||||||
|
let eth_dec_string: I256 = parse_units("-1.39563324", "ether").unwrap().into();
|
||||||
|
assert_eq!(eth_dec_string, I256::from_dec_str("-1395633240000000000").unwrap());
|
||||||
|
|
||||||
|
let eth: I256 = parse_units(-1, "ether").unwrap().into();
|
||||||
|
assert_eq!(eth, I256::from_raw(WEI_IN_ETHER) * I256::minus_one());
|
||||||
|
|
||||||
|
let val: I256 = parse_units("-2.3", "ether").unwrap().into();
|
||||||
|
assert_eq!(val, I256::from_dec_str("-2300000000000000000").unwrap());
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-.2", 2).unwrap().into();
|
||||||
|
assert_eq!(n, I256::from(-20), "leading dot");
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-333.21", 2).unwrap().into();
|
||||||
|
assert_eq!(n, I256::from(-33321), "trailing dot");
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-98766", 16).unwrap().into();
|
||||||
|
assert_eq!(n, I256::from_dec_str("-987660000000000000000").unwrap(), "no dot");
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-3_3_0", 3).unwrap().into();
|
||||||
|
assert_eq!(n, I256::from(-330000), "underscore");
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-330", 0).unwrap().into();
|
||||||
|
assert_eq!(n, I256::from(-330), "zero decimals");
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-.1234", 3).unwrap().into();
|
||||||
|
assert_eq!(n, I256::from(-123), "truncate too many decimals");
|
||||||
|
|
||||||
|
assert!(parse_units("-1", 80).is_err(), "overflow");
|
||||||
|
|
||||||
|
let two_e30 =
|
||||||
|
I256::from(-2) * I256::from_raw(U256([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]));
|
||||||
|
let n: I256 = parse_units("-2", 30).unwrap().into();
|
||||||
|
assert_eq!(n, two_e30, "-2e30");
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-.33_319_2", 0).unwrap().into();
|
||||||
|
assert_eq!(n, I256::zero(), "mix");
|
||||||
|
|
||||||
|
let n: I256 = parse_units("-", 3).unwrap().into();
|
||||||
|
assert_eq!(n, I256::zero(), "empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -97,6 +97,7 @@ async fn pending_txs_with_confirmations_testnet() {
|
||||||
|
|
||||||
#[cfg(not(feature = "celo"))]
|
#[cfg(not(feature = "celo"))]
|
||||||
use ethers_core::types::{Address, Eip1559TransactionRequest};
|
use ethers_core::types::{Address, Eip1559TransactionRequest};
|
||||||
|
use ethers_core::utils::parse_ether;
|
||||||
|
|
||||||
// different keys to avoid nonce errors
|
// different keys to avoid nonce errors
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
@ -317,7 +318,7 @@ impl TestWallets {
|
||||||
.nonce(nonce)
|
.nonce(nonce)
|
||||||
.to(addr)
|
.to(addr)
|
||||||
// 0.1 eth per wallet
|
// 0.1 eth per wallet
|
||||||
.value(parse_units("1", 18).unwrap());
|
.value(parse_ether("1").unwrap());
|
||||||
pending_txs.push(
|
pending_txs.push(
|
||||||
provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap(),
|
provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue