1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{Address, U256};
3use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
4
5use crate::storage_slot::{
6 derive_subspace_key, gas_constraints_vec_key, map_slot, multi_gas_base_fees_subspace,
7 multi_gas_constraints_vec_key, subspace_slot, vector_element_field, vector_element_key,
8 vector_length_slot, ARBOS_STATE_ADDRESS, L1_PRICING_SUBSPACE, L2_PRICING_SUBSPACE,
9 ROOT_STORAGE_KEY,
10};
11
12pub const ARBGASINFO_ADDRESS: Address = Address::new([
14 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15 0x00, 0x00, 0x00, 0x6c,
16]);
17
18const GET_L1_BASEFEE_ESTIMATE: [u8; 4] = [0xf5, 0xd6, 0xde, 0xd7]; const GET_L1_GAS_PRICE_ESTIMATE: [u8; 4] = [0x05, 0x5f, 0x36, 0x2f]; const GET_MINIMUM_GAS_PRICE: [u8; 4] = [0xf9, 0x18, 0x37, 0x9a]; const GET_PRICES_IN_WEI: [u8; 4] = [0x41, 0xb2, 0x47, 0xa8]; const GET_GAS_ACCOUNTING_PARAMS: [u8; 4] = [0x61, 0x2a, 0xf1, 0x78]; const GET_CURRENT_TX_L1_FEES: [u8; 4] = [0xc6, 0xf7, 0xde, 0x0e]; const GET_PRICES_IN_ARBGAS: [u8; 4] = [0x02, 0x19, 0x9f, 0x34]; const GET_L1_BASEFEE_ESTIMATE_INERTIA: [u8; 4] = [0x29, 0xeb, 0x31, 0xee]; const GET_GAS_BACKLOG: [u8; 4] = [0x1d, 0x5b, 0x5c, 0x20]; const GET_PRICING_INERTIA: [u8; 4] = [0x3d, 0xfb, 0x45, 0xb9]; const GET_GAS_BACKLOG_TOLERANCE: [u8; 4] = [0x25, 0x75, 0x4f, 0x91]; const GET_L1_PRICING_SURPLUS: [u8; 4] = [0x52, 0x0a, 0xcd, 0xd7]; const GET_PER_BATCH_GAS_CHARGE: [u8; 4] = [0x6e, 0xcc, 0xa4, 0x5a]; const GET_AMORTIZED_COST_CAP_BIPS: [u8; 4] = [0x7a, 0x7d, 0x6b, 0xeb]; const GET_L1_FEES_AVAILABLE: [u8; 4] = [0x5b, 0x39, 0xd2, 0x3c]; const GET_L1_REWARD_RATE: [u8; 4] = [0x8a, 0x5b, 0x1d, 0x28]; const GET_L1_REWARD_RECIPIENT: [u8; 4] = [0x9e, 0x6d, 0x7e, 0x31]; const GET_L1_PRICING_EQUILIBRATION_UNITS: [u8; 4] = [0xad, 0x26, 0xce, 0x90]; const GET_LAST_L1_PRICING_UPDATE_TIME: [u8; 4] = [0x13, 0x8b, 0x47, 0xb4]; const GET_L1_PRICING_FUNDS_DUE_FOR_REWARDS: [u8; 4] = [0x96, 0x3d, 0x60, 0x02]; const GET_L1_PRICING_UNITS_SINCE_UPDATE: [u8; 4] = [0xef, 0xf0, 0x13, 0x06]; const GET_LAST_L1_PRICING_SURPLUS: [u8; 4] = [0x29, 0x87, 0xd0, 0x27]; const GET_MAX_BLOCK_GAS_LIMIT: [u8; 4] = [0x03, 0x71, 0xfd, 0xb4]; const GET_MAX_TX_GAS_LIMIT: [u8; 4] = [0xaa, 0xe1, 0xcd, 0x4c]; const GET_GAS_PRICING_CONSTRAINTS: [u8; 4] = [0x23, 0x20, 0x27, 0xd1]; const GET_MULTI_GAS_PRICING_CONSTRAINTS: [u8; 4] = [0xbb, 0xfc, 0x0a, 0x72]; const GET_MULTI_GAS_BASE_FEE: [u8; 4] = [0xc0, 0xe1, 0x0b, 0xbb]; const GET_PRICES_IN_WEI_WITH_AGG: [u8; 4] = [0xba, 0x9c, 0x91, 0x6e]; const GET_PRICES_IN_ARBGAS_WITH_AGG: [u8; 4] = [0x7a, 0x1e, 0xa7, 0x32]; const SLOAD_GAS: u64 = 800;
52const COPY_GAS: u64 = 3;
53
54const L1_PAY_REWARDS_TO: u64 = 0;
56const L1_INERTIA: u64 = 2;
57const L1_PER_UNIT_REWARD: u64 = 3;
58const L1_PRICE_PER_UNIT: u64 = 7;
59const L1_LAST_SURPLUS: u64 = 8;
60const L1_PER_BATCH_GAS_COST: u64 = 9;
61const L1_AMORTIZED_COST_CAP_BIPS: u64 = 10;
62const L1_EQUILIBRATION_UNITS: u64 = 1;
63const L1_LAST_UPDATE_TIME: u64 = 4;
64const L1_FUNDS_DUE_FOR_REWARDS: u64 = 5;
65const L1_UNITS_SINCE: u64 = 6;
66const L1_FEES_AVAILABLE: u64 = 11;
67
68const L2_SPEED_LIMIT: u64 = 0;
70const L2_PER_BLOCK_GAS_LIMIT: u64 = 1;
71const L2_BASE_FEE: u64 = 2;
72const L2_MIN_BASE_FEE: u64 = 3;
73const L2_GAS_BACKLOG: u64 = 4;
74const L2_PRICING_INERTIA: u64 = 5;
75const L2_BACKLOG_TOLERANCE: u64 = 6;
76const L2_PER_TX_GAS_LIMIT: u64 = 7;
77
78const TX_DATA_NON_ZERO_GAS: u64 = 16;
79const ASSUMED_SIMPLE_TX_SIZE: u64 = 140;
80const STORAGE_WRITE_COST: u64 = 20_000;
81
82const BATCH_POSTER_TABLE_KEY: &[u8] = &[0];
84const TOTAL_FUNDS_DUE_OFFSET: u64 = 0;
86
87const L1_PRICER_FUNDS_POOL_ADDRESS: Address = Address::new([
89 0xa4, 0xb0, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
90 0xff, 0xff, 0xff, 0xff,
91]);
92
93pub fn create_arbgasinfo_precompile() -> DynPrecompile {
94 DynPrecompile::new_stateful(PrecompileId::custom("arbgasinfo"), handler)
95}
96
97fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
98 let gas_limit = input.gas;
99 let data = input.data;
100 if data.len() < 4 {
101 return Err(PrecompileError::other("input too short"));
102 }
103
104 let selector: [u8; 4] = [data[0], data[1], data[2], data[3]];
105
106 let result = match selector {
107 GET_L1_BASEFEE_ESTIMATE | GET_L1_GAS_PRICE_ESTIMATE => {
108 read_l1_field(&mut input, L1_PRICE_PER_UNIT)
109 }
110 GET_MINIMUM_GAS_PRICE => read_l2_field(&mut input, L2_MIN_BASE_FEE),
111 GET_PRICES_IN_WEI | GET_PRICES_IN_WEI_WITH_AGG => handle_prices_in_wei(&mut input),
112 GET_GAS_ACCOUNTING_PARAMS => handle_gas_accounting_params(&mut input),
113 GET_CURRENT_TX_L1_FEES => {
114 let gas_limit = input.gas;
118 let fee_wei = crate::get_current_tx_poster_fee();
119 let fee = U256::from(fee_wei);
120 Ok(PrecompileOutput::new(
121 (SLOAD_GAS + COPY_GAS).min(gas_limit),
122 fee.to_be_bytes::<32>().to_vec().into(),
123 ))
124 }
125 GET_PRICES_IN_ARBGAS | GET_PRICES_IN_ARBGAS_WITH_AGG => handle_prices_in_arbgas(&mut input),
126 GET_L1_BASEFEE_ESTIMATE_INERTIA => read_l1_field(&mut input, L1_INERTIA),
127 GET_GAS_BACKLOG => read_l2_field(&mut input, L2_GAS_BACKLOG),
128 GET_PRICING_INERTIA => read_l2_field(&mut input, L2_PRICING_INERTIA),
129 GET_GAS_BACKLOG_TOLERANCE => read_l2_field(&mut input, L2_BACKLOG_TOLERANCE),
130 GET_L1_PRICING_SURPLUS => handle_l1_pricing_surplus(&mut input),
131 GET_PER_BATCH_GAS_CHARGE => read_l1_field(&mut input, L1_PER_BATCH_GAS_COST),
132 GET_AMORTIZED_COST_CAP_BIPS => read_l1_field(&mut input, L1_AMORTIZED_COST_CAP_BIPS),
133 GET_L1_FEES_AVAILABLE => {
135 if let Some(r) = crate::check_method_version(10, 0) {
136 return r;
137 }
138 read_l1_field(&mut input, L1_FEES_AVAILABLE)
139 }
140 GET_L1_REWARD_RATE => {
142 if let Some(r) = crate::check_method_version(11, 0) {
143 return r;
144 }
145 read_l1_field(&mut input, L1_PER_UNIT_REWARD)
146 }
147 GET_L1_REWARD_RECIPIENT => {
149 if let Some(r) = crate::check_method_version(11, 0) {
150 return r;
151 }
152 read_l1_field(&mut input, L1_PAY_REWARDS_TO)
153 }
154 GET_L1_PRICING_EQUILIBRATION_UNITS => {
156 if let Some(r) = crate::check_method_version(20, 0) {
157 return r;
158 }
159 read_l1_field(&mut input, L1_EQUILIBRATION_UNITS)
160 }
161 GET_LAST_L1_PRICING_UPDATE_TIME => {
163 if let Some(r) = crate::check_method_version(20, 0) {
164 return r;
165 }
166 read_l1_field(&mut input, L1_LAST_UPDATE_TIME)
167 }
168 GET_L1_PRICING_FUNDS_DUE_FOR_REWARDS => {
170 if let Some(r) = crate::check_method_version(20, 0) {
171 return r;
172 }
173 read_l1_field(&mut input, L1_FUNDS_DUE_FOR_REWARDS)
174 }
175 GET_L1_PRICING_UNITS_SINCE_UPDATE => {
177 if let Some(r) = crate::check_method_version(20, 0) {
178 return r;
179 }
180 read_l1_field(&mut input, L1_UNITS_SINCE)
181 }
182 GET_LAST_L1_PRICING_SURPLUS => {
184 if let Some(r) = crate::check_method_version(20, 0) {
185 return r;
186 }
187 read_l1_field(&mut input, L1_LAST_SURPLUS)
188 }
189 GET_MAX_BLOCK_GAS_LIMIT => {
191 if let Some(r) = crate::check_method_version(50, 0) {
192 return r;
193 }
194 read_l2_field(&mut input, L2_PER_BLOCK_GAS_LIMIT)
195 }
196 GET_MAX_TX_GAS_LIMIT => {
198 if let Some(r) = crate::check_method_version(50, 0) {
199 return r;
200 }
201 read_l2_field(&mut input, L2_PER_TX_GAS_LIMIT)
202 }
203 GET_GAS_PRICING_CONSTRAINTS => {
205 if let Some(r) = crate::check_method_version(50, 0) {
206 return r;
207 }
208 handle_gas_pricing_constraints(&mut input)
209 }
210 GET_MULTI_GAS_PRICING_CONSTRAINTS => {
212 if let Some(r) = crate::check_method_version(60, 0) {
213 return r;
214 }
215 handle_multi_gas_pricing_constraints(&mut input)
216 }
217 GET_MULTI_GAS_BASE_FEE => {
219 if let Some(r) = crate::check_method_version(60, 0) {
220 return r;
221 }
222 handle_multi_gas_base_fee(&mut input)
223 }
224 _ => Err(PrecompileError::other("unknown selector")),
225 };
226 crate::gas_check(gas_limit, result)
227}
228
229fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), PrecompileError> {
232 input
233 .internals_mut()
234 .load_account(ARBOS_STATE_ADDRESS)
235 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
236 Ok(())
237}
238
239fn sload_field(input: &mut PrecompileInput<'_>, slot: U256) -> Result<U256, PrecompileError> {
240 let val = input
241 .internals_mut()
242 .sload(ARBOS_STATE_ADDRESS, slot)
243 .map_err(|_| PrecompileError::other("sload failed"))?;
244 Ok(val.data)
245}
246
247fn read_l1_field(input: &mut PrecompileInput<'_>, offset: u64) -> PrecompileResult {
248 let gas_limit = input.gas;
249 load_arbos(input)?;
250 let field_slot = subspace_slot(L1_PRICING_SUBSPACE, offset);
251 let value = sload_field(input, field_slot)?;
252 Ok(PrecompileOutput::new(
253 (2 * SLOAD_GAS + COPY_GAS).min(gas_limit),
254 value.to_be_bytes::<32>().to_vec().into(),
255 ))
256}
257
258fn read_l2_field(input: &mut PrecompileInput<'_>, offset: u64) -> PrecompileResult {
259 let gas_limit = input.gas;
260 load_arbos(input)?;
261 let field_slot = subspace_slot(L2_PRICING_SUBSPACE, offset);
262 let value = sload_field(input, field_slot)?;
263 Ok(PrecompileOutput::new(
264 (2 * SLOAD_GAS + COPY_GAS).min(gas_limit),
265 value.to_be_bytes::<32>().to_vec().into(),
266 ))
267}
268
269fn handle_l1_pricing_surplus(input: &mut PrecompileInput<'_>) -> PrecompileResult {
273 let gas_limit = input.gas;
274 let arbos_version = crate::get_arbos_version();
275
276 load_arbos(input)?;
277
278 let l1_sub_key = derive_subspace_key(ROOT_STORAGE_KEY, L1_PRICING_SUBSPACE);
280 let bpt_key = derive_subspace_key(l1_sub_key.as_slice(), BATCH_POSTER_TABLE_KEY);
281 let total_funds_due_slot = map_slot(bpt_key.as_slice(), TOTAL_FUNDS_DUE_OFFSET);
282 let total_funds_due = sload_field(input, total_funds_due_slot)?;
283
284 let fdr_slot = subspace_slot(L1_PRICING_SUBSPACE, L1_FUNDS_DUE_FOR_REWARDS);
286 let funds_due_for_rewards = sload_field(input, fdr_slot)?;
287
288 let need_funds = total_funds_due.saturating_add(funds_due_for_rewards);
289
290 let have_funds = if arbos_version >= 10 {
291 let slot = subspace_slot(L1_PRICING_SUBSPACE, L1_FEES_AVAILABLE);
293 sload_field(input, slot)?
294 } else {
295 let account = input
297 .internals_mut()
298 .load_account(L1_PRICER_FUNDS_POOL_ADDRESS)
299 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
300 account.data.info.balance
301 };
302
303 let surplus = if have_funds >= need_funds {
305 have_funds - need_funds
306 } else {
307 let deficit = need_funds - have_funds;
309 U256::ZERO.wrapping_sub(deficit)
310 };
311
312 let gas_cost = (4 * SLOAD_GAS + COPY_GAS).min(gas_limit);
313 Ok(PrecompileOutput::new(
314 gas_cost,
315 surplus.to_be_bytes::<32>().to_vec().into(),
316 ))
317}
318
319fn handle_prices_in_wei(input: &mut PrecompileInput<'_>) -> PrecompileResult {
320 let data_len = input.data.len();
321 let gas_limit = input.gas;
322 load_arbos(input)?;
323
324 let l1_price = sload_field(input, subspace_slot(L1_PRICING_SUBSPACE, L1_PRICE_PER_UNIT))?;
325 let l2_base = sload_field(input, subspace_slot(L2_PRICING_SUBSPACE, L2_BASE_FEE))?;
326 let l2_min = sload_field(input, subspace_slot(L2_PRICING_SUBSPACE, L2_MIN_BASE_FEE))?;
327
328 let wei_for_l1_calldata = l1_price.saturating_mul(U256::from(TX_DATA_NON_ZERO_GAS));
329 let per_l2_tx = wei_for_l1_calldata.saturating_mul(U256::from(ASSUMED_SIMPLE_TX_SIZE));
330 let per_arbgas_base = l2_base.min(l2_min);
331 let per_arbgas_congestion = l2_base.saturating_sub(per_arbgas_base);
332 let per_arbgas_total = l2_base;
333 let wei_for_l2_storage = l2_base.saturating_mul(U256::from(STORAGE_WRITE_COST));
334
335 let mut out = Vec::with_capacity(192);
336 out.extend_from_slice(&per_l2_tx.to_be_bytes::<32>());
337 out.extend_from_slice(&wei_for_l1_calldata.to_be_bytes::<32>());
338 out.extend_from_slice(&wei_for_l2_storage.to_be_bytes::<32>());
339 out.extend_from_slice(&per_arbgas_base.to_be_bytes::<32>());
340 out.extend_from_slice(&per_arbgas_congestion.to_be_bytes::<32>());
341 out.extend_from_slice(&per_arbgas_total.to_be_bytes::<32>());
342
343 let arg_words = (data_len as u64).saturating_sub(4).div_ceil(32);
345 let gas_cost = (3 * SLOAD_GAS + (arg_words + 6) * COPY_GAS).min(gas_limit);
346 Ok(PrecompileOutput::new(gas_cost, out.into()))
347}
348
349fn handle_gas_accounting_params(input: &mut PrecompileInput<'_>) -> PrecompileResult {
350 let gas_limit = input.gas;
351 load_arbos(input)?;
352
353 let speed_limit = sload_field(input, subspace_slot(L2_PRICING_SUBSPACE, L2_SPEED_LIMIT))?;
354 let gas_limit_val = sload_field(
355 input,
356 subspace_slot(L2_PRICING_SUBSPACE, L2_PER_BLOCK_GAS_LIMIT),
357 )?;
358
359 let mut out = Vec::with_capacity(96);
360 out.extend_from_slice(&speed_limit.to_be_bytes::<32>());
361 out.extend_from_slice(&gas_limit_val.to_be_bytes::<32>());
362 out.extend_from_slice(&gas_limit_val.to_be_bytes::<32>());
363
364 Ok(PrecompileOutput::new(
365 (3 * SLOAD_GAS + 3 * COPY_GAS).min(gas_limit),
366 out.into(),
367 ))
368}
369
370fn handle_prices_in_arbgas(input: &mut PrecompileInput<'_>) -> PrecompileResult {
371 let data_len = input.data.len();
372 let gas_limit = input.gas;
373 load_arbos(input)?;
374
375 let l1_price = sload_field(input, subspace_slot(L1_PRICING_SUBSPACE, L1_PRICE_PER_UNIT))?;
376 let l2_base = sload_field(input, subspace_slot(L2_PRICING_SUBSPACE, L2_BASE_FEE))?;
377
378 let wei_for_l1_calldata = l1_price.saturating_mul(U256::from(TX_DATA_NON_ZERO_GAS));
379 let wei_per_l2_tx = wei_for_l1_calldata.saturating_mul(U256::from(ASSUMED_SIMPLE_TX_SIZE));
380
381 let (gas_for_l1_calldata, gas_per_l2_tx) = if l2_base > U256::ZERO {
382 (wei_for_l1_calldata / l2_base, wei_per_l2_tx / l2_base)
383 } else {
384 (U256::ZERO, U256::ZERO)
385 };
386
387 let mut out = Vec::with_capacity(96);
388 out.extend_from_slice(&gas_per_l2_tx.to_be_bytes::<32>());
389 out.extend_from_slice(&gas_for_l1_calldata.to_be_bytes::<32>());
390 out.extend_from_slice(&U256::from(STORAGE_WRITE_COST).to_be_bytes::<32>());
391
392 let arg_words = (data_len as u64).saturating_sub(4).div_ceil(32);
394 let gas_cost = (2 * SLOAD_GAS + (arg_words + 3) * COPY_GAS).min(gas_limit);
395 Ok(PrecompileOutput::new(gas_cost, out.into()))
396}
397
398const CONSTRAINT_TARGET: u64 = 0;
402const CONSTRAINT_ADJ_WINDOW: u64 = 1;
403const CONSTRAINT_BACKLOG: u64 = 2;
404const MULTI_CONSTRAINT_WEIGHTED_BASE: u64 = 4;
405
406const NUM_RESOURCE_KIND: u64 = 8;
407const CURRENT_BLOCK_FEES_OFFSET: u64 = NUM_RESOURCE_KIND;
409
410fn handle_gas_pricing_constraints(input: &mut PrecompileInput<'_>) -> PrecompileResult {
412 let gas_limit = input.gas;
413 load_arbos(input)?;
414
415 let vec_key = gas_constraints_vec_key();
416 let count = sload_field(input, vector_length_slot(&vec_key))?.saturating_to::<u64>();
417 let mut sloads: u64 = 2; let mut out = Vec::with_capacity(64 + count as usize * 96);
421 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
422 out.extend_from_slice(&U256::from(count).to_be_bytes::<32>());
423
424 for i in 0..count {
425 let target = sload_field(input, vector_element_field(&vec_key, i, CONSTRAINT_TARGET))?;
426 let window = sload_field(
427 input,
428 vector_element_field(&vec_key, i, CONSTRAINT_ADJ_WINDOW),
429 )?;
430 let backlog = sload_field(input, vector_element_field(&vec_key, i, CONSTRAINT_BACKLOG))?;
431
432 out.extend_from_slice(&target.to_be_bytes::<32>());
433 out.extend_from_slice(&window.to_be_bytes::<32>());
434 out.extend_from_slice(&backlog.to_be_bytes::<32>());
435 sloads += 3;
436 }
437
438 let result_words = (out.len() as u64).div_ceil(32);
439 Ok(PrecompileOutput::new(
440 (sloads * SLOAD_GAS + result_words * COPY_GAS).min(gas_limit),
441 out.into(),
442 ))
443}
444
445fn handle_multi_gas_pricing_constraints(input: &mut PrecompileInput<'_>) -> PrecompileResult {
451 let gas_limit = input.gas;
452 load_arbos(input)?;
453
454 let vec_key = multi_gas_constraints_vec_key();
455 let count = sload_field(input, vector_length_slot(&vec_key))?.saturating_to::<u64>();
456 let mut sloads: u64 = 2; struct ConstraintData {
460 target: U256,
461 window: U256,
462 backlog: U256,
463 resources: Vec<(u8, U256)>,
464 }
465 let mut constraints = Vec::with_capacity(count as usize);
466
467 for i in 0..count {
468 let target = sload_field(input, vector_element_field(&vec_key, i, CONSTRAINT_TARGET))?;
469 let window = sload_field(
470 input,
471 vector_element_field(&vec_key, i, CONSTRAINT_ADJ_WINDOW),
472 )?;
473 let backlog = sload_field(input, vector_element_field(&vec_key, i, CONSTRAINT_BACKLOG))?;
474 sloads += 3;
475
476 let elem_key = vector_element_key(&vec_key, i);
477 let mut resources = Vec::new();
478 for kind in 0..NUM_RESOURCE_KIND {
479 let w = sload_field(
480 input,
481 map_slot(elem_key.as_slice(), MULTI_CONSTRAINT_WEIGHTED_BASE + kind),
482 )?;
483 sloads += 1;
484 if w > U256::ZERO {
485 resources.push((kind as u8, w));
486 }
487 }
488 constraints.push(ConstraintData {
489 target,
490 window,
491 backlog,
492 resources,
493 });
494 }
495
496 let n = constraints.len();
498 let mut out = Vec::new();
499
500 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
502 out.extend_from_slice(&U256::from(n).to_be_bytes::<32>());
504
505 let elem_sizes: Vec<usize> = constraints
507 .iter()
508 .map(|c| 4 * 32 + 32 + c.resources.len() * 64)
509 .collect();
510
511 let mut running_offset = n * 32;
513 for size in &elem_sizes {
514 out.extend_from_slice(&U256::from(running_offset).to_be_bytes::<32>());
515 running_offset += size;
516 }
517
518 for c in &constraints {
520 let m = c.resources.len();
521 out.extend_from_slice(&U256::from(4u64 * 32).to_be_bytes::<32>());
523 out.extend_from_slice(&c.window.to_be_bytes::<32>());
525 out.extend_from_slice(&c.target.to_be_bytes::<32>());
527 out.extend_from_slice(&c.backlog.to_be_bytes::<32>());
529 out.extend_from_slice(&U256::from(m).to_be_bytes::<32>());
531 for &(kind, ref weight) in &c.resources {
533 out.extend_from_slice(&U256::from(kind).to_be_bytes::<32>());
534 out.extend_from_slice(&weight.to_be_bytes::<32>());
535 }
536 }
537
538 let result_words = (out.len() as u64).div_ceil(32);
539 Ok(PrecompileOutput::new(
540 (sloads * SLOAD_GAS + result_words * COPY_GAS).min(gas_limit),
541 out.into(),
542 ))
543}
544
545fn handle_multi_gas_base_fee(input: &mut PrecompileInput<'_>) -> PrecompileResult {
547 let gas_limit = input.gas;
548 load_arbos(input)?;
549
550 let fees_key = multi_gas_base_fees_subspace();
551
552 let mut out = Vec::with_capacity(64 + NUM_RESOURCE_KIND as usize * 32);
553 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
555 out.extend_from_slice(&U256::from(NUM_RESOURCE_KIND).to_be_bytes::<32>());
556
557 for kind in 0..NUM_RESOURCE_KIND {
558 let slot = map_slot(fees_key.as_slice(), CURRENT_BLOCK_FEES_OFFSET + kind);
559 let fee = sload_field(input, slot)?;
560 out.extend_from_slice(&fee.to_be_bytes::<32>());
561 }
562
563 let result_words = (out.len() as u64).div_ceil(32);
564 Ok(PrecompileOutput::new(
565 ((1 + NUM_RESOURCE_KIND) * SLOAD_GAS + result_words * COPY_GAS).min(gas_limit),
566 out.into(),
567 ))
568}