1use super::fork::environment;
2use crate::{
3 EvmEnv,
4 constants::DEFAULT_CREATE2_DEPLOYER,
5 fork::{CreateFork, configure_env},
6};
7use alloy_primitives::{Address, B256, U256};
8use alloy_provider::{Provider, network::AnyRpcBlock};
9use eyre::WrapErr;
10use foundry_common::{ALCHEMY_FREE_TIER_CUPS, provider::ProviderBuilder};
11use foundry_config::{Chain, Config, GasLimit};
12use foundry_evm_networks::NetworkConfigs;
13use revm::context::{BlockEnv, TxEnv};
14use serde::{Deserialize, Serialize};
15use std::fmt::Write;
16use url::Url;
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct EvmOpts {
20 #[serde(flatten)]
22 pub env: Env,
23
24 #[serde(rename = "eth_rpc_url")]
26 pub fork_url: Option<String>,
27
28 pub fork_block_number: Option<u64>,
30
31 pub fork_retries: Option<u32>,
33
34 pub fork_retry_backoff: Option<u64>,
36
37 pub fork_headers: Option<Vec<String>>,
39
40 pub compute_units_per_second: Option<u64>,
44
45 pub no_rpc_rate_limit: bool,
47
48 pub no_storage_caching: bool,
50
51 pub initial_balance: U256,
53
54 pub sender: Address,
56
57 pub ffi: bool,
59
60 pub always_use_create_2_factory: bool,
62
63 pub verbosity: u8,
65
66 pub memory_limit: u64,
69
70 pub isolate: bool,
72
73 pub disable_block_gas_limit: bool,
75
76 pub enable_tx_gas_limit: bool,
78
79 #[serde(flatten)]
80 pub networks: NetworkConfigs,
82
83 pub create2_deployer: Address,
85}
86
87impl Default for EvmOpts {
88 fn default() -> Self {
89 Self {
90 env: Env::default(),
91 fork_url: None,
92 fork_block_number: None,
93 fork_retries: None,
94 fork_retry_backoff: None,
95 fork_headers: None,
96 compute_units_per_second: None,
97 no_rpc_rate_limit: false,
98 no_storage_caching: false,
99 initial_balance: U256::default(),
100 sender: Address::default(),
101 ffi: false,
102 always_use_create_2_factory: false,
103 verbosity: 0,
104 memory_limit: 0,
105 isolate: false,
106 disable_block_gas_limit: false,
107 enable_tx_gas_limit: false,
108 networks: NetworkConfigs::default(),
109 create2_deployer: DEFAULT_CREATE2_DEPLOYER,
110 }
111 }
112}
113
114impl EvmOpts {
115 pub async fn evm_env(&self) -> eyre::Result<crate::Env> {
120 if let Some(ref fork_url) = self.fork_url {
121 Ok(self.fork_evm_env(fork_url).await?.0)
122 } else {
123 Ok(self.local_evm_env())
124 }
125 }
126
127 pub async fn fork_evm_env(&self, fork_url: &str) -> eyre::Result<(crate::Env, AnyRpcBlock)> {
130 let provider = ProviderBuilder::new(fork_url)
131 .compute_units_per_second(self.get_compute_units_per_second())
132 .build()?;
133 environment(
134 &provider,
135 self.memory_limit,
136 self.env.gas_price.map(|v| v as u128),
137 self.env.chain_id,
138 self.fork_block_number,
139 self.sender,
140 self.disable_block_gas_limit,
141 self.enable_tx_gas_limit,
142 )
143 .await
144 .wrap_err_with(|| {
145 let mut msg = "could not instantiate forked environment".to_string();
146 if let Ok(url) = Url::parse(fork_url)
147 && let Some(provider) = url.host()
148 {
149 write!(msg, " with provider {provider}").unwrap();
150 }
151 msg
152 })
153 }
154
155 pub fn local_evm_env(&self) -> crate::Env {
157 let cfg = configure_env(
158 self.env.chain_id.unwrap_or(foundry_common::DEV_CHAIN_ID),
159 self.memory_limit,
160 self.disable_block_gas_limit,
161 self.enable_tx_gas_limit,
162 );
163
164 crate::Env {
165 evm_env: EvmEnv {
166 cfg_env: cfg,
167 block_env: BlockEnv {
168 number: self.env.block_number,
169 beneficiary: self.env.block_coinbase,
170 timestamp: self.env.block_timestamp,
171 difficulty: U256::from(self.env.block_difficulty),
172 prevrandao: Some(self.env.block_prevrandao),
173 basefee: self.env.block_base_fee_per_gas,
174 gas_limit: self.gas_limit(),
175 ..Default::default()
176 },
177 },
178 tx: TxEnv {
179 gas_price: self.env.gas_price.unwrap_or_default().into(),
180 gas_limit: self.gas_limit(),
181 caller: self.sender,
182 ..Default::default()
183 },
184 }
185 }
186
187 pub fn get_fork(&self, config: &Config, env: crate::Env) -> Option<CreateFork> {
201 let url = self.fork_url.clone()?;
202 let enable_caching = config.enable_caching(&url, env.evm_env.cfg_env.chain_id);
203 Some(CreateFork { url, enable_caching, env, evm_opts: self.clone() })
204 }
205
206 pub fn gas_limit(&self) -> u64 {
208 self.env.block_gas_limit.unwrap_or(self.env.gas_limit).0
209 }
210
211 pub async fn get_chain_id(&self) -> u64 {
217 if let Some(id) = self.env.chain_id {
218 return id;
219 }
220 self.get_remote_chain_id().await.unwrap_or(Chain::mainnet()).id()
221 }
222
223 pub fn get_compute_units_per_second(&self) -> u64 {
228 if self.no_rpc_rate_limit {
229 u64::MAX
230 } else if let Some(cups) = self.compute_units_per_second {
231 cups
232 } else {
233 ALCHEMY_FREE_TIER_CUPS
234 }
235 }
236
237 pub async fn get_remote_chain_id(&self) -> Option<Chain> {
239 if let Some(ref url) = self.fork_url {
240 trace!(?url, "retrieving chain via eth_chainId");
241 let provider = ProviderBuilder::new(url.as_str())
242 .compute_units_per_second(self.get_compute_units_per_second())
243 .build()
244 .ok()
245 .unwrap_or_else(|| panic!("Failed to establish provider to {url}"));
246
247 if let Ok(id) = provider.get_chain_id().await {
248 return Some(Chain::from(id));
249 }
250
251 if url.contains("mainnet") {
255 trace!(?url, "auto detected mainnet chain");
256 return Some(Chain::mainnet());
257 }
258 }
259
260 None
261 }
262}
263
264#[derive(Clone, Debug, Default, Serialize, Deserialize)]
265pub struct Env {
266 pub gas_limit: GasLimit,
268
269 pub chain_id: Option<u64>,
271
272 #[serde(default, skip_serializing_if = "Option::is_none")]
277 pub gas_price: Option<u64>,
278
279 pub block_base_fee_per_gas: u64,
281
282 pub tx_origin: Address,
284
285 pub block_coinbase: Address,
287
288 #[serde(
290 deserialize_with = "foundry_config::deserialize_u64_to_u256",
291 serialize_with = "foundry_config::serialize_u64_or_u256"
292 )]
293 pub block_timestamp: U256,
294
295 #[serde(
297 deserialize_with = "foundry_config::deserialize_u64_to_u256",
298 serialize_with = "foundry_config::serialize_u64_or_u256"
299 )]
300 pub block_number: U256,
301
302 pub block_difficulty: u64,
304
305 pub block_prevrandao: B256,
307
308 #[serde(default, skip_serializing_if = "Option::is_none")]
310 pub block_gas_limit: Option<GasLimit>,
311
312 #[serde(default, skip_serializing_if = "Option::is_none")]
314 pub code_size_limit: Option<usize>,
315}