1use super::{ScriptConfig, ScriptResult};
2use crate::build::ScriptPredeployLibraries;
3use alloy_eips::eip7702::SignedAuthorization;
4use alloy_primitives::{Address, Bytes, TxKind, U256};
5use alloy_rpc_types::TransactionRequest;
6use eyre::Result;
7use foundry_cheatcodes::BroadcastableTransaction;
8use foundry_config::Config;
9use foundry_evm::{
10 constants::CALLER,
11 executors::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult},
12 opts::EvmOpts,
13 revm::interpreter::{return_ok, InstructionResult},
14 traces::{TraceKind, Traces},
15};
16use std::collections::VecDeque;
17
18#[derive(Debug)]
20pub struct ScriptRunner {
21 pub executor: Executor,
22 pub evm_opts: EvmOpts,
23}
24
25impl ScriptRunner {
26 pub fn new(executor: Executor, evm_opts: EvmOpts) -> Self {
27 Self { executor, evm_opts }
28 }
29
30 pub fn setup(
32 &mut self,
33 libraries: &ScriptPredeployLibraries,
34 code: Bytes,
35 setup: bool,
36 script_config: &ScriptConfig,
37 is_broadcast: bool,
38 ) -> Result<(Address, ScriptResult)> {
39 trace!(target: "script", "executing setUP()");
40
41 if !is_broadcast {
42 if self.evm_opts.sender == Config::DEFAULT_SENDER {
43 self.executor.set_balance(self.evm_opts.sender, U256::MAX)?;
45 }
46
47 if script_config.evm_opts.fork_url.is_none() {
48 self.executor.deploy_create2_deployer()?;
49 }
50 }
51
52 let sender_nonce = script_config.sender_nonce;
53 self.executor.set_nonce(self.evm_opts.sender, sender_nonce)?;
54
55 self.executor.set_balance(CALLER, U256::MAX)?;
57
58 let mut library_transactions = VecDeque::new();
59 let mut traces = Traces::default();
60
61 match libraries {
63 ScriptPredeployLibraries::Default(libraries) => libraries.iter().for_each(|code| {
64 let result = self
65 .executor
66 .deploy(self.evm_opts.sender, code.clone(), U256::ZERO, None)
67 .expect("couldn't deploy library")
68 .raw;
69
70 if let Some(deploy_traces) = result.traces {
71 traces.push((TraceKind::Deployment, deploy_traces));
72 }
73
74 library_transactions.push_back(BroadcastableTransaction {
75 rpc: self.evm_opts.fork_url.clone(),
76 transaction: TransactionRequest {
77 from: Some(self.evm_opts.sender),
78 input: code.clone().into(),
79 nonce: Some(sender_nonce + library_transactions.len() as u64),
80 ..Default::default()
81 }
82 .into(),
83 })
84 }),
85 ScriptPredeployLibraries::Create2(libraries, salt) => {
86 let create2_deployer = self.executor.create2_deployer();
87 for library in libraries {
88 let address = create2_deployer.create2_from_code(salt, library.as_ref());
89 if !self.executor.is_empty_code(address)? {
91 continue;
92 }
93 let calldata = [salt.as_ref(), library.as_ref()].concat();
94 let result = self
95 .executor
96 .transact_raw(
97 self.evm_opts.sender,
98 create2_deployer,
99 calldata.clone().into(),
100 U256::from(0),
101 )
102 .expect("couldn't deploy library");
103
104 if let Some(deploy_traces) = result.traces {
105 traces.push((TraceKind::Deployment, deploy_traces));
106 }
107
108 library_transactions.push_back(BroadcastableTransaction {
109 rpc: self.evm_opts.fork_url.clone(),
110 transaction: TransactionRequest {
111 from: Some(self.evm_opts.sender),
112 input: calldata.into(),
113 nonce: Some(sender_nonce + library_transactions.len() as u64),
114 to: Some(TxKind::Call(create2_deployer)),
115 ..Default::default()
116 }
117 .into(),
118 });
119 }
120
121 self.executor.set_nonce(
124 self.evm_opts.sender,
125 sender_nonce + library_transactions.len() as u64,
126 )?;
127 }
128 };
129
130 let address = CALLER.create(self.executor.get_nonce(CALLER)?);
131
132 self.executor.set_balance(address, self.evm_opts.initial_balance)?;
135
136 let prev_sender_nonce = self.executor.get_nonce(self.evm_opts.sender)?;
143 if self.evm_opts.sender == CALLER {
144 self.executor.set_nonce(self.evm_opts.sender, u64::MAX / 2)?;
145 }
146
147 let DeployResult {
149 address,
150 raw: RawCallResult { mut logs, traces: constructor_traces, .. },
151 } = self
152 .executor
153 .deploy(CALLER, code, U256::ZERO, None)
154 .map_err(|err| eyre::eyre!("Failed to deploy script:\n{}", err))?;
155
156 if self.evm_opts.sender == CALLER {
157 self.executor.set_nonce(self.evm_opts.sender, prev_sender_nonce)?;
158 }
159
160 if script_config.config.script_execution_protection {
162 self.executor.set_script_execution(address);
163 }
164
165 traces.extend(constructor_traces.map(|traces| (TraceKind::Deployment, traces)));
166
167 let (success, gas_used, labeled_addresses, transactions) = if !setup {
169 self.executor.backend_mut().set_test_contract(address);
170 (true, 0, Default::default(), Some(library_transactions))
171 } else {
172 match self.executor.setup(Some(self.evm_opts.sender), address, None) {
173 Ok(RawCallResult {
174 reverted,
175 traces: setup_traces,
176 labels,
177 logs: setup_logs,
178 gas_used,
179 transactions: setup_transactions,
180 ..
181 }) => {
182 traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces)));
183 logs.extend_from_slice(&setup_logs);
184
185 if let Some(txs) = setup_transactions {
186 library_transactions.extend(txs);
187 }
188
189 (!reverted, gas_used, labels, Some(library_transactions))
190 }
191 Err(EvmError::Execution(err)) => {
192 let RawCallResult {
193 reverted,
194 traces: setup_traces,
195 labels,
196 logs: setup_logs,
197 gas_used,
198 transactions,
199 ..
200 } = err.raw;
201 traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces)));
202 logs.extend_from_slice(&setup_logs);
203
204 if let Some(txs) = transactions {
205 library_transactions.extend(txs);
206 }
207
208 (!reverted, gas_used, labels, Some(library_transactions))
209 }
210 Err(e) => return Err(e.into()),
211 }
212 };
213
214 Ok((
215 address,
216 ScriptResult {
217 returned: Bytes::new(),
218 success,
219 gas_used,
220 labeled_addresses,
221 transactions,
222 logs,
223 traces,
224 address: None,
225 ..Default::default()
226 },
227 ))
228 }
229
230 pub fn script(&mut self, address: Address, calldata: Bytes) -> Result<ScriptResult> {
232 self.call(self.evm_opts.sender, address, calldata, U256::ZERO, None, false)
233 }
234
235 pub fn simulate(
237 &mut self,
238 from: Address,
239 to: Option<Address>,
240 calldata: Option<Bytes>,
241 value: Option<U256>,
242 authorization_list: Option<Vec<SignedAuthorization>>,
243 ) -> Result<ScriptResult> {
244 if let Some(to) = to {
245 self.call(
246 from,
247 to,
248 calldata.unwrap_or_default(),
249 value.unwrap_or(U256::ZERO),
250 authorization_list,
251 true,
252 )
253 } else if to.is_none() {
254 let res = self.executor.deploy(
255 from,
256 calldata.expect("No data for create transaction"),
257 value.unwrap_or(U256::ZERO),
258 None,
259 );
260 let (address, RawCallResult { gas_used, logs, traces, .. }) = match res {
261 Ok(DeployResult { address, raw }) => (address, raw),
262 Err(EvmError::Execution(err)) => {
263 let ExecutionErr { raw, reason } = *err;
264 sh_err!("Failed with `{reason}`:\n")?;
265 (Address::ZERO, raw)
266 }
267 Err(e) => eyre::bail!("Failed deploying contract: {e:?}"),
268 };
269
270 Ok(ScriptResult {
271 returned: Bytes::new(),
272 success: address != Address::ZERO,
273 gas_used,
274 logs,
275 traces: traces
277 .map(|traces| vec![(TraceKind::Execution, traces)])
278 .unwrap_or_default(),
279 address: Some(address),
280 ..Default::default()
281 })
282 } else {
283 eyre::bail!("ENS not supported.");
284 }
285 }
286
287 fn call(
294 &mut self,
295 from: Address,
296 to: Address,
297 calldata: Bytes,
298 value: U256,
299 authorization_list: Option<Vec<SignedAuthorization>>,
300 commit: bool,
301 ) -> Result<ScriptResult> {
302 let mut res = if let Some(authorization_list) = authorization_list {
303 self.executor.call_raw_with_authorization(
304 from,
305 to,
306 calldata.clone(),
307 value,
308 authorization_list,
309 )?
310 } else {
311 self.executor.call_raw(from, to, calldata.clone(), value)?
312 };
313 let mut gas_used = res.gas_used;
314
315 if commit {
321 gas_used = self.search_optimal_gas_usage(&res, from, to, &calldata, value)?;
322 res = self.executor.transact_raw(from, to, calldata, value)?;
323 }
324
325 let RawCallResult { result, reverted, logs, traces, labels, transactions, .. } = res;
326 let breakpoints = res.cheatcodes.map(|cheats| cheats.breakpoints).unwrap_or_default();
327
328 Ok(ScriptResult {
329 returned: result,
330 success: !reverted,
331 gas_used,
332 logs,
333 traces: traces
334 .map(|traces| {
335 vec![(TraceKind::Execution, traces)]
338 })
339 .unwrap_or_default(),
340 labeled_addresses: labels,
341 transactions,
342 address: None,
343 breakpoints,
344 })
345 }
346
347 fn search_optimal_gas_usage(
354 &mut self,
355 res: &RawCallResult,
356 from: Address,
357 to: Address,
358 calldata: &Bytes,
359 value: U256,
360 ) -> Result<u64> {
361 let mut gas_used = res.gas_used;
362 if matches!(res.exit_reason, return_ok!()) {
363 let init_gas_limit = self.executor.env().tx.gas_limit;
365
366 let mut highest_gas_limit = gas_used * 3;
367 let mut lowest_gas_limit = gas_used;
368 let mut last_highest_gas_limit = highest_gas_limit;
369 while (highest_gas_limit - lowest_gas_limit) > 1 {
370 let mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2;
371 self.executor.env_mut().tx.gas_limit = mid_gas_limit;
372 let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?;
373 match res.exit_reason {
374 InstructionResult::Revert |
375 InstructionResult::OutOfGas |
376 InstructionResult::OutOfFunds => {
377 lowest_gas_limit = mid_gas_limit;
378 }
379 _ => {
380 highest_gas_limit = mid_gas_limit;
381 const ACCURACY: u64 = 10;
384 if (last_highest_gas_limit - highest_gas_limit) * ACCURACY /
385 last_highest_gas_limit <
386 1
387 {
388 gas_used = highest_gas_limit;
390 break;
391 }
392 last_highest_gas_limit = highest_gas_limit;
393 }
394 }
395 }
396 self.executor.env_mut().tx.gas_limit = init_gas_limit;
398 }
399 Ok(gas_used)
400 }
401}