use crate::address::Address;
use crate::rlp::{pack_rlp, RlpToken};
use num256::Uint256;
use sha3::{Digest, Keccak256};
pub fn calculate_contract_address(deployer: Address, nonce: Uint256) -> Address {
let rlp_data = pack_rlp(vec![RlpToken::List(vec![
RlpToken::String(deployer.as_bytes().to_vec()),
RlpToken::from(nonce),
])]);
let hash = Keccak256::digest(&rlp_data);
Address::from_slice(&hash[12..]).expect("Slice is exactly 20 bytes")
}
pub fn calculate_contract_address_create2(
deployer: Address,
salt: [u8; 32],
init_code_hash: [u8; 32],
) -> Address {
let mut data = Vec::with_capacity(85);
data.push(0xff);
data.extend_from_slice(deployer.as_bytes());
data.extend_from_slice(&salt);
data.extend_from_slice(&init_code_hash);
let hash = Keccak256::digest(&data);
Address::from_slice(&hash[12..]).expect("Slice is exactly 20 bytes")
}
pub fn hash_init_code(init_code: &[u8]) -> [u8; 32] {
let hash = Keccak256::digest(init_code);
let mut result = [0u8; 32];
result.copy_from_slice(&hash);
result
}
pub fn validate_init_code_size(init_code: &[u8]) -> bool {
const MAX_INIT_CODE_SIZE: usize = 49_152; init_code.len() <= MAX_INIT_CODE_SIZE
}
pub fn calculate_init_code_gas(init_code_size: usize) -> Uint256 {
const GAS_PER_WORD: u64 = 2;
const WORD_SIZE: usize = 32;
let words = init_code_size.div_ceil(WORD_SIZE);
Uint256::from(words as u64) * Uint256::from(GAS_PER_WORD)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::bytes_to_hex_str;
enum AddressVariant {
Create { nonce: u64 },
Create2 {
salt: [u8; 32],
init_code_hash: [u8; 32],
},
}
struct ContractAddressTest {
deployer: &'static str,
variant: AddressVariant,
expected: &'static str,
}
const VECTORS: &[ContractAddressTest] = &[
ContractAddressTest {
deployer: "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0",
variant: AddressVariant::Create { nonce: 0 },
expected: "0xcd234a471b72ba2f1ccf0a70fcaba648a5eecd8d",
},
ContractAddressTest {
deployer: "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0",
variant: AddressVariant::Create { nonce: 1 },
expected: "0x343c43a37d37dff08ae8c4a11544c718abb4fcf8",
},
ContractAddressTest {
deployer: "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0",
variant: AddressVariant::Create { nonce: 2 },
expected: "0xf778b86fa74e846c4f0a1fbd1335fe81c00a0c91",
},
ContractAddressTest {
deployer: "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0",
variant: AddressVariant::Create { nonce: 1000 },
expected: "0xB9cDb7F5e62043c1e4EB7a6d76eF8Ee246D364Ec",
},
ContractAddressTest {
deployer: "0xaf69eBeF35607d6834Dac02294792F7d21B95AF9",
variant: AddressVariant::Create { nonce: 344 },
expected: "0x67DB0a68230BA4104DD121Bd451Bd066e5c5fB74",
},
ContractAddressTest {
deployer: "0x0000000000000000000000000000000000000000",
variant: AddressVariant::Create2 {
salt: [0u8; 32],
init_code_hash: [0u8; 32],
},
expected: "0xffc4f52f884a02bcd5716744cd622127366f2edf",
},
ContractAddressTest {
deployer: "0xdeadbeef00000000000000000000000000000000",
variant: AddressVariant::Create2 {
salt: [0u8; 32],
init_code_hash: [0u8; 32],
},
expected: "0x85f15e045e1244ac03289b48448249dc0a34aa30",
},
ContractAddressTest {
deployer: "0x0000000000000000000000000000000000000000",
variant: AddressVariant::Create2 {
salt: {
let mut s = [0u8; 32];
s[31] = 1;
s
},
init_code_hash: [0u8; 32],
},
expected: "0x12741fEC8148E76ad3a51Cdd5fD73061C6a39148",
},
];
#[test]
fn contract_address_prediction() {
for (i, v) in VECTORS.iter().enumerate() {
let deployer: Address = v
.deployer
.parse()
.unwrap_or_else(|e| panic!("vector {i}: bad deployer address: {e}"));
let expected: Address = v
.expected
.parse()
.unwrap_or_else(|e| panic!("vector {i}: bad expected address: {e}"));
match v.variant {
AddressVariant::Create { nonce } => {
let got = calculate_contract_address(deployer, Uint256::from(nonce));
assert_eq!(
got, expected,
"vector {i} (CREATE): deployer={} nonce={} expected={} got={}",
v.deployer, nonce, expected, got
);
}
AddressVariant::Create2 {
salt,
init_code_hash,
} => {
let got = calculate_contract_address_create2(deployer, salt, init_code_hash);
assert_eq!(
got, expected,
"vector {i} (CREATE2): deployer={} expected={} got={}",
v.deployer, expected, got
);
}
}
}
}
#[test]
fn test_hash_init_code() {
let init_code = vec![0x60, 0x80, 0x60, 0x40, 0x52];
let hash = hash_init_code(&init_code);
assert_eq!(hash.len(), 32);
let hash2 = hash_init_code(&init_code);
assert_eq!(hash, hash2);
let different_code = vec![0x60, 0x80, 0x60, 0x40, 0x53];
let hash3 = hash_init_code(&different_code);
assert_ne!(hash, hash3);
}
#[test]
fn test_hash_init_code_empty() {
let init_code = vec![];
let hash = hash_init_code(&init_code);
assert_eq!(hash.len(), 32);
let expected = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";
assert_eq!(bytes_to_hex_str(&hash), expected);
}
#[test]
fn test_validate_init_code_size_valid() {
let code = vec![0u8; 1000]; assert!(validate_init_code_size(&code));
let code = vec![0u8; 49_152]; assert!(validate_init_code_size(&code));
}
#[test]
fn test_validate_init_code_size_invalid() {
let code = vec![0u8; 49_153]; assert!(!validate_init_code_size(&code));
let code = vec![0u8; 100_000]; assert!(!validate_init_code_size(&code));
}
#[test]
fn test_validate_init_code_size_empty() {
let code = vec![];
assert!(validate_init_code_size(&code));
}
#[test]
fn test_calculate_init_code_gas() {
assert_eq!(calculate_init_code_gas(0), Uint256::from(0u8));
assert_eq!(calculate_init_code_gas(1), Uint256::from(2u8));
assert_eq!(calculate_init_code_gas(32), Uint256::from(2u8));
assert_eq!(calculate_init_code_gas(33), Uint256::from(4u8));
assert_eq!(calculate_init_code_gas(64), Uint256::from(4u8));
assert_eq!(calculate_init_code_gas(100), Uint256::from(8u8));
assert_eq!(calculate_init_code_gas(1000), Uint256::from(64u8));
}
#[test]
fn test_calculate_init_code_gas_max_size() {
assert_eq!(calculate_init_code_gas(49_152), Uint256::from(3072u64));
}
}