Skip to main content

stylus_sdk/storage/
mod.rs

1// Copyright 2023-2024, Offchain Labs, Inc.
2// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md
3
4//! Solidity compatible storage types and persistent storage access.
5//!
6//! The Stylus node software is composed of two, fully-composable virtual machines.
7//! - The Stylus VM, which compiles WASM contracts built with SDKs like this one.
8//! - The Ethereum Virtual Machine, which interprets EVM bytecode from languages like Solidity and
9//!   Vyper.
10//!
11//! Though these two VMs differ in execution, they are backed by the same EVM State Trie.
12//! This means that Stylus contracts have access to the same, key-value based persistent storage
13//! familiar to Solidity devs.
14//!
15//! Because this resource is foreign to Rust, this module provides standard types and traits for
16//! accessing state when writing programs. To protect the user, the Stylus SDK safeguards storage
17//! access by leveraging Rust's borrow checker. It should never be possible to alias Storage without
18//! `unsafe` Rust, eliminating entire classes of errors at compile time.
19//!
20//! For a walkthrough of this module's features, please see [The Feature Overview][overview].
21//!
22//! [overview]: https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#storage
23
24use core::marker::PhantomData;
25
26use alloy_primitives::{Address, BlockHash, BlockNumber, FixedBytes, Signed, Uint, B256, U256};
27use alloy_sol_types::sol_data::{ByteCount, IntBitCount, SupportedFixedBytes, SupportedInt};
28pub use array::StorageArray;
29pub use bytes::{StorageBytes, StorageString};
30use cfg_if::cfg_if;
31pub use map::{StorageKey, StorageMap};
32use stylus_core::*;
33pub use traits::{
34    Erase, GlobalStorage, SimpleStorageType, StorageGuard, StorageGuardMut, StorageType,
35};
36pub use vec::StorageVec;
37
38use crate::host::VM;
39
40mod array;
41mod bytes;
42mod map;
43mod traits;
44mod vec;
45
46pub(crate) type Storage = StorageCache;
47
48/// Global accessor to persistent storage that relies on VM-level caching.
49///
50/// [`LocalStorageCache`]: super::LocalStorageCache
51pub struct StorageCache;
52
53impl GlobalStorage for StorageCache {
54    /// Retrieves a 32-byte EVM word from persistent storage.
55    fn get_word(vm: VM, key: U256) -> B256 {
56        cfg_if! {
57            if #[cfg(not(feature = "stylus-test"))] {
58                vm.storage_load_bytes32(key)
59            } else {
60                vm.host.storage_load_bytes32(key)
61            }
62        }
63    }
64
65    /// Stores a 32-byte EVM word to persistent storage.
66    ///
67    /// # Safety
68    ///
69    /// May alias storage.
70    unsafe fn set_word(vm: VM, key: U256, value: B256) {
71        cfg_if! {
72            if #[cfg(not(feature = "stylus-test"))] {
73                vm.storage_cache_bytes32(key, value)
74            } else {
75                vm.host.storage_cache_bytes32(key, value)
76            }
77        }
78    }
79}
80
81macro_rules! alias_ints {
82    ($($name:ident, $signed_name:ident, $bits:expr, $limbs:expr;)*) => {
83        $(
84            #[doc = concat!("Accessor for a storage-backed [`alloy_primitives::aliases::U", stringify!($bits), "`].")]
85            pub type $name = StorageUint<$bits, $limbs>;
86
87            #[doc = concat!("Accessor for a storage-backed [`alloy_primitives::aliases::I", stringify!($bits), "`].")]
88            pub type $signed_name = StorageSigned<$bits, $limbs>;
89        )*
90    };
91}
92
93macro_rules! alias_bytes {
94    ($($name:ident, $bits:expr, $bytes:expr;)*) => {
95        $(
96            #[doc = concat!("Accessor for a storage-backed [`alloy_primitives::aliases::B", stringify!($bits), "`].")]
97            pub type $name = StorageFixedBytes<$bytes>;
98        )*
99    };
100}
101
102alias_ints! {
103    StorageU8, StorageI8, 8, 1;
104    StorageU16, StorageI16, 16, 1;
105    StorageU32, StorageI32, 32, 1;
106    StorageU64, StorageI64, 64, 1;
107    StorageU96, StorageI96, 96, 2;
108    StorageU128, StorageI128, 128, 2;
109    StorageU160, StorageI160, 160, 3;
110    StorageU192, StorageI192, 192, 3;
111    StorageU256, StorageI256, 256, 4;
112}
113
114alias_bytes! {
115    StorageB8, 8, 1;
116    StorageB16, 16, 2;
117    StorageB32, 32, 4;
118    StorageB64, 64, 8;
119    StorageB96, 96, 12;
120    StorageB128, 128, 16;
121    StorageB160, 160, 20;
122    StorageB192, 192, 24;
123    StorageB224, 224, 28;
124    StorageB256, 256, 32;
125}
126
127/// Accessor for a storage-backed [`alloy_primitives::Uint`].
128///
129/// Note: in the future `L` won't be needed.
130// TODO: drop L after SupportedInt provides LIMBS (waiting for clarity reasons)
131// https://github.com/rust-lang/rust/issues/76560
132#[derive(Debug)]
133pub struct StorageUint<const B: usize, const L: usize>
134where
135    IntBitCount<B>: SupportedInt,
136{
137    slot: U256,
138    offset: u8,
139    __stylus_host: VM,
140}
141
142impl<const B: usize, const L: usize> HostAccess for StorageUint<B, L>
143where
144    IntBitCount<B>: SupportedInt,
145{
146    type Host = VM;
147
148    #[inline]
149    fn vm(&self) -> &Self::Host {
150        &self.__stylus_host
151    }
152}
153
154#[cfg(feature = "stylus-test")]
155impl<const B: usize, const L: usize, T> From<&T> for StorageUint<B, L>
156where
157    IntBitCount<B>: SupportedInt,
158    T: stylus_core::Host + Clone + 'static,
159{
160    fn from(host: &T) -> Self {
161        unsafe {
162            Self::new(
163                U256::ZERO,
164                0,
165                crate::host::VM {
166                    host: alloc::boxed::Box::new(host.clone()),
167                },
168            )
169        }
170    }
171}
172
173macro_rules! gen_int_wrap_ops {
174    ($( $(#[$docs:meta])* $fn:ident => $op:ident ),* $(,)?) => {
175        $(
176            $(#[$docs])*
177            #[inline]
178            pub fn $fn(&mut self, v: Uint<B, L>) -> Uint<B, L> {
179                let x = self.get().$op(v);
180                self.set(x);
181                x
182            }
183        )*
184    };
185}
186
187macro_rules! gen_int_checked_ops {
188    ($( $(#[$docs:meta])* $fn:ident => $op:ident ),* $(,)?) => {
189        $(
190            $(#[$docs])*
191            #[inline]
192            pub fn $fn(&mut self, v: Uint<B, L>) -> Option<Uint<B, L>> {
193                let r = self.get().$op(v);
194                if let Some(x) = r { self.set(x); }
195                r
196            }
197        )*
198    };
199}
200
201impl<const B: usize, const L: usize> StorageUint<B, L>
202where
203    IntBitCount<B>: SupportedInt,
204{
205    /// Gets the underlying [`alloy_primitives::Uint`] in persistent storage.
206    pub fn get(&self) -> Uint<B, L> {
207        unsafe { Storage::get_uint(self.__stylus_host.clone(), self.slot, self.offset.into()) }
208    }
209
210    /// Sets the underlying [`alloy_primitives::Uint`] in persistent storage.
211    pub fn set(&mut self, value: Uint<B, L>) {
212        unsafe {
213            Storage::set_uint(
214                self.__stylus_host.clone(),
215                self.slot,
216                self.offset.into(),
217                value,
218            )
219        };
220    }
221
222    gen_int_wrap_ops! {
223        /// Add to the underlying value, wrapping around if overflow.
224        /// Returns the new value.
225        update_wrap_add => wrapping_add,
226
227        /// Subtract the underlying value, wrapping around if overflow.
228        /// Returns the new value.
229        update_wrap_sub => wrapping_sub,
230
231        /// Divide the underlying value, wrapping around if overflow.
232        /// Returns the new value.
233        update_wrap_div => wrapping_div,
234
235        /// Multiply the underlying value, wrapping around if overflow.
236        /// Returns the new value.
237        update_wrap_mul => wrapping_mul,
238
239        /// Set the modulo of the value, panicking if rhs is 0.
240        /// Returns the new value.
241        update_wrap_rem => wrapping_rem
242    }
243
244    gen_int_checked_ops! {
245        /// Add to the underlying value, only setting if the value does not
246        /// overflow. Returns the value if set.
247        update_check_add => checked_add,
248
249        /// Subtract from the underlying value, only setting if the value does not
250        /// overflow. Returns the value if set.
251        update_check_sub => checked_sub,
252
253        /// Divide the underlying value, only setting if the value does not
254        /// overflow. Returns the value if set.
255        update_check_div => checked_div,
256
257        /// Divide the underlying value, only setting if the value does not
258        /// overflow. Returns the value if set.
259        update_check_mul => checked_mul,
260
261        /// Set the modulo of the value, returning None if overflow or rhs
262        /// is 0, only setting if the value would be Some. Returns the result.
263        update_check_rem => checked_rem
264    }
265}
266
267impl<const B: usize, const L: usize> StorageType for StorageUint<B, L>
268where
269    IntBitCount<B>: SupportedInt,
270{
271    type Wraps<'a> = Uint<B, L>;
272    type WrapsMut<'a> = StorageGuardMut<'a, Self>;
273
274    const SLOT_BYTES: usize = (B / 8);
275
276    unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
277        debug_assert!(B <= 256);
278        Self {
279            slot,
280            offset,
281            __stylus_host: host,
282        }
283    }
284
285    fn load<'s>(self) -> Self::Wraps<'s> {
286        self.get()
287    }
288
289    fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
290        StorageGuardMut::new(self)
291    }
292}
293
294impl<'a, const B: usize, const L: usize> SimpleStorageType<'a> for StorageUint<B, L>
295where
296    IntBitCount<B>: SupportedInt,
297{
298    fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
299        self.set(value);
300    }
301}
302
303impl<const B: usize, const L: usize> Erase for StorageUint<B, L>
304where
305    IntBitCount<B>: SupportedInt,
306{
307    fn erase(&mut self) {
308        self.set(Self::Wraps::ZERO);
309    }
310}
311
312impl<const B: usize, const L: usize> From<StorageUint<B, L>> for Uint<B, L>
313where
314    IntBitCount<B>: SupportedInt,
315{
316    fn from(value: StorageUint<B, L>) -> Self {
317        value.get()
318    }
319}
320
321/// Accessor for a storage-backed [`Signed`].
322///
323/// Note: in the future `L` won't be needed.
324// TODO: drop L after SupportedInt provides LIMBS (waiting for clarity reasons)
325// https://github.com/rust-lang/rust/issues/76560
326#[derive(Debug)]
327pub struct StorageSigned<const B: usize, const L: usize>
328where
329    IntBitCount<B>: SupportedInt,
330{
331    slot: U256,
332    offset: u8,
333    __stylus_host: VM,
334}
335
336impl<const B: usize, const L: usize> HostAccess for StorageSigned<B, L>
337where
338    IntBitCount<B>: SupportedInt,
339{
340    type Host = VM;
341
342    #[inline]
343    fn vm(&self) -> &Self::Host {
344        &self.__stylus_host
345    }
346}
347
348#[cfg(feature = "stylus-test")]
349impl<const B: usize, const L: usize, T> From<&T> for StorageSigned<B, L>
350where
351    IntBitCount<B>: SupportedInt,
352    T: stylus_core::Host + Clone + 'static,
353{
354    fn from(host: &T) -> Self {
355        unsafe {
356            Self::new(
357                U256::ZERO,
358                0,
359                crate::host::VM {
360                    host: alloc::boxed::Box::new(host.clone()),
361                },
362            )
363        }
364    }
365}
366
367impl<const B: usize, const L: usize> StorageSigned<B, L>
368where
369    IntBitCount<B>: SupportedInt,
370{
371    /// Gets the underlying [`Signed`] in persistent storage.
372    pub fn get(&self) -> Signed<B, L> {
373        unsafe { Storage::get_signed(self.__stylus_host.clone(), self.slot, self.offset.into()) }
374    }
375
376    /// Gets the underlying [`Signed`] in persistent storage.
377    pub fn set(&mut self, value: Signed<B, L>) {
378        unsafe {
379            Storage::set_signed(
380                self.__stylus_host.clone(),
381                self.slot,
382                self.offset.into(),
383                value,
384            )
385        };
386    }
387}
388
389impl<const B: usize, const L: usize> StorageType for StorageSigned<B, L>
390where
391    IntBitCount<B>: SupportedInt,
392{
393    type Wraps<'a> = Signed<B, L>;
394    type WrapsMut<'a> = StorageGuardMut<'a, Self>;
395
396    const SLOT_BYTES: usize = (B / 8);
397
398    unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
399        Self {
400            slot,
401            offset,
402            __stylus_host: host,
403        }
404    }
405
406    fn load<'s>(self) -> Self::Wraps<'s> {
407        self.get()
408    }
409
410    fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
411        StorageGuardMut::new(self)
412    }
413}
414
415impl<'a, const B: usize, const L: usize> SimpleStorageType<'a> for StorageSigned<B, L>
416where
417    IntBitCount<B>: SupportedInt,
418{
419    fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
420        self.set(value);
421    }
422}
423
424impl<const B: usize, const L: usize> Erase for StorageSigned<B, L>
425where
426    IntBitCount<B>: SupportedInt,
427{
428    fn erase(&mut self) {
429        self.set(Self::Wraps::ZERO)
430    }
431}
432
433impl<const B: usize, const L: usize> From<StorageSigned<B, L>> for Signed<B, L>
434where
435    IntBitCount<B>: SupportedInt,
436{
437    fn from(value: StorageSigned<B, L>) -> Self {
438        value.get()
439    }
440}
441
442/// Accessor for a storage-backed [`FixedBytes`].
443#[derive(Debug)]
444pub struct StorageFixedBytes<const N: usize> {
445    slot: U256,
446    offset: u8,
447    __stylus_host: VM,
448}
449
450impl<const N: usize> HostAccess for StorageFixedBytes<N> {
451    type Host = VM;
452
453    #[inline]
454    fn vm(&self) -> &Self::Host {
455        &self.__stylus_host
456    }
457}
458
459impl<const N: usize> StorageFixedBytes<N> {
460    /// Gets the underlying [`FixedBytes`] in persistent storage.
461    pub fn get(&self) -> FixedBytes<N> {
462        unsafe { Storage::get(self.__stylus_host.clone(), self.slot, self.offset.into()) }
463    }
464
465    /// Gets the underlying [`FixedBytes`] in persistent storage.
466    pub fn set(&mut self, value: FixedBytes<N>) {
467        unsafe {
468            Storage::set(
469                self.__stylus_host.clone(),
470                self.slot,
471                self.offset.into(),
472                value,
473            )
474        }
475    }
476}
477
478impl<const N: usize> StorageType for StorageFixedBytes<N>
479where
480    ByteCount<N>: SupportedFixedBytes,
481{
482    type Wraps<'a> = FixedBytes<N>;
483    type WrapsMut<'a> = StorageGuardMut<'a, Self>;
484
485    const SLOT_BYTES: usize = N;
486
487    unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
488        Self {
489            slot,
490            offset,
491            __stylus_host: host,
492        }
493    }
494
495    fn load<'s>(self) -> Self::Wraps<'s> {
496        self.get()
497    }
498
499    fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
500        StorageGuardMut::new(self)
501    }
502}
503
504#[cfg(feature = "stylus-test")]
505impl<const N: usize, T> From<&T> for StorageFixedBytes<N>
506where
507    ByteCount<N>: SupportedFixedBytes,
508    T: stylus_core::Host + Clone + 'static,
509{
510    fn from(host: &T) -> Self {
511        unsafe {
512            Self::new(
513                U256::ZERO,
514                0,
515                crate::host::VM {
516                    host: alloc::boxed::Box::new(host.clone()),
517                },
518            )
519        }
520    }
521}
522
523impl<'a, const N: usize> SimpleStorageType<'a> for StorageFixedBytes<N>
524where
525    ByteCount<N>: SupportedFixedBytes,
526{
527    fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
528        self.set(value);
529    }
530}
531
532impl<const N: usize> Erase for StorageFixedBytes<N>
533where
534    ByteCount<N>: SupportedFixedBytes,
535{
536    fn erase(&mut self) {
537        self.set(Self::Wraps::ZERO)
538    }
539}
540
541impl<const N: usize> From<StorageFixedBytes<N>> for FixedBytes<N> {
542    fn from(value: StorageFixedBytes<N>) -> Self {
543        value.get()
544    }
545}
546
547/// Accessor for a storage-backed [`bool`].
548#[derive(Debug)]
549pub struct StorageBool {
550    slot: U256,
551    offset: u8,
552    __stylus_host: VM,
553}
554
555impl HostAccess for StorageBool {
556    type Host = VM;
557
558    #[inline]
559    fn vm(&self) -> &Self::Host {
560        &self.__stylus_host
561    }
562}
563
564#[cfg(feature = "stylus-test")]
565impl<T> From<&T> for StorageBool
566where
567    T: stylus_core::Host + Clone + 'static,
568{
569    fn from(host: &T) -> Self {
570        unsafe {
571            Self::new(
572                U256::ZERO,
573                0,
574                crate::host::VM {
575                    host: alloc::boxed::Box::new(host.clone()),
576                },
577            )
578        }
579    }
580}
581
582impl StorageBool {
583    /// Gets the underlying [`bool`] in persistent storage.
584    pub fn get(&self) -> bool {
585        let data =
586            unsafe { Storage::get_byte(self.__stylus_host.clone(), self.slot, self.offset.into()) };
587        data != 0
588    }
589
590    /// Gets the underlying [`bool`] in persistent storage.
591    pub fn set(&mut self, value: bool) {
592        unsafe {
593            Storage::set_byte(
594                self.__stylus_host.clone(),
595                self.slot,
596                self.offset.into(),
597                value as u8,
598            )
599        }
600    }
601}
602
603impl StorageType for StorageBool {
604    type Wraps<'a> = bool;
605    type WrapsMut<'a> = StorageGuardMut<'a, Self>;
606
607    const SLOT_BYTES: usize = 1;
608
609    unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
610        Self {
611            slot,
612            offset,
613            __stylus_host: host,
614        }
615    }
616
617    fn load<'s>(self) -> Self::Wraps<'s> {
618        self.get()
619    }
620
621    fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
622        StorageGuardMut::new(self)
623    }
624}
625
626impl<'a> SimpleStorageType<'a> for StorageBool {
627    fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
628        self.set(value);
629    }
630}
631
632impl Erase for StorageBool {
633    fn erase(&mut self) {
634        self.set(false);
635    }
636}
637
638impl From<StorageBool> for bool {
639    fn from(value: StorageBool) -> Self {
640        value.get()
641    }
642}
643
644/// Accessor for a storage-backed [`Address`].
645#[derive(Debug)]
646pub struct StorageAddress {
647    slot: U256,
648    offset: u8,
649    __stylus_host: VM,
650}
651
652impl HostAccess for StorageAddress {
653    type Host = VM;
654
655    #[inline]
656    fn vm(&self) -> &Self::Host {
657        &self.__stylus_host
658    }
659}
660
661#[cfg(feature = "stylus-test")]
662impl<T> From<&T> for StorageAddress
663where
664    T: stylus_core::Host + Clone + 'static,
665{
666    fn from(host: &T) -> Self {
667        unsafe {
668            Self::new(
669                U256::ZERO,
670                0,
671                crate::host::VM {
672                    host: alloc::boxed::Box::new(host.clone()),
673                },
674            )
675        }
676    }
677}
678
679impl StorageAddress {
680    /// Gets the underlying [`Address`] in persistent storage.
681    pub fn get(&self) -> Address {
682        unsafe {
683            Storage::get::<20>(self.__stylus_host.clone(), self.slot, self.offset.into()).into()
684        }
685    }
686
687    /// Gets the underlying [`Address`] in persistent storage.
688    pub fn set(&mut self, value: Address) {
689        unsafe {
690            Storage::set::<20>(
691                self.__stylus_host.clone(),
692                self.slot,
693                self.offset.into(),
694                value.into(),
695            )
696        }
697    }
698}
699
700impl StorageType for StorageAddress {
701    type Wraps<'a> = Address;
702    type WrapsMut<'a> = StorageGuardMut<'a, Self>;
703
704    const SLOT_BYTES: usize = 20;
705
706    unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
707        Self {
708            slot,
709            offset,
710            __stylus_host: host,
711        }
712    }
713
714    fn load<'s>(self) -> Self::Wraps<'s> {
715        self.get()
716    }
717
718    fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
719        StorageGuardMut::new(self)
720    }
721}
722
723impl<'a> SimpleStorageType<'a> for StorageAddress {
724    fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
725        self.set(value);
726    }
727}
728
729impl Erase for StorageAddress {
730    fn erase(&mut self) {
731        self.set(Self::Wraps::ZERO);
732    }
733}
734
735impl From<StorageAddress> for Address {
736    fn from(value: StorageAddress) -> Self {
737        value.get()
738    }
739}
740
741/// Accessor for a storage-backed [`BlockNumber`].
742///
743/// This storage type allows convenient and type-safe storage of a
744/// [`BlockNumber`].
745#[derive(Debug)]
746pub struct StorageBlockNumber {
747    slot: U256,
748    offset: u8,
749    __stylus_host: VM,
750}
751
752impl HostAccess for StorageBlockNumber {
753    type Host = VM;
754
755    #[inline]
756    fn vm(&self) -> &Self::Host {
757        &self.__stylus_host
758    }
759}
760
761#[cfg(feature = "stylus-test")]
762impl<T> From<&T> for StorageBlockNumber
763where
764    T: stylus_core::Host + Clone + 'static,
765{
766    fn from(host: &T) -> Self {
767        unsafe {
768            Self::new(
769                U256::ZERO,
770                0,
771                crate::host::VM {
772                    host: alloc::boxed::Box::new(host.clone()),
773                },
774            )
775        }
776    }
777}
778
779impl StorageBlockNumber {
780    /// Gets the underlying [`BlockNumber`] in persistent storage.
781    pub fn get(&self) -> BlockNumber {
782        let data =
783            unsafe { Storage::get::<8>(self.__stylus_host.clone(), self.slot, self.offset.into()) };
784        u64::from_be_bytes(data.0)
785    }
786
787    /// Sets the underlying [`BlockNumber`] in persistent storage.
788    pub fn set(&mut self, value: BlockNumber) {
789        let value = FixedBytes::from(value.to_be_bytes());
790        unsafe {
791            Storage::set::<8>(
792                self.__stylus_host.clone(),
793                self.slot,
794                self.offset.into(),
795                value,
796            )
797        };
798    }
799}
800
801impl StorageType for StorageBlockNumber {
802    type Wraps<'a> = BlockNumber;
803    type WrapsMut<'a> = StorageGuardMut<'a, Self>;
804
805    const SLOT_BYTES: usize = 8;
806
807    unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
808        Self {
809            slot,
810            offset,
811            __stylus_host: host,
812        }
813    }
814
815    fn load<'s>(self) -> Self::Wraps<'s> {
816        self.get()
817    }
818
819    fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
820        StorageGuardMut::new(self)
821    }
822}
823
824impl<'a> SimpleStorageType<'a> for StorageBlockNumber {
825    fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
826        self.set(value);
827    }
828}
829
830impl Erase for StorageBlockNumber {
831    fn erase(&mut self) {
832        self.set(0);
833    }
834}
835
836impl From<StorageBlockNumber> for BlockNumber {
837    fn from(value: StorageBlockNumber) -> Self {
838        value.get()
839    }
840}
841
842/// Accessor for a storage-backed [`BlockHash`].
843///
844/// This storage type allows convenient and type-safe storage of a
845/// [`BlockHash`].
846#[derive(Clone, Debug)]
847pub struct StorageBlockHash {
848    slot: U256,
849    __stylus_host: VM,
850}
851
852impl HostAccess for StorageBlockHash {
853    type Host = VM;
854
855    #[inline]
856    fn vm(&self) -> &Self::Host {
857        &self.__stylus_host
858    }
859}
860
861#[cfg(feature = "stylus-test")]
862impl<T> From<&T> for StorageBlockHash
863where
864    T: stylus_core::Host + Clone + 'static,
865{
866    fn from(host: &T) -> Self {
867        unsafe {
868            Self::new(
869                U256::ZERO,
870                0,
871                crate::host::VM {
872                    host: alloc::boxed::Box::new(host.clone()),
873                },
874            )
875        }
876    }
877}
878
879impl StorageBlockHash {
880    /// Gets the underlying [`BlockHash`] in persistent storage.
881    pub fn get(&self) -> BlockHash {
882        Storage::get_word(self.__stylus_host.clone(), self.slot)
883    }
884
885    /// Sets the underlying [`BlockHash`] in persistent storage.
886    pub fn set(&mut self, value: BlockHash) {
887        unsafe { Storage::set_word(self.__stylus_host.clone(), self.slot, value) }
888    }
889}
890
891impl StorageType for StorageBlockHash {
892    type Wraps<'a> = BlockHash;
893    type WrapsMut<'a> = StorageGuardMut<'a, Self>;
894
895    unsafe fn new(slot: U256, _offset: u8, host: VM) -> Self {
896        Self {
897            slot,
898            __stylus_host: host,
899        }
900    }
901
902    fn load<'s>(self) -> Self::Wraps<'s> {
903        self.get()
904    }
905
906    fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
907        StorageGuardMut::new(self)
908    }
909}
910
911impl<'a> SimpleStorageType<'a> for StorageBlockHash {
912    fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
913        self.set(value);
914    }
915}
916
917impl Erase for StorageBlockHash {
918    fn erase(&mut self) {
919        self.set(Self::Wraps::ZERO);
920    }
921}
922
923impl From<StorageBlockHash> for BlockHash {
924    fn from(value: StorageBlockHash) -> Self {
925        value.get()
926    }
927}
928
929/// We implement `StorageType` for `PhantomData` so that storage types can be generic.
930impl<T> StorageType for PhantomData<T> {
931    type Wraps<'a>
932        = Self
933    where
934        Self: 'a;
935    type WrapsMut<'a>
936        = Self
937    where
938        Self: 'a;
939
940    const REQUIRED_SLOTS: usize = 0;
941    const SLOT_BYTES: usize = 0;
942
943    unsafe fn new(_slot: U256, _offset: u8, _host: VM) -> Self {
944        Self
945    }
946
947    fn load<'s>(self) -> Self::Wraps<'s>
948    where
949        Self: 's,
950    {
951        self
952    }
953
954    fn load_mut<'s>(self) -> Self::WrapsMut<'s>
955    where
956        Self: 's,
957    {
958        self
959    }
960}