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};
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 logs = self
107 .logs()
108 .iter()
109 .enumerate()
110 .map(|(index, log)| alloy_rpc_types::Log {
111 inner: log.clone(),
112 block_hash: Some(block_numhash.hash),
113 block_number: Some(block_numhash.number),
114 block_timestamp: Some(block_timestamp),
115 transaction_hash: Some(transaction_hash),
116 transaction_index: Some(transaction_index),
117 log_index: Some((next_log_index + index) as u64),
118 removed: false,
119 })
120 .collect::<Vec<_>>();
121 #[cfg(feature = "optimism")]
122 let (deposit_nonce, deposit_receipt_version) =
123 (self.deposit_nonce(), self.deposit_receipt_version());
124 #[cfg(not(feature = "optimism"))]
125 let (deposit_nonce, deposit_receipt_version) = (None, None);
126 FoundryReceiptEnvelope::<alloy_rpc_types::Log>::from_parts(
127 self.status(),
128 self.cumulative_gas_used(),
129 logs,
130 self.tx_type(),
131 deposit_nonce,
132 deposit_receipt_version,
133 )
134 }
135}
136
137impl<T> FoundryReceiptEnvelope<T> {
138 pub const fn tx_type(&self) -> FoundryTxType {
140 match self {
141 Self::Legacy(_) => FoundryTxType::Legacy,
142 Self::Eip2930(_) => FoundryTxType::Eip2930,
143 Self::Eip1559(_) => FoundryTxType::Eip1559,
144 Self::Eip4844(_) => FoundryTxType::Eip4844,
145 Self::Eip7702(_) => FoundryTxType::Eip7702,
146 #[cfg(feature = "optimism")]
147 Self::PostExec(_) => FoundryTxType::PostExec,
148 #[cfg(feature = "optimism")]
149 Self::Deposit(_) => FoundryTxType::Deposit,
150 Self::Tempo(_) => FoundryTxType::Tempo,
151 }
152 }
153
154 pub const fn status(&self) -> bool {
156 self.as_receipt().status.coerce_status()
157 }
158
159 pub const fn cumulative_gas_used(&self) -> u64 {
161 self.as_receipt().cumulative_gas_used
162 }
163
164 pub fn map_logs<U>(self, f: impl FnMut(T) -> U) -> FoundryReceiptEnvelope<U> {
168 match self {
169 Self::Legacy(r) => FoundryReceiptEnvelope::Legacy(r.map_logs(f)),
170 Self::Eip2930(r) => FoundryReceiptEnvelope::Eip2930(r.map_logs(f)),
171 Self::Eip1559(r) => FoundryReceiptEnvelope::Eip1559(r.map_logs(f)),
172 Self::Eip4844(r) => FoundryReceiptEnvelope::Eip4844(r.map_logs(f)),
173 Self::Eip7702(r) => FoundryReceiptEnvelope::Eip7702(r.map_logs(f)),
174 #[cfg(feature = "optimism")]
175 Self::PostExec(r) => FoundryReceiptEnvelope::PostExec(r.map_logs(f)),
176 #[cfg(feature = "optimism")]
177 Self::Deposit(r) => FoundryReceiptEnvelope::Deposit(
178 r.map_receipt(|r: OpDepositReceipt<T>| r.map_logs(f)),
179 ),
180 Self::Tempo(r) => FoundryReceiptEnvelope::Tempo(r.map_logs(f)),
181 }
182 }
183
184 pub fn logs(&self) -> &[T] {
186 &self.as_receipt().logs
187 }
188
189 pub fn into_logs(self) -> Vec<T> {
191 self.into_receipt().logs
192 }
193
194 pub const fn logs_bloom(&self) -> &Bloom {
196 match self {
197 Self::Legacy(t) => &t.logs_bloom,
198 Self::Eip2930(t) => &t.logs_bloom,
199 Self::Eip1559(t) => &t.logs_bloom,
200 Self::Eip4844(t) => &t.logs_bloom,
201 Self::Eip7702(t) => &t.logs_bloom,
202 #[cfg(feature = "optimism")]
203 Self::PostExec(t) => &t.logs_bloom,
204 #[cfg(feature = "optimism")]
205 Self::Deposit(t) => &t.logs_bloom,
206 Self::Tempo(t) => &t.logs_bloom,
207 }
208 }
209
210 pub fn into_receipt(self) -> Receipt<T> {
212 match self {
213 Self::Legacy(t)
214 | Self::Eip2930(t)
215 | Self::Eip1559(t)
216 | Self::Eip4844(t)
217 | Self::Eip7702(t)
218 | Self::Tempo(t) => t.receipt,
219 #[cfg(feature = "optimism")]
220 Self::PostExec(t) => t.receipt,
221 #[cfg(feature = "optimism")]
222 Self::Deposit(t) => t.receipt.into_inner(),
223 }
224 }
225
226 pub const fn as_receipt(&self) -> &Receipt<T> {
228 match self {
229 Self::Legacy(t)
230 | Self::Eip2930(t)
231 | Self::Eip1559(t)
232 | Self::Eip4844(t)
233 | Self::Eip7702(t)
234 | Self::Tempo(t) => &t.receipt,
235 #[cfg(feature = "optimism")]
236 Self::PostExec(t) => &t.receipt,
237 #[cfg(feature = "optimism")]
238 Self::Deposit(t) => &t.receipt.inner,
239 }
240 }
241}
242
243impl<T> TxReceipt for FoundryReceiptEnvelope<T>
244where
245 T: Clone + core::fmt::Debug + PartialEq + Eq + Send + Sync,
246{
247 type Log = T;
248
249 fn status_or_post_state(&self) -> Eip658Value {
250 self.as_receipt().status
251 }
252
253 fn status(&self) -> bool {
254 self.status()
255 }
256
257 fn bloom(&self) -> Bloom {
259 *self.logs_bloom()
260 }
261
262 fn bloom_cheap(&self) -> Option<Bloom> {
263 Some(self.bloom())
264 }
265
266 fn cumulative_gas_used(&self) -> u64 {
268 self.cumulative_gas_used()
269 }
270
271 fn logs(&self) -> &[T] {
273 self.logs()
274 }
275}
276
277impl Encodable for FoundryReceiptEnvelope {
278 fn encode(&self, out: &mut dyn bytes::BufMut) {
279 match self {
280 Self::Legacy(r) => r.encode(out),
281 receipt => {
282 let payload_len = match receipt {
283 Self::Eip2930(r) => r.length() + 1,
284 Self::Eip1559(r) => r.length() + 1,
285 Self::Eip4844(r) => r.length() + 1,
286 Self::Eip7702(r) => r.length() + 1,
287 #[cfg(feature = "optimism")]
288 Self::PostExec(r) => r.length() + 1,
289 #[cfg(feature = "optimism")]
290 Self::Deposit(r) => r.length() + 1,
291 Self::Tempo(r) => r.length() + 1,
292 _ => unreachable!("receipt already matched"),
293 };
294
295 match receipt {
296 Self::Eip2930(r) => {
297 Header { list: true, payload_length: payload_len }.encode(out);
298 EIP2930_TX_TYPE_ID.encode(out);
299 r.encode(out);
300 }
301 Self::Eip1559(r) => {
302 Header { list: true, payload_length: payload_len }.encode(out);
303 EIP1559_TX_TYPE_ID.encode(out);
304 r.encode(out);
305 }
306 Self::Eip4844(r) => {
307 Header { list: true, payload_length: payload_len }.encode(out);
308 EIP4844_TX_TYPE_ID.encode(out);
309 r.encode(out);
310 }
311 Self::Eip7702(r) => {
312 Header { list: true, payload_length: payload_len }.encode(out);
313 EIP7702_TX_TYPE_ID.encode(out);
314 r.encode(out);
315 }
316 #[cfg(feature = "optimism")]
317 Self::PostExec(r) => {
318 Header { list: true, payload_length: payload_len }.encode(out);
319 POST_EXEC_TX_TYPE_ID.encode(out);
320 r.encode(out);
321 }
322 #[cfg(feature = "optimism")]
323 Self::Deposit(r) => {
324 Header { list: true, payload_length: payload_len }.encode(out);
325 DEPOSIT_TX_TYPE_ID.encode(out);
326 r.encode(out);
327 }
328 Self::Tempo(r) => {
329 Header { list: true, payload_length: payload_len }.encode(out);
330 TEMPO_TX_TYPE_ID.encode(out);
331 r.encode(out);
332 }
333 _ => unreachable!("receipt already matched"),
334 }
335 }
336 }
337 }
338}
339
340impl Decodable for FoundryReceiptEnvelope {
341 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
342 use bytes::Buf;
343 use std::cmp::Ordering;
344
345 let rlp_type = *buf
349 .first()
350 .ok_or(alloy_rlp::Error::Custom("cannot decode a receipt from empty bytes"))?;
351
352 match rlp_type.cmp(&alloy_rlp::EMPTY_LIST_CODE) {
353 Ordering::Less => {
354 let _header = Header::decode(buf)?;
356 let receipt_type = *buf.first().ok_or(alloy_rlp::Error::Custom(
357 "typed receipt cannot be decoded from an empty slice",
358 ))?;
359 if receipt_type == EIP2930_TX_TYPE_ID {
360 buf.advance(1);
361 <ReceiptWithBloom as Decodable>::decode(buf)
362 .map(FoundryReceiptEnvelope::Eip2930)
363 } else if receipt_type == EIP1559_TX_TYPE_ID {
364 buf.advance(1);
365 <ReceiptWithBloom as Decodable>::decode(buf)
366 .map(FoundryReceiptEnvelope::Eip1559)
367 } else if receipt_type == EIP4844_TX_TYPE_ID {
368 buf.advance(1);
369 <ReceiptWithBloom as Decodable>::decode(buf)
370 .map(FoundryReceiptEnvelope::Eip4844)
371 } else if receipt_type == EIP7702_TX_TYPE_ID {
372 buf.advance(1);
373 <ReceiptWithBloom as Decodable>::decode(buf)
374 .map(FoundryReceiptEnvelope::Eip7702)
375 } else if receipt_type == TEMPO_TX_TYPE_ID {
376 buf.advance(1);
377 <ReceiptWithBloom as Decodable>::decode(buf).map(FoundryReceiptEnvelope::Tempo)
378 } else {
379 #[cfg(feature = "optimism")]
380 {
381 if receipt_type == POST_EXEC_TX_TYPE_ID {
382 buf.advance(1);
383 return <ReceiptWithBloom as Decodable>::decode(buf)
384 .map(FoundryReceiptEnvelope::PostExec);
385 }
386 if receipt_type == DEPOSIT_TX_TYPE_ID {
387 buf.advance(1);
388 return <OpDepositReceiptWithBloom as Decodable>::decode(buf)
389 .map(FoundryReceiptEnvelope::Deposit);
390 }
391 }
392 Err(alloy_rlp::Error::Custom("invalid receipt type"))
393 }
394 }
395 Ordering::Equal => {
396 Err(alloy_rlp::Error::Custom("an empty list is not a valid receipt encoding"))
397 }
398 Ordering::Greater => {
399 <ReceiptWithBloom as Decodable>::decode(buf).map(FoundryReceiptEnvelope::Legacy)
400 }
401 }
402 }
403}
404
405impl Typed2718 for FoundryReceiptEnvelope {
406 fn ty(&self) -> u8 {
407 match self {
408 Self::Legacy(_) => LEGACY_TX_TYPE_ID,
409 Self::Eip2930(_) => EIP2930_TX_TYPE_ID,
410 Self::Eip1559(_) => EIP1559_TX_TYPE_ID,
411 Self::Eip4844(_) => EIP4844_TX_TYPE_ID,
412 Self::Eip7702(_) => EIP7702_TX_TYPE_ID,
413 #[cfg(feature = "optimism")]
414 Self::PostExec(_) => POST_EXEC_TX_TYPE_ID,
415 #[cfg(feature = "optimism")]
416 Self::Deposit(_) => DEPOSIT_TX_TYPE_ID,
417 Self::Tempo(_) => TEMPO_TX_TYPE_ID,
418 }
419 }
420}
421
422impl Encodable2718 for FoundryReceiptEnvelope {
423 fn encode_2718_len(&self) -> usize {
424 match self {
425 Self::Legacy(r) => r.length(),
426 Self::Eip2930(r) => 1 + r.length(),
427 Self::Eip1559(r) => 1 + r.length(),
428 Self::Eip4844(r) => 1 + r.length(),
429 Self::Eip7702(r) => 1 + r.length(),
430 #[cfg(feature = "optimism")]
431 Self::PostExec(r) => 1 + r.length(),
432 #[cfg(feature = "optimism")]
433 Self::Deposit(r) => 1 + r.length(),
434 Self::Tempo(r) => 1 + r.length(),
435 }
436 }
437
438 fn encode_2718(&self, out: &mut dyn BufMut) {
439 if let Some(ty) = self.type_flag() {
440 out.put_u8(ty);
441 }
442 match self {
443 Self::Legacy(r)
444 | Self::Eip2930(r)
445 | Self::Eip1559(r)
446 | Self::Eip4844(r)
447 | Self::Eip7702(r)
448 | Self::Tempo(r) => r.encode(out),
449 #[cfg(feature = "optimism")]
450 Self::PostExec(r) => r.encode(out),
451 #[cfg(feature = "optimism")]
452 Self::Deposit(r) => r.encode(out),
453 }
454 }
455}
456
457impl Decodable2718 for FoundryReceiptEnvelope {
458 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result<Self, Eip2718Error> {
459 #[cfg(feature = "optimism")]
460 {
461 if ty == DEPOSIT_TX_TYPE_ID {
462 return Ok(Self::Deposit(OpDepositReceiptWithBloom::decode(buf)?));
463 }
464 if ty == POST_EXEC_TX_TYPE_ID {
465 return Ok(Self::PostExec(ReceiptWithBloom::decode(buf)?));
466 }
467 }
468 if ty == TEMPO_TX_TYPE_ID {
469 return Ok(Self::Tempo(ReceiptWithBloom::decode(buf)?));
470 }
471 match ReceiptEnvelope::typed_decode(ty, buf)? {
472 ReceiptEnvelope::Eip2930(tx) => Ok(Self::Eip2930(tx)),
473 ReceiptEnvelope::Eip1559(tx) => Ok(Self::Eip1559(tx)),
474 ReceiptEnvelope::Eip4844(tx) => Ok(Self::Eip4844(tx)),
475 ReceiptEnvelope::Eip7702(tx) => Ok(Self::Eip7702(tx)),
476 _ => Err(Eip2718Error::RlpError(alloy_rlp::Error::Custom("unexpected tx type"))),
477 }
478 }
479
480 fn fallback_decode(buf: &mut &[u8]) -> Result<Self, Eip2718Error> {
481 match ReceiptEnvelope::fallback_decode(buf)? {
482 ReceiptEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)),
483 _ => Err(Eip2718Error::RlpError(alloy_rlp::Error::Custom("unexpected tx type"))),
484 }
485 }
486}
487
488impl From<FoundryReceiptEnvelope<alloy_rpc_types::Log>> for OtsReceipt {
489 fn from(receipt: FoundryReceiptEnvelope<alloy_rpc_types::Log>) -> Self {
490 Self {
491 status: receipt.status(),
492 cumulative_gas_used: receipt.cumulative_gas_used(),
493 logs: Some(receipt.logs().to_vec()),
494 logs_bloom: Some(receipt.logs_bloom().to_owned()),
495 r#type: receipt.tx_type() as u8,
496 }
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503 use alloy_primitives::{Address, B256, Bytes, LogData, hex};
504 use std::str::FromStr;
505
506 #[test]
507 fn encode_legacy_receipt() {
508 let expected = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap();
509
510 let mut data = vec![];
511 let receipt = FoundryReceiptEnvelope::Legacy(ReceiptWithBloom {
512 receipt: Receipt {
513 status: false.into(),
514 cumulative_gas_used: 0x1,
515 logs: vec![Log {
516 address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
517 data: LogData::new_unchecked(
518 vec![
519 B256::from_str(
520 "000000000000000000000000000000000000000000000000000000000000dead",
521 )
522 .unwrap(),
523 B256::from_str(
524 "000000000000000000000000000000000000000000000000000000000000beef",
525 )
526 .unwrap(),
527 ],
528 Bytes::from_str("0100ff").unwrap(),
529 ),
530 }],
531 },
532 logs_bloom: [0; 256].into(),
533 });
534
535 receipt.encode(&mut data);
536
537 assert_eq!(receipt.length(), expected.len());
539 assert_eq!(data, expected);
540 }
541
542 #[test]
543 fn decode_legacy_receipt() {
544 let data = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap();
545
546 let expected = FoundryReceiptEnvelope::Legacy(ReceiptWithBloom {
547 receipt: Receipt {
548 status: false.into(),
549 cumulative_gas_used: 0x1,
550 logs: vec![Log {
551 address: Address::from_str("0000000000000000000000000000000000000011").unwrap(),
552 data: LogData::new_unchecked(
553 vec![
554 B256::from_str(
555 "000000000000000000000000000000000000000000000000000000000000dead",
556 )
557 .unwrap(),
558 B256::from_str(
559 "000000000000000000000000000000000000000000000000000000000000beef",
560 )
561 .unwrap(),
562 ],
563 Bytes::from_str("0100ff").unwrap(),
564 ),
565 }],
566 },
567 logs_bloom: [0; 256].into(),
568 });
569
570 let receipt = FoundryReceiptEnvelope::decode(&mut &data[..]).unwrap();
571
572 assert_eq!(receipt, expected);
573 }
574
575 #[test]
576 fn encode_tempo_receipt() {
577 use alloy_network::eip2718::Encodable2718;
578 use tempo_primitives::TEMPO_TX_TYPE_ID;
579
580 let receipt = FoundryReceiptEnvelope::Tempo(ReceiptWithBloom {
581 receipt: Receipt {
582 status: true.into(),
583 cumulative_gas_used: 157716,
584 logs: vec![Log {
585 address: Address::from_str("20c0000000000000000000000000000000000000").unwrap(),
586 data: LogData::new_unchecked(
587 vec![
588 B256::from_str(
589 "8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
590 )
591 .unwrap(),
592 B256::from_str(
593 "000000000000000000000000566ff0f4a6114f8072ecdc8a7a8a13d8d0c6b45f",
594 )
595 .unwrap(),
596 B256::from_str(
597 "000000000000000000000000dec0000000000000000000000000000000000000",
598 )
599 .unwrap(),
600 ],
601 Bytes::from_str(
602 "0000000000000000000000000000000000000000000000000000000000989680",
603 )
604 .unwrap(),
605 ),
606 }],
607 },
608 logs_bloom: [0; 256].into(),
609 });
610
611 assert_eq!(receipt.tx_type(), FoundryTxType::Tempo);
612 assert_eq!(receipt.ty(), TEMPO_TX_TYPE_ID);
613 assert!(receipt.status());
614 assert_eq!(receipt.cumulative_gas_used(), 157716);
615 assert_eq!(receipt.logs().len(), 1);
616
617 let mut encoded = Vec::new();
619 receipt.encode_2718(&mut encoded);
620
621 assert_eq!(encoded[0], TEMPO_TX_TYPE_ID);
623
624 let decoded = FoundryReceiptEnvelope::decode(&mut &encoded[..]).unwrap();
626 assert_eq!(receipt, decoded);
627 }
628
629 #[test]
630 fn decode_tempo_receipt() {
631 use alloy_network::eip2718::Encodable2718;
632 use tempo_primitives::TEMPO_TX_TYPE_ID;
633
634 let receipt = FoundryReceiptEnvelope::Tempo(ReceiptWithBloom {
635 receipt: Receipt { status: true.into(), cumulative_gas_used: 21000, logs: vec![] },
636 logs_bloom: [0; 256].into(),
637 });
638
639 let mut encoded = Vec::new();
641 receipt.encode_2718(&mut encoded);
642 assert_eq!(encoded[0], TEMPO_TX_TYPE_ID);
643
644 use alloy_network::eip2718::Decodable2718;
645 let decoded = FoundryReceiptEnvelope::decode_2718(&mut &encoded[..]).unwrap();
646 assert_eq!(receipt, decoded);
647 }
648
649 #[test]
650 fn tempo_receipt_from_parts() {
651 let receipt = FoundryReceiptEnvelope::<alloy_rpc_types::Log>::from_parts(
652 true,
653 100000,
654 vec![],
655 FoundryTxType::Tempo,
656 None,
657 None,
658 );
659
660 assert_eq!(receipt.tx_type(), FoundryTxType::Tempo);
661 assert!(receipt.status());
662 assert_eq!(receipt.cumulative_gas_used(), 100000);
663 assert!(receipt.logs().is_empty());
664 #[cfg(feature = "optimism")]
665 {
666 assert!(receipt.deposit_nonce().is_none());
667 assert!(receipt.deposit_receipt_version().is_none());
668 }
669 }
670
671 #[test]
672 fn tempo_receipt_map_logs() {
673 let receipt = FoundryReceiptEnvelope::Tempo(ReceiptWithBloom {
674 receipt: Receipt {
675 status: true.into(),
676 cumulative_gas_used: 21000,
677 logs: vec![Log {
678 address: Address::from_str("20c0000000000000000000000000000000000000").unwrap(),
679 data: LogData::new_unchecked(vec![], Bytes::default()),
680 }],
681 },
682 logs_bloom: [0; 256].into(),
683 });
684
685 let mapped = receipt.map_logs(|log| log);
687 assert_eq!(mapped.logs().len(), 1);
688 assert_eq!(mapped.tx_type(), FoundryTxType::Tempo);
689 }
690}