reth_optimism_flashblocks/payload.rs
1use alloy_consensus::BlockHeader;
2use alloy_eips::eip4895::Withdrawal;
3use alloy_primitives::{bytes, Address, Bloom, Bytes, B256, U256};
4use alloy_rpc_types_engine::PayloadId;
5use derive_more::Deref;
6use reth_node_api::NodePrimitives;
7use reth_optimism_evm::OpNextBlockEnvAttributes;
8use reth_optimism_primitives::OpReceipt;
9use reth_rpc_eth_types::PendingBlock;
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12
13/// Represents a Flashblock, a real-time block-like structure emitted by the Base L2 chain.
14///
15/// A Flashblock provides a snapshot of a block’s effects before finalization,
16/// allowing faster insight into state transitions, balance changes, and logs.
17/// It includes a diff of the block’s execution and associated metadata.
18///
19/// See: [Base Flashblocks Documentation](https://docs.base.org/chain/flashblocks)
20#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
21pub struct FlashBlock {
22 /// The unique payload ID as assigned by the execution engine for this block.
23 pub payload_id: PayloadId,
24 /// A sequential index that identifies the order of this Flashblock.
25 pub index: u64,
26 /// A subset of block header fields.
27 pub base: Option<ExecutionPayloadBaseV1>,
28 /// The execution diff representing state transitions and transactions.
29 pub diff: ExecutionPayloadFlashblockDeltaV1,
30 /// Additional metadata about the block such as receipts and balances.
31 pub metadata: Metadata,
32}
33
34impl FlashBlock {
35 /// Returns the block number of this flashblock.
36 pub const fn block_number(&self) -> u64 {
37 self.metadata.block_number
38 }
39
40 /// Returns the first parent hash of this flashblock.
41 pub fn parent_hash(&self) -> Option<B256> {
42 Some(self.base.as_ref()?.parent_hash)
43 }
44}
45
46/// A trait for decoding flashblocks from bytes.
47pub trait FlashBlockDecoder: Send + 'static {
48 /// Decodes `bytes` into a [`FlashBlock`].
49 fn decode(&self, bytes: bytes::Bytes) -> eyre::Result<FlashBlock>;
50}
51
52/// Default implementation of the decoder.
53impl FlashBlockDecoder for () {
54 fn decode(&self, bytes: bytes::Bytes) -> eyre::Result<FlashBlock> {
55 FlashBlock::decode(bytes)
56 }
57}
58
59/// Provides metadata about the block that may be useful for indexing or analysis.
60#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
61pub struct Metadata {
62 /// The number of the block in the L2 chain.
63 pub block_number: u64,
64 /// A map of addresses to their updated balances after the block execution.
65 /// This represents balance changes due to transactions, rewards, or system transfers.
66 pub new_account_balances: BTreeMap<Address, U256>,
67 /// Execution receipts for all transactions in the block.
68 /// Contains logs, gas usage, and other EVM-level metadata.
69 pub receipts: BTreeMap<B256, OpReceipt>,
70}
71
72/// Represents the base configuration of an execution payload that remains constant
73/// throughout block construction. This includes fundamental block properties like
74/// parent hash, block number, and other header fields that are determined at
75/// block creation and cannot be modified.
76#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Serialize)]
77pub struct ExecutionPayloadBaseV1 {
78 /// Ecotone parent beacon block root
79 pub parent_beacon_block_root: B256,
80 /// The parent hash of the block.
81 pub parent_hash: B256,
82 /// The fee recipient of the block.
83 pub fee_recipient: Address,
84 /// The previous randao of the block.
85 pub prev_randao: B256,
86 /// The block number.
87 #[serde(with = "alloy_serde::quantity")]
88 pub block_number: u64,
89 /// The gas limit of the block.
90 #[serde(with = "alloy_serde::quantity")]
91 pub gas_limit: u64,
92 /// The timestamp of the block.
93 #[serde(with = "alloy_serde::quantity")]
94 pub timestamp: u64,
95 /// The extra data of the block.
96 pub extra_data: Bytes,
97 /// The base fee per gas of the block.
98 pub base_fee_per_gas: U256,
99}
100
101/// Represents the modified portions of an execution payload within a flashblock.
102/// This structure contains only the fields that can be updated during block construction,
103/// such as state root, receipts, logs, and new transactions. Other immutable block fields
104/// like parent hash and block number are excluded since they remain constant throughout
105/// the block's construction.
106#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Serialize)]
107pub struct ExecutionPayloadFlashblockDeltaV1 {
108 /// The state root of the block.
109 pub state_root: B256,
110 /// The receipts root of the block.
111 pub receipts_root: B256,
112 /// The logs bloom of the block.
113 pub logs_bloom: Bloom,
114 /// The gas used of the block.
115 #[serde(with = "alloy_serde::quantity")]
116 pub gas_used: u64,
117 /// The block hash of the block.
118 pub block_hash: B256,
119 /// The transactions of the block.
120 pub transactions: Vec<Bytes>,
121 /// Array of [`Withdrawal`] enabled with V2
122 pub withdrawals: Vec<Withdrawal>,
123 /// The withdrawals root of the block.
124 pub withdrawals_root: B256,
125}
126
127impl From<ExecutionPayloadBaseV1> for OpNextBlockEnvAttributes {
128 fn from(value: ExecutionPayloadBaseV1) -> Self {
129 Self {
130 timestamp: value.timestamp,
131 suggested_fee_recipient: value.fee_recipient,
132 prev_randao: value.prev_randao,
133 gas_limit: value.gas_limit,
134 parent_beacon_block_root: Some(value.parent_beacon_block_root),
135 extra_data: value.extra_data,
136 }
137 }
138}
139
140/// The pending block built with all received Flashblocks alongside the metadata for the last added
141/// Flashblock.
142#[derive(Debug, Clone, Deref)]
143pub struct PendingFlashBlock<N: NodePrimitives> {
144 /// The complete pending block built out of all received Flashblocks.
145 #[deref]
146 pub pending: PendingBlock<N>,
147 /// A sequential index that identifies the last Flashblock added to this block.
148 pub last_flashblock_index: u64,
149 /// The last Flashblock block hash,
150 pub last_flashblock_hash: B256,
151 /// Whether the [`PendingBlock`] has a properly computed stateroot.
152 pub has_computed_state_root: bool,
153}
154
155impl<N: NodePrimitives> PendingFlashBlock<N> {
156 /// Create new pending flashblock.
157 pub const fn new(
158 pending: PendingBlock<N>,
159 last_flashblock_index: u64,
160 last_flashblock_hash: B256,
161 has_computed_state_root: bool,
162 ) -> Self {
163 Self { pending, last_flashblock_index, last_flashblock_hash, has_computed_state_root }
164 }
165
166 /// Returns the properly calculated state root for that block if it was computed.
167 pub fn computed_state_root(&self) -> Option<B256> {
168 self.has_computed_state_root.then_some(self.pending.block().state_root())
169 }
170}