@@ -204,106 +204,156 @@ mod tests {
204204 use super :: * ;
205205 use crate :: utils:: bytes_to_hex_str;
206206
207- #[ test]
208- fn test_calculate_contract_address_nonce_0 ( ) {
209- let deployer: Address = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"
210- . parse ( )
211- . unwrap ( ) ;
212- let nonce = Uint256 :: from ( 0u8 ) ;
213- let expected: Address = "0xcd234a471b72ba2f1ccf0a70fcaba648a5eecd8d"
214- . parse ( )
215- . unwrap ( ) ;
216-
217- let result = calculate_contract_address ( deployer, nonce) ;
218- assert_eq ! ( result, expected) ;
207+ /// Variant of contract address derivation.
208+ enum AddressVariant {
209+ /// CREATE: address = keccak256(rlp([deployer, nonce]))[12:]
210+ Create { nonce : u64 } ,
211+ /// CREATE2: address = keccak256(0xff ++ deployer ++ salt ++ keccak256(init_code))[12:]
212+ Create2 {
213+ salt : [ u8 ; 32 ] ,
214+ init_code_hash : [ u8 ; 32 ] ,
215+ } ,
219216 }
220217
221- #[ test]
222- fn test_calculate_contract_address_nonce_1 ( ) {
223- let deployer: Address = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"
224- . parse ( )
225- . unwrap ( ) ;
226- let nonce = Uint256 :: from ( 1u8 ) ;
227- let expected: Address = "0x343c43a37d37dff08ae8c4a11544c718abb4fcf8"
228- . parse ( )
229- . unwrap ( ) ;
230-
231- let result = calculate_contract_address ( deployer, nonce) ;
232- assert_eq ! ( result, expected) ;
218+ /// Minimum data needed to verify contract address prediction.
219+ ///
220+ /// Supports both CREATE and CREATE2 opcode variants.
221+ ///
222+ /// For CREATE:
223+ /// address = keccak256(rlp([deployer, nonce]))[12:]
224+ ///
225+ /// For CREATE2:
226+ /// address = keccak256(0xff ++ deployer ++ salt ++ keccak256(init_code))[12:]
227+ ///
228+ /// To add test vectors, find a real Ethereum contract deployment and record:
229+ /// - The deployer (transaction `from` field)
230+ /// - For CREATE: the deployer's nonce at that block
231+ /// - For CREATE2: the salt and init code hash used
232+ /// - The resulting contract address (transaction receipt `contractAddress` field)
233+ struct ContractAddressTest {
234+ deployer : & ' static str ,
235+ variant : AddressVariant ,
236+ expected : & ' static str ,
233237 }
234238
235- #[ test]
236- fn test_calculate_contract_address_nonce_2 ( ) {
237- let deployer: Address = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"
238- . parse ( )
239- . unwrap ( ) ;
240- let nonce = Uint256 :: from ( 2u8 ) ;
241- let expected: Address = "0xf778b86fa74e846c4f0a1fbd1335fe81c00a0c91"
242- . parse ( )
243- . unwrap ( ) ;
244-
245- let result = calculate_contract_address ( deployer, nonce) ;
246- assert_eq ! ( result, expected) ;
247- }
248-
249- #[ test]
250- fn test_calculate_contract_address_high_nonce ( ) {
251- // Test with a larger nonce value
252- let deployer: Address = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"
253- . parse ( )
254- . unwrap ( ) ;
255- let nonce = Uint256 :: from ( 1000u32 ) ;
256-
257- // Should not panic and should return a valid address
258- let result = calculate_contract_address ( deployer, nonce) ;
259- assert_eq ! ( result. as_bytes( ) . len( ) , 20 ) ;
260- }
261-
262- #[ test]
263- fn test_calculate_contract_address_create2_zero ( ) {
239+ /// Known contract address derivation vectors.
240+ ///
241+ /// Includes both CREATE and CREATE2 test cases.
242+ ///
243+ /// Sources / how to verify:
244+ /// - Etherscan: look up a contract deployment tx, the receipt `contractAddress`
245+ /// is the expected address, and the tx `nonce` is the deployer nonce.
246+ /// - Python: import rlp; from eth_utils import keccak; keccak(rlp.encode([bytes.fromhex(addr[2:]), nonce]))[12:].hex()
247+ /// - Cast: cast compute-address --nonce <nonce> <deployer> (for CREATE)
248+ /// - Cast: cast compute-address --code-hash <hash> --salt <salt> <deployer> (for CREATE2)
249+ const VECTORS : & [ ContractAddressTest ] = & [
250+ // CREATE tests
251+ // From EIP-55 reference implementation and clarity doctest.
252+ ContractAddressTest {
253+ deployer : "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0" ,
254+ variant : AddressVariant :: Create { nonce : 0 } ,
255+ expected : "0xcd234a471b72ba2f1ccf0a70fcaba648a5eecd8d" ,
256+ } ,
257+ // Same deployer with nonce 1
258+ ContractAddressTest {
259+ deployer : "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0" ,
260+ variant : AddressVariant :: Create { nonce : 1 } ,
261+ expected : "0x343c43a37d37dff08ae8c4a11544c718abb4fcf8" ,
262+ } ,
263+ // Same deployer with nonce 2
264+ ContractAddressTest {
265+ deployer : "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0" ,
266+ variant : AddressVariant :: Create { nonce : 2 } ,
267+ expected : "0xf778b86fa74e846c4f0a1fbd1335fe81c00a0c91" ,
268+ } ,
269+ // Test with a larger nonce value (> 128 for multi-byte RLP encoding)
270+ ContractAddressTest {
271+ deployer : "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0" ,
272+ variant : AddressVariant :: Create { nonce : 1000 } ,
273+ expected : "0xB9cDb7F5e62043c1e4EB7a6d76eF8Ee246D364Ec" ,
274+ } ,
275+ ContractAddressTest {
276+ deployer : "0xaf69eBeF35607d6834Dac02294792F7d21B95AF9" ,
277+ variant : AddressVariant :: Create { nonce : 344 } ,
278+ expected : "0x67DB0a68230BA4104DD121Bd451Bd066e5c5fB74" ,
279+ } ,
280+ // CREATE2 tests
264281 // All-zero deployer, salt, and init_code_hash
265- let deployer: Address = "0x0000000000000000000000000000000000000000"
266- . parse ( )
267- . unwrap ( ) ;
268- let salt = [ 0u8 ; 32 ] ;
269- let init_code_hash = [ 0u8 ; 32 ] ;
270- let expected: Address = "0xffc4f52f884a02bcd5716744cd622127366f2edf"
271- . parse ( )
272- . unwrap ( ) ;
273-
274- let result = calculate_contract_address_create2 ( deployer, salt, init_code_hash) ;
275- assert_eq ! ( result, expected) ;
276- }
277-
278- #[ test]
279- fn test_calculate_contract_address_create2_custom ( ) {
280- let deployer: Address = "0xdeadbeef00000000000000000000000000000000"
281- . parse ( )
282- . unwrap ( ) ;
283- let salt = [ 0u8 ; 32 ] ;
284- let init_code_hash = [ 0u8 ; 32 ] ;
285- let expected: Address = "0x85f15e045e1244ac03289b48448249dc0a34aa30"
286- . parse ( )
287- . unwrap ( ) ;
288-
289- let result = calculate_contract_address_create2 ( deployer, salt, init_code_hash) ;
290- assert_eq ! ( result, expected) ;
291- }
282+ ContractAddressTest {
283+ deployer : "0x0000000000000000000000000000000000000000" ,
284+ variant : AddressVariant :: Create2 {
285+ salt : [ 0u8 ; 32 ] ,
286+ init_code_hash : [ 0u8 ; 32 ] ,
287+ } ,
288+ expected : "0xffc4f52f884a02bcd5716744cd622127366f2edf" ,
289+ } ,
290+ // Custom deployer with CREATE2
291+ ContractAddressTest {
292+ deployer : "0xdeadbeef00000000000000000000000000000000" ,
293+ variant : AddressVariant :: Create2 {
294+ salt : [ 0u8 ; 32 ] ,
295+ init_code_hash : [ 0u8 ; 32 ] ,
296+ } ,
297+ expected : "0x85f15e045e1244ac03289b48448249dc0a34aa30" ,
298+ } ,
299+ // CREATE2 with different salt
300+ ContractAddressTest {
301+ deployer : "0x0000000000000000000000000000000000000000" ,
302+ variant : AddressVariant :: Create2 {
303+ salt : {
304+ let mut s = [ 0u8 ; 32 ] ;
305+ s[ 31 ] = 1 ;
306+ s
307+ } ,
308+ init_code_hash : [ 0u8 ; 32 ] ,
309+ } ,
310+ expected : "0x12741fEC8148E76ad3a51Cdd5fD73061C6a39148" ,
311+ } ,
312+ // TODO: add deployer + nonce + expected from real mainnet deployments
313+ // Example: record the `from`, `nonce`, and receipt `contractAddress` from
314+ // any contract-creation transaction on Etherscan.
315+ //
316+ // ContractAddressTest {
317+ // deployer: "0x...",
318+ // variant: AddressVariant::Create { nonce: 0 },
319+ // expected: "0x...",
320+ // },
321+ ] ;
292322
293323 #[ test]
294- fn test_calculate_contract_address_create2_different_salt ( ) {
295- let deployer: Address = "0x0000000000000000000000000000000000000000"
296- . parse ( )
297- . unwrap ( ) ;
298- let mut salt = [ 0u8 ; 32 ] ;
299- salt[ 31 ] = 1 ; // Different salt
300- let init_code_hash = [ 0u8 ; 32 ] ;
301-
302- let result1 = calculate_contract_address_create2 ( deployer, [ 0u8 ; 32 ] , init_code_hash) ;
303- let result2 = calculate_contract_address_create2 ( deployer, salt, init_code_hash) ;
304-
305- // Different salts should produce different addresses
306- assert_ne ! ( result1, result2) ;
324+ fn contract_address_prediction ( ) {
325+ for ( i, v) in VECTORS . iter ( ) . enumerate ( ) {
326+ let deployer: Address = v
327+ . deployer
328+ . parse ( )
329+ . unwrap_or_else ( |e| panic ! ( "vector {i}: bad deployer address: {e}" ) ) ;
330+ let expected: Address = v
331+ . expected
332+ . parse ( )
333+ . unwrap_or_else ( |e| panic ! ( "vector {i}: bad expected address: {e}" ) ) ;
334+
335+ match v. variant {
336+ AddressVariant :: Create { nonce } => {
337+ let got = calculate_contract_address ( deployer, Uint256 :: from ( nonce) ) ;
338+ assert_eq ! (
339+ got, expected,
340+ "vector {i} (CREATE): deployer={} nonce={} expected={} got={}" ,
341+ v. deployer, nonce, expected, got
342+ ) ;
343+ }
344+ AddressVariant :: Create2 {
345+ salt,
346+ init_code_hash,
347+ } => {
348+ let got = calculate_contract_address_create2 ( deployer, salt, init_code_hash) ;
349+ assert_eq ! (
350+ got, expected,
351+ "vector {i} (CREATE2): deployer={} expected={} got={}" ,
352+ v. deployer, expected, got
353+ ) ;
354+ }
355+ }
356+ }
307357 }
308358
309359 #[ test]
0 commit comments