@@ -499,6 +499,11 @@ pub struct Cheatcodes {
499499 pub wallets : Option < Wallets > ,
500500 /// Signatures identifier for decoding events and functions
501501 pub signatures_identifier : Option < SignaturesIdentifier > ,
502+ /// Used to determine whether the broadcasted call has non-fixed gas limit.
503+ /// Holds values for (seen opcode GAS, seen opcode CALL) pair.
504+ /// If GAS opcode is followed by CALL opcode then both flags are marked true and call
505+ /// has non-fixed gas limit, otherwise the call is considered to have fixed gas limit.
506+ pub dynamic_gas_limit_sequence : Option < ( bool , bool ) > ,
502507}
503508
504509// This is not derived because calling this in `fn new` with `..Default::default()` creates a second
@@ -554,6 +559,7 @@ impl Cheatcodes {
554559 deprecated : Default :: default ( ) ,
555560 wallets : Default :: default ( ) ,
556561 signatures_identifier : SignaturesIdentifier :: new ( true ) . ok ( ) ,
562+ dynamic_gas_limit_sequence : Default :: default ( ) ,
557563 }
558564 }
559565
@@ -852,8 +858,15 @@ impl Cheatcodes {
852858 } ) ;
853859 }
854860
855- let is_fixed_gas_limit = check_if_fixed_gas_limit ( & ecx, call. gas_limit ) ;
856-
861+ let ( gas_seen, call_seen) =
862+ self . dynamic_gas_limit_sequence . take ( ) . unwrap_or_default ( ) ;
863+ // Transaction has fixed gas limit if no GAS opcode seen before CALL opcode.
864+ let mut is_fixed_gas_limit = !( gas_seen && call_seen) ;
865+ // Additional check as transfers in forge scripts seem to be estimated at 2300
866+ // by revm leading to "Intrinsic gas too low" failure when simulated on chain.
867+ if call. gas_limit < 21_000 {
868+ is_fixed_gas_limit = false ;
869+ }
857870 let input = TransactionInput :: new ( call. input . bytes ( ecx) ) ;
858871 // Ensure account is touched.
859872 ecx. journaled_state . touch ( broadcast. new_origin ) ;
@@ -1077,6 +1090,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
10771090 fn step ( & mut self , interpreter : & mut Interpreter , ecx : Ecx ) {
10781091 self . pc = interpreter. bytecode . pc ( ) ;
10791092
1093+ if self . broadcast . is_some ( ) {
1094+ self . record_gas_limit_opcode ( interpreter) ;
1095+ }
1096+
10801097 // `pauseGasMetering`: pause / resume interpreter gas.
10811098 if self . gas_metering . paused {
10821099 self . meter_gas ( interpreter) ;
@@ -1117,6 +1134,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
11171134 }
11181135
11191136 fn step_end ( & mut self , interpreter : & mut Interpreter , ecx : Ecx ) {
1137+ if self . broadcast . is_some ( ) {
1138+ self . set_gas_limit_type ( interpreter) ;
1139+ }
1140+
11201141 if self . gas_metering . paused {
11211142 self . meter_gas_end ( interpreter) ;
11221143 }
@@ -1614,7 +1635,7 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
16141635
16151636 if curr_depth == broadcast. depth {
16161637 input. set_caller ( broadcast. new_origin ) ;
1617- let is_fixed_gas_limit = check_if_fixed_gas_limit ( & ecx , input . gas_limit ( ) ) ;
1638+
16181639 // Ensure account is touched.
16191640 ecx. journaled_state . touch ( broadcast. new_origin ) ;
16201641
@@ -1627,7 +1648,6 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
16271648 value : Some ( input. value ( ) ) ,
16281649 input : TransactionInput :: new ( input. init_code ( ) ) ,
16291650 nonce : Some ( account. info . nonce ) ,
1630- gas : if is_fixed_gas_limit { Some ( input. gas_limit ( ) ) } else { None } ,
16311651 ..Default :: default ( )
16321652 }
16331653 . into ( ) ,
@@ -2296,6 +2316,40 @@ impl Cheatcodes {
22962316 ( REVERT , 0 , 1 , false ) ,
22972317 ) ;
22982318 }
2319+
2320+ #[ cold]
2321+ fn record_gas_limit_opcode ( & mut self , interpreter : & mut Interpreter ) {
2322+ match interpreter. bytecode . opcode ( ) {
2323+ // If current opcode is CREATE2 then set non-fixed gas limit.
2324+ op:: CREATE2 => self . dynamic_gas_limit_sequence = Some ( ( true , true ) ) ,
2325+ op:: GAS => {
2326+ if self . dynamic_gas_limit_sequence . is_none ( ) {
2327+ // If current opcode is GAS then mark as seen.
2328+ self . dynamic_gas_limit_sequence = Some ( ( true , false ) ) ;
2329+ }
2330+ }
2331+ _ => { }
2332+ }
2333+ }
2334+
2335+ #[ cold]
2336+ fn set_gas_limit_type ( & mut self , interpreter : & mut Interpreter ) {
2337+ // Early exit in case we already determined is non-fixed gas limit.
2338+ if matches ! ( self . dynamic_gas_limit_sequence, Some ( ( true , true ) ) ) {
2339+ return ;
2340+ }
2341+
2342+ // Record CALL opcode if GAS opcode was seen.
2343+ if matches ! ( self . dynamic_gas_limit_sequence, Some ( ( true , false ) ) )
2344+ && interpreter. bytecode . opcode ( ) == op:: CALL
2345+ {
2346+ self . dynamic_gas_limit_sequence = Some ( ( true , true ) ) ;
2347+ return ;
2348+ }
2349+
2350+ // Reset dynamic gas limit sequence if GAS opcode was not followed by a CALL opcode.
2351+ self . dynamic_gas_limit_sequence = None ;
2352+ }
22992353}
23002354
23012355/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
@@ -2323,20 +2377,6 @@ fn disallowed_mem_write(
23232377 ) ) ;
23242378}
23252379
2326- // Determines if the gas limit on a given call was manually set in the script and should therefore
2327- // not be overwritten by later estimations
2328- fn check_if_fixed_gas_limit ( ecx : & Ecx , call_gas_limit : u64 ) -> bool {
2329- // If the gas limit was not set in the source code it is set to the estimated gas left at the
2330- // time of the call, which should be rather close to configured gas limit.
2331- // TODO: Find a way to reliably make this determination.
2332- // For example by generating it in the compilation or EVM simulation process
2333- ecx. tx . gas_limit > ecx. block . gas_limit &&
2334- call_gas_limit <= ecx. block . gas_limit
2335- // Transfers in forge scripts seem to be estimated at 2300 by revm leading to "Intrinsic
2336- // gas too low" failure when simulated on chain
2337- && call_gas_limit > 2300
2338- }
2339-
23402380/// Returns true if the kind of account access is a call.
23412381fn access_is_call ( kind : crate :: Vm :: AccountAccessKind ) -> bool {
23422382 matches ! (
0 commit comments