//! RPC receipt response builder, extends a layer one receipt with layer two data. use reth_primitives::{Address, Receipt, TransactionMeta, TransactionSigned, TxKind}; use reth_rpc_types::{ AnyReceiptEnvelope, AnyTransactionReceipt, Log, OtherFields, ReceiptWithBloom, TransactionReceipt, WithOtherFields, }; use revm_primitives::calc_blob_gasprice; use super::{EthApiError, EthResult}; /// Receipt response builder. #[derive(Debug)] pub struct ReceiptBuilder { /// The base response body, contains L1 fields. base: TransactionReceipt>, /// Additional L2 fields. other: OtherFields, } impl ReceiptBuilder { /// Returns a new builder with the base response body (L1 fields) set. /// /// Note: This requires _all_ block receipts because we need to calculate the gas used by the /// transaction. pub fn new( transaction: &TransactionSigned, meta: TransactionMeta, receipt: &Receipt, all_receipts: &[Receipt], ) -> EthResult { // Note: we assume this transaction is valid, because it's mined (or part of pending block) // and we don't need to check for pre EIP-2 let from = transaction .recover_signer_unchecked() .ok_or(EthApiError::InvalidTransactionSignature)?; // get the previous transaction cumulative gas used let gas_used = if meta.index == 0 { receipt.cumulative_gas_used } else { let prev_tx_idx = (meta.index - 1) as usize; all_receipts .get(prev_tx_idx) .map(|prev_receipt| receipt.cumulative_gas_used - prev_receipt.cumulative_gas_used) .unwrap_or_default() }; let blob_gas_used = transaction.transaction.blob_gas_used(); // Blob gas price should only be present if the transaction is a blob transaction let blob_gas_price = blob_gas_used.and_then(|_| meta.excess_blob_gas.map(calc_blob_gasprice)); let logs_bloom = receipt.bloom_slow(); // get number of logs in the block let mut num_logs = 0; for prev_receipt in all_receipts.iter().take(meta.index as usize) { num_logs += prev_receipt.logs.len(); } let logs: Vec = receipt .logs .iter() .enumerate() .map(|(tx_log_idx, log)| Log { inner: log.clone(), block_hash: Some(meta.block_hash), block_number: Some(meta.block_number), block_timestamp: Some(meta.timestamp), transaction_hash: Some(meta.tx_hash), transaction_index: Some(meta.index), log_index: Some((num_logs + tx_log_idx) as u64), removed: false, }) .collect(); let rpc_receipt = reth_rpc_types::Receipt { status: receipt.success.into(), cumulative_gas_used: receipt.cumulative_gas_used as u128, logs, }; let (contract_address, to) = match transaction.transaction.kind() { TxKind::Create => (Some(from.create(transaction.transaction.nonce())), None), TxKind::Call(addr) => (None, Some(Address(*addr))), }; #[allow(clippy::needless_update)] let base = TransactionReceipt { inner: AnyReceiptEnvelope { inner: ReceiptWithBloom { receipt: rpc_receipt, logs_bloom }, r#type: transaction.transaction.tx_type().into(), }, transaction_hash: meta.tx_hash, transaction_index: Some(meta.index), block_hash: Some(meta.block_hash), block_number: Some(meta.block_number), from, to, gas_used: gas_used as u128, contract_address, effective_gas_price: transaction.effective_gas_price(meta.base_fee), // TODO pre-byzantium receipts have a post-transaction state root state_root: None, // EIP-4844 fields blob_gas_price, blob_gas_used: blob_gas_used.map(u128::from), authorization_list: transaction.authorization_list().map(|l| l.to_vec()), }; Ok(Self { base, other: Default::default() }) } /// Adds fields to response body. pub fn add_other_fields(mut self, mut fields: OtherFields) -> Self { self.other.append(&mut fields); self } /// Builds a receipt response from the base response body, and any set additional fields. pub fn build(self) -> AnyTransactionReceipt { let Self { base, other } = self; WithOtherFields { inner: base, other } } }