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