/// Utilities for launching a ganache-cli testnet instance #[cfg(not(target_arch = "wasm32"))] mod ganache; #[cfg(not(target_arch = "wasm32"))] pub use ganache::{Ganache, GanacheInstance}; /// Utilities for launching a go-ethereum dev-mode instance #[cfg(not(target_arch = "wasm32"))] mod geth; #[cfg(not(target_arch = "wasm32"))] pub use geth::{Geth, GethInstance}; /// Utilities for working with a `genesis.json` and other chain config structs. mod genesis; pub use genesis::{ChainConfig, CliqueConfig, EthashConfig, Genesis, GenesisAccount}; /// Utilities for launching an anvil instance #[cfg(not(target_arch = "wasm32"))] mod anvil; #[cfg(not(target_arch = "wasm32"))] pub use anvil::{Anvil, AnvilInstance}; /// Moonbeam utils pub mod moonbeam; mod hash; pub use hash::{hash_message, id, keccak256, serialize}; mod units; use serde::{Deserialize, Deserializer}; pub use units::Units; /// Re-export RLP pub use rlp; /// Re-export hex pub use hex; use crate::types::{Address, Bytes, ParseI256Error, H256, I256, U256, U64}; use ethabi::ethereum_types::FromDecStrErr; use k256::ecdsa::SigningKey; use std::{ collections::HashMap, convert::{TryFrom, TryInto}, fmt, str::FromStr, }; use thiserror::Error; /// I256 overflows for numbers wider than 77 units. const OVERFLOW_I256_UNITS: usize = 77; /// U256 overflows for numbers wider than 78 units. const OVERFLOW_U256_UNITS: usize = 78; /// Re-export of serde-json #[doc(hidden)] pub mod __serde_json { pub use serde_json::*; } #[derive(Error, Debug)] pub enum ConversionError { #[error("Unknown units: {0}")] UnrecognizedUnits(String), #[error("bytes32 strings must not exceed 32 bytes in length")] TextTooLong, #[error(transparent)] Utf8Error(#[from] std::str::Utf8Error), #[error(transparent)] InvalidFloat(#[from] std::num::ParseFloatError), #[error(transparent)] FromDecStrError(#[from] FromDecStrErr), #[error("Overflow parsing string")] ParseOverflow, #[error(transparent)] ParseI256Error(#[from] ParseI256Error), } /// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei pub const WEI_IN_ETHER: U256 = U256([0x0de0b6b3a7640000, 0x0, 0x0, 0x0]); /// The number of blocks from the past for which the fee rewards are fetched for fee estimation. pub const EIP1559_FEE_ESTIMATION_PAST_BLOCKS: u64 = 10; /// The default percentile of gas premiums that are fetched for fee estimation. pub const EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE: f64 = 5.0; /// The default max priority fee per gas, used in case the base fee is within a threshold. pub const EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE: u64 = 3_000_000_000; /// The threshold for base fee below which we use the default priority fee, and beyond which we /// estimate an appropriate value for priority fee. pub const EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER: u64 = 100_000_000_000; /// The threshold max change/difference (in %) at which we will ignore the fee history values /// under it. 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, Debug, Ord, PartialOrd, Eq, PartialEq)] pub enum ParseUnits { U256(U256), I256(I256), } impl From for U256 { fn from(n: ParseUnits) -> Self { match n { ParseUnits::U256(n) => n, ParseUnits::I256(n) => n.into_raw(), } } } impl fmt::Display for ParseUnits { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParseUnits::U256(val) => val.fmt(f), ParseUnits::I256(val) => val.fmt(f), } } } 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 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 /// in ether (instead of wei) /// /// Divides the input by 1e18 pub fn format_ether>(amount: T) -> U256 { amount.into() / WEI_IN_ETHER } /// Divides the provided amount with 10^{units} provided. /// /// ``` /// use ethers_core::{types::U256, utils::format_units}; /// /// let eth = format_units(1395633240123456000_u128, "ether").unwrap(); /// assert_eq!(eth.parse::().unwrap(), 1.395633240123456); /// /// let eth = format_units(U256::from_dec_str("1395633240123456000").unwrap(), "ether").unwrap(); /// assert_eq!(eth.parse::().unwrap(), 1.395633240123456); /// /// let eth = format_units(U256::from_dec_str("1395633240123456789").unwrap(), "ether").unwrap(); /// 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(amount: T, units: K) -> Result where T: Into, K: TryInto, { let units: usize = units.try_into()?.into(); let amount = amount.into(); match amount { // 2**256 ~= 1.16e77 ParseUnits::U256(_) if units >= OVERFLOW_U256_UNITS => { return Err(ConversionError::ParseOverflow) } // 2**255 ~= 5.79e76 ParseUnits::I256(_) if units >= OVERFLOW_I256_UNITS => { return Err(ConversionError::ParseOverflow) } _ => {} }; let exp10 = U256::exp10(units); // `decimals` are formatted twice because U256 does not support alignment (`:0>width`). match amount { ParseUnits::U256(amount) => { let integer = amount / exp10; let decimals = (amount % exp10).to_string(); Ok(format!("{integer}.{decimals:0>units$}")) } ParseUnits::I256(amount) => { let exp10 = I256::from_raw(exp10); let sign = if amount.is_negative() { "-" } else { "" }; let integer = (amount / exp10).twos_complement(); let decimals = ((amount % exp10).twos_complement()).to_string(); Ok(format!("{sign}{integer}.{decimals:0>units$}")) } } } /// Converts the input to a U256 and converts from Ether to Wei. /// /// ``` /// use ethers_core::{types::U256, utils::{parse_ether, WEI_IN_ETHER}}; /// /// let eth = U256::from(WEI_IN_ETHER); /// 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(eth: S) -> Result { Ok(parse_units(eth, "ether")?.into()) } /// Multiplies the provided amount with 10^{units} provided. /// /// ``` /// 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().into()); /// assert_eq!(amount_in_gwei, parse_units("15.230001000000000000", "gwei").unwrap().into()); /// 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 /// 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().into()); /// ``` pub fn parse_units(amount: S, units: K) -> Result where S: ToString, K: TryInto + Copy, { let exponent: u32 = units.try_into()?.as_num(); 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('.') { amount_str.remove(di); amount_str[di..].len() as u32 } else { 0 }; if dec_len > 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)]; if negative { // 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 { let mut a_uint = U256::from_dec_str(&amount_str)?; a_uint *= U256::from(10) .checked_pow(U256::from(exponent - dec_len)) .ok_or(ConversionError::ParseOverflow)?; Ok(ParseUnits::U256(a_uint)) } } /// 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. pub fn get_contract_address(sender: impl Into
, nonce: impl Into) -> Address { let mut stream = rlp::RlpStream::new(); stream.begin_list(2); stream.append(&sender.into()); stream.append(&nonce.into()); let hash = keccak256(&stream.out()); let mut bytes = [0u8; 20]; bytes.copy_from_slice(&hash[12..]); Address::from(bytes) } /// Returns the CREATE2 address of a smart contract as specified in /// [EIP1014](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1014.md) /// /// keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))[12..] pub fn get_create2_address( from: impl Into
, salt: impl AsRef<[u8]>, init_code: impl AsRef<[u8]>, ) -> Address { let init_code_hash = keccak256(init_code.as_ref()); get_create2_address_from_hash(from, salt, init_code_hash) } /// Returns the CREATE2 address of a smart contract as specified in /// [EIP1014](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1014.md), /// taking the pre-computed hash of the init code as input. /// /// keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))[12..] /// /// # Example /// /// Calculate the address of a UniswapV3 pool. /// /// ``` /// use ethers_core::{ /// abi, /// abi::Token, /// types::{Address, Bytes, U256}, /// utils::{get_create2_address_from_hash, keccak256}, /// }; /// /// let init_code_hash = hex::decode("e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54").unwrap(); /// let factory: Address = "0x1F98431c8aD98523631AE4a59f267346ea31F984" /// .parse() /// .unwrap(); /// let token0: Address = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" /// .parse() /// .unwrap(); /// let token1: Address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" /// .parse() /// .unwrap(); /// let fee = U256::from(500_u64); /// /// // abi.encode(token0 as address, token1 as address, fee as uint256) /// let input = abi::encode(&[ /// Token::Address(token0), /// Token::Address(token1), /// Token::Uint(fee), /// ]); /// /// // keccak256(abi.encode(token0, token1, fee)) /// let salt = keccak256(&input); /// let pool_address = get_create2_address_from_hash(factory, salt, init_code_hash); /// /// assert_eq!( /// pool_address, /// "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640" // USDC/ETH pool address /// .parse() /// .unwrap() /// ); /// ``` pub fn get_create2_address_from_hash( from: impl Into
, salt: impl AsRef<[u8]>, init_code_hash: impl AsRef<[u8]>, ) -> Address { let from = from.into(); let salt = salt.as_ref(); let init_code_hash = init_code_hash.as_ref(); let mut bytes = Vec::with_capacity(1 + 20 + salt.len() + init_code_hash.len()); bytes.push(0xff); bytes.extend_from_slice(from.as_bytes()); bytes.extend_from_slice(salt); bytes.extend_from_slice(init_code_hash); let hash = keccak256(bytes); let mut bytes = [0u8; 20]; bytes.copy_from_slice(&hash[12..]); Address::from(bytes) } /// Converts a K256 SigningKey to an Ethereum Address pub fn secret_key_to_address(secret_key: &SigningKey) -> Address { let public_key = secret_key.verifying_key(); let public_key = public_key.to_encoded_point(/* compress = */ false); let public_key = public_key.as_bytes(); debug_assert_eq!(public_key[0], 0x04); let hash = keccak256(&public_key[1..]); let mut bytes = [0u8; 20]; bytes.copy_from_slice(&hash[12..]); Address::from(bytes) } /// Encodes an Ethereum address to its [EIP-55] checksum. /// /// You can optionally specify an [EIP-155 chain ID] to encode the address using the [EIP-1191] /// extension. /// /// [EIP-55]: https://eips.ethereum.org/EIPS/eip-55 /// [EIP-155 chain ID]: https://eips.ethereum.org/EIPS/eip-155 /// [EIP-1191]: https://eips.ethereum.org/EIPS/eip-1191 pub fn to_checksum(addr: &Address, chain_id: Option) -> String { let prefixed_addr = match chain_id { Some(chain_id) => format!("{chain_id}0x{addr:x}"), None => format!("{addr:x}"), }; let hash = hex::encode(keccak256(prefixed_addr)); let hash = hash.as_bytes(); let addr_hex = hex::encode(addr.as_bytes()); let addr_hex = addr_hex.as_bytes(); addr_hex.iter().zip(hash).fold("0x".to_owned(), |mut encoded, (addr, hash)| { encoded.push(if *hash >= 56 { addr.to_ascii_uppercase() as char } else { addr.to_ascii_lowercase() as char }); encoded }) } /// Returns a bytes32 string representation of text. If the length of text exceeds 32 bytes, /// an error is returned. pub fn format_bytes32_string(text: &str) -> Result<[u8; 32], ConversionError> { let str_bytes: &[u8] = text.as_bytes(); if str_bytes.len() > 32 { return Err(ConversionError::TextTooLong) } let mut bytes32: [u8; 32] = [0u8; 32]; bytes32[..str_bytes.len()].copy_from_slice(str_bytes); Ok(bytes32) } /// Returns the decoded string represented by the bytes32 encoded data. pub fn parse_bytes32_string(bytes: &[u8; 32]) -> Result<&str, ConversionError> { let mut length = 0; while length < 32 && bytes[length] != 0 { length += 1; } Ok(std::str::from_utf8(&bytes[..length])?) } /// The default EIP-1559 fee estimator which is based on the work by [MyCrypto](https://github.com/MyCryptoHQ/MyCrypto/blob/master/src/services/ApiService/Gas/eip1559.ts) pub fn eip1559_default_estimator(base_fee_per_gas: U256, rewards: Vec>) -> (U256, U256) { let max_priority_fee_per_gas = if base_fee_per_gas < U256::from(EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER) { U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE) } else { std::cmp::max( estimate_priority_fee(rewards), U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE), ) }; let potential_max_fee = base_fee_surged(base_fee_per_gas); let max_fee_per_gas = if max_priority_fee_per_gas > potential_max_fee { max_priority_fee_per_gas + potential_max_fee } else { potential_max_fee }; (max_fee_per_gas, max_priority_fee_per_gas) } /// Converts a Bytes value into a H256, accepting inputs that are less than 32 bytes long. These /// inputs will be left padded with zeros. pub fn from_bytes_to_h256<'de, D>(bytes: Bytes) -> Result where D: Deserializer<'de>, { if bytes.0.len() > 32 { return Err(serde::de::Error::custom("input too long to be a H256")) } // left pad with zeros to 32 bytes let mut padded = [0u8; 32]; padded[32 - bytes.0.len()..].copy_from_slice(&bytes.0); // then convert to H256 without a panic Ok(H256::from_slice(&padded)) } /// Deserializes the input into an Option>, using from_unformatted_hex to /// deserialize the keys and values. pub fn from_unformatted_hex_map<'de, D>( deserializer: D, ) -> Result>, D::Error> where D: Deserializer<'de>, { let map = Option::>::deserialize(deserializer)?; match map { Some(mut map) => { let mut res_map = HashMap::new(); for (k, v) in map.drain() { let k_deserialized = from_bytes_to_h256::<'de, D>(k)?; let v_deserialized = from_bytes_to_h256::<'de, D>(v)?; res_map.insert(k_deserialized, v_deserialized); } Ok(Some(res_map)) } None => Ok(None), } } /// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with /// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number). pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(untagged)] enum IntOrHex { Int(serde_json::Number), Hex(String), } match IntOrHex::deserialize(deserializer)? { IntOrHex::Hex(s) => U256::from_str(s.as_str()).map_err(serde::de::Error::custom), IntOrHex::Int(n) => U256::from_dec_str(&n.to_string()).map_err(serde::de::Error::custom), } } /// Deserializes the input into a U64, accepting both 0x-prefixed hex and decimal strings with /// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number). pub fn from_u64_or_hex<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(untagged)] enum IntOrHex { Int(serde_json::Number), Hex(String), } match IntOrHex::deserialize(deserializer)? { IntOrHex::Hex(s) => U64::from_str(s.as_str()).map_err(serde::de::Error::custom), IntOrHex::Int(n) => U64::from_dec_str(&n.to_string()).map_err(serde::de::Error::custom), } } /// Deserializes the input into an `Option`, using [`from_int_or_hex`] to deserialize the /// inner value. pub fn from_int_or_hex_opt<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(Some(from_int_or_hex(deserializer)?)) } /// Deserializes the input into an `Option`, using [`from_u64_or_hex`] to deserialize the /// inner value. pub fn from_u64_or_hex_opt<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(Some(from_u64_or_hex(deserializer)?.as_u64())) } fn estimate_priority_fee(rewards: Vec>) -> U256 { let mut rewards: Vec = rewards.iter().map(|r| r[0]).filter(|r| *r > U256::zero()).collect(); if rewards.is_empty() { return U256::zero() } if rewards.len() == 1 { return rewards[0] } // Sort the rewards as we will eventually take the median. rewards.sort(); // A copy of the same vector is created for convenience to calculate percentage change // between subsequent fee values. let mut rewards_copy = rewards.clone(); rewards_copy.rotate_left(1); let mut percentage_change: Vec = rewards .iter() .zip(rewards_copy.iter()) .map(|(a, b)| { let a = I256::try_from(*a).expect("priority fee overflow"); let b = I256::try_from(*b).expect("priority fee overflow"); ((b - a) * 100) / a }) .collect(); percentage_change.pop(); // Fetch the max of the percentage change, and that element's index. let max_change = percentage_change.iter().max().unwrap(); let max_change_index = percentage_change.iter().position(|&c| c == *max_change).unwrap(); // If we encountered a big change in fees at a certain position, then consider only // the values >= it. let values = if *max_change >= EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE.into() && (max_change_index >= (rewards.len() / 2)) { rewards[max_change_index..].to_vec() } else { rewards }; // Return the median. values[values.len() / 2] } fn base_fee_surged(base_fee_per_gas: U256) -> U256 { if base_fee_per_gas <= U256::from(40_000_000_000u64) { base_fee_per_gas * 2 } else if base_fee_per_gas <= U256::from(100_000_000_000u64) { base_fee_per_gas * 16 / 10 } else if base_fee_per_gas <= U256::from(200_000_000_000u64) { base_fee_per_gas * 14 / 10 } else { base_fee_per_gas * 12 / 10 } } /// A bit of hack to find unused TCP ports. /// /// Does not guarantee that the given port is unused after the function exists, just that it was /// unused before the function started (i.e., it does not reserve a port). #[cfg(not(target_arch = "wasm32"))] pub(crate) fn unused_ports() -> [u16; N] { use std::net::{SocketAddr, TcpListener}; std::array::from_fn(|_| { let addr = SocketAddr::from(([127, 0, 0, 1], 0)); TcpListener::bind(addr).expect("Failed to create TCP listener to find unused port") }) .map(|listener| { listener .local_addr() .expect("Failed to read TCP listener local_addr to find unused port") .port() }) } #[cfg(test)] mod tests { use super::*; use hex_literal::hex; #[test] fn wei_in_ether() { assert_eq!(WEI_IN_ETHER.as_u64(), 1e18 as u64); } #[test] fn test_format_units_unsigned() { let gwei_in_ether = format_units(WEI_IN_ETHER, 9).unwrap(); assert_eq!(gwei_in_ether.parse::().unwrap() as u64, 1e9 as u64); let eth = format_units(WEI_IN_ETHER, "ether").unwrap(); assert_eq!(eth.parse::().unwrap() as u64, 1); let eth = format_units(1395633240123456000_u128, "ether").unwrap(); assert_eq!(eth.parse::().unwrap(), 1.395633240123456); let eth = format_units(U256::from_dec_str("1395633240123456000").unwrap(), "ether").unwrap(); assert_eq!(eth.parse::().unwrap(), 1.395633240123456); let eth = format_units(U256::from_dec_str("1395633240123456789").unwrap(), "ether").unwrap(); assert_eq!(eth, "1.395633240123456789"); let eth = format_units(U256::from_dec_str("1005633240123456789").unwrap(), "ether").unwrap(); assert_eq!(eth, "1.005633240123456789"); let eth = format_units(u8::MAX, 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"); let eth = format_units(U256::MAX, 77).unwrap(); assert_eq!( eth, "1.15792089237316195423570985008687907853269984665640564039457584007913129639935" ); let err = format_units(U256::MAX, 78).unwrap_err(); assert!(matches!(err, ConversionError::ParseOverflow)); } #[test] fn test_format_units_signed() { let eth = format_units(I256::from_dec_str("-1395633240123456000").unwrap(), "ether").unwrap(); assert_eq!(eth.parse::().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::().unwrap(), -0.0128_f64); let eth = format_units(i8::MAX, 4).unwrap(); assert_eq!(eth, "0.0127"); assert_eq!(eth.parse::().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"); let eth = format_units(I256::MIN, 76).unwrap(); assert_eq!( eth, "-5.7896044618658097711785492504343953926634992332820282019728792003956564819968" ); let err = format_units(I256::MIN, 77).unwrap_err(); assert!(matches!(err, ConversionError::ParseOverflow)); } #[test] fn parse_large_units() { let decimals = 27u32; let val = "10.55"; let n: U256 = parse_units(val, decimals).unwrap().into(); assert_eq!(n.to_string(), "10550000000000000000000000000"); } #[test] fn test_parse_units() { let gwei: U256 = parse_units(1.5, 9).unwrap().into(); assert_eq!(gwei.as_u64(), 15e8 as u64); let token: U256 = parse_units(1163.56926418, 8).unwrap().into(); assert_eq!(token.as_u64(), 116356926418); let eth_dec_float: U256 = parse_units(1.39563324, "ether").unwrap().into(); assert_eq!(eth_dec_float, U256::from_dec_str("1395633240000000000").unwrap()); let eth_dec_string: U256 = parse_units("1.39563324", "ether").unwrap().into(); assert_eq!(eth_dec_string, U256::from_dec_str("1395633240000000000").unwrap()); let eth: U256 = parse_units(1, "ether").unwrap().into(); assert_eq!(eth, WEI_IN_ETHER); let val: U256 = parse_units("2.3", "ether").unwrap().into(); assert_eq!(val, U256::from_dec_str("2300000000000000000").unwrap()); let n: U256 = parse_units(".2", 2).unwrap().into(); assert_eq!(n, U256::from(20), "leading dot"); let n: U256 = parse_units("333.21", 2).unwrap().into(); assert_eq!(n, U256::from(33321), "trailing dot"); let n: U256 = parse_units("98766", 16).unwrap().into(); assert_eq!(n, U256::from_dec_str("987660000000000000000").unwrap(), "no dot"); 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", -1).is_err(), "neg units"); let two_e30 = U256::from(2) * U256([0x4674edea40000000, 0xc9f2c9cd0, 0x0, 0x0]); let n: U256 = parse_units("2", 30).unwrap().into(); 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] fn addr_checksum() { let addr_list = vec![ // mainnet ( None, "27b1fdb04752bbc536007a920d24acb045561c26", "0x27b1fdb04752bbc536007a920d24acb045561c26", ), ( None, "3599689e6292b81b2d85451025146515070129bb", "0x3599689E6292b81B2d85451025146515070129Bb", ), ( None, "42712d45473476b98452f434e72461577d686318", "0x42712D45473476b98452f434e72461577D686318", ), ( None, "52908400098527886e0f7030069857d2e4169ee7", "0x52908400098527886E0F7030069857D2E4169EE7", ), ( None, "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", ), ( None, "6549f4939460de12611948b3f82b88c3c8975323", "0x6549f4939460DE12611948b3f82b88C3C8975323", ), ( None, "66f9664f97f2b50f62d13ea064982f936de76657", "0x66f9664f97F2b50F62D13eA064982f936dE76657", ), ( None, "88021160c5c792225e4e5452585947470010289d", "0x88021160C5C792225E4E5452585947470010289D", ), // rsk mainnet ( Some(30), "27b1fdb04752bbc536007a920d24acb045561c26", "0x27b1FdB04752BBc536007A920D24ACB045561c26", ), ( Some(30), "3599689e6292b81b2d85451025146515070129bb", "0x3599689E6292B81B2D85451025146515070129Bb", ), ( Some(30), "42712d45473476b98452f434e72461577d686318", "0x42712D45473476B98452f434E72461577d686318", ), ( Some(30), "52908400098527886e0f7030069857d2e4169ee7", "0x52908400098527886E0F7030069857D2E4169ee7", ), ( Some(30), "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", "0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", ), ( Some(30), "6549f4939460de12611948b3f82b88c3c8975323", "0x6549F4939460DE12611948B3F82B88C3C8975323", ), ( Some(30), "66f9664f97f2b50f62d13ea064982f936de76657", "0x66F9664f97f2B50F62d13EA064982F936de76657", ), ]; for (chain_id, addr, checksummed_addr) in addr_list { let addr = addr.parse::
().unwrap(); assert_eq!(to_checksum(&addr, chain_id), String::from(checksummed_addr)); } } #[test] fn contract_address() { // http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed let from = "6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0".parse::
().unwrap(); for (nonce, expected) in [ "cd234a471b72ba2f1ccf0a70fcaba648a5eecd8d", "343c43a37d37dff08ae8c4a11544c718abb4fcf8", "f778b86fa74e846c4f0a1fbd1335fe81c00a0c91", "fffd933a0bc612844eaf0c6fe3e5b8e9b6c1d19c", ] .iter() .enumerate() { let address = get_contract_address(from, nonce); assert_eq!(address, expected.parse::
().unwrap()); } } #[test] // Test vectors from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1014.md#examples fn create2_address() { for (from, salt, init_code, expected) in &[ ( "0000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "00", "4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38", ), ( "deadbeef00000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "00", "B928f69Bb1D91Cd65274e3c79d8986362984fDA3", ), ( "deadbeef00000000000000000000000000000000", "000000000000000000000000feed000000000000000000000000000000000000", "00", "D04116cDd17beBE565EB2422F2497E06cC1C9833", ), ( "0000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "deadbeef", "70f2b2914A2a4b783FaEFb75f459A580616Fcb5e", ), ( "00000000000000000000000000000000deadbeef", "00000000000000000000000000000000000000000000000000000000cafebabe", "deadbeef", "60f3f640a8508fC6a86d45DF051962668E1e8AC7", ), ( "00000000000000000000000000000000deadbeef", "00000000000000000000000000000000000000000000000000000000cafebabe", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C", ), ( "0000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "", "E33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0", ), ] { // get_create2_address() let from = from.parse::
().unwrap(); let salt = hex::decode(salt).unwrap(); let init_code = hex::decode(init_code).unwrap(); let expected = expected.parse::
().unwrap(); assert_eq!(expected, get_create2_address(from, salt.clone(), init_code.clone())); // get_create2_address_from_hash() let init_code_hash = keccak256(init_code).to_vec(); assert_eq!(expected, get_create2_address_from_hash(from, salt, init_code_hash)) } } #[test] fn bytes32_string_parsing() { let text_bytes_list = vec![ ("", hex!("0000000000000000000000000000000000000000000000000000000000000000")), ("A", hex!("4100000000000000000000000000000000000000000000000000000000000000")), ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345", hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"), ), ( "!@#$%^&*(),./;'[]", hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"), ), ]; for (text, bytes) in text_bytes_list { assert_eq!(text, parse_bytes32_string(&bytes).unwrap()); } } #[test] fn bytes32_string_formatting() { let text_bytes_list = vec![ ("", hex!("0000000000000000000000000000000000000000000000000000000000000000")), ("A", hex!("4100000000000000000000000000000000000000000000000000000000000000")), ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345", hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"), ), ( "!@#$%^&*(),./;'[]", hex!("21402324255e262a28292c2e2f3b275b5d000000000000000000000000000000"), ), ]; for (text, bytes) in text_bytes_list { assert_eq!(bytes, format_bytes32_string(text).unwrap()); } } #[test] fn bytes32_string_formatting_too_long() { assert!(matches!( format_bytes32_string("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456").unwrap_err(), ConversionError::TextTooLong )); } #[test] fn test_eip1559_default_estimator() { // If the base fee is below the triggering base fee, we should get the default priority fee // with the base fee surged. let base_fee_per_gas = U256::from(EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER) - 1; let rewards: Vec> = vec![vec![]]; let (base_fee, priority_fee) = eip1559_default_estimator(base_fee_per_gas, rewards); assert_eq!(priority_fee, U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE)); assert_eq!(base_fee, base_fee_surged(base_fee_per_gas)); // If the base fee is above the triggering base fee, we calculate the priority fee using // the fee history (rewards). let base_fee_per_gas = U256::from(EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER) + 1; let rewards: Vec> = vec![ vec![100_000_000_000u64.into()], vec![105_000_000_000u64.into()], vec![102_000_000_000u64.into()], ]; // say, last 3 blocks let (base_fee, priority_fee) = eip1559_default_estimator(base_fee_per_gas, rewards.clone()); assert_eq!(base_fee, base_fee_surged(base_fee_per_gas)); assert_eq!(priority_fee, estimate_priority_fee(rewards.clone())); // The median should be taken because none of the changes are big enough to ignore values. assert_eq!(estimate_priority_fee(rewards), 102_000_000_000u64.into()); // Ensure fee estimation doesn't panic when overflowing a u32. This had been a divide by // zero. let overflow = U256::from(u32::MAX) + 1; let rewards_overflow: Vec> = vec![vec![overflow], vec![overflow]]; assert_eq!(estimate_priority_fee(rewards_overflow), overflow); } }