1use std::sync::Arc;
2
3use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
4use alloy_primitives::{Address, U256};
5use alloy_sol_types::SolInterface;
6use arb_context::ArbPrecompileCtx;
7use arb_storage::ARBOS_STATE_ADDRESS;
8use revm::{
9 context_interface::block::Block,
10 precompile::{PrecompileId, PrecompileOutput, PrecompileResult},
11};
12
13use crate::{ArbPrecompileError, interfaces::IArbGasInfo};
14
15pub const ARBGASINFO_ADDRESS: Address = Address::new([
17 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
18 0x00, 0x00, 0x00, 0x6c,
19]);
20
21const SLOAD_GAS: u64 = 800;
22const COPY_GAS: u64 = 3;
23
24const TX_DATA_NON_ZERO_GAS: u64 = 16;
25const ASSUMED_SIMPLE_TX_SIZE: u64 = 140;
26const STORAGE_WRITE_COST: u64 = 20_000;
27
28use arbos::l1_pricing::L1_PRICER_FUNDS_POOL_ADDRESS;
29
30pub fn create_arbgasinfo_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
31 DynPrecompile::new_stateful(PrecompileId::custom("arbgasinfo"), move |input| {
32 handler(input, &ctx)
33 })
34}
35
36fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
37 let mut gas_used = 0u64;
38 let gas_limit = input.gas;
39 crate::init_precompile_gas(&mut gas_used, ctx, input.data.len());
40
41 let call = match IArbGasInfo::ArbGasInfoCalls::abi_decode(input.data) {
42 Ok(c) => c,
43 Err(_) => return crate::burn_all_revert(gas_limit),
44 };
45 if let Some(r) = crate::reject_nonpayable_value(input.value, input.data, gas_limit, &[]) {
46 return r;
47 }
48 if let Some(r) = crate::reject_delegate_nonpure(
49 input.target_address != input.bytecode_address,
50 input.data,
51 gas_limit,
52 &[],
53 ) {
54 return r;
55 }
56
57 use IArbGasInfo::ArbGasInfoCalls as Calls;
58 let result = match call {
59 Calls::getL1BaseFeeEstimate(_) | Calls::getL1GasPriceEstimate(_) => {
60 read_l1_price_per_unit(&mut input, &mut gas_used, ctx)
61 }
62 Calls::getMinimumGasPrice(_) => read_l2_min_base_fee(&mut input, &mut gas_used, ctx),
63 Calls::getPricesInWei(_) | Calls::getPricesInWeiWithAggregator(_) => {
64 handle_prices_in_wei(&mut input, &mut gas_used, ctx)
65 }
66 Calls::getGasAccountingParams(_) => {
67 handle_gas_accounting_params(&mut input, &mut gas_used, ctx)
68 }
69 Calls::getCurrentTxL1GasFees(_) => {
70 let fee = U256::from(ctx.tx_snapshot().poster_fee);
71 crate::charge_computation(&mut gas_used, ctx, COPY_GAS);
72 Ok(PrecompileOutput::new(
73 gas_used.min(gas_limit),
74 fee.to_be_bytes::<32>().to_vec().into(),
75 ))
76 }
77 Calls::getPricesInArbGas(_) | Calls::getPricesInArbGasWithAggregator(_) => {
78 handle_prices_in_arbgas(&mut input, &mut gas_used, ctx)
79 }
80 Calls::getL1BaseFeeEstimateInertia(_) => read_l1_inertia(&mut input, &mut gas_used, ctx),
81 Calls::getGasBacklog(_) => read_l2_gas_backlog(&mut input, &mut gas_used, ctx),
82 Calls::getPricingInertia(_) => read_l2_pricing_inertia(&mut input, &mut gas_used, ctx),
83 Calls::getGasBacklogTolerance(_) => {
84 read_l2_backlog_tolerance(&mut input, &mut gas_used, ctx)
85 }
86 Calls::getL1PricingSurplus(_) => handle_l1_pricing_surplus(&mut input, &mut gas_used, ctx),
87 Calls::getPerBatchGasCharge(_) => {
88 read_l1_per_batch_gas_cost(&mut input, &mut gas_used, ctx)
89 }
90 Calls::getAmortizedCostCapBips(_) => {
91 read_l1_amortized_cost_cap_bips(&mut input, &mut gas_used, ctx)
92 }
93 Calls::getL1FeesAvailable(_) => {
94 if let Some(r) = crate::check_method_version(ctx, gas_limit, 10, 0) {
95 return r;
96 }
97 read_l1_fees_available(&mut input, &mut gas_used, ctx)
98 }
99 Calls::getL1RewardRate(_) => {
100 if let Some(r) = crate::check_method_version(ctx, gas_limit, 11, 0) {
101 return r;
102 }
103 read_l1_per_unit_reward(&mut input, &mut gas_used, ctx)
104 }
105 Calls::getL1RewardRecipient(_) => {
106 if let Some(r) = crate::check_method_version(ctx, gas_limit, 11, 0) {
107 return r;
108 }
109 read_l1_pay_rewards_to(&mut input, &mut gas_used, ctx)
110 }
111 Calls::getL1PricingEquilibrationUnits(_) => {
112 if let Some(r) = crate::check_method_version(ctx, gas_limit, 20, 0) {
113 return r;
114 }
115 read_l1_equilibration_units(&mut input, &mut gas_used, ctx)
116 }
117 Calls::getLastL1PricingUpdateTime(_) => {
118 if let Some(r) = crate::check_method_version(ctx, gas_limit, 20, 0) {
119 return r;
120 }
121 read_l1_last_update_time(&mut input, &mut gas_used, ctx)
122 }
123 Calls::getL1PricingFundsDueForRewards(_) => {
124 if let Some(r) = crate::check_method_version(ctx, gas_limit, 20, 0) {
125 return r;
126 }
127 read_l1_funds_due_for_rewards(&mut input, &mut gas_used, ctx)
128 }
129 Calls::getL1PricingUnitsSinceUpdate(_) => {
130 if let Some(r) = crate::check_method_version(ctx, gas_limit, 20, 0) {
131 return r;
132 }
133 read_l1_units_since_update(&mut input, &mut gas_used, ctx)
134 }
135 Calls::getLastL1PricingSurplus(_) => {
136 if let Some(r) = crate::check_method_version(ctx, gas_limit, 20, 0) {
137 return r;
138 }
139 read_l1_last_surplus(&mut input, &mut gas_used, ctx)
140 }
141 Calls::getMaxBlockGasLimit(_) => {
142 if let Some(r) = crate::check_method_version(ctx, gas_limit, 50, 0) {
143 return r;
144 }
145 read_l2_per_block_gas_limit(&mut input, &mut gas_used, ctx)
146 }
147 Calls::getMaxTxGasLimit(_) => {
148 if let Some(r) = crate::check_method_version(ctx, gas_limit, 50, 0) {
149 return r;
150 }
151 read_l2_per_tx_gas_limit(&mut input, &mut gas_used, ctx)
152 }
153 Calls::getGasPricingConstraints(_) => {
154 if let Some(r) = crate::check_method_version(ctx, gas_limit, 50, 0) {
155 return r;
156 }
157 handle_gas_pricing_constraints(&mut input, &mut gas_used, ctx)
158 }
159 Calls::getMultiGasPricingConstraints(_) => {
160 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
161 return r;
162 }
163 handle_multi_gas_pricing_constraints(&mut input, &mut gas_used, ctx)
164 }
165 Calls::getMultiGasBaseFee(_) => {
166 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
167 return r;
168 }
169 handle_multi_gas_base_fee(&mut input, &mut gas_used, ctx)
170 }
171 };
172 crate::gas_check(ctx, gas_limit, gas_used, result)
173}
174
175fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), ArbPrecompileError> {
178 input
179 .internals_mut()
180 .load_account(ARBOS_STATE_ADDRESS)
181 .map_err(ArbPrecompileError::fatal)?;
182 Ok(())
183}
184
185fn field_read_output(
186 gas_used: &mut u64,
187 ctx: &ArbPrecompileCtx,
188 gas_limit: u64,
189 value: U256,
190) -> PrecompileResult {
191 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
194 crate::charge_computation(gas_used, ctx, COPY_GAS);
195 Ok(PrecompileOutput::new(
196 (*gas_used).min(gas_limit),
197 value.to_be_bytes::<32>().to_vec().into(),
198 ))
199}
200
201fn read_l1_price_per_unit(
204 input: &mut PrecompileInput<'_>,
205 gas_used: &mut u64,
206 ctx: &ArbPrecompileCtx,
207) -> PrecompileResult {
208 let gas_limit = input.gas;
209 load_arbos(input)?;
210 let internals = input.internals_mut();
211 let arb_state = ctx
212 .block
213 .arbos_state(internals)
214 .map_err(ArbPrecompileError::fatal)?;
215 let value = arb_state
216 .l1_pricing_state
217 .price_per_unit(internals)
218 .map_err(ArbPrecompileError::fatal)?;
219 field_read_output(gas_used, ctx, gas_limit, value)
220}
221
222fn read_l1_inertia(
223 input: &mut PrecompileInput<'_>,
224 gas_used: &mut u64,
225 ctx: &ArbPrecompileCtx,
226) -> PrecompileResult {
227 let gas_limit = input.gas;
228 load_arbos(input)?;
229 let internals = input.internals_mut();
230 let arb_state = ctx
231 .block
232 .arbos_state(internals)
233 .map_err(ArbPrecompileError::fatal)?;
234 let value = arb_state
235 .l1_pricing_state
236 .inertia(internals)
237 .map_err(ArbPrecompileError::fatal)?;
238 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
239}
240
241fn read_l1_per_unit_reward(
242 input: &mut PrecompileInput<'_>,
243 gas_used: &mut u64,
244 ctx: &ArbPrecompileCtx,
245) -> PrecompileResult {
246 let gas_limit = input.gas;
247 load_arbos(input)?;
248 let internals = input.internals_mut();
249 let arb_state = ctx
250 .block
251 .arbos_state(internals)
252 .map_err(ArbPrecompileError::fatal)?;
253 let value = arb_state
254 .l1_pricing_state
255 .per_unit_reward(internals)
256 .map_err(ArbPrecompileError::fatal)?;
257 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
258}
259
260fn read_l1_pay_rewards_to(
261 input: &mut PrecompileInput<'_>,
262 gas_used: &mut u64,
263 ctx: &ArbPrecompileCtx,
264) -> PrecompileResult {
265 let gas_limit = input.gas;
266 load_arbos(input)?;
267 let internals = input.internals_mut();
268 let arb_state = ctx
269 .block
270 .arbos_state(internals)
271 .map_err(ArbPrecompileError::fatal)?;
272 let addr = arb_state
273 .l1_pricing_state
274 .pay_rewards_to(internals)
275 .map_err(ArbPrecompileError::fatal)?;
276 field_read_output(
277 gas_used,
278 ctx,
279 gas_limit,
280 U256::from_be_slice(addr.as_slice()),
281 )
282}
283
284fn read_l1_last_surplus(
285 input: &mut PrecompileInput<'_>,
286 gas_used: &mut u64,
287 ctx: &ArbPrecompileCtx,
288) -> PrecompileResult {
289 let gas_limit = input.gas;
290 load_arbos(input)?;
291 let internals = input.internals_mut();
292 let arb_state = ctx
293 .block
294 .arbos_state(internals)
295 .map_err(ArbPrecompileError::fatal)?;
296 let (magnitude, negative) = arb_state
297 .l1_pricing_state
298 .last_surplus(internals)
299 .map_err(ArbPrecompileError::fatal)?;
300 let raw = if negative {
301 U256::ZERO.wrapping_sub(magnitude)
302 } else {
303 magnitude
304 };
305 field_read_output(gas_used, ctx, gas_limit, raw)
306}
307
308fn read_l1_per_batch_gas_cost(
309 input: &mut PrecompileInput<'_>,
310 gas_used: &mut u64,
311 ctx: &ArbPrecompileCtx,
312) -> PrecompileResult {
313 let gas_limit = input.gas;
314 load_arbos(input)?;
315 let internals = input.internals_mut();
316 let arb_state = ctx
317 .block
318 .arbos_state(internals)
319 .map_err(ArbPrecompileError::fatal)?;
320 let value = arb_state
321 .l1_pricing_state
322 .per_batch_gas_cost(internals)
323 .map_err(ArbPrecompileError::fatal)?;
324 field_read_output(gas_used, ctx, gas_limit, U256::from(value as u64))
325}
326
327fn read_l1_amortized_cost_cap_bips(
328 input: &mut PrecompileInput<'_>,
329 gas_used: &mut u64,
330 ctx: &ArbPrecompileCtx,
331) -> PrecompileResult {
332 let gas_limit = input.gas;
333 load_arbos(input)?;
334 let internals = input.internals_mut();
335 let arb_state = ctx
336 .block
337 .arbos_state(internals)
338 .map_err(ArbPrecompileError::fatal)?;
339 let value = arb_state
340 .l1_pricing_state
341 .amortized_cost_cap_bips(internals)
342 .map_err(ArbPrecompileError::fatal)?;
343 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
344}
345
346fn read_l1_equilibration_units(
347 input: &mut PrecompileInput<'_>,
348 gas_used: &mut u64,
349 ctx: &ArbPrecompileCtx,
350) -> PrecompileResult {
351 let gas_limit = input.gas;
352 load_arbos(input)?;
353 let internals = input.internals_mut();
354 let arb_state = ctx
355 .block
356 .arbos_state(internals)
357 .map_err(ArbPrecompileError::fatal)?;
358 let value = arb_state
359 .l1_pricing_state
360 .equilibration_units(internals)
361 .map_err(ArbPrecompileError::fatal)?;
362 field_read_output(gas_used, ctx, gas_limit, value)
363}
364
365fn read_l1_last_update_time(
366 input: &mut PrecompileInput<'_>,
367 gas_used: &mut u64,
368 ctx: &ArbPrecompileCtx,
369) -> PrecompileResult {
370 let gas_limit = input.gas;
371 load_arbos(input)?;
372 let internals = input.internals_mut();
373 let arb_state = ctx
374 .block
375 .arbos_state(internals)
376 .map_err(ArbPrecompileError::fatal)?;
377 let value = arb_state
378 .l1_pricing_state
379 .last_update_time(internals)
380 .map_err(ArbPrecompileError::fatal)?;
381 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
382}
383
384fn read_l1_funds_due_for_rewards(
385 input: &mut PrecompileInput<'_>,
386 gas_used: &mut u64,
387 ctx: &ArbPrecompileCtx,
388) -> PrecompileResult {
389 let gas_limit = input.gas;
390 load_arbos(input)?;
391 let internals = input.internals_mut();
392 let arb_state = ctx
393 .block
394 .arbos_state(internals)
395 .map_err(ArbPrecompileError::fatal)?;
396 let value = arb_state
397 .l1_pricing_state
398 .funds_due_for_rewards(internals)
399 .map_err(ArbPrecompileError::fatal)?;
400 field_read_output(gas_used, ctx, gas_limit, value)
401}
402
403fn read_l1_units_since_update(
404 input: &mut PrecompileInput<'_>,
405 gas_used: &mut u64,
406 ctx: &ArbPrecompileCtx,
407) -> PrecompileResult {
408 let gas_limit = input.gas;
409 load_arbos(input)?;
410 let internals = input.internals_mut();
411 let arb_state = ctx
412 .block
413 .arbos_state(internals)
414 .map_err(ArbPrecompileError::fatal)?;
415 let value = arb_state
416 .l1_pricing_state
417 .units_since_update(internals)
418 .map_err(ArbPrecompileError::fatal)?;
419 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
420}
421
422fn read_l1_fees_available(
423 input: &mut PrecompileInput<'_>,
424 gas_used: &mut u64,
425 ctx: &ArbPrecompileCtx,
426) -> PrecompileResult {
427 let gas_limit = input.gas;
428 load_arbos(input)?;
429 let internals = input.internals_mut();
430 let arb_state = ctx
431 .block
432 .arbos_state(internals)
433 .map_err(ArbPrecompileError::fatal)?;
434 let value = arb_state
435 .l1_pricing_state
436 .l1_fees_available(internals)
437 .map_err(ArbPrecompileError::fatal)?;
438 field_read_output(gas_used, ctx, gas_limit, value)
439}
440
441fn read_l2_min_base_fee(
444 input: &mut PrecompileInput<'_>,
445 gas_used: &mut u64,
446 ctx: &ArbPrecompileCtx,
447) -> PrecompileResult {
448 let gas_limit = input.gas;
449 load_arbos(input)?;
450 let internals = input.internals_mut();
451 let arb_state = ctx
452 .block
453 .arbos_state(internals)
454 .map_err(ArbPrecompileError::fatal)?;
455 let value = arb_state
456 .l2_pricing_state
457 .min_base_fee_wei(internals)
458 .map_err(ArbPrecompileError::fatal)?;
459 field_read_output(gas_used, ctx, gas_limit, value)
460}
461
462fn read_l2_gas_backlog(
463 input: &mut PrecompileInput<'_>,
464 gas_used: &mut u64,
465 ctx: &ArbPrecompileCtx,
466) -> PrecompileResult {
467 let gas_limit = input.gas;
468 load_arbos(input)?;
469 let internals = input.internals_mut();
470 let arb_state = ctx
471 .block
472 .arbos_state(internals)
473 .map_err(ArbPrecompileError::fatal)?;
474 let value = arb_state
475 .l2_pricing_state
476 .gas_backlog(internals)
477 .map_err(ArbPrecompileError::fatal)?;
478 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
479}
480
481fn read_l2_pricing_inertia(
482 input: &mut PrecompileInput<'_>,
483 gas_used: &mut u64,
484 ctx: &ArbPrecompileCtx,
485) -> PrecompileResult {
486 let gas_limit = input.gas;
487 load_arbos(input)?;
488 let internals = input.internals_mut();
489 let arb_state = ctx
490 .block
491 .arbos_state(internals)
492 .map_err(ArbPrecompileError::fatal)?;
493 let value = arb_state
494 .l2_pricing_state
495 .pricing_inertia(internals)
496 .map_err(ArbPrecompileError::fatal)?;
497 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
498}
499
500fn read_l2_backlog_tolerance(
501 input: &mut PrecompileInput<'_>,
502 gas_used: &mut u64,
503 ctx: &ArbPrecompileCtx,
504) -> PrecompileResult {
505 let gas_limit = input.gas;
506 load_arbos(input)?;
507 let internals = input.internals_mut();
508 let arb_state = ctx
509 .block
510 .arbos_state(internals)
511 .map_err(ArbPrecompileError::fatal)?;
512 let value = arb_state
513 .l2_pricing_state
514 .backlog_tolerance(internals)
515 .map_err(ArbPrecompileError::fatal)?;
516 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
517}
518
519fn read_l2_per_block_gas_limit(
520 input: &mut PrecompileInput<'_>,
521 gas_used: &mut u64,
522 ctx: &ArbPrecompileCtx,
523) -> PrecompileResult {
524 let gas_limit = input.gas;
525 load_arbos(input)?;
526 let internals = input.internals_mut();
527 let arb_state = ctx
528 .block
529 .arbos_state(internals)
530 .map_err(ArbPrecompileError::fatal)?;
531 let value = arb_state
532 .l2_pricing_state
533 .per_block_gas_limit(internals)
534 .map_err(ArbPrecompileError::fatal)?;
535 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
536}
537
538fn read_l2_per_tx_gas_limit(
539 input: &mut PrecompileInput<'_>,
540 gas_used: &mut u64,
541 ctx: &ArbPrecompileCtx,
542) -> PrecompileResult {
543 let gas_limit = input.gas;
544 load_arbos(input)?;
545 let internals = input.internals_mut();
546 let arb_state = ctx
547 .block
548 .arbos_state(internals)
549 .map_err(ArbPrecompileError::fatal)?;
550 let value = arb_state
551 .l2_pricing_state
552 .per_tx_gas_limit(internals)
553 .map_err(ArbPrecompileError::fatal)?;
554 field_read_output(gas_used, ctx, gas_limit, U256::from(value))
555}
556
557fn handle_l1_pricing_surplus(
563 input: &mut PrecompileInput<'_>,
564 gas_used: &mut u64,
565 ctx: &ArbPrecompileCtx,
566) -> PrecompileResult {
567 let gas_limit = input.gas;
568 let arbos_version = ctx.block.arbos_version;
569 load_arbos(input)?;
570
571 let internals = input.internals_mut();
572 let arb_state = ctx
573 .block
574 .arbos_state(internals)
575 .map_err(ArbPrecompileError::fatal)?;
576
577 let bpt = arb_state.l1_pricing_state.batch_poster_table();
578 let total_funds_due = bpt
579 .total_funds_due(internals)
580 .map_err(ArbPrecompileError::fatal)?;
581 let funds_due_for_rewards = arb_state
582 .l1_pricing_state
583 .funds_due_for_rewards(internals)
584 .map_err(ArbPrecompileError::fatal)?;
585 let need_funds = total_funds_due.saturating_add(funds_due_for_rewards);
586
587 let have_funds = if arbos_version >= 10 {
588 arb_state
589 .l1_pricing_state
590 .l1_fees_available(internals)
591 .map_err(ArbPrecompileError::fatal)?
592 } else {
593 let account = internals
594 .load_account(L1_PRICER_FUNDS_POOL_ADDRESS)
595 .map_err(ArbPrecompileError::fatal)?;
596 account.data.info.balance
597 };
598
599 let surplus = if have_funds >= need_funds {
600 have_funds - need_funds
601 } else {
602 let deficit = need_funds - have_funds;
603 U256::ZERO.wrapping_sub(deficit)
604 };
605
606 let body_sloads = if arbos_version >= 10 { 3 } else { 2 };
608 crate::charge_storage_read(gas_used, ctx, body_sloads * SLOAD_GAS);
609 crate::charge_computation(gas_used, ctx, COPY_GAS);
610 Ok(PrecompileOutput::new(
611 (*gas_used).min(gas_limit),
612 surplus.to_be_bytes::<32>().to_vec().into(),
613 ))
614}
615
616fn handle_prices_in_wei(
617 input: &mut PrecompileInput<'_>,
618 gas_used: &mut u64,
619 ctx: &ArbPrecompileCtx,
620) -> PrecompileResult {
621 let data_len = input.data.len();
622 let gas_limit = input.gas;
623 let arbos_version = ctx.block.arbos_version;
624
625 let block_basefee = U256::from(input.internals().block_env().basefee());
629 load_arbos(input)?;
630
631 let internals = input.internals_mut();
632 let arb_state = ctx
633 .block
634 .arbos_state(internals)
635 .map_err(ArbPrecompileError::fatal)?;
636
637 let l1_price = arb_state
638 .l1_pricing_state
639 .price_per_unit(internals)
640 .map_err(ArbPrecompileError::fatal)?;
641
642 let read_min_base = arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_4;
644 let l2_min = if read_min_base {
645 arb_state
646 .l2_pricing_state
647 .min_base_fee_wei(internals)
648 .map_err(ArbPrecompileError::fatal)?
649 } else {
650 U256::ZERO
651 };
652 let l2_gas_price = if block_basefee.is_zero() {
653 arb_state
654 .l2_pricing_state
655 .base_fee_wei(internals)
656 .map_err(ArbPrecompileError::fatal)?
657 } else {
658 block_basefee
659 };
660
661 let wei_for_l1_calldata = l1_price.saturating_mul(U256::from(TX_DATA_NON_ZERO_GAS));
662 let per_l2_tx = wei_for_l1_calldata.saturating_mul(U256::from(ASSUMED_SIMPLE_TX_SIZE));
663 let (per_arbgas_base, per_arbgas_congestion) = if read_min_base {
664 let base = l2_gas_price.min(l2_min);
665 (base, l2_gas_price.saturating_sub(base))
666 } else {
667 (l2_gas_price, U256::ZERO)
668 };
669 let per_arbgas_total = l2_gas_price;
670 let wei_for_l2_storage = l2_gas_price.saturating_mul(U256::from(STORAGE_WRITE_COST));
671
672 let mut out = Vec::with_capacity(192);
673 out.extend_from_slice(&per_l2_tx.to_be_bytes::<32>());
674 out.extend_from_slice(&wei_for_l1_calldata.to_be_bytes::<32>());
675 out.extend_from_slice(&wei_for_l2_storage.to_be_bytes::<32>());
676 out.extend_from_slice(&per_arbgas_base.to_be_bytes::<32>());
677 out.extend_from_slice(&per_arbgas_congestion.to_be_bytes::<32>());
678 out.extend_from_slice(&per_arbgas_total.to_be_bytes::<32>());
679
680 let _ = data_len;
682 let body_sloads = if read_min_base { 2 } else { 1 };
683 crate::charge_storage_read(gas_used, ctx, body_sloads * SLOAD_GAS);
684 crate::charge_computation(gas_used, ctx, 6 * COPY_GAS);
685 Ok(PrecompileOutput::new(
686 (*gas_used).min(gas_limit),
687 out.into(),
688 ))
689}
690
691fn handle_gas_accounting_params(
692 input: &mut PrecompileInput<'_>,
693 gas_used: &mut u64,
694 ctx: &ArbPrecompileCtx,
695) -> PrecompileResult {
696 let gas_limit = input.gas;
697 load_arbos(input)?;
698
699 let internals = input.internals_mut();
700 let arb_state = ctx
701 .block
702 .arbos_state(internals)
703 .map_err(ArbPrecompileError::fatal)?;
704 let speed_limit = arb_state
705 .l2_pricing_state
706 .speed_limit_per_second(internals)
707 .map_err(ArbPrecompileError::fatal)?;
708 let gas_limit_val = arb_state
709 .l2_pricing_state
710 .per_block_gas_limit(internals)
711 .map_err(ArbPrecompileError::fatal)?;
712
713 let speed_word = U256::from(speed_limit);
714 let limit_word = U256::from(gas_limit_val);
715 let mut out = Vec::with_capacity(96);
716 out.extend_from_slice(&speed_word.to_be_bytes::<32>());
717 out.extend_from_slice(&limit_word.to_be_bytes::<32>());
718 out.extend_from_slice(&limit_word.to_be_bytes::<32>());
719
720 crate::charge_storage_read(gas_used, ctx, 2 * SLOAD_GAS);
721 crate::charge_computation(gas_used, ctx, 3 * COPY_GAS);
722 Ok(PrecompileOutput::new(
723 (*gas_used).min(gas_limit),
724 out.into(),
725 ))
726}
727
728fn handle_prices_in_arbgas(
729 input: &mut PrecompileInput<'_>,
730 gas_used: &mut u64,
731 ctx: &ArbPrecompileCtx,
732) -> PrecompileResult {
733 let data_len = input.data.len();
734 let gas_limit = input.gas;
735
736 let block_basefee = U256::from(input.internals().block_env().basefee());
737 load_arbos(input)?;
738
739 let internals = input.internals_mut();
740 let arb_state = ctx
741 .block
742 .arbos_state(internals)
743 .map_err(ArbPrecompileError::fatal)?;
744 let l1_price = arb_state
745 .l1_pricing_state
746 .price_per_unit(internals)
747 .map_err(ArbPrecompileError::fatal)?;
748 let l2_gas_price = if block_basefee.is_zero() {
749 arb_state
750 .l2_pricing_state
751 .base_fee_wei(internals)
752 .map_err(ArbPrecompileError::fatal)?
753 } else {
754 block_basefee
755 };
756
757 let arbos_version = ctx.block.arbos_version;
758 let wei_for_l1_calldata = l1_price.saturating_mul(U256::from(TX_DATA_NON_ZERO_GAS));
759
760 let gas_for_l1_calldata = if l2_gas_price > U256::ZERO {
761 wei_for_l1_calldata / l2_gas_price
762 } else {
763 U256::ZERO
764 };
765 let gas_per_l2_tx = if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_4 {
768 let wei_per_l2_tx = wei_for_l1_calldata.saturating_mul(U256::from(ASSUMED_SIMPLE_TX_SIZE));
769 if l2_gas_price > U256::ZERO {
770 wei_per_l2_tx / l2_gas_price
771 } else {
772 U256::ZERO
773 }
774 } else {
775 U256::from(ASSUMED_SIMPLE_TX_SIZE)
776 };
777
778 let mut out = Vec::with_capacity(96);
779 out.extend_from_slice(&gas_per_l2_tx.to_be_bytes::<32>());
780 out.extend_from_slice(&gas_for_l1_calldata.to_be_bytes::<32>());
781 out.extend_from_slice(&U256::from(STORAGE_WRITE_COST).to_be_bytes::<32>());
782
783 let _ = data_len;
785 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
786 crate::charge_computation(gas_used, ctx, 3 * COPY_GAS);
787 Ok(PrecompileOutput::new(
788 (*gas_used).min(gas_limit),
789 out.into(),
790 ))
791}
792
793const RESOURCE_KIND_SINGLE_DIM: u64 = 6;
798
799fn handle_gas_pricing_constraints(
801 input: &mut PrecompileInput<'_>,
802 gas_used: &mut u64,
803 ctx: &ArbPrecompileCtx,
804) -> PrecompileResult {
805 let gas_limit = input.gas;
806 load_arbos(input)?;
807
808 let internals = input.internals_mut();
809 let arb_state = ctx
810 .block
811 .arbos_state(internals)
812 .map_err(ArbPrecompileError::fatal)?;
813
814 let count = arb_state
815 .l2_pricing_state
816 .gas_constraints_length(internals)
817 .map_err(ArbPrecompileError::fatal)?;
818 let mut sloads: u64 = 2; let mut out = Vec::with_capacity(64 + count as usize * 96);
822 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
823 out.extend_from_slice(&U256::from(count).to_be_bytes::<32>());
824
825 for i in 0..count {
826 let constraint = arb_state.l2_pricing_state.open_gas_constraint_at(i);
827 let target = constraint
828 .target(internals)
829 .map_err(ArbPrecompileError::fatal)?;
830 let window = constraint
831 .adjustment_window(internals)
832 .map_err(ArbPrecompileError::fatal)?;
833 let backlog = constraint
834 .backlog(internals)
835 .map_err(ArbPrecompileError::fatal)?;
836
837 out.extend_from_slice(&U256::from(target).to_be_bytes::<32>());
838 out.extend_from_slice(&U256::from(window).to_be_bytes::<32>());
839 out.extend_from_slice(&U256::from(backlog).to_be_bytes::<32>());
840 sloads += 3;
841 }
842
843 let result_words = (out.len() as u64).div_ceil(32);
844 let body_sloads = sloads.saturating_sub(1);
846 crate::charge_storage_read(gas_used, ctx, body_sloads * SLOAD_GAS);
847 crate::charge_computation(gas_used, ctx, result_words * COPY_GAS);
848 Ok(PrecompileOutput::new(
849 (*gas_used).min(gas_limit),
850 out.into(),
851 ))
852}
853
854fn handle_multi_gas_pricing_constraints(
860 input: &mut PrecompileInput<'_>,
861 gas_used: &mut u64,
862 ctx: &ArbPrecompileCtx,
863) -> PrecompileResult {
864 use arb_primitives::multigas::ResourceKind;
865 let gas_limit = input.gas;
866 load_arbos(input)?;
867
868 let internals = input.internals_mut();
869 let arb_state = ctx
870 .block
871 .arbos_state(internals)
872 .map_err(ArbPrecompileError::fatal)?;
873
874 let count = arb_state
875 .l2_pricing_state
876 .multi_gas_constraints_length(internals)
877 .map_err(ArbPrecompileError::fatal)?;
878 let mut sloads: u64 = 2; struct ConstraintData {
881 target: u64,
882 window: u32,
883 backlog: u64,
884 resources: Vec<(u8, u64)>,
885 }
886 let mut constraints = Vec::with_capacity(count as usize);
887
888 for i in 0..count {
889 let constraint = arb_state.l2_pricing_state.open_multi_gas_constraint_at(i);
890 let target = constraint
891 .target(internals)
892 .map_err(ArbPrecompileError::fatal)?;
893 let window = constraint
894 .adjustment_window(internals)
895 .map_err(ArbPrecompileError::fatal)?;
896 let backlog = constraint
897 .backlog(internals)
898 .map_err(ArbPrecompileError::fatal)?;
899 sloads += 3;
900
901 let mut resources = Vec::new();
902 for kind in ResourceKind::ALL {
903 let weight = constraint
904 .resource_weight(internals, kind)
905 .map_err(ArbPrecompileError::fatal)?;
906 sloads += 1;
907 if weight > 0 {
908 resources.push((kind as u8, weight));
909 }
910 }
911 constraints.push(ConstraintData {
912 target,
913 window,
914 backlog,
915 resources,
916 });
917 }
918
919 let n = constraints.len();
920 let mut out = Vec::new();
921 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
922 out.extend_from_slice(&U256::from(n).to_be_bytes::<32>());
923
924 let elem_sizes: Vec<usize> = constraints
925 .iter()
926 .map(|c| 4 * 32 + 32 + c.resources.len() * 64)
927 .collect();
928
929 let mut running_offset = n * 32;
930 for size in &elem_sizes {
931 out.extend_from_slice(&U256::from(running_offset).to_be_bytes::<32>());
932 running_offset += size;
933 }
934
935 for c in &constraints {
936 let m = c.resources.len();
937 out.extend_from_slice(&U256::from(4u64 * 32).to_be_bytes::<32>());
938 out.extend_from_slice(&U256::from(c.window).to_be_bytes::<32>());
939 out.extend_from_slice(&U256::from(c.target).to_be_bytes::<32>());
940 out.extend_from_slice(&U256::from(c.backlog).to_be_bytes::<32>());
941 out.extend_from_slice(&U256::from(m).to_be_bytes::<32>());
942 for &(kind, weight) in &c.resources {
943 out.extend_from_slice(&U256::from(kind).to_be_bytes::<32>());
944 out.extend_from_slice(&U256::from(weight).to_be_bytes::<32>());
945 }
946 }
947
948 let result_words = (out.len() as u64).div_ceil(32);
949 let body_sloads = sloads.saturating_sub(1);
950 crate::charge_storage_read(gas_used, ctx, body_sloads * SLOAD_GAS);
951 crate::charge_computation(gas_used, ctx, result_words * COPY_GAS);
952 Ok(PrecompileOutput::new(
953 (*gas_used).min(gas_limit),
954 out.into(),
955 ))
956}
957
958fn handle_multi_gas_base_fee(
962 input: &mut PrecompileInput<'_>,
963 gas_used: &mut u64,
964 ctx: &ArbPrecompileCtx,
965) -> PrecompileResult {
966 use arb_primitives::multigas::{NUM_RESOURCE_KIND, ResourceKind};
967 let gas_limit = input.gas;
968 load_arbos(input)?;
969
970 let internals = input.internals_mut();
971 let arb_state = ctx
972 .block
973 .arbos_state(internals)
974 .map_err(ArbPrecompileError::fatal)?;
975
976 let base_fee_wei = arb_state
977 .l2_pricing_state
978 .base_fee_wei(internals)
979 .map_err(ArbPrecompileError::fatal)?;
980 let multi_gas_fees = arb_state.l2_pricing_state.multi_gas_fees();
981
982 let mut out = Vec::with_capacity(64 + NUM_RESOURCE_KIND * 32);
983 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
984 out.extend_from_slice(&U256::from(NUM_RESOURCE_KIND).to_be_bytes::<32>());
985
986 for kind in ResourceKind::ALL {
987 let raw = multi_gas_fees
988 .get_current_block_fee(internals, kind)
989 .map_err(ArbPrecompileError::fatal)?;
990 let fee = if kind as u64 == RESOURCE_KIND_SINGLE_DIM || raw == U256::ZERO {
991 base_fee_wei
992 } else {
993 raw
994 };
995 out.extend_from_slice(&fee.to_be_bytes::<32>());
996 }
997
998 let result_words = (out.len() as u64).div_ceil(32);
999 let body_sloads = 1 + NUM_RESOURCE_KIND as u64;
1001 crate::charge_storage_read(gas_used, ctx, body_sloads * SLOAD_GAS);
1002 crate::charge_computation(gas_used, ctx, result_words * COPY_GAS);
1003 Ok(PrecompileOutput::new(
1004 (*gas_used).min(gas_limit),
1005 out.into(),
1006 ))
1007}