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, map::AddressHashMap};
7use eyre::Result;
8use foundry_cheatcodes::BroadcastableTransaction;
9use foundry_common::{LIBRARY_DEPLOYER, 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 collect_debug_bytecodes: bool,
30}
31
32impl<FEN: FoundryEvmNetwork> ScriptRunner<FEN> {
33 pub const fn new(executor: Executor<FEN>, evm_opts: EvmOpts) -> Self {
34 Self { executor, evm_opts, collect_debug_bytecodes: false }
35 }
36
37 pub const fn with_debug_bytecodes(mut self, collect_debug_bytecodes: bool) -> Self {
38 self.collect_debug_bytecodes = collect_debug_bytecodes;
39 self
40 }
41
42 fn maybe_debug_bytecodes(
43 &self,
44 debug_bytecodes: AddressHashMap<Bytes>,
45 ) -> AddressHashMap<Bytes> {
46 if self.collect_debug_bytecodes { debug_bytecodes } else { Default::default() }
47 }
48
49 fn extend_debug_bytecodes(
50 &self,
51 target: &mut AddressHashMap<Bytes>,
52 debug_bytecodes: AddressHashMap<Bytes>,
53 ) {
54 if self.collect_debug_bytecodes {
55 target.extend(debug_bytecodes);
56 }
57 }
58
59 fn deploy_local_libraries(
60 &mut self,
61 libraries: &[foundry_linking::LinkedLibrary],
62 debug_bytecodes: &mut AddressHashMap<Bytes>,
63 ) -> Result<()> {
64 if libraries.is_empty() {
65 return Ok(());
66 }
67 let balance = self.executor.get_balance(LIBRARY_DEPLOYER)?;
68 let nonce = self.executor.get_nonce(LIBRARY_DEPLOYER)?;
69 self.executor.set_balance(LIBRARY_DEPLOYER, U256::MAX)?;
70 self.executor.set_nonce(LIBRARY_DEPLOYER, 0)?;
71 for library in libraries {
72 let DeployResult { address, raw } = self
73 .executor
74 .deploy(LIBRARY_DEPLOYER, library.bytecode.clone(), U256::ZERO, None)
75 .map_err(|err| eyre::eyre!("couldn't deploy local library: {err}"))?;
76 eyre::ensure!(
77 library.address == address,
78 "local library deployed at an unexpected address"
79 );
80 self.extend_debug_bytecodes(debug_bytecodes, raw.debug_bytecodes);
81 }
82 self.executor.set_balance(LIBRARY_DEPLOYER, balance)?;
83 self.executor.set_nonce(LIBRARY_DEPLOYER, nonce)?;
84 Ok(())
85 }
86
87 pub fn setup(
89 &mut self,
90 libraries: &ScriptPredeployLibraries,
91 code: Bytes,
92 setup: bool,
93 script_config: &ScriptConfig<FEN>,
94 is_broadcast: bool,
95 ) -> Result<(Address, ScriptResult<FEN::Network>)> {
96 trace!(target: "script", "executing setUP()");
97
98 if !is_broadcast {
99 if self.evm_opts.sender == Config::DEFAULT_SENDER {
100 self.executor.set_balance(self.evm_opts.sender, U256::MAX)?;
102 }
103
104 if script_config.evm_opts.fork_url.is_none()
105 && !script_config.evm_opts.networks.is_tempo()
106 {
107 self.executor.deploy_create2_deployer()?;
108 }
109 }
110
111 let sender_nonce = script_config.sender_nonce;
112 self.executor.set_nonce(self.evm_opts.sender, sender_nonce)?;
113
114 self.executor.set_balance(CALLER, U256::MAX)?;
116
117 let mut library_transactions = VecDeque::new();
118 let mut traces = Traces::default();
119 let mut debug_bytecodes: AddressHashMap<Bytes> = Default::default();
120
121 match libraries {
123 ScriptPredeployLibraries::Default { onchain, local } => {
124 self.deploy_local_libraries(local, &mut debug_bytecodes)?;
125 for library in onchain {
126 let code = &library.bytecode;
127 let RawCallResult {
128 traces: deploy_traces,
129 debug_bytecodes: deploy_debug_bytecodes,
130 ..
131 } = self
132 .executor
133 .deploy(self.evm_opts.sender, code.clone(), U256::ZERO, None)
134 .map_err(|err| eyre::eyre!("couldn't deploy library: {err}"))?
135 .raw;
136
137 self.extend_debug_bytecodes(&mut debug_bytecodes, deploy_debug_bytecodes);
138
139 if let Some(deploy_traces) = deploy_traces {
140 traces.push((TraceKind::Deployment, deploy_traces));
141 }
142
143 let mut tx_req = TransactionRequestFor::<FEN>::default()
144 .with_from(self.evm_opts.sender)
145 .with_input(code.clone())
146 .with_nonce(sender_nonce + library_transactions.len() as u64);
147
148 script_config.tempo.apply::<FEN::Network>(&mut tx_req, None);
149
150 library_transactions.push_back(BroadcastableTransaction {
151 rpc: self.evm_opts.fork_url.clone(),
152 transaction: TransactionMaybeSigned::new(tx_req),
153 })
154 }
155 }
156 ScriptPredeployLibraries::Create2 { onchain, salt, local } => {
157 self.deploy_local_libraries(local, &mut debug_bytecodes)?;
158 let create2_deployer = self.executor.create2_deployer();
159 for library in onchain {
160 let address =
161 create2_deployer.create2_from_code(salt, library.bytecode.as_ref());
162 if !self.executor.is_empty_code(address)? {
164 continue;
165 }
166 let calldata = [salt.as_ref(), library.bytecode.as_ref()].concat();
167 let RawCallResult {
168 traces: deploy_traces,
169 debug_bytecodes: deploy_debug_bytecodes,
170 ..
171 } = self
172 .executor
173 .transact_raw(
174 self.evm_opts.sender,
175 create2_deployer,
176 calldata.clone().into(),
177 U256::ZERO,
178 )
179 .map_err(|err| eyre::eyre!("couldn't deploy library: {err}"))?;
180
181 self.extend_debug_bytecodes(&mut debug_bytecodes, deploy_debug_bytecodes);
182
183 if let Some(deploy_traces) = deploy_traces {
184 traces.push((TraceKind::Deployment, deploy_traces));
185 }
186
187 let mut tx_req = TransactionRequestFor::<FEN>::default()
188 .with_from(self.evm_opts.sender)
189 .with_input(calldata)
190 .with_nonce(sender_nonce + library_transactions.len() as u64)
191 .with_to(create2_deployer);
192
193 script_config.tempo.apply::<FEN::Network>(&mut tx_req, None);
194
195 library_transactions.push_back(BroadcastableTransaction {
196 rpc: self.evm_opts.fork_url.clone(),
197 transaction: TransactionMaybeSigned::new(tx_req),
198 });
199 }
200
201 self.executor.set_nonce(
204 self.evm_opts.sender,
205 sender_nonce + library_transactions.len() as u64,
206 )?;
207 }
208 };
209
210 let address = CALLER.create(self.executor.get_nonce(CALLER)?);
211
212 self.executor.set_balance(address, self.evm_opts.initial_balance)?;
215
216 let prev_sender_nonce = self.executor.get_nonce(self.evm_opts.sender)?;
223 if self.evm_opts.sender == CALLER {
224 self.executor.set_nonce(self.evm_opts.sender, u64::MAX / 2)?;
225 }
226
227 let DeployResult {
229 address,
230 raw:
231 RawCallResult {
232 mut logs,
233 traces: constructor_traces,
234 debug_bytecodes: constructor_debug_bytecodes,
235 ..
236 },
237 } = self
238 .executor
239 .deploy(CALLER, code, U256::ZERO, None)
240 .map_err(|err| eyre::eyre!("Failed to deploy script:\n{}", err))?;
241
242 if self.evm_opts.sender == CALLER {
243 self.executor.set_nonce(self.evm_opts.sender, prev_sender_nonce)?;
244 }
245
246 if script_config.config.script_execution_protection {
248 self.executor.set_script_execution(address);
249 }
250
251 traces.extend(constructor_traces.map(|traces| (TraceKind::Deployment, traces)));
252 self.extend_debug_bytecodes(&mut debug_bytecodes, constructor_debug_bytecodes);
253
254 let (success, gas_used, labeled_addresses, transactions) = if setup {
256 let result = match self.executor.setup(Some(self.evm_opts.sender), address, None) {
257 Ok(result) => result,
258 Err(EvmError::Execution(err)) => err.raw,
259 Err(err) => return Err(err.into()),
260 };
261 let RawCallResult {
262 reverted,
263 traces: setup_traces,
264 labels,
265 logs: setup_logs,
266 gas_used,
267 debug_bytecodes: setup_debug_bytecodes,
268 transactions: setup_transactions,
269 ..
270 } = result;
271 traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces)));
272 logs.extend_from_slice(&setup_logs);
273 self.extend_debug_bytecodes(&mut debug_bytecodes, setup_debug_bytecodes);
274 if let Some(txs) = setup_transactions {
275 library_transactions.extend(txs);
276 }
277 (!reverted, gas_used, labels, Some(library_transactions))
278 } else {
279 self.executor.backend_mut().set_test_contract(address);
280 (true, 0, Default::default(), Some(library_transactions))
281 };
282
283 Ok((
284 address,
285 ScriptResult {
286 returned: Bytes::new(),
287 success,
288 gas_used,
289 labeled_addresses,
290 debug_bytecodes: self.maybe_debug_bytecodes(debug_bytecodes),
291 transactions,
292 logs,
293 traces,
294 address: None,
295 ..Default::default()
296 },
297 ))
298 }
299
300 pub fn script(
302 &mut self,
303 address: Address,
304 calldata: Bytes,
305 ) -> Result<ScriptResult<FEN::Network>> {
306 self.call(self.evm_opts.sender, address, calldata, U256::ZERO, None, false)
307 }
308
309 pub fn simulate(
311 &mut self,
312 from: Address,
313 to: Option<Address>,
314 calldata: Option<Bytes>,
315 value: Option<U256>,
316 authorization_list: Option<Vec<SignedAuthorization>>,
317 ) -> Result<ScriptResult<FEN::Network>> {
318 if let Some(to) = to {
319 self.call(
320 from,
321 to,
322 calldata.unwrap_or_default(),
323 value.unwrap_or(U256::ZERO),
324 authorization_list,
325 true,
326 )
327 } else {
328 let res = self.executor.deploy(
329 from,
330 calldata.expect("No data for create transaction"),
331 value.unwrap_or(U256::ZERO),
332 None,
333 );
334 self.deployment_result(res)
335 }
336 }
337
338 pub(crate) fn deployment_result(
339 &self,
340 res: Result<DeployResult<FEN>, EvmError<FEN>>,
341 ) -> Result<ScriptResult<FEN::Network>> {
342 let (address, RawCallResult { gas_used, logs, traces, debug_bytecodes, exit_reason, .. }) =
343 match res {
344 Ok(DeployResult { address, raw }) => (address, raw),
345 Err(EvmError::Execution(err)) => {
346 let ExecutionErr { raw, reason } = *err;
347 sh_err!("Failed with `{reason}`:\n")?;
348 (Address::ZERO, raw)
349 }
350 Err(e) => {
351 eyre::bail!("Failed deploying contract: {e:?}");
352 }
353 };
354
355 Ok(ScriptResult {
356 returned: Bytes::new(),
357 success: !address.is_zero(),
358 gas_used,
359 logs,
360 debug_bytecodes: self.maybe_debug_bytecodes(debug_bytecodes),
361 traces: traces.map(|traces| vec![(TraceKind::Execution, traces)]).unwrap_or_default(),
363 exit_reason,
364 address: Some(address),
365 ..Default::default()
366 })
367 }
368
369 fn call(
376 &mut self,
377 from: Address,
378 to: Address,
379 calldata: Bytes,
380 value: U256,
381 authorization_list: Option<Vec<SignedAuthorization>>,
382 commit: bool,
383 ) -> Result<ScriptResult<FEN::Network>> {
384 let mut res = if let Some(authorization_list) = &authorization_list {
385 self.executor.call_raw_with_authorization(
386 from,
387 to,
388 calldata.clone(),
389 value,
390 authorization_list.clone(),
391 )?
392 } else {
393 self.executor.call_raw(from, to, calldata.clone(), value)?
394 };
395 let mut gas_used = res.gas_used;
396
397 if commit {
403 gas_used = self.search_optimal_gas_usage(&res, from, to, &calldata, value)?;
404 res = if let Some(authorization_list) = authorization_list {
405 self.executor.transact_raw_with_authorization(
406 from,
407 to,
408 calldata,
409 value,
410 authorization_list,
411 )?
412 } else {
413 self.executor.transact_raw(from, to, calldata, value)?
414 }
415 }
416
417 Ok(self.call_result(res, gas_used))
418 }
419
420 pub(crate) fn call_result(
421 &self,
422 res: RawCallResult<FEN>,
423 gas_used: u64,
424 ) -> ScriptResult<FEN::Network> {
425 let RawCallResult {
426 result,
427 reverted,
428 logs,
429 traces,
430 labels,
431 transactions,
432 debug_bytecodes,
433 exit_reason,
434 cheatcodes,
435 ..
436 } = res;
437 let breakpoints = cheatcodes.map(|cheats| cheats.breakpoints).unwrap_or_default();
438
439 ScriptResult {
440 returned: result,
441 success: !reverted,
442 gas_used,
443 logs,
444 debug_bytecodes: self.maybe_debug_bytecodes(debug_bytecodes),
445 traces: traces
446 .map(|traces| {
447 vec![(TraceKind::Execution, traces)]
450 })
451 .unwrap_or_default(),
452 labeled_addresses: labels,
453 transactions,
454 exit_reason,
455 address: None,
456 breakpoints,
457 }
458 }
459
460 fn search_optimal_gas_usage(
467 &mut self,
468 res: &RawCallResult<FEN>,
469 from: Address,
470 to: Address,
471 calldata: &Bytes,
472 value: U256,
473 ) -> Result<u64> {
474 let mut gas_used = res.gas_used;
475 if matches!(res.exit_reason, Some(return_ok!())) {
476 let init_gas_limit = self.executor.tx_env().gas_limit();
478
479 let mut search = GasSearch::new(gas_used);
480 while let Some(limit) = search.next_limit() {
481 self.executor.tx_env_mut().set_gas_limit(limit);
482 let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?;
483 search.record(limit, needs_more_gas(res.exit_reason));
484 }
485 gas_used = search.gas_used();
486 self.executor.tx_env_mut().set_gas_limit(init_gas_limit);
488 }
489 Ok(gas_used)
490 }
491}
492
493pub(crate) struct GasSearch {
495 gas_used: u64,
496 highest: u64,
497 lowest: u64,
498 last_highest: u64,
499 done: bool,
500}
501
502impl GasSearch {
503 pub(crate) const fn new(gas_used: u64) -> Self {
504 Self {
505 gas_used,
506 highest: gas_used * 3,
507 lowest: gas_used,
508 last_highest: gas_used * 3,
509 done: false,
510 }
511 }
512
513 pub(crate) const fn next_limit(&self) -> Option<u64> {
514 if !self.done && self.highest - self.lowest > 1 {
515 Some((self.highest + self.lowest) / 2)
516 } else {
517 None
518 }
519 }
520
521 pub(crate) const fn record(&mut self, limit: u64, needs_more_gas: bool) {
524 if needs_more_gas {
525 self.lowest = limit;
526 } else {
527 self.highest = limit;
528 if (self.last_highest - self.highest) * 10 / self.last_highest < 1 {
530 self.gas_used = self.highest;
531 self.done = true;
532 } else {
533 self.last_highest = self.highest;
534 }
535 }
536 }
537
538 pub(crate) const fn gas_used(&self) -> u64 {
539 self.gas_used
540 }
541}
542
543pub(crate) const fn needs_more_gas(exit_reason: Option<InstructionResult>) -> bool {
545 matches!(
546 exit_reason,
547 Some(
548 InstructionResult::Revert | InstructionResult::OutOfGas | InstructionResult::OutOfFunds
549 )
550 )
551}
552
553#[cfg(test)]
554mod gas_search_tests {
555 use super::*;
556
557 #[test]
558 fn successful_probes_keep_existing_ten_percent_stop() {
559 let mut search = GasSearch::new(100);
560 for expected in [200, 150, 125, 112, 106] {
561 assert_eq!(search.next_limit(), Some(expected));
562 search.record(expected, false);
563 }
564 assert_eq!(search.next_limit(), None);
565 assert_eq!(search.gas_used(), 106);
566 }
567
568 #[test]
569 fn unsuccessful_probes_keep_original_estimate() {
570 let mut search = GasSearch::new(100);
571 while let Some(limit) = search.next_limit() {
572 search.record(limit, true);
573 }
574 assert_eq!(search.gas_used(), 100);
575 assert_eq!(GasSearch::new(0).next_limit(), None);
576 }
577
578 #[test]
579 fn only_revert_and_running_out_mean_too_little_gas() {
580 for reason in
581 [InstructionResult::Revert, InstructionResult::OutOfGas, InstructionResult::OutOfFunds]
582 {
583 assert!(needs_more_gas(Some(reason)));
584 }
585 for reason in [
586 Some(InstructionResult::Return),
587 Some(InstructionResult::Stop),
588 Some(InstructionResult::InvalidFEOpcode),
589 None,
590 ] {
591 assert!(!needs_more_gas(reason));
592 }
593 }
594}