1use primitives::{
11 eip4844::{GAS_PER_BLOB, MIN_BLOB_GASPRICE},
12 eip7918,
13};
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct BlobExcessGasAndPrice {
23 pub excess_blob_gas: u64,
25 pub blob_gasprice: u128,
29}
30
31impl BlobExcessGasAndPrice {
32 pub fn new(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> Self {
36 let blob_gasprice = calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction);
37 Self {
38 excess_blob_gas,
39 blob_gasprice,
40 }
41 }
42
43 #[deprecated(
48 note = "Use `calc_excess_blob_gas` and `BlobExcessGasAndPrice::new` instead. Only works for forks before Osaka."
49 )]
50 pub fn from_parent_and_target(
51 parent_excess_blob_gas: u64,
52 parent_blob_gas_used: u64,
53 parent_target_blob_gas_per_block: u64,
54 blob_base_fee_update_fraction: u64,
55 ) -> Self {
56 Self::new(
57 calc_excess_blob_gas(
58 parent_excess_blob_gas,
59 parent_blob_gas_used,
60 parent_target_blob_gas_per_block,
61 ),
62 blob_base_fee_update_fraction,
63 )
64 }
65}
66
67#[inline]
70pub fn calc_excess_blob_gas(
71 parent_excess_blob_gas: u64,
72 parent_blob_gas_used: u64,
73 parent_target_blob_gas_per_block: u64,
74) -> u64 {
75 calc_excess_blob_gas_osaka(
76 parent_excess_blob_gas,
77 parent_blob_gas_used,
78 parent_target_blob_gas_per_block,
79 false,
80 0,
81 0,
82 0,
83 0,
84 0,
85 )
86}
87
88#[allow(clippy::too_many_arguments)]
97#[inline]
98pub fn calc_excess_blob_gas_osaka(
99 parent_excess_blob_gas: u64,
100 parent_blob_gas_used: u64,
101 parent_target_blob_gas_per_block: u64,
102 is_osaka: bool,
103 parent_base_fee_per_gas: u64,
104 parent_blob_base_fee_per_gas: u64,
105 parent_blob_base_fee_update_fraction: u64,
106 max_blob_count: u64,
107 target_blob_count: u64,
108) -> u64 {
109 let excess_and_used = parent_excess_blob_gas.saturating_add(parent_blob_gas_used);
110
111 if is_osaka {
112 if excess_and_used < parent_target_blob_gas_per_block {
113 return 0;
114 }
115
116 if (eip7918::BLOB_BASE_COST.saturating_mul(parent_base_fee_per_gas) as u128)
117 > (GAS_PER_BLOB as u128).saturating_mul(get_base_fee_per_blob_gas(
118 parent_blob_base_fee_per_gas,
119 parent_blob_base_fee_update_fraction,
120 ))
121 {
122 return excess_and_used.saturating_add(
123 parent_blob_gas_used.saturating_mul(max_blob_count - target_blob_count)
124 / max_blob_count,
125 );
126 }
127 }
128
129 excess_and_used.saturating_sub(parent_target_blob_gas_per_block)
130}
131
132#[inline]
137pub fn calc_blob_gasprice(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> u128 {
138 fake_exponential(
139 MIN_BLOB_GASPRICE,
140 excess_blob_gas,
141 blob_base_fee_update_fraction,
142 )
143}
144
145pub fn get_base_fee_per_blob_gas(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> u128 {
148 calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction)
149}
150
151#[inline]
162pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 {
163 assert_ne!(denominator, 0, "attempt to divide by zero");
164 let factor = factor as u128;
165 let numerator = numerator as u128;
166 let denominator = denominator as u128;
167
168 let mut i = 1;
169 let mut output = 0;
170 let mut numerator_accum = factor * denominator;
171 while numerator_accum > 0 {
172 output += numerator_accum;
173
174 numerator_accum = (numerator_accum * numerator) / (denominator * i);
176 i += 1;
177 }
178 output / denominator
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use primitives::eip4844::{
185 self, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, GAS_PER_BLOB,
186 TARGET_BLOB_GAS_PER_BLOCK_CANCUN as TARGET_BLOB_GAS_PER_BLOCK,
187 };
188
189 #[test]
191 fn test_calc_excess_blob_gas() {
192 for t @ &(excess, blobs, expected) in &[
193 (0, 0, 0),
196 (0, 1, 0),
197 (0, TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, 0),
198 (
201 0,
202 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
203 GAS_PER_BLOB,
204 ),
205 (
206 1,
207 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
208 GAS_PER_BLOB + 1,
209 ),
210 (
211 1,
212 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 2,
213 2 * GAS_PER_BLOB + 1,
214 ),
215 (
218 TARGET_BLOB_GAS_PER_BLOCK,
219 TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB,
220 TARGET_BLOB_GAS_PER_BLOCK,
221 ),
222 (
223 TARGET_BLOB_GAS_PER_BLOCK,
224 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
225 TARGET_BLOB_GAS_PER_BLOCK - GAS_PER_BLOB,
226 ),
227 (
228 TARGET_BLOB_GAS_PER_BLOCK,
229 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 2,
230 TARGET_BLOB_GAS_PER_BLOCK - (2 * GAS_PER_BLOB),
231 ),
232 (
233 GAS_PER_BLOB - 1,
234 (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
235 0,
236 ),
237 ] {
238 let actual = calc_excess_blob_gas(
239 excess,
240 blobs * GAS_PER_BLOB,
241 eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN,
242 );
243 assert_eq!(actual, expected, "test: {t:?}");
244 }
245 }
246
247 #[test]
249 fn test_calc_blob_fee_cancun() {
250 let blob_fee_vectors = &[
251 (0, 1),
252 (2314057, 1),
253 (2314058, 2),
254 (10 * 1024 * 1024, 23),
255 (148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
263 ];
264
265 for &(excess, expected) in blob_fee_vectors {
266 let actual = calc_blob_gasprice(excess, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN);
267 assert_eq!(actual, expected, "test: {excess}");
268 }
269 }
270
271 #[test]
273 fn fake_exp() {
274 for t @ &(factor, numerator, denominator, expected) in &[
275 (1u64, 0u64, 1u64, 1u128),
276 (38493, 0, 1000, 38493),
277 (0, 1234, 2345, 0),
278 (1, 2, 1, 6), (1, 4, 2, 6),
280 (1, 3, 1, 16), (1, 6, 2, 18),
282 (1, 4, 1, 49), (1, 8, 2, 50),
284 (10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
290 (1, 380928, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, 1),
291 ] {
292 let actual = fake_exponential(factor, numerator, denominator);
293 assert_eq!(actual, expected, "test: {t:?}");
294 }
295 }
296}