1use super::{PreSimulationState, RpcSimulationContext};
4use crate::{
5 ScriptResult,
6 runner::{GasSearch, ScriptRunner, needs_more_gas},
7 simulate::FilledTransactionsState,
8 transaction::ScriptTransactionBuilder,
9};
10use alloy_eips::eip7702::SignedAuthorization;
11use alloy_evm::EvmEnv;
12use alloy_network::Ethereum;
13use alloy_primitives::{Address, Bytes, TxKind, U256, map::HashMap};
14use eyre::{Result, WrapErr};
15use foundry_common::TransactionMaybeSigned;
16use foundry_evm::{
17 backend::DatabaseExt,
18 core::{
19 FoundryBlock, FoundryTransaction,
20 evm::{BlockContext, MonadEvmNetwork},
21 },
22 executors::{DeployResult, EvmError},
23 revm::{
24 context::{Transaction, TxEnv},
25 context_interface::result::Output,
26 interpreter::return_ok,
27 },
28};
29use monad_revm::{MonadChainContext, MonadHardfork};
30
31struct MonadSimulation {
32 runner: ScriptRunner<MonadEvmNetwork>,
33 cursor: Option<BlockContext<MonadEvmNetwork>>,
34}
35
36impl MonadSimulation {
37 fn new(runner: ScriptRunner<MonadEvmNetwork>) -> Result<Self> {
38 let cursor = runner.executor.backend().block_context_for_synthetic_transaction()?;
39 Ok(Self { runner, cursor })
40 }
41
42 fn context(&self, tx: &TxEnv) -> Result<MonadChainContext> {
43 self.cursor.as_ref().map_or_else(
44 || self.runner.executor.backend().chain_context_for_synthetic_transaction(tx),
45 |cursor| Ok(cursor.next_transaction(tx)),
46 )
47 }
48
49 fn record(&mut self, tx: TxEnv) {
50 if let Some(cursor) = &mut self.cursor {
51 cursor.record_transaction(tx);
52 }
53 }
54
55 fn prepare_call(
56 &self,
57 from: Address,
58 to: Address,
59 calldata: Bytes,
60 value: U256,
61 authorization_list: Option<Vec<SignedAuthorization>>,
62 ) -> (EvmEnv<MonadHardfork>, TxEnv) {
63 let (env, mut tx) = self.runner.executor.prepare_call_env(from, to.into(), calldata, value);
64 if let Some(authorization_list) = authorization_list {
65 tx.set_signed_authorization(authorization_list);
66 tx.set_tx_type(4);
67 }
68 (env, tx)
69 }
70
71 fn simulate(
72 &mut self,
73 transaction: &TransactionMaybeSigned<Ethereum>,
74 is_fixed_gas_limit: bool,
75 ) -> Result<ScriptResult<Ethereum>> {
76 let from = transaction.from().expect("transaction sender missing");
77 let calldata = transaction.input().cloned();
78 let value = transaction.value().unwrap_or_default();
79 let authorization_list = transaction.authorization_list();
80 let Some(to) = transaction.to() else {
81 let (env, tx) = if is_fixed_gas_limit {
82 self.runner.prepare_fixed_gas_env(transaction)
83 } else {
84 self.runner.executor.prepare_call_env(
85 from,
86 TxKind::Create,
87 calldata.expect("No data for create transaction"),
88 value,
89 )
90 };
91 let context = self.context(&tx)?;
92 let result = self
93 .runner
94 .executor
95 .transact_with_env_and_context(env, tx, context)
96 .map_err(EvmError::from)
97 .and_then(|raw| {
98 self.record(raw.tx_env.clone());
100 let raw = raw.into_result(None)?;
101 let Some(Output::Create(_, Some(address))) = raw.out else {
102 panic!("Deployment succeeded, but no address was returned: {raw:#?}");
103 };
104 self.runner.executor.backend_mut().add_persistent_account(address);
105 Ok(DeployResult { raw, address })
106 });
107 return self.runner.deployment_result(result);
108 };
109
110 if is_fixed_gas_limit {
111 let (env, tx) = self.runner.prepare_fixed_gas_env(transaction);
112 let context = self.context(&tx)?;
113 let result = self.runner.executor.transact_with_env_and_context(env, tx, context)?;
114 self.record(result.tx_env.clone());
115 let gas_used = result.gas_used;
116 return Ok(self.runner.call_result(result, gas_used));
117 }
118
119 let calldata = calldata.unwrap_or_default();
120 let (env, tx) =
121 self.prepare_call(from, to, calldata.clone(), value, authorization_list.clone());
122 let context = self.context(&tx)?;
123 let result = self.runner.executor.call_with_env_and_context(env, tx, context)?;
124 let mut gas_used = result.gas_used;
125 if matches!(result.exit_reason, Some(return_ok!())) {
126 let initial_limit = self.runner.executor.tx_env().gas_limit();
127 let mut search = GasSearch::new(gas_used);
128 while let Some(limit) = search.next_limit() {
129 self.runner.executor.tx_env_mut().set_gas_limit(limit);
131 let (env, tx) = self.prepare_call(from, to, calldata.clone(), value, None);
132 let context = self.context(&tx)?;
133 let result = self.runner.executor.call_with_env_and_context(env, tx, context)?;
134 search.record(limit, needs_more_gas(result.exit_reason));
135 }
136 gas_used = search.gas_used();
137 self.runner.executor.tx_env_mut().set_gas_limit(initial_limit);
138 }
139
140 let (env, tx) = self.prepare_call(from, to, calldata, value, authorization_list);
141 let context = self.context(&tx)?;
142 let result = self.runner.executor.transact_with_env_and_context(env, tx, context)?;
143 self.record(result.tx_env.clone());
144 Ok(self.runner.call_result(result, gas_used))
145 }
146
147 fn advance_block(&mut self) {
148 if let Some(cursor) = &mut self.cursor {
149 cursor.advance_block();
150 }
151 let block = &mut self.runner.executor.evm_env_mut().block_env;
152 block.set_number(block.number + U256::ONE);
153 }
154}
155
156impl PreSimulationState<MonadEvmNetwork> {
157 pub(crate) async fn fill_monad_metadata(
158 self,
159 ) -> Result<FilledTransactionsState<MonadEvmNetwork>> {
160 if self.args.skip_simulation {
161 return self.fill_without_simulation().await;
162 }
163
164 let mut contexts = HashMap::default();
165 for (rpc, context) in self.build_runners().await? {
166 contexts.insert(
167 rpc,
168 RpcSimulationContext {
169 runner: MonadSimulation::new(context.runner)?,
170 decoder: context.decoder,
171 },
172 );
173 }
174 let transactions =
175 self.transaction_metadata(|rpc| &contexts.get(rpc).expect("invalid rpc url").decoder)?;
176 self.show_simulation_header()?;
177 let results = transactions
178 .into_iter()
179 .map(|transaction| {
180 let rpc = transaction.rpc.clone();
181 let simulation = &mut contexts.get_mut(&rpc).expect("invalid rpc url").runner;
182
183 let tx = transaction.tx();
184 let to = tx.to();
185 let result = simulation
186 .simulate(tx, transaction.is_fixed_gas_limit)
187 .wrap_err("Internal EVM error during simulation")?;
188 if !result.success {
189 return Ok((rpc, None, false, result.traces));
190 }
191 if self.args.slow {
192 simulation.advance_block();
193 }
194 let is_noop = if let Some(to) = to {
195 simulation.runner.executor.is_empty_code(to)?
196 && tx.value().unwrap_or_default().is_zero()
197 } else {
198 false
199 };
200 let transaction = ScriptTransactionBuilder::from(transaction)
201 .with_execution_result(
202 &result,
203 self.args.gas_estimate_multiplier,
204 &self.build_data,
205 )
206 .build();
207 eyre::Ok((rpc, Some(transaction), is_noop, result.traces))
208 })
209 .collect::<Vec<_>>();
210 let transactions = self.collect_simulation_results(results, &contexts).await?;
211 Ok(self.into_filled(transactions))
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use alloy_eips::eip2930::{AccessList, AccessListItem};
219 use alloy_network::TransactionBuilder;
220 use alloy_primitives::{B256, bytes};
221 use alloy_rpc_types::TransactionRequest;
222 use foundry_evm::{
223 backend::Backend,
224 executors::ExecutorBuilder,
225 opts::EvmOpts,
226 revm::{DatabaseRef, bytecode::Bytecode, interpreter::InstructionResult},
227 };
228 use foundry_evm_networks::NetworkConfigs;
229
230 fn simulation() -> MonadSimulation {
231 let mut env = EvmEnv::<MonadHardfork>::default();
232 env.cfg_env.disable_nonce_check = true;
234 let executor = ExecutorBuilder::<MonadEvmNetwork>::new().gas_limit(1 << 20).build(
235 env,
236 TxEnv::default(),
237 Backend::spawn(None).unwrap(),
238 NetworkConfigs::with_monad(),
239 );
240 let runner = ScriptRunner::new(executor, EvmOpts::default());
241 let mut simulation = MonadSimulation::new(runner).unwrap();
242 assert!(simulation.cursor.is_none());
243 simulation.cursor = Some(BlockContext::new(Vec::new(), Vec::new(), Vec::new()));
244 simulation
245 }
246
247 fn assert_transaction_count(simulation: &MonadSimulation, count: usize) {
248 let cursor = simulation.cursor.as_ref().unwrap();
249 assert!(cursor.clone().before_transaction(count).is_ok());
250 assert!(cursor.clone().before_transaction(count + 1).is_err());
251 }
252
253 #[test]
254 fn calls_and_gas_probes_record_only_committed_transactions() {
255 let other = simulation();
256 let mut simulation = simulation();
257 let sender = Address::with_last_byte(0x42);
258 let recipient = Address::with_last_byte(0x43);
259 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
260 let result =
261 simulation.simulate(&transaction(sender, Some(recipient), None), false).unwrap();
262 assert!(result.success);
263 assert_transaction_count(&simulation, 1);
264 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 1);
265
266 simulation.advance_block();
267 assert_transaction_count(&simulation, 0);
268 assert_eq!(simulation.runner.executor.evm_env().block_env.number, U256::ONE);
269 assert_transaction_count(&other, 0);
270 }
271
272 #[test]
273 fn deployment_and_reverted_call_both_advance_the_cursor() {
274 let mut simulation = simulation();
275 let sender = Address::with_last_byte(0x42);
276 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
277 let initcode = bytes!("6005600c60003960056000f360006000fd");
279 let deployment =
280 simulation.simulate(&transaction(sender, None, Some(initcode)), false).unwrap();
281 assert!(deployment.success);
282 assert_transaction_count(&simulation, 1);
283 let address = deployment.address.unwrap();
284 assert!(!simulation.runner.executor.is_empty_code(address).unwrap());
285 let result = simulation.simulate(&transaction(sender, Some(address), None), false).unwrap();
286 assert!(!result.success);
287 assert_transaction_count(&simulation, 2);
288 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 2);
289 }
290
291 #[test]
292 fn validation_failure_does_not_advance_the_cursor() {
293 let mut simulation = simulation();
294 simulation.runner.executor.set_gas_limit(1);
295 let sender = Address::with_last_byte(0x42);
296 let result = simulation
297 .simulate(&transaction(sender, Some(Address::with_last_byte(0x43)), None), false);
298 assert!(result.is_err());
299 assert_transaction_count(&simulation, 0);
300 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 0);
301 }
302
303 #[test]
304 fn authorization_preparation_preserves_explicit_empty_list() {
305 let simulation = simulation();
306 let (_, tx) = simulation.prepare_call(
307 Address::with_last_byte(0x42),
308 Address::with_last_byte(0x43),
309 Bytes::new(),
310 U256::ZERO,
311 Some(Vec::new()),
312 );
313 assert_eq!(tx.tx_type(), 4);
314 assert_transaction_count(&simulation, 0);
315 }
316
317 fn transaction(
318 from: Address,
319 to: Option<Address>,
320 input: Option<Bytes>,
321 ) -> TransactionMaybeSigned<Ethereum> {
322 TransactionMaybeSigned::new(
323 TransactionRequest::default()
324 .with_from(from)
325 .with_kind(to.map_or(TxKind::Create, TxKind::Call))
326 .with_input(input.unwrap_or_default()),
327 )
328 }
329
330 #[test]
331 fn fixed_gas_calls_and_deployments_preserve_block_gas_limit() {
332 let mut simulation = simulation();
333 let sender = Address::with_last_byte(0x42);
334 let block_gas_limit = 30_000_000;
335 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
336 simulation.runner.executor.evm_env_mut().block_env.set_gas_limit(block_gas_limit);
337 let executor_gas_limit = simulation.runner.executor.gas_limit();
338
339 let initcode = bytes!("456000556009601060003960096000f34560005260206000f3");
341 let deployment = simulation
342 .simulate(&fixed_transaction(sender, None, Some(initcode), 100_000), true)
343 .unwrap();
344 assert!(deployment.success);
345 let address = deployment.address.unwrap();
346 assert_eq!(
347 simulation.runner.executor.backend().storage_ref(address, U256::ZERO).unwrap(),
348 U256::from(block_gas_limit),
349 );
350 assert_transaction_count(&simulation, 1);
351 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 1);
352
353 let result = simulation
354 .simulate(&fixed_transaction(sender, Some(address), None, 50_000), true)
355 .unwrap();
356 assert!(result.success);
357 assert_eq!(U256::from_be_slice(&result.returned), U256::from(block_gas_limit));
358 assert_transaction_count(&simulation, 2);
359 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 2);
360 assert_eq!(simulation.runner.executor.gas_limit(), executor_gas_limit);
361 assert_eq!(simulation.runner.executor.evm_env().block_env.gas_limit, block_gas_limit);
362 }
363
364 #[test]
365 fn fixed_gas_access_lists_do_not_leak_to_following_transactions() {
366 let mut simulation = simulation();
367 simulation.runner.executor.set_spec_id(MonadHardfork::MonadTen);
368 let sender = Address::with_last_byte(0x42);
369 let recipient = Address::with_last_byte(0x43);
370 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
371 let executor_gas_limit = simulation.runner.executor.gas_limit();
372
373 let mut code = Vec::new();
376 for page in 0..32u16 {
377 code.push(0x61);
378 code.extend_from_slice(&(page * 128).to_be_bytes());
379 code.extend_from_slice(&[0x54, 0x50]);
380 }
381 code.push(0x00);
382 simulation.runner.executor.set_code(recipient, Bytecode::new_raw(code.into())).unwrap();
383 let access_list = AccessList(vec![AccessListItem {
384 address: recipient,
385 storage_keys: (0..32).map(|page| B256::from(U256::from(page * 128))).collect(),
386 }]);
387 let mut transaction = TransactionMaybeSigned::new(
388 TransactionRequest::default()
389 .with_from(sender)
390 .with_to(recipient)
391 .with_gas_price(0)
392 .with_gas_limit(100_000)
393 .with_access_list(access_list),
394 );
395 let result = simulation.simulate(&transaction, true).unwrap();
396 assert!(result.success);
397 assert_transaction_count(&simulation, 1);
398 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 1);
399
400 transaction.as_unsigned_mut().unwrap().access_list = None;
401 let result = simulation.simulate(&transaction, true).unwrap();
402 assert!(!result.success);
403 assert_eq!(result.exit_reason, Some(InstructionResult::OutOfGas));
404 assert_transaction_count(&simulation, 2);
405 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 2);
406
407 let result = simulation.simulate(&transaction, false).unwrap();
408 assert!(result.success);
409 assert_transaction_count(&simulation, 3);
410 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 3);
411 assert_eq!(simulation.runner.executor.gas_limit(), executor_gas_limit);
412 }
413
414 #[test]
415 fn fixed_gas_validation_failure_does_not_advance_the_cursor() {
416 let mut simulation = simulation();
417 let sender = Address::with_last_byte(0x42);
418 let recipient = Address::with_last_byte(0x43);
419 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
420 let result =
421 simulation.simulate(&fixed_transaction(sender, Some(recipient), None, 1), true);
422 assert!(result.is_err());
423 assert_transaction_count(&simulation, 0);
424 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 0);
425
426 let result = simulation
427 .simulate(&fixed_transaction(sender, Some(recipient), None, 21_000), true)
428 .unwrap();
429 assert!(result.success);
430 assert_transaction_count(&simulation, 1);
431 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 1);
432 }
433
434 fn fixed_transaction(
435 from: Address,
436 to: Option<Address>,
437 input: Option<Bytes>,
438 gas_limit: u64,
439 ) -> TransactionMaybeSigned<Ethereum> {
440 let mut transaction = transaction(from, to, input);
441 transaction.as_unsigned_mut().unwrap().set_gas_limit(gas_limit);
442 transaction
443 }
444
445 #[test]
446 fn fixed_gas_failed_deployments_advance_the_cursor() {
447 let mut simulation = simulation();
448 let sender = Address::with_last_byte(0x42);
449 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
450
451 for (index, (initcode, exit_reason)) in [
452 (bytes!("60006000fd"), InstructionResult::Revert),
453 (bytes!("5b600056"), InstructionResult::OutOfGas),
454 ]
455 .into_iter()
456 .enumerate()
457 {
458 let result = simulation
459 .simulate(&fixed_transaction(sender, None, Some(initcode), 60_000), true)
460 .unwrap();
461 assert!(!result.success);
462 assert_eq!(result.exit_reason, Some(exit_reason));
463 assert_eq!(result.address, Some(Address::ZERO));
464 assert_transaction_count(&simulation, index + 1);
465 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), index as u64 + 1);
466 }
467 }
468}