alloy_eips/
eip7840.rs

1//! Contains constants and utility functions for [EIP-7840](https://github.com/ethereum/EIPs/tree/master/EIPS/eip-7840.md)
2
3use crate::{
4    eip4844::{self, DATA_GAS_PER_BLOB},
5    eip7594, eip7691, eip7892,
6};
7
8/// BLOB_BASE_COST represents the minimum execution gas required to include a blob in a block,
9/// as defined by [EIP-7918 (Decoupling Blob Gas from Execution Gas)](https://eips.ethereum.org/EIPS/eip-7918).
10/// This ensures that even though blob gas and execution gas are decoupled, there is still a base
11/// cost in execution gas to include blobs.
12pub const BLOB_BASE_COST: u64 = 2_u64.pow(13);
13
14/// Configuration for the blob-related calculations.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(
18    feature = "serde",
19    serde(from = "serde_impl::SerdeHelper", into = "serde_impl::SerdeHelper")
20)]
21pub struct BlobParams {
22    /// Target blob count for the block.
23    pub target_blob_count: u64,
24    /// Max blob count for the block.
25    pub max_blob_count: u64,
26    /// Update fraction for excess blob gas calculation.
27    pub update_fraction: u128,
28    /// Minimum gas price for a data blob.
29    ///
30    /// Not required per EIP-7840 and assumed to be the default
31    /// [`eip4844::BLOB_TX_MIN_BLOB_GASPRICE`] if not set.
32    pub min_blob_fee: u128,
33    /// Maximum number of blobs per transaction.
34    ///
35    /// Defaults to `max_blob_count` unless set otherwise.
36    pub max_blobs_per_tx: u64,
37    /// Minimum execution gas required to include a blob in a block.
38    ///
39    /// Defaults to `0` unless set otherwise.
40    pub blob_base_cost: u64,
41}
42
43impl BlobParams {
44    /// Returns [`BlobParams`] configuration activated with Cancun hardfork.
45    pub const fn cancun() -> Self {
46        Self {
47            target_blob_count: eip4844::TARGET_BLOBS_PER_BLOCK_DENCUN,
48            max_blob_count: eip4844::MAX_BLOBS_PER_BLOCK_DENCUN as u64,
49            update_fraction: eip4844::BLOB_GASPRICE_UPDATE_FRACTION,
50            min_blob_fee: eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
51            max_blobs_per_tx: eip4844::MAX_BLOBS_PER_BLOCK_DENCUN as u64,
52            blob_base_cost: 0,
53        }
54    }
55
56    /// Returns [`BlobParams`] configuration activated with Prague hardfork.
57    pub const fn prague() -> Self {
58        Self {
59            target_blob_count: eip7691::TARGET_BLOBS_PER_BLOCK_ELECTRA,
60            max_blob_count: eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA,
61            update_fraction: eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA,
62            min_blob_fee: eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
63            max_blobs_per_tx: eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA,
64            blob_base_cost: 0,
65        }
66    }
67
68    /// Returns [`BlobParams`] configuration activated with Osaka hardfork.
69    pub const fn osaka() -> Self {
70        Self {
71            target_blob_count: eip7691::TARGET_BLOBS_PER_BLOCK_ELECTRA,
72            max_blob_count: eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA,
73            update_fraction: eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA,
74            min_blob_fee: eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
75            max_blobs_per_tx: eip7594::MAX_BLOBS_PER_TX_FUSAKA,
76            blob_base_cost: BLOB_BASE_COST,
77        }
78    }
79
80    /// [`BlobParams`] for the [EIP-7892](https://eips.ethereum.org/EIPS/eip-7892) Blob parameter only hardfork BPO1.
81    pub const fn bpo1() -> Self {
82        Self {
83            target_blob_count: eip7892::BPO1_TARGET_BLOBS_PER_BLOCK,
84            max_blob_count: eip7892::BPO1_MAX_BLOBS_PER_BLOCK,
85            ..Self::osaka()
86        }
87    }
88
89    /// [`BlobParams`] for the [EIP-7892](https://eips.ethereum.org/EIPS/eip-7892) Blob parameter only hardfork BPO2
90    pub const fn bpo2() -> Self {
91        Self {
92            target_blob_count: eip7892::BPO2_TARGET_BLOBS_PER_BLOCK,
93            max_blob_count: eip7892::BPO2_MAX_BLOBS_PER_BLOCK,
94            ..Self::osaka()
95        }
96    }
97
98    /// Set max blobs per transaction on [`BlobParams`].
99    pub const fn with_max_blobs_per_tx(mut self, max_blobs_per_tx: u64) -> Self {
100        self.max_blobs_per_tx = max_blobs_per_tx;
101        self
102    }
103
104    /// Set blob base cost on [`BlobParams`].
105    pub const fn with_blob_base_cost(mut self, blob_base_cost: u64) -> Self {
106        self.blob_base_cost = blob_base_cost;
107        self
108    }
109
110    /// Returns the maximum available blob gas in a block.
111    ///
112    /// This is `max blob count * DATA_GAS_PER_BLOB`
113    pub const fn max_blob_gas_per_block(&self) -> u64 {
114        self.max_blob_count * DATA_GAS_PER_BLOB
115    }
116
117    /// Returns the blob gas target per block.
118    ///
119    /// This is `target blob count * DATA_GAS_PER_BLOB`
120    pub const fn target_blob_gas_per_block(&self) -> u64 {
121        self.target_blob_count * DATA_GAS_PER_BLOB
122    }
123
124    /// Calculates the `excess_blob_gas` value for the next block based on the current block
125    /// `excess_blob_gas` and `blob_gas_used`.
126    #[inline]
127    #[deprecated(note = "Use `next_block_excess_blob_gas_osaka` instead")]
128    pub const fn next_block_excess_blob_gas(
129        &self,
130        excess_blob_gas: u64,
131        blob_gas_used: u64,
132    ) -> u64 {
133        self.next_block_excess_blob_gas_osaka(excess_blob_gas, blob_gas_used, 0)
134    }
135
136    /// Calculates the `excess_blob_gas` value for the next block based on the current block
137    /// `excess_blob_gas`, `blob_gas_used` and `base_fee_per_gas`.
138    #[inline]
139    pub const fn next_block_excess_blob_gas_osaka(
140        &self,
141        excess_blob_gas: u64,
142        blob_gas_used: u64,
143        base_fee_per_gas: u64,
144    ) -> u64 {
145        let next_excess_blob_gas = excess_blob_gas + blob_gas_used;
146        let target_blob_gas = self.target_blob_gas_per_block();
147        if next_excess_blob_gas < target_blob_gas {
148            return 0;
149        }
150
151        if self.blob_base_cost as u128 * base_fee_per_gas as u128
152            > DATA_GAS_PER_BLOB as u128 * self.calc_blob_fee(excess_blob_gas)
153        {
154            let scaled_excess = blob_gas_used * (self.max_blob_count - self.target_blob_count)
155                / self.max_blob_count;
156            excess_blob_gas + scaled_excess
157        } else {
158            next_excess_blob_gas - target_blob_gas
159        }
160    }
161
162    /// Calculates the blob fee for block based on its `excess_blob_gas`.
163    #[inline]
164    pub const fn calc_blob_fee(&self, excess_blob_gas: u64) -> u128 {
165        eip4844::fake_exponential(self.min_blob_fee, excess_blob_gas as u128, self.update_fraction)
166    }
167}
168
169#[cfg(feature = "serde")]
170mod serde_impl {
171    use crate::{eip4844, eip7840::BlobParams};
172
173    #[derive(serde::Serialize, serde::Deserialize, Clone, Copy)]
174    #[serde(rename_all = "camelCase")]
175    pub(crate) struct SerdeHelper {
176        #[serde(rename = "baseFeeUpdateFraction")]
177        update_fraction: u128,
178        #[serde(rename = "max")]
179        max_blob_count: u64,
180        #[serde(rename = "target")]
181        target_blob_count: u64,
182        #[serde(skip_serializing)]
183        min_blob_fee: Option<u128>,
184    }
185
186    impl From<BlobParams> for SerdeHelper {
187        fn from(params: BlobParams) -> Self {
188            let BlobParams {
189                target_blob_count,
190                max_blob_count,
191                update_fraction,
192                min_blob_fee,
193                max_blobs_per_tx: _,
194                blob_base_cost: _,
195            } = params;
196
197            Self {
198                target_blob_count,
199                max_blob_count,
200                update_fraction,
201                min_blob_fee: (min_blob_fee != eip4844::BLOB_TX_MIN_BLOB_GASPRICE)
202                    .then_some(min_blob_fee),
203            }
204        }
205    }
206
207    impl From<SerdeHelper> for BlobParams {
208        fn from(helper: SerdeHelper) -> Self {
209            let SerdeHelper { target_blob_count, max_blob_count, update_fraction, min_blob_fee } =
210                helper;
211
212            Self {
213                target_blob_count,
214                max_blob_count,
215                update_fraction,
216                min_blob_fee: min_blob_fee.unwrap_or(eip4844::BLOB_TX_MIN_BLOB_GASPRICE),
217                max_blobs_per_tx: max_blob_count,
218                blob_base_cost: 0,
219            }
220        }
221    }
222}