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