chisel/
runner.rs

1//! ChiselRunner
2//!
3//! This module contains the `ChiselRunner` struct, which assists with deploying
4//! and calling the REPL contract on a in-memory REVM instance.
5
6use alloy_primitives::{Address, Bytes, Log, U256, map::AddressHashMap};
7use eyre::Result;
8use foundry_evm::{
9    executors::{DeployResult, Executor, RawCallResult},
10    traces::{TraceKind, Traces},
11};
12
13/// The function selector of the REPL contract's entrypoint, the `run()` function.
14static RUN_SELECTOR: [u8; 4] = [0xc0, 0x40, 0x62, 0x26];
15
16/// The Chisel Runner
17///
18/// Based off of foundry's forge cli runner for scripting.
19/// See: [runner](cli::cmd::forge::script::runner.rs)
20#[derive(Debug)]
21pub struct ChiselRunner {
22    /// The Executor
23    pub executor: Executor,
24    /// An initial balance
25    pub initial_balance: U256,
26    /// The sender
27    pub sender: Address,
28    /// Input calldata appended to `RUN_SELECTOR`
29    pub input: Option<Vec<u8>>,
30}
31
32/// Represents the result of a Chisel REPL run
33#[derive(Debug, Default)]
34pub struct ChiselResult {
35    /// Was the run a success?
36    pub success: bool,
37    /// Transaction logs
38    pub logs: Vec<Log>,
39    /// Call traces
40    pub traces: Traces,
41    /// Amount of gas used in the transaction
42    pub gas_used: u64,
43    /// Map of addresses to their labels
44    pub labeled_addresses: AddressHashMap<String>,
45    /// Return data
46    pub returned: Bytes,
47    /// Called address
48    pub address: Address,
49    /// EVM State at the final instruction of the `run()` function
50    pub state: Option<(Vec<U256>, Vec<u8>)>,
51}
52
53/// ChiselRunner implementation
54impl ChiselRunner {
55    /// Create a new [ChiselRunner]
56    ///
57    /// ### Takes
58    ///
59    /// An [Executor], the initial balance of the sender, and the sender's [Address].
60    ///
61    /// ### Returns
62    ///
63    /// A new [ChiselRunner]
64    pub fn new(
65        executor: Executor,
66        initial_balance: U256,
67        sender: Address,
68        input: Option<Vec<u8>>,
69    ) -> Self {
70        Self { executor, initial_balance, sender, input }
71    }
72
73    /// Run a contract as a REPL session
74    pub fn run(&mut self, bytecode: Bytes) -> Result<ChiselResult> {
75        // Set the sender's balance to [U256::MAX] for deployment of the REPL contract.
76        self.executor.set_balance(self.sender, U256::MAX)?;
77
78        // Deploy an instance of the REPL contract
79        // We don't care about deployment traces / logs here
80        let DeployResult { address, .. } = self
81            .executor
82            .deploy(self.sender, bytecode, U256::ZERO, None)
83            .map_err(|err| eyre::eyre!("Failed to deploy REPL contract:\n{}", err))?;
84
85        // Reset the sender's balance to the initial balance for calls.
86        self.executor.set_balance(self.sender, self.initial_balance)?;
87
88        // Append the input to the `RUN_SELECTOR` to form the calldata
89        let mut calldata = RUN_SELECTOR.to_vec();
90        if let Some(mut input) = self.input.clone() {
91            calldata.append(&mut input);
92        }
93
94        let res = self.executor.transact_raw(self.sender, address, calldata.into(), U256::ZERO)?;
95
96        let RawCallResult {
97            result, reverted, logs, traces, labels, chisel_state, gas_used, ..
98        } = res;
99
100        Ok(ChiselResult {
101            returned: result,
102            success: !reverted,
103            gas_used,
104            logs,
105            traces: traces.map(|traces| vec![(TraceKind::Execution, traces)]).unwrap_or_default(),
106            labeled_addresses: labels,
107            address,
108            state: chisel_state,
109        })
110    }
111}