Skip to main content

alloy_eip8141/
receipt.rs

1use alloc::vec::Vec;
2
3use alloy_primitives::Address;
4use alloy_rlp::{RlpDecodable, RlpEncodable};
5
6/// EIP-8141 top-level frame status code.
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
8#[repr(u8)]
9#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
10#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
11#[cfg_attr(feature = "borsh", borsh(use_discriminant = true))]
12pub enum FrameStatus {
13    /// Frame reverted or otherwise failed.
14    #[default]
15    Failure = 0x00,
16    /// Frame completed successfully.
17    Success = 0x01,
18    /// Frame was skipped because an atomic batch failed.
19    SkippedAtomicBatch = 0x02,
20}
21
22impl FrameStatus {
23    /// Returns true if this is [`Self::Failure`].
24    pub const fn is_failure(self) -> bool {
25        matches!(self, Self::Failure)
26    }
27
28    /// Returns true if this is [`Self::Success`].
29    pub const fn is_success(self) -> bool {
30        matches!(self, Self::Success)
31    }
32
33    /// Returns true if this is [`Self::SkippedAtomicBatch`].
34    pub const fn is_skipped_atomic_batch(self) -> bool {
35        matches!(self, Self::SkippedAtomicBatch)
36    }
37
38    /// Attempts to convert a raw status byte into a [`FrameStatus`].
39    pub const fn try_from_u8(value: u8) -> Option<Self> {
40        match value {
41            0x00 => Some(Self::Failure),
42            0x01 => Some(Self::Success),
43            0x02 => Some(Self::SkippedAtomicBatch),
44            _ => None,
45        }
46    }
47}
48
49impl_u8_discriminant!(FrameStatus, InvalidStatus, "invalid EIP-8141 frame status");
50
51/// Gas used by a frame, reported independently for each gas dimension.
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)]
53#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
56#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
57pub struct FrameGasUsed {
58    /// Execution gas used by the frame, before transaction-level refunds.
59    #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::quantity"))]
60    pub execution: u64,
61    /// State gas attributed to the frame after refills and rollbacks.
62    #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::quantity"))]
63    pub state: u64,
64}
65
66/// Receipt information for a single frame.
67///
68/// The `serde` representation mirrors the consensus encoding and nests `gasUsed` as an object
69/// with `execution` and `state` fields. It is not the JSON-RPC receipt shape.
70#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)]
71#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
74#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
75pub struct FrameReceipt<Log = alloy_primitives::Log> {
76    /// Top-level frame status code.
77    pub status: FrameStatus,
78    /// Gas used by this frame in the execution and state dimensions.
79    pub gas_used: FrameGasUsed,
80    /// Logs emitted by this frame.
81    pub logs: Vec<Log>,
82}
83
84/// EIP-8141 receipt payload.
85#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)]
86#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
89#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
90pub struct FrameReceiptPayload<Log = alloy_primitives::Log> {
91    /// Cumulative gas used by the block after this transaction.
92    #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::quantity"))]
93    pub cumulative_gas_used: u64,
94    /// Account that paid the transaction fee.
95    pub payer: Address,
96    /// Per-frame receipt entries.
97    pub frame_receipts: Vec<FrameReceipt<Log>>,
98}
99
100impl<Log> FrameReceiptPayload<Log> {
101    /// Maps the log type in every frame receipt.
102    pub fn map_logs<U>(self, mut f: impl FnMut(Log) -> U) -> FrameReceiptPayload<U> {
103        FrameReceiptPayload {
104            cumulative_gas_used: self.cumulative_gas_used,
105            payer: self.payer,
106            frame_receipts: self
107                .frame_receipts
108                .into_iter()
109                .map(|receipt| FrameReceipt {
110                    status: receipt.status,
111                    gas_used: receipt.gas_used,
112                    logs: receipt.logs.into_iter().map(&mut f).collect(),
113                })
114                .collect(),
115        }
116    }
117}