arbos/l2_pricing/
mod.rs

1mod gas_constraint;
2mod model;
3mod multi_gas_constraint;
4mod multi_gas_fees;
5
6pub use gas_constraint::{open_gas_constraint, GasConstraint};
7pub use model::*;
8pub use multi_gas_constraint::{open_multi_gas_constraint, MultiGasConstraint};
9pub use multi_gas_fees::MultiGasFees;
10
11use alloy_primitives::U256;
12use revm::Database;
13
14use arb_primitives::multigas::NUM_RESOURCE_KIND;
15use arb_storage::{
16    open_sub_storage_vector, Storage, StorageBackedBigUint, StorageBackedUint64, SubStorageVector,
17};
18
19// Storage offsets for L2 pricing state.
20const SPEED_LIMIT_PER_SECOND_OFFSET: u64 = 0;
21const PER_BLOCK_GAS_LIMIT_OFFSET: u64 = 1;
22const BASE_FEE_WEI_OFFSET: u64 = 2;
23const MIN_BASE_FEE_WEI_OFFSET: u64 = 3;
24const GAS_BACKLOG_OFFSET: u64 = 4;
25const PRICING_INERTIA_OFFSET: u64 = 5;
26const BACKLOG_TOLERANCE_OFFSET: u64 = 6;
27const 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<D> {
56    pub backing_storage: Storage<D>,
57    pub arbos_version: u64,
58    speed_limit_per_second: StorageBackedUint64<D>,
59    per_block_gas_limit: StorageBackedUint64<D>,
60    base_fee_wei: StorageBackedBigUint<D>,
61    min_base_fee_wei: StorageBackedBigUint<D>,
62    gas_backlog: StorageBackedUint64<D>,
63    pricing_inertia: StorageBackedUint64<D>,
64    backlog_tolerance: StorageBackedUint64<D>,
65    per_tx_gas_limit: StorageBackedUint64<D>,
66    gas_constraints: SubStorageVector<D>,
67    multi_gas_constraints: SubStorageVector<D>,
68    multi_gas_base_fees: Storage<D>,
69}
70
71pub fn initialize_l2_pricing_state<D: Database>(sto: &Storage<D>) {
72    let state = sto.state_ptr();
73    let base_key = sto.base_key();
74
75    let _ = StorageBackedUint64::new(state, base_key, SPEED_LIMIT_PER_SECOND_OFFSET)
76        .set(INITIAL_SPEED_LIMIT_PER_SECOND_V0);
77    let _ = StorageBackedUint64::new(state, base_key, PER_BLOCK_GAS_LIMIT_OFFSET)
78        .set(INITIAL_PER_BLOCK_GAS_LIMIT_V0);
79    let _ =
80        StorageBackedUint64::new(state, base_key, BASE_FEE_WEI_OFFSET).set(INITIAL_BASE_FEE_WEI);
81    let _ = StorageBackedBigUint::new(state, base_key, MIN_BASE_FEE_WEI_OFFSET)
82        .set(U256::from(INITIAL_MINIMUM_BASE_FEE_WEI));
83    let _ = StorageBackedUint64::new(state, base_key, GAS_BACKLOG_OFFSET).set(0);
84    let _ = StorageBackedUint64::new(state, base_key, PRICING_INERTIA_OFFSET)
85        .set(INITIAL_PRICING_INERTIA);
86    let _ = StorageBackedUint64::new(state, base_key, BACKLOG_TOLERANCE_OFFSET)
87        .set(INITIAL_BACKLOG_TOLERANCE);
88}
89
90pub fn open_l2_pricing_state<D: Database>(
91    sto: Storage<D>,
92    arbos_version: u64,
93) -> L2PricingState<D> {
94    let state = sto.state_ptr();
95    let base_key = sto.base_key();
96
97    let gc_sto = sto.open_sub_storage(GAS_CONSTRAINTS_KEY);
98    let mgc_sto = sto.open_sub_storage(MULTI_GAS_CONSTRAINTS_KEY);
99    let mgf_sto = sto.open_sub_storage(MULTI_GAS_BASE_FEES_KEY);
100
101    L2PricingState {
102        arbos_version,
103        speed_limit_per_second: StorageBackedUint64::new(
104            state,
105            base_key,
106            SPEED_LIMIT_PER_SECOND_OFFSET,
107        ),
108        per_block_gas_limit: StorageBackedUint64::new(state, base_key, PER_BLOCK_GAS_LIMIT_OFFSET),
109        base_fee_wei: StorageBackedBigUint::new(state, base_key, BASE_FEE_WEI_OFFSET),
110        min_base_fee_wei: StorageBackedBigUint::new(state, base_key, MIN_BASE_FEE_WEI_OFFSET),
111        gas_backlog: StorageBackedUint64::new(state, base_key, GAS_BACKLOG_OFFSET),
112        pricing_inertia: StorageBackedUint64::new(state, base_key, PRICING_INERTIA_OFFSET),
113        backlog_tolerance: StorageBackedUint64::new(state, base_key, BACKLOG_TOLERANCE_OFFSET),
114        per_tx_gas_limit: StorageBackedUint64::new(state, base_key, PER_TX_GAS_LIMIT_OFFSET),
115        gas_constraints: open_sub_storage_vector(gc_sto),
116        multi_gas_constraints: open_sub_storage_vector(mgc_sto),
117        multi_gas_base_fees: mgf_sto,
118        backing_storage: sto,
119    }
120}
121
122impl<D: Database> L2PricingState<D> {
123    pub fn open(sto: Storage<D>, arbos_version: u64) -> Self {
124        open_l2_pricing_state(sto, arbos_version)
125    }
126
127    pub fn initialize(sto: &Storage<D>) {
128        initialize_l2_pricing_state(sto);
129    }
130
131    // --- Getters/Setters ---
132
133    pub fn base_fee_wei(&self) -> Result<U256, ()> {
134        self.base_fee_wei.get()
135    }
136
137    pub fn set_base_fee_wei(&self, val: U256) -> Result<(), ()> {
138        self.base_fee_wei.set(val)
139    }
140
141    pub fn min_base_fee_wei(&self) -> Result<U256, ()> {
142        self.min_base_fee_wei.get()
143    }
144
145    pub fn set_min_base_fee_wei(&self, val: U256) -> Result<(), ()> {
146        self.min_base_fee_wei.set(val)
147    }
148
149    pub fn speed_limit_per_second(&self) -> Result<u64, ()> {
150        self.speed_limit_per_second.get()
151    }
152
153    pub fn set_speed_limit_per_second(&self, limit: u64) -> Result<(), ()> {
154        self.speed_limit_per_second.set(limit)
155    }
156
157    pub fn per_block_gas_limit(&self) -> Result<u64, ()> {
158        self.per_block_gas_limit.get()
159    }
160
161    pub fn set_max_per_block_gas_limit(&self, limit: u64) -> Result<(), ()> {
162        self.per_block_gas_limit.set(limit)
163    }
164
165    pub fn per_tx_gas_limit(&self) -> Result<u64, ()> {
166        self.per_tx_gas_limit.get()
167    }
168
169    pub fn set_max_per_tx_gas_limit(&self, limit: u64) -> Result<(), ()> {
170        self.per_tx_gas_limit.set(limit)
171    }
172
173    pub fn gas_backlog(&self) -> Result<u64, ()> {
174        self.gas_backlog.get()
175    }
176
177    pub fn set_gas_backlog(&self, backlog: u64) -> Result<(), ()> {
178        self.gas_backlog.set(backlog)
179    }
180
181    pub fn pricing_inertia(&self) -> Result<u64, ()> {
182        self.pricing_inertia.get()
183    }
184
185    pub fn set_pricing_inertia(&self, val: u64) -> Result<(), ()> {
186        self.pricing_inertia.set(val)
187    }
188
189    pub fn backlog_tolerance(&self) -> Result<u64, ()> {
190        self.backlog_tolerance.get()
191    }
192
193    pub fn set_backlog_tolerance(&self, val: u64) -> Result<(), ()> {
194        self.backlog_tolerance.set(val)
195    }
196
197    // --- Gas Constraints ---
198
199    pub fn gas_constraints_length(&self) -> Result<u64, ()> {
200        self.gas_constraints.length()
201    }
202
203    pub fn open_gas_constraint_at(&self, index: u64) -> GasConstraint<D> {
204        open_gas_constraint(self.gas_constraints.at(index))
205    }
206
207    pub fn add_gas_constraint(
208        &self,
209        target: u64,
210        adjustment_window: u64,
211        backlog: u64,
212    ) -> Result<(), ()> {
213        let sto = self.gas_constraints.push()?;
214        let c = open_gas_constraint(sto);
215        c.set_target(target)?;
216        c.set_adjustment_window(adjustment_window)?;
217        c.set_backlog(backlog)?;
218        Ok(())
219    }
220
221    pub fn clear_gas_constraints(&self) -> Result<(), ()> {
222        let len = self.gas_constraints.length()?;
223        for i in 0..len {
224            let c = self.open_gas_constraint_at(i);
225            c.clear()?;
226        }
227        // Reset vector length by popping all
228        for _ in 0..len {
229            self.gas_constraints.pop()?;
230        }
231        Ok(())
232    }
233
234    // --- Multi-Gas Constraints ---
235
236    pub fn multi_gas_constraints_length(&self) -> Result<u64, ()> {
237        self.multi_gas_constraints.length()
238    }
239
240    pub fn open_multi_gas_constraint_at(&self, index: u64) -> MultiGasConstraint<D> {
241        open_multi_gas_constraint(self.multi_gas_constraints.at(index))
242    }
243
244    pub fn add_multi_gas_constraint(
245        &self,
246        target: u64,
247        adjustment_window: u32,
248        backlog: u64,
249        weights: &[u64; NUM_RESOURCE_KIND],
250    ) -> Result<(), ()> {
251        let sto = self.multi_gas_constraints.push()?;
252        let c = open_multi_gas_constraint(sto);
253        c.set_target(target)?;
254        c.set_adjustment_window(adjustment_window)?;
255        c.set_backlog(backlog)?;
256        c.set_resource_weights(weights)?;
257        Ok(())
258    }
259
260    pub fn clear_multi_gas_constraints(&self) -> Result<(), ()> {
261        let len = self.multi_gas_constraints.length()?;
262        for i in 0..len {
263            let c = self.open_multi_gas_constraint_at(i);
264            c.clear()?;
265        }
266        for _ in 0..len {
267            self.multi_gas_constraints.pop()?;
268        }
269        Ok(())
270    }
271
272    pub fn restrict(&self, _err: ()) {
273        // No-op restriction
274    }
275}