arbos/l2_pricing/
mod.rs

1mod error;
2mod gas_constraint;
3mod model;
4mod multi_gas_constraint;
5mod multi_gas_fees;
6
7use alloy_primitives::U256;
8use arb_primitives::multigas::NUM_RESOURCE_KIND;
9use arb_storage::{
10    Storage, StorageBackedBigUint, StorageBackedUint64, StorageBackend, SubStorageVector,
11    SystemStateBackend, open_sub_storage_vector,
12};
13pub use error::L2PricingError;
14pub use gas_constraint::{GasConstraint, open_gas_constraint};
15pub use model::*;
16pub use multi_gas_constraint::{MultiGasConstraint, open_multi_gas_constraint};
17pub use multi_gas_fees::MultiGasFees;
18
19// Storage offsets for L2 pricing state.
20pub const SPEED_LIMIT_PER_SECOND_OFFSET: u64 = 0;
21pub const PER_BLOCK_GAS_LIMIT_OFFSET: u64 = 1;
22pub const BASE_FEE_WEI_OFFSET: u64 = 2;
23pub const MIN_BASE_FEE_WEI_OFFSET: u64 = 3;
24pub const GAS_BACKLOG_OFFSET: u64 = 4;
25pub const PRICING_INERTIA_OFFSET: u64 = 5;
26pub const BACKLOG_TOLERANCE_OFFSET: u64 = 6;
27pub const PER_TX_GAS_LIMIT_OFFSET: u64 = 7;
28
29// Subspace keys for L2 pricing partitions.
30const GAS_CONSTRAINTS_KEY: &[u8] = &[0];
31const MULTI_GAS_CONSTRAINTS_KEY: &[u8] = &[1];
32const MULTI_GAS_BASE_FEES_KEY: &[u8] = &[2];
33
34// Constants.
35pub const GETH_BLOCK_GAS_LIMIT: u64 = 1 << 50;
36pub const GAS_CONSTRAINTS_MAX_NUM: u64 = 20;
37pub const MAX_PRICING_EXPONENT_BIPS: u64 = 85_000;
38
39// EIP-2200 storage costs.
40pub const STORAGE_READ_COST: u64 = 800; // SloadGasEIP2200
41pub const STORAGE_WRITE_COST: u64 = 20_000; // SstoreSetGasEIP2200
42
43// Initial values.
44pub const INITIAL_SPEED_LIMIT_PER_SECOND_V0: u64 = 1_000_000;
45pub const INITIAL_SPEED_LIMIT_PER_SECOND_V6: u64 = 7_000_000;
46pub const INITIAL_PER_BLOCK_GAS_LIMIT_V0: u64 = 20_000_000;
47pub const INITIAL_PER_BLOCK_GAS_LIMIT_V6: u64 = 32_000_000;
48pub const INITIAL_MINIMUM_BASE_FEE_WEI: u64 = 100_000_000; // 0.1 Gwei
49pub const INITIAL_BASE_FEE_WEI: u64 = INITIAL_MINIMUM_BASE_FEE_WEI;
50pub const INITIAL_PRICING_INERTIA: u64 = 102;
51pub const INITIAL_BACKLOG_TOLERANCE: u64 = 10;
52pub const INITIAL_PER_TX_GAS_LIMIT_V50: u64 = 32_000_000;
53
54/// L2 pricing state manages gas pricing for L2 execution.
55pub struct L2PricingState<'a, D> {
56    pub backing_storage: Storage<'a, D>,
57    pub arbos_version: u64,
58    speed_limit_per_second: StorageBackedUint64,
59    per_block_gas_limit: StorageBackedUint64,
60    base_fee_wei: StorageBackedBigUint,
61    min_base_fee_wei: StorageBackedBigUint,
62    gas_backlog: StorageBackedUint64,
63    pricing_inertia: StorageBackedUint64,
64    backlog_tolerance: StorageBackedUint64,
65    per_tx_gas_limit: StorageBackedUint64,
66    gas_constraints: SubStorageVector,
67    multi_gas_constraints: SubStorageVector,
68    multi_gas_base_fees: Storage<'a, D>,
69}
70
71pub fn initialize_l2_pricing_state<D, B: StorageBackend>(
72    sto: &Storage<'_, D>,
73    backend: &mut B,
74) -> Result<(), L2PricingError> {
75    let base_key = sto.base_key();
76
77    StorageBackedUint64::new(base_key, SPEED_LIMIT_PER_SECOND_OFFSET)
78        .set(backend, INITIAL_SPEED_LIMIT_PER_SECOND_V0)?;
79    StorageBackedUint64::new(base_key, PER_BLOCK_GAS_LIMIT_OFFSET)
80        .set(backend, INITIAL_PER_BLOCK_GAS_LIMIT_V0)?;
81    StorageBackedUint64::new(base_key, BASE_FEE_WEI_OFFSET).set(backend, INITIAL_BASE_FEE_WEI)?;
82    StorageBackedBigUint::new(base_key, MIN_BASE_FEE_WEI_OFFSET)
83        .set(backend, U256::from(INITIAL_MINIMUM_BASE_FEE_WEI))?;
84    StorageBackedUint64::new(base_key, GAS_BACKLOG_OFFSET).set(backend, 0)?;
85    StorageBackedUint64::new(base_key, PRICING_INERTIA_OFFSET)
86        .set(backend, INITIAL_PRICING_INERTIA)?;
87    StorageBackedUint64::new(base_key, BACKLOG_TOLERANCE_OFFSET)
88        .set(backend, INITIAL_BACKLOG_TOLERANCE)?;
89    Ok(())
90}
91
92pub fn open_l2_pricing_state<D>(sto: Storage<'_, D>, arbos_version: u64) -> L2PricingState<'_, D> {
93    let base_key = sto.base_key();
94
95    let gc_sto = sto.open_sub_storage(GAS_CONSTRAINTS_KEY);
96    let mgc_sto = sto.open_sub_storage(MULTI_GAS_CONSTRAINTS_KEY);
97    let mgf_sto = sto.open_sub_storage(MULTI_GAS_BASE_FEES_KEY);
98
99    L2PricingState {
100        arbos_version,
101        speed_limit_per_second: StorageBackedUint64::new(base_key, SPEED_LIMIT_PER_SECOND_OFFSET),
102        per_block_gas_limit: StorageBackedUint64::new(base_key, PER_BLOCK_GAS_LIMIT_OFFSET),
103        base_fee_wei: StorageBackedBigUint::new(base_key, BASE_FEE_WEI_OFFSET),
104        min_base_fee_wei: StorageBackedBigUint::new(base_key, MIN_BASE_FEE_WEI_OFFSET),
105        gas_backlog: StorageBackedUint64::new(base_key, GAS_BACKLOG_OFFSET),
106        pricing_inertia: StorageBackedUint64::new(base_key, PRICING_INERTIA_OFFSET),
107        backlog_tolerance: StorageBackedUint64::new(base_key, BACKLOG_TOLERANCE_OFFSET),
108        per_tx_gas_limit: StorageBackedUint64::new(base_key, PER_TX_GAS_LIMIT_OFFSET),
109        gas_constraints: open_sub_storage_vector(gc_sto),
110        multi_gas_constraints: open_sub_storage_vector(mgc_sto),
111        multi_gas_base_fees: mgf_sto,
112        backing_storage: sto,
113    }
114}
115
116impl<'a, D> L2PricingState<'a, D> {
117    pub fn open(sto: Storage<'a, D>, arbos_version: u64) -> Self {
118        open_l2_pricing_state(sto, arbos_version)
119    }
120
121    pub fn initialize<B: StorageBackend>(
122        sto: &Storage<'_, D>,
123        backend: &mut B,
124    ) -> Result<(), L2PricingError> {
125        initialize_l2_pricing_state(sto, backend)
126    }
127
128    // --- Getters/Setters ---
129
130    pub fn base_fee_wei<B: SystemStateBackend>(
131        &self,
132        backend: &mut B,
133    ) -> Result<U256, L2PricingError> {
134        Ok(self.base_fee_wei.get(backend)?)
135    }
136
137    pub fn set_base_fee_wei<B: StorageBackend>(
138        &self,
139        backend: &mut B,
140        val: U256,
141    ) -> Result<(), L2PricingError> {
142        Ok(self.base_fee_wei.set(backend, val)?)
143    }
144
145    pub fn min_base_fee_wei<B: SystemStateBackend>(
146        &self,
147        backend: &mut B,
148    ) -> Result<U256, L2PricingError> {
149        Ok(self.min_base_fee_wei.get(backend)?)
150    }
151
152    pub fn set_min_base_fee_wei<B: StorageBackend>(
153        &self,
154        backend: &mut B,
155        val: U256,
156    ) -> Result<(), L2PricingError> {
157        Ok(self.min_base_fee_wei.set(backend, val)?)
158    }
159
160    pub fn speed_limit_per_second<B: SystemStateBackend>(
161        &self,
162        backend: &mut B,
163    ) -> Result<u64, L2PricingError> {
164        Ok(self.speed_limit_per_second.get(backend)?)
165    }
166
167    pub fn set_speed_limit_per_second<B: StorageBackend>(
168        &self,
169        backend: &mut B,
170        limit: u64,
171    ) -> Result<(), L2PricingError> {
172        Ok(self.speed_limit_per_second.set(backend, limit)?)
173    }
174
175    pub fn per_block_gas_limit<B: SystemStateBackend>(
176        &self,
177        backend: &mut B,
178    ) -> Result<u64, L2PricingError> {
179        Ok(self.per_block_gas_limit.get(backend)?)
180    }
181
182    pub fn set_max_per_block_gas_limit<B: StorageBackend>(
183        &self,
184        backend: &mut B,
185        limit: u64,
186    ) -> Result<(), L2PricingError> {
187        Ok(self.per_block_gas_limit.set(backend, limit)?)
188    }
189
190    pub fn per_tx_gas_limit<B: SystemStateBackend>(
191        &self,
192        backend: &mut B,
193    ) -> Result<u64, L2PricingError> {
194        Ok(self.per_tx_gas_limit.get(backend)?)
195    }
196
197    pub fn set_max_per_tx_gas_limit<B: StorageBackend>(
198        &self,
199        backend: &mut B,
200        limit: u64,
201    ) -> Result<(), L2PricingError> {
202        Ok(self.per_tx_gas_limit.set(backend, limit)?)
203    }
204
205    pub fn gas_backlog<B: SystemStateBackend>(
206        &self,
207        backend: &mut B,
208    ) -> Result<u64, L2PricingError> {
209        Ok(self.gas_backlog.get(backend)?)
210    }
211
212    pub fn set_gas_backlog<B: StorageBackend>(
213        &self,
214        backend: &mut B,
215        backlog: u64,
216    ) -> Result<(), L2PricingError> {
217        Ok(self.gas_backlog.set(backend, backlog)?)
218    }
219
220    pub fn pricing_inertia<B: SystemStateBackend>(
221        &self,
222        backend: &mut B,
223    ) -> Result<u64, L2PricingError> {
224        Ok(self.pricing_inertia.get(backend)?)
225    }
226
227    pub fn set_pricing_inertia<B: StorageBackend>(
228        &self,
229        backend: &mut B,
230        val: u64,
231    ) -> Result<(), L2PricingError> {
232        Ok(self.pricing_inertia.set(backend, val)?)
233    }
234
235    pub fn backlog_tolerance<B: SystemStateBackend>(
236        &self,
237        backend: &mut B,
238    ) -> Result<u64, L2PricingError> {
239        Ok(self.backlog_tolerance.get(backend)?)
240    }
241
242    pub fn set_backlog_tolerance<B: StorageBackend>(
243        &self,
244        backend: &mut B,
245        val: u64,
246    ) -> Result<(), L2PricingError> {
247        Ok(self.backlog_tolerance.set(backend, val)?)
248    }
249
250    // --- Gas Constraints ---
251
252    pub fn gas_constraints_length<B: SystemStateBackend>(
253        &self,
254        backend: &mut B,
255    ) -> Result<u64, L2PricingError> {
256        Ok(self.gas_constraints.length(backend)?)
257    }
258
259    pub fn open_gas_constraint_at(&self, index: u64) -> GasConstraint {
260        open_gas_constraint(self.gas_constraints.at(index))
261    }
262
263    pub fn add_gas_constraint<B: StorageBackend>(
264        &self,
265        backend: &mut B,
266        target: u64,
267        adjustment_window: u64,
268        backlog: u64,
269    ) -> Result<(), L2PricingError> {
270        let key = self.gas_constraints.push(backend)?;
271        let c = open_gas_constraint(key);
272        c.set_target(backend, target)?;
273        c.set_adjustment_window(backend, adjustment_window)?;
274        c.set_backlog(backend, backlog)?;
275        Ok(())
276    }
277
278    pub fn clear_gas_constraints<B: StorageBackend>(
279        &self,
280        backend: &mut B,
281    ) -> Result<(), L2PricingError> {
282        let len = self.gas_constraints.length(backend)?;
283        for i in 0..len {
284            let c = self.open_gas_constraint_at(i);
285            c.clear(backend)?;
286        }
287        for _ in 0..len {
288            self.gas_constraints.pop(backend)?;
289        }
290        Ok(())
291    }
292
293    // --- Multi-Gas Constraints ---
294
295    pub fn multi_gas_constraints_length<B: SystemStateBackend>(
296        &self,
297        backend: &mut B,
298    ) -> Result<u64, L2PricingError> {
299        Ok(self.multi_gas_constraints.length(backend)?)
300    }
301
302    pub fn open_multi_gas_constraint_at(&self, index: u64) -> MultiGasConstraint {
303        open_multi_gas_constraint(self.multi_gas_constraints.at(index))
304    }
305
306    pub fn add_multi_gas_constraint<B: StorageBackend>(
307        &self,
308        backend: &mut B,
309        target: u64,
310        adjustment_window: u32,
311        backlog: u64,
312        weights: &[u64; NUM_RESOURCE_KIND],
313    ) -> Result<(), L2PricingError> {
314        let key = self.multi_gas_constraints.push(backend)?;
315        let c = open_multi_gas_constraint(key);
316        c.set_target(backend, target)?;
317        c.set_adjustment_window(backend, adjustment_window)?;
318        c.set_backlog(backend, backlog)?;
319        c.set_resource_weights(backend, weights)?;
320        Ok(())
321    }
322
323    pub fn clear_multi_gas_constraints<B: StorageBackend>(
324        &self,
325        backend: &mut B,
326    ) -> Result<(), L2PricingError> {
327        let len = self.multi_gas_constraints.length(backend)?;
328        for i in 0..len {
329            let c = self.open_multi_gas_constraint_at(i);
330            c.clear(backend)?;
331        }
332        for _ in 0..len {
333            self.multi_gas_constraints.pop(backend)?;
334        }
335        Ok(())
336    }
337
338    pub fn restrict(&self, _err: ()) {
339        // No-op restriction
340    }
341
342    /// Per-resource-kind base fee accessor for the current/next block.
343    pub fn multi_gas_fees(&self) -> MultiGasFees<'a, D> {
344        multi_gas_fees::open_multi_gas_fees(self.multi_gas_base_fees.clone())
345    }
346}