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 ..Self::osaka()
86 }
87 }
88
89 pub const fn bpo2() -> Self {
91 Self {
92 target_blob_count: eip7892::BPO2_TARGET_BLOBS_PER_BLOCK,
93 max_blob_count: eip7892::BPO2_MAX_BLOBS_PER_BLOCK,
94 ..Self::osaka()
95 }
96 }
97
98 pub const fn with_max_blobs_per_tx(mut self, max_blobs_per_tx: u64) -> Self {
100 self.max_blobs_per_tx = max_blobs_per_tx;
101 self
102 }
103
104 pub const fn with_blob_base_cost(mut self, blob_base_cost: u64) -> Self {
106 self.blob_base_cost = blob_base_cost;
107 self
108 }
109
110 pub const fn max_blob_gas_per_block(&self) -> u64 {
114 self.max_blob_count * DATA_GAS_PER_BLOB
115 }
116
117 pub const fn target_blob_gas_per_block(&self) -> u64 {
121 self.target_blob_count * DATA_GAS_PER_BLOB
122 }
123
124 #[inline]
127 #[deprecated(note = "Use `next_block_excess_blob_gas_osaka` instead")]
128 pub const fn next_block_excess_blob_gas(
129 &self,
130 excess_blob_gas: u64,
131 blob_gas_used: u64,
132 ) -> u64 {
133 self.next_block_excess_blob_gas_osaka(excess_blob_gas, blob_gas_used, 0)
134 }
135
136 #[inline]
139 pub const fn next_block_excess_blob_gas_osaka(
140 &self,
141 excess_blob_gas: u64,
142 blob_gas_used: u64,
143 base_fee_per_gas: u64,
144 ) -> u64 {
145 let next_excess_blob_gas = excess_blob_gas + blob_gas_used;
146 let target_blob_gas = self.target_blob_gas_per_block();
147 if next_excess_blob_gas < target_blob_gas {
148 return 0;
149 }
150
151 if self.blob_base_cost as u128 * base_fee_per_gas as u128
152 > DATA_GAS_PER_BLOB as u128 * self.calc_blob_fee(excess_blob_gas)
153 {
154 let scaled_excess = blob_gas_used * (self.max_blob_count - self.target_blob_count)
155 / self.max_blob_count;
156 excess_blob_gas + scaled_excess
157 } else {
158 next_excess_blob_gas - target_blob_gas
159 }
160 }
161
162 #[inline]
164 pub const fn calc_blob_fee(&self, excess_blob_gas: u64) -> u128 {
165 eip4844::fake_exponential(self.min_blob_fee, excess_blob_gas as u128, self.update_fraction)
166 }
167}
168
169#[cfg(feature = "serde")]
170mod serde_impl {
171 use crate::{eip4844, eip7840::BlobParams};
172
173 #[derive(serde::Serialize, serde::Deserialize, Clone, Copy)]
174 #[serde(rename_all = "camelCase")]
175 pub(crate) struct SerdeHelper {
176 #[serde(rename = "baseFeeUpdateFraction")]
177 update_fraction: u128,
178 #[serde(rename = "max")]
179 max_blob_count: u64,
180 #[serde(rename = "target")]
181 target_blob_count: u64,
182 #[serde(skip_serializing)]
183 min_blob_fee: Option<u128>,
184 }
185
186 impl From<BlobParams> for SerdeHelper {
187 fn from(params: BlobParams) -> Self {
188 let BlobParams {
189 target_blob_count,
190 max_blob_count,
191 update_fraction,
192 min_blob_fee,
193 max_blobs_per_tx: _,
194 blob_base_cost: _,
195 } = params;
196
197 Self {
198 target_blob_count,
199 max_blob_count,
200 update_fraction,
201 min_blob_fee: (min_blob_fee != eip4844::BLOB_TX_MIN_BLOB_GASPRICE)
202 .then_some(min_blob_fee),
203 }
204 }
205 }
206
207 impl From<SerdeHelper> for BlobParams {
208 fn from(helper: SerdeHelper) -> Self {
209 let SerdeHelper { target_blob_count, max_blob_count, update_fraction, min_blob_fee } =
210 helper;
211
212 Self {
213 target_blob_count,
214 max_blob_count,
215 update_fraction,
216 min_blob_fee: min_blob_fee.unwrap_or(eip4844::BLOB_TX_MIN_BLOB_GASPRICE),
217 max_blobs_per_tx: max_blob_count,
218 blob_base_cost: 0,
219 }
220 }
221 }
222}