@@ -18,6 +18,9 @@ use solang_parser::pt;
18
18
use std:: { collections:: HashMap , fs, path:: PathBuf } ;
19
19
use yansi:: Paint ;
20
20
21
+ /// The minimum Solidity version of the `Vm` interface.
22
+ pub const MIN_VM_VERSION : Version = Version :: new ( 0 , 6 , 2 ) ;
23
+
21
24
/// Solidity source for the `Vm` interface in [forge-std](https://github.com/foundry-rs/forge-std)
22
25
static VM_SOURCE : & str = include_str ! ( "../../../testdata/cheats/Vm.sol" ) ;
23
26
@@ -69,6 +72,8 @@ pub struct SessionSourceConfig {
69
72
pub foundry_config : Config ,
70
73
/// EVM Options
71
74
pub evm_opts : EvmOpts ,
75
+ /// Disable the default `Vm` import.
76
+ pub no_vm : bool ,
72
77
#[ serde( skip) ]
73
78
/// In-memory REVM db for the session's runner.
74
79
pub backend : Option < Backend > ,
@@ -184,9 +189,13 @@ impl SessionSource {
184
189
///
185
190
/// A new instance of [SessionSource]
186
191
#[ 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
+ }
190
199
191
200
Self {
192
201
file_name : PathBuf :: from ( "ReplContract.sol" . to_string ( ) ) ,
@@ -315,14 +324,15 @@ impl SessionSource {
315
324
sources. insert ( self . file_name . clone ( ) , Source :: new ( self . to_repl_source ( ) ) ) ;
316
325
317
326
// 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" ) )
324
334
{
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 ) ) ;
326
336
}
327
337
328
338
// we only care about the solidity source, so we can safely unwrap
@@ -446,24 +456,27 @@ impl SessionSource {
446
456
/// The [SessionSource] represented as a Forge Script contract.
447
457
pub fn to_script_source ( & self ) -> String {
448
458
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
+
449
464
format ! (
450
465
r#"
451
466
// SPDX-License-Identifier: UNLICENSED
452
467
pragma solidity ^{major}.{minor}.{patch};
453
468
454
- import {{Script}} from "forge-std/Script.sol";
455
- {}
469
+ {script_import}
470
+ {global_code }
456
471
457
- contract {} is Script {{
458
- {}
459
-
472
+ contract {contract_name } is Script {{
473
+ {top_level_code }
474
+
460
475
/// @notice Script entry point
461
476
function run() public {{
462
- {}
477
+ {run_code }
463
478
}}
464
- }}
465
- "# ,
466
- self . global_code, self . contract_name, self . top_level_code, self . run_code,
479
+ }}"# ,
467
480
)
468
481
}
469
482
@@ -474,25 +487,34 @@ contract {} is Script {{
474
487
/// The [SessionSource] represented as a REPL contract.
475
488
pub fn to_repl_source ( & self ) -> String {
476
489
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
+
477
501
format ! (
478
502
r#"
479
503
// SPDX-License-Identifier: UNLICENSED
480
504
pragma solidity ^{major}.{minor}.{patch};
481
505
482
- import {{Vm}} from "forge-std/Vm.sol";
483
- {}
506
+ {vm_import}
507
+ {global_code }
484
508
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 }
488
512
489
513
/// @notice REPL contract entry point
490
514
function run() public {{
491
- {}
515
+ {run_code }
492
516
}}
493
- }}
494
- "# ,
495
- self . global_code, self . contract_name, self . top_level_code, self . run_code,
517
+ }}"# ,
496
518
)
497
519
}
498
520
@@ -646,14 +668,17 @@ pub fn parse_fragment(
646
668
) -> Option < ParseTreeFragment > {
647
669
let mut base = SessionSource :: new ( solc, config) ;
648
670
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) ,
651
674
}
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) ,
654
678
}
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) ,
657
682
}
658
683
659
684
None
0 commit comments