Skip to main content

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