@@ -248,23 +248,111 @@ impl L1BlockInfo {
248248 . wrapping_div ( TX_L1_FEE_PRECISION_U256 ) // account for penalty
249249 }
250250
251+ fn calculate_tx_l1_cost_galileo (
252+ & self ,
253+ tx_size : usize , // size of the original rlp-encoded transaction
254+ spec_id : ScrollSpecId ,
255+ compressed_size : usize , // size of the compressed rlp-encoded transaction
256+ ) -> U256 {
257+ // Post Galileo rollup fee formula:
258+ // rollup_fee(tx) = fee_per_byte * compressed_size(tx) * (1 + penalty(tx)) / PRECISION
259+ //
260+ // Where:
261+ // fee_per_byte = (exec_scalar * l1_base_fee + blob_scalar * l1_blob_base_fee)
262+ // compressed_size(tx) = min(len(zstd(rlp(tx))), len(rlp(tx)))
263+ // penalty(tx) = compressed_size(tx) / penalty_factor
264+
265+ assert ! (
266+ compressed_size <= tx_size,
267+ "transaction compressed size {compressed_size} must be less than or equal to the original size {tx_size}"
268+ ) ;
269+
270+ let compressed_size = U256 :: from ( compressed_size) ;
271+
272+ let exec_scalar = self
273+ . l1_commit_scalar
274+ . unwrap_or_else ( || panic ! ( "missing exec scalar in spec_id={spec_id:?}" ) ) ;
275+
276+ let blob_scalar = self
277+ . l1_blob_scalar
278+ . unwrap_or_else ( || panic ! ( "missing l1 blob scalar in spec_id={spec_id:?}" ) ) ;
279+
280+ let l1_blob_base_fee = self
281+ . l1_blob_base_fee
282+ . unwrap_or_else ( || panic ! ( "missing l1 blob base fee in spec_id={spec_id:?}" ) ) ;
283+
284+ let penalty_factor = match self . penalty_factor {
285+ Some ( f) if f == U256 :: ZERO => U256 :: ONE , // sanitize zero penalty factor
286+ Some ( f) => f,
287+ None => panic ! ( "missing penalty factor in spec_id={spec_id:?}" ) ,
288+ } ;
289+
290+ // fee_per_byte = (exec_scalar * l1_base_fee) + (blob_scalar * l1_blob_base_fee)
291+ let component_exec = exec_scalar. saturating_mul ( self . l1_base_fee ) ;
292+ let component_blob = blob_scalar. saturating_mul ( l1_blob_base_fee) ;
293+ let fee_per_byte = component_exec. saturating_add ( component_blob) ;
294+
295+ // base_term = fee_per_byte * compressed_size
296+ let base_term = fee_per_byte. saturating_mul ( compressed_size) ;
297+
298+ // penalty_term = (base_term * compressed_size) / penalty_factor
299+ let penalty_term = base_term. saturating_mul ( compressed_size) . wrapping_div ( penalty_factor) ;
300+
301+ // rollup_fee = (base_term + penalty_term) / PRECISION
302+ base_term. saturating_add ( penalty_term) . wrapping_div ( TX_L1_FEE_PRECISION_U256 )
303+ }
304+
251305 /// Calculate the gas cost of a transaction based on L1 block data posted on L2.
252306 pub fn calculate_tx_l1_cost (
253307 & self ,
254308 input : & [ u8 ] ,
255309 spec_id : ScrollSpecId ,
256310 compression_ratio : Option < U256 > ,
311+ compressed_size : Option < usize > ,
257312 ) -> U256 {
258313 let l1_cost = if !spec_id. is_enabled_in ( ScrollSpecId :: CURIE ) {
259314 self . calculate_tx_l1_cost_shanghai ( input, spec_id)
260315 } else if !spec_id. is_enabled_in ( ScrollSpecId :: FEYNMAN ) {
261316 self . calculate_tx_l1_cost_curie ( input, spec_id)
262- } else {
317+ } else if !spec_id . is_enabled_in ( ScrollSpecId :: GALILEO ) {
263318 let compression_ratio = compression_ratio. unwrap_or_else ( || {
264319 panic ! ( "compression ratio should be set in spec_id={spec_id:?}" )
265320 } ) ;
266321 self . calculate_tx_l1_cost_feynman ( input, spec_id, compression_ratio)
322+ } else {
323+ let compressed_size = compressed_size
324+ . unwrap_or_else ( || panic ! ( "compressed size should be set in spec_id={spec_id:?}" ) ) ;
325+ self . calculate_tx_l1_cost_galileo ( input. len ( ) , spec_id, compressed_size)
267326 } ;
268327 l1_cost. min ( U64_MAX )
269328 }
270329}
330+
331+ #[ cfg( test) ]
332+ mod tests {
333+ use super :: * ;
334+ use revm:: primitives:: uint;
335+ use rstest:: rstest;
336+
337+ #[ rstest]
338+ #[ case( 50 , uint!( 171557471810_ U256 ) ) ] // ~0.06 cents
339+ #[ case( 100 , uint!( 344821983141_ U256 ) ) ] // ~0.12 cents
340+ #[ case( 1024 , uint!( 3854009072433_ U256 ) ) ] // ~1.35 cents
341+ #[ case( 10 * 1024 , uint!( 70759382824796_ U256 ) ) ] // ~24.77 cents
342+ #[ case( 1024 * 1024 , uint!( 378961881717079120_ U256 ) ) ] // ~1325 USD
343+ fn test_rollup_fee_galileo ( #[ case] compressed_size : usize , #[ case] expected : U256 ) {
344+ let gpo = L1BlockInfo {
345+ l1_base_fee : uint ! ( 1_000_000_000_ U256 ) , // 1 gwei
346+ l1_blob_base_fee : Some ( uint ! ( 1_000_000_000_ U256 ) ) , // 1 gwei
347+ l1_commit_scalar : Some ( uint ! ( 2394981796_ U256 ) ) , // 2.39
348+ l1_blob_scalar : Some ( uint ! ( 1019097245_ U256 ) ) , // 1.02
349+ penalty_factor : Some ( uint ! ( 10000_ U256 ) ) ,
350+ ..Default :: default ( )
351+ } ;
352+
353+ let tx_size = 1e10 as usize ; // dummy, but make sure this value is larger than the compressed size
354+ let spec = ScrollSpecId :: GALILEO ;
355+ let actual = gpo. calculate_tx_l1_cost_galileo ( tx_size, spec, compressed_size) ;
356+ assert_eq ! ( expected, actual) ;
357+ }
358+ }
0 commit comments