arb_precompiles/
arbinfo.rs

1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{Address, U256};
3use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
4
5/// ArbInfo precompile address (0x65).
6pub const ARBINFO_ADDRESS: Address = Address::new([
7    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8    0x00, 0x00, 0x00, 0x65,
9]);
10
11// Function selectors.
12const GET_BALANCE: [u8; 4] = [0xf8, 0xb2, 0xcb, 0x4f]; // getBalance(address)
13const GET_CODE: [u8; 4] = [0x7e, 0x10, 0x5c, 0xe2]; // getCode(address)
14
15const COPY_GAS: u64 = 3;
16const SLOAD_GAS: u64 = 800;
17
18pub fn create_arbinfo_precompile() -> DynPrecompile {
19    DynPrecompile::new_stateful(PrecompileId::custom("arbinfo"), handler)
20}
21
22fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
23    let gas_limit = input.gas;
24    let data = input.data;
25    if data.len() < 4 {
26        return Err(PrecompileError::other("input too short"));
27    }
28
29    let selector: [u8; 4] = [data[0], data[1], data[2], data[3]];
30
31    let result = match selector {
32        GET_BALANCE => handle_get_balance(&mut input),
33        GET_CODE => handle_get_code(&mut input),
34        _ => Err(PrecompileError::other("unknown ArbInfo selector")),
35    };
36    crate::gas_check(gas_limit, result)
37}
38
39fn handle_get_balance(input: &mut PrecompileInput<'_>) -> PrecompileResult {
40    let data = input.data;
41    if data.len() < 36 {
42        return Err(PrecompileError::other("input too short"));
43    }
44
45    let addr = Address::from_slice(&data[16..36]);
46    let gas_limit = input.gas;
47    let internals = input.internals_mut();
48
49    let acct = internals
50        .load_account(addr)
51        .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
52
53    let balance = acct.data.info.balance;
54    // OpenArbosState (800) + argsCost (3) + BalanceGasEIP1884 (700) + resultCost (3).
55    let gas_cost = (SLOAD_GAS + 3 + 700 + COPY_GAS).min(gas_limit);
56
57    Ok(PrecompileOutput::new(
58        gas_cost,
59        balance.to_be_bytes::<32>().to_vec().into(),
60    ))
61}
62
63fn handle_get_code(input: &mut PrecompileInput<'_>) -> PrecompileResult {
64    let data = input.data;
65    if data.len() < 36 {
66        return Err(PrecompileError::other("input too short"));
67    }
68
69    let addr = Address::from_slice(&data[16..36]);
70    let gas_limit = input.gas;
71    let internals = input.internals_mut();
72
73    let acct = internals
74        .load_account_code(addr)
75        .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
76
77    let code = acct
78        .data
79        .code()
80        .map(|c| c.original_bytes())
81        .unwrap_or_default();
82
83    // ABI-encode: offset(32) + length(32) + data (padded)
84    let pad = (32 - code.len() % 32) % 32;
85    let mut out = Vec::with_capacity(64 + code.len() + pad);
86    out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
87    out.extend_from_slice(&U256::from(code.len()).to_be_bytes::<32>());
88    out.extend_from_slice(&code);
89    out.extend(std::iter::repeat_n(0u8, pad));
90
91    // OpenArbosState (800) + argsCost (3) + ColdSloadCostEIP2929 (2100) +
92    // CopyGas * WordsForBytes(code_len) + resultCost for ABI-encoded bytes.
93    let code_words = (code.len() as u64).div_ceil(32);
94    let result_words = (out.len() as u64).div_ceil(32);
95    let gas_cost =
96        (SLOAD_GAS + 3 + 2100 + COPY_GAS * code_words + COPY_GAS * result_words).min(gas_limit);
97    Ok(PrecompileOutput::new(gas_cost, out.into()))
98}