1use std::sync::Arc;
2
3use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
4use alloy_primitives::{Address, B256, U256};
5use alloy_sol_types::{SolEvent, SolInterface};
6use arb_context::ArbPrecompileCtx;
7use arb_primitives::multigas::NUM_RESOURCE_KIND;
8use arb_storage::{
9 ARBOS_STATE_ADDRESS, STORAGE_READ_GAS, STORAGE_WRITE_GAS, STORAGE_WRITE_ZERO_GAS, write_cost,
10};
11use arbos::{
12 address_set::AddressSet,
13 programs::params::{
14 COST_SCALAR_PERCENT, MIN_CACHED_GAS_UNITS, MIN_INIT_GAS_UNITS, StylusParams,
15 },
16};
17use revm::{
18 precompile::{PrecompileId, PrecompileOutput, PrecompileResult},
19 primitives::Log,
20};
21
22use crate::{ArbPrecompileError, interfaces::IArbOwner};
23
24pub const ARBOWNER_ADDRESS: Address = Address::new([
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27 0x00, 0x00, 0x00, 0x70,
28]);
29
30const ARBOS_VERSION_60: u64 = 60;
31
32use arbos::l1_pricing::L1_PRICER_FUNDS_POOL_ADDRESS;
33
34const SLOAD_GAS: u64 = STORAGE_READ_GAS;
35const SSTORE_GAS: u64 = STORAGE_WRITE_GAS;
36const SSTORE_ZERO_GAS: u64 = STORAGE_WRITE_ZERO_GAS;
37const COPY_GAS: u64 = 3;
38const WARM_SLOAD_GAS: u64 = 100;
40
41pub fn create_arbowner_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
42 DynPrecompile::new_stateful(PrecompileId::custom("arbowner"), move |input| {
43 handler(input, &ctx)
44 })
45}
46
47fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
48 let mg_snapshot = ctx.snapshot_precompile_multi_gas();
52 let mut gas_used = 0u64;
53 let gas_limit = input.gas;
54 let data = input.data;
55 if data.len() < 4 {
56 ctx.restore_precompile_multi_gas(mg_snapshot);
57 return crate::burn_all_revert(gas_limit);
58 }
59 let selector: [u8; 4] = [data[0], data[1], data[2], data[3]];
60
61 if let Some(r) = crate::reject_nonpayable_value(input.value, data, gas_limit, &[]) {
62 ctx.restore_precompile_multi_gas(mg_snapshot);
63 return r;
64 }
65
66 if let Some(r) = crate::reject_static_unless_read(
69 input.is_static,
70 data,
71 gas_limit,
72 &[
73 [0x26, 0xef, 0x7f, 0x68], [0x51, 0x6b, 0x4e, 0x0f], [0xc6, 0x86, 0xf4, 0xdb], [0x3f, 0x86, 0x01, 0xe4], [0xb3, 0x23, 0x52, 0xc3], [0x59, 0x5f, 0xbb, 0x5a], [0x3c, 0xaa, 0x5f, 0x12], [0x2d, 0x91, 0x25, 0xe9], [0xee, 0x95, 0xa8, 0x24], ],
83 ) {
84 ctx.restore_precompile_multi_gas(mg_snapshot);
85 return r;
86 }
87
88 if let Some(r) = crate::reject_delegate_nonpure(
90 input.target_address != input.bytecode_address,
91 data,
92 gas_limit,
93 &[],
94 ) {
95 ctx.restore_precompile_multi_gas(mg_snapshot);
96 return r;
97 }
98
99 if let Err(e) = verify_owner(&mut input, &mut gas_used, ctx) {
100 ctx.restore_precompile_multi_gas(mg_snapshot);
101 return Err(e.into());
102 }
103
104 let call = match IArbOwner::ArbOwnerCalls::abi_decode(data) {
105 Ok(c) => c,
106 Err(_) => {
107 ctx.restore_precompile_multi_gas(mg_snapshot);
108 return crate::burn_all_revert(gas_limit);
109 }
110 };
111
112 gas_used = 0;
113 crate::init_precompile_gas(&mut gas_used, ctx, data.len());
114
115 use IArbOwner::ArbOwnerCalls as Calls;
116 let is_read_only = matches!(
117 call,
118 Calls::getNetworkFeeAccount(_)
119 | Calls::getInfraFeeAccount(_)
120 | Calls::isChainOwner(_)
121 | Calls::getAllChainOwners(_)
122 | Calls::isTransactionFilterer(_)
123 | Calls::getAllTransactionFilterers(_)
124 | Calls::isNativeTokenOwner(_)
125 | Calls::getAllNativeTokenOwners(_)
126 | Calls::getFilteredFundsRecipient(_)
127 );
128
129 let result = match call {
130 Calls::getNetworkFeeAccount(_) => {
132 handle_get_network_fee_account(&mut input, &mut gas_used, ctx)
133 }
134 Calls::getInfraFeeAccount(_) => {
135 if let Some(r) = crate::check_method_version(ctx, gas_limit, 5, 0) {
136 return r;
137 }
138 handle_get_infra_fee_account(&mut input, &mut gas_used, ctx)
139 }
140 Calls::getFilteredFundsRecipient(_) => {
141 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
142 return r;
143 }
144 handle_get_filtered_funds_recipient(&mut input, &mut gas_used, ctx)
145 }
146 Calls::isChainOwner(_) => {
147 handle_is_member(&mut input, &mut gas_used, AddressSetKind::ChainOwners, ctx)
148 }
149 Calls::getAllChainOwners(_) => {
150 handle_get_all_members(&mut input, &mut gas_used, AddressSetKind::ChainOwners, ctx)
151 }
152 Calls::getAllTransactionFilterers(_) => {
153 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
154 return r;
155 }
156 handle_get_all_members(
157 &mut input,
158 &mut gas_used,
159 AddressSetKind::TransactionFilterers,
160 ctx,
161 )
162 }
163 Calls::getAllNativeTokenOwners(_) => {
164 if let Some(r) = crate::check_method_version(ctx, gas_limit, 41, 0) {
165 return r;
166 }
167 handle_get_all_members(
168 &mut input,
169 &mut gas_used,
170 AddressSetKind::NativeTokenOwners,
171 ctx,
172 )
173 }
174 Calls::isTransactionFilterer(_) => {
175 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
176 return r;
177 }
178 handle_is_member(
179 &mut input,
180 &mut gas_used,
181 AddressSetKind::TransactionFilterers,
182 ctx,
183 )
184 }
185 Calls::isNativeTokenOwner(_) => {
186 if let Some(r) = crate::check_method_version(ctx, gas_limit, 41, 0) {
187 return r;
188 }
189 handle_is_member(
190 &mut input,
191 &mut gas_used,
192 AddressSetKind::NativeTokenOwners,
193 ctx,
194 )
195 }
196
197 Calls::addChainOwner(_) => handle_add_chain_owner(&mut input, &mut gas_used, ctx),
199 Calls::removeChainOwner(_) => handle_remove_chain_owner(&mut input, &mut gas_used, ctx),
200
201 Calls::setNetworkFeeAccount(_) => {
203 handle_set_network_fee_account(&mut input, &mut gas_used, ctx)
204 }
205 Calls::setInfraFeeAccount(_) => {
206 if let Some(r) = crate::check_method_version(ctx, gas_limit, 5, 0) {
207 return r;
208 }
209 handle_set_infra_fee_account(&mut input, &mut gas_used, ctx)
210 }
211 Calls::setBrotliCompressionLevel(_) => {
212 if let Some(r) = crate::check_method_version(ctx, gas_limit, 20, 0) {
213 return r;
214 }
215 handle_set_brotli_compression_level(&mut input, &mut gas_used, ctx)
216 }
217 Calls::scheduleArbOSUpgrade(_) => handle_schedule_upgrade(&mut input, &mut gas_used, ctx),
218
219 Calls::setSpeedLimit(_) => match data.get(4..36) {
221 None => Err(ArbPrecompileError::empty_revert(gas_used).into()),
222 Some(bytes) if U256::from_be_slice(bytes).is_zero() => {
223 Err(ArbPrecompileError::empty_revert(gas_used).into())
224 }
225 Some(_) => handle_set_speed_limit(&mut input, &mut gas_used, ctx),
226 },
227 Calls::setL2BaseFee(_) => handle_set_l2_base_fee(&mut input, &mut gas_used, ctx),
228 Calls::setMinimumL2BaseFee(_) => handle_set_min_l2_base_fee(&mut input, &mut gas_used, ctx),
229 Calls::setMaxBlockGasLimit(_) => {
230 if let Some(r) = crate::check_method_version(ctx, gas_limit, 50, 0) {
231 return r;
232 }
233 handle_set_max_block_gas_limit(&mut input, &mut gas_used, ctx)
234 }
235 Calls::setMaxTxGasLimit(_) => handle_set_max_tx_gas_limit(&mut input, &mut gas_used, ctx),
236 Calls::setL2GasPricingInertia(_) => match data.get(4..36) {
237 None => Err(ArbPrecompileError::empty_revert(gas_used).into()),
238 Some(bytes) if U256::from_be_slice(bytes).is_zero() => {
239 Err(ArbPrecompileError::empty_revert(gas_used).into())
240 }
241 Some(_) => handle_set_l2_pricing_inertia(&mut input, &mut gas_used, ctx),
242 },
243 Calls::setL2GasBacklogTolerance(_) => {
244 handle_set_l2_backlog_tolerance(&mut input, &mut gas_used, ctx)
245 }
246 Calls::setGasBacklog(_) => {
247 if let Some(r) = crate::check_method_version(ctx, gas_limit, 50, 0) {
248 return r;
249 }
250 handle_set_gas_backlog(&mut input, &mut gas_used, ctx)
251 }
252
253 Calls::setL1PricingEquilibrationUnits(_) => {
255 handle_set_l1_equilibration_units(&mut input, &mut gas_used, ctx)
256 }
257 Calls::setL1PricingInertia(_) | Calls::setL1BaseFeeEstimateInertia(_) => {
258 handle_set_l1_inertia(&mut input, &mut gas_used, ctx)
259 }
260 Calls::setL1PricingRewardRecipient(_) => {
261 handle_set_l1_pay_rewards_to(&mut input, &mut gas_used, ctx)
262 }
263 Calls::setL1PricingRewardRate(_) => {
264 handle_set_l1_per_unit_reward(&mut input, &mut gas_used, ctx)
265 }
266 Calls::setL1PricePerUnit(_) => handle_set_l1_price_per_unit(&mut input, &mut gas_used, ctx),
267 Calls::setParentGasFloorPerToken(_) => {
268 if let Some(r) = crate::check_method_version(ctx, gas_limit, 50, 0) {
269 return r;
270 }
271 handle_set_parent_gas_floor_per_token(&mut input, &mut gas_used, ctx)
272 }
273 Calls::setPerBatchGasCharge(_) => {
274 handle_set_per_batch_gas_cost(&mut input, &mut gas_used, ctx)
275 }
276 Calls::setAmortizedCostCapBips(_) => {
277 handle_set_amortized_cost_cap_bips(&mut input, &mut gas_used, ctx)
278 }
279 Calls::releaseL1PricerSurplusFunds(_) => {
280 if let Some(r) = crate::check_method_version(ctx, gas_limit, 10, 0) {
281 return r;
282 }
283 handle_release_l1_pricer_surplus_funds(&mut input, &mut gas_used, ctx)
284 }
285
286 Calls::setInkPrice(_) => {
288 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
289 return r;
290 }
291 match read_u32_param(gas_used, data) {
292 Err(e) => Err(e.into()),
293 Ok(val) if val == 0 || val > 0xFF_FFFF => {
294 Err(ArbPrecompileError::empty_revert(gas_used).into())
295 }
296 Ok(val) => {
297 write_stylus_param(&mut input, &mut gas_used, |p| p.ink_price = val, ctx)
298 }
299 }
300 }
301 Calls::setWasmMaxStackDepth(_) => {
302 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
303 return r;
304 }
305 let val = read_u32_param(gas_used, data)?;
306 write_stylus_param(&mut input, &mut gas_used, |p| p.max_stack_depth = val, ctx)
307 }
308 Calls::setWasmFreePages(_) => {
309 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
310 return r;
311 }
312 let val = read_u16_param(gas_used, data, 0)?;
313 write_stylus_param(&mut input, &mut gas_used, |p| p.free_pages = val, ctx)
314 }
315 Calls::setWasmPageGas(_) => {
316 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
317 return r;
318 }
319 let val = read_u16_param(gas_used, data, 0)?;
320 write_stylus_param(&mut input, &mut gas_used, |p| p.page_gas = val, ctx)
321 }
322 Calls::setWasmPageLimit(_) => {
323 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
324 return r;
325 }
326 let val = read_u16_param(gas_used, data, 0)?;
327 write_stylus_param(&mut input, &mut gas_used, |p| p.page_limit = val, ctx)
328 }
329 Calls::setWasmMinInitGas(_) => {
330 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
331 return r;
332 }
333 let gas = read_u8_param(gas_used, data, 0)?;
334 let cached = read_u16_param(gas_used, data, 1)?;
335 let min_init_gas = saturating_u8(div_ceil(gas as u64, MIN_INIT_GAS_UNITS));
336 let min_cached_init_gas = saturating_u8(div_ceil(cached as u64, MIN_CACHED_GAS_UNITS));
337 write_stylus_param(
338 &mut input,
339 &mut gas_used,
340 |p| {
341 p.min_init_gas = min_init_gas;
342 p.min_cached_init_gas = min_cached_init_gas;
343 },
344 ctx,
345 )
346 }
347 Calls::setWasmInitCostScalar(_) => {
348 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
349 return r;
350 }
351 let percent = read_u64_param(gas_used, data)?;
352 let stored = saturating_u8(div_ceil(percent, COST_SCALAR_PERCENT));
353 write_stylus_param(
354 &mut input,
355 &mut gas_used,
356 |p| p.init_cost_scalar = stored,
357 ctx,
358 )
359 }
360 Calls::setWasmExpiryDays(_) => {
361 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
362 return r;
363 }
364 let val = read_u16_param(gas_used, data, 0)?;
365 write_stylus_param(&mut input, &mut gas_used, |p| p.expiry_days = val, ctx)
366 }
367 Calls::setWasmKeepaliveDays(_) => {
368 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
369 return r;
370 }
371 let val = read_u16_param(gas_used, data, 0)?;
372 write_stylus_param(&mut input, &mut gas_used, |p| p.keepalive_days = val, ctx)
373 }
374 Calls::setWasmBlockCacheSize(_) => {
375 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
376 return r;
377 }
378 let val = read_u16_param(gas_used, data, 0)?;
379 write_stylus_param(&mut input, &mut gas_used, |p| p.block_cache_size = val, ctx)
380 }
381 Calls::setWasmMaxSize(_) => {
382 if let Some(r) = crate::check_method_version(ctx, gas_limit, 40, 0) {
383 return r;
384 }
385 let val = read_u32_param(gas_used, data)?;
386 write_stylus_param(&mut input, &mut gas_used, |p| p.max_wasm_size = val, ctx)
387 }
388 Calls::setWasmActivationGas(_) => {
389 if let Some(r) = crate::check_method_version(
390 ctx,
391 gas_limit,
392 arb_chainspec::arbos_version::ARBOS_VERSION_59,
393 0,
394 ) {
395 return r;
396 }
397 if data.len() < 36 {
398 return crate::burn_all_revert(gas_limit);
399 }
400 let val = U256::from_be_slice(&data[4..36]);
401 handle_set_activation_gas(&mut input, &mut gas_used, val, ctx)
402 }
403 Calls::setMaxStylusContractFragments(_) => {
404 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
405 return r;
406 }
407 let val = read_u8_param(gas_used, data, 0)?;
408 write_stylus_param(
409 &mut input,
410 &mut gas_used,
411 |p| p.max_fragment_count = val,
412 ctx,
413 )
414 }
415 Calls::addWasmCacheManager(_) => {
416 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
417 return r;
418 }
419 handle_add_cache_manager(&mut input, &mut gas_used, ctx)
420 }
421 Calls::removeWasmCacheManager(_) => {
422 if let Some(r) = crate::check_method_version(ctx, gas_limit, 30, 0) {
423 return r;
424 }
425 handle_remove_cache_manager(&mut input, &mut gas_used, ctx)
426 }
427 Calls::setCalldataPriceIncrease(_) => {
428 if let Some(r) = crate::check_method_version(ctx, gas_limit, 40, 0) {
429 return r;
430 }
431 handle_set_calldata_price_increase(&mut input, &mut gas_used, ctx)
432 }
433 Calls::setCollectTips(_) => {
434 if let Some(r) = crate::check_method_version(ctx, gas_limit, ARBOS_VERSION_60, 0) {
435 return r;
436 }
437 handle_set_collect_tips(&mut input, &mut gas_used, ctx)
438 }
439
440 Calls::addTransactionFilterer(_) => {
442 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
443 return r;
444 }
445 handle_add_to_set_with_feature_check(
446 &mut input,
447 &mut gas_used,
448 AddressSetKind::TransactionFilterers,
449 FeatureTimeKind::TransactionFiltering,
450 Some(IArbOwner::TransactionFiltererAdded::SIGNATURE_HASH),
451 ctx,
452 )
453 }
454 Calls::removeTransactionFilterer(_) => {
455 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
456 return r;
457 }
458 handle_remove_from_set(
459 &mut input,
460 &mut gas_used,
461 AddressSetKind::TransactionFilterers,
462 Some(IArbOwner::TransactionFiltererRemoved::SIGNATURE_HASH),
463 ctx,
464 )
465 }
466 Calls::setTransactionFilteringFrom(_) => {
467 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
468 return r;
469 }
470 handle_set_feature_time(
471 &mut input,
472 &mut gas_used,
473 FeatureTimeKind::TransactionFiltering,
474 ctx,
475 )
476 }
477 Calls::setFilteredFundsRecipient(_) => {
478 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
479 return r;
480 }
481 handle_set_filtered_funds_recipient(&mut input, &mut gas_used, ctx)
482 }
483
484 Calls::setNativeTokenManagementFrom(_) => {
486 if let Some(r) = crate::check_method_version(ctx, gas_limit, 41, 0) {
487 return r;
488 }
489 handle_set_feature_time(&mut input, &mut gas_used, FeatureTimeKind::NativeToken, ctx)
490 }
491 Calls::addNativeTokenOwner(_) => {
492 if let Some(r) = crate::check_method_version(ctx, gas_limit, 41, 0) {
493 return r;
494 }
495 handle_add_to_set_with_feature_check(
496 &mut input,
497 &mut gas_used,
498 AddressSetKind::NativeTokenOwners,
499 FeatureTimeKind::NativeToken,
500 Some(IArbOwner::NativeTokenOwnerAdded::SIGNATURE_HASH),
501 ctx,
502 )
503 }
504 Calls::removeNativeTokenOwner(_) => {
505 if let Some(r) = crate::check_method_version(ctx, gas_limit, 41, 0) {
506 return r;
507 }
508 handle_remove_from_set(
509 &mut input,
510 &mut gas_used,
511 AddressSetKind::NativeTokenOwners,
512 Some(IArbOwner::NativeTokenOwnerRemoved::SIGNATURE_HASH),
513 ctx,
514 )
515 }
516
517 Calls::setGasPricingConstraints(_) => {
519 if let Some(r) = crate::check_method_version(ctx, gas_limit, 50, 0) {
520 return r;
521 }
522 handle_set_gas_pricing_constraints(&mut input, &mut gas_used, ctx)
523 }
524 Calls::setMultiGasPricingConstraints(_) => {
525 if let Some(r) = crate::check_method_version(ctx, gas_limit, 60, 0) {
526 return r;
527 }
528 handle_set_multi_gas_pricing_constraints(&mut input, &mut gas_used, ctx)
529 }
530
531 Calls::setChainConfig(_) => {
533 if let Some(r) = crate::check_method_version(ctx, gas_limit, 11, 0) {
534 return r;
535 }
536 handle_set_chain_config(&mut input, &mut gas_used, ctx)
537 }
538 };
539
540 let result = match result {
541 Ok(output) => {
542 if output.reverted {
543 Ok(PrecompileOutput::new_reverted(0, output.bytes))
544 } else {
545 let arbos_version = ctx.block.arbos_version;
546 if !is_read_only || arbos_version < 11 {
547 emit_owner_acts(&mut input, &selector, data);
548 }
549 Ok(PrecompileOutput::new(0, output.bytes))
550 }
551 }
552 Err(_) => Ok(PrecompileOutput::new_reverted(0, Default::default())),
553 };
554 ctx.restore_precompile_multi_gas(mg_snapshot);
555 if gas_used > gas_limit {
557 return Ok(PrecompileOutput::new_reverted(0, Default::default()));
558 }
559 result
560}
561
562fn verify_owner(
565 input: &mut PrecompileInput<'_>,
566 gas_used: &mut u64,
567 ctx: &ArbPrecompileCtx,
568) -> Result<(), ArbPrecompileError> {
569 let caller = input.caller;
570 load_arbos(input)?;
571
572 let internals = input.internals_mut();
573 let arb_state = ctx
574 .block
575 .arbos_state(internals)
576 .map_err(ArbPrecompileError::fatal)?;
577 let is_owner = arb_state
578 .chain_owners
579 .is_member(internals, caller)
580 .map_err(ArbPrecompileError::fatal)?;
581 crate::charge_precompile_gas(gas_used, 2 * SLOAD_GAS);
582 if !is_owner {
583 return Err(ArbPrecompileError::empty_revert(*gas_used));
584 }
585 Ok(())
586}
587
588fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), ArbPrecompileError> {
591 input
592 .internals_mut()
593 .load_account(ARBOS_STATE_ADDRESS)
594 .map_err(ArbPrecompileError::fatal)?;
595 Ok(())
596}
597
598#[derive(Clone, Copy)]
599enum AddressSetKind {
600 ChainOwners,
601 NativeTokenOwners,
602 TransactionFilterers,
603}
604
605#[derive(Clone, Copy)]
606enum FeatureTimeKind {
607 NativeToken,
608 TransactionFiltering,
609}
610
611fn address_set<'a, D, B>(
612 state: &'a arbos::arbos_state::ArbosState<'_, D, B>,
613 kind: AddressSetKind,
614) -> &'a AddressSet<'a, D>
615where
616 B: arbos::burn::Burner,
617{
618 match kind {
619 AddressSetKind::ChainOwners => &state.chain_owners,
620 AddressSetKind::NativeTokenOwners => &state.native_token_owners,
621 AddressSetKind::TransactionFilterers => &state.transaction_filterers,
622 }
623}
624
625fn read_feature_time<D, B, C>(
626 state: &arbos::arbos_state::ArbosState<'_, D, B>,
627 backend: &mut C,
628 kind: FeatureTimeKind,
629) -> Result<u64, ArbPrecompileError>
630where
631 B: arbos::burn::Burner,
632 C: arb_storage::StorageBackend,
633{
634 match kind {
635 FeatureTimeKind::NativeToken => state.native_token_management_from_time(backend),
636 FeatureTimeKind::TransactionFiltering => state.transaction_filtering_from_time(backend),
637 }
638 .map_err(ArbPrecompileError::fatal)
639}
640
641fn write_feature_time<D, B, C>(
642 state: &arbos::arbos_state::ArbosState<'_, D, B>,
643 backend: &mut C,
644 kind: FeatureTimeKind,
645 value: u64,
646) -> Result<(), ArbPrecompileError>
647where
648 B: arbos::burn::Burner,
649 C: arb_storage::StorageBackend,
650{
651 match kind {
652 FeatureTimeKind::NativeToken => state.set_native_token_management_from_time(backend, value),
653 FeatureTimeKind::TransactionFiltering => {
654 state.set_transaction_filtering_from_time(backend, value)
655 }
656 }
657 .map_err(ArbPrecompileError::fatal)
658}
659
660fn field_read_output(gas_limit: u64, gas_used: u64, value: U256) -> PrecompileResult {
661 Ok(PrecompileOutput::new(
662 gas_used.min(gas_limit),
663 value.to_be_bytes::<32>().to_vec().into(),
664 ))
665}
666
667fn handle_get_network_fee_account(
670 input: &mut PrecompileInput<'_>,
671 gas_used: &mut u64,
672 ctx: &ArbPrecompileCtx,
673) -> PrecompileResult {
674 let gas_limit = input.gas;
675 load_arbos(input)?;
676 let internals = input.internals_mut();
677 let arb_state = ctx
678 .block
679 .arbos_state(internals)
680 .map_err(ArbPrecompileError::fatal)?;
681 let addr = arb_state
682 .network_fee_account(internals)
683 .map_err(ArbPrecompileError::fatal)?;
684 crate::charge_precompile_gas(gas_used, SLOAD_GAS + COPY_GAS);
685 field_read_output(gas_limit, *gas_used, U256::from_be_slice(addr.as_slice()))
686}
687
688fn handle_get_infra_fee_account(
689 input: &mut PrecompileInput<'_>,
690 gas_used: &mut u64,
691 ctx: &ArbPrecompileCtx,
692) -> PrecompileResult {
693 let gas_limit = input.gas;
694 load_arbos(input)?;
695 let internals = input.internals_mut();
696 let arb_state = ctx
697 .block
698 .arbos_state(internals)
699 .map_err(ArbPrecompileError::fatal)?;
700 let addr = arb_state
701 .infra_fee_account(internals)
702 .map_err(ArbPrecompileError::fatal)?;
703 crate::charge_precompile_gas(gas_used, SLOAD_GAS + COPY_GAS);
704 field_read_output(gas_limit, *gas_used, U256::from_be_slice(addr.as_slice()))
705}
706
707fn handle_get_filtered_funds_recipient(
708 input: &mut PrecompileInput<'_>,
709 gas_used: &mut u64,
710 ctx: &ArbPrecompileCtx,
711) -> PrecompileResult {
712 let gas_limit = input.gas;
713 load_arbos(input)?;
714 let internals = input.internals_mut();
715 let arb_state = ctx
716 .block
717 .arbos_state(internals)
718 .map_err(ArbPrecompileError::fatal)?;
719 let addr = arb_state
720 .filtered_funds_recipient(internals)
721 .map_err(ArbPrecompileError::fatal)?;
722 crate::charge_precompile_gas(gas_used, SLOAD_GAS + COPY_GAS);
723 field_read_output(gas_limit, *gas_used, U256::from_be_slice(addr.as_slice()))
724}
725
726fn handle_set_network_fee_account(
729 input: &mut PrecompileInput<'_>,
730 gas_used: &mut u64,
731 ctx: &ArbPrecompileCtx,
732) -> PrecompileResult {
733 let data = input.data;
734 if data.len() < 36 {
735 return crate::burn_all_revert(input.gas);
736 }
737 let gas_limit = input.gas;
738 let addr = Address::from_slice(&data[16..36]);
739 load_arbos(input)?;
740 let internals = input.internals_mut();
741 let arb_state = ctx
742 .block
743 .arbos_state(internals)
744 .map_err(ArbPrecompileError::fatal)?;
745 arb_state
746 .set_network_fee_account(internals, addr)
747 .map_err(ArbPrecompileError::fatal)?;
748 ctx.block
749 .state_params_dirty
750 .store(true, std::sync::atomic::Ordering::Relaxed);
751 crate::charge_precompile_gas(gas_used, write_cost(addr.is_zero()));
752 Ok(PrecompileOutput::new(
753 (*gas_used).min(gas_limit),
754 Vec::new().into(),
755 ))
756}
757
758fn handle_set_infra_fee_account(
759 input: &mut PrecompileInput<'_>,
760 gas_used: &mut u64,
761 ctx: &ArbPrecompileCtx,
762) -> PrecompileResult {
763 let data = input.data;
764 if data.len() < 36 {
765 return crate::burn_all_revert(input.gas);
766 }
767 let gas_limit = input.gas;
768 let addr = Address::from_slice(&data[16..36]);
769 load_arbos(input)?;
770 let internals = input.internals_mut();
771 let arb_state = ctx
772 .block
773 .arbos_state(internals)
774 .map_err(ArbPrecompileError::fatal)?;
775 arb_state
776 .set_infra_fee_account(internals, addr)
777 .map_err(ArbPrecompileError::fatal)?;
778 ctx.block
779 .state_params_dirty
780 .store(true, std::sync::atomic::Ordering::Relaxed);
781 crate::charge_precompile_gas(gas_used, write_cost(addr.is_zero()));
782 Ok(PrecompileOutput::new(
783 (*gas_used).min(gas_limit),
784 Vec::new().into(),
785 ))
786}
787
788fn handle_set_brotli_compression_level(
789 input: &mut PrecompileInput<'_>,
790 gas_used: &mut u64,
791 ctx: &ArbPrecompileCtx,
792) -> PrecompileResult {
793 let data = input.data;
794 if data.len() < 36 {
795 return crate::burn_all_revert(input.gas);
796 }
797 let gas_limit = input.gas;
798 let level: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
799 load_arbos(input)?;
800 let internals = input.internals_mut();
801 let arb_state = ctx
802 .block
803 .arbos_state(internals)
804 .map_err(ArbPrecompileError::fatal)?;
805 match arb_state.set_brotli_compression_level(internals, level) {
806 Ok(()) => {
807 ctx.block
808 .state_params_dirty
809 .store(true, std::sync::atomic::Ordering::Relaxed);
810 }
811 Err(arbos::arbos_state::ArbosStateError::InvalidBrotliCompressionLevel) => {
812 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
813 }
814 Err(e) => return Err(ArbPrecompileError::fatal(e).into()),
815 }
816 crate::charge_precompile_gas(gas_used, write_cost(level == 0));
817 Ok(PrecompileOutput::new(
818 (*gas_used).min(gas_limit),
819 Vec::new().into(),
820 ))
821}
822
823fn handle_set_filtered_funds_recipient(
824 input: &mut PrecompileInput<'_>,
825 gas_used: &mut u64,
826 ctx: &ArbPrecompileCtx,
827) -> PrecompileResult {
828 let data = input.data;
829 if data.len() < 36 {
830 return crate::burn_all_revert(input.gas);
831 }
832 let gas_limit = input.gas;
833 let addr = Address::from_slice(&data[16..36]);
834 load_arbos(input)?;
835 let internals = input.internals_mut();
836 let arb_state = ctx
837 .block
838 .arbos_state(internals)
839 .map_err(ArbPrecompileError::fatal)?;
840 arb_state
841 .set_filtered_funds_recipient(internals, addr)
842 .map_err(ArbPrecompileError::fatal)?;
843 emit_address_event(
844 input,
845 IArbOwner::FilteredFundsRecipientSet::SIGNATURE_HASH,
846 addr,
847 );
848 crate::charge_precompile_gas(gas_used, write_cost(addr.is_zero()));
849 Ok(PrecompileOutput::new(
850 (*gas_used).min(gas_limit),
851 Vec::new().into(),
852 ))
853}
854
855fn handle_schedule_upgrade(
856 input: &mut PrecompileInput<'_>,
857 gas_used: &mut u64,
858 ctx: &ArbPrecompileCtx,
859) -> PrecompileResult {
860 let data = input.data;
861 if data.len() < 68 {
862 return crate::burn_all_revert(input.gas);
863 }
864 let gas_limit = input.gas;
865 let new_version: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
866 let timestamp: u64 = U256::from_be_slice(&data[36..68]).try_into().unwrap_or(0);
867 load_arbos(input)?;
868 let internals = input.internals_mut();
869 let arb_state = ctx
870 .block
871 .arbos_state(internals)
872 .map_err(ArbPrecompileError::fatal)?;
873 arb_state
874 .schedule_arbos_upgrade(internals, new_version, timestamp)
875 .map_err(ArbPrecompileError::fatal)?;
876 crate::charge_precompile_gas(
877 gas_used,
878 write_cost(new_version == 0) + write_cost(timestamp == 0),
879 );
880 Ok(PrecompileOutput::new(
881 (*gas_used).min(gas_limit),
882 Vec::new().into(),
883 ))
884}
885
886fn handle_set_speed_limit(
889 input: &mut PrecompileInput<'_>,
890 gas_used: &mut u64,
891 ctx: &ArbPrecompileCtx,
892) -> PrecompileResult {
893 let data = input.data;
894 if data.len() < 36 {
895 return crate::burn_all_revert(input.gas);
896 }
897 let gas_limit = input.gas;
898 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
899 load_arbos(input)?;
900 let internals = input.internals_mut();
901 let arb_state = ctx
902 .block
903 .arbos_state(internals)
904 .map_err(ArbPrecompileError::fatal)?;
905 arb_state
906 .l2_pricing_state
907 .set_speed_limit_per_second(internals, val)
908 .map_err(ArbPrecompileError::fatal)?;
909 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
910 Ok(PrecompileOutput::new(
911 (*gas_used).min(gas_limit),
912 Vec::new().into(),
913 ))
914}
915
916fn handle_set_l2_base_fee(
917 input: &mut PrecompileInput<'_>,
918 gas_used: &mut u64,
919 ctx: &ArbPrecompileCtx,
920) -> PrecompileResult {
921 let data = input.data;
922 if data.len() < 36 {
923 return crate::burn_all_revert(input.gas);
924 }
925 let gas_limit = input.gas;
926 let val = U256::from_be_slice(&data[4..36]);
927 load_arbos(input)?;
928 let internals = input.internals_mut();
929 let arb_state = ctx
930 .block
931 .arbos_state(internals)
932 .map_err(ArbPrecompileError::fatal)?;
933 arb_state
934 .l2_pricing_state
935 .set_base_fee_wei(internals, val)
936 .map_err(ArbPrecompileError::fatal)?;
937 crate::charge_precompile_gas(gas_used, write_cost(val.is_zero()));
938 Ok(PrecompileOutput::new(
939 (*gas_used).min(gas_limit),
940 Vec::new().into(),
941 ))
942}
943
944fn handle_set_min_l2_base_fee(
945 input: &mut PrecompileInput<'_>,
946 gas_used: &mut u64,
947 ctx: &ArbPrecompileCtx,
948) -> PrecompileResult {
949 let data = input.data;
950 if data.len() < 36 {
951 return crate::burn_all_revert(input.gas);
952 }
953 let gas_limit = input.gas;
954 let val = U256::from_be_slice(&data[4..36]);
955 load_arbos(input)?;
956 let internals = input.internals_mut();
957 let arb_state = ctx
958 .block
959 .arbos_state(internals)
960 .map_err(ArbPrecompileError::fatal)?;
961 arb_state
962 .l2_pricing_state
963 .set_min_base_fee_wei(internals, val)
964 .map_err(ArbPrecompileError::fatal)?;
965 ctx.block
966 .state_params_dirty
967 .store(true, std::sync::atomic::Ordering::Relaxed);
968 crate::charge_precompile_gas(gas_used, write_cost(val.is_zero()));
969 Ok(PrecompileOutput::new(
970 (*gas_used).min(gas_limit),
971 Vec::new().into(),
972 ))
973}
974
975fn handle_set_max_block_gas_limit(
976 input: &mut PrecompileInput<'_>,
977 gas_used: &mut u64,
978 ctx: &ArbPrecompileCtx,
979) -> PrecompileResult {
980 let data = input.data;
981 if data.len() < 36 {
982 return crate::burn_all_revert(input.gas);
983 }
984 let gas_limit = input.gas;
985 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
986 load_arbos(input)?;
987 let internals = input.internals_mut();
988 let arb_state = ctx
989 .block
990 .arbos_state(internals)
991 .map_err(ArbPrecompileError::fatal)?;
992 arb_state
993 .l2_pricing_state
994 .set_max_per_block_gas_limit(internals, val)
995 .map_err(ArbPrecompileError::fatal)?;
996 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
997 Ok(PrecompileOutput::new(
998 (*gas_used).min(gas_limit),
999 Vec::new().into(),
1000 ))
1001}
1002
1003fn handle_set_max_tx_gas_limit(
1004 input: &mut PrecompileInput<'_>,
1005 gas_used: &mut u64,
1006 ctx: &ArbPrecompileCtx,
1007) -> PrecompileResult {
1008 let data = input.data;
1009 if data.len() < 36 {
1010 return crate::burn_all_revert(input.gas);
1011 }
1012 let gas_limit = input.gas;
1013 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1014 load_arbos(input)?;
1015 let internals = input.internals_mut();
1016 let arb_state = ctx
1017 .block
1018 .arbos_state(internals)
1019 .map_err(ArbPrecompileError::fatal)?;
1020 if arb_state.arbos_version() < 50 {
1021 arb_state
1022 .l2_pricing_state
1023 .set_max_per_block_gas_limit(internals, val)
1024 .map_err(ArbPrecompileError::fatal)?;
1025 } else {
1026 arb_state
1027 .l2_pricing_state
1028 .set_max_per_tx_gas_limit(internals, val)
1029 .map_err(ArbPrecompileError::fatal)?;
1030 }
1031 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1032 Ok(PrecompileOutput::new(
1033 (*gas_used).min(gas_limit),
1034 Vec::new().into(),
1035 ))
1036}
1037
1038fn handle_set_l2_pricing_inertia(
1039 input: &mut PrecompileInput<'_>,
1040 gas_used: &mut u64,
1041 ctx: &ArbPrecompileCtx,
1042) -> PrecompileResult {
1043 let data = input.data;
1044 if data.len() < 36 {
1045 return crate::burn_all_revert(input.gas);
1046 }
1047 let gas_limit = input.gas;
1048 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1049 load_arbos(input)?;
1050 let internals = input.internals_mut();
1051 let arb_state = ctx
1052 .block
1053 .arbos_state(internals)
1054 .map_err(ArbPrecompileError::fatal)?;
1055 arb_state
1056 .l2_pricing_state
1057 .set_pricing_inertia(internals, val)
1058 .map_err(ArbPrecompileError::fatal)?;
1059 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1060 Ok(PrecompileOutput::new(
1061 (*gas_used).min(gas_limit),
1062 Vec::new().into(),
1063 ))
1064}
1065
1066fn handle_set_l2_backlog_tolerance(
1067 input: &mut PrecompileInput<'_>,
1068 gas_used: &mut u64,
1069 ctx: &ArbPrecompileCtx,
1070) -> PrecompileResult {
1071 let data = input.data;
1072 if data.len() < 36 {
1073 return crate::burn_all_revert(input.gas);
1074 }
1075 let gas_limit = input.gas;
1076 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1077 load_arbos(input)?;
1078 let internals = input.internals_mut();
1079 let arb_state = ctx
1080 .block
1081 .arbos_state(internals)
1082 .map_err(ArbPrecompileError::fatal)?;
1083 arb_state
1084 .l2_pricing_state
1085 .set_backlog_tolerance(internals, val)
1086 .map_err(ArbPrecompileError::fatal)?;
1087 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1088 Ok(PrecompileOutput::new(
1089 (*gas_used).min(gas_limit),
1090 Vec::new().into(),
1091 ))
1092}
1093
1094fn handle_set_gas_backlog(
1095 input: &mut PrecompileInput<'_>,
1096 gas_used: &mut u64,
1097 ctx: &ArbPrecompileCtx,
1098) -> PrecompileResult {
1099 let data = input.data;
1100 if data.len() < 36 {
1101 return crate::burn_all_revert(input.gas);
1102 }
1103 let gas_limit = input.gas;
1104 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1105 load_arbos(input)?;
1106 let internals = input.internals_mut();
1107 let arb_state = ctx
1108 .block
1109 .arbos_state(internals)
1110 .map_err(ArbPrecompileError::fatal)?;
1111 arb_state
1112 .l2_pricing_state
1113 .set_gas_backlog(internals, val)
1114 .map_err(ArbPrecompileError::fatal)?;
1115 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1116 Ok(PrecompileOutput::new(
1117 (*gas_used).min(gas_limit),
1118 Vec::new().into(),
1119 ))
1120}
1121
1122fn handle_set_l1_equilibration_units(
1125 input: &mut PrecompileInput<'_>,
1126 gas_used: &mut u64,
1127 ctx: &ArbPrecompileCtx,
1128) -> PrecompileResult {
1129 let data = input.data;
1130 if data.len() < 36 {
1131 return crate::burn_all_revert(input.gas);
1132 }
1133 let gas_limit = input.gas;
1134 let val = U256::from_be_slice(&data[4..36]);
1135 load_arbos(input)?;
1136 let internals = input.internals_mut();
1137 let arb_state = ctx
1138 .block
1139 .arbos_state(internals)
1140 .map_err(ArbPrecompileError::fatal)?;
1141 arb_state
1142 .l1_pricing_state
1143 .set_equilibration_units(internals, val)
1144 .map_err(ArbPrecompileError::fatal)?;
1145 crate::charge_precompile_gas(gas_used, write_cost(val.is_zero()));
1146 Ok(PrecompileOutput::new(
1147 (*gas_used).min(gas_limit),
1148 Vec::new().into(),
1149 ))
1150}
1151
1152fn handle_set_l1_inertia(
1153 input: &mut PrecompileInput<'_>,
1154 gas_used: &mut u64,
1155 ctx: &ArbPrecompileCtx,
1156) -> PrecompileResult {
1157 let data = input.data;
1158 if data.len() < 36 {
1159 return crate::burn_all_revert(input.gas);
1160 }
1161 let gas_limit = input.gas;
1162 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1163 load_arbos(input)?;
1164 let internals = input.internals_mut();
1165 let arb_state = ctx
1166 .block
1167 .arbos_state(internals)
1168 .map_err(ArbPrecompileError::fatal)?;
1169 arb_state
1170 .l1_pricing_state
1171 .set_inertia(internals, val)
1172 .map_err(ArbPrecompileError::fatal)?;
1173 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1174 Ok(PrecompileOutput::new(
1175 (*gas_used).min(gas_limit),
1176 Vec::new().into(),
1177 ))
1178}
1179
1180fn handle_set_l1_pay_rewards_to(
1181 input: &mut PrecompileInput<'_>,
1182 gas_used: &mut u64,
1183 ctx: &ArbPrecompileCtx,
1184) -> PrecompileResult {
1185 let data = input.data;
1186 if data.len() < 36 {
1187 return crate::burn_all_revert(input.gas);
1188 }
1189 let gas_limit = input.gas;
1190 let addr = Address::from_slice(&data[16..36]);
1191 load_arbos(input)?;
1192 let internals = input.internals_mut();
1193 let arb_state = ctx
1194 .block
1195 .arbos_state(internals)
1196 .map_err(ArbPrecompileError::fatal)?;
1197 arb_state
1198 .l1_pricing_state
1199 .set_pay_rewards_to(internals, addr)
1200 .map_err(ArbPrecompileError::fatal)?;
1201 crate::charge_precompile_gas(gas_used, write_cost(addr.is_zero()));
1202 Ok(PrecompileOutput::new(
1203 (*gas_used).min(gas_limit),
1204 Vec::new().into(),
1205 ))
1206}
1207
1208fn handle_set_l1_per_unit_reward(
1209 input: &mut PrecompileInput<'_>,
1210 gas_used: &mut u64,
1211 ctx: &ArbPrecompileCtx,
1212) -> PrecompileResult {
1213 let data = input.data;
1214 if data.len() < 36 {
1215 return crate::burn_all_revert(input.gas);
1216 }
1217 let gas_limit = input.gas;
1218 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1219 load_arbos(input)?;
1220 let internals = input.internals_mut();
1221 let arb_state = ctx
1222 .block
1223 .arbos_state(internals)
1224 .map_err(ArbPrecompileError::fatal)?;
1225 arb_state
1226 .l1_pricing_state
1227 .set_per_unit_reward(internals, val)
1228 .map_err(ArbPrecompileError::fatal)?;
1229 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1230 Ok(PrecompileOutput::new(
1231 (*gas_used).min(gas_limit),
1232 Vec::new().into(),
1233 ))
1234}
1235
1236fn handle_set_l1_price_per_unit(
1237 input: &mut PrecompileInput<'_>,
1238 gas_used: &mut u64,
1239 ctx: &ArbPrecompileCtx,
1240) -> PrecompileResult {
1241 let data = input.data;
1242 if data.len() < 36 {
1243 return crate::burn_all_revert(input.gas);
1244 }
1245 let gas_limit = input.gas;
1246 let val = U256::from_be_slice(&data[4..36]);
1247 load_arbos(input)?;
1248 let internals = input.internals_mut();
1249 let arb_state = ctx
1250 .block
1251 .arbos_state(internals)
1252 .map_err(ArbPrecompileError::fatal)?;
1253 arb_state
1254 .l1_pricing_state
1255 .set_price_per_unit(internals, val)
1256 .map_err(ArbPrecompileError::fatal)?;
1257 crate::charge_precompile_gas(gas_used, write_cost(val.is_zero()));
1258 Ok(PrecompileOutput::new(
1259 (*gas_used).min(gas_limit),
1260 Vec::new().into(),
1261 ))
1262}
1263
1264fn handle_set_parent_gas_floor_per_token(
1265 input: &mut PrecompileInput<'_>,
1266 gas_used: &mut u64,
1267 ctx: &ArbPrecompileCtx,
1268) -> PrecompileResult {
1269 let data = input.data;
1270 if data.len() < 36 {
1271 return crate::burn_all_revert(input.gas);
1272 }
1273 let gas_limit = input.gas;
1274 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1275 load_arbos(input)?;
1276 let internals = input.internals_mut();
1277 let arb_state = ctx
1278 .block
1279 .arbos_state(internals)
1280 .map_err(ArbPrecompileError::fatal)?;
1281 arb_state
1282 .l1_pricing_state
1283 .set_parent_gas_floor_per_token(internals, val)
1284 .map_err(ArbPrecompileError::fatal)?;
1285 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1286 Ok(PrecompileOutput::new(
1287 (*gas_used).min(gas_limit),
1288 Vec::new().into(),
1289 ))
1290}
1291
1292fn handle_set_per_batch_gas_cost(
1293 input: &mut PrecompileInput<'_>,
1294 gas_used: &mut u64,
1295 ctx: &ArbPrecompileCtx,
1296) -> PrecompileResult {
1297 let data = input.data;
1298 if data.len() < 36 {
1299 return crate::burn_all_revert(input.gas);
1300 }
1301 let gas_limit = input.gas;
1302 let raw = U256::from_be_slice(&data[4..36]);
1303 let val_u64: u64 = raw.try_into().unwrap_or(u64::MAX);
1305 let val_i64 = val_u64 as i64;
1306 load_arbos(input)?;
1307 let internals = input.internals_mut();
1308 let arb_state = ctx
1309 .block
1310 .arbos_state(internals)
1311 .map_err(ArbPrecompileError::fatal)?;
1312 arb_state
1313 .l1_pricing_state
1314 .set_per_batch_gas_cost(internals, val_i64)
1315 .map_err(ArbPrecompileError::fatal)?;
1316 crate::charge_precompile_gas(gas_used, write_cost(val_i64 == 0));
1317 Ok(PrecompileOutput::new(
1318 (*gas_used).min(gas_limit),
1319 Vec::new().into(),
1320 ))
1321}
1322
1323fn handle_set_amortized_cost_cap_bips(
1324 input: &mut PrecompileInput<'_>,
1325 gas_used: &mut u64,
1326 ctx: &ArbPrecompileCtx,
1327) -> PrecompileResult {
1328 let data = input.data;
1329 if data.len() < 36 {
1330 return crate::burn_all_revert(input.gas);
1331 }
1332 let gas_limit = input.gas;
1333 let val: u64 = U256::from_be_slice(&data[4..36]).try_into().unwrap_or(0);
1334 load_arbos(input)?;
1335 let internals = input.internals_mut();
1336 let arb_state = ctx
1337 .block
1338 .arbos_state(internals)
1339 .map_err(ArbPrecompileError::fatal)?;
1340 arb_state
1341 .l1_pricing_state
1342 .set_amortized_cost_cap_bips(internals, val)
1343 .map_err(ArbPrecompileError::fatal)?;
1344 crate::charge_precompile_gas(gas_used, write_cost(val == 0));
1345 Ok(PrecompileOutput::new(
1346 (*gas_used).min(gas_limit),
1347 Vec::new().into(),
1348 ))
1349}
1350
1351fn handle_is_member(
1354 input: &mut PrecompileInput<'_>,
1355 gas_used: &mut u64,
1356 kind: AddressSetKind,
1357 ctx: &ArbPrecompileCtx,
1358) -> PrecompileResult {
1359 let data = input.data;
1360 if data.len() < 36 {
1361 return crate::burn_all_revert(input.gas);
1362 }
1363 let gas_limit = input.gas;
1364 let addr = Address::from_slice(&data[16..36]);
1365 load_arbos(input)?;
1366 let internals = input.internals_mut();
1367 let arb_state = ctx
1368 .block
1369 .arbos_state(internals)
1370 .map_err(ArbPrecompileError::fatal)?;
1371 let is_member = address_set(arb_state, kind)
1372 .is_member(internals, addr)
1373 .map_err(ArbPrecompileError::fatal)?;
1374 let result = if is_member {
1375 U256::from(1u64)
1376 } else {
1377 U256::ZERO
1378 };
1379 crate::charge_precompile_gas(gas_used, SLOAD_GAS + COPY_GAS);
1380 Ok(PrecompileOutput::new(
1381 (*gas_used).min(gas_limit),
1382 result.to_be_bytes::<32>().to_vec().into(),
1383 ))
1384}
1385
1386fn handle_get_all_members(
1387 input: &mut PrecompileInput<'_>,
1388 gas_used: &mut u64,
1389 kind: AddressSetKind,
1390 ctx: &ArbPrecompileCtx,
1391) -> PrecompileResult {
1392 let gas_limit = input.gas;
1393 const MAX_MEMBERS: u64 = 65_536;
1394 load_arbos(input)?;
1395 let internals = input.internals_mut();
1396 let arb_state = ctx
1397 .block
1398 .arbos_state(internals)
1399 .map_err(ArbPrecompileError::fatal)?;
1400 let members = address_set(arb_state, kind)
1401 .all_members(internals, MAX_MEMBERS)
1402 .map_err(ArbPrecompileError::fatal)?;
1403 let count = members.len() as u64;
1404
1405 let mut out = Vec::with_capacity(64 + 32 * members.len());
1406 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
1407 out.extend_from_slice(&U256::from(count).to_be_bytes::<32>());
1408 for member in &members {
1409 let mut word = [0u8; 32];
1410 word[12..32].copy_from_slice(member.as_slice());
1411 out.extend_from_slice(&word);
1412 }
1413
1414 let extra = (1 + count) * SLOAD_GAS + COPY_GAS;
1415 crate::charge_precompile_gas(gas_used, extra);
1416 Ok(PrecompileOutput::new(
1417 (*gas_used).min(gas_limit),
1418 out.into(),
1419 ))
1420}
1421
1422fn handle_add_chain_owner(
1423 input: &mut PrecompileInput<'_>,
1424 gas_used: &mut u64,
1425 ctx: &ArbPrecompileCtx,
1426) -> PrecompileResult {
1427 let data = input.data;
1428 if data.len() < 36 {
1429 return crate::burn_all_revert(input.gas);
1430 }
1431 let gas_limit = input.gas;
1432 let addr = Address::from_slice(&data[16..36]);
1433 load_arbos(input)?;
1434
1435 let internals = input.internals_mut();
1436 let arb_state = ctx
1437 .block
1438 .arbos_state(internals)
1439 .map_err(ArbPrecompileError::fatal)?;
1440 let arbos_version = arb_state.arbos_version();
1441 arb_state
1442 .chain_owners
1443 .add(internals, addr)
1444 .map_err(ArbPrecompileError::fatal)?;
1445 if arbos_version >= 60 {
1446 emit_address_event(input, IArbOwner::ChainOwnerAdded::SIGNATURE_HASH, addr);
1447 }
1448
1449 crate::charge_precompile_gas(
1450 gas_used,
1451 3 * SLOAD_GAS + 2 * SSTORE_GAS + write_cost(addr.is_zero()),
1452 );
1453 Ok(PrecompileOutput::new(
1454 (*gas_used).min(gas_limit),
1455 Vec::new().into(),
1456 ))
1457}
1458
1459fn handle_remove_chain_owner(
1460 input: &mut PrecompileInput<'_>,
1461 gas_used: &mut u64,
1462 ctx: &ArbPrecompileCtx,
1463) -> PrecompileResult {
1464 let data = input.data;
1465 if data.len() < 36 {
1466 return crate::burn_all_revert(input.gas);
1467 }
1468 let gas_limit = input.gas;
1469 let addr = Address::from_slice(&data[16..36]);
1470 load_arbos(input)?;
1471
1472 let internals = input.internals_mut();
1473 let arb_state = ctx
1474 .block
1475 .arbos_state(internals)
1476 .map_err(ArbPrecompileError::fatal)?;
1477 let arbos_version = arb_state.arbos_version();
1478 if !arb_state
1479 .chain_owners
1480 .is_member(internals, addr)
1481 .map_err(ArbPrecompileError::fatal)?
1482 {
1483 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1484 }
1485 crate::charge_precompile_gas(gas_used, SLOAD_GAS);
1486 arb_state
1487 .chain_owners
1488 .remove(internals, addr, arbos_version, gas_used)
1489 .map_err(ArbPrecompileError::fatal)?;
1490 if arbos_version >= 60 {
1491 emit_address_event(input, IArbOwner::ChainOwnerRemoved::SIGNATURE_HASH, addr);
1492 }
1493
1494 Ok(PrecompileOutput::new(
1495 (*gas_used).min(gas_limit),
1496 Vec::new().into(),
1497 ))
1498}
1499
1500fn handle_release_l1_pricer_surplus_funds(
1503 input: &mut PrecompileInput<'_>,
1504 gas_used: &mut u64,
1505 ctx: &ArbPrecompileCtx,
1506) -> PrecompileResult {
1507 let data = input.data;
1508 if data.len() < 36 {
1509 return crate::burn_all_revert(input.gas);
1510 }
1511 let gas_limit = input.gas;
1512 let max_wei = U256::from_be_slice(&data[4..36]);
1513
1514 let pool_balance = {
1515 let acct = input
1516 .internals_mut()
1517 .load_account(L1_PRICER_FUNDS_POOL_ADDRESS)
1518 .map_err(ArbPrecompileError::fatal)?;
1519 acct.data.info.balance
1520 };
1521
1522 load_arbos(input)?;
1523 let internals = input.internals_mut();
1524 let arb_state = ctx
1525 .block
1526 .arbos_state(internals)
1527 .map_err(ArbPrecompileError::fatal)?;
1528 let recognized = arb_state
1529 .l1_pricing_state
1530 .l1_fees_available(internals)
1531 .map_err(ArbPrecompileError::fatal)?;
1532
1533 if pool_balance <= recognized {
1534 crate::charge_precompile_gas(gas_used, SLOAD_GAS + COPY_GAS + 100);
1535 return Ok(PrecompileOutput::new(
1536 (*gas_used).min(gas_limit),
1537 U256::ZERO.to_be_bytes::<32>().to_vec().into(),
1538 ));
1539 }
1540
1541 let mut wei_to_transfer = pool_balance - recognized;
1542 if wei_to_transfer > max_wei {
1543 wei_to_transfer = max_wei;
1544 }
1545
1546 let new_available = recognized + wei_to_transfer;
1547 arb_state
1548 .l1_pricing_state
1549 .set_l1_fees_available(internals, new_available)
1550 .map_err(ArbPrecompileError::fatal)?;
1551
1552 crate::charge_precompile_gas(gas_used, SLOAD_GAS + SSTORE_GAS + COPY_GAS + 100);
1553 Ok(PrecompileOutput::new(
1554 (*gas_used).min(gas_limit),
1555 wei_to_transfer.to_be_bytes::<32>().to_vec().into(),
1556 ))
1557}
1558
1559fn write_stylus_param(
1562 input: &mut PrecompileInput<'_>,
1563 gas_used: &mut u64,
1564 mutate: impl FnOnce(&mut StylusParams),
1565 ctx: &ArbPrecompileCtx,
1566) -> PrecompileResult {
1567 let gas_limit = input.gas;
1568 load_arbos(input)?;
1569 let internals = input.internals_mut();
1570 let arb_state = ctx
1571 .block
1572 .arbos_state(internals)
1573 .map_err(ArbPrecompileError::fatal)?;
1574 let mut params = arb_state
1575 .programs
1576 .params(internals)
1577 .map_err(ArbPrecompileError::fatal)?;
1578 mutate(&mut params);
1579 arb_state
1580 .programs
1581 .save_params(internals, ¶ms)
1582 .map_err(ArbPrecompileError::fatal)?;
1583 crate::charge_precompile_gas(gas_used, WARM_SLOAD_GAS + SSTORE_GAS);
1584 Ok(PrecompileOutput::new(
1585 (*gas_used).min(gas_limit),
1586 Vec::new().into(),
1587 ))
1588}
1589
1590fn handle_set_activation_gas(
1591 input: &mut PrecompileInput<'_>,
1592 gas_used: &mut u64,
1593 value: U256,
1594 ctx: &ArbPrecompileCtx,
1595) -> PrecompileResult {
1596 let gas_limit = input.gas;
1597 load_arbos(input)?;
1598 let internals = input.internals_mut();
1599 let arb_state = ctx
1600 .block
1601 .arbos_state(internals)
1602 .map_err(ArbPrecompileError::fatal)?;
1603 let value_u64: u64 = value.try_into().unwrap_or(u64::MAX);
1604 arb_state
1605 .programs
1606 .set_activation_gas(internals, value_u64)
1607 .map_err(ArbPrecompileError::fatal)?;
1608 crate::charge_precompile_gas(gas_used, write_cost(value_u64 == 0));
1609 Ok(PrecompileOutput::new(
1610 (*gas_used).min(gas_limit),
1611 Vec::new().into(),
1612 ))
1613}
1614
1615fn arg_word(data: &[u8], index: usize, gas_used: u64) -> Result<U256, ArbPrecompileError> {
1620 let start = 4 + index * 32;
1621 data.get(start..start + 32)
1622 .map(U256::from_be_slice)
1623 .ok_or_else(|| ArbPrecompileError::empty_revert(gas_used))
1624}
1625
1626fn read_u8_param(gas_used: u64, data: &[u8], index: usize) -> Result<u8, ArbPrecompileError> {
1627 arg_word(data, index, gas_used)?
1628 .try_into()
1629 .map_err(|_| ArbPrecompileError::empty_revert(gas_used))
1630}
1631
1632fn read_u16_param(gas_used: u64, data: &[u8], index: usize) -> Result<u16, ArbPrecompileError> {
1633 arg_word(data, index, gas_used)?
1634 .try_into()
1635 .map_err(|_| ArbPrecompileError::empty_revert(gas_used))
1636}
1637
1638fn read_u32_param(gas_used: u64, data: &[u8]) -> Result<u32, ArbPrecompileError> {
1639 arg_word(data, 0, gas_used)?
1640 .try_into()
1641 .map_err(|_| ArbPrecompileError::empty_revert(gas_used))
1642}
1643
1644fn read_u64_param(gas_used: u64, data: &[u8]) -> Result<u64, ArbPrecompileError> {
1645 arg_word(data, 0, gas_used)?
1646 .try_into()
1647 .map_err(|_| ArbPrecompileError::empty_revert(gas_used))
1648}
1649
1650fn div_ceil(value: u64, divisor: u64) -> u64 {
1652 value.saturating_add(divisor - 1) / divisor
1653}
1654
1655fn saturating_u8(value: u64) -> u8 {
1657 value.min(u8::MAX as u64) as u8
1658}
1659
1660fn handle_add_cache_manager(
1663 input: &mut PrecompileInput<'_>,
1664 gas_used: &mut u64,
1665 ctx: &ArbPrecompileCtx,
1666) -> PrecompileResult {
1667 let data = input.data;
1668 if data.len() < 36 {
1669 return crate::burn_all_revert(input.gas);
1670 }
1671 let gas_limit = input.gas;
1672 let addr = Address::from_slice(&data[16..36]);
1673 load_arbos(input)?;
1674 let internals = input.internals_mut();
1675 let arb_state = ctx
1676 .block
1677 .arbos_state(internals)
1678 .map_err(ArbPrecompileError::fatal)?;
1679 arb_state
1680 .programs
1681 .cache_managers
1682 .add(internals, addr)
1683 .map_err(ArbPrecompileError::fatal)?;
1684 crate::charge_precompile_gas(
1685 gas_used,
1686 3 * SLOAD_GAS + 2 * SSTORE_GAS + write_cost(addr.is_zero()),
1687 );
1688 Ok(PrecompileOutput::new(
1689 (*gas_used).min(gas_limit),
1690 Vec::new().into(),
1691 ))
1692}
1693
1694fn handle_remove_cache_manager(
1695 input: &mut PrecompileInput<'_>,
1696 gas_used: &mut u64,
1697 ctx: &ArbPrecompileCtx,
1698) -> PrecompileResult {
1699 let data = input.data;
1700 if data.len() < 36 {
1701 return crate::burn_all_revert(input.gas);
1702 }
1703 let gas_limit = input.gas;
1704 let addr = Address::from_slice(&data[16..36]);
1705 load_arbos(input)?;
1706 let internals = input.internals_mut();
1707 let arb_state = ctx
1708 .block
1709 .arbos_state(internals)
1710 .map_err(ArbPrecompileError::fatal)?;
1711 let arbos_version = arb_state.arbos_version();
1712 if !arb_state
1713 .programs
1714 .cache_managers
1715 .is_member(internals, addr)
1716 .map_err(ArbPrecompileError::fatal)?
1717 {
1718 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1719 }
1720 crate::charge_precompile_gas(gas_used, SLOAD_GAS);
1721 arb_state
1722 .programs
1723 .cache_managers
1724 .remove(internals, addr, arbos_version, gas_used)
1725 .map_err(ArbPrecompileError::fatal)?;
1726 Ok(PrecompileOutput::new(
1727 (*gas_used).min(gas_limit),
1728 Vec::new().into(),
1729 ))
1730}
1731
1732const FEATURE_ENABLE_DELAY: u64 = 7 * 24 * 60 * 60;
1734
1735fn handle_set_feature_time(
1736 input: &mut PrecompileInput<'_>,
1737 gas_used: &mut u64,
1738 kind: FeatureTimeKind,
1739 ctx: &ArbPrecompileCtx,
1740) -> PrecompileResult {
1741 let data = input.data;
1742 if data.len() < 36 {
1743 return crate::burn_all_revert(input.gas);
1744 }
1745 let gas_limit = input.gas;
1746 let timestamp: u64 = U256::from_be_slice(&data[4..36])
1747 .try_into()
1748 .map_err(|_| ArbPrecompileError::empty_revert(*gas_used))?;
1749
1750 load_arbos(input)?;
1751 let now: u64 = input
1752 .internals_mut()
1753 .block_timestamp()
1754 .try_into()
1755 .unwrap_or(0u64);
1756 let internals = input.internals_mut();
1757 let arb_state = ctx
1758 .block
1759 .arbos_state(internals)
1760 .map_err(ArbPrecompileError::fatal)?;
1761
1762 if timestamp == 0 {
1763 write_feature_time(arb_state, internals, kind, 0)?;
1764 crate::charge_precompile_gas(gas_used, write_cost(true));
1765 return Ok(PrecompileOutput::new(
1766 (*gas_used).min(gas_limit),
1767 Vec::new().into(),
1768 ));
1769 }
1770
1771 let stored = read_feature_time(arb_state, internals, kind)?;
1772
1773 if (stored > now + FEATURE_ENABLE_DELAY || stored == 0)
1774 && timestamp < now + FEATURE_ENABLE_DELAY
1775 {
1776 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1777 }
1778 if stored > now && stored <= now + FEATURE_ENABLE_DELAY && timestamp < stored {
1779 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1780 }
1781
1782 write_feature_time(arb_state, internals, kind, timestamp)?;
1783 crate::charge_precompile_gas(gas_used, SLOAD_GAS + SSTORE_GAS);
1784 Ok(PrecompileOutput::new(
1785 (*gas_used).min(gas_limit),
1786 Vec::new().into(),
1787 ))
1788}
1789
1790fn emit_address_event(input: &mut PrecompileInput<'_>, topic0: B256, addr: Address) {
1791 let topic1 = B256::left_padding_from(addr.as_slice());
1792 input.internals_mut().log(Log::new_unchecked(
1793 ARBOWNER_ADDRESS,
1794 vec![topic0, topic1],
1795 alloy_primitives::Bytes::new(),
1796 ));
1797}
1798
1799fn handle_add_to_set_with_feature_check(
1800 input: &mut PrecompileInput<'_>,
1801 gas_used: &mut u64,
1802 set_kind: AddressSetKind,
1803 feature_kind: FeatureTimeKind,
1804 event_topic: Option<B256>,
1805 ctx: &ArbPrecompileCtx,
1806) -> PrecompileResult {
1807 let data = input.data;
1808 if data.len() < 36 {
1809 return crate::burn_all_revert(input.gas);
1810 }
1811 let gas_limit = input.gas;
1812 let addr = Address::from_slice(&data[16..36]);
1813 load_arbos(input)?;
1814 let now: u64 = input
1815 .internals_mut()
1816 .block_timestamp()
1817 .try_into()
1818 .unwrap_or(0u64);
1819 let internals = input.internals_mut();
1820 let arb_state = ctx
1821 .block
1822 .arbos_state(internals)
1823 .map_err(ArbPrecompileError::fatal)?;
1824
1825 let enabled_time = read_feature_time(arb_state, internals, feature_kind)?;
1826 if enabled_time == 0 || enabled_time > now {
1827 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1828 }
1829
1830 address_set(arb_state, set_kind)
1831 .add(internals, addr)
1832 .map_err(ArbPrecompileError::fatal)?;
1833
1834 if let Some(topic0) = event_topic {
1835 emit_address_event(input, topic0, addr);
1836 }
1837
1838 crate::charge_precompile_gas(
1840 gas_used,
1841 4 * SLOAD_GAS + 2 * SSTORE_GAS + write_cost(addr.is_zero()),
1842 );
1843 Ok(PrecompileOutput::new(
1844 (*gas_used).min(gas_limit),
1845 Vec::new().into(),
1846 ))
1847}
1848
1849fn handle_remove_from_set(
1850 input: &mut PrecompileInput<'_>,
1851 gas_used: &mut u64,
1852 set_kind: AddressSetKind,
1853 event_topic: Option<B256>,
1854 ctx: &ArbPrecompileCtx,
1855) -> PrecompileResult {
1856 let data = input.data;
1857 if data.len() < 36 {
1858 return crate::burn_all_revert(input.gas);
1859 }
1860 let gas_limit = input.gas;
1861 let addr = Address::from_slice(&data[16..36]);
1862 load_arbos(input)?;
1863
1864 let internals = input.internals_mut();
1865 let arb_state = ctx
1866 .block
1867 .arbos_state(internals)
1868 .map_err(ArbPrecompileError::fatal)?;
1869 let arbos_version = arb_state.arbos_version();
1870
1871 let set = address_set(arb_state, set_kind);
1872 if !set
1873 .is_member(internals, addr)
1874 .map_err(ArbPrecompileError::fatal)?
1875 {
1876 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1877 }
1878 crate::charge_precompile_gas(gas_used, SLOAD_GAS);
1879 set.remove(internals, addr, arbos_version, gas_used)
1880 .map_err(ArbPrecompileError::fatal)?;
1881
1882 if let Some(topic0) = event_topic {
1883 emit_address_event(input, topic0, addr);
1884 }
1885
1886 Ok(PrecompileOutput::new(
1887 (*gas_used).min(gas_limit),
1888 Vec::new().into(),
1889 ))
1890}
1891
1892const GAS_CONSTRAINTS_MAX_NUM: usize = 20;
1895const MAX_PRICING_EXPONENT_BIPS: u64 = 85_000;
1896
1897fn handle_set_gas_pricing_constraints(
1898 input: &mut PrecompileInput<'_>,
1899 gas_used: &mut u64,
1900 ctx: &ArbPrecompileCtx,
1901) -> PrecompileResult {
1902 let data = input.data;
1903 if data.len() < 68 {
1905 return crate::burn_all_revert(input.gas);
1906 }
1907 let gas_limit = input.gas;
1908
1909 let count: u64 = U256::from_be_slice(&data[36..68])
1910 .try_into()
1911 .map_err(|_| ArbPrecompileError::empty_revert(*gas_used))?;
1912
1913 let expected_len = 68 + (count as usize) * 96;
1914 if data.len() < expected_len {
1915 return crate::burn_all_revert(gas_limit);
1916 }
1917
1918 load_arbos(input)?;
1919 let internals = input.internals_mut();
1920 let arb_state = ctx
1921 .block
1922 .arbos_state(internals)
1923 .map_err(ArbPrecompileError::fatal)?;
1924
1925 arb_state
1926 .l2_pricing_state
1927 .clear_gas_constraints(internals)
1928 .map_err(ArbPrecompileError::fatal)?;
1929
1930 let arbos_version = arb_state.arbos_version();
1931 use arb_chainspec::arbos_version as arb_ver;
1932 if (arb_ver::ARBOS_VERSION_MULTI_CONSTRAINT_FIX..arb_ver::ARBOS_VERSION_MULTI_GAS_CONSTRAINTS)
1933 .contains(&arbos_version)
1934 && (count as usize) > GAS_CONSTRAINTS_MAX_NUM
1935 {
1936 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1937 }
1938
1939 for i in 0..count {
1940 let base = 68 + (i as usize) * 96;
1941 let target: u64 = U256::from_be_slice(&data[base..base + 32])
1942 .try_into()
1943 .unwrap_or(0);
1944 let window: u64 = U256::from_be_slice(&data[base + 32..base + 64])
1945 .try_into()
1946 .unwrap_or(0);
1947 let backlog: u64 = U256::from_be_slice(&data[base + 64..base + 96])
1948 .try_into()
1949 .unwrap_or(0);
1950
1951 if target == 0 || window == 0 {
1952 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
1953 }
1954
1955 arb_state
1956 .l2_pricing_state
1957 .add_gas_constraint(internals, target, window, backlog)
1958 .map_err(ArbPrecompileError::fatal)?;
1959 }
1960
1961 Ok(PrecompileOutput::new(
1964 (*gas_used).min(gas_limit),
1965 Vec::new().into(),
1966 ))
1967}
1968
1969fn handle_set_multi_gas_pricing_constraints(
1972 input: &mut PrecompileInput<'_>,
1973 gas_used: &mut u64,
1974 ctx: &ArbPrecompileCtx,
1975) -> PrecompileResult {
1976 let data = input.data;
1977 if data.len() < 68 {
1978 return crate::burn_all_revert(input.gas);
1979 }
1980 let gas_limit = input.gas;
1981
1982 let _outer_offset: usize = U256::from_be_slice(&data[4..36])
1983 .try_into()
1984 .unwrap_or(0usize);
1985 let count: u64 = U256::from_be_slice(&data[36..68])
1986 .try_into()
1987 .map_err(|_| ArbPrecompileError::empty_revert(*gas_used))?;
1988
1989 let array_data_start = 68;
1990
1991 let mut struct_offsets = Vec::with_capacity(count as usize);
1992 for i in 0..count as usize {
1993 let offset_pos = array_data_start + i * 32;
1994 if data.len() < offset_pos + 32 {
1995 return crate::burn_all_revert(gas_limit);
1996 }
1997 let offset: usize = U256::from_be_slice(&data[offset_pos..offset_pos + 32])
1998 .try_into()
1999 .unwrap_or(0);
2000 struct_offsets.push(array_data_start + offset);
2001 }
2002
2003 load_arbos(input)?;
2004 let internals = input.internals_mut();
2005 let arb_state = ctx
2006 .block
2007 .arbos_state(internals)
2008 .map_err(ArbPrecompileError::fatal)?;
2009
2010 arb_state
2011 .l2_pricing_state
2012 .clear_multi_gas_constraints(internals)
2013 .map_err(ArbPrecompileError::fatal)?;
2014
2015 for (i, &struct_start) in struct_offsets.iter().enumerate() {
2016 if data.len() < struct_start + 128 {
2017 return crate::burn_all_revert(gas_limit);
2018 }
2019
2020 let resources_offset: usize = U256::from_be_slice(&data[struct_start..struct_start + 32])
2021 .try_into()
2022 .unwrap_or(0);
2023 let window: u32 = U256::from_be_slice(&data[struct_start + 32..struct_start + 64])
2024 .try_into()
2025 .unwrap_or(0);
2026 let target: u64 = U256::from_be_slice(&data[struct_start + 64..struct_start + 96])
2027 .try_into()
2028 .unwrap_or(0);
2029 let backlog: u64 = U256::from_be_slice(&data[struct_start + 96..struct_start + 128])
2030 .try_into()
2031 .unwrap_or(0);
2032
2033 if target == 0 || window == 0 {
2034 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
2035 }
2036 let resources_start = struct_start + resources_offset;
2037
2038 if data.len() < resources_start + 32 {
2039 return crate::burn_all_revert(gas_limit);
2040 }
2041
2042 let num_resources: usize =
2043 U256::from_be_slice(&data[resources_start..resources_start + 32])
2044 .try_into()
2045 .unwrap_or(0);
2046
2047 let mut weights = [0u64; NUM_RESOURCE_KIND];
2048 for r in 0..num_resources {
2049 let r_start = resources_start + 32 + r * 64;
2050 if data.len() < r_start + 64 {
2051 return crate::burn_all_revert(gas_limit);
2052 }
2053 let resource: u8 = U256::from_be_slice(&data[r_start..r_start + 32])
2054 .try_into()
2055 .unwrap_or(0);
2056 let weight: u64 = U256::from_be_slice(&data[r_start + 32..r_start + 64])
2057 .try_into()
2058 .unwrap_or(0);
2059
2060 if !arb_primitives::multigas::ResourceKind::is_valid_id(resource) {
2061 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
2062 }
2063 weights[resource as usize] = weight;
2064 }
2065
2066 arb_state
2067 .l2_pricing_state
2068 .add_multi_gas_constraint(internals, target, window, backlog, &weights)
2069 .map_err(ArbPrecompileError::fatal)?;
2070
2071 validate_multi_gas_exponents(internals, arb_state, (i as u64) + 1, *gas_used)?;
2072 }
2073
2074 Ok(PrecompileOutput::new(
2077 (*gas_used).min(gas_limit),
2078 Vec::new().into(),
2079 ))
2080}
2081
2082fn validate_multi_gas_exponents<D, B, C>(
2083 backend: &mut C,
2084 arb_state: &arbos::arbos_state::ArbosState<'_, D, B>,
2085 count: u64,
2086 gas_used: u64,
2087) -> Result<(), ArbPrecompileError>
2088where
2089 B: arbos::burn::Burner,
2090 C: arb_storage::StorageBackend,
2091{
2092 use arb_primitives::multigas::ResourceKind;
2093 let mut exponents = [0u64; NUM_RESOURCE_KIND];
2094
2095 for i in 0..count {
2096 let constraint = arb_state.l2_pricing_state.open_multi_gas_constraint_at(i);
2097 let target = constraint
2098 .target(backend)
2099 .map_err(ArbPrecompileError::fatal)?;
2100 let backlog = constraint
2101 .backlog(backend)
2102 .map_err(ArbPrecompileError::fatal)?;
2103
2104 if backlog == 0 {
2105 continue;
2106 }
2107
2108 let window = constraint
2109 .adjustment_window(backend)
2110 .map_err(ArbPrecompileError::fatal)? as u64;
2111 let max_weight = constraint
2112 .max_weight(backend)
2113 .map_err(ArbPrecompileError::fatal)?;
2114
2115 if max_weight == 0 || target == 0 || window == 0 {
2116 continue;
2117 }
2118
2119 let divisor = (window as u128)
2120 .saturating_mul(target as u128)
2121 .saturating_mul(max_weight as u128);
2122
2123 for (r, exponent) in exponents.iter_mut().enumerate().take(NUM_RESOURCE_KIND) {
2124 let kind = ResourceKind::ALL[r];
2125 if kind == ResourceKind::SingleDim {
2126 continue;
2127 }
2128 let weight = constraint
2129 .resource_weight(backend, kind)
2130 .map_err(ArbPrecompileError::fatal)?;
2131 if weight == 0 {
2132 continue;
2133 }
2134
2135 let dividend = (backlog as u128)
2136 .saturating_mul(weight as u128)
2137 .saturating_mul(10_000);
2138 let exp = dividend.checked_div(divisor).unwrap_or(0) as u64;
2139 *exponent = exponent.saturating_add(exp);
2140 }
2141 }
2142
2143 for &exp in &exponents {
2144 if exp > MAX_PRICING_EXPONENT_BIPS {
2145 return Err(ArbPrecompileError::empty_revert(gas_used));
2146 }
2147 }
2148
2149 Ok(())
2150}
2151
2152fn handle_set_chain_config(
2155 input: &mut PrecompileInput<'_>,
2156 gas_used: &mut u64,
2157 ctx: &ArbPrecompileCtx,
2158) -> PrecompileResult {
2159 let data = input.data;
2160 if data.len() < 68 {
2161 return crate::burn_all_revert(input.gas);
2162 }
2163 let gas_limit = input.gas;
2164
2165 let bytes_len: usize = U256::from_be_slice(&data[36..68])
2166 .try_into()
2167 .map_err(|_| ArbPrecompileError::empty_revert(*gas_used))?;
2168
2169 if data.len() < 68 + bytes_len {
2170 return crate::burn_all_revert(gas_limit);
2171 }
2172 let config_bytes = data[68..68 + bytes_len].to_vec();
2173
2174 load_arbos(input)?;
2175 let internals = input.internals_mut();
2176 let arb_state = ctx
2177 .block
2178 .arbos_state(internals)
2179 .map_err(ArbPrecompileError::fatal)?;
2180
2181 let old_len = arb_state
2182 .chain_config(internals)
2183 .map_err(ArbPrecompileError::fatal)?
2184 .len() as u64;
2185 arb_state
2186 .set_chain_config(internals, &config_bytes)
2187 .map_err(ArbPrecompileError::fatal)?;
2188
2189 let bytes_len = bytes_len as u64;
2192 let old_data_slots = old_len.div_ceil(32);
2193 let new_full_words = bytes_len / 32;
2194 let len_write = write_cost(bytes_len == 0);
2195 let tail_write = write_cost(bytes_len.is_multiple_of(32));
2196 let extra = SLOAD_GAS
2197 + (old_data_slots + 1) * SSTORE_ZERO_GAS
2198 + len_write
2199 + new_full_words * SSTORE_GAS
2200 + tail_write;
2201 crate::charge_precompile_gas(gas_used, extra);
2202 Ok(PrecompileOutput::new(
2203 (*gas_used).min(gas_limit),
2204 Vec::new().into(),
2205 ))
2206}
2207
2208fn handle_set_calldata_price_increase(
2209 input: &mut PrecompileInput<'_>,
2210 gas_used: &mut u64,
2211 ctx: &ArbPrecompileCtx,
2212) -> PrecompileResult {
2213 let data = input.data;
2214 if data.len() < 36 {
2215 return crate::burn_all_revert(input.gas);
2216 }
2217 let gas_limit = input.gas;
2218 let enabled = U256::from_be_slice(&data[4..36]) != U256::ZERO;
2219
2220 load_arbos(input)?;
2221 let internals = input.internals_mut();
2222 let arb_state = ctx
2223 .block
2224 .arbos_state(internals)
2225 .map_err(ArbPrecompileError::fatal)?;
2226 let new_features = arb_state
2227 .features
2228 .set_calldata_price_increase(internals, enabled)
2229 .map_err(ArbPrecompileError::fatal)?;
2230 ctx.block
2231 .state_params_dirty
2232 .store(true, std::sync::atomic::Ordering::Relaxed);
2233
2234 crate::charge_precompile_gas(gas_used, SLOAD_GAS + write_cost(new_features == U256::ZERO));
2235 Ok(PrecompileOutput::new(
2236 (*gas_used).min(gas_limit),
2237 Vec::new().into(),
2238 ))
2239}
2240
2241fn handle_set_collect_tips(
2242 input: &mut PrecompileInput<'_>,
2243 gas_used: &mut u64,
2244 ctx: &ArbPrecompileCtx,
2245) -> PrecompileResult {
2246 let data = input.data;
2247 if data.len() < 36 {
2248 return crate::burn_all_revert(input.gas);
2249 }
2250 let gas_limit = input.gas;
2251 let enabled = U256::from_be_slice(&data[4..36]) != U256::ZERO;
2252
2253 load_arbos(input)?;
2254 let internals = input.internals_mut();
2255 let arb_state = ctx
2256 .block
2257 .arbos_state(internals)
2258 .map_err(ArbPrecompileError::fatal)?;
2259 arb_state
2260 .set_collect_tips(internals, enabled)
2261 .map_err(ArbPrecompileError::fatal)?;
2262 ctx.block
2263 .state_params_dirty
2264 .store(true, std::sync::atomic::Ordering::Relaxed);
2265 crate::charge_precompile_gas(gas_used, write_cost(!enabled));
2266 Ok(PrecompileOutput::new(
2267 (*gas_used).min(gas_limit),
2268 Vec::new().into(),
2269 ))
2270}
2271
2272fn emit_owner_acts(input: &mut PrecompileInput<'_>, selector: &[u8; 4], calldata: &[u8]) {
2274 let topic0 = IArbOwner::OwnerActs::SIGNATURE_HASH;
2275 let mut method_topic = [0u8; 32];
2276 method_topic[..4].copy_from_slice(selector);
2277 let topic1 = B256::from(method_topic);
2278 let topic2 = B256::left_padding_from(input.caller.as_slice());
2279
2280 let mut log_data = Vec::with_capacity(64 + calldata.len().div_ceil(32) * 32);
2281 log_data.extend_from_slice(&U256::from(32).to_be_bytes::<32>());
2282 log_data.extend_from_slice(&U256::from(calldata.len()).to_be_bytes::<32>());
2283 log_data.extend_from_slice(calldata);
2284 let pad = (32 - (calldata.len() % 32)) % 32;
2285 log_data.extend(std::iter::repeat_n(0u8, pad));
2286
2287 input.internals_mut().log(Log::new_unchecked(
2288 ARBOWNER_ADDRESS,
2289 vec![topic0, topic1, topic2],
2290 log_data.into(),
2291 ));
2292}