1use alloy_primitives::U256;
4use core::ops::Mul;
5
6pub use op_alloy_flz::flz_compress_len;
8
9const ZERO_BYTE_COST: u64 = 4;
11const NON_ZERO_BYTE_COST: u64 = 16;
12
13pub fn data_gas_bedrock(input: &[u8]) -> U256 {
19 data_gas_regolith(input) + U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68))
20}
21
22pub fn data_gas_regolith(input: &[u8]) -> U256 {
26 let rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| {
27 acc + if *byte == 0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST }
28 }));
29
30 rollup_data_gas_cost
31}
32
33pub fn data_gas_fjord(input: &[u8]) -> U256 {
37 let estimated_size = U256::from(tx_estimated_size_fjord(input));
38 estimated_size
39 .saturating_mul(U256::from(NON_ZERO_BYTE_COST))
40 .wrapping_div(U256::from(1_000_000))
41}
42
43pub fn tx_estimated_size_fjord(input: &[u8]) -> u64 {
47 let fastlz_size = flz_compress_len(input) as u64;
48
49 fastlz_size.saturating_mul(836_500).saturating_sub(42_585_600).max(100_000_000)
50}
51
52fn bedrock_tx_cost(
59 rollup_data_gas_cost: U256,
60 l1_fee_overhead: U256,
61 base_fee: U256,
62 l1_fee_scalar: U256,
63) -> U256 {
64 rollup_data_gas_cost
65 .saturating_add(l1_fee_overhead)
66 .saturating_mul(base_fee)
67 .saturating_mul(l1_fee_scalar)
68 .wrapping_div(U256::from(1_000_000)) }
70
71pub fn calculate_tx_l1_cost_bedrock_empty_scalars(
75 input: &[u8],
76 l1_fee_overhead: U256,
77 base_fee: U256,
78 l1_fee_scalar: U256,
79) -> U256 {
80 if input.is_empty() || input.first() == Some(&0x7F) {
81 return U256::ZERO;
82 }
83
84 let rollup_data_gas_cost = data_gas_regolith(input);
85
86 bedrock_tx_cost(rollup_data_gas_cost, l1_fee_overhead, base_fee, l1_fee_scalar)
87}
88
89pub fn calculate_tx_l1_cost_bedrock(
91 input: &[u8],
92 l1_fee_overhead: U256,
93 base_fee: U256,
94 l1_fee_scalar: U256,
95) -> U256 {
96 if input.is_empty() || input.first() == Some(&0x7F) {
97 return U256::ZERO;
98 }
99
100 let rollup_data_gas_cost = data_gas_bedrock(input);
101
102 bedrock_tx_cost(rollup_data_gas_cost, l1_fee_overhead, base_fee, l1_fee_scalar)
103}
104
105pub fn calculate_tx_l1_cost_regolith(
107 input: &[u8],
108 l1_fee_overhead: U256,
109 base_fee: U256,
110 l1_fee_scalar: U256,
111) -> U256 {
112 if input.is_empty() || input.first() == Some(&0x7F) {
113 return U256::ZERO;
114 }
115
116 let rollup_data_gas_cost = data_gas_regolith(input);
117
118 bedrock_tx_cost(rollup_data_gas_cost, l1_fee_overhead, base_fee, l1_fee_scalar)
119}
120
121pub fn calculate_tx_l1_cost_ecotone(
132 input: &[u8],
133 base_fee: U256,
134 base_fee_scalar: U256,
135 blob_base_fee: U256,
136 blob_base_fee_scalar: U256,
137) -> U256 {
138 if input.is_empty() || input.first() == Some(&0x7F) {
139 return U256::ZERO;
140 }
141
142 let rollup_data_gas_cost = data_gas_regolith(input);
143 let l1_fee_scaled = calculate_l1_fee_scaled_ecotone(
144 base_fee,
145 base_fee_scalar,
146 blob_base_fee,
147 blob_base_fee_scalar,
148 );
149
150 l1_fee_scaled
151 .saturating_mul(rollup_data_gas_cost)
152 .wrapping_div(U256::from(1_000_000 * NON_ZERO_BYTE_COST))
153}
154
155pub fn calculate_tx_l1_cost_fjord(
160 input: &[u8],
161 base_fee: U256,
162 base_fee_scalar: U256,
163 blob_base_fee: U256,
164 blob_base_fee_scalar: U256,
165) -> U256 {
166 if input.is_empty() || input.first() == Some(&0x7F) {
167 return U256::ZERO;
168 }
169
170 let l1_fee_scaled = calculate_l1_fee_scaled_ecotone(
171 base_fee,
172 base_fee_scalar,
173 blob_base_fee,
174 blob_base_fee_scalar,
175 );
176 let estimated_size = U256::from(tx_estimated_size_fjord(input));
177
178 estimated_size.saturating_mul(l1_fee_scaled).wrapping_div(U256::from(1_000_000_000_000u64))
179}
180
181fn calculate_l1_fee_scaled_ecotone(
183 base_fee: U256,
184 base_fee_scalar: U256,
185 blob_base_fee: U256,
186 blob_base_fee_scalar: U256,
187) -> U256 {
188 let calldata_cost_per_byte: U256 =
189 base_fee.saturating_mul(U256::from(NON_ZERO_BYTE_COST)).saturating_mul(base_fee_scalar);
190 let blob_cost_per_byte = blob_base_fee.saturating_mul(blob_base_fee_scalar);
191
192 U256::from(calldata_cost_per_byte).saturating_add(blob_cost_per_byte)
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use alloy_primitives::{bytes, hex};
199
200 #[test]
201 fn test_data_gas_bedrock() {
202 let input_1 = bytes!("FACADE");
205
206 let input_2 = bytes!("FA00CA00DE");
209
210 let bedrock_data_gas = data_gas_bedrock(&input_1);
214 assert_eq!(bedrock_data_gas, U256::from(1136));
215
216 let bedrock_data_gas = data_gas_bedrock(&input_2);
220 assert_eq!(bedrock_data_gas, U256::from(1144));
221 }
222
223 #[test]
224 fn test_data_gas_regolith() {
225 let input_1 = bytes!("FACADE");
228
229 let input_2 = bytes!("FA00CA00DE");
232
233 let bedrock_data_gas = data_gas_regolith(&input_1);
236 assert_eq!(bedrock_data_gas, U256::from(48));
237
238 let bedrock_data_gas = data_gas_regolith(&input_2);
241 assert_eq!(bedrock_data_gas, U256::from(56));
242 }
243
244 #[test]
245 fn test_data_gas_fjord() {
246 let input_1 = bytes!("FACADE");
249
250 let input_2 = bytes!("FA00CA00DE");
253
254 let fjord_data_gas = data_gas_fjord(&input_1);
257 assert_eq!(fjord_data_gas, U256::from(1600));
258
259 let fjord_data_gas = data_gas_fjord(&input_2);
262 assert_eq!(fjord_data_gas, U256::from(1600));
263 }
264
265 #[test]
266 fn test_calculate_tx_l1_cost_bedrock() {
267 let base_fee = U256::from(1_000);
269 let l1_fee_overhead = U256::from(1_000);
270 let l1_fee_scalar = U256::from(1_000);
271
272 let input = bytes!("FACADE");
276 let gas_cost =
277 calculate_tx_l1_cost_bedrock(&input, l1_fee_overhead, base_fee, l1_fee_scalar);
278 assert_eq!(gas_cost, U256::from(2136));
279
280 let input = bytes!("");
282 let gas_cost =
283 calculate_tx_l1_cost_bedrock(&input, l1_fee_overhead, base_fee, l1_fee_scalar);
284 assert_eq!(gas_cost, U256::ZERO);
285
286 let input = bytes!("7FFACADE");
288 let gas_cost =
289 calculate_tx_l1_cost_bedrock(&input, l1_fee_overhead, base_fee, l1_fee_scalar);
290 assert_eq!(gas_cost, U256::ZERO);
291 }
292
293 #[test]
294 fn test_calculate_tx_l1_cost_regolith() {
295 let base_fee = U256::from(1_000);
297 let l1_fee_overhead = U256::from(1_000);
298 let l1_fee_scalar = U256::from(1_000);
299
300 let input = bytes!("FACADE");
304 let gas_cost =
305 calculate_tx_l1_cost_regolith(&input, l1_fee_overhead, base_fee, l1_fee_scalar);
306 assert_eq!(gas_cost, U256::from(1048));
307
308 let input = bytes!("");
310 let gas_cost =
311 calculate_tx_l1_cost_regolith(&input, l1_fee_overhead, base_fee, l1_fee_scalar);
312 assert_eq!(gas_cost, U256::ZERO);
313
314 let input = bytes!("7FFACADE");
316 let gas_cost =
317 calculate_tx_l1_cost_regolith(&input, l1_fee_overhead, base_fee, l1_fee_scalar);
318 assert_eq!(gas_cost, U256::ZERO);
319 }
320
321 #[test]
322 fn test_calculate_tx_l1_cost_ecotone() {
323 let base_fee = U256::from(1_000);
324 let blob_base_fee = U256::from(1_000);
325 let blob_base_fee_scalar = U256::from(1_000);
326 let base_fee_scalar = U256::from(1_000);
327
328 let input = bytes!("FACADE");
332 let gas_cost = calculate_tx_l1_cost_ecotone(
333 &input,
334 base_fee,
335 base_fee_scalar,
336 blob_base_fee,
337 blob_base_fee_scalar,
338 );
339 assert_eq!(gas_cost, U256::from(51));
340
341 let input = bytes!("");
343 let gas_cost = calculate_tx_l1_cost_ecotone(
344 &input,
345 base_fee,
346 base_fee_scalar,
347 blob_base_fee,
348 blob_base_fee_scalar,
349 );
350 assert_eq!(gas_cost, U256::ZERO);
351
352 let input = bytes!("7FFACADE");
354 let gas_cost = calculate_tx_l1_cost_ecotone(
355 &input,
356 base_fee,
357 base_fee_scalar,
358 blob_base_fee,
359 blob_base_fee_scalar,
360 );
361 assert_eq!(gas_cost, U256::ZERO);
362 }
363
364 #[test]
365 fn test_calculate_tx_l1_cost_fjord() {
366 let base_fee = U256::from(1_000);
370 let blob_base_fee = U256::from(1_000);
371 let blob_base_fee_scalar = U256::from(1_000);
372 let base_fee_scalar = U256::from(1_000);
373
374 let input = bytes!("FACADE");
379 let gas_cost = calculate_tx_l1_cost_fjord(
383 &input,
384 base_fee,
385 base_fee_scalar,
386 blob_base_fee,
387 blob_base_fee_scalar,
388 );
389 assert_eq!(gas_cost, U256::from(1700));
390
391 let input = bytes!(
396 "02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"
397 );
398 let gas_cost = calculate_tx_l1_cost_fjord(
402 &input,
403 base_fee,
404 base_fee_scalar,
405 blob_base_fee,
406 blob_base_fee_scalar,
407 );
408 assert_eq!(gas_cost, U256::from(2148));
409
410 let input = bytes!("");
412 let gas_cost = calculate_tx_l1_cost_fjord(
413 &input,
414 base_fee,
415 base_fee_scalar,
416 blob_base_fee,
417 blob_base_fee_scalar,
418 );
419 assert_eq!(gas_cost, U256::ZERO);
420
421 let input = bytes!("7FFACADE");
423 let gas_cost = calculate_tx_l1_cost_fjord(
424 &input,
425 base_fee,
426 base_fee_scalar,
427 blob_base_fee,
428 blob_base_fee_scalar,
429 );
430 assert_eq!(gas_cost, U256::ZERO);
431 }
432
433 #[test]
434 fn calculate_tx_l1_cost_fjord_actual_block() {
435 let base_fee = U256::from(1055991687);
438 let blob_base_fee = U256::from(1);
439 let blob_base_fee_scalar = U256::from(1014213);
440 let base_fee_scalar = U256::from(5227);
441
442 const TX: &[u8] = &hex!(
445 "02f904940a8303fba78401d6d2798401db2b6d830493e0943e6f4f7866654c18f536170780344aa8772950b680b904246a761202000000000000000000000000087000a300de7200382b55d40045000000e5d60e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000022482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000dc6ff44d5d932cbd77b52e5612ba0529dc6226f1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c0000000000000000000000000000000000000000000000049b9ca9a6943400000000000000000000000000000000000000000000000000000000000000000000000000000000000021c4928109acb0659a88ae5329b5374a3024694c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024b6b55f250000000000000000000000000000000000000000000000049b9ca9a694340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000415ec214a3950bea839a7e6fbb0ba1540ac2076acd50820e2d5ef83d0902cdffb24a47aff7de5190290769c4f0a9c6fabf63012986a0d590b1b571547a8c7050ea1b00000000000000000000000000000000000000000000000000000000000000c080a06db770e6e25a617fe9652f0958bd9bd6e49281a53036906386ed39ec48eadf63a07f47cf51a4a40b4494cf26efc686709a9b03939e20ee27e59682f5faa536667e"
446 );
447
448 let expected_data_gas = U256::from(4471);
451 let expected_l1_fee = U256::from_be_bytes(hex!(
452 "00000000000000000000000000000000000000000000000000000005bf1ab43d"
453 ));
454
455 let data_gas = data_gas_fjord(TX);
457 assert_eq!(data_gas, expected_data_gas);
458 let l1_fee = calculate_tx_l1_cost_fjord(
459 TX,
460 base_fee,
461 base_fee_scalar,
462 blob_base_fee,
463 blob_base_fee_scalar,
464 );
465 assert_eq!(l1_fee, expected_l1_fee)
466 }
467}