arb_precompiles/
arbinfo.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::{ArbPrecompileError, interfaces::IArbInfo};
10
11/// ArbInfo precompile address (0x65).
12pub const ARBINFO_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, 0x65,
15]);
16
17const COPY_GAS: u64 = 3;
18
19pub fn create_arbinfo_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
20    DynPrecompile::new_stateful(PrecompileId::custom("arbinfo"), move |input| {
21        handler(input, &ctx)
22    })
23}
24
25fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
26    let mut gas_used = 0u64;
27    let gas_limit = input.gas;
28    crate::init_precompile_gas(&mut gas_used, ctx, input.data.len());
29
30    let call = match IArbInfo::ArbInfoCalls::abi_decode(input.data) {
31        Ok(c) => c,
32        Err(_) => return crate::burn_all_revert(gas_limit),
33    };
34    if let Some(r) = crate::reject_nonpayable_value(input.value, input.data, gas_limit, &[]) {
35        return r;
36    }
37    if let Some(r) = crate::reject_delegate_nonpure(
38        input.target_address != input.bytecode_address,
39        input.data,
40        gas_limit,
41        &[],
42    ) {
43        return r;
44    }
45
46    use IArbInfo::ArbInfoCalls;
47    let result = match call {
48        ArbInfoCalls::getBalance(c) => {
49            handle_get_balance(&mut input, &mut gas_used, ctx, c.account)
50        }
51        ArbInfoCalls::getCode(c) => handle_get_code(&mut input, &mut gas_used, ctx, c.account),
52    };
53    crate::gas_check(ctx, gas_limit, gas_used, result)
54}
55
56fn handle_get_balance(
57    input: &mut PrecompileInput<'_>,
58    gas_used: &mut u64,
59    ctx: &ArbPrecompileCtx,
60    addr: Address,
61) -> PrecompileResult {
62    let gas_limit = input.gas;
63    let balance = crate::without_access_list_effect(input.internals_mut(), |internals| {
64        internals
65            .load_account(addr)
66            .map(|acct| acct.data.info.balance)
67            .map_err(ArbPrecompileError::fatal)
68    })?;
69    crate::charge_computation(gas_used, ctx, 700);
70    crate::charge_computation(gas_used, ctx, COPY_GAS);
71    Ok(PrecompileOutput::new(
72        (*gas_used).min(gas_limit),
73        balance.to_be_bytes::<32>().to_vec().into(),
74    ))
75}
76
77fn handle_get_code(
78    input: &mut PrecompileInput<'_>,
79    gas_used: &mut u64,
80    ctx: &ArbPrecompileCtx,
81    addr: Address,
82) -> PrecompileResult {
83    let gas_limit = input.gas;
84    let code = crate::without_access_list_effect(input.internals_mut(), |internals| {
85        internals
86            .load_account_code(addr)
87            .map(|acct| {
88                acct.data
89                    .code()
90                    .map(|c| c.original_bytes())
91                    .unwrap_or_default()
92            })
93            .map_err(ArbPrecompileError::fatal)
94    })?;
95
96    let pad = (32 - code.len() % 32) % 32;
97    let mut out = Vec::with_capacity(64 + code.len() + pad);
98    out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
99    out.extend_from_slice(&U256::from(code.len()).to_be_bytes::<32>());
100    out.extend_from_slice(&code);
101    out.extend(std::iter::repeat_n(0u8, pad));
102
103    let code_words = (code.len() as u64).div_ceil(32);
104    let result_words = (out.len() as u64).div_ceil(32);
105    crate::charge_storage_read(gas_used, ctx, 2100 + COPY_GAS * code_words);
106    crate::charge_computation(gas_used, ctx, COPY_GAS * result_words);
107    Ok(PrecompileOutput::new(
108        (*gas_used).min(gas_limit),
109        out.into(),
110    ))
111}