1use 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
48pub struct StorageCache;
52
53impl GlobalStorage for StorageCache {
54 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 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#[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 pub fn get(&self) -> Uint<B, L> {
207 unsafe { Storage::get_uint(self.__stylus_host.clone(), self.slot, self.offset.into()) }
208 }
209
210 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 update_wrap_add => wrapping_add,
226
227 update_wrap_sub => wrapping_sub,
230
231 update_wrap_div => wrapping_div,
234
235 update_wrap_mul => wrapping_mul,
238
239 update_wrap_rem => wrapping_rem
242 }
243
244 gen_int_checked_ops! {
245 update_check_add => checked_add,
248
249 update_check_sub => checked_sub,
252
253 update_check_div => checked_div,
256
257 update_check_mul => checked_mul,
260
261 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#[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 pub fn get(&self) -> Signed<B, L> {
373 unsafe { Storage::get_signed(self.__stylus_host.clone(), self.slot, self.offset.into()) }
374 }
375
376 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#[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 pub fn get(&self) -> FixedBytes<N> {
462 unsafe { Storage::get(self.__stylus_host.clone(), self.slot, self.offset.into()) }
463 }
464
465 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#[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 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 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#[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 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 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#[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 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 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#[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 pub fn get(&self) -> BlockHash {
882 Storage::get_word(self.__stylus_host.clone(), self.slot)
883 }
884
885 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
929impl<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}