foundry_evm_core/fork/
init.rs

1use crate::utils::apply_chain_and_block_specific_env_changes;
2use alloy_consensus::BlockHeader;
3use alloy_primitives::{Address, U256};
4use alloy_provider::{network::BlockResponse, Network, Provider};
5use alloy_rpc_types::BlockNumberOrTag;
6use eyre::WrapErr;
7use foundry_common::NON_ARCHIVE_NODE_WARNING;
8use revm::primitives::{BlockEnv, CfgEnv, Env, TxEnv};
9
10/// Initializes a REVM block environment based on a forked
11/// ethereum provider.
12pub async fn environment<N: Network, P: Provider<N>>(
13    provider: &P,
14    memory_limit: u64,
15    gas_price: Option<u128>,
16    override_chain_id: Option<u64>,
17    pin_block: Option<u64>,
18    origin: Address,
19    disable_block_gas_limit: bool,
20) -> eyre::Result<(Env, N::BlockResponse)> {
21    let block_number = if let Some(pin_block) = pin_block {
22        pin_block
23    } else {
24        provider.get_block_number().await.wrap_err("failed to get latest block number")?
25    };
26    let (fork_gas_price, rpc_chain_id, block) = tokio::try_join!(
27        provider.get_gas_price(),
28        provider.get_chain_id(),
29        provider.get_block_by_number(BlockNumberOrTag::Number(block_number))
30    )?;
31    let block = if let Some(block) = block {
32        block
33    } else {
34        if let Ok(latest_block) = provider.get_block_number().await {
35            // If the `eth_getBlockByNumber` call succeeds, but returns null instead of
36            // the block, and the block number is less than equal the latest block, then
37            // the user is forking from a non-archive node with an older block number.
38            if block_number <= latest_block {
39                error!("{NON_ARCHIVE_NODE_WARNING}");
40            }
41            eyre::bail!(
42                "failed to get block for block number: {block_number}; \
43                 latest block number: {latest_block}"
44            );
45        }
46        eyre::bail!("failed to get block for block number: {block_number}")
47    };
48
49    let mut cfg = CfgEnv::default();
50    cfg.chain_id = override_chain_id.unwrap_or(rpc_chain_id);
51    cfg.memory_limit = memory_limit;
52    cfg.limit_contract_code_size = Some(usize::MAX);
53    // EIP-3607 rejects transactions from senders with deployed code.
54    // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the caller
55    // is a contract. So we disable the check by default.
56    cfg.disable_eip3607 = true;
57    cfg.disable_block_gas_limit = disable_block_gas_limit;
58
59    let mut env = Env {
60        cfg,
61        block: BlockEnv {
62            number: U256::from(block.header().number()),
63            timestamp: U256::from(block.header().timestamp()),
64            coinbase: block.header().beneficiary(),
65            difficulty: block.header().difficulty(),
66            prevrandao: block.header().mix_hash(),
67            basefee: U256::from(block.header().base_fee_per_gas().unwrap_or_default()),
68            gas_limit: U256::from(block.header().gas_limit()),
69            ..Default::default()
70        },
71        tx: TxEnv {
72            caller: origin,
73            gas_price: U256::from(gas_price.unwrap_or(fork_gas_price)),
74            chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id)),
75            gas_limit: block.header().gas_limit() as u64,
76            ..Default::default()
77        },
78    };
79
80    apply_chain_and_block_specific_env_changes::<N>(&mut env, &block);
81
82    Ok((env, block))
83}