arbos/util/
address_alias.rs

1use alloy_primitives::Address;
2
3/// The offset applied to L1 addresses when they appear on L2.
4/// This prevents a contract on L1 from impersonating an L2 address.
5pub const ADDRESS_ALIAS_OFFSET: Address = {
6    let bytes: [u8; 20] = [
7        0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8        0x00, 0x00, 0x00, 0x11, 0x11,
9    ];
10    Address::new(bytes)
11};
12
13/// The inverse offset for remapping L2 aliased addresses back to L1.
14pub const INVERSE_ADDRESS_ALIAS_OFFSET: Address = {
15    let bytes: [u8; 20] = [
16        0xee, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
17        0x00, 0x00, 0x00, 0xee, 0xef,
18    ];
19    Address::new(bytes)
20};
21
22/// Applies the L1→L2 address alias offset.
23pub fn remap_l1_address(l1_address: Address) -> Address {
24    address_add(l1_address, ADDRESS_ALIAS_OFFSET)
25}
26
27/// Removes the L1→L2 address alias offset.
28pub fn inverse_remap_l1_address(l2_address: Address) -> Address {
29    address_add(l2_address, INVERSE_ADDRESS_ALIAS_OFFSET)
30}
31
32/// Wrapping addition of two addresses (treated as 160-bit integers).
33fn address_add(a: Address, b: Address) -> Address {
34    let a_bytes = a.0 .0;
35    let b_bytes = b.0 .0;
36    let mut result = [0u8; 20];
37    let mut carry: u16 = 0;
38    for i in (0..20).rev() {
39        let sum = a_bytes[i] as u16 + b_bytes[i] as u16 + carry;
40        result[i] = sum as u8;
41        carry = sum >> 8;
42    }
43    Address::new(result)
44}
45
46/// Whether a transaction type uses address aliasing.
47pub fn does_tx_type_alias(tx_type: u8) -> bool {
48    // ArbitrumUnsignedTx = 0x65, ArbitrumContractTx = 0x66, ArbitrumRetryTx = 0x68
49    matches!(tx_type, 0x65 | 0x66 | 0x68)
50}
51
52/// Whether a transaction type incurs L1 poster costs and standard fee distribution.
53///
54/// In Nitro, the GasChargingHook sets SkipL1Charging=false for ALL on-chain txs,
55/// meaning poster gas is computed for every tx that enters the EVM. Only types that
56/// end early in StartTxHook (deposit, internal, submit-retryable) never reach the
57/// gas charging phase. RetryTx has its own special gas/fee handling.
58///
59/// UnsignedTx (0x65) and ContractTx (0x66) are L1→L2 messages that execute through
60/// the normal EVM path and MUST have poster costs and fee distribution, matching
61/// standard user txs.
62pub fn tx_type_has_poster_costs(tx_type: u8) -> bool {
63    !matches!(
64        tx_type,
65        0x64  // ArbitrumDepositTx — ends early in StartTxHook
66        | 0x68 // ArbitrumRetryTx — has its own fee path in EndTxHook
67        | 0x69 // ArbitrumSubmitRetryableTx — ends early in StartTxHook
68        | 0x6a // ArbitrumInternalTx — ends early in StartTxHook
69    )
70}