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::{FoundryTransactionBuilder, 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 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 self.executor.deploy_create2_deployer()?;
55 }
56 }
57
58 let sender_nonce = script_config.sender_nonce;
59 self.executor.set_nonce(self.evm_opts.sender, sender_nonce)?;
60
61 self.executor.set_balance(CALLER, U256::MAX)?;
63
64 let mut library_transactions = VecDeque::new();
65 let mut traces = Traces::default();
66
67 match libraries {
69 ScriptPredeployLibraries::Default(libraries) => libraries.iter().for_each(|code| {
70 let result = self
71 .executor
72 .deploy(self.evm_opts.sender, code.clone(), U256::ZERO, None)
73 .expect("couldn't deploy library")
74 .raw;
75
76 if let Some(deploy_traces) = result.traces {
77 traces.push((TraceKind::Deployment, deploy_traces));
78 }
79
80 let mut tx_req = TransactionRequestFor::<FEN>::default()
81 .with_from(self.evm_opts.sender)
82 .with_input(code.clone())
83 .with_nonce(sender_nonce + library_transactions.len() as u64);
84
85 if let Some(fee_token) = script_config.fee_token {
86 tx_req.set_fee_token(fee_token);
87 }
88
89 library_transactions.push_back(BroadcastableTransaction {
90 rpc: self.evm_opts.fork_url.clone(),
91 transaction: TransactionMaybeSigned::new(tx_req),
92 })
93 }),
94 ScriptPredeployLibraries::Create2(libraries, salt) => {
95 let create2_deployer = self.executor.create2_deployer();
96 for library in libraries {
97 let address = create2_deployer.create2_from_code(salt, library.as_ref());
98 if !self.executor.is_empty_code(address)? {
100 continue;
101 }
102 let calldata = [salt.as_ref(), library.as_ref()].concat();
103 let result = self
104 .executor
105 .transact_raw(
106 self.evm_opts.sender,
107 create2_deployer,
108 calldata.clone().into(),
109 U256::from(0),
110 )
111 .expect("couldn't deploy library");
112
113 if let Some(deploy_traces) = result.traces {
114 traces.push((TraceKind::Deployment, deploy_traces));
115 }
116
117 let mut tx_req = TransactionRequestFor::<FEN>::default()
118 .with_from(self.evm_opts.sender)
119 .with_input(calldata)
120 .with_nonce(sender_nonce + library_transactions.len() as u64)
121 .with_to(create2_deployer);
122
123 if let Some(fee_token) = script_config.fee_token {
124 tx_req.set_fee_token(fee_token);
125 }
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, .. }) = 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 address: Some(address),
296 ..Default::default()
297 })
298 }
299 }
300
301 fn call(
308 &mut self,
309 from: Address,
310 to: Address,
311 calldata: Bytes,
312 value: U256,
313 authorization_list: Option<Vec<SignedAuthorization>>,
314 commit: bool,
315 ) -> Result<ScriptResult<FEN::Network>> {
316 let mut res = if let Some(authorization_list) = &authorization_list {
317 self.executor.call_raw_with_authorization(
318 from,
319 to,
320 calldata.clone(),
321 value,
322 authorization_list.clone(),
323 )?
324 } else {
325 self.executor.call_raw(from, to, calldata.clone(), value)?
326 };
327 let mut gas_used = res.gas_used;
328
329 if commit {
335 gas_used = self.search_optimal_gas_usage(&res, from, to, &calldata, value)?;
336 res = if let Some(authorization_list) = authorization_list {
337 self.executor.transact_raw_with_authorization(
338 from,
339 to,
340 calldata,
341 value,
342 authorization_list,
343 )?
344 } else {
345 self.executor.transact_raw(from, to, calldata, value)?
346 }
347 }
348
349 let RawCallResult { result, reverted, logs, traces, labels, transactions, .. } = res;
350 let breakpoints = res.cheatcodes.map(|cheats| cheats.breakpoints).unwrap_or_default();
351
352 Ok(ScriptResult {
353 returned: result,
354 success: !reverted,
355 gas_used,
356 logs,
357 traces: traces
358 .map(|traces| {
359 vec![(TraceKind::Execution, traces)]
362 })
363 .unwrap_or_default(),
364 labeled_addresses: labels,
365 transactions,
366 address: None,
367 breakpoints,
368 })
369 }
370
371 fn search_optimal_gas_usage(
378 &mut self,
379 res: &RawCallResult<FEN>,
380 from: Address,
381 to: Address,
382 calldata: &Bytes,
383 value: U256,
384 ) -> Result<u64> {
385 let mut gas_used = res.gas_used;
386 if matches!(res.exit_reason, Some(return_ok!())) {
387 let init_gas_limit = self.executor.tx_env().gas_limit();
389
390 let mut highest_gas_limit = gas_used * 3;
391 let mut lowest_gas_limit = gas_used;
392 let mut last_highest_gas_limit = highest_gas_limit;
393 while (highest_gas_limit - lowest_gas_limit) > 1 {
394 let mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2;
395 self.executor.tx_env_mut().set_gas_limit(mid_gas_limit);
396 let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?;
397 match res.exit_reason {
398 Some(
399 InstructionResult::Revert
400 | InstructionResult::OutOfGas
401 | InstructionResult::OutOfFunds,
402 ) => {
403 lowest_gas_limit = mid_gas_limit;
404 }
405 _ => {
406 highest_gas_limit = mid_gas_limit;
407 const ACCURACY: u64 = 10;
410 if (last_highest_gas_limit - highest_gas_limit) * ACCURACY
411 / last_highest_gas_limit
412 < 1
413 {
414 gas_used = highest_gas_limit;
416 break;
417 }
418 last_highest_gas_limit = highest_gas_limit;
419 }
420 }
421 }
422 self.executor.tx_env_mut().set_gas_limit(init_gas_limit);
424 }
425 Ok(gas_used)
426 }
427}