1use super::{ScriptConfig, ScriptResult};
2use crate::build::ScriptPredeployLibraries;
3use alloy_eips::eip7702::SignedAuthorization;
4use alloy_evm::revm::context::Transaction;
5use alloy_network::TransactionBuilder;
6use alloy_primitives::{Address, Bytes, U256};
7use eyre::Result;
8use foundry_cheatcodes::BroadcastableTransaction;
9use foundry_common::TransactionMaybeSigned;
10use foundry_config::Config;
11use foundry_evm::{
12 constants::CALLER,
13 core::{
14 FoundryTransaction,
15 evm::{FoundryEvmNetwork, TransactionRequestFor},
16 },
17 executors::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult},
18 opts::EvmOpts,
19 revm::interpreter::{InstructionResult, return_ok},
20 traces::{TraceKind, Traces},
21};
22use std::collections::VecDeque;
23
24#[derive(Debug)]
26pub struct ScriptRunner<FEN: FoundryEvmNetwork> {
27 pub executor: Executor<FEN>,
28 pub evm_opts: EvmOpts,
29}
30
31impl<FEN: FoundryEvmNetwork> ScriptRunner<FEN> {
32 pub const fn new(executor: Executor<FEN>, evm_opts: EvmOpts) -> Self {
33 Self { executor, evm_opts }
34 }
35
36 pub fn setup(
38 &mut self,
39 libraries: &ScriptPredeployLibraries,
40 code: Bytes,
41 setup: bool,
42 script_config: &ScriptConfig<FEN>,
43 is_broadcast: bool,
44 ) -> Result<(Address, ScriptResult<FEN::Network>)> {
45 trace!(target: "script", "executing setUP()");
46
47 if !is_broadcast {
48 if self.evm_opts.sender == Config::DEFAULT_SENDER {
49 self.executor.set_balance(self.evm_opts.sender, U256::MAX)?;
51 }
52
53 if script_config.evm_opts.fork_url.is_none()
54 && !script_config.evm_opts.networks.is_tempo()
55 {
56 self.executor.deploy_create2_deployer()?;
57 }
58 }
59
60 let sender_nonce = script_config.sender_nonce;
61 self.executor.set_nonce(self.evm_opts.sender, sender_nonce)?;
62
63 self.executor.set_balance(CALLER, U256::MAX)?;
65
66 let mut library_transactions = VecDeque::new();
67 let mut traces = Traces::default();
68
69 match libraries {
71 ScriptPredeployLibraries::Default(libraries) => {
72 for code in libraries {
73 let result = self
74 .executor
75 .deploy(self.evm_opts.sender, code.clone(), U256::ZERO, None)
76 .expect("couldn't deploy library")
77 .raw;
78
79 if let Some(deploy_traces) = result.traces {
80 traces.push((TraceKind::Deployment, deploy_traces));
81 }
82
83 let mut tx_req = TransactionRequestFor::<FEN>::default()
84 .with_from(self.evm_opts.sender)
85 .with_input(code.clone())
86 .with_nonce(sender_nonce + library_transactions.len() as u64);
87
88 script_config.tempo.apply::<FEN::Network>(&mut tx_req, None);
89
90 library_transactions.push_back(BroadcastableTransaction {
91 rpc: self.evm_opts.fork_url.clone(),
92 transaction: TransactionMaybeSigned::new(tx_req),
93 })
94 }
95 }
96 ScriptPredeployLibraries::Create2(libraries, salt) => {
97 let create2_deployer = self.executor.create2_deployer();
98 for library in libraries {
99 let address = create2_deployer.create2_from_code(salt, library.as_ref());
100 if !self.executor.is_empty_code(address)? {
102 continue;
103 }
104 let calldata = [salt.as_ref(), library.as_ref()].concat();
105 let result = self
106 .executor
107 .transact_raw(
108 self.evm_opts.sender,
109 create2_deployer,
110 calldata.clone().into(),
111 U256::from(0),
112 )
113 .expect("couldn't deploy library");
114
115 if let Some(deploy_traces) = result.traces {
116 traces.push((TraceKind::Deployment, deploy_traces));
117 }
118
119 let mut tx_req = TransactionRequestFor::<FEN>::default()
120 .with_from(self.evm_opts.sender)
121 .with_input(calldata)
122 .with_nonce(sender_nonce + library_transactions.len() as u64)
123 .with_to(create2_deployer);
124
125 script_config.tempo.apply::<FEN::Network>(&mut tx_req, None);
126
127 library_transactions.push_back(BroadcastableTransaction {
128 rpc: self.evm_opts.fork_url.clone(),
129 transaction: TransactionMaybeSigned::new(tx_req),
130 });
131 }
132
133 self.executor.set_nonce(
136 self.evm_opts.sender,
137 sender_nonce + library_transactions.len() as u64,
138 )?;
139 }
140 };
141
142 let address = CALLER.create(self.executor.get_nonce(CALLER)?);
143
144 self.executor.set_balance(address, self.evm_opts.initial_balance)?;
147
148 let prev_sender_nonce = self.executor.get_nonce(self.evm_opts.sender)?;
155 if self.evm_opts.sender == CALLER {
156 self.executor.set_nonce(self.evm_opts.sender, u64::MAX / 2)?;
157 }
158
159 let DeployResult {
161 address,
162 raw: RawCallResult { mut logs, traces: constructor_traces, .. },
163 } = self
164 .executor
165 .deploy(CALLER, code, U256::ZERO, None)
166 .map_err(|err| eyre::eyre!("Failed to deploy script:\n{}", err))?;
167
168 if self.evm_opts.sender == CALLER {
169 self.executor.set_nonce(self.evm_opts.sender, prev_sender_nonce)?;
170 }
171
172 if script_config.config.script_execution_protection {
174 self.executor.set_script_execution(address);
175 }
176
177 traces.extend(constructor_traces.map(|traces| (TraceKind::Deployment, traces)));
178
179 let (success, gas_used, labeled_addresses, transactions) = if setup {
181 match self.executor.setup(Some(self.evm_opts.sender), address, None) {
182 Ok(RawCallResult {
183 reverted,
184 traces: setup_traces,
185 labels,
186 logs: setup_logs,
187 gas_used,
188 transactions: setup_transactions,
189 ..
190 }) => {
191 traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces)));
192 logs.extend_from_slice(&setup_logs);
193
194 if let Some(txs) = setup_transactions {
195 library_transactions.extend(txs);
196 }
197
198 (!reverted, gas_used, labels, Some(library_transactions))
199 }
200 Err(EvmError::Execution(err)) => {
201 let RawCallResult {
202 reverted,
203 traces: setup_traces,
204 labels,
205 logs: setup_logs,
206 gas_used,
207 transactions,
208 ..
209 } = err.raw;
210 traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces)));
211 logs.extend_from_slice(&setup_logs);
212
213 if let Some(txs) = transactions {
214 library_transactions.extend(txs);
215 }
216
217 (!reverted, gas_used, labels, Some(library_transactions))
218 }
219 Err(e) => return Err(e.into()),
220 }
221 } else {
222 self.executor.backend_mut().set_test_contract(address);
223 (true, 0, Default::default(), Some(library_transactions))
224 };
225
226 Ok((
227 address,
228 ScriptResult {
229 returned: Bytes::new(),
230 success,
231 gas_used,
232 labeled_addresses,
233 transactions,
234 logs,
235 traces,
236 address: None,
237 ..Default::default()
238 },
239 ))
240 }
241
242 pub fn script(
244 &mut self,
245 address: Address,
246 calldata: Bytes,
247 ) -> Result<ScriptResult<FEN::Network>> {
248 self.call(self.evm_opts.sender, address, calldata, U256::ZERO, None, false)
249 }
250
251 pub fn simulate(
253 &mut self,
254 from: Address,
255 to: Option<Address>,
256 calldata: Option<Bytes>,
257 value: Option<U256>,
258 authorization_list: Option<Vec<SignedAuthorization>>,
259 ) -> Result<ScriptResult<FEN::Network>> {
260 if let Some(to) = to {
261 self.call(
262 from,
263 to,
264 calldata.unwrap_or_default(),
265 value.unwrap_or(U256::ZERO),
266 authorization_list,
267 true,
268 )
269 } else {
270 let res = self.executor.deploy(
271 from,
272 calldata.expect("No data for create transaction"),
273 value.unwrap_or(U256::ZERO),
274 None,
275 );
276 let (address, RawCallResult { gas_used, logs, traces, exit_reason, .. }) = match res {
277 Ok(DeployResult { address, raw }) => (address, raw),
278 Err(EvmError::Execution(err)) => {
279 let ExecutionErr { raw, reason } = *err;
280 sh_err!("Failed with `{reason}`:\n")?;
281 (Address::ZERO, raw)
282 }
283 Err(e) => eyre::bail!("Failed deploying contract: {e:?}"),
284 };
285
286 Ok(ScriptResult {
287 returned: Bytes::new(),
288 success: address != Address::ZERO,
289 gas_used,
290 logs,
291 traces: traces
293 .map(|traces| vec![(TraceKind::Execution, traces)])
294 .unwrap_or_default(),
295 exit_reason,
296 address: Some(address),
297 ..Default::default()
298 })
299 }
300 }
301
302 fn call(
309 &mut self,
310 from: Address,
311 to: Address,
312 calldata: Bytes,
313 value: U256,
314 authorization_list: Option<Vec<SignedAuthorization>>,
315 commit: bool,
316 ) -> Result<ScriptResult<FEN::Network>> {
317 let mut res = if let Some(authorization_list) = &authorization_list {
318 self.executor.call_raw_with_authorization(
319 from,
320 to,
321 calldata.clone(),
322 value,
323 authorization_list.clone(),
324 )?
325 } else {
326 self.executor.call_raw(from, to, calldata.clone(), value)?
327 };
328 let mut gas_used = res.gas_used;
329
330 if commit {
336 gas_used = self.search_optimal_gas_usage(&res, from, to, &calldata, value)?;
337 res = if let Some(authorization_list) = authorization_list {
338 self.executor.transact_raw_with_authorization(
339 from,
340 to,
341 calldata,
342 value,
343 authorization_list,
344 )?
345 } else {
346 self.executor.transact_raw(from, to, calldata, value)?
347 }
348 }
349
350 let RawCallResult {
351 result, reverted, logs, traces, labels, transactions, exit_reason, ..
352 } = res;
353 let breakpoints = res.cheatcodes.map(|cheats| cheats.breakpoints).unwrap_or_default();
354
355 Ok(ScriptResult {
356 returned: result,
357 success: !reverted,
358 gas_used,
359 logs,
360 traces: traces
361 .map(|traces| {
362 vec![(TraceKind::Execution, traces)]
365 })
366 .unwrap_or_default(),
367 labeled_addresses: labels,
368 transactions,
369 exit_reason,
370 address: None,
371 breakpoints,
372 })
373 }
374
375 fn search_optimal_gas_usage(
382 &mut self,
383 res: &RawCallResult<FEN>,
384 from: Address,
385 to: Address,
386 calldata: &Bytes,
387 value: U256,
388 ) -> Result<u64> {
389 let mut gas_used = res.gas_used;
390 if matches!(res.exit_reason, Some(return_ok!())) {
391 let init_gas_limit = self.executor.tx_env().gas_limit();
393
394 let mut highest_gas_limit = gas_used * 3;
395 let mut lowest_gas_limit = gas_used;
396 let mut last_highest_gas_limit = highest_gas_limit;
397 while (highest_gas_limit - lowest_gas_limit) > 1 {
398 let mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2;
399 self.executor.tx_env_mut().set_gas_limit(mid_gas_limit);
400 let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?;
401 match res.exit_reason {
402 Some(
403 InstructionResult::Revert
404 | InstructionResult::OutOfGas
405 | InstructionResult::OutOfFunds,
406 ) => {
407 lowest_gas_limit = mid_gas_limit;
408 }
409 _ => {
410 highest_gas_limit = mid_gas_limit;
411 const ACCURACY: u64 = 10;
414 if (last_highest_gas_limit - highest_gas_limit) * ACCURACY
415 / last_highest_gas_limit
416 < 1
417 {
418 gas_used = highest_gas_limit;
420 break;
421 }
422 last_highest_gas_limit = highest_gas_limit;
423 }
424 }
425 }
426 self.executor.tx_env_mut().set_gas_limit(init_gas_limit);
428 }
429 Ok(gas_used)
430 }
431}