arb_precompiles/
arbostest.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 revm::precompile::{PrecompileId, PrecompileOutput, PrecompileResult};
8
9use crate::interfaces::IArbosTest;
10
11/// ArbosTest precompile address (0x69). Burns arbitrary amounts of L2 gas.
12pub const ARBOSTEST_ADDRESS: Address = Address::new([
13    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14    0x00, 0x00, 0x00, 0x69,
15]);
16
17pub fn create_arbostest_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
18    DynPrecompile::new_stateful(PrecompileId::custom("arbostest"), move |input| {
19        handler(input, &ctx)
20    })
21}
22
23fn handler(input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
24    let mut gas_used = 0u64;
25    let gas_limit = input.gas;
26    if !ctx.block.allow_debug_precompiles {
27        return crate::burn_all_revert(gas_limit);
28    }
29    // `burnArbGas` is `pure` (no state access) — skip the OpenArbosState SLOAD.
30    crate::init_precompile_gas_pure(&mut gas_used, ctx, input.data.len());
31
32    let call = match IArbosTest::ArbosTestCalls::abi_decode(input.data) {
33        Ok(c) => c,
34        Err(_) => return crate::burn_all_revert(gas_limit),
35    };
36
37    if let Some(r) = crate::reject_nonpayable_value(input.value, input.data, gas_limit, &[]) {
38        return r;
39    }
40
41    use IArbosTest::ArbosTestCalls;
42    let result = match call {
43        ArbosTestCalls::burnArbGas(c) => {
44            handle_burn_arb_gas(&mut gas_used, ctx, gas_limit, c.gasAmount)
45        }
46    };
47    crate::gas_check(ctx, gas_limit, gas_used, result)
48}
49
50fn handle_burn_arb_gas(
51    gas_used: &mut u64,
52    ctx: &ArbPrecompileCtx,
53    gas_limit: u64,
54    amount: U256,
55) -> PrecompileResult {
56    let Ok(to_burn) = u64::try_from(amount) else {
57        return Ok(PrecompileOutput::new_reverted(
58            *gas_used,
59            Default::default(),
60        ));
61    };
62    // Burning more than the remaining gas consumes all of it yet still
63    // succeeds; smaller amounts are charged as computation as usual.
64    let remaining = gas_limit.saturating_sub(*gas_used);
65    crate::charge_computation(gas_used, ctx, to_burn.min(remaining));
66    Ok(PrecompileOutput::new(*gas_used, Default::default()))
67}