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