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 pub const fn next_block_excess_blob_gas_osaka(
131 &self,
132 excess_blob_gas: u64,
133 blob_gas_used: u64,
134 base_fee_per_gas: u64,
135 ) -> u64 {
136 let next_excess_blob_gas = excess_blob_gas + blob_gas_used;
137 let target_blob_gas = self.target_blob_gas_per_block();
138 if next_excess_blob_gas < target_blob_gas {
139 return 0;
140 }
141
142 if self.blob_base_cost as u128 * base_fee_per_gas as u128
143 > DATA_GAS_PER_BLOB as u128 * self.calc_blob_fee(excess_blob_gas)
144 {
145 let scaled_excess = blob_gas_used * (self.max_blob_count - self.target_blob_count)
146 / self.max_blob_count;
147 excess_blob_gas + scaled_excess
148 } else {
149 next_excess_blob_gas - target_blob_gas
150 }
151 }
152
153 #[inline]
155 pub const fn calc_blob_fee(&self, excess_blob_gas: u64) -> u128 {
156 eip4844::fake_exponential(self.min_blob_fee, excess_blob_gas as u128, self.update_fraction)
157 }
158}
159
160#[cfg(feature = "serde")]
161mod serde_impl {
162 use crate::{eip4844, eip7840::BlobParams};
163
164 #[derive(serde::Serialize, serde::Deserialize, Clone, Copy)]
165 #[serde(rename_all = "camelCase")]
166 pub(crate) struct SerdeHelper {
167 #[serde(rename = "baseFeeUpdateFraction")]
168 update_fraction: u128,
169 #[serde(rename = "max")]
170 max_blob_count: u64,
171 #[serde(rename = "target")]
172 target_blob_count: u64,
173 #[serde(skip)]
174 min_blob_fee: Option<u128>,
175 }
176
177 impl From<BlobParams> for SerdeHelper {
178 fn from(params: BlobParams) -> Self {
179 let BlobParams {
180 target_blob_count,
181 max_blob_count,
182 update_fraction,
183 min_blob_fee,
184 max_blobs_per_tx: _,
185 blob_base_cost: _,
186 } = params;
187
188 Self {
189 target_blob_count,
190 max_blob_count,
191 update_fraction,
192 min_blob_fee: (min_blob_fee != eip4844::BLOB_TX_MIN_BLOB_GASPRICE)
193 .then_some(min_blob_fee),
194 }
195 }
196 }
197
198 impl From<SerdeHelper> for BlobParams {
199 fn from(helper: SerdeHelper) -> Self {
200 let SerdeHelper { target_blob_count, max_blob_count, update_fraction, min_blob_fee } =
201 helper;
202
203 Self {
204 target_blob_count,
205 max_blob_count,
206 update_fraction,
207 min_blob_fee: min_blob_fee.unwrap_or(eip4844::BLOB_TX_MIN_BLOB_GASPRICE),
208 max_blobs_per_tx: max_blob_count,
209 blob_base_cost: 0,
210 }
211 }
212 }
213}