Skip to main content

bal_codec/
schema.rs

1//! Wire structures. This file — and only this file — mirrors the EIP text.
2//! A spec change lands here as field edits; derive macros regenerate RLP.
3
4use alloy_primitives::{Address, Bytes, U256};
5use alloy_rlp::{RlpDecodable, RlpEncodable};
6
7/// Position of a change in the block's life cycle.
8/// `0` = pre-execution system calls, `1..=n` = transactions, `n+1` = post-execution.
9pub type BlockAccessIndex = u32;
10
11/// One write to a storage slot.
12#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct StorageChange {
15    /// Where in the block the write happened.
16    pub block_access_index: BlockAccessIndex,
17    /// Post-value. There are no pre-values anywhere in a BAL.
18    pub value: U256,
19}
20
21/// One balance change.
22#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct BalanceChange {
25    /// Where in the block the change happened.
26    pub block_access_index: BlockAccessIndex,
27    /// Balance after the change.
28    pub post_balance: U256,
29}
30
31/// One nonce change.
32#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct NonceChange {
35    /// Where in the block the change happened.
36    pub block_access_index: BlockAccessIndex,
37    /// Nonce after the change.
38    pub new_nonce: u64,
39}
40
41/// One code change (deployment or self-destruct).
42#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44pub struct CodeChange {
45    /// Where in the block the change happened.
46    pub block_access_index: BlockAccessIndex,
47    /// Code after the change.
48    pub new_code: Bytes,
49}
50
51/// All writes to one slot within the block.
52#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub struct SlotChanges {
55    /// Slot key.
56    pub slot: U256,
57    /// Sorted by `block_access_index`, non-empty.
58    pub changes: Vec<StorageChange>,
59}
60
61/// Everything that happened to one account within the block.
62#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64pub struct AccountChanges {
65    /// Account address.
66    pub address: Address,
67    /// Sorted by slot key.
68    pub storage_changes: Vec<SlotChanges>,
69    /// Keys only — slots read (or written with an unchanged value). Sorted.
70    pub storage_reads: Vec<U256>,
71    /// Sorted by index.
72    pub balance_changes: Vec<BalanceChange>,
73    /// Sorted by index.
74    pub nonce_changes: Vec<NonceChange>,
75    /// Sorted by index.
76    pub code_changes: Vec<CodeChange>,
77}
78
79/// The whole list. Wire form is a bare RLP list of `AccountChanges`
80/// (no wrapping struct), so encoding is transparent over `accounts`.
81#[derive(Clone, Debug, Default, PartialEq, Eq)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub struct BlockAccessList {
84    /// Sorted by address; every touched account appears exactly once.
85    pub accounts: Vec<AccountChanges>,
86}
87
88impl alloy_rlp::Encodable for BlockAccessList {
89    fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
90        self.accounts.encode(out)
91    }
92    fn length(&self) -> usize {
93        self.accounts.length()
94    }
95}
96
97impl alloy_rlp::Decodable for BlockAccessList {
98    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
99        Ok(Self {
100            accounts: Vec::<AccountChanges>::decode(buf)?,
101        })
102    }
103}