1
+ use revm_interpreter:: Gas ;
2
+
1
3
use crate :: optimism:: fast_lz:: flz_compress_len;
2
4
use crate :: primitives:: { address, db:: Database , Address , SpecId , U256 } ;
3
5
use core:: ops:: Mul ;
@@ -11,6 +13,17 @@ const BASE_FEE_SCALAR_OFFSET: usize = 16;
11
13
/// The two 4-byte Ecotone fee scalar values are packed into the same storage slot as the 8-byte sequence number.
12
14
/// Byte offset within the storage slot of the 4-byte blobBaseFeeScalar attribute.
13
15
const BLOB_BASE_FEE_SCALAR_OFFSET : usize = 20 ;
16
+ /// The Isthmus operator fee scalar values are similarly packed. Byte offset within
17
+ /// the storage slot of the 4-byte operatorFeeScalar attribute.
18
+ const OPERATOR_FEE_SCALAR_OFFSET : usize = 20 ;
19
+ /// The Isthmus operator fee scalar values are similarly packed. Byte offset within
20
+ /// the storage slot of the 8-byte operatorFeeConstant attribute.
21
+ const OPERATOR_FEE_CONSTANT_OFFSET : usize = 24 ;
22
+
23
+ /// The fixed point decimal scaling factor associated with the operator fee scalar.
24
+ ///
25
+ /// Allows users to use 6 decimal points of precision when specifying the operator_fee_scalar.
26
+ const OPERATOR_FEE_SCALAR_DECIMAL : u64 = 1_000_000 ;
14
27
15
28
const L1_BASE_FEE_SLOT : U256 = U256 :: from_limbs ( [ 1u64 , 0 , 0 , 0 ] ) ;
16
29
const L1_OVERHEAD_SLOT : U256 = U256 :: from_limbs ( [ 5u64 , 0 , 0 , 0 ] ) ;
@@ -23,12 +36,19 @@ const ECOTONE_L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([7u64, 0, 0, 0]);
23
36
/// offsets [BASE_FEE_SCALAR_OFFSET] and [BLOB_BASE_FEE_SCALAR_OFFSET] respectively.
24
37
const ECOTONE_L1_FEE_SCALARS_SLOT : U256 = U256 :: from_limbs ( [ 3u64 , 0 , 0 , 0 ] ) ;
25
38
39
+ /// This storage slot stores the 32-bit operatorFeeScalar and operatorFeeConstant attributes at
40
+ /// offsets [OPERATOR_FEE_SCALAR_OFFSET] and [OPERATOR_FEE_CONSTANT_OFFSET] respectively.
41
+ const OPERATOR_FEE_SCALARS_SLOT : U256 = U256 :: from_limbs ( [ 8u64 , 0 , 0 , 0 ] ) ;
42
+
26
43
/// An empty 64-bit set of scalar values.
27
44
const EMPTY_SCALARS : [ u8 ; 8 ] = [ 0u8 ; 8 ] ;
28
45
29
46
/// The address of L1 fee recipient.
30
47
pub const L1_FEE_RECIPIENT : Address = address ! ( "420000000000000000000000000000000000001A" ) ;
31
48
49
+ /// The address of the operator fee recipient.
50
+ pub const OPERATOR_FEE_RECIPIENT : Address = address ! ( "420000000000000000000000000000000000001B" ) ;
51
+
32
52
/// The address of the base fee recipient.
33
53
pub const BASE_FEE_RECIPIENT : Address = address ! ( "4200000000000000000000000000000000000019" ) ;
34
54
@@ -68,8 +88,12 @@ pub struct L1BlockInfo {
68
88
pub l1_blob_base_fee : Option < U256 > ,
69
89
/// The current L1 blob base fee scalar. None if Ecotone is not activated.
70
90
pub l1_blob_base_fee_scalar : Option < U256 > ,
91
+ /// The current L1 blob base fee. None if Isthmus is not activated, except if `empty_scalars` is `true`.
92
+ pub operator_fee_scalar : Option < U256 > ,
93
+ /// The current L1 blob base fee scalar. None if Isthmus is not activated.
94
+ pub operator_fee_constant : Option < U256 > ,
71
95
/// True if Ecotone is activated, but the L1 fee scalars have not yet been set.
72
- pub ( crate ) empty_scalars : bool ,
96
+ pub ( crate ) empty_ecotone_scalars : bool ,
73
97
}
74
98
75
99
impl L1BlockInfo {
@@ -109,22 +133,94 @@ impl L1BlockInfo {
109
133
110
134
// Check if the L1 fee scalars are empty. If so, we use the Bedrock cost function. The L1 fee overhead is
111
135
// only necessary if `empty_scalars` is true, as it was deprecated in Ecotone.
112
- let empty_scalars = l1_blob_base_fee. is_zero ( )
136
+ let empty_ecotone_scalars = l1_blob_base_fee. is_zero ( )
113
137
&& l1_fee_scalars[ BASE_FEE_SCALAR_OFFSET ..BLOB_BASE_FEE_SCALAR_OFFSET + 4 ]
114
138
== EMPTY_SCALARS ;
115
- let l1_fee_overhead = empty_scalars
139
+ let l1_fee_overhead = empty_ecotone_scalars
116
140
. then ( || db. storage ( L1_BLOCK_CONTRACT , L1_OVERHEAD_SLOT ) )
117
141
. transpose ( ) ?;
118
142
119
- Ok ( L1BlockInfo {
120
- l1_base_fee,
121
- l1_base_fee_scalar,
122
- l1_blob_base_fee : Some ( l1_blob_base_fee) ,
123
- l1_blob_base_fee_scalar : Some ( l1_blob_base_fee_scalar) ,
124
- empty_scalars,
125
- l1_fee_overhead,
126
- } )
143
+ if spec_id. is_enabled_in ( SpecId :: ISTHMUS ) {
144
+ let operator_fee_scalars = db
145
+ . storage ( L1_BLOCK_CONTRACT , OPERATOR_FEE_SCALARS_SLOT ) ?
146
+ . to_be_bytes :: < 32 > ( ) ;
147
+
148
+ // Post-isthmus L1 block info
149
+ // The `operator_fee_scalar` is stored as a big endian u32 at
150
+ // OPERATOR_FEE_SCALAR_OFFSET.
151
+ let operator_fee_scalar = U256 :: from_be_slice (
152
+ operator_fee_scalars
153
+ [ OPERATOR_FEE_SCALAR_OFFSET ..OPERATOR_FEE_SCALAR_OFFSET + 4 ]
154
+ . as_ref ( ) ,
155
+ ) ;
156
+ // The `operator_fee_constant` is stored as a big endian u64 at
157
+ // OPERATOR_FEE_CONSTANT_OFFSET.
158
+ let operator_fee_constant = U256 :: from_be_slice (
159
+ operator_fee_scalars
160
+ [ OPERATOR_FEE_CONSTANT_OFFSET ..OPERATOR_FEE_CONSTANT_OFFSET + 8 ]
161
+ . as_ref ( ) ,
162
+ ) ;
163
+ Ok ( L1BlockInfo {
164
+ l1_base_fee,
165
+ l1_base_fee_scalar,
166
+ l1_blob_base_fee : Some ( l1_blob_base_fee) ,
167
+ l1_blob_base_fee_scalar : Some ( l1_blob_base_fee_scalar) ,
168
+ empty_ecotone_scalars,
169
+ l1_fee_overhead,
170
+ operator_fee_scalar : Some ( operator_fee_scalar) ,
171
+ operator_fee_constant : Some ( operator_fee_constant) ,
172
+ } )
173
+ } else {
174
+ // Pre-isthmus L1 block info
175
+ Ok ( L1BlockInfo {
176
+ l1_base_fee,
177
+ l1_base_fee_scalar,
178
+ l1_blob_base_fee : Some ( l1_blob_base_fee) ,
179
+ l1_blob_base_fee_scalar : Some ( l1_blob_base_fee_scalar) ,
180
+ empty_ecotone_scalars,
181
+ l1_fee_overhead,
182
+ ..Default :: default ( )
183
+ } )
184
+ }
185
+ }
186
+ }
187
+
188
+ /// Calculate the operator fee for executing this transaction.
189
+ ///
190
+ /// Introduced in isthmus. Prior to isthmus, the operator fee is always zero.
191
+ pub fn operator_fee_charge ( & self , gas_limit : U256 , spec_id : SpecId ) -> U256 {
192
+ if !spec_id. is_enabled_in ( SpecId :: ISTHMUS ) {
193
+ return U256 :: ZERO ;
194
+ }
195
+ let operator_fee_scalar = self
196
+ . operator_fee_scalar
197
+ . expect ( "Missing operator fee scalar for isthmus L1 Block" ) ;
198
+ let operator_fee_constant = self
199
+ . operator_fee_constant
200
+ . expect ( "Missing operator fee constant for isthmus L1 Block" ) ;
201
+
202
+ let product = gas_limit. saturating_mul ( operator_fee_scalar)
203
+ / ( U256 :: from ( OPERATOR_FEE_SCALAR_DECIMAL ) ) ;
204
+
205
+ product. saturating_add ( operator_fee_constant)
206
+ }
207
+
208
+ /// Calculate the operator fee for executing this transaction.
209
+ ///
210
+ /// Introduced in isthmus. Prior to isthmus, the operator fee is always zero.
211
+ pub fn operator_fee_refund ( & self , gas : & Gas , spec_id : SpecId ) -> U256 {
212
+ if !spec_id. is_enabled_in ( SpecId :: ISTHMUS ) {
213
+ return U256 :: ZERO ;
127
214
}
215
+
216
+ let operator_fee_scalar = self
217
+ . operator_fee_scalar
218
+ . expect ( "Missing operator fee scalar for isthmus L1 Block" ) ;
219
+
220
+ // We're computing the difference between two operator fees, so no need to include the
221
+ // constant.
222
+
223
+ operator_fee_scalar. saturating_mul ( U256 :: from ( gas. remaining ( ) + gas. refunded ( ) as u64 ) )
128
224
}
129
225
130
226
/// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per byte
@@ -213,7 +309,7 @@ impl L1BlockInfo {
213
309
// There is an edgecase where, for the very first Ecotone block (unless it is activated at Genesis), we must
214
310
// use the Bedrock cost function. To determine if this is the case, we can check if the Ecotone parameters are
215
311
// unset.
216
- if self . empty_scalars {
312
+ if self . empty_ecotone_scalars {
217
313
return self . calculate_tx_l1_cost_bedrock ( input, spec_id) ;
218
314
}
219
315
@@ -371,7 +467,7 @@ mod tests {
371
467
assert_eq ! ( gas_cost, U256 :: ZERO ) ;
372
468
373
469
// If the scalars are empty, the bedrock cost function should be used.
374
- l1_block_info. empty_scalars = true ;
470
+ l1_block_info. empty_ecotone_scalars = true ;
375
471
let input = bytes ! ( "FACADE" ) ;
376
472
let gas_cost = l1_block_info. calculate_tx_l1_cost ( & input, SpecId :: ECOTONE ) ;
377
473
assert_eq ! ( gas_cost, U256 :: from( 1048 ) ) ;
0 commit comments