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