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::from(0),
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 match self.executor.setup(Some(self.evm_opts.sender), address, None) {
257 Ok(RawCallResult {
258 reverted,
259 traces: setup_traces,
260 labels,
261 logs: setup_logs,
262 gas_used,
263 debug_bytecodes: setup_debug_bytecodes,
264 transactions: setup_transactions,
265 ..
266 }) => {
267 traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces)));
268 logs.extend_from_slice(&setup_logs);
269 self.extend_debug_bytecodes(&mut debug_bytecodes, setup_debug_bytecodes);
270
271 if let Some(txs) = setup_transactions {
272 library_transactions.extend(txs);
273 }
274
275 (!reverted, gas_used, labels, Some(library_transactions))
276 }
277 Err(EvmError::Execution(err)) => {
278 let RawCallResult {
279 reverted,
280 traces: setup_traces,
281 labels,
282 logs: setup_logs,
283 gas_used,
284 debug_bytecodes: setup_debug_bytecodes,
285 transactions,
286 ..
287 } = err.raw;
288 traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces)));
289 logs.extend_from_slice(&setup_logs);
290 self.extend_debug_bytecodes(&mut debug_bytecodes, setup_debug_bytecodes);
291
292 if let Some(txs) = transactions {
293 library_transactions.extend(txs);
294 }
295
296 (!reverted, gas_used, labels, Some(library_transactions))
297 }
298 Err(e) => return Err(e.into()),
299 }
300 } else {
301 self.executor.backend_mut().set_test_contract(address);
302 (true, 0, Default::default(), Some(library_transactions))
303 };
304
305 Ok((
306 address,
307 ScriptResult {
308 returned: Bytes::new(),
309 success,
310 gas_used,
311 labeled_addresses,
312 debug_bytecodes: self.maybe_debug_bytecodes(debug_bytecodes),
313 transactions,
314 logs,
315 traces,
316 address: None,
317 ..Default::default()
318 },
319 ))
320 }
321
322 pub fn script(
324 &mut self,
325 address: Address,
326 calldata: Bytes,
327 ) -> Result<ScriptResult<FEN::Network>> {
328 self.call(self.evm_opts.sender, address, calldata, U256::ZERO, None, false)
329 }
330
331 pub fn simulate(
333 &mut self,
334 from: Address,
335 to: Option<Address>,
336 calldata: Option<Bytes>,
337 value: Option<U256>,
338 authorization_list: Option<Vec<SignedAuthorization>>,
339 ) -> Result<ScriptResult<FEN::Network>> {
340 if let Some(to) = to {
341 self.call(
342 from,
343 to,
344 calldata.unwrap_or_default(),
345 value.unwrap_or(U256::ZERO),
346 authorization_list,
347 true,
348 )
349 } else {
350 let res = self.executor.deploy(
351 from,
352 calldata.expect("No data for create transaction"),
353 value.unwrap_or(U256::ZERO),
354 None,
355 );
356 self.deployment_result(res)
357 }
358 }
359
360 pub(crate) fn deployment_result(
361 &self,
362 res: Result<DeployResult<FEN>, EvmError<FEN>>,
363 ) -> Result<ScriptResult<FEN::Network>> {
364 let (address, RawCallResult { gas_used, logs, traces, debug_bytecodes, exit_reason, .. }) =
365 match res {
366 Ok(DeployResult { address, raw }) => (address, raw),
367 Err(EvmError::Execution(err)) => {
368 let ExecutionErr { raw, reason } = *err;
369 sh_err!("Failed with `{reason}`:\n")?;
370 (Address::ZERO, raw)
371 }
372 Err(e) => {
373 eyre::bail!("Failed deploying contract: {e:?}");
374 }
375 };
376
377 Ok(ScriptResult {
378 returned: Bytes::new(),
379 success: address != Address::ZERO,
380 gas_used,
381 logs,
382 debug_bytecodes: self.maybe_debug_bytecodes(debug_bytecodes),
383 traces: traces.map(|traces| vec![(TraceKind::Execution, traces)]).unwrap_or_default(),
385 exit_reason,
386 address: Some(address),
387 ..Default::default()
388 })
389 }
390
391 fn call(
398 &mut self,
399 from: Address,
400 to: Address,
401 calldata: Bytes,
402 value: U256,
403 authorization_list: Option<Vec<SignedAuthorization>>,
404 commit: bool,
405 ) -> Result<ScriptResult<FEN::Network>> {
406 let mut res = if let Some(authorization_list) = &authorization_list {
407 self.executor.call_raw_with_authorization(
408 from,
409 to,
410 calldata.clone(),
411 value,
412 authorization_list.clone(),
413 )?
414 } else {
415 self.executor.call_raw(from, to, calldata.clone(), value)?
416 };
417 let mut gas_used = res.gas_used;
418
419 if commit {
425 gas_used = self.search_optimal_gas_usage(&res, from, to, &calldata, value)?;
426 res = if let Some(authorization_list) = authorization_list {
427 self.executor.transact_raw_with_authorization(
428 from,
429 to,
430 calldata,
431 value,
432 authorization_list,
433 )?
434 } else {
435 self.executor.transact_raw(from, to, calldata, value)?
436 }
437 }
438
439 Ok(self.call_result(res, gas_used))
440 }
441
442 pub(crate) fn call_result(
443 &self,
444 res: RawCallResult<FEN>,
445 gas_used: u64,
446 ) -> ScriptResult<FEN::Network> {
447 let RawCallResult {
448 result,
449 reverted,
450 logs,
451 traces,
452 labels,
453 transactions,
454 debug_bytecodes,
455 exit_reason,
456 cheatcodes,
457 ..
458 } = res;
459 let breakpoints = cheatcodes.map(|cheats| cheats.breakpoints).unwrap_or_default();
460
461 ScriptResult {
462 returned: result,
463 success: !reverted,
464 gas_used,
465 logs,
466 debug_bytecodes: self.maybe_debug_bytecodes(debug_bytecodes),
467 traces: traces
468 .map(|traces| {
469 vec![(TraceKind::Execution, traces)]
472 })
473 .unwrap_or_default(),
474 labeled_addresses: labels,
475 transactions,
476 exit_reason,
477 address: None,
478 breakpoints,
479 }
480 }
481
482 fn search_optimal_gas_usage(
489 &mut self,
490 res: &RawCallResult<FEN>,
491 from: Address,
492 to: Address,
493 calldata: &Bytes,
494 value: U256,
495 ) -> Result<u64> {
496 let mut gas_used = res.gas_used;
497 if matches!(res.exit_reason, Some(return_ok!())) {
498 let init_gas_limit = self.executor.tx_env().gas_limit();
500
501 let mut search = GasSearch::new(gas_used);
502 while let Some(limit) = search.next_limit() {
503 self.executor.tx_env_mut().set_gas_limit(limit);
504 let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?;
505 search.record(limit, res.exit_reason);
506 }
507 gas_used = search.gas_used();
508 self.executor.tx_env_mut().set_gas_limit(init_gas_limit);
510 }
511 Ok(gas_used)
512 }
513}
514
515pub(crate) struct GasSearch {
517 gas_used: u64,
518 highest: u64,
519 lowest: u64,
520 last_highest: u64,
521 done: bool,
522}
523
524impl GasSearch {
525 pub(crate) const fn new(gas_used: u64) -> Self {
526 Self {
527 gas_used,
528 highest: gas_used * 3,
529 lowest: gas_used,
530 last_highest: gas_used * 3,
531 done: false,
532 }
533 }
534
535 pub(crate) const fn next_limit(&self) -> Option<u64> {
536 if !self.done && self.highest - self.lowest > 1 {
537 Some((self.highest + self.lowest) / 2)
538 } else {
539 None
540 }
541 }
542
543 pub(crate) const fn record(&mut self, limit: u64, exit_reason: Option<InstructionResult>) {
544 match exit_reason {
545 Some(
546 InstructionResult::Revert
547 | InstructionResult::OutOfGas
548 | InstructionResult::OutOfFunds,
549 ) => {
550 self.lowest = limit;
551 }
552 _ => {
553 self.highest = limit;
554 if (self.last_highest - self.highest) * 10 / self.last_highest < 1 {
556 self.gas_used = self.highest;
557 self.done = true;
558 } else {
559 self.last_highest = self.highest;
560 }
561 }
562 }
563 }
564
565 pub(crate) const fn gas_used(&self) -> u64 {
566 self.gas_used
567 }
568}
569
570#[cfg(test)]
571mod gas_search_tests {
572 use super::*;
573
574 #[test]
575 fn successful_probes_keep_existing_ten_percent_stop() {
576 let mut search = GasSearch::new(100);
577 for expected in [200, 150, 125, 112, 106] {
578 assert_eq!(search.next_limit(), Some(expected));
579 search.record(expected, Some(InstructionResult::Return));
580 }
581 assert_eq!(search.next_limit(), None);
582 assert_eq!(search.gas_used(), 106);
583 }
584
585 #[test]
586 fn unsuccessful_probes_keep_original_estimate() {
587 let mut search = GasSearch::new(100);
588 while let Some(limit) = search.next_limit() {
589 search.record(limit, Some(InstructionResult::OutOfGas));
590 }
591 assert_eq!(search.gas_used(), 100);
592 assert_eq!(GasSearch::new(0).next_limit(), None);
593 }
594}