perf(utils): avoid unnecessary allocations (#2046)
* perf(utils): avoid unnecessary allocations * docs: add more to_checksum information * docs * use address
This commit is contained in:
parent
3c65997eae
commit
b4b153a364
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- Avoid unnecessary allocations in `utils` [#2046](https://github.com/gakonst/ethers-rs/pull/2046)
|
||||||
- Add abigen support for hardhat generated bytecode json format [#2012](https://github.com/gakonst/ethers-rs/pull/2012)
|
- Add abigen support for hardhat generated bytecode json format [#2012](https://github.com/gakonst/ethers-rs/pull/2012)
|
||||||
- Fix typo in `RwClient` docs for `write_client` method.
|
- Fix typo in `RwClient` docs for `write_client` method.
|
||||||
- Add support for Geth `debug_traceCall` [#1949](https://github.com/gakonst/ethers-rs/pull/1949)
|
- Add support for Geth `debug_traceCall` [#1949](https://github.com/gakonst/ethers-rs/pull/1949)
|
||||||
|
|
|
@ -1,36 +1,42 @@
|
||||||
//! Various utilities for manipulating Ethereum related dat
|
//! Various utilities for manipulating Ethereum related data.
|
||||||
|
|
||||||
use ethabi::ethereum_types::H256;
|
use ethabi::ethereum_types::H256;
|
||||||
use tiny_keccak::{Hasher, Keccak};
|
use tiny_keccak::{Hasher, Keccak};
|
||||||
|
|
||||||
|
/// Hash a message according to [EIP-191] (version `0x01`).
|
||||||
|
///
|
||||||
|
/// The final message is a UTF-8 string, encoded as follows:
|
||||||
|
/// `"\x19Ethereum Signed Message:\n" + message.length + message`
|
||||||
|
///
|
||||||
|
/// This message is then hashed using [Keccak-256](keccak256).
|
||||||
|
///
|
||||||
|
/// [EIP-191]: https://eips.ethereum.org/EIPS/eip-191
|
||||||
|
pub fn hash_message<T: AsRef<[u8]>>(message: T) -> H256 {
|
||||||
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
|
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
|
||||||
|
|
||||||
/// Hash a message according to EIP-191.
|
|
||||||
///
|
|
||||||
/// The data is a UTF-8 encoded string and will enveloped as follows:
|
|
||||||
/// `"\x19Ethereum Signed Message:\n" + message.length + message` and hashed
|
|
||||||
/// using keccak256.
|
|
||||||
pub fn hash_message<S>(message: S) -> H256
|
|
||||||
where
|
|
||||||
S: AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
let message = message.as_ref();
|
let message = message.as_ref();
|
||||||
|
let len = message.len();
|
||||||
|
let len_string = len.to_string();
|
||||||
|
|
||||||
let mut eth_message = format!("{PREFIX}{}", message.len()).into_bytes();
|
let mut eth_message = Vec::with_capacity(PREFIX.len() + len_string.len() + len);
|
||||||
|
eth_message.extend_from_slice(PREFIX.as_bytes());
|
||||||
|
eth_message.extend_from_slice(len_string.as_bytes());
|
||||||
eth_message.extend_from_slice(message);
|
eth_message.extend_from_slice(message);
|
||||||
|
|
||||||
keccak256(ð_message).into()
|
H256(keccak256(ð_message))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the Keccak-256 hash of input bytes.
|
/// Compute the Keccak-256 hash of input bytes.
|
||||||
|
///
|
||||||
|
/// Note that strings are interpreted as UTF-8 bytes,
|
||||||
// TODO: Add Solidity Keccak256 packing support
|
// TODO: Add Solidity Keccak256 packing support
|
||||||
pub fn keccak256<S>(bytes: S) -> [u8; 32]
|
pub fn keccak256<T: AsRef<[u8]>>(bytes: T) -> [u8; 32] {
|
||||||
where
|
|
||||||
S: AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
let mut output = [0u8; 32];
|
let mut output = [0u8; 32];
|
||||||
|
|
||||||
let mut hasher = Keccak::v256();
|
let mut hasher = Keccak::v256();
|
||||||
hasher.update(bytes.as_ref());
|
hasher.update(bytes.as_ref());
|
||||||
hasher.finalize(&mut output);
|
hasher.finalize(&mut output);
|
||||||
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +59,7 @@ pub fn id<S: AsRef<str>>(signature: S) -> [u8; 4] {
|
||||||
///
|
///
|
||||||
/// If the type returns an error during serialization.
|
/// If the type returns an error during serialization.
|
||||||
pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
|
pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
|
||||||
serde_json::to_value(t).expect("Types never fail to serialize.")
|
serde_json::to_value(t).expect("Failed to serialize value")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub use rlp;
|
||||||
/// Re-export hex
|
/// Re-export hex
|
||||||
pub use hex;
|
pub use hex;
|
||||||
|
|
||||||
use crate::types::{Address, Bytes, ParseI256Error, I256, U256};
|
use crate::types::{Address, 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};
|
||||||
|
@ -212,10 +212,7 @@ where
|
||||||
/// assert_eq!(eth, parse_ether(1usize).unwrap());
|
/// assert_eq!(eth, parse_ether(1usize).unwrap());
|
||||||
/// assert_eq!(eth, parse_ether("1").unwrap());
|
/// assert_eq!(eth, parse_ether("1").unwrap());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse_ether<S>(eth: S) -> Result<U256, ConversionError>
|
pub fn parse_ether<S: ToString>(eth: S) -> Result<U256, ConversionError> {
|
||||||
where
|
|
||||||
S: ToString,
|
|
||||||
{
|
|
||||||
Ok(parse_units(eth, "ether")?.into())
|
Ok(parse_units(eth, "ether")?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,10 +305,11 @@ pub fn get_contract_address(sender: impl Into<Address>, nonce: impl Into<U256>)
|
||||||
/// keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))[12..]
|
/// keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))[12..]
|
||||||
pub fn get_create2_address(
|
pub fn get_create2_address(
|
||||||
from: impl Into<Address>,
|
from: impl Into<Address>,
|
||||||
salt: impl Into<Bytes>,
|
salt: impl AsRef<[u8]>,
|
||||||
init_code: impl Into<Bytes>,
|
init_code: impl AsRef<[u8]>,
|
||||||
) -> Address {
|
) -> Address {
|
||||||
get_create2_address_from_hash(from, salt, keccak256(init_code.into().as_ref()).to_vec())
|
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
|
/// Returns the CREATE2 address of a smart contract as specified in
|
||||||
|
@ -332,9 +330,7 @@ pub fn get_create2_address(
|
||||||
/// utils::{get_create2_address_from_hash, keccak256},
|
/// utils::{get_create2_address_from_hash, keccak256},
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let UNISWAP_V3_POOL_INIT_CODE_HASH = Bytes::from(
|
/// let init_code_hash = hex::decode("e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54").unwrap();
|
||||||
/// hex::decode("e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54").unwrap(),
|
|
||||||
/// );
|
|
||||||
/// let factory: Address = "0x1F98431c8aD98523631AE4a59f267346ea31F984"
|
/// let factory: Address = "0x1F98431c8aD98523631AE4a59f267346ea31F984"
|
||||||
/// .parse()
|
/// .parse()
|
||||||
/// .unwrap();
|
/// .unwrap();
|
||||||
|
@ -344,19 +340,18 @@ pub fn get_create2_address(
|
||||||
/// let token1: Address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
/// let token1: Address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
||||||
/// .parse()
|
/// .parse()
|
||||||
/// .unwrap();
|
/// .unwrap();
|
||||||
/// let fee = 500;
|
/// let fee = U256::from(500_u64);
|
||||||
///
|
///
|
||||||
/// // abi.encode(token0 as address, token1 as address, fee as uint256)
|
/// // abi.encode(token0 as address, token1 as address, fee as uint256)
|
||||||
/// let input = abi::encode(&vec![
|
/// let input = abi::encode(&[
|
||||||
/// Token::Address(token0),
|
/// Token::Address(token0),
|
||||||
/// Token::Address(token1),
|
/// Token::Address(token1),
|
||||||
/// Token::Uint(U256::from(fee)),
|
/// Token::Uint(fee),
|
||||||
/// ]);
|
/// ]);
|
||||||
///
|
///
|
||||||
/// // keccak256(abi.encode(token0, token1, fee))
|
/// // keccak256(abi.encode(token0, token1, fee))
|
||||||
/// let salt = keccak256(&input);
|
/// let salt = keccak256(&input);
|
||||||
/// let pool_address =
|
/// let pool_address = get_create2_address_from_hash(factory, salt, init_code_hash);
|
||||||
/// get_create2_address_from_hash(factory, salt.to_vec(), UNISWAP_V3_POOL_INIT_CODE_HASH);
|
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// pool_address,
|
/// pool_address,
|
||||||
|
@ -367,12 +362,18 @@ pub fn get_create2_address(
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_create2_address_from_hash(
|
pub fn get_create2_address_from_hash(
|
||||||
from: impl Into<Address>,
|
from: impl Into<Address>,
|
||||||
salt: impl Into<Bytes>,
|
salt: impl AsRef<[u8]>,
|
||||||
init_code_hash: impl Into<Bytes>,
|
init_code_hash: impl AsRef<[u8]>,
|
||||||
) -> Address {
|
) -> Address {
|
||||||
let bytes =
|
let from = from.into();
|
||||||
[&[0xff], from.into().as_bytes(), salt.into().as_ref(), init_code_hash.into().as_ref()]
|
let salt = salt.as_ref();
|
||||||
.concat();
|
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 hash = keccak256(bytes);
|
||||||
|
|
||||||
|
@ -388,11 +389,20 @@ pub fn secret_key_to_address(secret_key: &SigningKey) -> Address {
|
||||||
let public_key = public_key.as_bytes();
|
let public_key = public_key.as_bytes();
|
||||||
debug_assert_eq!(public_key[0], 0x04);
|
debug_assert_eq!(public_key[0], 0x04);
|
||||||
let hash = keccak256(&public_key[1..]);
|
let hash = keccak256(&public_key[1..]);
|
||||||
Address::from_slice(&hash[12..])
|
|
||||||
|
let mut bytes = [0u8; 20];
|
||||||
|
bytes.copy_from_slice(&hash[12..]);
|
||||||
|
Address::from(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an Ethereum address to the checksum encoding
|
/// Encodes an Ethereum address to its [EIP-55] checksum.
|
||||||
/// Ref: <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md>
|
///
|
||||||
|
/// 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<u8>) -> String {
|
pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
|
||||||
let prefixed_addr = match chain_id {
|
let prefixed_addr = match chain_id {
|
||||||
Some(chain_id) => format!("{chain_id}0x{addr:x}"),
|
Some(chain_id) => format!("{chain_id}0x{addr:x}"),
|
||||||
|
|
Loading…
Reference in New Issue