Skip to main content

anvil/eth/backend/executor/
optimism.rs

1//! OP-stack receipt construction for the Anvil block executor.
2
3use alloy_consensus::{Eip658Value, Receipt, ReceiptWithBloom, Transaction};
4use alloy_eips::Encodable2718;
5use alloy_primitives::{Address, Log};
6use foundry_evm::hardfork::{FoundryHardfork, OpHardfork};
7use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope};
8use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom};
9use op_revm::{L1BlockInfo, estimate_tx_compressed_size};
10use revm::{Database, context_interface::result::ExecutionResult, state::EvmState};
11
12use super::AnvilBlockExecutor;
13
14impl<E> AnvilBlockExecutor<E> {
15    /// Configures OP-specific block accounting without changing the shared constructor.
16    pub(crate) fn set_optimism_hardfork(&mut self, hardfork: FoundryHardfork) {
17        self.optimism_jovian = OpHardfork::from(hardfork) >= OpHardfork::Jovian;
18    }
19}
20
21/// Returns the blob gas accounted for by an OP transaction under the active hardfork.
22pub(crate) fn blob_gas_used<DB: Database>(
23    db: &mut DB,
24    tx: &FoundryTxEnvelope,
25    jovian: bool,
26) -> Result<u64, alloy_evm::block::BlockExecutionError> {
27    if !jovian || matches!(tx, FoundryTxEnvelope::Deposit(_)) {
28        return Ok(tx.blob_gas_used().unwrap_or_default());
29    }
30
31    let encoded = estimate_tx_compressed_size(tx.encoded_2718().as_ref()).saturating_div(1_000_000);
32    let scalar = L1BlockInfo::fetch_da_footprint_gas_scalar(db)
33        .map_err(alloy_evm::block::BlockExecutionError::other)?;
34    Ok(encoded.saturating_mul(u64::from(scalar)))
35}
36
37/// Builds a mined OP deposit receipt and derives its fork-specific metadata.
38pub(crate) fn build_mined_deposit_receipt<H>(
39    result: ExecutionResult<H>,
40    state: &EvmState,
41    sender: Address,
42    cumulative_gas_used: u64,
43) -> FoundryReceiptEnvelope {
44    let deposit_nonce = state.get(&sender).map(|account| account.info.nonce);
45    build_deposit_receipt(result, cumulative_gas_used, deposit_nonce, deposit_nonce.map(|_| 1))
46}
47
48/// Builds an RPC-simulated OP deposit receipt and derives its fork-specific metadata.
49pub(crate) fn build_simulated_deposit_receipt<H>(
50    hardfork: FoundryHardfork,
51    caller_nonce: u64,
52    result: &ExecutionResult<H>,
53    logs: Vec<Log>,
54    cumulative_gas_used: u64,
55) -> FoundryReceiptEnvelope {
56    let hardfork = OpHardfork::from(hardfork);
57    let deposit_nonce = (hardfork >= OpHardfork::Regolith).then_some(caller_nonce);
58    let deposit_receipt_version = (hardfork >= OpHardfork::Canyon).then_some(1);
59    let receipt =
60        Receipt { status: Eip658Value::Eip658(result.is_success()), cumulative_gas_used, logs }
61            .with_bloom();
62    wrap_deposit_receipt(receipt, deposit_nonce, deposit_receipt_version)
63}
64
65fn build_deposit_receipt<H>(
66    result: ExecutionResult<H>,
67    cumulative_gas_used: u64,
68    deposit_nonce: Option<u64>,
69    deposit_receipt_version: Option<u64>,
70) -> FoundryReceiptEnvelope {
71    let receipt = Receipt {
72        status: Eip658Value::Eip658(result.is_success()),
73        cumulative_gas_used,
74        logs: result.into_logs(),
75    }
76    .with_bloom();
77    wrap_deposit_receipt(receipt, deposit_nonce, deposit_receipt_version)
78}
79
80fn wrap_deposit_receipt(
81    receipt: ReceiptWithBloom<Receipt>,
82    deposit_nonce: Option<u64>,
83    deposit_receipt_version: Option<u64>,
84) -> FoundryReceiptEnvelope {
85    FoundryReceiptEnvelope::Deposit(OpDepositReceiptWithBloom {
86        receipt: OpDepositReceipt {
87            inner: receipt.receipt,
88            deposit_nonce,
89            deposit_receipt_version,
90        },
91        logs_bloom: receipt.logs_bloom,
92    })
93}