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