arb_precompiles/
arbownerpublic.rs

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 arbos::address_set::AddressSetError;
9use revm::precompile::{PrecompileId, PrecompileOutput, PrecompileResult};
10
11use crate::{ArbPrecompileError, interfaces::IArbOwnerPublic};
12
13/// ArbOwnerPublic precompile address (0x6b).
14pub const ARBOWNERPUBLIC_ADDRESS: Address = Address::new([
15    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16    0x00, 0x00, 0x00, 0x6b,
17]);
18
19const SLOAD_GAS: u64 = 800;
20const SSTORE_GAS: u64 = 20_000;
21const COPY_GAS: u64 = 3;
22
23pub fn create_arbownerpublic_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
24    DynPrecompile::new_stateful(PrecompileId::custom("arbownerpublic"), move |input| {
25        handler(input, &ctx)
26    })
27}
28
29fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
30    let mut gas_used = 0u64;
31    let gas_limit = input.gas;
32    crate::init_precompile_gas(&mut gas_used, ctx, input.data.len());
33
34    let call = match IArbOwnerPublic::ArbOwnerPublicCalls::abi_decode(input.data) {
35        Ok(c) => c,
36        Err(_) => return crate::burn_all_revert(gas_limit),
37    };
38    if let Some(r) = crate::reject_nonpayable_value(input.value, input.data, gas_limit, &[]) {
39        return r;
40    }
41    if let Some(r) = crate::reject_static_write(
42        input.is_static,
43        input.data,
44        gas_limit,
45        &[[0x6f, 0xe8, 0x63, 0x73]],
46    ) {
47        return r;
48    }
49    if let Some(r) = crate::reject_delegate_nonpure(
50        input.target_address != input.bytecode_address,
51        input.data,
52        gas_limit,
53        &[],
54    ) {
55        return r;
56    }
57
58    use IArbOwnerPublic::ArbOwnerPublicCalls as Calls;
59    let result = match call {
60        Calls::getNetworkFeeAccount(_) => read_network_fee_account(&mut input, &mut gas_used, ctx),
61        Calls::getInfraFeeAccount(_) => {
62            if let Some(r) = crate::check_method_version(
63                ctx,
64                gas_limit,
65                arb_chainspec::arbos_version::ARBOS_VERSION_5,
66                0,
67            ) {
68                return r;
69            }
70            read_infra_fee_account(&mut input, &mut gas_used, ctx)
71        }
72        Calls::getBrotliCompressionLevel(_) => {
73            if let Some(r) = crate::check_method_version(
74                ctx,
75                gas_limit,
76                arb_chainspec::arbos_version::ARBOS_VERSION_20,
77                0,
78            ) {
79                return r;
80            }
81            read_brotli_compression_level(&mut input, &mut gas_used, ctx)
82        }
83        Calls::getScheduledUpgrade(_) => {
84            if let Some(r) = crate::check_method_version(
85                ctx,
86                gas_limit,
87                arb_chainspec::arbos_version::ARBOS_VERSION_20,
88                0,
89            ) {
90                return r;
91            }
92            handle_scheduled_upgrade(&mut input, &mut gas_used, ctx)
93        }
94        Calls::isChainOwner(c) => handle_is_chain_owner(&mut input, &mut gas_used, c.addr, ctx),
95        Calls::getAllChainOwners(_) => handle_get_all_chain_owners(&mut input, &mut gas_used, ctx),
96        Calls::rectifyChainOwner(c) => {
97            if let Some(r) = crate::check_method_version(
98                ctx,
99                gas_limit,
100                arb_chainspec::arbos_version::ARBOS_VERSION_11,
101                0,
102            ) {
103                return r;
104            }
105            handle_rectify_chain_owner(&mut input, &mut gas_used, c.ownerToRectify, ctx)
106        }
107        Calls::isNativeTokenOwner(c) => {
108            if let Some(r) = crate::check_method_version(
109                ctx,
110                gas_limit,
111                arb_chainspec::arbos_version::ARBOS_VERSION_41,
112                0,
113            ) {
114                return r;
115            }
116            handle_is_native_token_owner(&mut input, &mut gas_used, c.addr, ctx)
117        }
118        Calls::isTransactionFilterer(c) => {
119            if let Some(r) = crate::check_method_version(
120                ctx,
121                gas_limit,
122                arb_chainspec::arbos_version::ARBOS_VERSION_TRANSACTION_FILTERING,
123                0,
124            ) {
125                return r;
126            }
127            handle_is_transaction_filterer(&mut input, &mut gas_used, c.filterer, ctx)
128        }
129        Calls::getAllNativeTokenOwners(_) => {
130            if let Some(r) = crate::check_method_version(
131                ctx,
132                gas_limit,
133                arb_chainspec::arbos_version::ARBOS_VERSION_41,
134                0,
135            ) {
136                return r;
137            }
138            handle_get_all_native_token_owners(&mut input, &mut gas_used, ctx)
139        }
140        Calls::getAllTransactionFilterers(_) => {
141            if let Some(r) = crate::check_method_version(
142                ctx,
143                gas_limit,
144                arb_chainspec::arbos_version::ARBOS_VERSION_TRANSACTION_FILTERING,
145                0,
146            ) {
147                return r;
148            }
149            handle_get_all_transaction_filterers(&mut input, &mut gas_used, ctx)
150        }
151        Calls::getNativeTokenManagementFrom(_) => {
152            if let Some(r) = crate::check_method_version(
153                ctx,
154                gas_limit,
155                arb_chainspec::arbos_version::ARBOS_VERSION_50,
156                0,
157            ) {
158                return r;
159            }
160            read_native_token_management_from(&mut input, &mut gas_used, ctx)
161        }
162        Calls::getTransactionFilteringFrom(_) => {
163            if let Some(r) = crate::check_method_version(
164                ctx,
165                gas_limit,
166                arb_chainspec::arbos_version::ARBOS_VERSION_TRANSACTION_FILTERING,
167                0,
168            ) {
169                return r;
170            }
171            read_transaction_filtering_from(&mut input, &mut gas_used, ctx)
172        }
173        Calls::getFilteredFundsRecipient(_) => {
174            if let Some(r) = crate::check_method_version(
175                ctx,
176                gas_limit,
177                arb_chainspec::arbos_version::ARBOS_VERSION_TRANSACTION_FILTERING,
178                0,
179            ) {
180                return r;
181            }
182            read_filtered_funds_recipient(&mut input, &mut gas_used, ctx)
183        }
184        Calls::isCalldataPriceIncreaseEnabled(_) => {
185            if let Some(r) = crate::check_method_version(
186                ctx,
187                gas_limit,
188                arb_chainspec::arbos_version::ARBOS_VERSION_40,
189                0,
190            ) {
191                return r;
192            }
193            handle_is_calldata_price_increase_enabled(&mut input, &mut gas_used, ctx)
194        }
195        Calls::getParentGasFloorPerToken(_) => {
196            if let Some(r) = crate::check_method_version(
197                ctx,
198                gas_limit,
199                arb_chainspec::arbos_version::ARBOS_VERSION_50,
200                0,
201            ) {
202                return r;
203            }
204            read_parent_gas_floor_per_token(&mut input, &mut gas_used, ctx)
205        }
206        Calls::getMaxStylusContractFragments(_) => {
207            if let Some(r) = crate::check_method_version(
208                ctx,
209                gas_limit,
210                arb_chainspec::arbos_version::ARBOS_VERSION_60,
211                0,
212            ) {
213                return r;
214            }
215            handle_max_stylus_fragments(&mut input, &mut gas_used, ctx)
216        }
217        Calls::getCollectTips(_) => {
218            if let Some(r) = crate::check_method_version(
219                ctx,
220                gas_limit,
221                arb_chainspec::arbos_version::ARBOS_VERSION_60,
222                0,
223            ) {
224                return r;
225            }
226            handle_get_collect_tips(&mut input, &mut gas_used, ctx)
227        }
228    };
229    crate::gas_check(ctx, gas_limit, gas_used, result)
230}
231
232fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), ArbPrecompileError> {
233    input
234        .internals_mut()
235        .load_account(ARBOS_STATE_ADDRESS)
236        .map_err(ArbPrecompileError::fatal)?;
237    Ok(())
238}
239
240fn field_read_output(
241    gas_used: &mut u64,
242    ctx: &ArbPrecompileCtx,
243    gas_limit: u64,
244    value: U256,
245) -> PrecompileResult {
246    crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
247    crate::charge_computation(gas_used, ctx, COPY_GAS);
248    Ok(PrecompileOutput::new(
249        (*gas_used).min(gas_limit),
250        value.to_be_bytes::<32>().to_vec().into(),
251    ))
252}
253
254fn read_network_fee_account(
255    input: &mut PrecompileInput<'_>,
256    gas_used: &mut u64,
257    ctx: &ArbPrecompileCtx,
258) -> PrecompileResult {
259    let gas_limit = input.gas;
260    load_arbos(input)?;
261    let internals = input.internals_mut();
262    let arb_state = ctx
263        .block
264        .arbos_state(internals)
265        .map_err(ArbPrecompileError::fatal)?;
266    let addr = arb_state
267        .network_fee_account(internals)
268        .map_err(ArbPrecompileError::fatal)?;
269    field_read_output(
270        gas_used,
271        ctx,
272        gas_limit,
273        U256::from_be_slice(addr.as_slice()),
274    )
275}
276
277fn read_infra_fee_account(
278    input: &mut PrecompileInput<'_>,
279    gas_used: &mut u64,
280    ctx: &ArbPrecompileCtx,
281) -> PrecompileResult {
282    let gas_limit = input.gas;
283    load_arbos(input)?;
284    let internals = input.internals_mut();
285    let arb_state = ctx
286        .block
287        .arbos_state(internals)
288        .map_err(ArbPrecompileError::fatal)?;
289    let addr = if ctx.block.arbos_version < arb_chainspec::arbos_version::ARBOS_VERSION_6 {
290        arb_state
291            .network_fee_account(internals)
292            .map_err(ArbPrecompileError::fatal)?
293    } else {
294        arb_state
295            .infra_fee_account(internals)
296            .map_err(ArbPrecompileError::fatal)?
297    };
298    field_read_output(
299        gas_used,
300        ctx,
301        gas_limit,
302        U256::from_be_slice(addr.as_slice()),
303    )
304}
305
306fn read_brotli_compression_level(
307    input: &mut PrecompileInput<'_>,
308    gas_used: &mut u64,
309    ctx: &ArbPrecompileCtx,
310) -> PrecompileResult {
311    let gas_limit = input.gas;
312    load_arbos(input)?;
313    let internals = input.internals_mut();
314    let arb_state = ctx
315        .block
316        .arbos_state(internals)
317        .map_err(ArbPrecompileError::fatal)?;
318    let value = arb_state
319        .brotli_compression_level(internals)
320        .map_err(ArbPrecompileError::fatal)?;
321    field_read_output(gas_used, ctx, gas_limit, U256::from(value))
322}
323
324fn read_native_token_management_from(
325    input: &mut PrecompileInput<'_>,
326    gas_used: &mut u64,
327    ctx: &ArbPrecompileCtx,
328) -> PrecompileResult {
329    let gas_limit = input.gas;
330    load_arbos(input)?;
331    let internals = input.internals_mut();
332    let arb_state = ctx
333        .block
334        .arbos_state(internals)
335        .map_err(ArbPrecompileError::fatal)?;
336    let value = arb_state
337        .native_token_management_from_time(internals)
338        .map_err(ArbPrecompileError::fatal)?;
339    field_read_output(gas_used, ctx, gas_limit, U256::from(value))
340}
341
342fn read_transaction_filtering_from(
343    input: &mut PrecompileInput<'_>,
344    gas_used: &mut u64,
345    ctx: &ArbPrecompileCtx,
346) -> PrecompileResult {
347    let gas_limit = input.gas;
348    load_arbos(input)?;
349    let internals = input.internals_mut();
350    let arb_state = ctx
351        .block
352        .arbos_state(internals)
353        .map_err(ArbPrecompileError::fatal)?;
354    let value = arb_state
355        .transaction_filtering_from_time(internals)
356        .map_err(ArbPrecompileError::fatal)?;
357    field_read_output(gas_used, ctx, gas_limit, U256::from(value))
358}
359
360fn read_filtered_funds_recipient(
361    input: &mut PrecompileInput<'_>,
362    gas_used: &mut u64,
363    ctx: &ArbPrecompileCtx,
364) -> PrecompileResult {
365    let gas_limit = input.gas;
366    load_arbos(input)?;
367    let internals = input.internals_mut();
368    let arb_state = ctx
369        .block
370        .arbos_state(internals)
371        .map_err(ArbPrecompileError::fatal)?;
372    let addr = arb_state
373        .filtered_funds_recipient(internals)
374        .map_err(ArbPrecompileError::fatal)?;
375    field_read_output(
376        gas_used,
377        ctx,
378        gas_limit,
379        U256::from_be_slice(addr.as_slice()),
380    )
381}
382
383fn read_parent_gas_floor_per_token(
384    input: &mut PrecompileInput<'_>,
385    gas_used: &mut u64,
386    ctx: &ArbPrecompileCtx,
387) -> PrecompileResult {
388    let gas_limit = input.gas;
389    load_arbos(input)?;
390    let internals = input.internals_mut();
391    let arb_state = ctx
392        .block
393        .arbos_state(internals)
394        .map_err(ArbPrecompileError::fatal)?;
395    let value = arb_state
396        .l1_pricing_state
397        .parent_gas_floor_per_token(internals)
398        .map_err(ArbPrecompileError::fatal)?;
399    field_read_output(gas_used, ctx, gas_limit, U256::from(value))
400}
401
402fn handle_scheduled_upgrade(
403    input: &mut PrecompileInput<'_>,
404    gas_used: &mut u64,
405    ctx: &ArbPrecompileCtx,
406) -> PrecompileResult {
407    let gas_limit = input.gas;
408    load_arbos(input)?;
409    let internals = input.internals_mut();
410    let arb_state = ctx
411        .block
412        .arbos_state(internals)
413        .map_err(ArbPrecompileError::fatal)?;
414    let (mut version, mut timestamp) = arb_state
415        .get_scheduled_upgrade(internals)
416        .map_err(ArbPrecompileError::fatal)?;
417    if ctx.block.arbos_version >= version {
418        version = 0;
419        timestamp = 0;
420    }
421
422    let mut out = Vec::with_capacity(64);
423    out.extend_from_slice(&U256::from(version).to_be_bytes::<32>());
424    out.extend_from_slice(&U256::from(timestamp).to_be_bytes::<32>());
425
426    crate::charge_storage_read(gas_used, ctx, 2 * SLOAD_GAS);
427    crate::charge_computation(gas_used, ctx, 2 * COPY_GAS);
428    Ok(PrecompileOutput::new(
429        (*gas_used).min(gas_limit),
430        out.into(),
431    ))
432}
433
434fn handle_is_chain_owner(
435    input: &mut PrecompileInput<'_>,
436    gas_used: &mut u64,
437    addr: Address,
438    ctx: &ArbPrecompileCtx,
439) -> PrecompileResult {
440    let gas_limit = input.gas;
441    load_arbos(input)?;
442    let internals = input.internals_mut();
443    let arb_state = ctx
444        .block
445        .arbos_state(internals)
446        .map_err(ArbPrecompileError::fatal)?;
447    let is_owner = arb_state
448        .chain_owners
449        .is_member(internals, addr)
450        .map_err(ArbPrecompileError::fatal)?;
451    let result = if is_owner {
452        U256::from(1u64)
453    } else {
454        U256::ZERO
455    };
456
457    crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
458    crate::charge_computation(gas_used, ctx, COPY_GAS);
459    Ok(PrecompileOutput::new(
460        (*gas_used).min(gas_limit),
461        result.to_be_bytes::<32>().to_vec().into(),
462    ))
463}
464
465fn handle_is_native_token_owner(
466    input: &mut PrecompileInput<'_>,
467    gas_used: &mut u64,
468    addr: Address,
469    ctx: &ArbPrecompileCtx,
470) -> PrecompileResult {
471    let gas_limit = input.gas;
472    load_arbos(input)?;
473    let internals = input.internals_mut();
474    let arb_state = ctx
475        .block
476        .arbos_state(internals)
477        .map_err(ArbPrecompileError::fatal)?;
478    let is_member = arb_state
479        .native_token_owners
480        .is_member(internals, addr)
481        .map_err(ArbPrecompileError::fatal)?;
482    let result = if is_member {
483        U256::from(1u64)
484    } else {
485        U256::ZERO
486    };
487
488    crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
489    crate::charge_computation(gas_used, ctx, COPY_GAS);
490    Ok(PrecompileOutput::new(
491        (*gas_used).min(gas_limit),
492        result.to_be_bytes::<32>().to_vec().into(),
493    ))
494}
495
496fn handle_is_transaction_filterer(
497    input: &mut PrecompileInput<'_>,
498    gas_used: &mut u64,
499    addr: Address,
500    ctx: &ArbPrecompileCtx,
501) -> PrecompileResult {
502    let gas_limit = input.gas;
503    load_arbos(input)?;
504    let internals = input.internals_mut();
505    let arb_state = ctx
506        .block
507        .arbos_state(internals)
508        .map_err(ArbPrecompileError::fatal)?;
509    let is_member = arb_state
510        .transaction_filterers
511        .is_member(internals, addr)
512        .map_err(ArbPrecompileError::fatal)?;
513    let result = if is_member {
514        U256::from(1u64)
515    } else {
516        U256::ZERO
517    };
518
519    crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
520    crate::charge_computation(gas_used, ctx, COPY_GAS);
521    Ok(PrecompileOutput::new(
522        (*gas_used).min(gas_limit),
523        result.to_be_bytes::<32>().to_vec().into(),
524    ))
525}
526
527enum AddressSetKind {
528    ChainOwners,
529    NativeTokenOwners,
530    TransactionFilterers,
531}
532
533fn handle_get_all_chain_owners(
534    input: &mut PrecompileInput<'_>,
535    gas_used: &mut u64,
536    ctx: &ArbPrecompileCtx,
537) -> PrecompileResult {
538    handle_get_all_set_members(input, gas_used, AddressSetKind::ChainOwners, 256, ctx)
539}
540
541fn handle_get_all_native_token_owners(
542    input: &mut PrecompileInput<'_>,
543    gas_used: &mut u64,
544    ctx: &ArbPrecompileCtx,
545) -> PrecompileResult {
546    handle_get_all_set_members(
547        input,
548        gas_used,
549        AddressSetKind::NativeTokenOwners,
550        65_536,
551        ctx,
552    )
553}
554
555fn handle_get_all_transaction_filterers(
556    input: &mut PrecompileInput<'_>,
557    gas_used: &mut u64,
558    ctx: &ArbPrecompileCtx,
559) -> PrecompileResult {
560    handle_get_all_set_members(
561        input,
562        gas_used,
563        AddressSetKind::TransactionFilterers,
564        65_536,
565        ctx,
566    )
567}
568
569fn handle_get_all_set_members(
570    input: &mut PrecompileInput<'_>,
571    gas_used: &mut u64,
572    kind: AddressSetKind,
573    cap: u64,
574    ctx: &ArbPrecompileCtx,
575) -> PrecompileResult {
576    let gas_limit = input.gas;
577    load_arbos(input)?;
578    let internals = input.internals_mut();
579    let arb_state = ctx
580        .block
581        .arbos_state(internals)
582        .map_err(ArbPrecompileError::fatal)?;
583    let set = match kind {
584        AddressSetKind::ChainOwners => &arb_state.chain_owners,
585        AddressSetKind::NativeTokenOwners => &arb_state.native_token_owners,
586        AddressSetKind::TransactionFilterers => &arb_state.transaction_filterers,
587    };
588    let members = set
589        .all_members(internals, cap)
590        .map_err(ArbPrecompileError::fatal)?;
591    let count = members.len() as u64;
592
593    let mut out = Vec::with_capacity(64 + members.len() * 32);
594    out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
595    out.extend_from_slice(&U256::from(count).to_be_bytes::<32>());
596    for member in &members {
597        let mut word = [0u8; 32];
598        word[12..32].copy_from_slice(member.as_slice());
599        out.extend_from_slice(&word);
600    }
601
602    crate::charge_storage_read(gas_used, ctx, (1 + count) * SLOAD_GAS);
603    crate::charge_computation(gas_used, ctx, (2 + count) * COPY_GAS);
604    Ok(PrecompileOutput::new(
605        (*gas_used).min(gas_limit),
606        out.into(),
607    ))
608}
609
610fn handle_rectify_chain_owner(
611    input: &mut PrecompileInput<'_>,
612    gas_used: &mut u64,
613    addr: Address,
614    ctx: &ArbPrecompileCtx,
615) -> PrecompileResult {
616    let gas_limit = input.gas;
617    load_arbos(input)?;
618
619    let internals = input.internals_mut();
620    let arb_state = ctx
621        .block
622        .arbos_state(internals)
623        .map_err(ArbPrecompileError::fatal)?;
624
625    match arb_state.chain_owners.rectify_mapping(internals, addr) {
626        Ok(()) => {}
627        Err(AddressSetError::Storage(s)) => return Err(ArbPrecompileError::fatal(s).into()),
628        Err(AddressSetError::NotMember) => {
629            crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
630            return Err(ArbPrecompileError::empty_revert(*gas_used).into());
631        }
632        Err(AddressSetError::MappingAlreadyConsistent) => {
633            crate::charge_storage_read(gas_used, ctx, 4 * SLOAD_GAS);
634            return Err(ArbPrecompileError::empty_revert(*gas_used).into());
635        }
636    }
637
638    let topic0 = alloy_primitives::keccak256("ChainOwnerRectified(address)");
639    let addr_hash = alloy_primitives::B256::left_padding_from(addr.as_slice());
640    input
641        .internals_mut()
642        .log(alloy_primitives::Log::new_unchecked(
643            ARBOWNERPUBLIC_ADDRESS,
644            vec![topic0],
645            addr_hash.0.to_vec().into(),
646        ));
647
648    const SSTORE_ZERO_GAS: u64 = 5_000;
649    const RECTIFY_EVENT_GAS: u64 = 1_006;
650    crate::charge_storage_read(gas_used, ctx, 7 * SLOAD_GAS);
651    crate::charge_storage_write(gas_used, ctx, SSTORE_ZERO_GAS + 3 * SSTORE_GAS);
652    crate::charge_history_growth(gas_used, ctx, RECTIFY_EVENT_GAS);
653    // No return value: result cost covers zero words.
654    Ok(PrecompileOutput::new(
655        (*gas_used).min(gas_limit),
656        Vec::new().into(),
657    ))
658}
659
660fn handle_is_calldata_price_increase_enabled(
661    input: &mut PrecompileInput<'_>,
662    gas_used: &mut u64,
663    ctx: &ArbPrecompileCtx,
664) -> PrecompileResult {
665    let gas_limit = input.gas;
666    load_arbos(input)?;
667    let internals = input.internals_mut();
668    let arb_state = ctx
669        .block
670        .arbos_state(internals)
671        .map_err(ArbPrecompileError::fatal)?;
672    let enabled = arb_state
673        .features
674        .is_increased_calldata_price_enabled(internals)
675        .map_err(ArbPrecompileError::fatal)?;
676    let value = if enabled {
677        U256::from(1u64)
678    } else {
679        U256::ZERO
680    };
681    crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
682    crate::charge_computation(gas_used, ctx, COPY_GAS);
683    Ok(PrecompileOutput::new(
684        (*gas_used).min(gas_limit),
685        value.to_be_bytes::<32>().to_vec().into(),
686    ))
687}
688
689fn handle_max_stylus_fragments(
690    input: &mut PrecompileInput<'_>,
691    gas_used: &mut u64,
692    ctx: &ArbPrecompileCtx,
693) -> PrecompileResult {
694    let gas_limit = input.gas;
695    load_arbos(input)?;
696    let internals = input.internals_mut();
697    let arb_state = ctx
698        .block
699        .arbos_state(internals)
700        .map_err(ArbPrecompileError::fatal)?;
701    let params = arb_state
702        .programs
703        .params(internals)
704        .map_err(ArbPrecompileError::fatal)?;
705    let mut count = params.max_fragment_count;
706    if count == 0 {
707        count = arbos::programs::params::INITIAL_MAX_FRAGMENT_COUNT;
708    }
709    let mut out = [0u8; 32];
710    out[31] = count;
711    crate::charge_params_read(gas_used, ctx);
712    crate::charge_computation(gas_used, ctx, COPY_GAS);
713    Ok(PrecompileOutput::new(
714        (*gas_used).min(gas_limit),
715        out.to_vec().into(),
716    ))
717}
718
719fn handle_get_collect_tips(
720    input: &mut PrecompileInput<'_>,
721    gas_used: &mut u64,
722    ctx: &ArbPrecompileCtx,
723) -> PrecompileResult {
724    let gas_limit = input.gas;
725    load_arbos(input)?;
726    let internals = input.internals_mut();
727    let arb_state = ctx
728        .block
729        .arbos_state(internals)
730        .map_err(ArbPrecompileError::fatal)?;
731    let value = arb_state
732        .collect_tips(internals)
733        .map_err(ArbPrecompileError::fatal)?;
734
735    let mut out = [0u8; 32];
736    if value {
737        out[31] = 1;
738    }
739    crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
740    crate::charge_computation(gas_used, ctx, COPY_GAS);
741    Ok(PrecompileOutput::new(
742        (*gas_used).min(gas_limit),
743        out.to_vec().into(),
744    ))
745}