arb_precompiles/
arbinfo.rs

1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{Address, U256};
3use alloy_sol_types::SolInterface;
4use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
5
6use crate::interfaces::IArbInfo;
7
8/// ArbInfo precompile address (0x65).
9pub const ARBINFO_ADDRESS: Address = Address::new([
10    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11    0x00, 0x00, 0x00, 0x65,
12]);
13
14const COPY_GAS: u64 = 3;
15const SLOAD_GAS: u64 = 800;
16
17pub fn create_arbinfo_precompile() -> DynPrecompile {
18    DynPrecompile::new_stateful(PrecompileId::custom("arbinfo"), handler)
19}
20
21fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
22    let gas_limit = input.gas;
23    crate::init_precompile_gas(input.data.len());
24
25    let call = match IArbInfo::ArbInfoCalls::abi_decode(input.data) {
26        Ok(c) => c,
27        Err(_) => return crate::burn_all_revert(gas_limit),
28    };
29
30    use IArbInfo::ArbInfoCalls;
31    let result = match call {
32        ArbInfoCalls::getBalance(c) => handle_get_balance(&mut input, c.account),
33        ArbInfoCalls::getCode(c) => handle_get_code(&mut input, c.account),
34    };
35    crate::gas_check(gas_limit, result)
36}
37
38fn handle_get_balance(input: &mut PrecompileInput<'_>, addr: Address) -> PrecompileResult {
39    let gas_limit = input.gas;
40    let internals = input.internals_mut();
41
42    let acct = internals
43        .load_account(addr)
44        .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
45
46    let balance = acct.data.info.balance;
47    // OpenArbosState (800) + argsCost (3) + BalanceGasEIP1884 (700) + resultCost (3).
48    let gas_cost = (SLOAD_GAS + 3 + 700 + COPY_GAS).min(gas_limit);
49
50    Ok(PrecompileOutput::new(
51        gas_cost,
52        balance.to_be_bytes::<32>().to_vec().into(),
53    ))
54}
55
56fn handle_get_code(input: &mut PrecompileInput<'_>, addr: Address) -> PrecompileResult {
57    let gas_limit = input.gas;
58    let internals = input.internals_mut();
59
60    let acct = internals
61        .load_account_code(addr)
62        .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
63
64    let code = acct
65        .data
66        .code()
67        .map(|c| c.original_bytes())
68        .unwrap_or_default();
69
70    let pad = (32 - code.len() % 32) % 32;
71    let mut out = Vec::with_capacity(64 + code.len() + pad);
72    out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
73    out.extend_from_slice(&U256::from(code.len()).to_be_bytes::<32>());
74    out.extend_from_slice(&code);
75    out.extend(std::iter::repeat_n(0u8, pad));
76
77    // OpenArbosState(800) + args(3) + ColdSloadCostEIP2929(2100) + code + result copy.
78    let code_words = (code.len() as u64).div_ceil(32);
79    let result_words = (out.len() as u64).div_ceil(32);
80    let gas_cost =
81        (SLOAD_GAS + 3 + 2100 + COPY_GAS * code_words + COPY_GAS * result_words).min(gas_limit);
82    Ok(PrecompileOutput::new(gas_cost, out.into()))
83}