1use alloy::primitives::{Address, U256};
15use std::marker::PhantomData;
16use tempo_precompiles_macros;
17
18use crate::{
19 error::Result,
20 storage::{Handler, LayoutCtx, Storable, StorableType, packing, types::Slot},
21};
22
23tempo_precompiles_macros::storable_arrays!();
25tempo_precompiles_macros::storable_nested_arrays!();
27
28pub struct ArrayHandler<T, const N: usize>
56where
57 T: StorableType,
58{
59 base_slot: U256,
60 address: Address,
61 _phantom: PhantomData<T>,
62}
63
64impl<T, const N: usize> ArrayHandler<T, N>
65where
66 T: StorableType,
67{
68 #[inline]
70 pub fn new(base_slot: U256, address: Address) -> Self {
71 Self {
72 base_slot,
73 address,
74 _phantom: PhantomData,
75 }
76 }
77
78 #[inline]
80 fn as_slot(&self) -> Slot<[T; N]> {
81 Slot::new(self.base_slot, self.address)
82 }
83
84 #[inline]
89 pub fn base_slot(&self) -> ::alloy::primitives::U256 {
90 self.base_slot
91 }
92
93 #[inline]
95 pub const fn len(&self) -> usize {
96 N
97 }
98
99 #[inline]
101 pub const fn is_empty(&self) -> bool {
102 N == 0
103 }
104
105 #[inline]
111 pub fn at(&self, index: usize) -> Option<T::Handler>
112 where
113 T: StorableType,
114 {
115 if index >= N {
116 return None;
117 }
118
119 let (base_slot, layout_ctx) = if T::BYTES <= 16 {
121 let location = packing::calc_element_loc(index, T::BYTES);
122 (
123 self.base_slot + U256::from(location.offset_slots),
124 LayoutCtx::packed(location.offset_bytes),
125 )
126 } else {
127 (
128 self.base_slot + U256::from(index * T::SLOTS),
129 LayoutCtx::FULL,
130 )
131 };
132
133 Some(T::handle(base_slot, layout_ctx, self.address))
134 }
135}
136
137impl<T, const N: usize> Handler<[T; N]> for ArrayHandler<T, N>
138where
139 T: StorableType,
140 [T; N]: Storable,
141{
142 #[inline]
144 fn read(&self) -> Result<[T; N]> {
145 self.as_slot().read()
146 }
147
148 #[inline]
150 fn write(&mut self, value: [T; N]) -> Result<()> {
151 self.as_slot().write(value)
152 }
153
154 #[inline]
156 fn delete(&mut self) -> Result<()> {
157 self.as_slot().delete()
158 }
159
160 #[inline]
162 fn t_read(&self) -> Result<[T; N]> {
163 self.as_slot().t_read()
164 }
165
166 #[inline]
168 fn t_write(&mut self, value: [T; N]) -> Result<()> {
169 self.as_slot().t_write(value)
170 }
171
172 #[inline]
174 fn t_delete(&mut self) -> Result<()> {
175 self.as_slot().t_delete()
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::{
183 storage::{Layout, LayoutCtx, PrecompileStorageProvider, StorageCtx},
184 test_util::setup_storage,
185 };
186 use proptest::prelude::*;
187
188 fn arb_safe_slot() -> impl Strategy<Value = U256> {
190 any::<[u64; 4]>().prop_map(|limbs| {
191 U256::from_limbs(limbs) % (U256::MAX - U256::from(10000))
193 })
194 }
195
196 #[test]
197 fn test_array_u8_32_single_slot() {
198 let (mut storage, address) = setup_storage();
199 let base_slot = U256::ZERO;
200
201 let data: [u8; 32] = [
203 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
204 25, 26, 27, 28, 29, 30, 31, 32,
205 ];
206
207 assert_eq!(<[u8; 32] as StorableType>::LAYOUT, Layout::Slots(1));
209
210 StorageCtx::enter(&mut storage, || {
212 let mut slot = <[u8; 32]>::handle(base_slot, LayoutCtx::FULL, address);
213 slot.write(data).unwrap();
214 let loaded = slot.read().unwrap();
215 assert_eq!(loaded, data, "[u8; 32] roundtrip failed");
216
217 slot.delete().unwrap();
219 });
220 let slot_value = storage.sload(address, base_slot).unwrap();
221 assert_eq!(slot_value, U256::ZERO, "Slot not cleared after delete");
222 }
223
224 #[test]
225 fn test_array_u64_5_multi_slot() {
226 let (mut storage, address) = setup_storage();
227 let base_slot = U256::from(100);
228
229 let data: [u64; 5] = [1, 2, 3, 4, 5];
231
232 assert_eq!(<[u64; 5] as StorableType>::LAYOUT, Layout::Slots(2));
234
235 StorageCtx::enter(&mut storage, || {
237 let mut slot = <[u64; 5]>::handle(base_slot, LayoutCtx::FULL, address);
238 slot.write(data).unwrap();
239 let loaded = slot.read().unwrap();
240 assert_eq!(loaded, data, "[u64; 5] roundtrip failed");
241 });
242
243 let slot0 = storage.sload(address, base_slot).unwrap();
245 let slot1 = storage.sload(address, base_slot + U256::ONE).unwrap();
246 assert_ne!(slot0, U256::ZERO, "Slot 0 should be non-zero");
247 assert_ne!(slot1, U256::ZERO, "Slot 1 should be non-zero");
248
249 StorageCtx::enter(&mut storage, || {
251 let mut slot = <[u64; 5]>::handle(base_slot, LayoutCtx::FULL, address);
252 slot.delete().unwrap();
253 });
254 let slot0_after = storage.sload(address, base_slot).unwrap();
255 let slot1_after = storage.sload(address, base_slot + U256::ONE).unwrap();
256 assert_eq!(slot0_after, U256::ZERO, "Slot 0 not cleared");
257 assert_eq!(slot1_after, U256::ZERO, "Slot 1 not cleared");
258 }
259
260 #[test]
261 fn test_array_u16_packing() {
262 let (mut storage, address) = setup_storage();
263 let base_slot = U256::from(200);
264
265 let data: [u16; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
267
268 assert_eq!(<[u16; 16] as StorableType>::LAYOUT, Layout::Slots(1));
270
271 StorageCtx::enter(&mut storage, || {
273 let mut slot = <[u16; 16]>::handle(base_slot, LayoutCtx::FULL, address);
274 slot.write(data).unwrap();
275 let loaded = slot.read().unwrap();
276 assert_eq!(loaded, data, "[u16; 16] roundtrip failed");
277 });
278 }
279
280 #[test]
281 fn test_array_u256_no_packing() {
282 let (mut storage, address) = setup_storage();
283 let base_slot = U256::from(300);
284
285 let data: [U256; 3] = [U256::from(12345), U256::from(67890), U256::from(111111)];
287
288 assert_eq!(<[U256; 3] as StorableType>::LAYOUT, Layout::Slots(3));
290
291 StorageCtx::enter(&mut storage, || {
293 let mut slot = <[U256; 3]>::handle(base_slot, LayoutCtx::FULL, address);
294 slot.write(data).unwrap();
295 let loaded = slot.read().unwrap();
296 assert_eq!(loaded, data, "[U256; 3] roundtrip failed");
297 });
298
299 for (i, expected_value) in data.iter().enumerate() {
301 let slot_value = storage.sload(address, base_slot + U256::from(i)).unwrap();
302 assert_eq!(slot_value, *expected_value, "Slot {i} mismatch");
303 }
304 }
305
306 #[test]
307 fn test_array_address_no_packing() {
308 let (mut storage, address) = setup_storage();
309 let base_slot = U256::from(400);
310
311 let data: [Address; 3] = [
313 Address::repeat_byte(0x11),
314 Address::repeat_byte(0x22),
315 Address::repeat_byte(0x33),
316 ];
317
318 assert_eq!(<[Address; 3] as StorableType>::LAYOUT, Layout::Slots(3));
320
321 StorageCtx::enter(&mut storage, || {
323 let mut slot = <[Address; 3]>::handle(base_slot, LayoutCtx::FULL, address);
324 slot.write(data).unwrap();
325 let loaded = slot.read().unwrap();
326 assert_eq!(loaded, data, "[Address; 3] roundtrip failed");
327 });
328 }
329
330 #[test]
331 fn test_array_empty_single_element() {
332 let (mut storage, address) = setup_storage();
333 let base_slot = U256::from(500);
334
335 let data: [u8; 1] = [42];
337
338 assert_eq!(<[u8; 1] as StorableType>::LAYOUT, Layout::Slots(1));
340
341 StorageCtx::enter(&mut storage, || {
343 let mut slot = <[u8; 1]>::handle(base_slot, LayoutCtx::FULL, address);
344 slot.write(data).unwrap();
345 let loaded = slot.read().unwrap();
346 assert_eq!(loaded, data, "[u8; 1] roundtrip failed");
347 });
348 }
349
350 #[test]
351 fn test_nested_array_u8_4x8() {
352 let (mut storage, address) = setup_storage();
353 let base_slot = U256::from(600);
354
355 let data: [[u8; 4]; 8] = [
359 [1, 2, 3, 4],
360 [5, 6, 7, 8],
361 [9, 10, 11, 12],
362 [13, 14, 15, 16],
363 [17, 18, 19, 20],
364 [21, 22, 23, 24],
365 [25, 26, 27, 28],
366 [29, 30, 31, 32],
367 ];
368
369 assert_eq!(<[[u8; 4]; 8] as StorableType>::LAYOUT, Layout::Slots(8));
371
372 StorageCtx::enter(&mut storage, || {
374 let mut slot = <[[u8; 4]; 8]>::handle(base_slot, LayoutCtx::FULL, address);
375 slot.write(data).unwrap();
376 let loaded = slot.read().unwrap();
377 assert_eq!(loaded, data, "[[u8; 4]; 8] roundtrip failed");
378
379 slot.delete().unwrap();
381 });
382 for i in 0..8 {
383 let slot_value = storage.sload(address, base_slot + U256::from(i)).unwrap();
384 assert_eq!(slot_value, U256::ZERO, "Slot {i} not cleared after delete");
385 }
386 }
387
388 #[test]
389 fn test_nested_array_u16_2x8() {
390 let (mut storage, address) = setup_storage();
391 let base_slot = U256::from(700);
392
393 let data: [[u16; 2]; 8] = [
398 [100, 101],
399 [200, 201],
400 [300, 301],
401 [400, 401],
402 [500, 501],
403 [600, 601],
404 [700, 701],
405 [800, 801],
406 ];
407
408 assert_eq!(<[[u16; 2]; 8] as StorableType>::LAYOUT, Layout::Slots(8));
410
411 StorageCtx::enter(&mut storage, || {
413 let mut slot = <[[u16; 2]; 8]>::handle(base_slot, LayoutCtx::FULL, address);
414 slot.write(data).unwrap();
415 let loaded = slot.read().unwrap();
416 assert_eq!(loaded, data, "[[u16; 2]; 8] roundtrip failed");
417
418 slot.delete().unwrap();
420 });
421 for i in 0..8 {
422 let slot_value = storage.sload(address, base_slot + U256::from(i)).unwrap();
423 assert_eq!(slot_value, U256::ZERO, "Slot {i} not cleared after delete");
424 }
425 }
426
427 proptest! {
428 #![proptest_config(ProptestConfig::with_cases(500))]
429
430 #[test]
431 fn test_array_u8_32(
432 data in prop::array::uniform32(any::<u8>()),
433 base_slot in arb_safe_slot()
434 ) {
435 let (mut storage, address) = setup_storage();
436
437 StorageCtx::enter(&mut storage, || {
439 let mut slot = <[u8; 32]>::handle(base_slot, LayoutCtx::FULL, address);
440 slot.write(data).unwrap();
441 let loaded = slot.read().unwrap();
442 prop_assert_eq!(&loaded, &data, "[u8; 32] roundtrip failed");
443
444 slot.delete().unwrap();
446 Ok(())
447 })?;
448 let slot_value = storage.sload(address, base_slot).unwrap();
449 prop_assert_eq!(slot_value, U256::ZERO, "Slot not cleared after delete");
450 }
451
452 #[test]
453 fn test_array_u16_16(
454 data in prop::array::uniform16(any::<u16>()),
455 base_slot in arb_safe_slot()
456 ) {
457 let (mut storage, address) = setup_storage();
458
459 StorageCtx::enter(&mut storage, || {
461 let mut slot = <[u16; 16]>::handle(base_slot, LayoutCtx::FULL, address);
462 slot.write(data).unwrap();
463 let loaded = slot.read().unwrap();
464 prop_assert_eq!(&loaded, &data, "[u16; 16] roundtrip failed");
465 Ok(())
466 })?;
467 }
468
469 #[test]
470 fn test_array_u256_5(
471 data in prop::array::uniform5(any::<u64>()).prop_map(|arr| arr.map(U256::from)),
472 base_slot in arb_safe_slot()
473 ) {
474 let (mut storage, address) = setup_storage();
475
476 StorageCtx::enter(&mut storage, || {
478 let mut slot = <[U256; 5]>::handle(base_slot, LayoutCtx::FULL, address);
479 slot.write(data).unwrap();
480 let loaded = slot.read().unwrap();
481 prop_assert_eq!(&loaded, &data, "[U256; 5] roundtrip failed");
482 Ok(())
483 })?;
484
485 for (i, expected_value) in data.iter().enumerate() {
487 let slot_value = storage.sload(address, base_slot + U256::from(i)).unwrap();
488 prop_assert_eq!(slot_value, *expected_value, "Slot {} mismatch", i);
489 }
490
491 StorageCtx::enter(&mut storage, || {
493 let mut slot = <[U256; 5]>::handle(base_slot, LayoutCtx::FULL, address);
494 slot.delete().unwrap();
495 Ok::<(), proptest::test_runner::TestCaseError>(())
496 })?;
497 for i in 0..5 {
498 let slot_value = storage.sload(address, base_slot + U256::from(i)).unwrap();
499 prop_assert_eq!(slot_value, U256::ZERO, "Slot {} not cleared", i);
500 }
501 }
502 }
503}