1use alloy_consensus::{
2 Eip658Value, Receipt, ReceiptEnvelope, ReceiptWithBloom, TxReceipt, Typed2718,
3};
4use alloy_network::eip2718::{
5 Decodable2718, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID,
6 Eip2718Error, Encodable2718, LEGACY_TX_TYPE_ID,
7};
8use alloy_primitives::{Bloom, Log, TxHash, logs_bloom};
9use alloy_rlp::{BufMut, Decodable, Encodable, bytes};
10use alloy_rpc_types::{BlockNumHash, trace::otterscan::OtsReceipt};
11#[cfg(feature = "optimism")]
12use op_alloy_consensus::{
13 DEPOSIT_TX_TYPE_ID, OpDepositReceipt, OpDepositReceiptWithBloom, POST_EXEC_TX_TYPE_ID,
14};
15use serde::{Deserialize, Serialize};
16use tempo_primitives::TEMPO_TX_TYPE_ID;
17
18use crate::FoundryTxType;
19
20#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(tag = "type")]
22pub enum FoundryReceiptEnvelope<T = Log> {
23 #[serde(rename = "0x0", alias = "0x00")]
24 Legacy(ReceiptWithBloom<Receipt<T>>),
25 #[serde(rename = "0x1", alias = "0x01")]
26 Eip2930(ReceiptWithBloom<Receipt<T>>),
27 #[serde(rename = "0x2", alias = "0x02")]
28 Eip1559(ReceiptWithBloom<Receipt<T>>),
29 #[serde(rename = "0x3", alias = "0x03")]
30 Eip4844(ReceiptWithBloom<Receipt<T>>),
31 #[serde(rename = "0x4", alias = "0x04")]
32 Eip7702(ReceiptWithBloom<Receipt<T>>),
33 #[cfg(feature = "optimism")]
34 #[serde(rename = "0x7D", alias = "0x7d")]
35 PostExec(ReceiptWithBloom<Receipt<T>>),
36 #[cfg(feature = "optimism")]
37 #[serde(rename = "0x7E", alias = "0x7e")]
38 Deposit(OpDepositReceiptWithBloom<T>),
39 #[serde(rename = "0x76")]
40 Tempo(ReceiptWithBloom<Receipt<T>>),
41}
42
43impl FoundryReceiptEnvelope<alloy_rpc_types::Log> {
44 pub fn from_parts(
46 status: bool,
47 cumulative_gas_used: u64,
48 logs: impl IntoIterator<Item = alloy_rpc_types::Log>,
49 tx_type: FoundryTxType,
50 #[cfg_attr(not(feature = "optimism"), allow(unused_variables))] deposit_nonce: Option<u64>,
51 #[cfg_attr(not(feature = "optimism"), allow(unused_variables))]
52 deposit_receipt_version: Option<u64>,
53 ) -> Self {
54 let logs = logs.into_iter().collect::<Vec<_>>();
55 let logs_bloom = logs_bloom(logs.iter().map(|l| &l.inner));
56 let inner_receipt =
57 Receipt { status: Eip658Value::Eip658(status), cumulative_gas_used, logs };
58 match tx_type {
59 FoundryTxType::Legacy => {
60 Self::Legacy(ReceiptWithBloom { receipt: inner_receipt, logs_bloom })
61 }
62 FoundryTxType::Eip2930 => {
63 Self::Eip2930(ReceiptWithBloom { receipt: inner_receipt, logs_bloom })
64 }
65 FoundryTxType::Eip1559 => {
66 Self::Eip1559(ReceiptWithBloom { receipt: inner_receipt, logs_bloom })
67 }
68 FoundryTxType::Eip4844 => {
69 Self::Eip4844(ReceiptWithBloom { receipt: inner_receipt, logs_bloom })
70 }
71 FoundryTxType::Eip7702 => {
72 Self::Eip7702(ReceiptWithBloom { receipt: inner_receipt, logs_bloom })
73 }
74 #[cfg(feature = "optimism")]
75 FoundryTxType::PostExec => {
76 Self::PostExec(ReceiptWithBloom { receipt: inner_receipt, logs_bloom })
77 }
78 #[cfg(feature = "optimism")]
79 FoundryTxType::Deposit => {
80 let inner = OpDepositReceiptWithBloom {
81 receipt: OpDepositReceipt {
82 inner: inner_receipt,
83 deposit_nonce,
84 deposit_receipt_version,
85 },
86 logs_bloom,
87 };
88 Self::Deposit(inner)
89 }
90 FoundryTxType::Tempo => {
91 Self::Tempo(ReceiptWithBloom { receipt: inner_receipt, logs_bloom })
92 }
93 }
94 }
95}
96
97impl FoundryReceiptEnvelope<Log> {
98 pub fn convert_logs_rpc(
99 self,
100 block_numhash: BlockNumHash,
101 block_timestamp: u64,
102 transaction_hash: TxHash,
103 transaction_index: u64,
104 next_log_index: usize,
105 ) -> FoundryReceiptEnvelope<alloy_rpc_types::Log> {
106 let mut index = 0;
107 self.map_logs(|inner| {
108 let log = alloy_rpc_types::Log {
109 inner,
110 block_hash: Some(block_numhash.hash),
111 block_number: Some(block_numhash.number),
112 block_timestamp: Some(block_timestamp),
113 transaction_hash: Some(transaction_hash),
114 transaction_index: Some(transaction_index),
115 log_index: Some((next_log_index + index) as u64),
116 removed: false,
117 };
118 index += 1;
119 log
120 })
121 }
122}
123
124impl<T> FoundryReceiptEnvelope<T> {
125 #[cfg(feature = "optimism")]
127 pub const fn is_deposit(&self) -> bool {
128 matches!(self, Self::Deposit(_))
129 }
130
131 #[cfg(feature = "optimism")]
133 pub const fn is_post_exec(&self) -> bool {
134 matches!(self, Self::PostExec(_))
135 }
136
137 pub const fn is_tempo(&self) -> bool {
139 matches!(self, Self::Tempo(_))
140 }
141
142 pub const fn tx_type(&self) -> FoundryTxType {
144 match self {
145 Self::Legacy(_) => FoundryTxType::Legacy,
146 Self::Eip2930(_) => FoundryTxType::Eip2930,
147 Self::Eip1559(_) => FoundryTxType::Eip1559,
148 Self::Eip4844(_) => FoundryTxType::Eip4844,
149 Self::Eip7702(_) => FoundryTxType::Eip7702,
150 #[cfg(feature = "optimism")]
151 Self::PostExec(_) => FoundryTxType::PostExec,
152 #[cfg(feature = "optimism")]
153 Self::Deposit(_) => FoundryTxType::Deposit,
154 Self::Tempo(_) => FoundryTxType::Tempo,
155 }
156 }
157
158 pub const fn status(&self) -> bool {
160 self.as_receipt().status.coerce_status()
161 }
162
163 pub const fn cumulative_gas_used(&self) -> u64 {
165 self.as_receipt().cumulative_gas_used
166 }
167
168 pub fn map_logs<U>(self, f: impl FnMut(T) -> U) -> FoundryReceiptEnvelope<U> {
172 match self {
173 Self::Legacy(r) => FoundryReceiptEnvelope::Legacy(r.map_logs(f)),
174 Self::Eip2930(r) => FoundryReceiptEnvelope::Eip2930(r.map_logs(f)),
175 Self::Eip1559(r) => FoundryReceiptEnvelope::Eip1559(r.map_logs(f)),
176 Self::Eip4844(r) => FoundryReceiptEnvelope::Eip4844(r.map_logs(f)),
177 Self::Eip7702(r) => FoundryReceiptEnvelope::Eip7702(r.map_logs(f)),
178 #[cfg(feature = "optimism")]
179 Self::PostExec(r) => FoundryReceiptEnvelope::PostExec(r.map_logs(f)),
180 #[cfg(feature = "optimism")]
181 Self::Deposit(r) => FoundryReceiptEnvelope::Deposit(
182 r.map_receipt(|r: OpDepositReceipt<T>| r.map_logs(f)),
183 ),
184 Self::Tempo(r) => FoundryReceiptEnvelope::Tempo(r.map_logs(f)),
185 }
186 }
187
188 pub fn logs(&self) -> &[T] {
190 &self.as_receipt().logs
191 }
192
193 pub fn into_logs(self) -> Vec<T> {
195 self.into_receipt().logs
196 }
197
198 pub const fn logs_bloom(&self) -> &Bloom {
200 match self {
201 Self::Legacy(t) => &t.logs_bloom,
202 Self::Eip2930(t) => &t.logs_bloom,
203 Self::Eip1559(t) => &t.logs_bloom,
204 Self::Eip4844(t) => &t.logs_bloom,
205 Self::Eip7702(t) => &t.logs_bloom,
206 #[cfg(feature = "optimism")]
207 Self::PostExec(t) => &t.logs_bloom,
208 #[cfg(feature = "optimism")]
209 Self::Deposit(t) => &t.logs_bloom,
210 Self::Tempo(t) => &t.logs_bloom,
211 }
212 }
213
214 pub fn into_receipt(self) -> Receipt<T> {
216 match self {
217 Self::Legacy(t)
218 | Self::Eip2930(t)
219 | Self::Eip1559(t)
220 | Self::Eip4844(t)
221 | Self::Eip7702(t)
222 | Self::Tempo(t) => t.receipt,
223 #[cfg(feature = "optimism")]
224 Self::PostExec(t) => t.receipt,
225 #[cfg(feature = "optimism")]
226 Self::Deposit(t) => t.receipt.into_inner(),
227 }
228 }
229
230 pub const fn as_receipt(&self) -> &Receipt<T> {
232 match self {
233 Self::Legacy(t)
234 | Self::Eip2930(t)
235 | Self::Eip1559(t)
236 | Self::Eip4844(t)
237 | Self::Eip7702(t)
238 | Self::Tempo(t) => &t.receipt,
239 #[cfg(feature = "optimism")]
240 Self::PostExec(t) => &t.receipt,
241 #[cfg(feature = "optimism")]
242 Self::Deposit(t) => &t.receipt.inner,
243 }
244 }
245}
246
247impl<T> TxReceipt for FoundryReceiptEnvelope<T>
248where
249 T: Clone + core::fmt::Debug + PartialEq + Eq + Send + Sync,
250{
251 type Log = T;
252
253 fn status_or_post_state(&self) -> Eip658Value {
254 self.as_receipt().status
255 }
256
257 fn status(&self) -> bool {
258 self.status()
259 }
260
261 fn bloom(&self) -> Bloom {
263 *self.logs_bloom()
264 }
265
266 fn bloom_cheap(&self) -> Option<Bloom> {
267 Some(self.bloom())
268 }
269
270 fn cumulative_gas_used(&self) -> u64 {
272 self.cumulative_gas_used()
273 }
274
275 fn logs(&self) -> &[T] {
277 self.logs()
278 }
279}
280
281impl Encodable for FoundryReceiptEnvelope {
282 fn encode(&self, out: &mut dyn bytes::BufMut) {
283 self.network_encode(out);
284 }
285
286 fn length(&self) -> usize {
287 self.network_len()
288 }
289}
290
291impl Decodable for FoundryReceiptEnvelope {
292 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
293 Self::network_decode(buf).map_err(Into::into)
294 }
295}
296
297impl Typed2718 for FoundryReceiptEnvelope {
298 fn ty(&self) -> u8 {
299 match self {
300 Self::Legacy(_) => LEGACY_TX_TYPE_ID,
301 Self::Eip2930(_) => EIP2930_TX_TYPE_ID,
302 Self::Eip1559(_) => EIP1559_TX_TYPE_ID,
303 Self::Eip4844(_) => EIP4844_TX_TYPE_ID,
304 Self::Eip7702(_) => EIP7702_TX_TYPE_ID,
305 #[cfg(feature = "optimism")]
306 Self::PostExec(_) => POST_EXEC_TX_TYPE_ID,
307 #[cfg(feature = "optimism")]
308 Self::Deposit(_) => DEPOSIT_TX_TYPE_ID,
309 Self::Tempo(_) => TEMPO_TX_TYPE_ID,
310 }
311 }
312}
313
314impl Encodable2718 for FoundryReceiptEnvelope {
315 fn encode_2718_len(&self) -> usize {
316 match self {
317 Self::Legacy(r) => r.length(),
318 Self::Eip2930(r) => 1 + r.length(),
319 Self::Eip1559(r) => 1 + r.length(),
320 Self::Eip4844(r) => 1 + r.length(),
321 Self::Eip7702(r) => 1 + r.length(),
322 #[cfg(feature = "optimism")]
323 Self::PostExec(r) => 1 + r.length(),
324 #[cfg(feature = "optimism")]
325 Self::Deposit(r) => 1 + r.length(),
326 Self::Tempo(r) => 1 + r.length(),
327 }
328 }
329
330 fn encode_2718(&self, out: &mut dyn BufMut) {
331 if let Some(ty) = self.type_flag() {
332 out.put_u8(ty);
333 }
334 match self {
335 Self::Legacy(r)
336 | Self::Eip2930(r)
337 | Self::Eip1559(r)
338 | Self::Eip4844(r)
339 | Self::Eip7702(r)
340 | Self::Tempo(r) => r.encode(out),
341 #[cfg(feature = "optimism")]
342 Self::PostExec(r) => r.encode(out),
343 #[cfg(feature = "optimism")]
344 Self::Deposit(r) => r.encode(out),
345 }
346 }
347}
348
349impl Decodable2718 for FoundryReceiptEnvelope {
350 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result<Self, Eip2718Error> {
351 #[cfg(feature = "optimism")]
352 {
353 if ty == DEPOSIT_TX_TYPE_ID {
354 return Ok(Self::Deposit(OpDepositReceiptWithBloom::decode(buf)?));
355 }
356 if ty == POST_EXEC_TX_TYPE_ID {
357 return Ok(Self::PostExec(ReceiptWithBloom::decode(buf)?));
358 }
359 }
360 if ty == TEMPO_TX_TYPE_ID {
361 return Ok(Self::Tempo(ReceiptWithBloom::decode(buf)?));
362 }
363 match ReceiptEnvelope::typed_decode(ty, buf)? {
364 ReceiptEnvelope::Eip2930(tx) => Ok(Self::Eip2930(tx)),
365 ReceiptEnvelope::Eip1559(tx) => Ok(Self::Eip1559(tx)),
366 ReceiptEnvelope::Eip4844(tx) => Ok(Self::Eip4844(tx)),
367 ReceiptEnvelope::Eip7702(tx) => Ok(Self::Eip7702(tx)),
368 _ => Err(Eip2718Error::RlpError(alloy_rlp::Error::Custom("unexpected tx type"))),
369 }
370 }
371
372 fn fallback_decode(buf: &mut &[u8]) -> Result<Self, Eip2718Error> {
373 match ReceiptEnvelope::fallback_decode(buf)? {
374 ReceiptEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)),
375 _ => Err(Eip2718Error::RlpError(alloy_rlp::Error::Custom("unexpected tx type"))),
376 }
377 }
378}
379
380impl From<FoundryReceiptEnvelope<alloy_rpc_types::Log>> for OtsReceipt {
381 fn from(receipt: FoundryReceiptEnvelope<alloy_rpc_types::Log>) -> Self {
382 Self {
383 status: receipt.status(),
384 cumulative_gas_used: receipt.cumulative_gas_used(),
385 logs: Some(receipt.logs().to_vec()),
386 logs_bloom: Some(receipt.logs_bloom().to_owned()),
387 r#type: receipt.tx_type() as u8,
388 }
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use alloy_primitives::{Address, B256, Bytes, LogData, hex};
396 use std::str::FromStr;
397
398 fn receipt_for(tx_type: FoundryTxType) -> FoundryReceiptEnvelope {
399 FoundryReceiptEnvelope::<alloy_rpc_types::Log>::from_parts(
400 true,
401 0,
402 Vec::new(),
403 tx_type,
404 None,
405 None,
406 )
407 .map_logs(|log| log.inner)
408 }
409
410 #[test]
411 fn rlp_roundtrip() {
412 fn assert_roundtrip(receipt: FoundryReceiptEnvelope) {
413 let mut encoded = Vec::new();
414 receipt.encode(&mut encoded);
415 assert_eq!(encoded.len(), receipt.length());
416
417 let mut encoded = encoded.as_slice();
418 let decoded = FoundryReceiptEnvelope::decode(&mut encoded).unwrap();
419 assert_eq!(decoded, receipt);
420 assert!(encoded.is_empty());
421 }
422
423 for tx_type in [
424 FoundryTxType::Legacy,
425 FoundryTxType::Eip2930,
426 FoundryTxType::Eip1559,
427 FoundryTxType::Eip4844,
428 FoundryTxType::Eip7702,
429 FoundryTxType::Tempo,
430 ] {
431 assert_roundtrip(receipt_for(tx_type));
432 }
433 #[cfg(feature = "optimism")]
434 for tx_type in [FoundryTxType::PostExec, FoundryTxType::Deposit] {
435 assert_roundtrip(receipt_for(tx_type));
436 }
437
438 let logs = vec![Log {
440 address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
441 data: LogData::new_unchecked(
442 vec![B256::repeat_byte(0x22)],
443 Bytes::from_static(&[0x01, 0x02, 0x03]),
444 ),
445 }];
446 let logs_bloom = logs_bloom(&logs);
447 let receipt = Receipt { status: false.into(), cumulative_gas_used: 0x2a, logs };
448 assert_roundtrip(FoundryReceiptEnvelope::Eip1559(ReceiptWithBloom {
449 receipt: receipt.clone(),
450 logs_bloom,
451 }));
452 #[cfg(feature = "optimism")]
455 assert_roundtrip(FoundryReceiptEnvelope::Deposit(OpDepositReceiptWithBloom {
456 receipt: OpDepositReceipt {
457 inner: receipt,
458 deposit_nonce: Some(7),
459 deposit_receipt_version: Some(1),
460 },
461 logs_bloom,
462 }));
463 }
464
465 #[test]
466 fn encode_typed_receipt_uses_rlp_string() {
467 let receipt = receipt_for(FoundryTxType::Eip2930);
468 let mut encoded = Vec::new();
469 receipt.encode(&mut encoded);
470
471 assert_eq!(&encoded[..4], &[0xb9, 0x01, 0x0a, EIP2930_TX_TYPE_ID]);
473 }
474
475 #[test]
476 fn receipt_predicates() {
477 assert!(receipt_for(FoundryTxType::Legacy).is_legacy());
478 assert!(receipt_for(FoundryTxType::Eip2930).is_eip2930());
479 assert!(receipt_for(FoundryTxType::Eip1559).is_eip1559());
480 assert!(receipt_for(FoundryTxType::Eip4844).is_eip4844());
481 assert!(receipt_for(FoundryTxType::Eip7702).is_eip7702());
482 assert!(receipt_for(FoundryTxType::Tempo).is_tempo());
483 assert!(!receipt_for(FoundryTxType::Tempo).is_legacy());
484
485 #[cfg(feature = "optimism")]
486 {
487 assert!(receipt_for(FoundryTxType::Deposit).is_deposit());
488 assert!(receipt_for(FoundryTxType::PostExec).is_post_exec());
489 }
490 }
491
492 #[test]
493 fn encode_legacy_receipt() {
494 let expected = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap();
495
496 let mut data = vec![];
497 let receipt = FoundryReceiptEnvelope::Legacy(ReceiptWithBloom {
498 receipt: Receipt {
499 status: false.into(),
500 cumulative_gas_used: 0x1,
501 logs: vec![Log {
502 address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
503 data: LogData::new_unchecked(
504 vec![
505 B256::from_str(
506 "000000000000000000000000000000000000000000000000000000000000dead",
507 )
508 .unwrap(),
509 B256::from_str(
510 "000000000000000000000000000000000000000000000000000000000000beef",
511 )
512 .unwrap(),
513 ],
514 Bytes::from_str("0100ff").unwrap(),
515 ),
516 }],
517 },
518 logs_bloom: [0; 256].into(),
519 });
520
521 receipt.encode(&mut data);
522
523 assert_eq!(receipt.length(), expected.len());
525 assert_eq!(data, expected);
526 }
527
528 #[test]
529 fn decode_legacy_receipt() {
530 let data = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap();
531
532 let expected = FoundryReceiptEnvelope::Legacy(ReceiptWithBloom {
533 receipt: Receipt {
534 status: false.into(),
535 cumulative_gas_used: 0x1,
536 logs: vec![Log {
537 address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
538 data: LogData::new_unchecked(
539 vec![
540 B256::from_str(
541 "000000000000000000000000000000000000000000000000000000000000dead",
542 )
543 .unwrap(),
544 B256::from_str(
545 "000000000000000000000000000000000000000000000000000000000000beef",
546 )
547 .unwrap(),
548 ],
549 Bytes::from_str("0100ff").unwrap(),
550 ),
551 }],
552 },
553 logs_bloom: [0; 256].into(),
554 });
555
556 let receipt = FoundryReceiptEnvelope::decode(&mut &data[..]).unwrap();
557
558 assert_eq!(receipt, expected);
559 }
560
561 #[test]
562 fn encode_tempo_receipt() {
563 let receipt = FoundryReceiptEnvelope::Tempo(ReceiptWithBloom {
564 receipt: Receipt {
565 status: true.into(),
566 cumulative_gas_used: 157716,
567 logs: vec![Log {
568 address: Address::from_str("20c0000000000000000000000000000000000000").unwrap(),
569 data: LogData::new_unchecked(
570 vec![
571 B256::from_str(
572 "8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
573 )
574 .unwrap(),
575 B256::from_str(
576 "000000000000000000000000566ff0f4a6114f8072ecdc8a7a8a13d8d0c6b45f",
577 )
578 .unwrap(),
579 B256::from_str(
580 "000000000000000000000000dec0000000000000000000000000000000000000",
581 )
582 .unwrap(),
583 ],
584 Bytes::from_str(
585 "0000000000000000000000000000000000000000000000000000000000989680",
586 )
587 .unwrap(),
588 ),
589 }],
590 },
591 logs_bloom: [0; 256].into(),
592 });
593
594 assert_eq!(receipt.tx_type(), FoundryTxType::Tempo);
595 assert_eq!(receipt.ty(), TEMPO_TX_TYPE_ID);
596 assert!(receipt.status());
597 assert_eq!(receipt.cumulative_gas_used(), 157716);
598 assert_eq!(receipt.logs().len(), 1);
599
600 let mut encoded_2718 = Vec::new();
602 receipt.encode_2718(&mut encoded_2718);
603 assert_eq!(encoded_2718[0], TEMPO_TX_TYPE_ID);
604
605 let mut encoded = Vec::new();
607 receipt.encode(&mut encoded);
608 let decoded = FoundryReceiptEnvelope::decode(&mut &encoded[..]).unwrap();
609 assert_eq!(receipt, decoded);
610 }
611
612 #[test]
613 fn decode_tempo_receipt() {
614 let receipt = FoundryReceiptEnvelope::Tempo(ReceiptWithBloom {
615 receipt: Receipt { status: true.into(), cumulative_gas_used: 21000, logs: vec![] },
616 logs_bloom: [0; 256].into(),
617 });
618
619 let mut encoded = Vec::new();
621 receipt.encode_2718(&mut encoded);
622 assert_eq!(encoded[0], TEMPO_TX_TYPE_ID);
623
624 let decoded = FoundryReceiptEnvelope::decode_2718(&mut &encoded[..]).unwrap();
625 assert_eq!(receipt, decoded);
626 }
627
628 #[test]
629 fn tempo_receipt_from_parts() {
630 let receipt = FoundryReceiptEnvelope::<alloy_rpc_types::Log>::from_parts(
631 true,
632 100000,
633 vec![],
634 FoundryTxType::Tempo,
635 None,
636 None,
637 );
638
639 assert_eq!(receipt.tx_type(), FoundryTxType::Tempo);
640 assert!(receipt.status());
641 assert_eq!(receipt.cumulative_gas_used(), 100000);
642 assert!(receipt.logs().is_empty());
643 #[cfg(feature = "optimism")]
644 {
645 assert!(receipt.deposit_nonce().is_none());
646 assert!(receipt.deposit_receipt_version().is_none());
647 }
648 }
649
650 #[test]
651 fn tempo_receipt_map_logs() {
652 let receipt = FoundryReceiptEnvelope::Tempo(ReceiptWithBloom {
653 receipt: Receipt {
654 status: true.into(),
655 cumulative_gas_used: 21000,
656 logs: vec![Log {
657 address: Address::from_str("20c0000000000000000000000000000000000000").unwrap(),
658 data: LogData::new_unchecked(vec![], Bytes::default()),
659 }],
660 },
661 logs_bloom: [0; 256].into(),
662 });
663
664 let mapped = receipt.map_logs(|log| log);
666 assert_eq!(mapped.logs().len(), 1);
667 assert_eq!(mapped.tx_type(), FoundryTxType::Tempo);
668 }
669}