foundry_evm_core/fork/
init.rs

1use crate::{AsEnvMut, Env, EvmEnv, utils::apply_chain_and_block_specific_env_changes};
2use alloy_consensus::BlockHeader;
3use alloy_primitives::{Address, U256};
4use alloy_provider::{Network, Provider, network::BlockResponse};
5use alloy_rpc_types::BlockNumberOrTag;
6use foundry_common::NON_ARCHIVE_NODE_WARNING;
7use foundry_evm_networks::NetworkConfigs;
8use revm::context::{BlockEnv, CfgEnv, TxEnv};
9
10/// Initializes a REVM block environment based on a forked
11/// ethereum provider.
12#[allow(clippy::too_many_arguments)]
13pub async fn environment<N: Network, P: Provider<N>>(
14    provider: &P,
15    memory_limit: u64,
16    override_gas_price: Option<u128>,
17    override_chain_id: Option<u64>,
18    pin_block: Option<u64>,
19    origin: Address,
20    disable_block_gas_limit: bool,
21    enable_tx_gas_limit: bool,
22    configs: NetworkConfigs,
23) -> eyre::Result<(Env, N::BlockResponse)> {
24    trace!(
25        %memory_limit,
26        ?override_gas_price,
27        ?override_chain_id,
28        ?pin_block,
29        %origin,
30        %disable_block_gas_limit,
31        %enable_tx_gas_limit,
32        ?configs,
33        "creating fork environment"
34    );
35    let bn = match pin_block {
36        Some(bn) => BlockNumberOrTag::Number(bn),
37        None => BlockNumberOrTag::Latest,
38    };
39    let (gas_price, chain_id, block) = tokio::try_join!(
40        option_try_or_else(override_gas_price, async || provider.get_gas_price().await),
41        option_try_or_else(override_chain_id, async || provider.get_chain_id().await),
42        provider.get_block_by_number(bn)
43    )?;
44    let Some(block) = block else {
45        let bn_msg = match bn {
46            BlockNumberOrTag::Number(bn) => format!("block number: {bn}"),
47            bn => format!("{bn} block"),
48        };
49        let latest_msg = if let Ok(latest_block) = provider.get_block_number().await {
50            // If the `eth_getBlockByNumber` call succeeds, but returns null instead of
51            // the block, and the block number is less than equal the latest block, then
52            // the user is forking from a non-archive node with an older block number.
53            if let Some(block_number) = pin_block
54                && block_number <= latest_block
55            {
56                error!("{NON_ARCHIVE_NODE_WARNING}");
57            }
58            format!("; latest block number: {latest_block}")
59        } else {
60            Default::default()
61        };
62        eyre::bail!("failed to get {bn_msg}{latest_msg}");
63    };
64
65    let cfg = configure_env(chain_id, memory_limit, disable_block_gas_limit, enable_tx_gas_limit);
66
67    let mut env = Env {
68        evm_env: EvmEnv {
69            cfg_env: cfg,
70            block_env: BlockEnv {
71                number: U256::from(block.header().number()),
72                timestamp: U256::from(block.header().timestamp()),
73                beneficiary: block.header().beneficiary(),
74                difficulty: block.header().difficulty(),
75                prevrandao: block.header().mix_hash(),
76                basefee: block.header().base_fee_per_gas().unwrap_or_default(),
77                gas_limit: block.header().gas_limit(),
78                ..Default::default()
79            },
80        },
81        tx: TxEnv {
82            caller: origin,
83            gas_price,
84            chain_id: Some(chain_id),
85            gas_limit: block.header().gas_limit(),
86            ..Default::default()
87        },
88    };
89
90    apply_chain_and_block_specific_env_changes::<N>(env.as_env_mut(), &block, configs);
91
92    Ok((env, block))
93}
94
95async fn option_try_or_else<T, E>(
96    option: Option<T>,
97    f: impl AsyncFnOnce() -> Result<T, E>,
98) -> Result<T, E> {
99    if let Some(value) = option { Ok(value) } else { f().await }
100}
101
102/// Configures the environment for the given chain id and memory limit.
103pub fn configure_env(
104    chain_id: u64,
105    memory_limit: u64,
106    disable_block_gas_limit: bool,
107    enable_tx_gas_limit: bool,
108) -> CfgEnv {
109    let mut cfg = CfgEnv::default();
110    cfg.chain_id = chain_id;
111    cfg.memory_limit = memory_limit;
112    cfg.limit_contract_code_size = Some(usize::MAX);
113    // EIP-3607 rejects transactions from senders with deployed code.
114    // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the caller
115    // is a contract. So we disable the check by default.
116    cfg.disable_eip3607 = true;
117    cfg.disable_block_gas_limit = disable_block_gas_limit;
118    cfg.disable_nonce_check = true;
119    // By default do not enforce transaction gas limits imposed by Osaka (EIP-7825).
120    // Users can opt-in to enable these limits by setting `enable_tx_gas_limit` to true.
121    if !enable_tx_gas_limit {
122        cfg.tx_gas_limit_cap = Some(u64::MAX);
123    }
124    cfg
125}