foundry_primitives/network/
receipt.rs1use alloy_consensus::{Receipt, TxReceipt};
2use alloy_network::{AnyReceiptEnvelope, AnyTransactionReceipt, ReceiptResponse};
3use alloy_primitives::{Address, B256, BlockHash, TxHash, U64};
4use alloy_rpc_types::{ConversionError, Log, TransactionReceipt};
5use alloy_serde::WithOtherFields;
6use derive_more::AsRef;
7use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom};
8use serde::{Deserialize, Serialize};
9use tempo_primitives::TEMPO_TX_TYPE_ID;
10
11use crate::FoundryReceiptEnvelope;
12
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, AsRef)]
14pub struct FoundryTxReceipt(pub WithOtherFields<TransactionReceipt<FoundryReceiptEnvelope<Log>>>);
15
16impl FoundryTxReceipt {
17 pub fn new(inner: TransactionReceipt<FoundryReceiptEnvelope<Log>>) -> Self {
18 Self(WithOtherFields::new(inner))
19 }
20}
21
22impl ReceiptResponse for FoundryTxReceipt {
23 fn contract_address(&self) -> Option<Address> {
24 self.0.contract_address
25 }
26
27 fn status(&self) -> bool {
28 self.0.inner.status()
29 }
30
31 fn block_hash(&self) -> Option<BlockHash> {
32 self.0.block_hash
33 }
34
35 fn block_number(&self) -> Option<u64> {
36 self.0.block_number
37 }
38
39 fn transaction_hash(&self) -> TxHash {
40 self.0.transaction_hash
41 }
42
43 fn transaction_index(&self) -> Option<u64> {
44 self.0.transaction_index()
45 }
46
47 fn gas_used(&self) -> u64 {
48 self.0.gas_used()
49 }
50
51 fn effective_gas_price(&self) -> u128 {
52 self.0.effective_gas_price()
53 }
54
55 fn blob_gas_used(&self) -> Option<u64> {
56 self.0.blob_gas_used()
57 }
58
59 fn blob_gas_price(&self) -> Option<u128> {
60 self.0.blob_gas_price()
61 }
62
63 fn from(&self) -> Address {
64 self.0.from()
65 }
66
67 fn to(&self) -> Option<Address> {
68 self.0.to()
69 }
70
71 fn cumulative_gas_used(&self) -> u64 {
72 self.0.cumulative_gas_used()
73 }
74
75 fn state_root(&self) -> Option<B256> {
76 self.0.state_root()
77 }
78}
79
80impl TryFrom<AnyTransactionReceipt> for FoundryTxReceipt {
81 type Error = ConversionError;
82
83 fn try_from(receipt: AnyTransactionReceipt) -> Result<Self, Self::Error> {
84 let WithOtherFields {
85 inner:
86 TransactionReceipt {
87 transaction_hash,
88 transaction_index,
89 block_hash,
90 block_number,
91 gas_used,
92 contract_address,
93 effective_gas_price,
94 from,
95 to,
96 blob_gas_price,
97 blob_gas_used,
98 inner: AnyReceiptEnvelope { inner: receipt_with_bloom, r#type },
99 },
100 other,
101 } = receipt;
102
103 Ok(Self(WithOtherFields {
104 inner: TransactionReceipt {
105 transaction_hash,
106 transaction_index,
107 block_hash,
108 block_number,
109 gas_used,
110 contract_address,
111 effective_gas_price,
112 from,
113 to,
114 blob_gas_price,
115 blob_gas_used,
116 inner: match r#type {
117 0x00 => FoundryReceiptEnvelope::Legacy(receipt_with_bloom),
118 0x01 => FoundryReceiptEnvelope::Eip2930(receipt_with_bloom),
119 0x02 => FoundryReceiptEnvelope::Eip1559(receipt_with_bloom),
120 0x03 => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom),
121 0x04 => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom),
122 TEMPO_TX_TYPE_ID => FoundryReceiptEnvelope::Tempo(receipt_with_bloom),
123 0x7E => {
124 let deposit_nonce = other
128 .get_deserialized::<U64>("depositNonce")
129 .transpose()
130 .ok()
131 .flatten()
132 .map(|v| v.to::<u64>());
133 let deposit_receipt_version = other
134 .get_deserialized::<U64>("depositReceiptVersion")
135 .transpose()
136 .ok()
137 .flatten()
138 .map(|v| v.to::<u64>());
139
140 FoundryReceiptEnvelope::Deposit(OpDepositReceiptWithBloom {
141 receipt: OpDepositReceipt {
142 inner: Receipt {
143 status: alloy_consensus::Eip658Value::Eip658(
144 receipt_with_bloom.status(),
145 ),
146 cumulative_gas_used: receipt_with_bloom.cumulative_gas_used(),
147 logs: receipt_with_bloom.receipt.logs,
148 },
149 deposit_nonce,
150 deposit_receipt_version,
151 },
152 logs_bloom: receipt_with_bloom.logs_bloom,
153 })
154 }
155 _ => {
156 let tx_type = r#type;
157 return Err(ConversionError::Custom(format!(
158 "Unknown transaction receipt type: 0x{tx_type:02X}"
159 )));
160 }
161 },
162 },
163 other,
164 }))
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
174 fn test_receipt_convert() {
175 let s = r#"{"type":"0x4","status":"0x1","cumulativeGasUsed":"0x903fd1","logs":[{"address":"0x0000d9fcd47bf761e7287d8ee09917d7e2100000","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000234ce51365b9c417171b6dad280f49143e1b0547"],"data":"0x00000000000000000000000000000000000000000000032139b42c3431700000","blockHash":"0xd26b59c1d8b5bfa9362d19eb0da3819dfe0b367987a71f6d30908dd45e0d7a60","blockNumber":"0x159663e","blockTimestamp":"0x68411f7b","transactionHash":"0x17a6af73d1317e69cfc3cac9221bd98261d40f24815850a44dbfbf96652ae52a","transactionIndex":"0x22","logIndex":"0x158","removed":false}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000008100000000000000000000000000000000000000000000000020000200000000000000800000000800000000000000010000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x17a6af73d1317e69cfc3cac9221bd98261d40f24815850a44dbfbf96652ae52a","transactionIndex":"0x22","blockHash":"0xd26b59c1d8b5bfa9362d19eb0da3819dfe0b367987a71f6d30908dd45e0d7a60","blockNumber":"0x159663e","gasUsed":"0x28ee7","effectiveGasPrice":"0x4bf02090","from":"0x234ce51365b9c417171b6dad280f49143e1b0547","to":"0x234ce51365b9c417171b6dad280f49143e1b0547","contractAddress":null}"#;
176 let receipt: AnyTransactionReceipt = serde_json::from_str(s).unwrap();
177 let _converted = FoundryTxReceipt::try_from(receipt).unwrap();
178 }
179}