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 eyre::WrapErr;
7use foundry_common::NON_ARCHIVE_NODE_WARNING;
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    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) -> eyre::Result<(Env, N::BlockResponse)> {
23    let block_number = if let Some(pin_block) = pin_block {
24        pin_block
25    } else {
26        provider.get_block_number().await.wrap_err("failed to get latest block number")?
27    };
28    let (fork_gas_price, rpc_chain_id, block) = tokio::try_join!(
29        provider.get_gas_price(),
30        provider.get_chain_id(),
31        provider.get_block_by_number(BlockNumberOrTag::Number(block_number))
32    )?;
33    let block = if let Some(block) = block {
34        block
35    } else {
36        if let Ok(latest_block) = provider.get_block_number().await {
37            // If the `eth_getBlockByNumber` call succeeds, but returns null instead of
38            // the block, and the block number is less than equal the latest block, then
39            // the user is forking from a non-archive node with an older block number.
40            if block_number <= latest_block {
41                error!("{NON_ARCHIVE_NODE_WARNING}");
42            }
43            eyre::bail!(
44                "failed to get block for block number: {block_number}; \
45                 latest block number: {latest_block}"
46            );
47        }
48        eyre::bail!("failed to get block for block number: {block_number}")
49    };
50
51    let cfg = configure_env(
52        override_chain_id.unwrap_or(rpc_chain_id),
53        memory_limit,
54        disable_block_gas_limit,
55        enable_tx_gas_limit,
56    );
57
58    let mut env = Env {
59        evm_env: EvmEnv {
60            cfg_env: cfg,
61            block_env: BlockEnv {
62                number: U256::from(block.header().number()),
63                timestamp: U256::from(block.header().timestamp()),
64                beneficiary: block.header().beneficiary(),
65                difficulty: block.header().difficulty(),
66                prevrandao: block.header().mix_hash(),
67                basefee: block.header().base_fee_per_gas().unwrap_or_default(),
68                gas_limit: block.header().gas_limit(),
69                ..Default::default()
70            },
71        },
72        tx: TxEnv {
73            caller: origin,
74            gas_price: gas_price.unwrap_or(fork_gas_price),
75            chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id)),
76            gas_limit: block.header().gas_limit() as u64,
77            ..Default::default()
78        },
79    };
80
81    apply_chain_and_block_specific_env_changes::<N>(env.as_env_mut(), &block);
82
83    Ok((env, block))
84}
85
86/// Configures the environment for the given chain id and memory limit.
87pub fn configure_env(
88    chain_id: u64,
89    memory_limit: u64,
90    disable_block_gas_limit: bool,
91    enable_tx_gas_limit: bool,
92) -> CfgEnv {
93    let mut cfg = CfgEnv::default();
94    cfg.chain_id = chain_id;
95    cfg.memory_limit = memory_limit;
96    cfg.limit_contract_code_size = Some(usize::MAX);
97    // EIP-3607 rejects transactions from senders with deployed code.
98    // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the caller
99    // is a contract. So we disable the check by default.
100    cfg.disable_eip3607 = true;
101    cfg.disable_block_gas_limit = disable_block_gas_limit;
102    cfg.disable_nonce_check = true;
103    // By default do not enforce transaction gas limits imposed by Osaka (EIP-7825).
104    // Users can opt-in to enable these limits by setting `enable_tx_gas_limit` to true.
105    if !enable_tx_gas_limit {
106        cfg.tx_gas_limit_cap = Some(u64::MAX);
107    }
108    cfg
109}