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            update_fraction: eip7892::BPO1_BASE_UPDATE_FRACTION as u128,
86            ..Self::osaka()
87        }
88    }
89
90    /// [`BlobParams`] for the [EIP-7892](https://eips.ethereum.org/EIPS/eip-7892) Blob parameter only hardfork BPO2
91    pub const fn bpo2() -> Self {
92        Self {
93            target_blob_count: eip7892::BPO2_TARGET_BLOBS_PER_BLOCK,
94            max_blob_count: eip7892::BPO2_MAX_BLOBS_PER_BLOCK,
95            update_fraction: eip7892::BPO2_BASE_UPDATE_FRACTION as u128,
96            ..Self::osaka()
97        }
98    }
99
100    /// Set max blobs per transaction on [`BlobParams`].
101    pub const fn with_max_blobs_per_tx(mut self, max_blobs_per_tx: u64) -> Self {
102        self.max_blobs_per_tx = max_blobs_per_tx;
103        self
104    }
105
106    /// Set blob base cost on [`BlobParams`].
107    pub const fn with_blob_base_cost(mut self, blob_base_cost: u64) -> Self {
108        self.blob_base_cost = blob_base_cost;
109        self
110    }
111
112    /// Returns the maximum available blob gas in a block.
113    ///
114    /// This is `max blob count * DATA_GAS_PER_BLOB`
115    pub const fn max_blob_gas_per_block(&self) -> u64 {
116        self.max_blob_count * DATA_GAS_PER_BLOB
117    }
118
119    /// Returns the blob gas target per block.
120    ///
121    /// This is `target blob count * DATA_GAS_PER_BLOB`
122    pub const fn target_blob_gas_per_block(&self) -> u64 {
123        self.target_blob_count * DATA_GAS_PER_BLOB
124    }
125
126    /// Calculates the `excess_blob_gas` value for the next block based on the current block
127    /// `excess_blob_gas` and `blob_gas_used`.
128    #[inline]
129    #[deprecated(note = "Use `next_block_excess_blob_gas_osaka` instead")]
130    pub const fn next_block_excess_blob_gas(
131        &self,
132        excess_blob_gas: u64,
133        blob_gas_used: u64,
134    ) -> u64 {
135        self.next_block_excess_blob_gas_osaka(excess_blob_gas, blob_gas_used, 0)
136    }
137
138    /// Calculates the `excess_blob_gas` value for the next block based on the current block
139    /// `excess_blob_gas`, `blob_gas_used` and `base_fee_per_gas`.
140    #[inline]
141    pub const fn next_block_excess_blob_gas_osaka(
142        &self,
143        excess_blob_gas: u64,
144        blob_gas_used: u64,
145        base_fee_per_gas: u64,
146    ) -> u64 {
147        let next_excess_blob_gas = excess_blob_gas + blob_gas_used;
148        let target_blob_gas = self.target_blob_gas_per_block();
149        if next_excess_blob_gas < target_blob_gas {
150            return 0;
151        }
152
153        if self.blob_base_cost as u128 * base_fee_per_gas as u128
154            > DATA_GAS_PER_BLOB as u128 * self.calc_blob_fee(excess_blob_gas)
155        {
156            let scaled_excess = blob_gas_used * (self.max_blob_count - self.target_blob_count)
157                / self.max_blob_count;
158            excess_blob_gas + scaled_excess
159        } else {
160            next_excess_blob_gas - target_blob_gas
161        }
162    }
163
164    /// Calculates the blob fee for block based on its `excess_blob_gas`.
165    #[inline]
166    pub const fn calc_blob_fee(&self, excess_blob_gas: u64) -> u128 {
167        eip4844::fake_exponential(self.min_blob_fee, excess_blob_gas as u128, self.update_fraction)
168    }
169}
170
171#[cfg(feature = "serde")]
172mod serde_impl {
173    use crate::{eip4844, eip7840::BlobParams};
174
175    #[derive(serde::Serialize, serde::Deserialize, Clone, Copy)]
176    #[serde(rename_all = "camelCase")]
177    pub(crate) struct SerdeHelper {
178        #[serde(rename = "baseFeeUpdateFraction")]
179        update_fraction: u128,
180        #[serde(rename = "max")]
181        max_blob_count: u64,
182        #[serde(rename = "target")]
183        target_blob_count: u64,
184        #[serde(skip_serializing)]
185        min_blob_fee: Option<u128>,
186    }
187
188    impl From<BlobParams> for SerdeHelper {
189        fn from(params: BlobParams) -> Self {
190            let BlobParams {
191                target_blob_count,
192                max_blob_count,
193                update_fraction,
194                min_blob_fee,
195                max_blobs_per_tx: _,
196                blob_base_cost: _,
197            } = params;
198
199            Self {
200                target_blob_count,
201                max_blob_count,
202                update_fraction,
203                min_blob_fee: (min_blob_fee != eip4844::BLOB_TX_MIN_BLOB_GASPRICE)
204                    .then_some(min_blob_fee),
205            }
206        }
207    }
208
209    impl From<SerdeHelper> for BlobParams {
210        fn from(helper: SerdeHelper) -> Self {
211            let SerdeHelper { target_blob_count, max_blob_count, update_fraction, min_blob_fee } =
212                helper;
213
214            Self {
215                target_blob_count,
216                max_blob_count,
217                update_fraction,
218                min_blob_fee: min_blob_fee.unwrap_or(eip4844::BLOB_TX_MIN_BLOB_GASPRICE),
219                max_blobs_per_tx: max_blob_count,
220                blob_base_cost: 0,
221            }
222        }
223    }
224}