Skip to content

Commit bcb5404

Browse files
committed
Handle constructor expectRevert
1 parent 6182d31 commit bcb5404

File tree

3 files changed

+167
-14
lines changed

3 files changed

+167
-14
lines changed

crates/cheatcodes/src/fs.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ use alloy_json_abi::ContractObject;
77
use alloy_network::AnyTransactionReceipt;
88
use alloy_primitives::{hex, map::Entry, Bytes, U256};
99
use alloy_provider::network::ReceiptResponse;
10-
use alloy_sol_types::SolValue;
10+
use alloy_sol_types::{SolInterface, SolValue};
1111
use dialoguer::{Input, Password};
1212
use forge_script_sequence::{BroadcastReader, TransactionWithMetadata};
1313
use foundry_common::fs;
1414
use foundry_config::fs_permissions::FsAccessKind;
1515
use revm::interpreter::CreateInputs;
1616
use revm_inspectors::tracing::types::CallKind;
1717
use semver::Version;
18+
use spec::Vm;
1819
use std::{
1920
io::{BufRead, BufReader, Write},
2021
path::{Path, PathBuf},
@@ -371,19 +372,27 @@ fn deploy_code(
371372
revm::primitives::CreateScheme::Create
372373
};
373374

374-
let address = executor
375-
.exec_create(
376-
CreateInputs {
377-
caller: ccx.caller,
378-
scheme,
379-
value: value.unwrap_or(U256::ZERO),
380-
init_code: bytecode.into(),
381-
gas_limit: ccx.gas_limit,
382-
},
383-
ccx,
384-
)?
385-
.address
386-
.ok_or_else(|| fmt_err!("contract creation failed"))?;
375+
let outcome = executor.exec_create(
376+
CreateInputs {
377+
caller: ccx.caller,
378+
scheme,
379+
value: value.unwrap_or(U256::ZERO),
380+
init_code: bytecode.into(),
381+
gas_limit: ccx.gas_limit,
382+
},
383+
ccx,
384+
)?;
385+
386+
if !outcome.result.result.is_ok() {
387+
if let Ok(e) = alloy_sol_types::ContractError::<Vm::VmErrors>::abi_decode(
388+
outcome.result.output.as_ref(),
389+
false,
390+
) {
391+
bail!("{}", e.to_string())
392+
}
393+
}
394+
395+
let address = outcome.address.ok_or_else(|| fmt_err!("contract creation failed"))?;
387396

388397
Ok(address.abi_encode())
389398
}

crates/cheatcodes/src/test/revert_handlers.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ fn handle_revert(
113113
return Ok(());
114114
}
115115

116+
// Handle special case for linked tests that use deployCode cheatcode.
117+
if let Some(actual) = actual.strip_prefix("vm.deployCode: revert: ") {
118+
return if expected == actual {
119+
Ok(())
120+
} else {
121+
Err(fmt_err!("Error != expected error: {} != {}", actual, expected))
122+
}
123+
}
124+
116125
Err(fmt_err!("Error != expected error: {} != {}", actual, expected))
117126
}
118127
}

