@@ -144,6 +144,60 @@ fn test_verify_bytecode_with_ignore(
144144 ) ;
145145 }
146146}
147+
148+ #[ expect( clippy:: too_many_arguments) ]
149+ fn test_verify_bytecode_mismatch (
150+ prj : TestProject ,
151+ mut cmd : TestCommand ,
152+ addr : & str ,
153+ contract_name : & str ,
154+ source_code : & str ,
155+ config : Config ,
156+ verifier : & str ,
157+ verifier_url : & str ,
158+ ) {
159+ let etherscan_key = next_etherscan_api_key ( ) ;
160+ let rpc_url = next_http_archive_rpc_url ( ) ;
161+
162+ // Fetch real source code
163+ let real_source = cmd
164+ . cast_fuse ( )
165+ . args ( [ "source" , addr, "--flatten" , "--etherscan-api-key" , & etherscan_key] )
166+ . assert_success ( )
167+ . get_output ( )
168+ . stdout_lossy ( ) ;
169+
170+ prj. add_source ( contract_name, & real_source) ;
171+ prj. write_config ( config) ;
172+ // Build once with correct source (creates cache)
173+ cmd. forge_fuse ( ) . arg ( "build" ) . assert_success ( ) ;
174+
175+ // Now replace with different incorrect source code
176+ prj. add_source ( contract_name, source_code) ;
177+ let args = vec ! [
178+ "verify-bytecode" ,
179+ addr,
180+ contract_name,
181+ "--etherscan-api-key" ,
182+ & etherscan_key,
183+ "--verifier" ,
184+ verifier,
185+ "--verifier-url" ,
186+ verifier_url,
187+ "--rpc-url" ,
188+ & rpc_url,
189+ ] ;
190+ let output = cmd. forge_fuse ( ) . args ( args) . assert_success ( ) . get_output ( ) . stderr_lossy ( ) ;
191+
192+ // Verify that bytecode does NOT match (recompiled with incorrect source)
193+ assert ! (
194+ output. contains( format!( "Error: Creation code did not match" ) . as_str( ) ) ,
195+ ) ;
196+ assert ! (
197+ output. contains( format!( "Error: Runtime code did not match" ) . as_str( ) ) ,
198+ ) ;
199+ }
200+
147201forgetest_async ! ( can_verify_bytecode_no_metadata, |prj, cmd| {
148202 test_verify_bytecode(
149203 prj,
@@ -296,6 +350,37 @@ forgetest_async!(can_ignore_runtime, |prj, cmd| {
296350 ) ;
297351} ) ;
298352
353+ // Test that verification fails when source code doesn't match deployed bytecode
354+ forgetest_async ! ( can_verify_bytecode_fails_on_source_mismatch, |prj, cmd| {
355+ let modified_source = r#"
356+ contract SystemConfig {
357+ uint256 public constant MODIFIED_VALUE = 999;
358+
359+ function someFunction() public pure returns (uint256) {
360+ return MODIFIED_VALUE;
361+ }
362+ }
363+ "# ;
364+
365+ test_verify_bytecode_mismatch(
366+ prj,
367+ cmd,
368+ "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1" ,
369+ "SystemConfig" ,
370+ modified_source,
371+ Config {
372+ evm_version: EvmVersion :: London ,
373+ optimizer_runs: Some ( 999999 ) ,
374+ optimizer: Some ( true ) ,
375+ cbor_metadata: false ,
376+ bytecode_hash: BytecodeHash :: None ,
377+ ..Default :: default ( )
378+ } ,
379+ "etherscan" ,
380+ "https://api.etherscan.io/v2/api?chainid=1" ,
381+ ) ;
382+ } ) ;
383+
299384// Test predeploy contracts
300385// TODO: Add test utils for base such as basescan keys and alchemy keys.
301386// WETH9 Predeploy
0 commit comments