foundry_evm_core/
utils.rs1use crate::EnvMut;
2use alloy_chains::Chain;
3use alloy_consensus::{BlockHeader, private::alloy_eips::eip7840::BlobParams};
4use alloy_hardforks::EthereumHardfork;
5use alloy_json_abi::{Function, JsonAbi};
6use alloy_network::{AnyTxEnvelope, TransactionResponse};
7use alloy_primitives::{Address, B256, ChainId, Selector, TxKind, U256};
8use alloy_provider::{Network, network::BlockResponse};
9use alloy_rpc_types::{Transaction, TransactionRequest};
10use foundry_config::NamedChain;
11use foundry_evm_networks::NetworkConfigs;
12use revm::primitives::{
13 eip4844::{BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE},
14 hardfork::SpecId,
15};
16pub use revm::state::EvmState as StateChangeset;
17
18#[cold]
20#[inline(always)]
21pub fn cold_path() {
22 }
24
25pub fn apply_chain_and_block_specific_env_changes<N: Network>(
32 env: EnvMut<'_>,
33 block: &N::BlockResponse,
34 configs: NetworkConfigs,
35) {
36 use NamedChain::*;
37
38 if let Ok(chain) = NamedChain::try_from(env.cfg.chain_id) {
39 let block_number = block.header().number();
40
41 match chain {
42 Mainnet => {
43 if block_number >= 15_537_351u64 {
45 env.block.difficulty = env.block.prevrandao.unwrap_or_default().into();
46 }
47
48 return;
49 }
50 BinanceSmartChain | BinanceSmartChainTestnet => {
51 env.block.prevrandao = Some(env.block.difficulty.into());
58 return;
59 }
60 c if c.is_arbitrum() => {
61 if let Some(l1_block_number) = block
64 .other_fields()
65 .and_then(|other| other.get("l1BlockNumber").cloned())
66 .and_then(|l1_block_number| {
67 serde_json::from_value::<U256>(l1_block_number).ok()
68 })
69 {
70 env.block.number = l1_block_number.to();
71 }
72 }
73 _ => {}
74 }
75 }
76
77 if configs.bypass_prevrandao(env.cfg.chain_id) && env.block.prevrandao.is_none() {
78 env.block.prevrandao = Some(B256::random());
80 }
81
82 if block.header().difficulty().is_zero() {
84 env.block.difficulty = env.block.prevrandao.unwrap_or_default().into();
85 }
86}
87
88pub fn get_blob_params(chain_id: ChainId, timestamp: u64) -> BlobParams {
93 let hardfork = EthereumHardfork::from_chain_and_timestamp(Chain::from_id(chain_id), timestamp)
94 .unwrap_or_default();
95
96 match hardfork {
97 EthereumHardfork::Prague => BlobParams::prague(),
98 EthereumHardfork::Osaka => BlobParams::osaka(),
99 EthereumHardfork::Bpo1 => BlobParams::bpo1(),
100 EthereumHardfork::Bpo2 => BlobParams::bpo2(),
101
102 EthereumHardfork::Bpo3 => BlobParams::bpo2(),
104 EthereumHardfork::Bpo4 => BlobParams::bpo2(),
105 EthereumHardfork::Bpo5 => BlobParams::bpo2(),
106 EthereumHardfork::Amsterdam => BlobParams::bpo2(),
107
108 _ => BlobParams::cancun(),
110 }
111}
112
113pub fn get_blob_base_fee_update_fraction(chain_id: ChainId, timestamp: u64) -> u64 {
116 get_blob_params(chain_id, timestamp).update_fraction as u64
117}
118
119pub fn get_blob_base_fee_update_fraction_by_spec_id(spec: SpecId) -> u64 {
121 if spec >= SpecId::PRAGUE {
122 BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE
123 } else {
124 BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN
125 }
126}
127
128pub fn get_function<'a>(
130 contract_name: &str,
131 selector: Selector,
132 abi: &'a JsonAbi,
133) -> eyre::Result<&'a Function> {
134 abi.functions()
135 .find(|func| func.selector() == selector)
136 .ok_or_else(|| eyre::eyre!("{contract_name} does not have the selector {selector}"))
137}
138
139pub fn configure_tx_env(env: &mut EnvMut<'_>, tx: &Transaction<AnyTxEnvelope>) {
142 let from = tx.from();
143 if let AnyTxEnvelope::Ethereum(tx) = &tx.inner.inner() {
144 configure_tx_req_env(env, &tx.clone().into(), Some(from)).expect("cannot fail");
145 }
146}
147
148pub fn configure_tx_req_env(
152 env: &mut EnvMut<'_>,
153 tx: &TransactionRequest,
154 impersonated_from: Option<Address>,
155) -> eyre::Result<()> {
156 let tx_type = tx.transaction_type.unwrap_or_else(|| tx.minimal_tx_type() as u8);
158 env.tx.tx_type = tx_type;
159
160 let TransactionRequest {
161 nonce,
162 from,
163 to,
164 value,
165 gas_price,
166 gas,
167 max_fee_per_gas,
168 max_priority_fee_per_gas,
169 max_fee_per_blob_gas,
170 ref input,
171 chain_id,
172 ref blob_versioned_hashes,
173 ref access_list,
174 ref authorization_list,
175 transaction_type: _,
176 sidecar: _,
177 } = *tx;
178
179 env.tx.kind = to.unwrap_or(TxKind::Create);
181 env.tx.caller =
184 impersonated_from.unwrap_or(from.ok_or_else(|| eyre::eyre!("missing `from` field"))?);
185 env.tx.gas_limit = gas.ok_or_else(|| eyre::eyre!("missing `gas` field"))?;
186 env.tx.nonce = nonce.unwrap_or_default();
187 env.tx.value = value.unwrap_or_default();
188 env.tx.data = input.input().cloned().unwrap_or_default();
189 env.tx.chain_id = chain_id;
190
191 env.tx.access_list = access_list.clone().unwrap_or_default();
193
194 env.tx.gas_price = gas_price.or(max_fee_per_gas).unwrap_or_default();
196 env.tx.gas_priority_fee = max_priority_fee_per_gas;
197
198 env.tx.blob_hashes = blob_versioned_hashes.clone().unwrap_or_default();
200 env.tx.max_fee_per_blob_gas = max_fee_per_blob_gas.unwrap_or_default();
201
202 env.tx.set_signed_authorization(authorization_list.clone().unwrap_or_default());
204
205 Ok(())
206}