1use super::{PreSimulationState, RpcContexts, RpcSimulationContext, context_for_rpc};
4use crate::{
5 ScriptResult,
6 runner::{GasSearch, ScriptRunner},
7 simulate::FilledTransactionsState,
8 transaction::ScriptTransactionBuilder,
9};
10use alloy_eips::eip7702::SignedAuthorization;
11use alloy_network::Ethereum;
12use alloy_primitives::{Address, Bytes, TxKind, U256, map::HashMap};
13use eyre::{Result, WrapErr};
14use foundry_evm::{
15 backend::DatabaseExt,
16 core::{
17 FoundryBlock, FoundryTransaction,
18 evm::{BlockContext, ChainFor, EvmEnvFor, MonadEvmNetwork, TxEnvFor},
19 },
20 executors::{DeployResult, EvmError},
21 revm::{context::Transaction, context_interface::result::Output, interpreter::return_ok},
22};
23use futures::future::join_all;
24use parking_lot::RwLock;
25use std::sync::Arc;
26
27struct MonadSimulation {
28 runner: ScriptRunner<MonadEvmNetwork>,
29 cursor: Option<BlockContext<MonadEvmNetwork>>,
30}
31
32impl MonadSimulation {
33 fn new(runner: ScriptRunner<MonadEvmNetwork>) -> Result<Self> {
34 let cursor = runner.executor.backend().block_context_for_synthetic_transaction()?;
35 Ok(Self { runner, cursor })
36 }
37
38 fn context(&self, tx: &TxEnvFor<MonadEvmNetwork>) -> Result<ChainFor<MonadEvmNetwork>> {
39 self.cursor.as_ref().map_or_else(
40 || self.runner.executor.backend().chain_context_for_synthetic_transaction(tx),
41 |cursor| Ok(cursor.next_transaction(tx)),
42 )
43 }
44
45 fn record(&mut self, tx: TxEnvFor<MonadEvmNetwork>) {
46 if let Some(cursor) = &mut self.cursor {
47 cursor.record_transaction(tx);
48 }
49 }
50
51 fn prepare_call(
52 &self,
53 from: Address,
54 to: Address,
55 calldata: Bytes,
56 value: U256,
57 authorization_list: Option<Vec<SignedAuthorization>>,
58 ) -> (EvmEnvFor<MonadEvmNetwork>, TxEnvFor<MonadEvmNetwork>) {
59 let (env, mut tx) = self.runner.executor.prepare_call_env(from, to.into(), calldata, value);
60 if let Some(authorization_list) = authorization_list {
61 tx.set_signed_authorization(authorization_list);
62 tx.set_tx_type(4);
63 }
64 (env, tx)
65 }
66
67 fn simulate(
68 &mut self,
69 from: Address,
70 to: Option<Address>,
71 calldata: Option<Bytes>,
72 value: Option<U256>,
73 authorization_list: Option<Vec<SignedAuthorization>>,
74 ) -> Result<ScriptResult<Ethereum>> {
75 let value = value.unwrap_or_default();
76 let Some(to) = to else {
77 let (env, tx) = self.runner.executor.prepare_call_env(
78 from,
79 TxKind::Create,
80 calldata.expect("No data for create transaction"),
81 value,
82 );
83 let context = self.context(&tx)?;
84 let result = self
85 .runner
86 .executor
87 .transact_with_env_and_context(env, tx, context)
88 .map_err(EvmError::from)
89 .and_then(|raw| {
90 self.record(raw.tx_env.clone());
92 let raw = raw.into_result(None)?;
93 let Some(Output::Create(_, Some(address))) = raw.out else {
94 panic!("Deployment succeeded, but no address was returned: {raw:#?}");
95 };
96 self.runner.executor.backend_mut().add_persistent_account(address);
97 Ok(DeployResult { raw, address })
98 });
99 return self.runner.deployment_result(result);
100 };
101
102 let calldata = calldata.unwrap_or_default();
103 let (env, tx) =
104 self.prepare_call(from, to, calldata.clone(), value, authorization_list.clone());
105 let context = self.context(&tx)?;
106 let result = self.runner.executor.call_with_env_and_context(env, tx, context)?;
107 let mut gas_used = result.gas_used;
108 if matches!(result.exit_reason, Some(return_ok!())) {
109 let initial_limit = self.runner.executor.tx_env().gas_limit();
110 let mut search = GasSearch::new(gas_used);
111 while let Some(limit) = search.next_limit() {
112 self.runner.executor.tx_env_mut().set_gas_limit(limit);
114 let (env, tx) = self.prepare_call(from, to, calldata.clone(), value, None);
115 let context = self.context(&tx)?;
116 let result = self.runner.executor.call_with_env_and_context(env, tx, context)?;
117 search.record(limit, result.exit_reason);
118 }
119 gas_used = search.gas_used();
120 self.runner.executor.tx_env_mut().set_gas_limit(initial_limit);
121 }
122
123 let (env, tx) = self.prepare_call(from, to, calldata, value, authorization_list);
124 let context = self.context(&tx)?;
125 let result = self.runner.executor.transact_with_env_and_context(env, tx, context)?;
126 self.record(result.tx_env.clone());
127 Ok(self.runner.call_result(result, gas_used))
128 }
129
130 fn advance_block(&mut self) {
131 if let Some(cursor) = &mut self.cursor {
132 cursor.advance_block();
133 }
134 let block = &mut self.runner.executor.evm_env_mut().block_env;
135 block.set_number(block.number + U256::from(1));
136 }
137}
138
139impl PreSimulationState<MonadEvmNetwork> {
140 pub(crate) async fn fill_monad_metadata(
141 self,
142 ) -> Result<FilledTransactionsState<MonadEvmNetwork>> {
143 if self.args.skip_simulation {
144 return self.fill_without_simulation().await;
145 }
146
147 let mut contexts = HashMap::default();
148 for (rpc, context) in self.build_runners().await? {
149 contexts.insert(
150 rpc,
151 RpcSimulationContext {
152 runner: RwLock::new(MonadSimulation::new(context.runner.into_inner())?),
153 decoder: context.decoder,
154 },
155 );
156 }
157 let contexts = Arc::new(contexts);
158 let transactions =
159 self.transaction_metadata(&RpcContexts::Simulation(Arc::clone(&contexts)))?;
160 let futs = transactions
161 .into_iter()
162 .map(|mut transaction| async {
163 let rpc = transaction.rpc.clone();
164 let context = context_for_rpc(&contexts, &rpc);
165 let mut simulation = context.runner.write();
166 let tx = transaction.tx_mut();
167 let to = tx.to();
168 let result = simulation
169 .simulate(
170 tx.from()
171 .expect("transaction doesn't have a `from` address at execution time"),
172 to,
173 tx.input().cloned(),
174 tx.value(),
175 tx.authorization_list(),
176 )
177 .wrap_err("Internal EVM error during simulation")?;
178 if !result.success {
179 return Ok((rpc, None, false, result.traces));
180 }
181 if self.args.slow {
182 simulation.advance_block();
183 }
184 let is_noop = if let Some(to) = to {
185 simulation.runner.executor.is_empty_code(to)?
186 && tx.value().unwrap_or_default().is_zero()
187 } else {
188 false
189 };
190 let transaction = ScriptTransactionBuilder::from(transaction)
191 .with_execution_result(
192 &result,
193 self.args.gas_estimate_multiplier,
194 &self.build_data,
195 )
196 .build();
197 eyre::Ok((rpc, Some(transaction), is_noop, result.traces))
198 })
199 .collect::<Vec<_>>();
200 self.show_simulation_header()?;
201 let transactions = self.collect_simulation_results(join_all(futs).await, &contexts).await?;
202 Ok(self.into_filled(transactions))
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use alloy_primitives::hex;
210 use foundry_evm::{backend::Backend, executors::ExecutorBuilder, opts::EvmOpts};
211 use foundry_evm_networks::NetworkConfigs;
212
213 fn simulation() -> MonadSimulation {
214 let mut env = EvmEnvFor::<MonadEvmNetwork>::default();
215 env.cfg_env.disable_nonce_check = true;
217 let executor = ExecutorBuilder::<MonadEvmNetwork>::new().gas_limit(1 << 20).build(
218 env,
219 TxEnvFor::<MonadEvmNetwork>::default(),
220 Backend::spawn(None).unwrap(),
221 NetworkConfigs::with_monad(),
222 );
223 let runner = ScriptRunner::new(executor, EvmOpts::default());
224 let mut simulation = MonadSimulation::new(runner).unwrap();
225 assert!(simulation.cursor.is_none());
226 simulation.cursor = Some(BlockContext::new(Vec::new(), Vec::new(), Vec::new()));
227 simulation
228 }
229
230 fn assert_transaction_count(simulation: &MonadSimulation, count: usize) {
231 let cursor = simulation.cursor.as_ref().unwrap();
232 assert!(cursor.clone().before_transaction(count).is_ok());
233 assert!(cursor.clone().before_transaction(count + 1).is_err());
234 }
235
236 #[test]
237 fn calls_and_gas_probes_record_only_committed_transactions() {
238 let other = simulation();
239 let mut simulation = simulation();
240 let sender = Address::with_last_byte(0x42);
241 let recipient = Address::with_last_byte(0x43);
242 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
243 let result = simulation.simulate(sender, Some(recipient), None, None, None).unwrap();
244 assert!(result.success);
245 assert_transaction_count(&simulation, 1);
246 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 1);
247
248 simulation.advance_block();
249 assert_transaction_count(&simulation, 0);
250 assert_eq!(simulation.runner.executor.evm_env().block_env.number, U256::from(1));
251 assert_transaction_count(&other, 0);
252 }
253
254 #[test]
255 fn deployment_and_reverted_call_both_advance_the_cursor() {
256 let mut simulation = simulation();
257 let sender = Address::with_last_byte(0x42);
258 simulation.runner.executor.set_balance(sender, U256::MAX).unwrap();
259 let initcode = Bytes::from_static(&hex!("6005600c60003960056000f360006000fd"));
261 let deployment = simulation.simulate(sender, None, Some(initcode), None, None).unwrap();
262 assert!(deployment.success);
263 assert_transaction_count(&simulation, 1);
264 let address = deployment.address.unwrap();
265 assert!(!simulation.runner.executor.is_empty_code(address).unwrap());
266 let result = simulation.simulate(sender, Some(address), None, None, None).unwrap();
267 assert!(!result.success);
268 assert_transaction_count(&simulation, 2);
269 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 2);
270 }
271
272 #[test]
273 fn validation_failure_does_not_advance_the_cursor() {
274 let mut simulation = simulation();
275 simulation.runner.executor.set_gas_limit(1);
276 let sender = Address::with_last_byte(0x42);
277 let result =
278 simulation.simulate(sender, Some(Address::with_last_byte(0x43)), None, None, None);
279 assert!(result.is_err());
280 assert_transaction_count(&simulation, 0);
281 assert_eq!(simulation.runner.executor.get_nonce(sender).unwrap(), 0);
282 }
283
284 #[test]
285 fn authorization_preparation_preserves_explicit_empty_list() {
286 let simulation = simulation();
287 let (_, tx) = simulation.prepare_call(
288 Address::with_last_byte(0x42),
289 Address::with_last_byte(0x43),
290 Bytes::new(),
291 U256::ZERO,
292 Some(Vec::new()),
293 );
294 assert_eq!(tx.tx_type(), 4);
295 assert_transaction_count(&simulation, 0);
296 }
297}