Skip to main content

foundry_evm_networks/
arbitrum.rs

1//! Minimal Arbitrum system contract compatibility helpers.
2
3use std::borrow::Cow;
4
5use alloy_chains::Chain;
6use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
7use alloy_primitives::{Address, Bytes, U256, address, hex};
8use revm::precompile::{PrecompileHalt, PrecompileId, PrecompileOutput, PrecompileResult};
9
10/// ArbSys system contract address.
11pub const ARB_SYS_ADDRESS: Address = address!("0000000000000000000000000000000000000064");
12
13/// `ArbSys.arbBlockNumber()` selector.
14pub const ARB_BLOCK_NUMBER_SELECTOR: [u8; 4] = hex!("a3b1b31d");
15
16/// Gas charged by Nitro for returning the 32-byte `arbBlockNumber()` result.
17pub const ARB_BLOCK_NUMBER_GAS_COST: u64 = 3;
18
19/// ID for the ArbSys precompile.
20pub static PRECOMPILE_ID_ARB_SYS: PrecompileId = PrecompileId::Custom(Cow::Borrowed("ArbSys"));
21
22/// Returns whether `chain_id` is an Arbitrum chain.
23pub fn is_arbitrum_chain(chain_id: u64) -> bool {
24    Chain::from_id(chain_id).is_arbitrum()
25}
26
27/// Returns the ABI-encoded result for `ArbSys.arbBlockNumber()`.
28pub fn arb_block_number_output(block_number: u64) -> Bytes {
29    Bytes::copy_from_slice(&U256::from(block_number).to_be_bytes::<32>())
30}
31
32/// Returns the gas cost and ABI-encoded result for `ArbSys.arbBlockNumber()`.
33pub fn arb_block_number_call(gas_limit: u64, block_number: u64) -> Option<(u64, Bytes)> {
34    (gas_limit >= ARB_BLOCK_NUMBER_GAS_COST)
35        .then(|| (ARB_BLOCK_NUMBER_GAS_COST, arb_block_number_output(block_number)))
36}
37
38/// Returns an ArbSys precompile for the provided L2 block number.
39pub fn arb_sys_precompile(block_number: u64) -> DynPrecompile {
40    DynPrecompile::new_stateful(PRECOMPILE_ID_ARB_SYS.clone(), move |input| {
41        arb_sys_precompile_call(input, block_number)
42    })
43}
44
45fn arb_sys_precompile_call(input: PrecompileInput<'_>, block_number: u64) -> PrecompileResult {
46    if input.data.get(..4) != Some(&ARB_BLOCK_NUMBER_SELECTOR) {
47        return Ok(PrecompileOutput::halt(
48            PrecompileHalt::Other("unsupported ArbSys selector".into()),
49            input.reservoir,
50        ));
51    }
52
53    let Some((gas_cost, output)) = arb_block_number_call(input.gas, block_number) else {
54        return Ok(PrecompileOutput::halt(PrecompileHalt::OutOfGas, input.reservoir));
55    };
56
57    Ok(PrecompileOutput::new(gas_cost, output, input.reservoir))
58}