1use 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
46pub struct StorageCache;
50
51impl GlobalStorage for StorageCache {
52 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 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 #[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 #[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#[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 pub fn get(&self) -> Uint<B, L> {
204 unsafe { Storage::get_uint(self.__stylus_host.clone(), self.slot, self.offset.into()) }
205 }
206
207 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#[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 pub fn get(&self) -> Signed<B, L> {
329 unsafe { Storage::get_signed(self.__stylus_host.clone(), self.slot, self.offset.into()) }
330 }
331
332 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#[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 pub fn get(&self) -> FixedBytes<N> {
421 unsafe { Storage::get(self.__stylus_host.clone(), self.slot, self.offset.into()) }
422 }
423
424 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#[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 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 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#[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 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 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#[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 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 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#[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 pub fn get(&self) -> BlockHash {
853 Storage::get_word(self.__stylus_host.clone(), self.slot)
854 }
855
856 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
900impl<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}