1use crate::EnvMut;
2use alloy_consensus::BlockHeader;
3use alloy_json_abi::{Function, JsonAbi};
4use alloy_network::{AnyTxEnvelope, TransactionResponse};
5use alloy_primitives::{Address, B256, Selector, TxKind, U256};
6use alloy_provider::{Network, network::BlockResponse};
7use alloy_rpc_types::{Transaction, TransactionRequest};
8use foundry_config::NamedChain;
9pub use revm::state::EvmStateas StateChangeset;
1011/// Depending on the configured chain id and block number this should apply any specific changes
12///
13/// - checks for prevrandao mixhash after merge
14/// - applies chain specifics: on Arbitrum `block.number` is the L1 block
15///
16/// Should be called with proper chain id (retrieved from provider if not provided).
17pub fn apply_chain_and_block_specific_env_changes<N: Network>(
18 env: EnvMut<'_>,
19 block: &N::BlockResponse,
20) {
21use NamedChain::*;
2223if let Ok(chain) = NamedChain::try_from(env.cfg.chain_id) {
24let block_number = block.header().number();
2526match chain {
27 Mainnet => {
28// after merge difficulty is supplanted with prevrandao EIP-4399
29if block_number >= 15_537_351u64 {
30env.block.difficulty = env.block.prevrandao.unwrap_or_default().into();
31 }
3233return;
34 }
35 BinanceSmartChain | BinanceSmartChainTestnet => {
36// https://github.com/foundry-rs/foundry/issues/9942
37 // As far as observed from the source code of bnb-chain/bsc, the `difficulty` field
38 // is still in use and returned by the corresponding opcode but `prevrandao`
39 // (`mixHash`) is always zero, even though bsc adopts the newer EVM
40 // specification. This will confuse revm and causes emulation
41 // failure.
42env.block.prevrandao = Some(env.block.difficulty.into());
43return;
44 }
45 Moonbeam | Moonbase | Moonriver | MoonbeamDev | Rsk | RskTestnet => {
46if env.block.prevrandao.is_none() {
47// <https://github.com/foundry-rs/foundry/issues/4232>
48env.block.prevrandao = Some(B256::random());
49 }
50 }
51 c if c.is_arbitrum() => {
52// on arbitrum `block.number` is the L1 block which is included in the
53 // `l1BlockNumber` field
54if let Some(l1_block_number) = block55 .other_fields()
56 .and_then(|other| other.get("l1BlockNumber").cloned())
57 .and_then(|l1_block_number| {
58serde_json::from_value::<U256>(l1_block_number).ok()
59 })
60 {
61env.block.number = l1_block_number.to();
62 }
63 }
64_ => {}
65 }
66 }
6768// if difficulty is `0` we assume it's past merge
69if block.header().difficulty().is_zero() {
70env.block.difficulty = env.block.prevrandao.unwrap_or_default().into();
71 }
72}
7374/// Given an ABI and selector, it tries to find the respective function.
75pub fn get_function<'a>(
76 contract_name: &str,
77 selector: Selector,
78 abi: &'a JsonAbi,
79) -> eyre::Result<&'a Function> {
80abi.functions()
81 .find(|func| func.selector() == selector)
82 .ok_or_else(|| eyre::eyre!("{contract_name} does not have the selector {selector}"))
83}
8485/// Configures the env for the given RPC transaction.
86/// Accounts for an impersonated transaction by resetting the `env.tx.caller` field to `tx.from`.
87pub fn configure_tx_env(env: &mut EnvMut<'_>, tx: &Transaction<AnyTxEnvelope>) {
88let from = tx.from();
89if let AnyTxEnvelope::Ethereum(tx) = &tx.inner.inner() {
90configure_tx_req_env(env, &tx.clone().into(), Some(from)).expect("cannot fail");
91 }
92}
9394/// Configures the env for the given RPC transaction request.
95/// `impersonated_from` is the address of the impersonated account. This helps account for an
96/// impersonated transaction by resetting the `env.tx.caller` field to `impersonated_from`.
97pub fn configure_tx_req_env(
98 env: &mut EnvMut<'_>,
99 tx: &TransactionRequest,
100 impersonated_from: Option<Address>,
101) -> eyre::Result<()> {
102// If no transaction type is provided, we need to infer it from the other fields.
103let tx_type = tx.transaction_type.unwrap_or_else(|| tx.minimal_tx_type() as u8);
104env.tx.tx_type = tx_type;
105106let TransactionRequest {
107 nonce,
108 from,
109 to,
110 value,
111 gas_price,
112 gas,
113 max_fee_per_gas,
114 max_priority_fee_per_gas,
115 max_fee_per_blob_gas,
116ref input,
117 chain_id,
118ref blob_versioned_hashes,
119ref access_list,
120ref authorization_list,
121 transaction_type: _,
122 sidecar: _,
123 } = *tx;
124125// If no `to` field then set create kind: https://eips.ethereum.org/EIPS/eip-2470#deployment-transaction
126env.tx.kind = to.unwrap_or(TxKind::Create);
127// If the transaction is impersonated, we need to set the caller to the from
128 // address Ref: https://github.com/foundry-rs/foundry/issues/9541
129env.tx.caller =
130impersonated_from.unwrap_or(from.ok_or_else(|| eyre::eyre!("missing `from` field"))?);
131env.tx.gas_limit = gas.ok_or_else(|| eyre::eyre!("missing `gas` field"))?;
132env.tx.nonce = nonce.unwrap_or_default();
133env.tx.value = value.unwrap_or_default();
134env.tx.data = input.input().cloned().unwrap_or_default();
135env.tx.chain_id = chain_id;
136137// Type 1, EIP-2930
138env.tx.access_list = access_list.clone().unwrap_or_default();
139140// Type 2, EIP-1559
141env.tx.gas_price = gas_price.or(max_fee_per_gas).unwrap_or_default();
142env.tx.gas_priority_fee = max_priority_fee_per_gas;
143144// Type 3, EIP-4844
145env.tx.blob_hashes = blob_versioned_hashes.clone().unwrap_or_default();
146env.tx.max_fee_per_blob_gas = max_fee_per_blob_gas.unwrap_or_default();
147148// Type 4, EIP-7702
149env.tx.set_signed_authorization(authorization_list.clone().unwrap_or_default());
150151Ok(())
152}