@@ -499,6 +499,10 @@ pub struct Cheatcodes {
499
499
pub wallets : Option < Wallets > ,
500
500
/// Signatures identifier for decoding events and functions
501
501
pub signatures_identifier : Option < SignaturesIdentifier > ,
502
+ /// Determine if broadcasted call has fixed gas by tracking checking that CALL is preceded by
503
+ /// GAS opcode. Set to option true when GAS opcode seen and to option false if next opcode is
504
+ /// CALL.
505
+ pub is_fixed_gas_limit : Option < bool > ,
502
506
}
503
507
504
508
// This is not derived because calling this in `fn new` with `..Default::default()` creates a second
@@ -554,6 +558,7 @@ impl Cheatcodes {
554
558
deprecated : Default :: default ( ) ,
555
559
wallets : Default :: default ( ) ,
556
560
signatures_identifier : SignaturesIdentifier :: new ( true ) . ok ( ) ,
561
+ is_fixed_gas_limit : Default :: default ( ) ,
557
562
}
558
563
}
559
564
@@ -852,8 +857,12 @@ impl Cheatcodes {
852
857
} ) ;
853
858
}
854
859
855
- let is_fixed_gas_limit = check_if_fixed_gas_limit ( & ecx, call. gas_limit ) ;
856
-
860
+ let mut is_fixed_gas_limit = self . is_fixed_gas_limit . take ( ) . unwrap_or_default ( ) ;
861
+ // Additional check as transfers in forge scripts seem to be estimated at 2300
862
+ // by revm leading to "Intrinsic gas too low" failure when simulated on chain.
863
+ if call. gas_limit < 21000 {
864
+ is_fixed_gas_limit = false ;
865
+ } ;
857
866
let input = TransactionInput :: new ( call. input . bytes ( ecx) ) ;
858
867
859
868
let account =
@@ -1075,6 +1084,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1075
1084
fn step ( & mut self , interpreter : & mut Interpreter , ecx : Ecx ) {
1076
1085
self . pc = interpreter. bytecode . pc ( ) ;
1077
1086
1087
+ if self . broadcast . is_some ( ) {
1088
+ self . record_gas_limit_opcode ( interpreter) ;
1089
+ }
1090
+
1078
1091
// `pauseGasMetering`: pause / resume interpreter gas.
1079
1092
if self . gas_metering . paused {
1080
1093
self . meter_gas ( interpreter) ;
@@ -1115,6 +1128,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1115
1128
}
1116
1129
1117
1130
fn step_end ( & mut self , interpreter : & mut Interpreter , ecx : Ecx ) {
1131
+ if self . broadcast . is_some ( ) {
1132
+ self . set_gas_limit_type ( interpreter) ;
1133
+ }
1134
+
1118
1135
if self . gas_metering . paused {
1119
1136
self . meter_gas_end ( interpreter) ;
1120
1137
}
@@ -1612,8 +1629,6 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1612
1629
1613
1630
if curr_depth == broadcast. depth {
1614
1631
input. set_caller ( broadcast. new_origin ) ;
1615
- let is_fixed_gas_limit = check_if_fixed_gas_limit ( & ecx, input. gas_limit ( ) ) ;
1616
-
1617
1632
let account = & ecx. journaled_state . inner . state ( ) [ & broadcast. new_origin ] ;
1618
1633
self . broadcastable_transactions . push_back ( BroadcastableTransaction {
1619
1634
rpc : ecx. journaled_state . database . active_fork_url ( ) ,
@@ -1623,7 +1638,6 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1623
1638
value : Some ( input. value ( ) ) ,
1624
1639
input : TransactionInput :: new ( input. init_code ( ) ) ,
1625
1640
nonce : Some ( account. info . nonce ) ,
1626
- gas : if is_fixed_gas_limit { Some ( input. gas_limit ( ) ) } else { None } ,
1627
1641
..Default :: default ( )
1628
1642
}
1629
1643
. into ( ) ,
@@ -2292,6 +2306,27 @@ impl Cheatcodes {
2292
2306
( REVERT , 0 , 1 , false ) ,
2293
2307
) ;
2294
2308
}
2309
+
2310
+ #[ cold]
2311
+ fn record_gas_limit_opcode ( & mut self , interpreter : & mut Interpreter ) {
2312
+ if interpreter. bytecode . opcode ( ) == op:: GAS {
2313
+ self . is_fixed_gas_limit = Some ( true ) ;
2314
+ }
2315
+ }
2316
+
2317
+ #[ cold]
2318
+ fn set_gas_limit_type ( & mut self , interpreter : & mut Interpreter ) {
2319
+ if interpreter. bytecode . opcode ( ) == op:: CALL {
2320
+ if self . is_fixed_gas_limit . is_some ( ) {
2321
+ // If GAS opcode was seen and current opcode is CALL then it doesn't have fixed gas
2322
+ // limit.
2323
+ self . is_fixed_gas_limit = Some ( false ) ;
2324
+ } else {
2325
+ // If GAS opcode wasn't seen then it has fixed call limit.
2326
+ self . is_fixed_gas_limit = Some ( true ) ;
2327
+ }
2328
+ }
2329
+ }
2295
2330
}
2296
2331
2297
2332
/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
@@ -2319,20 +2354,6 @@ fn disallowed_mem_write(
2319
2354
) ) ;
2320
2355
}
2321
2356
2322
- // Determines if the gas limit on a given call was manually set in the script and should therefore
2323
- // not be overwritten by later estimations
2324
- fn check_if_fixed_gas_limit ( ecx : & Ecx , call_gas_limit : u64 ) -> bool {
2325
- // If the gas limit was not set in the source code it is set to the estimated gas left at the
2326
- // time of the call, which should be rather close to configured gas limit.
2327
- // TODO: Find a way to reliably make this determination.
2328
- // For example by generating it in the compilation or EVM simulation process
2329
- ecx. tx . gas_limit > ecx. block . gas_limit &&
2330
- call_gas_limit <= ecx. block . gas_limit
2331
- // Transfers in forge scripts seem to be estimated at 2300 by revm leading to "Intrinsic
2332
- // gas too low" failure when simulated on chain
2333
- && call_gas_limit > 2300
2334
- }
2335
-
2336
2357
/// Returns true if the kind of account access is a call.
2337
2358
fn access_is_call ( kind : crate :: Vm :: AccountAccessKind ) -> bool {
2338
2359
matches ! (
0 commit comments