use ethers::abi::{encode, Token};
use ethers::types::{Address, H256};
use ethers::utils::keccak256;
use std::str::FromStr;
use crate::contracts;
use crate::error::{RelayerError, Result};
fn create2_address(deployer: Address, salt: H256, init_code_hash: H256) -> Address {
let mut buf = Vec::with_capacity(1 + 20 + 32 + 32);
buf.push(0xff);
buf.extend_from_slice(deployer.as_bytes());
buf.extend_from_slice(salt.as_bytes());
buf.extend_from_slice(init_code_hash.as_bytes());
let hash = keccak256(&buf);
Address::from_slice(&hash[12..])
}
pub fn derive_safe_address(signer: Address) -> Result<Address> {
let factory: Address = contracts::SAFE_FACTORY
.parse()
.map_err(|e: <Address as std::str::FromStr>::Err| RelayerError::InvalidAddress(e.to_string()))?;
let init_code_hash: H256 = contracts::SAFE_INIT_CODE_HASH
.parse()
.map_err(|e: <H256 as std::str::FromStr>::Err| RelayerError::Other(e.to_string()))?;
let mut encoded = [0u8; 32];
encoded[12..32].copy_from_slice(signer.as_bytes());
let salt = H256::from(keccak256(encoded));
Ok(create2_address(factory, salt, init_code_hash))
}
pub fn derive_proxy_address(signer: Address) -> Result<Address> {
let factory: Address = contracts::PROXY_FACTORY
.parse()
.map_err(|e: <Address as std::str::FromStr>::Err| RelayerError::InvalidAddress(e.to_string()))?;
let init_code_hash: H256 = contracts::PROXY_INIT_CODE_HASH
.parse()
.map_err(|e: <H256 as std::str::FromStr>::Err| RelayerError::Other(e.to_string()))?;
let salt = H256::from(keccak256(signer.as_bytes()));
Ok(create2_address(factory, salt, init_code_hash))
}
pub fn init_code_hash_erc1967(implementation: Address, args: &[u8]) -> Result<H256> {
let n = args.len();
if n > u8::MAX as usize {
return Err(RelayerError::Other(format!(
"ERC1967 args too long: {n} bytes (max 255)"
)));
}
let mut prefix = contracts::ERC1967_INIT_CODE_PREFIX;
let current_val = u16::from_be_bytes([prefix[1], prefix[2]]);
let new_val = current_val.checked_add(n as u16).unwrap();
let new_bytes = new_val.to_be_bytes();
prefix[1] = new_bytes[0];
prefix[2] = new_bytes[1];
let mut pre_image =
Vec::with_capacity(prefix.len() + 20 + 2 + 32 + 32 + args.len());
pre_image.extend_from_slice(&prefix);
pre_image.extend_from_slice(implementation.as_bytes());
pre_image.extend_from_slice(&contracts::ERC1967_MID);
pre_image.extend_from_slice(&contracts::ERC1967_CONST2);
pre_image.extend_from_slice(&contracts::ERC1967_CONST1);
pre_image.extend_from_slice(args);
Ok(H256::from(keccak256(&pre_image)))
}
pub fn derive_deposit_wallet_address(
owner: Address,
factory_addr: &str,
implementation_addr: &str,
) -> Result<Address> {
let factory = Address::from_str(factory_addr)
.map_err(|e| RelayerError::InvalidAddress(e.to_string()))?;
let implementation = Address::from_str(implementation_addr)
.map_err(|e| RelayerError::InvalidAddress(e.to_string()))?;
let mut wallet_id = [0u8; 32];
wallet_id[12..32].copy_from_slice(owner.as_bytes());
let args = encode(&[
Token::Address(factory),
Token::FixedBytes(wallet_id.to_vec()),
]);
let salt = H256::from(keccak256(&args));
let init_code_hash = init_code_hash_erc1967(implementation, &args)?;
Ok(create2_address(factory, salt, init_code_hash))
}
pub fn derive_deposit_wallet_address_for_chain(
owner: Address,
chain_id: u64,
) -> Result<Address> {
let implementation = contracts::deposit_wallet_implementation(chain_id)?;
derive_deposit_wallet_address(owner, contracts::DEPOSIT_WALLET_FACTORY, implementation)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_derive_safe_address() {
let signer: Address = "0x6e0c80c90ea6c15917308F820Eac91Ce2724B5b5"
.parse()
.unwrap();
let derived = derive_safe_address(signer).unwrap();
assert_ne!(derived, Address::zero());
assert_eq!(derived, derive_safe_address(signer).unwrap());
}
#[test]
fn deposit_wallet_args_len_is_64() {
let factory: Address = contracts::DEPOSIT_WALLET_FACTORY.parse().unwrap();
let owner: Address = "0x0000000000000000000000000000000000000001"
.parse()
.unwrap();
let mut wallet_id = [0u8; 32];
wallet_id[12..32].copy_from_slice(owner.as_bytes());
let args = encode(&[
Token::Address(factory),
Token::FixedBytes(wallet_id.to_vec()),
]);
assert_eq!(args.len(), 64);
}
#[test]
fn deposit_wallet_address_is_deterministic_and_nonzero() {
let owner: Address = "0xE51282BdEeeb988406B3f969a6277b02bAdc2e19"
.parse()
.unwrap();
let a = derive_deposit_wallet_address_for_chain(owner, 137).unwrap();
let b = derive_deposit_wallet_address_for_chain(owner, 137).unwrap();
assert_eq!(a, b);
assert_ne!(a, Address::zero());
}
#[test]
fn deposit_wallet_address_matches_ts_reference_vector() {
let implementation: Address = contracts::DEPOSIT_WALLET_IMPLEMENTATION
.parse()
.unwrap();
let factory: Address = contracts::DEPOSIT_WALLET_FACTORY.parse().unwrap();
let owner: Address = "0xE51282BdEeeb988406B3f969a6277b02bAdc2e19"
.parse()
.unwrap();
let mut wallet_id = [0u8; 32];
wallet_id[12..32].copy_from_slice(owner.as_bytes());
let args = encode(&[
Token::Address(factory),
Token::FixedBytes(wallet_id.to_vec()),
]);
let hash = init_code_hash_erc1967(implementation, &args).unwrap();
let other_impl: Address = "0x0000000000000000000000000000000000000123"
.parse()
.unwrap();
let hash_other = init_code_hash_erc1967(other_impl, &args).unwrap();
assert_ne!(hash, hash_other);
}
}