Skip to content

Commit 1e06cef

Browse files
committed
fix(forge): determine if fixed gas limit when simulate
1 parent 81d50d9 commit 1e06cef

File tree

1 file changed

+40
-19
lines changed

1 file changed

+40
-19
lines changed

crates/cheatcodes/src/inspector.rs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,10 @@ pub struct Cheatcodes {
499499
pub wallets: Option<Wallets>,
500500
/// Signatures identifier for decoding events and functions
501501
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>,
502506
}
503507

504508
// This is not derived because calling this in `fn new` with `..Default::default()` creates a second
@@ -554,6 +558,7 @@ impl Cheatcodes {
554558
deprecated: Default::default(),
555559
wallets: Default::default(),
556560
signatures_identifier: SignaturesIdentifier::new(true).ok(),
561+
is_fixed_gas_limit: Default::default(),
557562
}
558563
}
559564

@@ -852,8 +857,12 @@ impl Cheatcodes {
852857
});
853858
}
854859

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+
};
857866
let input = TransactionInput::new(call.input.bytes(ecx));
858867

859868
let account =
@@ -1075,6 +1084,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
10751084
fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
10761085
self.pc = interpreter.bytecode.pc();
10771086

1087+
if self.broadcast.is_some() {
1088+
self.record_gas_limit_opcode(interpreter);
1089+
}
1090+
10781091
// `pauseGasMetering`: pause / resume interpreter gas.
10791092
if self.gas_metering.paused {
10801093
self.meter_gas(interpreter);
@@ -1115,6 +1128,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
11151128
}
11161129

11171130
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+
11181135
if self.gas_metering.paused {
11191136
self.meter_gas_end(interpreter);
11201137
}
@@ -1612,8 +1629,6 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
16121629

16131630
if curr_depth == broadcast.depth {
16141631
input.set_caller(broadcast.new_origin);
1615-
let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, input.gas_limit());
1616-
16171632
let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
16181633
self.broadcastable_transactions.push_back(BroadcastableTransaction {
16191634
rpc: ecx.journaled_state.database.active_fork_url(),
@@ -1623,7 +1638,6 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
16231638
value: Some(input.value()),
16241639
input: TransactionInput::new(input.init_code()),
16251640
nonce: Some(account.info.nonce),
1626-
gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
16271641
..Default::default()
16281642
}
16291643
.into(),
@@ -2292,6 +2306,27 @@ impl Cheatcodes {
22922306
(REVERT, 0, 1, false),
22932307
);
22942308
}
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+
}
22952330
}
22962331

22972332
/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
@@ -2319,20 +2354,6 @@ fn disallowed_mem_write(
23192354
));
23202355
}
23212356

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-
23362357
/// Returns true if the kind of account access is a call.
23372358
fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
23382359
matches!(

0 commit comments

Comments
 (0)