1use crate::{
4 eip4844::{self, DATA_GAS_PER_BLOB},
5 eip7594, eip7691, eip7892,
6};
7use alloy_primitives::U256;
8
9pub const BLOB_BASE_COST: u64 = 2_u64.pow(13);
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[cfg_attr(
19 feature = "serde",
20 serde(from = "serde_impl::SerdeHelper", into = "serde_impl::SerdeHelper")
21)]
22#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
23pub struct BlobParams {
24 pub target_blob_count: u64,
26 pub max_blob_count: u64,
28 pub update_fraction: u128,
30 pub min_blob_fee: u128,
35 pub max_blobs_per_tx: u64,
39 pub blob_base_cost: u64,
44}
45
46impl BlobParams {
47 pub const fn cancun() -> Self {
49 Self {
50 target_blob_count: eip4844::TARGET_BLOBS_PER_BLOCK_DENCUN,
51 max_blob_count: eip4844::MAX_BLOBS_PER_BLOCK_DENCUN as u64,
52 update_fraction: eip4844::BLOB_GASPRICE_UPDATE_FRACTION,
53 min_blob_fee: eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
54 max_blobs_per_tx: eip4844::MAX_BLOBS_PER_BLOCK_DENCUN as u64,
55 blob_base_cost: 0,
56 }
57 }
58
59 pub const fn prague() -> Self {
61 Self {
62 target_blob_count: eip7691::TARGET_BLOBS_PER_BLOCK_ELECTRA,
63 max_blob_count: eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA,
64 update_fraction: eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA,
65 min_blob_fee: eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
66 max_blobs_per_tx: eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA,
67 blob_base_cost: 0,
68 }
69 }
70
71 pub const fn osaka() -> Self {
73 Self {
74 target_blob_count: eip7691::TARGET_BLOBS_PER_BLOCK_ELECTRA,
75 max_blob_count: eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA,
76 update_fraction: eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA,
77 min_blob_fee: eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
78 max_blobs_per_tx: eip7594::MAX_BLOBS_PER_TX_FUSAKA,
79 blob_base_cost: BLOB_BASE_COST,
80 }
81 }
82
83 pub const fn bpo1() -> Self {
85 Self {
86 target_blob_count: eip7892::BPO1_TARGET_BLOBS_PER_BLOCK,
87 max_blob_count: eip7892::BPO1_MAX_BLOBS_PER_BLOCK,
88 update_fraction: eip7892::BPO1_BASE_UPDATE_FRACTION as u128,
89 ..Self::osaka()
90 }
91 }
92
93 pub const fn bpo2() -> Self {
95 Self {
96 target_blob_count: eip7892::BPO2_TARGET_BLOBS_PER_BLOCK,
97 max_blob_count: eip7892::BPO2_MAX_BLOBS_PER_BLOCK,
98 update_fraction: eip7892::BPO2_BASE_UPDATE_FRACTION as u128,
99 ..Self::osaka()
100 }
101 }
102
103 pub const fn with_max_blobs_per_tx(mut self, max_blobs_per_tx: u64) -> Self {
105 self.max_blobs_per_tx = max_blobs_per_tx;
106 self
107 }
108
109 pub const fn with_blob_base_cost(mut self, blob_base_cost: u64) -> Self {
111 self.blob_base_cost = blob_base_cost;
112 self
113 }
114
115 pub const fn max_blob_gas_per_block(&self) -> u64 {
119 self.max_blob_count * DATA_GAS_PER_BLOB
120 }
121
122 pub const fn target_blob_gas_per_block(&self) -> u64 {
126 self.target_blob_count * DATA_GAS_PER_BLOB
127 }
128
129 #[inline]
132 pub fn next_block_excess_blob_gas_osaka(
133 &self,
134 excess_blob_gas: u64,
135 blob_gas_used: u64,
136 base_fee_per_gas: u64,
137 ) -> u64 {
138 let next_excess_blob_gas = excess_blob_gas + blob_gas_used;
139 let target_blob_gas = self.target_blob_gas_per_block();
140 if next_excess_blob_gas < target_blob_gas {
141 return 0;
142 }
143
144 if U256::from(self.blob_base_cost) * U256::from(base_fee_per_gas)
145 > U256::from(DATA_GAS_PER_BLOB) * U256::from(self.calc_blob_fee(excess_blob_gas))
146 {
147 let scaled_excess = blob_gas_used * (self.max_blob_count - self.target_blob_count)
148 / self.max_blob_count;
149 excess_blob_gas + scaled_excess
150 } else {
151 next_excess_blob_gas - target_blob_gas
152 }
153 }
154
155 #[inline]
157 pub fn calc_blob_fee(&self, excess_blob_gas: u64) -> u128 {
158 eip4844::fake_exponential(self.min_blob_fee, excess_blob_gas as u128, self.update_fraction)
159 }
160}
161
162#[cfg(feature = "serde")]
163mod serde_impl {
164 use crate::{eip4844, eip7840::BlobParams};
165
166 #[derive(serde::Serialize, serde::Deserialize, Clone, Copy)]
167 #[serde(rename_all = "camelCase")]
168 pub(crate) struct SerdeHelper {
169 #[serde(rename = "baseFeeUpdateFraction")]
170 update_fraction: u128,
171 #[serde(rename = "max")]
172 max_blob_count: u64,
173 #[serde(rename = "target")]
174 target_blob_count: u64,
175 #[serde(skip)]
176 min_blob_fee: Option<u128>,
177 }
178
179 impl From<BlobParams> for SerdeHelper {
180 fn from(params: BlobParams) -> Self {
181 let BlobParams {
182 target_blob_count,
183 max_blob_count,
184 update_fraction,
185 min_blob_fee,
186 max_blobs_per_tx: _,
187 blob_base_cost: _,
188 } = params;
189
190 Self {
191 target_blob_count,
192 max_blob_count,
193 update_fraction,
194 min_blob_fee: (min_blob_fee != eip4844::BLOB_TX_MIN_BLOB_GASPRICE)
195 .then_some(min_blob_fee),
196 }
197 }
198 }
199
200 impl From<SerdeHelper> for BlobParams {
201 fn from(helper: SerdeHelper) -> Self {
202 let SerdeHelper { target_blob_count, max_blob_count, update_fraction, min_blob_fee } =
203 helper;
204
205 Self {
206 target_blob_count,
207 max_blob_count,
208 update_fraction,
209 min_blob_fee: min_blob_fee.unwrap_or(eip4844::BLOB_TX_MIN_BLOB_GASPRICE),
210 max_blobs_per_tx: max_blob_count,
211 blob_base_cost: 0,
212 }
213 }
214 }
215}