foundry_primitives/network/
receipt.rs1use alloy_network::{AnyReceiptEnvelope, AnyTransactionReceipt, ReceiptResponse};
2use alloy_primitives::{Address, B256, BlockHash, TxHash};
3use alloy_rpc_types::{ConversionError, Log, TransactionReceipt};
4use alloy_serde::WithOtherFields;
5use derive_more::AsRef;
6use serde::{Deserialize, Serialize};
7use tempo_primitives::TEMPO_TX_TYPE_ID;
8
9#[cfg(feature = "optimism")]
10use super::optimism::build_deposit_receipt_envelope;
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 pub fn with_timestamp(
24 inner: TransactionReceipt<FoundryReceiptEnvelope<Log>>,
25 timestamp: u64,
26 ) -> Self {
27 let mut receipt = WithOtherFields::new(inner);
28 receipt
29 .other
30 .insert("blockTimestamp".to_string(), serde_json::to_value(timestamp).unwrap());
31 Self(receipt)
32 }
33
34 pub fn with_fee_payer(mut self, fee_payer: Address) -> Self {
36 self.0.other.insert("feePayer".to_string(), serde_json::to_value(fee_payer).unwrap());
37 self
38 }
39
40 pub fn with_fee_token(mut self, fee_token: Address) -> Self {
42 self.0.other.insert("feeToken".to_string(), serde_json::to_value(fee_token).unwrap());
43 self
44 }
45
46 pub fn block_timestamp(&self) -> Option<u64> {
48 self.0.other.get_deserialized::<u64>("blockTimestamp").transpose().ok().flatten()
49 }
50}
51
52impl ReceiptResponse for FoundryTxReceipt {
53 fn contract_address(&self) -> Option<Address> {
54 self.0.contract_address
55 }
56
57 fn status(&self) -> bool {
58 self.0.inner.status()
59 }
60
61 fn block_hash(&self) -> Option<BlockHash> {
62 self.0.block_hash
63 }
64
65 fn block_number(&self) -> Option<u64> {
66 self.0.block_number
67 }
68
69 fn transaction_hash(&self) -> TxHash {
70 self.0.transaction_hash
71 }
72
73 fn transaction_index(&self) -> Option<u64> {
74 self.0.transaction_index()
75 }
76
77 fn gas_used(&self) -> u64 {
78 self.0.gas_used()
79 }
80
81 fn effective_gas_price(&self) -> u128 {
82 self.0.effective_gas_price()
83 }
84
85 fn blob_gas_used(&self) -> Option<u64> {
86 self.0.blob_gas_used()
87 }
88
89 fn blob_gas_price(&self) -> Option<u128> {
90 self.0.blob_gas_price()
91 }
92
93 fn from(&self) -> Address {
94 self.0.from()
95 }
96
97 fn to(&self) -> Option<Address> {
98 self.0.to()
99 }
100
101 fn cumulative_gas_used(&self) -> u64 {
102 self.0.cumulative_gas_used()
103 }
104
105 fn state_root(&self) -> Option<B256> {
106 self.0.state_root()
107 }
108}
109
110impl TryFrom<AnyTransactionReceipt> for FoundryTxReceipt {
111 type Error = ConversionError;
112
113 fn try_from(receipt: AnyTransactionReceipt) -> Result<Self, Self::Error> {
114 let WithOtherFields {
115 inner:
116 TransactionReceipt {
117 transaction_hash,
118 transaction_index,
119 block_hash,
120 block_number,
121 gas_used,
122 contract_address,
123 effective_gas_price,
124 from,
125 to,
126 blob_gas_price,
127 blob_gas_used,
128 inner: AnyReceiptEnvelope { inner: receipt_with_bloom, r#type },
129 },
130 other,
131 } = receipt.0;
132
133 Ok(Self(WithOtherFields {
134 inner: TransactionReceipt {
135 transaction_hash,
136 transaction_index,
137 block_hash,
138 block_number,
139 gas_used,
140 contract_address,
141 effective_gas_price,
142 from,
143 to,
144 blob_gas_price,
145 blob_gas_used,
146 inner: match r#type {
147 0x00 => FoundryReceiptEnvelope::Legacy(receipt_with_bloom),
148 0x01 => FoundryReceiptEnvelope::Eip2930(receipt_with_bloom),
149 0x02 => FoundryReceiptEnvelope::Eip1559(receipt_with_bloom),
150 0x03 => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom),
151 0x04 => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom),
152 TEMPO_TX_TYPE_ID => FoundryReceiptEnvelope::Tempo(receipt_with_bloom),
153 #[cfg(feature = "optimism")]
154 0x7E => build_deposit_receipt_envelope(receipt_with_bloom, &other),
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}