1use alloy_consensus::{
2 Eip2718EncodableReceipt, Eip658Value, Receipt, ReceiptWithBloom, RlpDecodableReceipt,
3 RlpEncodableReceipt, TxReceipt, Typed2718,
4};
5use alloy_eips::{eip2718::Eip2718Result, Decodable2718, Encodable2718};
6use alloy_primitives::{Bloom, Log};
7use alloy_rlp::{BufMut, Decodable, Encodable, Header};
8use op_alloy_consensus::{OpDepositReceipt, OpTxType};
9use reth_primitives_traits::InMemorySize;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
16#[cfg_attr(feature = "reth-codec", reth_codecs::add_arbitrary_tests(rlp))]
17pub enum OpReceipt {
18 Legacy(Receipt),
20 Eip2930(Receipt),
22 Eip1559(Receipt),
24 Eip7702(Receipt),
26 Deposit(OpDepositReceipt),
28}
29
30impl OpReceipt {
31 pub const fn tx_type(&self) -> OpTxType {
33 match self {
34 Self::Legacy(_) => OpTxType::Legacy,
35 Self::Eip2930(_) => OpTxType::Eip2930,
36 Self::Eip1559(_) => OpTxType::Eip1559,
37 Self::Eip7702(_) => OpTxType::Eip7702,
38 Self::Deposit(_) => OpTxType::Deposit,
39 }
40 }
41
42 pub const fn as_receipt(&self) -> &Receipt {
44 match self {
45 Self::Legacy(receipt) |
46 Self::Eip2930(receipt) |
47 Self::Eip1559(receipt) |
48 Self::Eip7702(receipt) => receipt,
49 Self::Deposit(receipt) => &receipt.inner,
50 }
51 }
52
53 pub const fn as_receipt_mut(&mut self) -> &mut Receipt {
55 match self {
56 Self::Legacy(receipt) |
57 Self::Eip2930(receipt) |
58 Self::Eip1559(receipt) |
59 Self::Eip7702(receipt) => receipt,
60 Self::Deposit(receipt) => &mut receipt.inner,
61 }
62 }
63
64 pub fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize {
66 match self {
67 Self::Legacy(receipt) |
68 Self::Eip2930(receipt) |
69 Self::Eip1559(receipt) |
70 Self::Eip7702(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom),
71 Self::Deposit(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom),
72 }
73 }
74
75 pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) {
77 match self {
78 Self::Legacy(receipt) |
79 Self::Eip2930(receipt) |
80 Self::Eip1559(receipt) |
81 Self::Eip7702(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out),
82 Self::Deposit(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out),
83 }
84 }
85
86 pub fn rlp_header_inner(&self, bloom: &Bloom) -> Header {
88 Header { list: true, payload_length: self.rlp_encoded_fields_length(bloom) }
89 }
90
91 pub fn rlp_header_inner_without_bloom(&self) -> Header {
93 Header { list: true, payload_length: self.rlp_encoded_fields_length_without_bloom() }
94 }
95
96 pub fn rlp_decode_inner(
99 buf: &mut &[u8],
100 tx_type: OpTxType,
101 ) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
102 match tx_type {
103 OpTxType::Legacy => {
104 let ReceiptWithBloom { receipt, logs_bloom } =
105 RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
106 Ok(ReceiptWithBloom { receipt: Self::Legacy(receipt), logs_bloom })
107 }
108 OpTxType::Eip2930 => {
109 let ReceiptWithBloom { receipt, logs_bloom } =
110 RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
111 Ok(ReceiptWithBloom { receipt: Self::Eip2930(receipt), logs_bloom })
112 }
113 OpTxType::Eip1559 => {
114 let ReceiptWithBloom { receipt, logs_bloom } =
115 RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
116 Ok(ReceiptWithBloom { receipt: Self::Eip1559(receipt), logs_bloom })
117 }
118 OpTxType::Eip7702 => {
119 let ReceiptWithBloom { receipt, logs_bloom } =
120 RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
121 Ok(ReceiptWithBloom { receipt: Self::Eip7702(receipt), logs_bloom })
122 }
123 OpTxType::Deposit => {
124 let ReceiptWithBloom { receipt, logs_bloom } =
125 RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
126 Ok(ReceiptWithBloom { receipt: Self::Deposit(receipt), logs_bloom })
127 }
128 }
129 }
130
131 pub fn rlp_encode_fields_without_bloom(&self, out: &mut dyn BufMut) {
133 match self {
134 Self::Legacy(receipt) |
135 Self::Eip2930(receipt) |
136 Self::Eip1559(receipt) |
137 Self::Eip7702(receipt) => {
138 receipt.status.encode(out);
139 receipt.cumulative_gas_used.encode(out);
140 receipt.logs.encode(out);
141 }
142 Self::Deposit(receipt) => {
143 receipt.inner.status.encode(out);
144 receipt.inner.cumulative_gas_used.encode(out);
145 receipt.inner.logs.encode(out);
146 if let Some(nonce) = receipt.deposit_nonce {
147 nonce.encode(out);
148 }
149 if let Some(version) = receipt.deposit_receipt_version {
150 version.encode(out);
151 }
152 }
153 }
154 }
155
156 pub fn rlp_encoded_fields_length_without_bloom(&self) -> usize {
158 match self {
159 Self::Legacy(receipt) |
160 Self::Eip2930(receipt) |
161 Self::Eip1559(receipt) |
162 Self::Eip7702(receipt) => {
163 receipt.status.length() +
164 receipt.cumulative_gas_used.length() +
165 receipt.logs.length()
166 }
167 Self::Deposit(receipt) => {
168 receipt.inner.status.length() +
169 receipt.inner.cumulative_gas_used.length() +
170 receipt.inner.logs.length() +
171 receipt.deposit_nonce.map_or(0, |nonce| nonce.length()) +
172 receipt.deposit_receipt_version.map_or(0, |version| version.length())
173 }
174 }
175 }
176
177 pub fn rlp_decode_inner_without_bloom(
179 buf: &mut &[u8],
180 tx_type: OpTxType,
181 ) -> alloy_rlp::Result<Self> {
182 let header = Header::decode(buf)?;
183 if !header.list {
184 return Err(alloy_rlp::Error::UnexpectedString);
185 }
186
187 let remaining = buf.len();
188 let status = Decodable::decode(buf)?;
189 let cumulative_gas_used = Decodable::decode(buf)?;
190 let logs = Decodable::decode(buf)?;
191
192 let mut deposit_nonce = None;
193 let mut deposit_receipt_version = None;
194
195 if tx_type == OpTxType::Deposit && buf.len() + header.payload_length > remaining {
197 deposit_nonce = Some(Decodable::decode(buf)?);
198 if buf.len() + header.payload_length > remaining {
199 deposit_receipt_version = Some(Decodable::decode(buf)?);
200 }
201 }
202
203 if buf.len() + header.payload_length != remaining {
204 return Err(alloy_rlp::Error::UnexpectedLength);
205 }
206
207 match tx_type {
208 OpTxType::Legacy => Ok(Self::Legacy(Receipt { status, cumulative_gas_used, logs })),
209 OpTxType::Eip2930 => Ok(Self::Eip2930(Receipt { status, cumulative_gas_used, logs })),
210 OpTxType::Eip1559 => Ok(Self::Eip1559(Receipt { status, cumulative_gas_used, logs })),
211 OpTxType::Eip7702 => Ok(Self::Eip7702(Receipt { status, cumulative_gas_used, logs })),
212 OpTxType::Deposit => Ok(Self::Deposit(OpDepositReceipt {
213 inner: Receipt { status, cumulative_gas_used, logs },
214 deposit_nonce,
215 deposit_receipt_version,
216 })),
217 }
218 }
219}
220
221impl Eip2718EncodableReceipt for OpReceipt {
222 fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
223 !self.tx_type().is_legacy() as usize + self.rlp_header_inner(bloom).length_with_payload()
224 }
225
226 fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
227 if !self.tx_type().is_legacy() {
228 out.put_u8(self.tx_type() as u8);
229 }
230 self.rlp_header_inner(bloom).encode(out);
231 self.rlp_encode_fields(bloom, out);
232 }
233}
234
235impl RlpEncodableReceipt for OpReceipt {
236 fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
237 let mut len = self.eip2718_encoded_length_with_bloom(bloom);
238 if !self.tx_type().is_legacy() {
239 len += Header {
240 list: false,
241 payload_length: self.eip2718_encoded_length_with_bloom(bloom),
242 }
243 .length();
244 }
245
246 len
247 }
248
249 fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
250 if !self.tx_type().is_legacy() {
251 Header { list: false, payload_length: self.eip2718_encoded_length_with_bloom(bloom) }
252 .encode(out);
253 }
254 self.eip2718_encode_with_bloom(bloom, out);
255 }
256}
257
258impl RlpDecodableReceipt for OpReceipt {
259 fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
260 let header_buf = &mut &**buf;
261 let header = Header::decode(header_buf)?;
262
263 if header.list {
265 return Self::rlp_decode_inner(buf, OpTxType::Legacy)
266 }
267
268 *buf = *header_buf;
270
271 let remaining = buf.len();
272 let tx_type = OpTxType::decode(buf)?;
273 let this = Self::rlp_decode_inner(buf, tx_type)?;
274
275 if buf.len() + header.payload_length != remaining {
276 return Err(alloy_rlp::Error::UnexpectedLength);
277 }
278
279 Ok(this)
280 }
281}
282
283impl Encodable2718 for OpReceipt {
284 fn encode_2718_len(&self) -> usize {
285 !self.tx_type().is_legacy() as usize +
286 self.rlp_header_inner_without_bloom().length_with_payload()
287 }
288
289 fn encode_2718(&self, out: &mut dyn BufMut) {
290 if !self.tx_type().is_legacy() {
291 out.put_u8(self.tx_type() as u8);
292 }
293 self.rlp_header_inner_without_bloom().encode(out);
294 self.rlp_encode_fields_without_bloom(out);
295 }
296}
297
298impl Decodable2718 for OpReceipt {
299 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
300 Ok(Self::rlp_decode_inner_without_bloom(buf, OpTxType::try_from(ty)?)?)
301 }
302
303 fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
304 Ok(Self::rlp_decode_inner_without_bloom(buf, OpTxType::Legacy)?)
305 }
306}
307
308impl Encodable for OpReceipt {
309 fn encode(&self, out: &mut dyn BufMut) {
310 self.network_encode(out);
311 }
312
313 fn length(&self) -> usize {
314 self.network_len()
315 }
316}
317
318impl Decodable for OpReceipt {
319 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
320 Ok(Self::network_decode(buf)?)
321 }
322}
323
324impl TxReceipt for OpReceipt {
325 type Log = Log;
326
327 fn status_or_post_state(&self) -> Eip658Value {
328 self.as_receipt().status_or_post_state()
329 }
330
331 fn status(&self) -> bool {
332 self.as_receipt().status()
333 }
334
335 fn bloom(&self) -> Bloom {
336 self.as_receipt().bloom()
337 }
338
339 fn cumulative_gas_used(&self) -> u64 {
340 self.as_receipt().cumulative_gas_used()
341 }
342
343 fn logs(&self) -> &[Log] {
344 self.as_receipt().logs()
345 }
346}
347
348impl Typed2718 for OpReceipt {
349 fn ty(&self) -> u8 {
350 self.tx_type().into()
351 }
352}
353
354impl InMemorySize for OpReceipt {
355 fn size(&self) -> usize {
356 self.as_receipt().size()
357 }
358}
359
360impl reth_primitives_traits::Receipt for OpReceipt {}
361
362pub trait DepositReceipt: reth_primitives_traits::Receipt {
364 fn as_deposit_receipt_mut(&mut self) -> Option<&mut OpDepositReceipt>;
366}
367
368impl DepositReceipt for OpReceipt {
369 fn as_deposit_receipt_mut(&mut self) -> Option<&mut OpDepositReceipt> {
370 match self {
371 Self::Deposit(receipt) => Some(receipt),
372 _ => None,
373 }
374 }
375}
376
377#[cfg(feature = "reth-codec")]
378mod compact {
379 use super::*;
380 use alloc::borrow::Cow;
381 use reth_codecs::Compact;
382
383 #[derive(reth_codecs::CompactZstd)]
384 #[reth_zstd(
385 compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR,
386 decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR
387 )]
388 struct CompactOpReceipt<'a> {
389 tx_type: OpTxType,
390 success: bool,
391 cumulative_gas_used: u64,
392 #[expect(clippy::owned_cow)]
393 logs: Cow<'a, Vec<Log>>,
394 deposit_nonce: Option<u64>,
395 deposit_receipt_version: Option<u64>,
396 }
397
398 impl<'a> From<&'a OpReceipt> for CompactOpReceipt<'a> {
399 fn from(receipt: &'a OpReceipt) -> Self {
400 Self {
401 tx_type: receipt.tx_type(),
402 success: receipt.status(),
403 cumulative_gas_used: receipt.cumulative_gas_used(),
404 logs: Cow::Borrowed(&receipt.as_receipt().logs),
405 deposit_nonce: if let OpReceipt::Deposit(receipt) = receipt {
406 receipt.deposit_nonce
407 } else {
408 None
409 },
410 deposit_receipt_version: if let OpReceipt::Deposit(receipt) = receipt {
411 receipt.deposit_receipt_version
412 } else {
413 None
414 },
415 }
416 }
417 }
418
419 impl From<CompactOpReceipt<'_>> for OpReceipt {
420 fn from(receipt: CompactOpReceipt<'_>) -> Self {
421 let CompactOpReceipt {
422 tx_type,
423 success,
424 cumulative_gas_used,
425 logs,
426 deposit_nonce,
427 deposit_receipt_version,
428 } = receipt;
429
430 let inner =
431 Receipt { status: success.into(), cumulative_gas_used, logs: logs.into_owned() };
432
433 match tx_type {
434 OpTxType::Legacy => Self::Legacy(inner),
435 OpTxType::Eip2930 => Self::Eip2930(inner),
436 OpTxType::Eip1559 => Self::Eip1559(inner),
437 OpTxType::Eip7702 => Self::Eip7702(inner),
438 OpTxType::Deposit => Self::Deposit(OpDepositReceipt {
439 inner,
440 deposit_nonce,
441 deposit_receipt_version,
442 }),
443 }
444 }
445 }
446
447 impl Compact for OpReceipt {
448 fn to_compact<B>(&self, buf: &mut B) -> usize
449 where
450 B: bytes::BufMut + AsMut<[u8]>,
451 {
452 CompactOpReceipt::from(self).to_compact(buf)
453 }
454
455 fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
456 let (receipt, buf) = CompactOpReceipt::from_compact(buf, len);
457 (receipt.into(), buf)
458 }
459 }
460
461 #[cfg(test)]
462 #[test]
463 fn test_ensure_backwards_compatibility() {
464 use reth_codecs::{test_utils::UnusedBits, validate_bitflag_backwards_compat};
465
466 assert_eq!(CompactOpReceipt::bitflag_encoded_bytes(), 2);
467 validate_bitflag_backwards_compat!(CompactOpReceipt<'_>, UnusedBits::NotZero);
468 }
469}
470
471#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
472pub(super) mod serde_bincode_compat {
473 use serde::{Deserialize, Deserializer, Serialize, Serializer};
474 use serde_with::{DeserializeAs, SerializeAs};
475
476 #[derive(Debug, Serialize, Deserialize)]
492 pub enum OpReceipt<'a> {
493 Legacy(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>),
495 Eip2930(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>),
497 Eip1559(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>),
499 Eip7702(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>),
501 Deposit(
503 op_alloy_consensus::serde_bincode_compat::OpDepositReceipt<'a, alloy_primitives::Log>,
504 ),
505 }
506
507 impl<'a> From<&'a super::OpReceipt> for OpReceipt<'a> {
508 fn from(value: &'a super::OpReceipt) -> Self {
509 match value {
510 super::OpReceipt::Legacy(receipt) => Self::Legacy(receipt.into()),
511 super::OpReceipt::Eip2930(receipt) => Self::Eip2930(receipt.into()),
512 super::OpReceipt::Eip1559(receipt) => Self::Eip1559(receipt.into()),
513 super::OpReceipt::Eip7702(receipt) => Self::Eip7702(receipt.into()),
514 super::OpReceipt::Deposit(receipt) => Self::Deposit(receipt.into()),
515 }
516 }
517 }
518
519 impl<'a> From<OpReceipt<'a>> for super::OpReceipt {
520 fn from(value: OpReceipt<'a>) -> Self {
521 match value {
522 OpReceipt::Legacy(receipt) => Self::Legacy(receipt.into()),
523 OpReceipt::Eip2930(receipt) => Self::Eip2930(receipt.into()),
524 OpReceipt::Eip1559(receipt) => Self::Eip1559(receipt.into()),
525 OpReceipt::Eip7702(receipt) => Self::Eip7702(receipt.into()),
526 OpReceipt::Deposit(receipt) => Self::Deposit(receipt.into()),
527 }
528 }
529 }
530
531 impl SerializeAs<super::OpReceipt> for OpReceipt<'_> {
532 fn serialize_as<S>(source: &super::OpReceipt, serializer: S) -> Result<S::Ok, S::Error>
533 where
534 S: Serializer,
535 {
536 OpReceipt::<'_>::from(source).serialize(serializer)
537 }
538 }
539
540 impl<'de> DeserializeAs<'de, super::OpReceipt> for OpReceipt<'de> {
541 fn deserialize_as<D>(deserializer: D) -> Result<super::OpReceipt, D::Error>
542 where
543 D: Deserializer<'de>,
544 {
545 OpReceipt::<'_>::deserialize(deserializer).map(Into::into)
546 }
547 }
548
549 impl reth_primitives_traits::serde_bincode_compat::SerdeBincodeCompat for super::OpReceipt {
550 type BincodeRepr<'a> = OpReceipt<'a>;
551
552 fn as_repr(&self) -> Self::BincodeRepr<'_> {
553 self.into()
554 }
555
556 fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
557 repr.into()
558 }
559 }
560
561 #[cfg(test)]
562 mod tests {
563 use crate::{receipt::serde_bincode_compat, OpReceipt};
564 use arbitrary::Arbitrary;
565 use rand::Rng;
566 use serde::{Deserialize, Serialize};
567 use serde_with::serde_as;
568
569 #[test]
570 fn test_tx_bincode_roundtrip() {
571 #[serde_as]
572 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
573 struct Data {
574 #[serde_as(as = "serde_bincode_compat::OpReceipt<'_>")]
575 reseipt: OpReceipt,
576 }
577
578 let mut bytes = [0u8; 1024];
579 rand::rng().fill(bytes.as_mut_slice());
580 let mut data = Data {
581 reseipt: OpReceipt::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(),
582 };
583 let success = data.reseipt.as_receipt_mut().status.coerce_status();
584 data.reseipt.as_receipt_mut().status = success.into();
586
587 let encoded = bincode::serialize(&data).unwrap();
588 let decoded: Data = bincode::deserialize(&encoded).unwrap();
589 assert_eq!(decoded, data);
590 }
591 }
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597 use alloy_eips::eip2718::Encodable2718;
598 use alloy_primitives::{address, b256, bytes, hex_literal::hex, Bytes};
599 use alloy_rlp::Encodable;
600 use reth_codecs::Compact;
601
602 #[test]
603 fn test_decode_receipt() {
604 reth_codecs::test_utils::test_decode::<OpReceipt>(&hex!(
605 "c30328b52ffd23fc426961a00105007eb0042307705a97e503562eacf2b95060cce9de6de68386b6c155b73a9650021a49e2f8baad17f30faff5899d785c4c0873e45bc268bcf07560106424570d11f9a59e8f3db1efa4ceec680123712275f10d92c3411e1caaa11c7c5d591bc11487168e09934a9986848136da1b583babf3a7188e3aed007a1520f1cf4c1ca7d3482c6c28d37c298613c70a76940008816c4c95644579fd08471dc34732fd0f24"
606 ));
607 }
608
609 #[test]
611 fn encode_legacy_receipt() {
612 let expected = hex!(
613 "f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
614 );
615
616 let mut data = Vec::with_capacity(expected.length());
617 let receipt = ReceiptWithBloom {
618 receipt: OpReceipt::Legacy(Receipt {
619 status: Eip658Value::Eip658(false),
620 cumulative_gas_used: 0x1,
621 logs: vec![Log::new_unchecked(
622 address!("0x0000000000000000000000000000000000000011"),
623 vec![
624 b256!("0x000000000000000000000000000000000000000000000000000000000000dead"),
625 b256!("0x000000000000000000000000000000000000000000000000000000000000beef"),
626 ],
627 bytes!("0100ff"),
628 )],
629 }),
630 logs_bloom: [0; 256].into(),
631 };
632
633 receipt.encode(&mut data);
634
635 assert_eq!(receipt.length(), expected.len());
637 assert_eq!(data, expected);
638 }
639
640 #[test]
642 fn decode_legacy_receipt() {
643 let data = hex!(
644 "f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
645 );
646
647 let expected = ReceiptWithBloom {
649 receipt: OpReceipt::Legacy(Receipt {
650 status: Eip658Value::Eip658(false),
651 cumulative_gas_used: 0x1,
652 logs: vec![Log::new_unchecked(
653 address!("0x0000000000000000000000000000000000000011"),
654 vec![
655 b256!("0x000000000000000000000000000000000000000000000000000000000000dead"),
656 b256!("0x000000000000000000000000000000000000000000000000000000000000beef"),
657 ],
658 bytes!("0100ff"),
659 )],
660 }),
661 logs_bloom: [0; 256].into(),
662 };
663
664 let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
665 assert_eq!(receipt, expected);
666 }
667
668 #[test]
669 fn decode_deposit_receipt_regolith_roundtrip() {
670 let data = hex!(
671 "b901107ef9010c0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf"
672 );
673
674 let expected = ReceiptWithBloom {
676 receipt: OpReceipt::Deposit(OpDepositReceipt {
677 inner: Receipt {
678 status: Eip658Value::Eip658(true),
679 cumulative_gas_used: 46913,
680 logs: vec![],
681 },
682 deposit_nonce: Some(4012991),
683 deposit_receipt_version: None,
684 }),
685 logs_bloom: [0; 256].into(),
686 };
687
688 let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
689 assert_eq!(receipt, expected);
690
691 let mut buf = Vec::with_capacity(data.len());
692 receipt.encode(&mut buf);
693 assert_eq!(buf, &data[..]);
694 }
695
696 #[test]
697 fn decode_deposit_receipt_canyon_roundtrip() {
698 let data = hex!(
699 "b901117ef9010d0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf01"
700 );
701
702 let expected = ReceiptWithBloom {
704 receipt: OpReceipt::Deposit(OpDepositReceipt {
705 inner: Receipt {
706 status: Eip658Value::Eip658(true),
707 cumulative_gas_used: 46913,
708 logs: vec![],
709 },
710 deposit_nonce: Some(4012991),
711 deposit_receipt_version: Some(1),
712 }),
713 logs_bloom: [0; 256].into(),
714 };
715
716 let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
717 assert_eq!(receipt, expected);
718
719 let mut buf = Vec::with_capacity(data.len());
720 expected.encode(&mut buf);
721 assert_eq!(buf, &data[..]);
722 }
723
724 #[test]
725 fn gigantic_receipt() {
726 let receipt = OpReceipt::Legacy(Receipt {
727 status: Eip658Value::Eip658(true),
728 cumulative_gas_used: 16747627,
729 logs: vec![
730 Log::new_unchecked(
731 address!("0x4bf56695415f725e43c3e04354b604bcfb6dfb6e"),
732 vec![b256!(
733 "0xc69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9"
734 )],
735 Bytes::from(vec![1; 0xffffff]),
736 ),
737 Log::new_unchecked(
738 address!("0xfaca325c86bf9c2d5b413cd7b90b209be92229c2"),
739 vec![b256!(
740 "0x8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2"
741 )],
742 Bytes::from(vec![1; 0xffffff]),
743 ),
744 ],
745 });
746
747 let mut data = vec![];
748 receipt.to_compact(&mut data);
749 let (decoded, _) = OpReceipt::from_compact(&data[..], data.len());
750 assert_eq!(decoded, receipt);
751 }
752
753 #[test]
754 fn test_encode_2718_length() {
755 let receipt = ReceiptWithBloom {
756 receipt: OpReceipt::Eip1559(Receipt {
757 status: Eip658Value::Eip658(true),
758 cumulative_gas_used: 21000,
759 logs: vec![],
760 }),
761 logs_bloom: Bloom::default(),
762 };
763
764 let encoded = receipt.encoded_2718();
765 assert_eq!(
766 encoded.len(),
767 receipt.encode_2718_len(),
768 "Encoded length should match the actual encoded data length"
769 );
770
771 let legacy_receipt = ReceiptWithBloom {
773 receipt: OpReceipt::Legacy(Receipt {
774 status: Eip658Value::Eip658(true),
775 cumulative_gas_used: 21000,
776 logs: vec![],
777 }),
778 logs_bloom: Bloom::default(),
779 };
780
781 let legacy_encoded = legacy_receipt.encoded_2718();
782 assert_eq!(
783 legacy_encoded.len(),
784 legacy_receipt.encode_2718_len(),
785 "Encoded length for legacy receipt should match the actual encoded data length"
786 );
787 }
788}