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