1use alloy::primitives::{Address, U256};
2use std::marker::PhantomData;
3
4use crate::{
5 error::Result,
6 storage::{FieldLocation, Handler, LayoutCtx, Storable, StorableType, StorageCtx, StorageOps},
7};
8
9#[derive(Debug, Clone)]
28pub struct Slot<T> {
29 slot: U256,
30 ctx: LayoutCtx,
31 address: Address,
32 _ty: PhantomData<T>,
33}
34
35impl<T> Slot<T> {
36 #[inline]
41 pub fn new(slot: U256, address: Address) -> Self {
42 Self {
43 slot,
44 ctx: LayoutCtx::FULL,
45 address,
46 _ty: PhantomData,
47 }
48 }
49
50 #[inline]
54 pub fn new_with_ctx(slot: U256, ctx: LayoutCtx, address: Address) -> Self {
55 Self {
56 slot,
57 ctx,
58 address,
59 _ty: PhantomData,
60 }
61 }
62
63 #[inline]
68 pub fn new_at_offset(base_slot: U256, offset_slots: usize, address: Address) -> Self {
69 Self {
70 slot: base_slot.saturating_add(U256::from_limbs([offset_slots as u64, 0, 0, 0])),
71 ctx: LayoutCtx::FULL,
72 address,
73 _ty: PhantomData,
74 }
75 }
76
77 #[inline]
84 pub fn new_at_loc(base_slot: U256, loc: FieldLocation, address: Address) -> Self
85 where
86 T: StorableType,
87 {
88 debug_assert!(
89 T::IS_PACKABLE,
90 "`fn new_at_loc` can only be used with packable types"
91 );
92 Self {
93 slot: base_slot.saturating_add(U256::from_limbs([loc.offset_slots as u64, 0, 0, 0])),
94 ctx: LayoutCtx::packed(loc.offset_bytes),
95 address,
96 _ty: PhantomData,
97 }
98 }
99
100 #[inline]
104 pub const fn slot(&self) -> U256 {
105 self.slot
106 }
107
108 #[inline]
112 pub const fn offset(&self) -> Option<usize> {
113 self.ctx.packed_offset()
114 }
115}
116
117impl<T> StorageOps for Slot<T> {
118 fn load(&self, slot: U256) -> Result<U256> {
119 let storage = StorageCtx;
120 storage.sload(self.address, slot)
121 }
122
123 fn store(&mut self, slot: U256, value: U256) -> Result<()> {
124 let mut storage = StorageCtx;
125 storage.sstore(self.address, slot, value)
126 }
127
128 fn sinc(&mut self, slot: U256, delta: U256) -> Result<()> {
129 let mut storage = StorageCtx;
130 storage.sinc(self.address, slot, delta)
131 }
132
133 fn sdec(&mut self, slot: U256, delta: U256) -> Result<()> {
134 let mut storage = StorageCtx;
135 storage.sdec(self.address, slot, delta)
136 }
137}
138
139impl Slot<U256> {
140 #[inline]
142 pub fn sinc(&mut self, delta: U256) -> Result<()> {
143 <Self as StorageOps>::sinc(self, self.slot, delta)
144 }
145
146 #[inline]
148 pub fn sdec(&mut self, delta: U256) -> Result<()> {
149 <Self as StorageOps>::sdec(self, self.slot, delta)
150 }
151}
152
153struct TransientOps {
157 address: Address,
158}
159
160impl StorageOps for TransientOps {
161 fn load(&self, slot: U256) -> Result<U256> {
162 let storage = StorageCtx;
163 storage.tload(self.address, slot)
164 }
165
166 fn store(&mut self, slot: U256, value: U256) -> Result<()> {
167 let mut storage = StorageCtx;
168 storage.tstore(self.address, slot, value)
169 }
170}
171
172impl<T: Storable> Slot<T> {
173 fn transient(&self) -> TransientOps {
175 TransientOps {
176 address: self.address,
177 }
178 }
179}
180
181impl<T: Storable> Handler<T> for Slot<T> {
182 #[inline]
196 fn read(&self) -> Result<T> {
197 T::load(self, self.slot, self.ctx)
198 }
199
200 #[inline]
214 fn write(&mut self, value: T) -> Result<()> {
215 value.store(self, self.slot, self.ctx)
216 }
217
218 #[inline]
232 fn delete(&mut self) -> Result<()> {
233 T::delete(self, self.slot, self.ctx)
234 }
235
236 #[inline]
238 fn t_read(&self) -> Result<T> {
239 T::load(&self.transient(), self.slot, self.ctx)
240 }
241
242 #[inline]
244 fn t_write(&mut self, value: T) -> Result<()> {
245 value.store(&mut self.transient(), self.slot, self.ctx)
246 }
247
248 #[inline]
250 fn t_delete(&mut self) -> Result<()> {
251 T::delete(&mut self.transient(), self.slot, self.ctx)
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::{
259 storage::{Handler, PrecompileStorageProvider, StorageKey},
260 test_util::setup_storage,
261 };
262 use alloy::primitives::{Address, B256};
263 use proptest::prelude::*;
264
265 fn arb_address() -> impl Strategy<Value = Address> {
267 any::<[u8; 20]>().prop_map(Address::from)
268 }
269
270 fn arb_u256() -> impl Strategy<Value = U256> {
271 any::<[u64; 4]>().prop_map(U256::from_limbs)
272 }
273
274 #[test]
277 fn test_slot_size() {
278 assert_eq!(size_of::<Slot<U256>>(), 64);
280 assert_eq!(size_of::<Slot<Address>>(), 64);
281 assert_eq!(size_of::<Slot<bool>>(), 64);
282 }
283
284 #[test]
285 fn test_slot_number_extraction() -> eyre::Result<()> {
286 let (mut storage, address) = setup_storage();
287 StorageCtx::enter(&mut storage, || {
288 let slot_0 = Slot::<U256>::new(U256::ZERO, address);
289 let slot_1 = Slot::<Address>::new(U256::ONE, address);
290 let slot_max = Slot::<bool>::new(U256::MAX, address);
291 assert_eq!(slot_0.slot(), U256::ZERO);
292 assert_eq!(slot_1.slot(), U256::ONE);
293 assert_eq!(slot_max.slot(), U256::MAX);
294 Ok(())
295 })
296 }
297
298 #[test]
299 fn test_slot_edge_cases() -> eyre::Result<()> {
300 let (mut storage, address) = setup_storage();
301 StorageCtx::enter(&mut storage, || {
302 let mut slot_zero = Slot::<U256>::new(U256::ZERO, address);
304 assert_eq!(slot_zero.slot(), U256::ZERO);
305 let value_zero = U256::random();
306 slot_zero.write(value_zero)?;
307 assert_eq!(slot_zero.read()?, value_zero);
308
309 let mut slot_max = Slot::<U256>::new(U256::MAX, address);
311 assert_eq!(slot_max.slot(), U256::MAX);
312 let value_max = U256::random();
313 slot_max.write(value_max)?;
314 assert_eq!(slot_max.read()?, value_max);
315
316 Ok(())
317 })
318 }
319
320 #[test]
321 fn test_slot_read_write_types() -> eyre::Result<()> {
322 let (mut storage, address) = setup_storage();
323 let slot_num = U256::random();
324 let test_value = U256::random();
325
326 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
327 let mut u256_slot = Slot::<U256>::new(slot_num, address);
329 u256_slot.write(test_value)?;
330 assert_eq!(u256_slot.read()?, test_value);
331
332 let test_addr = Address::random();
334 let mut addr_slot = Slot::<Address>::new(U256::from(1), address);
335 addr_slot.write(test_addr)?;
336 assert_eq!(addr_slot.read()?, test_addr);
337
338 let mut bool_slot = Slot::<bool>::new(U256::from(2), address);
340 bool_slot.write(true)?;
341 assert!(bool_slot.read()?);
342 bool_slot.write(false)?;
343 assert!(!bool_slot.read()?);
344
345 let mut str_slot = Slot::<String>::new(U256::from(3), address);
347 str_slot.write("TestToken".to_string())?;
348 assert_eq!(str_slot.read()?, "TestToken");
349
350 Ok(())
351 })?;
352
353 let raw = storage.sload(address, slot_num)?;
355 assert_eq!(raw, test_value);
356 Ok(())
357 }
358
359 #[test]
360 fn test_u256_slot_sinc_sdec() -> eyre::Result<()> {
361 let (mut storage, address) = setup_storage();
362
363 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
364 let mut slot = Slot::<U256>::new(U256::from(7), address);
365
366 slot.sinc(U256::from(10))?;
367 assert_eq!(slot.read()?, U256::from(10));
368
369 slot.sdec(U256::from(3))?;
370 assert_eq!(slot.read()?, U256::from(7));
371
372 Ok(())
373 })
374 }
375
376 #[test]
377 fn test_slot_default_and_overwrite() -> eyre::Result<()> {
378 let (mut storage, address) = setup_storage();
379 StorageCtx::enter(&mut storage, || {
380 let mut slot = Slot::<u64>::new(U256::random(), address);
382 assert_eq!(slot.read()?, 0);
383
384 slot.write(100)?;
386 assert_eq!(slot.read()?, 100);
387 slot.write(200)?;
388 assert_eq!(slot.read()?, 200);
389
390 Ok(())
391 })
392 }
393
394 proptest! {
395 #![proptest_config(ProptestConfig::with_cases(500))]
396
397 #[test]
398 fn proptest_slot_read_write_u256(slot in arb_u256(),value in arb_u256()) {
399 let (mut storage, address) = setup_storage();
400 StorageCtx::enter(&mut storage, || -> std::result::Result<(), TestCaseError> {
401 let mut slot = Slot::<U256>::new(slot, address);
402
403 slot.write(value).unwrap();
405 let loaded = slot.read().unwrap();
406 prop_assert_eq!(loaded, value, "roundtrip failed");
407
408 slot.delete().unwrap();
410 let after_delete = slot.read().unwrap();
411 prop_assert_eq!(after_delete, U256::ZERO, "not zero after delete");
412 Ok(())
413 })?;
414 }
415
416 #[test]
417 fn proptest_slot_read_write_address(slot in arb_u256(),addr_value in arb_address()) {
418 let (mut storage, address) = setup_storage();
419 StorageCtx::enter(&mut storage, || -> std::result::Result<(), TestCaseError> {
420 let mut slot = Slot::<Address>::new(slot, address);
421
422 slot.write(addr_value).unwrap();
424 let loaded = slot.read().unwrap();
425 prop_assert_eq!(loaded, addr_value, "address roundtrip failed");
426 Ok(())
427 })?;
428 }
429
430 #[test]
431 fn proptest_slot_isolation(slot1 in arb_u256(), slot2 in arb_u256(), value1 in arb_u256(), value2 in arb_u256()) {
432 let (mut storage, address) = setup_storage();
433 StorageCtx::enter(&mut storage, || -> std::result::Result<(), TestCaseError> {
434 let mut slot1 = Slot::<U256>::new(slot1, address);
435 let mut slot2 = Slot::<U256>::new(slot2, address);
436
437 slot1.write(value1).unwrap();
438 slot2.write(value2).unwrap();
439
440 let loaded1 = slot1.read().unwrap();
442 let loaded2 = slot2.read().unwrap();
443
444 prop_assert_eq!(loaded1, value1, "slot 1 value changed");
445 prop_assert_eq!(loaded2, value2, "slot 2 value changed");
446
447 slot1.delete().unwrap();
449 let after_delete1 = slot1.read().unwrap();
450 let after_delete2 = slot2.read().unwrap();
451
452 prop_assert_eq!(after_delete1, U256::ZERO, "slot 1 not deleted");
453 prop_assert_eq!(after_delete2, value2, "slot 2 affected by slot 1 delete");
454 Ok(())
455 })?;
456 }
457 }
458
459 #[test]
462 fn test_slot_at_offset() -> eyre::Result<()> {
463 let (mut storage, address) = setup_storage();
464 StorageCtx::enter(&mut storage, || {
465 let pair_key = B256::random();
466 let base = pair_key.mapping_slot(U256::ZERO);
467 let test_addr = Address::random();
468
469 let mut slot = Slot::<Address>::new_at_offset(base, 0, address);
471 slot.write(test_addr)?;
472 assert_eq!(slot.read()?, test_addr);
473 slot.delete()?;
474 assert_eq!(slot.read()?, Address::ZERO);
475
476 Ok(())
477 })
478 }
479
480 #[test]
481 fn test_multiple_primitive_fields() -> eyre::Result<()> {
482 let (mut storage, address) = setup_storage();
483 StorageCtx::enter(&mut storage, || {
484 let key = B256::random();
485 let base = key.mapping_slot(U256::ZERO);
486
487 let field_0 = Address::random();
488 let field_1: u64 = (U256::random() % U256::from(u64::MAX)).to();
489 let field_2 = U256::random();
490
491 Slot::<Address>::new_at_offset(base, 0, address).write(field_0)?;
492 Slot::<u64>::new_at_offset(base, 1, address).write(field_1)?;
493 Slot::<U256>::new_at_offset(base, 2, address).write(field_2)?;
494
495 assert_eq!(
496 Slot::<Address>::new_at_offset(base, 0, address).read()?,
497 field_0
498 );
499 assert_eq!(
500 Slot::<u64>::new_at_offset(base, 1, address).read()?,
501 field_1
502 );
503 assert_eq!(
504 Slot::<U256>::new_at_offset(base, 2, address).read()?,
505 field_2
506 );
507
508 Ok(())
509 })
510 }
511
512 #[test]
515 fn test_transient_ops() -> eyre::Result<()> {
516 let (mut storage, address) = setup_storage();
517 StorageCtx::enter(&mut storage, || {
518 let mut u256_slot = Slot::<U256>::new(U256::from(1), address);
520 assert_eq!(u256_slot.t_read()?, U256::ZERO);
521
522 let num1 = U256::random();
523 let num2 = U256::random();
524 u256_slot.t_write(num1)?;
525 assert_eq!(u256_slot.t_read()?, num1);
526 u256_slot.t_write(num2)?;
527 assert_eq!(u256_slot.t_read()?, num2);
528 u256_slot.t_delete()?;
529 assert_eq!(u256_slot.t_read()?, U256::ZERO);
530
531 let mut addr_slot = Slot::<Address>::new(U256::from(2), address);
533 assert_eq!(addr_slot.t_read()?, Address::ZERO);
534
535 let addr1 = Address::random();
536 let addr2 = Address::random();
537 addr_slot.t_write(addr1)?;
538 assert_eq!(addr_slot.t_read()?, addr1);
539 addr_slot.t_write(addr2)?;
540 assert_eq!(addr_slot.t_read()?, addr2);
541 addr_slot.t_delete()?;
542 assert_eq!(addr_slot.t_read()?, Address::ZERO);
543
544 let mut bool_slot = Slot::<bool>::new(U256::from(3), address);
546 assert!(!bool_slot.t_read()?);
547
548 bool_slot.t_write(true)?;
549 assert!(bool_slot.t_read()?);
550 bool_slot.t_write(false)?;
551 assert!(!bool_slot.t_read()?);
552 bool_slot.t_delete()?;
553 assert!(!bool_slot.t_read()?);
554
555 Ok(())
556 })
557 }
558
559 #[test]
560 fn test_transient_persistence_isolation() -> eyre::Result<()> {
561 let (mut storage, address) = setup_storage();
562 let slot_num = U256::random();
563 let t_value = U256::random();
564 let s_value = U256::random();
565
566 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
567 let mut slot = Slot::<U256>::new(slot_num, address);
568
569 slot.write(s_value)?;
571 slot.t_write(t_value)?;
572 assert_eq!(slot.read()?, s_value);
573 assert_eq!(slot.t_read()?, t_value);
574
575 slot.t_delete()?;
577 assert_eq!(slot.read()?, s_value);
578 assert_eq!(slot.t_read()?, U256::ZERO);
579
580 slot.t_write(t_value)?;
582 assert_eq!(slot.t_read()?, t_value);
583
584 Ok(())
585 })?;
586
587 storage.clear_transient();
589
590 StorageCtx::enter(&mut storage, || {
592 let slot = Slot::<U256>::new(slot_num, address);
593 assert_eq!(slot.read()?, s_value);
594 assert_eq!(slot.t_read()?, U256::ZERO);
595 Ok(())
596 })
597 }
598}