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)]
21#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
22pub struct BlobParams {
23 pub target_blob_count: u64,
25 pub max_blob_count: u64,
27 pub update_fraction: u128,
29 pub min_blob_fee: u128,
34 pub max_blobs_per_tx: u64,
38 pub blob_base_cost: u64,
42}
43
44impl BlobParams {
45 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 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 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 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 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 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 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 pub const fn max_blob_gas_per_block(&self) -> u64 {
117 self.max_blob_count * DATA_GAS_PER_BLOB
118 }
119
120 pub const fn target_blob_gas_per_block(&self) -> u64 {
124 self.target_blob_count * DATA_GAS_PER_BLOB
125 }
126
127 #[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 #[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 #[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}