foundry_evm_core/
utils.rs

1pub use crate::ic::*;
2use crate::{backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, InspectorExt};
3use alloy_consensus::BlockHeader;
4use alloy_json_abi::{Function, JsonAbi};
5use alloy_network::{AnyTxEnvelope, TransactionResponse};
6use alloy_primitives::{Address, Selector, TxKind, B256, U256};
7use alloy_provider::{network::BlockResponse, Network};
8use alloy_rpc_types::{Transaction, TransactionRequest};
9use foundry_common::is_impersonated_tx;
10use foundry_config::NamedChain;
11use foundry_fork_db::DatabaseError;
12use revm::{
13    handler::register::EvmHandler,
14    interpreter::{
15        return_ok, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome,
16        Gas, InstructionResult, InterpreterResult,
17    },
18    precompile::secp256r1::P256VERIFY,
19    primitives::{CreateScheme, EVMError, HandlerCfg, SpecId, KECCAK_EMPTY},
20    FrameOrResult, FrameResult,
21};
22use std::{cell::RefCell, rc::Rc, sync::Arc};
23
24pub use revm::primitives::EvmState as StateChangeset;
25
26/// Depending on the configured chain id and block number this should apply any specific changes
27///
28/// - checks for prevrandao mixhash after merge
29/// - applies chain specifics: on Arbitrum `block.number` is the L1 block
30///
31/// Should be called with proper chain id (retrieved from provider if not provided).
32pub fn apply_chain_and_block_specific_env_changes<N: Network>(
33    env: &mut revm::primitives::Env,
34    block: &N::BlockResponse,
35) {
36    use NamedChain::*;
37    if let Ok(chain) = NamedChain::try_from(env.cfg.chain_id) {
38        let block_number = block.header().number();
39
40        match chain {
41            Mainnet => {
42                // after merge difficulty is supplanted with prevrandao EIP-4399
43                if block_number >= 15_537_351u64 {
44                    env.block.difficulty = env.block.prevrandao.unwrap_or_default().into();
45                }
46
47                return;
48            }
49            BinanceSmartChain | BinanceSmartChainTestnet => {
50                // https://github.com/foundry-rs/foundry/issues/9942
51                // As far as observed from the source code of bnb-chain/bsc, the `difficulty` field
52                // is still in use and returned by the corresponding opcode but `prevrandao`
53                // (`mixHash`) is always zero, even though bsc adopts the newer EVM
54                // specification. This will confuse revm and causes emulation
55                // failure.
56                env.block.prevrandao = Some(env.block.difficulty.into());
57                return;
58            }
59            Moonbeam | Moonbase | Moonriver | MoonbeamDev => {
60                if env.block.prevrandao.is_none() {
61                    // <https://github.com/foundry-rs/foundry/issues/4232>
62                    env.block.prevrandao = Some(B256::random());
63                }
64            }
65            c if c.is_arbitrum() => {
66                // on arbitrum `block.number` is the L1 block which is included in the
67                // `l1BlockNumber` field
68                if let Some(l1_block_number) = block
69                    .other_fields()
70                    .and_then(|other| other.get("l1BlockNumber").cloned())
71                    .and_then(|l1_block_number| {
72                        serde_json::from_value::<U256>(l1_block_number).ok()
73                    })
74                {
75                    env.block.number = l1_block_number;
76                }
77            }
78            _ => {}
79        }
80    }
81
82    // if difficulty is `0` we assume it's past merge
83    if block.header().difficulty().is_zero() {
84        env.block.difficulty = env.block.prevrandao.unwrap_or_default().into();
85    }
86}
87
88/// Given an ABI and selector, it tries to find the respective function.
89pub fn get_function<'a>(
90    contract_name: &str,
91    selector: Selector,
92    abi: &'a JsonAbi,
93) -> eyre::Result<&'a Function> {
94    abi.functions()
95        .find(|func| func.selector() == selector)
96        .ok_or_else(|| eyre::eyre!("{contract_name} does not have the selector {selector}"))
97}
98
99/// Configures the env for the given RPC transaction.
100/// Accounts for an impersonated transaction by resetting the `env.tx.caller` field to `tx.from`.
101pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction<AnyTxEnvelope>) {
102    let impersonated_from = is_impersonated_tx(&tx.inner).then_some(tx.from());
103    if let AnyTxEnvelope::Ethereum(tx) = &tx.inner.inner() {
104        configure_tx_req_env(env, &tx.clone().into(), impersonated_from).expect("cannot fail");
105    }
106}
107
108/// Configures the env for the given RPC transaction request.
109/// `impersonated_from` is the address of the impersonated account. This helps account for an
110/// impersonated transaction by resetting the `env.tx.caller` field to `impersonated_from`.
111pub fn configure_tx_req_env(
112    env: &mut revm::primitives::Env,
113    tx: &TransactionRequest,
114    impersonated_from: Option<Address>,
115) -> eyre::Result<()> {
116    let TransactionRequest {
117        nonce,
118        from,
119        to,
120        value,
121        gas_price,
122        gas,
123        max_fee_per_gas,
124        max_priority_fee_per_gas,
125        max_fee_per_blob_gas,
126        ref input,
127        chain_id,
128        ref blob_versioned_hashes,
129        ref access_list,
130        transaction_type: _,
131        ref authorization_list,
132        sidecar: _,
133    } = *tx;
134
135    // If no `to` field then set create kind: https://eips.ethereum.org/EIPS/eip-2470#deployment-transaction
136    env.tx.transact_to = to.unwrap_or(TxKind::Create);
137    // If the transaction is impersonated, we need to set the caller to the from
138    // address Ref: https://github.com/foundry-rs/foundry/issues/9541
139    env.tx.caller =
140        impersonated_from.unwrap_or(from.ok_or_else(|| eyre::eyre!("missing `from` field"))?);
141    env.tx.gas_limit = gas.ok_or_else(|| eyre::eyre!("missing `gas` field"))?;
142    env.tx.nonce = nonce;
143    env.tx.value = value.unwrap_or_default();
144    env.tx.data = input.input().cloned().unwrap_or_default();
145    env.tx.chain_id = chain_id;
146
147    // Type 1, EIP-2930
148    env.tx.access_list = access_list.clone().unwrap_or_default().0.into_iter().collect();
149
150    // Type 2, EIP-1559
151    env.tx.gas_price = U256::from(gas_price.or(max_fee_per_gas).unwrap_or_default());
152    env.tx.gas_priority_fee = max_priority_fee_per_gas.map(U256::from);
153
154    // Type 3, EIP-4844
155    env.tx.blob_hashes = blob_versioned_hashes.clone().unwrap_or_default();
156    env.tx.max_fee_per_blob_gas = max_fee_per_blob_gas.map(U256::from);
157
158    // Type 4, EIP-7702
159    if let Some(authorization_list) = authorization_list {
160        env.tx.authorization_list =
161            Some(revm::primitives::AuthorizationList::Signed(authorization_list.clone()));
162    }
163
164    Ok(())
165}
166
167/// Get the gas used, accounting for refunds
168pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 {
169    let refund_quotient = if SpecId::enabled(spec, SpecId::LONDON) { 5 } else { 2 };
170    spent - (refunded).min(spent / refund_quotient)
171}
172
173fn get_create2_factory_call_inputs(
174    salt: U256,
175    inputs: CreateInputs,
176    deployer: Address,
177) -> CallInputs {
178    let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat();
179    CallInputs {
180        caller: inputs.caller,
181        bytecode_address: deployer,
182        target_address: deployer,
183        scheme: CallScheme::Call,
184        value: CallValue::Transfer(inputs.value),
185        input: calldata.into(),
186        gas_limit: inputs.gas_limit,
187        is_static: false,
188        return_memory_offset: 0..0,
189        is_eof: false,
190    }
191}
192
193/// Used for routing certain CREATE2 invocations through CREATE2_DEPLOYER.
194///
195/// Overrides create hook with CALL frame if [InspectorExt::should_use_create2_factory] returns
196/// true. Keeps track of overridden frames and handles outcome in the overridden insert_call_outcome
197/// hook by inserting decoded address directly into interpreter.
198///
199/// Should be installed after [revm::inspector_handle_register] and before any other registers.
200pub fn create2_handler_register<I: InspectorExt>(
201    handler: &mut EvmHandler<'_, I, &mut dyn DatabaseExt>,
202) {
203    let create2_overrides = Rc::<RefCell<Vec<_>>>::new(RefCell::new(Vec::new()));
204
205    let create2_overrides_inner = create2_overrides.clone();
206    let old_handle = handler.execution.create.clone();
207    handler.execution.create =
208        Arc::new(move |ctx, mut inputs| -> Result<FrameOrResult, EVMError<DatabaseError>> {
209            let CreateScheme::Create2 { salt } = inputs.scheme else {
210                return old_handle(ctx, inputs);
211            };
212            if !ctx.external.should_use_create2_factory(&mut ctx.evm, &mut inputs) {
213                return old_handle(ctx, inputs);
214            }
215
216            let gas_limit = inputs.gas_limit;
217
218            // Get CREATE2 deployer.
219            let create2_deployer = ctx.external.create2_deployer();
220            // Generate call inputs for CREATE2 factory.
221            let mut call_inputs = get_create2_factory_call_inputs(salt, *inputs, create2_deployer);
222
223            // Call inspector to change input or return outcome.
224            let outcome = ctx.external.call(&mut ctx.evm, &mut call_inputs);
225
226            // Push data about current override to the stack.
227            create2_overrides_inner
228                .borrow_mut()
229                .push((ctx.evm.journaled_state.depth(), call_inputs.clone()));
230
231            // Sanity check that CREATE2 deployer exists.
232            let code_hash = ctx.evm.load_account(create2_deployer)?.info.code_hash;
233            if code_hash == KECCAK_EMPTY {
234                return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome {
235                    result: InterpreterResult {
236                        result: InstructionResult::Revert,
237                        output: format!("missing CREATE2 deployer: {create2_deployer}").into(),
238                        gas: Gas::new(gas_limit),
239                    },
240                    memory_offset: 0..0,
241                })))
242            } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
243                return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome {
244                    result: InterpreterResult {
245                        result: InstructionResult::Revert,
246                        output: "invalid CREATE2 deployer bytecode".into(),
247                        gas: Gas::new(gas_limit),
248                    },
249                    memory_offset: 0..0,
250                })))
251            }
252
253            // Handle potential inspector override.
254            if let Some(outcome) = outcome {
255                return Ok(FrameOrResult::Result(FrameResult::Call(outcome)));
256            }
257
258            // Create CALL frame for CREATE2 factory invocation.
259            let mut frame_or_result = ctx.evm.make_call_frame(&call_inputs);
260
261            if let Ok(FrameOrResult::Frame(frame)) = &mut frame_or_result {
262                ctx.external
263                    .initialize_interp(&mut frame.frame_data_mut().interpreter, &mut ctx.evm)
264            }
265            frame_or_result
266        });
267
268    let create2_overrides_inner = create2_overrides;
269    let old_handle = handler.execution.insert_call_outcome.clone();
270    handler.execution.insert_call_outcome =
271        Arc::new(move |ctx, frame, shared_memory, mut outcome| {
272            // If we are on the depth of the latest override, handle the outcome.
273            if create2_overrides_inner
274                .borrow()
275                .last()
276                .is_some_and(|(depth, _)| *depth == ctx.evm.journaled_state.depth())
277            {
278                let (_, call_inputs) = create2_overrides_inner.borrow_mut().pop().unwrap();
279                outcome = ctx.external.call_end(&mut ctx.evm, &call_inputs, outcome);
280
281                // Decode address from output.
282                let address = match outcome.instruction_result() {
283                    return_ok!() => Address::try_from(outcome.output().as_ref())
284                        .map_err(|_| {
285                            outcome.result = InterpreterResult {
286                                result: InstructionResult::Revert,
287                                output: "invalid CREATE2 factory output".into(),
288                                gas: Gas::new(call_inputs.gas_limit),
289                            };
290                        })
291                        .ok(),
292                    _ => None,
293                };
294                frame
295                    .frame_data_mut()
296                    .interpreter
297                    .insert_create_outcome(CreateOutcome { address, result: outcome.result });
298
299                Ok(())
300            } else {
301                old_handle(ctx, frame, shared_memory, outcome)
302            }
303        });
304}
305
306/// Adds Odyssey P256 precompile to the list of loaded precompiles.
307pub fn odyssey_handler_register<EXT, DB: revm::Database>(handler: &mut EvmHandler<'_, EXT, DB>) {
308    let prev = handler.pre_execution.load_precompiles.clone();
309    handler.pre_execution.load_precompiles = Arc::new(move || {
310        let mut loaded_precompiles = prev();
311
312        loaded_precompiles.extend([P256VERIFY]);
313
314        loaded_precompiles
315    });
316}
317
318/// Creates a new EVM with the given inspector.
319pub fn new_evm_with_inspector<'evm, 'i, 'db, I: InspectorExt + ?Sized>(
320    db: &'db mut dyn DatabaseExt,
321    env: revm::primitives::EnvWithHandlerCfg,
322    inspector: &'i mut I,
323) -> revm::Evm<'evm, &'i mut I, &'db mut dyn DatabaseExt> {
324    let revm::primitives::EnvWithHandlerCfg { env, handler_cfg } = env;
325
326    // NOTE: We could use `revm::Evm::builder()` here, but on the current patch it has some
327    // performance issues.
328    /*
329    revm::Evm::builder()
330        .with_db(db)
331        .with_env(env)
332        .with_external_context(inspector)
333        .with_handler_cfg(handler_cfg)
334        .append_handler_register(revm::inspector_handle_register)
335        .append_handler_register(create2_handler_register)
336        .build()
337    */
338
339    let mut handler = revm::Handler::new(handler_cfg);
340    handler.append_handler_register_plain(revm::inspector_handle_register);
341    if inspector.is_odyssey() {
342        handler.append_handler_register_plain(odyssey_handler_register);
343    }
344    handler.append_handler_register_plain(create2_handler_register);
345
346    let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector);
347
348    revm::Evm::new(context, handler)
349}
350
351pub fn new_evm_with_existing_context<'a>(
352    inner: revm::InnerEvmContext<&'a mut dyn DatabaseExt>,
353    inspector: &'a mut dyn InspectorExt,
354) -> revm::Evm<'a, &'a mut dyn InspectorExt, &'a mut dyn DatabaseExt> {
355    let handler_cfg = HandlerCfg::new(inner.spec_id());
356
357    let mut handler = revm::Handler::new(handler_cfg);
358    handler.append_handler_register_plain(revm::inspector_handle_register);
359    if inspector.is_odyssey() {
360        handler.append_handler_register_plain(odyssey_handler_register);
361    }
362    handler.append_handler_register_plain(create2_handler_register);
363
364    let context =
365        revm::Context::new(revm::EvmContext { inner, precompiles: Default::default() }, inspector);
366    revm::Evm::new(context, handler)
367}