Skip to content

Commit d21d4d6

Browse files
authored
feat(cheatcodes): add setEvmVersion / getEvmVersion (#12014)
* feat(cheatcodes): add setEvmVersion / getEvmVersion * Add better explanation re evm version * Nit * Update cheatcode note to reflect exact evm version needed
1 parent b2d8e31 commit d21d4d6

File tree

5 files changed

+162
-1
lines changed

5 files changed

+162
-1
lines changed

crates/cheatcodes/assets/cheatcodes.json

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cheatcodes/spec/src/vm.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,18 @@ interface Vm {
609609
#[cheatcode(group = Evm, safety = Unsafe)]
610610
function coolSlot(address target, bytes32 slot) external;
611611

612+
/// Returns the test or script execution evm version.
613+
///
614+
/// **Note:** The execution evm version is not the same as the compilation one.
615+
#[cheatcode(group = Evm, safety = Safe)]
616+
function getEvmVersion() external pure returns (string memory evm);
617+
618+
/// Set the exact test or script execution evm version, e.g. `berlin`, `cancun`.
619+
///
620+
/// **Note:** The execution evm version is not the same as the compilation one.
621+
#[cheatcode(group = Evm, safety = Safe)]
622+
function setEvmVersion(string calldata evm) external;
623+
612624
// -------- Call Manipulation --------
613625
// --- Mocks ---
614626

crates/cheatcodes/src/evm.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use foundry_common::{
2121
SlotInfo,
2222
},
2323
};
24+
use foundry_compilers::artifacts::EvmVersion;
2425
use foundry_evm_core::{
2526
ContextExt,
2627
backend::{DatabaseExt, RevertStateSnapshotAction},
@@ -45,6 +46,7 @@ use std::{
4546

4647
mod record_debug_step;
4748
use foundry_common::fmt::format_token_raw;
49+
use foundry_config::evm_spec_id;
4850
use record_debug_step::{convert_call_trace_to_debug_step, flatten_call_trace};
4951
use serde::Serialize;
5052

@@ -1103,6 +1105,23 @@ impl Cheatcode for stopAndReturnDebugTraceRecordingCall {
11031105
}
11041106
}
11051107

1108+
impl Cheatcode for setEvmVersionCall {
1109+
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
1110+
let Self { evm } = self;
1111+
ccx.ecx.cfg.spec = evm_spec_id(
1112+
EvmVersion::from_str(evm)
1113+
.map_err(|_| Error::from(format!("invalid evm version {evm}")))?,
1114+
);
1115+
Ok(Default::default())
1116+
}
1117+
}
1118+
1119+
impl Cheatcode for getEvmVersionCall {
1120+
fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
1121+
Ok(ccx.ecx.cfg.spec.to_string().to_lowercase().abi_encode())
1122+
}
1123+
}
1124+
11061125
pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result {
11071126
let account = ccx.ecx.journaled_state.load_account(*address)?;
11081127
Ok(account.info.nonce.abi_encode())

crates/forge/tests/it/spec.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,99 @@
11
//! Integration tests for EVM specifications.
22
33
use crate::{config::*, test_helpers::TEST_DATA_PARIS};
4-
use foundry_test_utils::Filter;
4+
use foundry_test_utils::{Filter, forgetest_init, rpc, str};
55
use revm::primitives::hardfork::SpecId;
66

77
#[tokio::test(flavor = "multi_thread")]
88
async fn test_shanghai_compat() {
99
let filter = Filter::new("", "ShanghaiCompat", ".*spec");
1010
TestConfig::with_filter(TEST_DATA_PARIS.runner(), filter).spec_id(SpecId::SHANGHAI).run().await;
1111
}
12+
13+
// Test evm version switch during tests / scripts.
14+
// <https://github.com/foundry-rs/foundry/issues/9840>
15+
// <https://github.com/foundry-rs/foundry/issues/6228>
16+
forgetest_init!(test_set_evm_version, |prj, cmd| {
17+
let endpoint = rpc::next_http_archive_rpc_url();
18+
prj.add_test(
19+
"TestEvmVersion.t.sol",
20+
&r#"
21+
import {Test} from "forge-std/Test.sol";
22+
23+
interface EvmVm {
24+
function getEvmVersion() external pure returns (string memory evm);
25+
function setEvmVersion(string calldata evm) external;
26+
}
27+
28+
interface ICreate2Deployer {
29+
function computeAddress(bytes32 salt, bytes32 codeHash) external view returns (address);
30+
}
31+
32+
contract TestEvmVersion is Test {
33+
function test_evm_version() public {
34+
EvmVm evm = EvmVm(address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))));
35+
vm.createSelectFork("<rpc>");
36+
37+
evm.setEvmVersion("istanbul");
38+
evm.getEvmVersion();
39+
40+
// revert with NotActivated for istanbul
41+
vm.expectRevert();
42+
compute();
43+
44+
evm.setEvmVersion("shanghai");
45+
evm.getEvmVersion();
46+
compute();
47+
48+
// switch to Paris, expect revert with NotActivated
49+
evm.setEvmVersion("paris");
50+
vm.expectRevert();
51+
compute();
52+
}
53+
54+
function compute() internal view {
55+
ICreate2Deployer(0x35Da41c476fA5c6De066f20556069096A1F39364).computeAddress(bytes32(0), bytes32(0));
56+
}
57+
}
58+
"#.replace("<rpc>", &endpoint),
59+
);
60+
61+
cmd.args(["test", "--mc", "TestEvmVersion", "-vvvv"]).assert_success().stdout_eq(str![[r#"
62+
[COMPILING_FILES] with [SOLC_VERSION]
63+
[SOLC_VERSION] [ELAPSED]
64+
Compiler run successful!
65+
66+
Ran 1 test for test/TestEvmVersion.t.sol:TestEvmVersion
67+
[PASS] test_evm_version() ([GAS])
68+
Traces:
69+
[..] TestEvmVersion::test_evm_version()
70+
├─ [0] VM::createSelectFork("<rpc url>")
71+
│ └─ ← [Return] 0
72+
├─ [0] VM::setEvmVersion("istanbul")
73+
│ └─ ← [Return]
74+
├─ [0] VM::getEvmVersion() [staticcall]
75+
│ └─ ← [Return] "istanbul"
76+
├─ [0] VM::expectRevert(custom error 0xf4844814)
77+
│ └─ ← [Return]
78+
├─ [..] 0x35Da41c476fA5c6De066f20556069096A1F39364::computeAddress(0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000) [staticcall]
79+
│ └─ ← [NotActivated] EvmError: NotActivated
80+
├─ [0] VM::setEvmVersion("shanghai")
81+
│ └─ ← [Return]
82+
├─ [0] VM::getEvmVersion() [staticcall]
83+
│ └─ ← [Return] "shanghai"
84+
├─ [..] 0x35Da41c476fA5c6De066f20556069096A1F39364::computeAddress(0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000) [staticcall]
85+
│ └─ ← [Return] 0x0f40d7B7669e3a6683EaB25358318fd42a9F2342
86+
├─ [0] VM::setEvmVersion("paris")
87+
│ └─ ← [Return]
88+
├─ [0] VM::expectRevert(custom error 0xf4844814)
89+
│ └─ ← [Return]
90+
├─ [..] 0x35Da41c476fA5c6De066f20556069096A1F39364::computeAddress(0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000) [staticcall]
91+
│ └─ ← [NotActivated] EvmError: NotActivated
92+
└─ ← [Stop]
93+
94+
Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED]
95+
96+
Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests)
97+
98+
"#]]);
99+
});

testdata/cheats/Vm.sol

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)