1use alloy_primitives::{keccak256, Address, B256, U256};
2
3pub const ARBOS_STATE_ADDRESS: Address = {
5 let mut bytes = [0u8; 20];
6 bytes[0] = 0xA4;
7 bytes[1] = 0xB0;
8 bytes[2] = 0x5F;
9 bytes[3] = 0xFF;
10 bytes[4] = 0xFF;
11 bytes[5] = 0xFF;
12 bytes[6] = 0xFF;
13 bytes[7] = 0xFF;
14 bytes[8] = 0xFF;
15 bytes[9] = 0xFF;
16 bytes[10] = 0xFF;
17 bytes[11] = 0xFF;
18 bytes[12] = 0xFF;
19 bytes[13] = 0xFF;
20 bytes[14] = 0xFF;
21 bytes[15] = 0xFF;
22 bytes[16] = 0xFF;
23 bytes[17] = 0xFF;
24 bytes[18] = 0xFF;
25 bytes[19] = 0xFF;
26 Address::new(bytes)
27};
28
29#[derive(Debug, Clone, Default)]
31pub struct ArbHeaderInfo {
32 pub send_root: B256,
34 pub send_count: u64,
36 pub l1_block_number: u64,
38 pub arbos_format_version: u64,
40}
41
42impl ArbHeaderInfo {
43 pub fn compute_mix_hash(&self) -> B256 {
45 compute_arbos_mixhash(
46 self.send_count,
47 self.l1_block_number,
48 self.arbos_format_version,
49 )
50 }
51}
52
53pub fn compute_arbos_mixhash(send_count: u64, l1_block_number: u64, arbos_version: u64) -> B256 {
57 let mut mix = [0u8; 32];
58 mix[0..8].copy_from_slice(&send_count.to_be_bytes());
59 mix[8..16].copy_from_slice(&l1_block_number.to_be_bytes());
60 mix[16..24].copy_from_slice(&arbos_version.to_be_bytes());
61 B256::from(mix)
62}
63
64pub fn extract_send_root_from_header_extra(extra: &[u8]) -> B256 {
66 if extra.len() >= 32 {
67 B256::from_slice(&extra[..32])
68 } else {
69 B256::ZERO
70 }
71}
72
73pub fn extract_arbos_version_from_mix_hash(mix_hash: B256) -> u64 {
75 let mut buf = [0u8; 8];
76 buf.copy_from_slice(&mix_hash.0[16..24]);
77 u64::from_be_bytes(buf)
78}
79
80pub fn extract_send_count_from_mix_hash(mix_hash: B256) -> u64 {
82 let mut buf = [0u8; 8];
83 buf.copy_from_slice(&mix_hash.0[0..8]);
84 u64::from_be_bytes(buf)
85}
86
87pub fn extract_l1_block_number_from_mix_hash(mix_hash: B256) -> u64 {
89 let mut buf = [0u8; 8];
90 buf.copy_from_slice(&mix_hash.0[8..16]);
91 u64::from_be_bytes(buf)
92}
93
94fn uint_to_hash_u64_be(k: u64) -> B256 {
96 let mut out = [0u8; 32];
97 out[24..32].copy_from_slice(&k.to_be_bytes());
98 B256::from(out)
99}
100
101fn storage_key_map(storage_key: &[u8], key: B256) -> B256 {
103 let boundary = 31usize;
104 let mut data = Vec::with_capacity(storage_key.len() + boundary);
105 data.extend_from_slice(storage_key);
106 data.extend_from_slice(&key.0[..boundary]);
107 let h = keccak256(&data);
108 let mut mapped = [0u8; 32];
109 mapped[..boundary].copy_from_slice(&h.0[..boundary]);
110 mapped[boundary] = key.0[boundary];
111 B256::from(mapped)
112}
113
114fn subspace(parent: &[u8], id: &[u8]) -> [u8; 32] {
116 let mut data = Vec::with_capacity(parent.len() + id.len());
117 data.extend_from_slice(parent);
118 data.extend_from_slice(id);
119 keccak256(&data).0
120}
121
122fn calc_num_partials(size: u64) -> u64 {
124 if size == 0 {
125 return 0;
126 }
127 64 - size.leading_zeros() as u64
128}
129
130pub fn read_storage_u64_be<F: Fn(Address, B256) -> Option<U256>>(
132 read_slot: &F,
133 addr: Address,
134 slot: B256,
135) -> Option<u64> {
136 let val = read_slot(addr, slot)?;
137 let bytes: [u8; 32] = val.to_be_bytes::<32>();
138 let mut buf = [0u8; 8];
139 buf.copy_from_slice(&bytes[24..32]);
140 Some(u64::from_be_bytes(buf))
141}
142
143pub fn read_storage_hash<F: Fn(Address, B256) -> Option<U256>>(
145 read_slot: &F,
146 addr: Address,
147 slot: B256,
148) -> Option<B256> {
149 let val = read_slot(addr, slot)?;
150 let bytes: [u8; 32] = val.to_be_bytes::<32>();
151 Some(B256::from(bytes))
152}
153
154pub fn merkle_root_from_partials<F: Fn(Address, B256) -> Option<U256>>(
156 read_slot: &F,
157 addr: Address,
158 send_merkle_storage_key: &[u8],
159 size: u64,
160) -> Option<B256> {
161 if size == 0 {
162 return Some(B256::ZERO);
163 }
164 let mut hash_so_far: Option<B256> = None;
165 let mut capacity_in_hash: u64 = 0;
166 let mut capacity = 1u64;
167 let num_partials = calc_num_partials(size);
168 for level in 0..num_partials {
169 let key = uint_to_hash_u64_be(2 + level);
170 let slot = storage_key_map(send_merkle_storage_key, key);
171 let partial = read_storage_hash(read_slot, addr, slot).unwrap_or(B256::ZERO);
172 if partial != B256::ZERO {
173 if let Some(mut h) = hash_so_far {
174 while capacity_in_hash < capacity {
175 let combined = [h.0.as_slice(), &[0u8; 32]].concat();
176 h = keccak256(&combined);
177 capacity_in_hash *= 2;
178 }
179 let combined = [partial.0.as_slice(), h.0.as_slice()].concat();
180 hash_so_far = Some(keccak256(&combined));
181 capacity_in_hash = 2 * capacity;
182 } else {
183 hash_so_far = Some(partial);
184 capacity_in_hash = capacity;
185 }
186 }
187 capacity = capacity.saturating_mul(2);
188 }
189 hash_so_far
190}
191
192pub fn derive_arb_header_info<F: Fn(Address, B256) -> Option<U256>>(
194 read_slot: &F,
195) -> Option<ArbHeaderInfo> {
196 let addr = ARBOS_STATE_ADDRESS;
197 let root_storage_key: &[u8] = &[];
198
199 let version_slot = storage_key_map(root_storage_key, uint_to_hash_u64_be(0));
200 let arbos_version = read_storage_u64_be(read_slot, addr, version_slot)?;
201
202 let send_merkle_sub = subspace(root_storage_key, &[5u8]);
203 let blockhashes_sub = subspace(root_storage_key, &[6u8]);
204
205 let send_count_slot = storage_key_map(&send_merkle_sub, uint_to_hash_u64_be(0));
206 let send_count = read_storage_u64_be(read_slot, addr, send_count_slot).unwrap_or(0);
207
208 let send_root = merkle_root_from_partials(read_slot, addr, &send_merkle_sub, send_count)
209 .unwrap_or(B256::ZERO);
210
211 let l1_block_num_slot = storage_key_map(&blockhashes_sub, uint_to_hash_u64_be(0));
212 let l1_block_number = read_storage_u64_be(read_slot, addr, l1_block_num_slot).unwrap_or(0);
213
214 Some(ArbHeaderInfo {
215 send_root,
216 send_count,
217 l1_block_number,
218 arbos_format_version: arbos_version,
219 })
220}
221
222pub fn arbos_l1_block_number_slot() -> (Address, B256) {
224 let addr = ARBOS_STATE_ADDRESS;
225 let root_storage_key: &[u8] = &[];
226 let blockhashes_sub = subspace(root_storage_key, &[6u8]);
227 let l1_block_num_slot = storage_key_map(&blockhashes_sub, uint_to_hash_u64_be(0));
228 (addr, l1_block_num_slot)
229}
230
231pub fn read_arbos_version<F: Fn(Address, B256) -> Option<U256>>(read_slot: &F) -> Option<u64> {
233 let addr = ARBOS_STATE_ADDRESS;
234 let root_storage_key: &[u8] = &[];
235 let version_slot = storage_key_map(root_storage_key, uint_to_hash_u64_be(0));
236 read_storage_u64_be(read_slot, addr, version_slot)
237}
238
239pub fn read_l2_per_block_gas_limit<F: Fn(Address, B256) -> Option<U256>>(
241 read_slot: &F,
242) -> Option<u64> {
243 let addr = ARBOS_STATE_ADDRESS;
244 let root_storage_key: &[u8] = &[];
245 let l2_pricing_subspace = subspace(root_storage_key, &[1u8]);
246 let per_block_gas_limit_slot = storage_key_map(&l2_pricing_subspace, uint_to_hash_u64_be(1));
247 read_storage_u64_be(read_slot, addr, per_block_gas_limit_slot)
248}
249
250pub fn read_l2_base_fee<F: Fn(Address, B256) -> Option<U256>>(read_slot: &F) -> Option<u64> {
252 let addr = ARBOS_STATE_ADDRESS;
253 let root_storage_key: &[u8] = &[];
254 let l2_pricing_subspace = subspace(root_storage_key, &[1u8]);
255 let price_per_unit_slot = storage_key_map(&l2_pricing_subspace, uint_to_hash_u64_be(2));
256 read_storage_u64_be(read_slot, addr, price_per_unit_slot)
257}