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
129struct TransientOps {
133 address: Address,
134}
135
136impl StorageOps for TransientOps {
137 fn load(&self, slot: U256) -> Result<U256> {
138 let storage = StorageCtx;
139 storage.tload(self.address, slot)
140 }
141
142 fn store(&mut self, slot: U256, value: U256) -> Result<()> {
143 let mut storage = StorageCtx;
144 storage.tstore(self.address, slot, value)
145 }
146}
147
148impl<T: Storable> Slot<T> {
149 fn transient(&self) -> TransientOps {
151 TransientOps {
152 address: self.address,
153 }
154 }
155}
156
157impl<T: Storable> Handler<T> for Slot<T> {
158 #[inline]
172 fn read(&self) -> Result<T> {
173 T::load(self, self.slot, self.ctx)
174 }
175
176 #[inline]
190 fn write(&mut self, value: T) -> Result<()> {
191 value.store(self, self.slot, self.ctx)
192 }
193
194 #[inline]
208 fn delete(&mut self) -> Result<()> {
209 T::delete(self, self.slot, self.ctx)
210 }
211
212 #[inline]
214 fn t_read(&self) -> Result<T> {
215 T::load(&self.transient(), self.slot, self.ctx)
216 }
217
218 #[inline]
220 fn t_write(&mut self, value: T) -> Result<()> {
221 value.store(&mut self.transient(), self.slot, self.ctx)
222 }
223
224 #[inline]
226 fn t_delete(&mut self) -> Result<()> {
227 T::delete(&mut self.transient(), self.slot, self.ctx)
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234 use crate::{
235 storage::{Handler, PrecompileStorageProvider, StorageKey},
236 test_util::setup_storage,
237 };
238 use alloy::primitives::{Address, B256};
239 use proptest::prelude::*;
240
241 fn arb_address() -> impl Strategy<Value = Address> {
243 any::<[u8; 20]>().prop_map(Address::from)
244 }
245
246 fn arb_u256() -> impl Strategy<Value = U256> {
247 any::<[u64; 4]>().prop_map(U256::from_limbs)
248 }
249
250 #[test]
253 fn test_slot_size() {
254 assert_eq!(size_of::<Slot<U256>>(), 64);
256 assert_eq!(size_of::<Slot<Address>>(), 64);
257 assert_eq!(size_of::<Slot<bool>>(), 64);
258 }
259
260 #[test]
261 fn test_slot_number_extraction() -> eyre::Result<()> {
262 let (mut storage, address) = setup_storage();
263 StorageCtx::enter(&mut storage, || {
264 let slot_0 = Slot::<U256>::new(U256::ZERO, address);
265 let slot_1 = Slot::<Address>::new(U256::ONE, address);
266 let slot_max = Slot::<bool>::new(U256::MAX, address);
267 assert_eq!(slot_0.slot(), U256::ZERO);
268 assert_eq!(slot_1.slot(), U256::ONE);
269 assert_eq!(slot_max.slot(), U256::MAX);
270 Ok(())
271 })
272 }
273
274 #[test]
275 fn test_slot_edge_cases() -> eyre::Result<()> {
276 let (mut storage, address) = setup_storage();
277 StorageCtx::enter(&mut storage, || {
278 let mut slot_zero = Slot::<U256>::new(U256::ZERO, address);
280 assert_eq!(slot_zero.slot(), U256::ZERO);
281 let value_zero = U256::random();
282 slot_zero.write(value_zero)?;
283 assert_eq!(slot_zero.read()?, value_zero);
284
285 let mut slot_max = Slot::<U256>::new(U256::MAX, address);
287 assert_eq!(slot_max.slot(), U256::MAX);
288 let value_max = U256::random();
289 slot_max.write(value_max)?;
290 assert_eq!(slot_max.read()?, value_max);
291
292 Ok(())
293 })
294 }
295
296 #[test]
297 fn test_slot_read_write_types() -> eyre::Result<()> {
298 let (mut storage, address) = setup_storage();
299 let slot_num = U256::random();
300 let test_value = U256::random();
301
302 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
303 let mut u256_slot = Slot::<U256>::new(slot_num, address);
305 u256_slot.write(test_value)?;
306 assert_eq!(u256_slot.read()?, test_value);
307
308 let test_addr = Address::random();
310 let mut addr_slot = Slot::<Address>::new(U256::from(1), address);
311 addr_slot.write(test_addr)?;
312 assert_eq!(addr_slot.read()?, test_addr);
313
314 let mut bool_slot = Slot::<bool>::new(U256::from(2), address);
316 bool_slot.write(true)?;
317 assert!(bool_slot.read()?);
318 bool_slot.write(false)?;
319 assert!(!bool_slot.read()?);
320
321 let mut str_slot = Slot::<String>::new(U256::from(3), address);
323 str_slot.write("TestToken".to_string())?;
324 assert_eq!(str_slot.read()?, "TestToken");
325
326 Ok(())
327 })?;
328
329 let raw = storage.sload(address, slot_num)?;
331 assert_eq!(raw, test_value);
332 Ok(())
333 }
334
335 #[test]
336 fn test_slot_default_and_overwrite() -> eyre::Result<()> {
337 let (mut storage, address) = setup_storage();
338 StorageCtx::enter(&mut storage, || {
339 let mut slot = Slot::<u64>::new(U256::random(), address);
341 assert_eq!(slot.read()?, 0);
342
343 slot.write(100)?;
345 assert_eq!(slot.read()?, 100);
346 slot.write(200)?;
347 assert_eq!(slot.read()?, 200);
348
349 Ok(())
350 })
351 }
352
353 proptest! {
354 #![proptest_config(ProptestConfig::with_cases(500))]
355
356 #[test]
357 fn proptest_slot_read_write_u256(slot in arb_u256(),value in arb_u256()) {
358 let (mut storage, address) = setup_storage();
359 StorageCtx::enter(&mut storage, || -> std::result::Result<(), TestCaseError> {
360 let mut slot = Slot::<U256>::new(slot, address);
361
362 slot.write(value).unwrap();
364 let loaded = slot.read().unwrap();
365 prop_assert_eq!(loaded, value, "roundtrip failed");
366
367 slot.delete().unwrap();
369 let after_delete = slot.read().unwrap();
370 prop_assert_eq!(after_delete, U256::ZERO, "not zero after delete");
371 Ok(())
372 })?;
373 }
374
375 #[test]
376 fn proptest_slot_read_write_address(slot in arb_u256(),addr_value in arb_address()) {
377 let (mut storage, address) = setup_storage();
378 StorageCtx::enter(&mut storage, || -> std::result::Result<(), TestCaseError> {
379 let mut slot = Slot::<Address>::new(slot, address);
380
381 slot.write(addr_value).unwrap();
383 let loaded = slot.read().unwrap();
384 prop_assert_eq!(loaded, addr_value, "address roundtrip failed");
385 Ok(())
386 })?;
387 }
388
389 #[test]
390 fn proptest_slot_isolation(slot1 in arb_u256(), slot2 in arb_u256(), value1 in arb_u256(), value2 in arb_u256()) {
391 let (mut storage, address) = setup_storage();
392 StorageCtx::enter(&mut storage, || -> std::result::Result<(), TestCaseError> {
393 let mut slot1 = Slot::<U256>::new(slot1, address);
394 let mut slot2 = Slot::<U256>::new(slot2, address);
395
396 slot1.write(value1).unwrap();
397 slot2.write(value2).unwrap();
398
399 let loaded1 = slot1.read().unwrap();
401 let loaded2 = slot2.read().unwrap();
402
403 prop_assert_eq!(loaded1, value1, "slot 1 value changed");
404 prop_assert_eq!(loaded2, value2, "slot 2 value changed");
405
406 slot1.delete().unwrap();
408 let after_delete1 = slot1.read().unwrap();
409 let after_delete2 = slot2.read().unwrap();
410
411 prop_assert_eq!(after_delete1, U256::ZERO, "slot 1 not deleted");
412 prop_assert_eq!(after_delete2, value2, "slot 2 affected by slot 1 delete");
413 Ok(())
414 })?;
415 }
416 }
417
418 #[test]
421 fn test_slot_at_offset() -> eyre::Result<()> {
422 let (mut storage, address) = setup_storage();
423 StorageCtx::enter(&mut storage, || {
424 let pair_key = B256::random();
425 let base = pair_key.mapping_slot(U256::ZERO);
426 let test_addr = Address::random();
427
428 let mut slot = Slot::<Address>::new_at_offset(base, 0, address);
430 slot.write(test_addr)?;
431 assert_eq!(slot.read()?, test_addr);
432 slot.delete()?;
433 assert_eq!(slot.read()?, Address::ZERO);
434
435 Ok(())
436 })
437 }
438
439 #[test]
440 fn test_multiple_primitive_fields() -> eyre::Result<()> {
441 let (mut storage, address) = setup_storage();
442 StorageCtx::enter(&mut storage, || {
443 let key = B256::random();
444 let base = key.mapping_slot(U256::ZERO);
445
446 let field_0 = Address::random();
447 let field_1: u64 = (U256::random() % U256::from(u64::MAX)).to();
448 let field_2 = U256::random();
449
450 Slot::<Address>::new_at_offset(base, 0, address).write(field_0)?;
451 Slot::<u64>::new_at_offset(base, 1, address).write(field_1)?;
452 Slot::<U256>::new_at_offset(base, 2, address).write(field_2)?;
453
454 assert_eq!(
455 Slot::<Address>::new_at_offset(base, 0, address).read()?,
456 field_0
457 );
458 assert_eq!(
459 Slot::<u64>::new_at_offset(base, 1, address).read()?,
460 field_1
461 );
462 assert_eq!(
463 Slot::<U256>::new_at_offset(base, 2, address).read()?,
464 field_2
465 );
466
467 Ok(())
468 })
469 }
470
471 #[test]
474 fn test_transient_ops() -> eyre::Result<()> {
475 let (mut storage, address) = setup_storage();
476 StorageCtx::enter(&mut storage, || {
477 let mut u256_slot = Slot::<U256>::new(U256::from(1), address);
479 assert_eq!(u256_slot.t_read()?, U256::ZERO);
480
481 let num1 = U256::random();
482 let num2 = U256::random();
483 u256_slot.t_write(num1)?;
484 assert_eq!(u256_slot.t_read()?, num1);
485 u256_slot.t_write(num2)?;
486 assert_eq!(u256_slot.t_read()?, num2);
487 u256_slot.t_delete()?;
488 assert_eq!(u256_slot.t_read()?, U256::ZERO);
489
490 let mut addr_slot = Slot::<Address>::new(U256::from(2), address);
492 assert_eq!(addr_slot.t_read()?, Address::ZERO);
493
494 let addr1 = Address::random();
495 let addr2 = Address::random();
496 addr_slot.t_write(addr1)?;
497 assert_eq!(addr_slot.t_read()?, addr1);
498 addr_slot.t_write(addr2)?;
499 assert_eq!(addr_slot.t_read()?, addr2);
500 addr_slot.t_delete()?;
501 assert_eq!(addr_slot.t_read()?, Address::ZERO);
502
503 let mut bool_slot = Slot::<bool>::new(U256::from(3), address);
505 assert!(!bool_slot.t_read()?);
506
507 bool_slot.t_write(true)?;
508 assert!(bool_slot.t_read()?);
509 bool_slot.t_write(false)?;
510 assert!(!bool_slot.t_read()?);
511 bool_slot.t_delete()?;
512 assert!(!bool_slot.t_read()?);
513
514 Ok(())
515 })
516 }
517
518 #[test]
519 fn test_transient_persistence_isolation() -> eyre::Result<()> {
520 let (mut storage, address) = setup_storage();
521 let slot_num = U256::random();
522 let t_value = U256::random();
523 let s_value = U256::random();
524
525 StorageCtx::enter(&mut storage, || -> eyre::Result<()> {
526 let mut slot = Slot::<U256>::new(slot_num, address);
527
528 slot.write(s_value)?;
530 slot.t_write(t_value)?;
531 assert_eq!(slot.read()?, s_value);
532 assert_eq!(slot.t_read()?, t_value);
533
534 slot.t_delete()?;
536 assert_eq!(slot.read()?, s_value);
537 assert_eq!(slot.t_read()?, U256::ZERO);
538
539 slot.t_write(t_value)?;
541 assert_eq!(slot.t_read()?, t_value);
542
543 Ok(())
544 })?;
545
546 storage.clear_transient();
548
549 StorageCtx::enter(&mut storage, || {
551 let slot = Slot::<U256>::new(slot_num, address);
552 assert_eq!(slot.read()?, s_value);
553 assert_eq!(slot.t_read()?, U256::ZERO);
554 Ok(())
555 })
556 }
557}