Skip to content

Commit 6b2633c

Browse files
authored
feat(chisel): add --no-vm option, enabled by default for old Solc versions (foundry-rs#6854)
* feat(chisel): add --no-vm option, enabled by default for old Solc versions * fix * feat: allow >=0.6.2 <0.8.4 * chore: clippy
1 parent 77e977f commit 6b2633c

File tree

7 files changed

+81
-58
lines changed

7 files changed

+81
-58
lines changed

crates/cheatcodes/spec/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,17 @@ mod tests {
120120
}
121121

122122
fn sol_iface() -> String {
123-
let cheats = Cheatcodes::new().to_string().trim().replace('\n', "\n ");
123+
let mut cheats = Cheatcodes::new();
124+
cheats.errors = Default::default(); // Skip errors to allow <0.8.4.
125+
let cheats = cheats.to_string().trim().replace('\n', "\n ");
124126
format!(
125127
"\
126128
// Automatically generated from `foundry-cheatcodes` Vm definitions. Do not modify manually.
127129
// This interface is just for internal testing purposes. Use `forge-std` instead.
128130
129131
// SPDX-License-Identifier: MIT OR Apache-2.0
130-
pragma solidity ^0.8.4;
132+
pragma solidity >=0.6.2 <0.9.0;
133+
pragma experimental ABIEncoderV2;
131134
132135
interface Vm {{
133136
{cheats}

crates/chisel/benches/session_source.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use chisel::session_source::{SessionSource, SessionSourceConfig};
22
use criterion::{criterion_group, Criterion};
33
use foundry_compilers::Solc;
4-
use foundry_config::Config;
5-
use foundry_evm::opts::EvmOpts;
64
use once_cell::sync::Lazy;
75
use std::hint::black_box;
86
use tokio::runtime::Runtime;
@@ -66,16 +64,7 @@ fn inspect(c: &mut Criterion) {
6664

6765
/// Helper function for getting an empty [SessionSource] with default configuration
6866
fn get_empty_session_source() -> SessionSource {
69-
SessionSource::new(
70-
SOLC.clone(),
71-
SessionSourceConfig {
72-
foundry_config: Config::default(),
73-
evm_opts: EvmOpts::default(),
74-
backend: None,
75-
traces: false,
76-
calldata: None,
77-
},
78-
)
67+
SessionSource::new(SOLC.clone(), SessionSourceConfig::default())
7968
}
8069

8170
fn rt() -> Runtime {

crates/chisel/bin/main.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ pub struct ChiselParser {
5353
#[clap(long, help_heading = "REPL options")]
5454
pub prelude: Option<PathBuf>,
5555

56+
/// Disable the default `Vm` import.
57+
#[clap(long, help_heading = "REPL options", long_help = format!(
58+
"Disable the default `Vm` import.\n\n\
59+
The import is disabled by default if the Solc version is less than {}.",
60+
chisel::session_source::MIN_VM_VERSION
61+
))]
62+
pub no_vm: bool,
63+
5664
#[clap(flatten)]
5765
pub opts: CoreBuildArgs,
5866

@@ -107,6 +115,7 @@ async fn main() -> eyre::Result<()> {
107115
// Enable traces if any level of verbosity was passed
108116
traces: config.verbosity > 0,
109117
foundry_config: config,
118+
no_vm: args.no_vm,
110119
evm_opts,
111120
backend: None,
112121
calldata: None,

crates/chisel/src/session_source.rs

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ use solang_parser::pt;
1818
use std::{collections::HashMap, fs, path::PathBuf};
1919
use yansi::Paint;
2020

21+
/// The minimum Solidity version of the `Vm` interface.
22+
pub const MIN_VM_VERSION: Version = Version::new(0, 6, 2);
23+
2124
/// Solidity source for the `Vm` interface in [forge-std](https://github.com/foundry-rs/forge-std)
2225
static VM_SOURCE: &str = include_str!("../../../testdata/cheats/Vm.sol");
2326

@@ -69,6 +72,8 @@ pub struct SessionSourceConfig {
6972
pub foundry_config: Config,
7073
/// EVM Options
7174
pub evm_opts: EvmOpts,
75+
/// Disable the default `Vm` import.
76+
pub no_vm: bool,
7277
#[serde(skip)]
7378
/// In-memory REVM db for the session's runner.
7479
pub backend: Option<Backend>,
@@ -184,9 +189,13 @@ impl SessionSource {
184189
///
185190
/// A new instance of [SessionSource]
186191
#[track_caller]
187-
pub fn new(solc: Solc, config: SessionSourceConfig) -> Self {
188-
#[cfg(debug_assertions)]
189-
let _ = solc.version().unwrap();
192+
pub fn new(solc: Solc, mut config: SessionSourceConfig) -> Self {
193+
if let Ok(v) = solc.version_short() {
194+
if v < MIN_VM_VERSION && !config.no_vm {
195+
tracing::info!(version=%v, minimum=%MIN_VM_VERSION, "Disabling VM injection");
196+
config.no_vm = true;
197+
}
198+
}
190199

191200
Self {
192201
file_name: PathBuf::from("ReplContract.sol".to_string()),
@@ -315,14 +324,15 @@ impl SessionSource {
315324
sources.insert(self.file_name.clone(), Source::new(self.to_repl_source()));
316325

317326
// Include Vm.sol if forge-std remapping is not available
318-
if !self
319-
.config
320-
.foundry_config
321-
.get_all_remappings()
322-
.into_iter()
323-
.any(|r| r.name.starts_with("forge-std"))
327+
if !self.config.no_vm &&
328+
!self
329+
.config
330+
.foundry_config
331+
.get_all_remappings()
332+
.into_iter()
333+
.any(|r| r.name.starts_with("forge-std"))
324334
{
325-
sources.insert(PathBuf::from("forge-std/Vm.sol"), Source::new(VM_SOURCE.to_owned()));
335+
sources.insert(PathBuf::from("forge-std/Vm.sol"), Source::new(VM_SOURCE));
326336
}
327337

328338
// we only care about the solidity source, so we can safely unwrap
@@ -446,24 +456,27 @@ impl SessionSource {
446456
/// The [SessionSource] represented as a Forge Script contract.
447457
pub fn to_script_source(&self) -> String {
448458
let Version { major, minor, patch, .. } = self.solc.version().unwrap();
459+
let Self { contract_name, global_code, top_level_code, run_code, config, .. } = self;
460+
461+
let script_import =
462+
if !config.no_vm { "import {Script} from \"forge-std/Script.sol\";\n" } else { "" };
463+
449464
format!(
450465
r#"
451466
// SPDX-License-Identifier: UNLICENSED
452467
pragma solidity ^{major}.{minor}.{patch};
453468
454-
import {{Script}} from "forge-std/Script.sol";
455-
{}
469+
{script_import}
470+
{global_code}
456471
457-
contract {} is Script {{
458-
{}
459-
472+
contract {contract_name} is Script {{
473+
{top_level_code}
474+
460475
/// @notice Script entry point
461476
function run() public {{
462-
{}
477+
{run_code}
463478
}}
464-
}}
465-
"#,
466-
self.global_code, self.contract_name, self.top_level_code, self.run_code,
479+
}}"#,
467480
)
468481
}
469482

@@ -474,25 +487,34 @@ contract {} is Script {{
474487
/// The [SessionSource] represented as a REPL contract.
475488
pub fn to_repl_source(&self) -> String {
476489
let Version { major, minor, patch, .. } = self.solc.version().unwrap();
490+
let Self { contract_name, global_code, top_level_code, run_code, config, .. } = self;
491+
492+
let (vm_import, vm_constant) = if !config.no_vm {
493+
(
494+
"import {Vm} from \"forge-std/Vm.sol\";\n",
495+
"Vm internal constant vm = Vm(address(uint160(uint256(keccak256(\"hevm cheat code\")))));\n"
496+
)
497+
} else {
498+
("", "")
499+
};
500+
477501
format!(
478502
r#"
479503
// SPDX-License-Identifier: UNLICENSED
480504
pragma solidity ^{major}.{minor}.{patch};
481505
482-
import {{Vm}} from "forge-std/Vm.sol";
483-
{}
506+
{vm_import}
507+
{global_code}
484508
485-
contract {} {{
486-
Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
487-
{}
509+
contract {contract_name} {{
510+
{vm_constant}
511+
{top_level_code}
488512
489513
/// @notice REPL contract entry point
490514
function run() public {{
491-
{}
515+
{run_code}
492516
}}
493-
}}
494-
"#,
495-
self.global_code, self.contract_name, self.top_level_code, self.run_code,
517+
}}"#,
496518
)
497519
}
498520

@@ -646,14 +668,17 @@ pub fn parse_fragment(
646668
) -> Option<ParseTreeFragment> {
647669
let mut base = SessionSource::new(solc, config);
648670

649-
if base.clone().with_run_code(buffer).parse().is_ok() {
650-
return Some(ParseTreeFragment::Function)
671+
match base.clone().with_run_code(buffer).parse() {
672+
Ok(_) => return Some(ParseTreeFragment::Function),
673+
Err(e) => tracing::debug!(?e),
651674
}
652-
if base.clone().with_top_level_code(buffer).parse().is_ok() {
653-
return Some(ParseTreeFragment::Contract)
675+
match base.clone().with_top_level_code(buffer).parse() {
676+
Ok(_) => return Some(ParseTreeFragment::Contract),
677+
Err(e) => tracing::debug!(?e),
654678
}
655-
if base.with_global_code(buffer).parse().is_ok() {
656-
return Some(ParseTreeFragment::Source)
679+
match base.with_global_code(buffer).parse() {
680+
Ok(_) => return Some(ParseTreeFragment::Source),
681+
Err(e) => tracing::debug!(?e),
657682
}
658683

659684
None

crates/chisel/tests/cache.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use chisel::session::ChiselSession;
22
use foundry_compilers::EvmVersion;
33
use foundry_config::Config;
4-
use foundry_evm::opts::EvmOpts;
54
use serial_test::serial;
65
use std::path::Path;
76

@@ -43,10 +42,7 @@ fn test_write_session() {
4342
// Create a new session
4443
let mut env = ChiselSession::new(chisel::session_source::SessionSourceConfig {
4544
foundry_config,
46-
evm_opts: EvmOpts::default(),
47-
backend: None,
48-
traces: false,
49-
calldata: None,
45+
..Default::default()
5046
})
5147
.unwrap_or_else(|e| panic!("Failed to create ChiselSession!, {}", e));
5248

crates/evm/core/src/fork/backend.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,14 @@ where
174174
entry.get_mut().push(listener);
175175
}
176176
Entry::Vacant(entry) => {
177-
trace!(target: "backendhandler", "preparing storage request, address={:?}, idx={}", address, idx);
177+
trace!(target: "backendhandler", %address, %idx, "preparing storage request");
178178
entry.insert(vec![listener]);
179179
let provider = self.provider.clone();
180180
let block_id = self.block_id;
181181
let fut = Box::pin(async move {
182-
let storage = provider.get_storage_at(address, idx, block_id).await;
183-
(storage.wrap_err("could not fetch slot {idx} from {address}"), address, idx)
182+
let storage =
183+
provider.get_storage_at(address, idx, block_id).await.map_err(Into::into);
184+
(storage, address, idx)
184185
});
185186
self.pending_requests.push(ProviderRequest::Storage(fut));
186187
}

testdata/cheats/Vm.sol

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

0 commit comments

Comments
 (0)