1use crate::{
4 eip4844::{self, DATA_GAS_PER_BLOB},
5 eip7594, eip7691, eip7892,
6};
7
8pub const BLOB_BASE_COST: u64 = 2_u64.pow(13);
13
14#[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 pub target_blob_count: u64,
24 pub max_blob_count: u64,
26 pub update_fraction: u128,
28 pub min_blob_fee: u128,
33 pub max_blobs_per_tx: u64,
37 pub blob_base_cost: u64,
41}
42
43impl BlobParams {
44 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 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 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 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 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 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 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 pub const fn max_blob_gas_per_block(&self) -> u64 {
116 self.max_blob_count * DATA_GAS_PER_BLOB
117 }
118
119 pub const fn target_blob_gas_per_block(&self) -> u64 {
123 self.target_blob_count * DATA_GAS_PER_BLOB
124 }
125
126 #[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 #[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 #[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}