@@ -65,19 +65,29 @@ pub(crate) fn merge_into(
6565
6666 let new_alloc = alloc. as_object ( ) . unwrap ( ) ;
6767 for ( addr, entry) in new_alloc {
68- if genesis_alloc. contains_key ( addr) && !force {
68+ let canonical = normalize_addr ( addr) ;
69+ let existing_key = genesis_alloc
70+ . keys ( )
71+ . find ( |k| normalize_addr ( k) == canonical)
72+ . cloned ( ) ;
73+ if existing_key. is_some ( ) && !force {
6974 eyre:: bail!( "address collision at {addr}; use --force to overwrite" ) ;
7075 }
71- genesis_alloc. insert ( addr. clone ( ) , entry. clone ( ) ) ;
76+ if let Some ( key) = existing_key {
77+ genesis_alloc. remove ( & key) ;
78+ }
79+ genesis_alloc. insert ( canonical, entry. clone ( ) ) ;
7280 }
7381
7482 Ok ( genesis)
7583}
7684
85+ fn normalize_addr ( addr : & str ) -> String {
86+ addr. strip_prefix ( "0x" ) . unwrap_or ( addr) . to_lowercase ( )
87+ }
88+
7789fn insert_contract ( alloc : & mut Map < String , Value > , contract : & GenesisContract ) {
78- // Address key without 0x prefix, using checksummed format
79- let addr_hex = format ! ( "{}" , contract. address) ;
80- let addr_key = addr_hex. strip_prefix ( "0x" ) . unwrap_or ( & addr_hex) ;
90+ let addr_key = normalize_addr ( & format ! ( "{}" , contract. address) ) ;
8191
8292 let mut storage_map = Map :: new ( ) ;
8393 for ( slot, value) in & contract. storage {
@@ -96,7 +106,7 @@ fn insert_contract(alloc: &mut Map<String, Value>, contract: &GenesisContract) {
96106 ) ;
97107 entry. insert ( "storage" . to_string ( ) , Value :: Object ( storage_map) ) ;
98108
99- alloc. insert ( addr_key. to_string ( ) , Value :: Object ( entry) ) ;
109+ alloc. insert ( addr_key, Value :: Object ( entry) ) ;
100110}
101111
102112/// Format a storage slot key as a full 32-byte hex string.
@@ -116,7 +126,7 @@ mod tests {
116126 chain : ChainConfig { chain_id : 1234 } ,
117127 contracts : ContractsConfig {
118128 admin_proxy : Some ( AdminProxyConfig {
119- address : address ! ( "000000000000000000000000000000000000Ad00 " ) ,
129+ address : address ! ( "000000000000000000000000000000000000ad00 " ) ,
120130 owner : address ! ( "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ) ,
121131 } ) ,
122132 fee_vault : None ,
@@ -132,10 +142,10 @@ mod tests {
132142 fn alloc_json_structure ( ) {
133143 let alloc = build_alloc ( & test_config ( ) ) ;
134144 let obj = alloc. as_object ( ) . unwrap ( ) ;
135- assert ! ( obj. contains_key( "000000000000000000000000000000000000Ad00 " ) ) ;
145+ assert ! ( obj. contains_key( "000000000000000000000000000000000000ad00 " ) ) ;
136146
137147 let entry = obj
138- . get ( "000000000000000000000000000000000000Ad00 " )
148+ . get ( "000000000000000000000000000000000000ad00 " )
139149 . unwrap ( )
140150 . as_object ( )
141151 . unwrap ( ) ;
@@ -150,7 +160,7 @@ mod tests {
150160 let storage = alloc
151161 . as_object ( )
152162 . unwrap ( )
153- . get ( "000000000000000000000000000000000000Ad00 " )
163+ . get ( "000000000000000000000000000000000000ad00 " )
154164 . unwrap ( )
155165 . get ( "storage" )
156166 . unwrap ( )
@@ -181,7 +191,7 @@ mod tests {
181191
182192 #[ test]
183193 fn merge_detects_collision ( ) {
184- let genesis = r#"{"alloc":{"000000000000000000000000000000000000Ad00 ":{"balance":"0x0"}}}"# ;
194+ let genesis = r#"{"alloc":{"000000000000000000000000000000000000ad00 ":{"balance":"0x0"}}}"# ;
185195 let tmp = tempfile:: NamedTempFile :: new ( ) . unwrap ( ) ;
186196 std:: fs:: write ( tmp. path ( ) , genesis) . unwrap ( ) ;
187197
@@ -195,11 +205,32 @@ mod tests {
195205
196206 #[ test]
197207 fn merge_force_overwrites ( ) {
198- let genesis = r#"{"alloc":{"000000000000000000000000000000000000Ad00 ":{"balance":"0x0"}}}"# ;
208+ let genesis = r#"{"alloc":{"000000000000000000000000000000000000ad00 ":{"balance":"0x0"}}}"# ;
199209 let tmp = tempfile:: NamedTempFile :: new ( ) . unwrap ( ) ;
200210 std:: fs:: write ( tmp. path ( ) , genesis) . unwrap ( ) ;
201211
202212 let result = merge_into ( & test_config ( ) , tmp. path ( ) , true ) ;
203213 assert ! ( result. is_ok( ) ) ;
204214 }
215+
216+ #[ test]
217+ fn merge_detects_collision_with_0x_prefix ( ) {
218+ let genesis =
219+ r#"{"alloc":{"0x000000000000000000000000000000000000ad00":{"balance":"0x0"}}}"# ;
220+ let tmp = tempfile:: NamedTempFile :: new ( ) . unwrap ( ) ;
221+ std:: fs:: write ( tmp. path ( ) , genesis) . unwrap ( ) ;
222+
223+ let result = merge_into ( & test_config ( ) , tmp. path ( ) , false ) ;
224+ assert ! ( result. is_err( ) ) ;
225+ }
226+
227+ #[ test]
228+ fn merge_detects_collision_with_mixed_case ( ) {
229+ let genesis = r#"{"alloc":{"000000000000000000000000000000000000AD00":{"balance":"0x0"}}}"# ;
230+ let tmp = tempfile:: NamedTempFile :: new ( ) . unwrap ( ) ;
231+ std:: fs:: write ( tmp. path ( ) , genesis) . unwrap ( ) ;
232+
233+ let result = merge_into ( & test_config ( ) , tmp. path ( ) , false ) ;
234+ assert ! ( result. is_err( ) ) ;
235+ }
205236}
0 commit comments