Skip to content

Commit f00cdf5

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

File tree

1 file changed

+34
-19
lines changed

1 file changed

+34
-19
lines changed

crates/cheatcodes/src/inspector.rs

Lines changed: 34 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,8 @@ impl Cheatcodes {
852857
});
853858
}
854859

855-
let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, call.gas_limit);
856-
860+
let is_fixed_gas_limit =
861+
self.is_fixed_gas_limit.take().is_some_and(|is_fixed| is_fixed);
857862
let input = TransactionInput::new(call.input.bytes(ecx));
858863

859864
let account =
@@ -1075,6 +1080,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
10751080
fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
10761081
self.pc = interpreter.bytecode.pc();
10771082

1083+
if self.broadcast.is_some() {
1084+
self.record_gas_limit_opcode(interpreter);
1085+
}
1086+
10781087
// `pauseGasMetering`: pause / resume interpreter gas.
10791088
if self.gas_metering.paused {
10801089
self.meter_gas(interpreter);
@@ -1115,6 +1124,10 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
11151124
}
11161125

11171126
fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1127+
if self.broadcast.is_some() {
1128+
self.set_gas_limit_type(interpreter);
1129+
}
1130+
11181131
if self.gas_metering.paused {
11191132
self.meter_gas_end(interpreter);
11201133
}
@@ -1612,8 +1625,6 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
16121625

16131626
if curr_depth == broadcast.depth {
16141627
input.set_caller(broadcast.new_origin);
1615-
let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, input.gas_limit());
1616-
16171628
let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
16181629
self.broadcastable_transactions.push_back(BroadcastableTransaction {
16191630
rpc: ecx.journaled_state.database.active_fork_url(),
@@ -1623,7 +1634,6 @@ impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
16231634
value: Some(input.value()),
16241635
input: TransactionInput::new(input.init_code()),
16251636
nonce: Some(account.info.nonce),
1626-
gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
16271637
..Default::default()
16281638
}
16291639
.into(),
@@ -2292,6 +2302,25 @@ impl Cheatcodes {
22922302
(REVERT, 0, 1, false),
22932303
);
22942304
}
2305+
2306+
#[cold]
2307+
fn record_gas_limit_opcode(&mut self, interpreter: &mut Interpreter) {
2308+
if interpreter.bytecode.opcode() == op::GAS {
2309+
self.is_fixed_gas_limit = Some(true);
2310+
}
2311+
}
2312+
2313+
#[cold]
2314+
fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2315+
if self.is_fixed_gas_limit.is_some() && interpreter.bytecode.opcode() == op::CALL {
2316+
// If GAS opcode was seen and current opcode is CALL then it doesn't have fixed gas
2317+
// limit.
2318+
self.is_fixed_gas_limit = Some(false);
2319+
} else {
2320+
// If current opcode is not CALL then reset previous seen GAS opcode.
2321+
self.is_fixed_gas_limit = None;
2322+
}
2323+
}
22952324
}
22962325

22972326
/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
@@ -2319,20 +2348,6 @@ fn disallowed_mem_write(
23192348
));
23202349
}
23212350

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

0 commit comments

Comments
 (0)