Skip to main content

foundry_evm/executors/
strategy.rs

1use std::{any::Any, fmt::Debug};
2
3use alloy_primitives::{Address, U256};
4use eyre::Result;
5use foundry_cheatcodes::CheatcodesStrategy;
6use foundry_compilers::{
7    ProjectCompileOutput, compilers::resolc::dual_compiled_contracts::DualCompiledContracts,
8};
9use foundry_evm_core::{
10    Env,
11    backend::{Backend, BackendResult, BackendStrategy, CowBackend},
12};
13use revm::{DatabaseRef, context::result::ResultAndState};
14
15use crate::inspectors::InspectorStack;
16
17use super::Executor;
18
19/// Context for [ExecutorStrategy].
20pub trait ExecutorStrategyContext: Debug + Send + Sync + Any {
21    /// Clone the strategy context.
22    fn new_cloned(&self) -> Box<dyn ExecutorStrategyContext>;
23    /// Alias as immutable reference of [Any].
24    fn as_any_ref(&self) -> &dyn Any;
25    /// Alias as mutable reference of [Any].
26    fn as_any_mut(&mut self) -> &mut dyn Any;
27}
28
29impl Clone for Box<dyn ExecutorStrategyContext> {
30    fn clone(&self) -> Self {
31        self.new_cloned()
32    }
33}
34
35/// Default strategy context object.
36impl ExecutorStrategyContext for () {
37    fn new_cloned(&self) -> Box<dyn ExecutorStrategyContext> {
38        Box::new(())
39    }
40
41    fn as_any_ref(&self) -> &dyn Any {
42        self
43    }
44
45    fn as_any_mut(&mut self) -> &mut dyn Any {
46        self
47    }
48}
49
50/// Stateless strategy runner for [ExecutorStrategy].
51pub trait ExecutorStrategyRunner: Debug + Send + Sync + ExecutorStrategyExt {
52    /// Creates a new [BackendStrategy].
53    fn new_backend_strategy(&self, ctx: &dyn ExecutorStrategyContext) -> BackendStrategy;
54
55    /// Creates a new [CheatcodesStrategy].
56    fn new_cheatcodes_strategy(&self, ctx: &dyn ExecutorStrategyContext) -> CheatcodesStrategy;
57
58    /// Set the balance of an account.
59    fn set_balance(
60        &self,
61        executor: &mut Executor,
62        address: Address,
63        amount: U256,
64    ) -> BackendResult<()>;
65
66    /// Gets the balance of an account
67    fn get_balance(&self, executor: &mut Executor, address: Address) -> BackendResult<U256>;
68
69    /// Set the nonce of an account.
70    fn set_nonce(&self, executor: &mut Executor, address: Address, nonce: u64)
71    -> BackendResult<()>;
72
73    /// Returns the nonce of an account.
74    fn get_nonce(&self, executor: &mut Executor, address: Address) -> BackendResult<u64>;
75
76    /// Execute a transaction and *WITHOUT* applying state changes.
77    fn call(
78        &self,
79        ctx: &dyn ExecutorStrategyContext,
80        backend: &mut CowBackend<'_>,
81        env: &mut Env,
82        executor_env: &Env,
83        inspector: &mut InspectorStack,
84    ) -> Result<ResultAndState>;
85
86    /// Execute a transaction and apply state changes.
87    fn transact(
88        &self,
89        ctx: &mut dyn ExecutorStrategyContext,
90        backend: &mut Backend,
91        env: &mut Env,
92        executor_env: &Env,
93        inspector: &mut InspectorStack,
94    ) -> Result<ResultAndState>;
95}
96
97/// Extended trait for Revive/PVM.
98pub trait ExecutorStrategyExt {
99    /// Set [DualCompiledContracts] on the context.
100    fn revive_set_dual_compiled_contracts(
101        &self,
102        _ctx: &mut dyn ExecutorStrategyContext,
103        _dual_compiled_contracts: DualCompiledContracts,
104    ) {
105    }
106
107    fn revive_set_compilation_output(
108        &self,
109        _ctx: &mut dyn ExecutorStrategyContext,
110        _output: ProjectCompileOutput,
111    ) {
112    }
113
114    fn start_transaction(&self, _ctx: &dyn ExecutorStrategyContext) {}
115    fn rollback_transaction(&self, _ctx: &dyn ExecutorStrategyContext) {}
116}
117
118/// Implements [ExecutorStrategyRunner] for EVM.
119#[derive(Debug, Default, Clone)]
120pub struct EvmExecutorStrategyRunner;
121
122impl ExecutorStrategyRunner for EvmExecutorStrategyRunner {
123    fn set_balance(
124        &self,
125        executor: &mut Executor,
126        address: Address,
127        amount: U256,
128    ) -> BackendResult<()> {
129        let mut account = executor.backend().basic_ref(address)?.unwrap_or_default();
130        account.balance = amount;
131        executor.backend_mut().insert_account_info(address, account);
132        Ok(())
133    }
134
135    fn get_balance(&self, executor: &mut Executor, address: Address) -> BackendResult<U256> {
136        Ok(executor.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
137    }
138
139    fn set_nonce(
140        &self,
141        executor: &mut Executor,
142        address: Address,
143        nonce: u64,
144    ) -> BackendResult<()> {
145        let mut account = executor.backend().basic_ref(address)?.unwrap_or_default();
146        account.nonce = nonce;
147        executor.backend_mut().insert_account_info(address, account);
148        Ok(())
149    }
150
151    fn get_nonce(&self, executor: &mut Executor, address: Address) -> BackendResult<u64> {
152        Ok(executor.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
153    }
154
155    fn call(
156        &self,
157        _ctx: &dyn ExecutorStrategyContext,
158        backend: &mut CowBackend<'_>,
159        env: &mut Env,
160        _executor_env: &Env,
161        inspector: &mut InspectorStack,
162    ) -> Result<ResultAndState> {
163        backend.inspect(env, inspector, Box::new(()))
164    }
165
166    fn transact(
167        &self,
168        _ctx: &mut dyn ExecutorStrategyContext,
169        backend: &mut Backend,
170        env: &mut Env,
171        _executor_env: &Env,
172        inspector: &mut InspectorStack,
173    ) -> Result<ResultAndState> {
174        backend.inspect(env, inspector, Box::new(()))
175    }
176
177    fn new_backend_strategy(&self, _ctx: &dyn ExecutorStrategyContext) -> BackendStrategy {
178        BackendStrategy::new_evm()
179    }
180
181    fn new_cheatcodes_strategy(&self, _ctx: &dyn ExecutorStrategyContext) -> CheatcodesStrategy {
182        CheatcodesStrategy::new_evm()
183    }
184}
185
186impl ExecutorStrategyExt for EvmExecutorStrategyRunner {}
187
188/// Defines the strategy for an [Executor].
189#[derive(Debug)]
190pub struct ExecutorStrategy {
191    /// Strategy runner.
192    pub runner: &'static dyn ExecutorStrategyRunner,
193    /// Strategy context.
194    pub context: Box<dyn ExecutorStrategyContext>,
195}
196
197impl ExecutorStrategy {
198    /// Creates a new EVM strategy for the [Executor].
199    pub fn new_evm() -> Self {
200        Self { runner: &EvmExecutorStrategyRunner, context: Box::new(()) }
201    }
202}
203
204impl Clone for ExecutorStrategy {
205    fn clone(&self) -> Self {
206        Self { runner: self.runner, context: self.context.clone() }
207    }
208}