reth_rpc_eth_types/
receipt.rs
1use super::EthResult;
4use alloy_consensus::{transaction::TransactionMeta, ReceiptEnvelope, TxReceipt};
5use alloy_eips::eip7840::BlobParams;
6use alloy_primitives::{Address, TxKind};
7use alloy_rpc_types_eth::{Log, ReceiptWithBloom, TransactionReceipt};
8use reth_ethereum_primitives::{Receipt, TransactionSigned, TxType};
9use reth_primitives_traits::SignedTransaction;
10
11pub fn build_receipt<R, T, E>(
13 transaction: &T,
14 meta: TransactionMeta,
15 receipt: &R,
16 all_receipts: &[R],
17 blob_params: Option<BlobParams>,
18 build_envelope: impl FnOnce(ReceiptWithBloom<alloy_consensus::Receipt<Log>>) -> E,
19) -> EthResult<TransactionReceipt<E>>
20where
21 R: TxReceipt<Log = alloy_primitives::Log>,
22 T: SignedTransaction,
23{
24 let from = transaction.recover_signer_unchecked()?;
27
28 let gas_used = if meta.index == 0 {
30 receipt.cumulative_gas_used()
31 } else {
32 let prev_tx_idx = (meta.index - 1) as usize;
33 all_receipts
34 .get(prev_tx_idx)
35 .map(|prev_receipt| receipt.cumulative_gas_used() - prev_receipt.cumulative_gas_used())
36 .unwrap_or_default()
37 };
38
39 let blob_gas_used = transaction.blob_gas_used();
40 let blob_gas_price =
42 blob_gas_used.and_then(|_| Some(blob_params?.calc_blob_fee(meta.excess_blob_gas?)));
43
44 let logs_bloom = receipt.bloom();
45
46 let mut num_logs = 0;
48 for prev_receipt in all_receipts.iter().take(meta.index as usize) {
49 num_logs += prev_receipt.logs().len();
50 }
51
52 let logs: Vec<Log> = receipt
53 .logs()
54 .iter()
55 .enumerate()
56 .map(|(tx_log_idx, log)| Log {
57 inner: log.clone(),
58 block_hash: Some(meta.block_hash),
59 block_number: Some(meta.block_number),
60 block_timestamp: Some(meta.timestamp),
61 transaction_hash: Some(meta.tx_hash),
62 transaction_index: Some(meta.index),
63 log_index: Some((num_logs + tx_log_idx) as u64),
64 removed: false,
65 })
66 .collect();
67
68 let rpc_receipt = alloy_rpc_types_eth::Receipt {
69 status: receipt.status_or_post_state(),
70 cumulative_gas_used: receipt.cumulative_gas_used(),
71 logs,
72 };
73
74 let (contract_address, to) = match transaction.kind() {
75 TxKind::Create => (Some(from.create(transaction.nonce())), None),
76 TxKind::Call(addr) => (None, Some(Address(*addr))),
77 };
78
79 Ok(TransactionReceipt {
80 inner: build_envelope(ReceiptWithBloom { receipt: rpc_receipt, logs_bloom }),
81 transaction_hash: meta.tx_hash,
82 transaction_index: Some(meta.index),
83 block_hash: Some(meta.block_hash),
84 block_number: Some(meta.block_number),
85 from,
86 to,
87 gas_used,
88 contract_address,
89 effective_gas_price: transaction.effective_gas_price(meta.base_fee),
90 blob_gas_price,
92 blob_gas_used,
93 })
94}
95
96#[derive(Debug)]
98pub struct EthReceiptBuilder {
99 pub base: TransactionReceipt,
101}
102
103impl EthReceiptBuilder {
104 pub fn new(
109 transaction: &TransactionSigned,
110 meta: TransactionMeta,
111 receipt: &Receipt,
112 all_receipts: &[Receipt],
113 blob_params: Option<BlobParams>,
114 ) -> EthResult<Self> {
115 let base = build_receipt(
116 transaction,
117 meta,
118 receipt,
119 all_receipts,
120 blob_params,
121 |receipt_with_bloom| match receipt.tx_type {
122 TxType::Legacy => ReceiptEnvelope::Legacy(receipt_with_bloom),
123 TxType::Eip2930 => ReceiptEnvelope::Eip2930(receipt_with_bloom),
124 TxType::Eip1559 => ReceiptEnvelope::Eip1559(receipt_with_bloom),
125 TxType::Eip4844 => ReceiptEnvelope::Eip4844(receipt_with_bloom),
126 TxType::Eip7702 => ReceiptEnvelope::Eip7702(receipt_with_bloom),
127 #[allow(unreachable_patterns)]
128 _ => unreachable!(),
129 },
130 )?;
131
132 Ok(Self { base })
133 }
134
135 pub fn build(self) -> TransactionReceipt {
137 self.base
138 }
139}