diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs index 3d0ffc73..e874dc39 100644 --- a/ethers-core/src/utils/mod.rs +++ b/ethers-core/src/utils/mod.rs @@ -8,3 +8,157 @@ pub use solc::{CompiledContract, Solc}; mod hash; pub use hash::{hash_message, id, keccak256, serialize}; + +/// Re-export RLP +pub use rlp; + +use crate::types::{Address, Bytes, U256}; + +/// 1 Ether = 1e18 Wei +pub const WEI: usize = 1000000000000000000; + +/// 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 +} + +/// Divides with the number of decimals +pub fn format_units>(amount: T, decimals: usize) -> U256 { + amount.into() / decimals +} + +/// Converts a string to a U256 and converts from Ether to Wei. +/// +/// Multiplies the input by 1e18 +pub fn parse_ether(eth: &str) -> Result { + Ok(eth.parse::()? * WEI) +} + +/// Multiplies with the number of decimals +pub fn parse_units(eth: &str, decimals: usize) -> Result { + Ok(eth.parse::()? * decimals) +} + +/// 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 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 Into, + init_code: impl Into, +) -> Address { + let bytes = [ + &[0xff], + from.into().as_bytes(), + salt.into().as_ref(), + &keccak256(init_code.into().as_ref()), + ] + .concat(); + + let hash = keccak256(&bytes); + + let mut bytes = [0u8; 20]; + bytes.copy_from_slice(&hash[12..]); + Address::from(bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + use rustc_hex::FromHex; + + #[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", + ), + ] { + let from = from.parse::
().unwrap(); + let salt = salt.from_hex::>().unwrap(); + let init_code = init_code.from_hex::>().unwrap(); + let expected = expected.parse::
().unwrap(); + assert_eq!(expected, get_create2_address(from, salt, init_code)) + } + } +} diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index 0510466e..8c6fe0f2 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -182,9 +182,14 @@ impl Provider

{ /// Returns the nonce of the address pub async fn get_transaction_count( &self, - from: Address, + from: impl Into, block: Option, ) -> Result { + let from = match from.into() { + NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?, + NameOrAddress::Address(addr) => addr, + }; + let from = utils::serialize(&from); let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest)); Ok(self @@ -197,9 +202,14 @@ impl Provider

{ /// Returns the account's balance pub async fn get_balance( &self, - from: Address, + from: impl Into, block: Option, ) -> Result { + let from = match from.into() { + NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?, + NameOrAddress::Address(addr) => addr, + }; + let from = utils::serialize(&from); let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest)); Ok(self