crates/forge/tests/cli/test_optimizer.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,3 +1165,138 @@ Compiling 1 files with [..]
11651165
11661166
"#]]);
11671167
});
1168+
1169+
// Counter contract with constructor reverts and emitted events.
1170+
forgetest_init!(preprocess_contract_with_require_and_emit, |prj, cmd| {
1171+
prj.wipe_contracts();
1172+
prj.update_config(|config| {
1173+
config.dynamic_test_linking = true;
1174+
});
1175+
1176+
prj.add_source(
1177+
"Counter.sol",
1178+
r#"
1179+
contract Counter {
1180+
event CounterCreated(uint256 number);
1181+
uint256 public number;
1182+
1183+
constructor(uint256 no) {
1184+
require(no != 1, "ctor revert");
1185+
emit CounterCreated(10);
1186+
}
1187+
}
1188+
"#,
1189+
)
1190+
.unwrap();
1191+
1192+
prj.add_test(
1193+
"Counter.t.sol",
1194+
r#"
1195+
import {Test} from "forge-std/Test.sol";
1196+
import {Counter} from "../src/Counter.sol";
1197+
1198+
contract CounterTest is Test {
1199+
function test_assert_constructor_revert() public {
1200+
vm.expectRevert("ctor revert");
1201+
new Counter(1);
1202+
}
1203+
1204+
function test_assert_constructor_emit() public {
1205+
vm.expectEmit(true, true, true, true);
1206+
emit Counter.CounterCreated(10);
1207+
1208+
new Counter(11);
1209+
}
1210+
}
1211+
"#,
1212+
)
1213+
.unwrap();
1214+
// All 20 files are compiled on first run.
1215+
cmd.args(["test"]).with_no_redact().assert_success().stdout_eq(str![[r#"
1216+
...
1217+
Compiling 20 files with [..]
1218+
...
1219+
1220+
"#]]);
1221+
1222+
// Change Counter implementation to revert with different message.
1223+
prj.add_source(
1224+
"Counter.sol",
1225+
r#"
1226+
contract Counter {
1227+
event CounterCreated(uint256 number);
1228+
uint256 public number;
1229+
1230+
constructor(uint256 no) {
1231+
require(no != 1, "ctor revert update");
1232+
emit CounterCreated(10);
1233+
}
1234+
}
1235+
"#,
1236+
)
1237+
.unwrap();
1238+
// Assert that only 1 file is compiled (Counter source contract) and revert test fails.
1239+
cmd.with_no_redact().assert_failure().stdout_eq(str![[r#"
1240+
...
1241+
Compiling 1 files with [..]
1242+
...
1243+
[PASS] test_assert_constructor_emit() (gas: [..])
1244+
[FAIL: Error != expected error: ctor revert update != ctor revert] test_assert_constructor_revert() (gas: [..])
1245+
...
1246+
1247+
"#]]);
1248+
1249+
// Change Counter implementation and don't revert.
1250+
prj.add_source(
1251+
"Counter.sol",
1252+
r#"
1253+
contract Counter {
1254+
event CounterCreated(uint256 number);
1255+
uint256 public number;
1256+
1257+
constructor(uint256 no) {
1258+
require(no != 0, "ctor revert");
1259+
emit CounterCreated(10);
1260+
}
1261+
}
1262+
"#,
1263+
)
1264+
.unwrap();
1265+
// Assert that only 1 file is compiled (Counter source contract) and revert test fails.
1266+
cmd.with_no_redact().assert_failure().stdout_eq(str![[r#"
1267+
...
1268+
Compiling 1 files with [..]
1269+
...
1270+
[PASS] test_assert_constructor_emit() (gas: [..])
1271+
[FAIL: next call did not revert as expected] test_assert_constructor_revert() (gas: [..])
1272+
...
1273+
1274+
"#]]);
1275+
1276+
// Change Counter implementation to emit different event.
1277+
prj.add_source(
1278+
"Counter.sol",
1279+
r#"
1280+
contract Counter {
1281+
event CounterCreated(uint256 number);
1282+
uint256 public number;
1283+
1284+
constructor(uint256 no) {
1285+
require(no != 0, "ctor revert");
1286+
emit CounterCreated(100);
1287+
}
1288+
}
1289+
"#,
1290+
)
1291+
.unwrap();
1292+
// Assert that only 1 file is compiled (Counter source contract) and emit test fails.
1293+
cmd.with_no_redact().assert_failure().stdout_eq(str![[r#"
1294+
...
1295+
Compiling 1 files with [..]
1296+
...
1297+
[FAIL: expected an emit, but no logs were emitted afterwards. you might have mismatched events or not enough events were emitted] test_assert_constructor_emit() (gas: [..])
1298+
[FAIL: next call did not revert as expected] test_assert_constructor_revert() (gas: [..])
1299+
...
1300+
1301+
"#]]);
1302+
});

0 commit comments

Comments
 (0)