use core::marker::PhantomData;
use alloy_primitives::{Address, BlockHash, BlockNumber, FixedBytes, Signed, Uint, B256, U256};
use alloy_sol_types::sol_data::{ByteCount, IntBitCount, SupportedFixedBytes, SupportedInt};
pub use array::StorageArray;
pub use bytes::{StorageBytes, StorageString};
use cfg_if::cfg_if;
pub use map::{StorageKey, StorageMap};
use stylus_core::*;
pub use traits::{
Erase, GlobalStorage, SimpleStorageType, StorageGuard, StorageGuardMut, StorageType,
};
pub use vec::StorageVec;
use crate::host::VM;
mod array;
mod bytes;
mod map;
mod traits;
mod vec;
pub(crate) type Storage = StorageCache;
pub struct StorageCache;
impl GlobalStorage for StorageCache {
fn get_word(vm: VM, key: U256) -> B256 {
cfg_if! {
if #[cfg(not(feature = "stylus-test"))] {
vm.storage_load_bytes32(key)
} else {
vm.host.storage_load_bytes32(key)
}
}
}
unsafe fn set_word(vm: VM, key: U256, value: B256) {
cfg_if! {
if #[cfg(not(feature = "stylus-test"))] {
vm.storage_cache_bytes32(key, value)
} else {
vm.host.storage_cache_bytes32(key, value)
}
}
}
}
macro_rules! alias_ints {
($($name:ident, $signed_name:ident, $bits:expr, $limbs:expr;)*) => {
$(
#[doc = concat!("Accessor for a storage-backed [`alloy_primitives::aliases::U", stringify!($bits), "`].")]
pub type $name = StorageUint<$bits, $limbs>;
#[doc = concat!("Accessor for a storage-backed [`alloy_primitives::aliases::I", stringify!($bits), "`].")]
pub type $signed_name = StorageSigned<$bits, $limbs>;
)*
};
}
macro_rules! alias_bytes {
($($name:ident, $bits:expr, $bytes:expr;)*) => {
$(
#[doc = concat!("Accessor for a storage-backed [`alloy_primitives::aliases::B", stringify!($bits), "`].")]
pub type $name = StorageFixedBytes<$bytes>;
)*
};
}
alias_ints! {
StorageU8, StorageI8, 8, 1;
StorageU16, StorageI16, 16, 1;
StorageU32, StorageI32, 32, 1;
StorageU64, StorageI64, 64, 1;
StorageU96, StorageI96, 96, 2;
StorageU128, StorageI128, 128, 2;
StorageU160, StorageI160, 160, 3;
StorageU192, StorageI192, 192, 3;
StorageU256, StorageI256, 256, 4;
}
alias_bytes! {
StorageB8, 8, 1;
StorageB16, 16, 2;
StorageB32, 32, 4;
StorageB64, 64, 8;
StorageB96, 96, 12;
StorageB128, 128, 16;
StorageB160, 160, 20;
StorageB192, 192, 24;
StorageB224, 224, 28;
StorageB256, 256, 32;
}
#[derive(Debug)]
pub struct StorageUint<const B: usize, const L: usize>
where
IntBitCount<B>: SupportedInt,
{
slot: U256,
offset: u8,
__stylus_host: VM,
}
impl<const B: usize, const L: usize> HostAccess for StorageUint<B, L>
where
IntBitCount<B>: SupportedInt,
{
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
#[cfg(feature = "stylus-test")]
impl<const B: usize, const L: usize, T> From<&T> for StorageUint<B, L>
where
IntBitCount<B>: SupportedInt,
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
macro_rules! gen_int_wrap_ops {
($( $(#[$docs:meta])* $fn:ident => $op:ident ),* $(,)?) => {
$(
$(#[$docs])*
#[inline]
pub fn $fn(&mut self, v: Uint<B, L>) -> Uint<B, L> {
let x = self.get().$op(v);
self.set(x);
x
}
)*
};
}
macro_rules! gen_int_checked_ops {
($( $(#[$docs:meta])* $fn:ident => $op:ident ),* $(,)?) => {
$(
$(#[$docs])*
#[inline]
pub fn $fn(&mut self, v: Uint<B, L>) -> Option<Uint<B, L>> {
let r = self.get().$op(v);
if let Some(x) = r { self.set(x); }
r
}
)*
};
}
impl<const B: usize, const L: usize> StorageUint<B, L>
where
IntBitCount<B>: SupportedInt,
{
pub fn get(&self) -> Uint<B, L> {
unsafe { Storage::get_uint(self.__stylus_host.clone(), self.slot, self.offset.into()) }
}
pub fn set(&mut self, value: Uint<B, L>) {
unsafe {
Storage::set_uint(
self.__stylus_host.clone(),
self.slot,
self.offset.into(),
value,
)
};
}
gen_int_wrap_ops! {
update_wrap_add => wrapping_add,
update_wrap_sub => wrapping_sub,
update_wrap_div => wrapping_div,
update_wrap_mul => wrapping_mul,
update_wrap_rem => wrapping_rem
}
gen_int_checked_ops! {
update_check_add => checked_add,
update_check_sub => checked_sub,
update_check_div => checked_div,
update_check_mul => checked_mul,
update_check_rem => checked_rem
}
}
impl<const B: usize, const L: usize> StorageType for StorageUint<B, L>
where
IntBitCount<B>: SupportedInt,
{
type Wraps<'a> = Uint<B, L>;
type WrapsMut<'a> = StorageGuardMut<'a, Self>;
const SLOT_BYTES: usize = (B / 8);
unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
debug_assert!(B <= 256);
Self {
slot,
offset,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
self.get()
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
impl<'a, const B: usize, const L: usize> SimpleStorageType<'a> for StorageUint<B, L>
where
IntBitCount<B>: SupportedInt,
{
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
self.set(value);
}
}
impl<const B: usize, const L: usize> Erase for StorageUint<B, L>
where
IntBitCount<B>: SupportedInt,
{
fn erase(&mut self) {
self.set(Self::Wraps::ZERO);
}
}
impl<const B: usize, const L: usize> From<StorageUint<B, L>> for Uint<B, L>
where
IntBitCount<B>: SupportedInt,
{
fn from(value: StorageUint<B, L>) -> Self {
value.get()
}
}
#[derive(Debug)]
pub struct StorageSigned<const B: usize, const L: usize>
where
IntBitCount<B>: SupportedInt,
{
slot: U256,
offset: u8,
__stylus_host: VM,
}
impl<const B: usize, const L: usize> HostAccess for StorageSigned<B, L>
where
IntBitCount<B>: SupportedInt,
{
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
#[cfg(feature = "stylus-test")]
impl<const B: usize, const L: usize, T> From<&T> for StorageSigned<B, L>
where
IntBitCount<B>: SupportedInt,
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
impl<const B: usize, const L: usize> StorageSigned<B, L>
where
IntBitCount<B>: SupportedInt,
{
pub fn get(&self) -> Signed<B, L> {
unsafe { Storage::get_signed(self.__stylus_host.clone(), self.slot, self.offset.into()) }
}
pub fn set(&mut self, value: Signed<B, L>) {
unsafe {
Storage::set_signed(
self.__stylus_host.clone(),
self.slot,
self.offset.into(),
value,
)
};
}
}
impl<const B: usize, const L: usize> StorageType for StorageSigned<B, L>
where
IntBitCount<B>: SupportedInt,
{
type Wraps<'a> = Signed<B, L>;
type WrapsMut<'a> = StorageGuardMut<'a, Self>;
const SLOT_BYTES: usize = (B / 8);
unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
Self {
slot,
offset,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
self.get()
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
impl<'a, const B: usize, const L: usize> SimpleStorageType<'a> for StorageSigned<B, L>
where
IntBitCount<B>: SupportedInt,
{
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
self.set(value);
}
}
impl<const B: usize, const L: usize> Erase for StorageSigned<B, L>
where
IntBitCount<B>: SupportedInt,
{
fn erase(&mut self) {
self.set(Self::Wraps::ZERO)
}
}
impl<const B: usize, const L: usize> From<StorageSigned<B, L>> for Signed<B, L>
where
IntBitCount<B>: SupportedInt,
{
fn from(value: StorageSigned<B, L>) -> Self {
value.get()
}
}
#[derive(Debug)]
pub struct StorageFixedBytes<const N: usize> {
slot: U256,
offset: u8,
__stylus_host: VM,
}
impl<const N: usize> HostAccess for StorageFixedBytes<N> {
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
impl<const N: usize> StorageFixedBytes<N> {
pub fn get(&self) -> FixedBytes<N> {
unsafe { Storage::get(self.__stylus_host.clone(), self.slot, self.offset.into()) }
}
pub fn set(&mut self, value: FixedBytes<N>) {
unsafe {
Storage::set(
self.__stylus_host.clone(),
self.slot,
self.offset.into(),
value,
)
}
}
}
impl<const N: usize> StorageType for StorageFixedBytes<N>
where
ByteCount<N>: SupportedFixedBytes,
{
type Wraps<'a> = FixedBytes<N>;
type WrapsMut<'a> = StorageGuardMut<'a, Self>;
const SLOT_BYTES: usize = N;
unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
Self {
slot,
offset,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
self.get()
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
#[cfg(feature = "stylus-test")]
impl<const N: usize, T> From<&T> for StorageFixedBytes<N>
where
ByteCount<N>: SupportedFixedBytes,
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
impl<'a, const N: usize> SimpleStorageType<'a> for StorageFixedBytes<N>
where
ByteCount<N>: SupportedFixedBytes,
{
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
self.set(value);
}
}
impl<const N: usize> Erase for StorageFixedBytes<N>
where
ByteCount<N>: SupportedFixedBytes,
{
fn erase(&mut self) {
self.set(Self::Wraps::ZERO)
}
}
impl<const N: usize> From<StorageFixedBytes<N>> for FixedBytes<N> {
fn from(value: StorageFixedBytes<N>) -> Self {
value.get()
}
}
#[derive(Debug)]
pub struct StorageBool {
slot: U256,
offset: u8,
__stylus_host: VM,
}
impl HostAccess for StorageBool {
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
#[cfg(feature = "stylus-test")]
impl<T> From<&T> for StorageBool
where
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
impl StorageBool {
pub fn get(&self) -> bool {
let data =
unsafe { Storage::get_byte(self.__stylus_host.clone(), self.slot, self.offset.into()) };
data != 0
}
pub fn set(&mut self, value: bool) {
unsafe {
Storage::set_byte(
self.__stylus_host.clone(),
self.slot,
self.offset.into(),
value as u8,
)
}
}
}
impl StorageType for StorageBool {
type Wraps<'a> = bool;
type WrapsMut<'a> = StorageGuardMut<'a, Self>;
const SLOT_BYTES: usize = 1;
unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
Self {
slot,
offset,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
self.get()
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
impl<'a> SimpleStorageType<'a> for StorageBool {
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
self.set(value);
}
}
impl Erase for StorageBool {
fn erase(&mut self) {
self.set(false);
}
}
impl From<StorageBool> for bool {
fn from(value: StorageBool) -> Self {
value.get()
}
}
#[derive(Debug)]
pub struct StorageAddress {
slot: U256,
offset: u8,
__stylus_host: VM,
}
impl HostAccess for StorageAddress {
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
#[cfg(feature = "stylus-test")]
impl<T> From<&T> for StorageAddress
where
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
impl StorageAddress {
pub fn get(&self) -> Address {
unsafe {
Storage::get::<20>(self.__stylus_host.clone(), self.slot, self.offset.into()).into()
}
}
pub fn set(&mut self, value: Address) {
unsafe {
Storage::set::<20>(
self.__stylus_host.clone(),
self.slot,
self.offset.into(),
value.into(),
)
}
}
}
impl StorageType for StorageAddress {
type Wraps<'a> = Address;
type WrapsMut<'a> = StorageGuardMut<'a, Self>;
const SLOT_BYTES: usize = 20;
unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
Self {
slot,
offset,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
self.get()
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
impl<'a> SimpleStorageType<'a> for StorageAddress {
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
self.set(value);
}
}
impl Erase for StorageAddress {
fn erase(&mut self) {
self.set(Self::Wraps::ZERO);
}
}
impl From<StorageAddress> for Address {
fn from(value: StorageAddress) -> Self {
value.get()
}
}
#[derive(Debug)]
pub struct StorageBlockNumber {
slot: U256,
offset: u8,
__stylus_host: VM,
}
impl HostAccess for StorageBlockNumber {
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
#[cfg(feature = "stylus-test")]
impl<T> From<&T> for StorageBlockNumber
where
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
impl StorageBlockNumber {
pub fn get(&self) -> BlockNumber {
let data =
unsafe { Storage::get::<8>(self.__stylus_host.clone(), self.slot, self.offset.into()) };
u64::from_be_bytes(data.0)
}
pub fn set(&mut self, value: BlockNumber) {
let value = FixedBytes::from(value.to_be_bytes());
unsafe {
Storage::set::<8>(
self.__stylus_host.clone(),
self.slot,
self.offset.into(),
value,
)
};
}
}
impl StorageType for StorageBlockNumber {
type Wraps<'a> = BlockNumber;
type WrapsMut<'a> = StorageGuardMut<'a, Self>;
const SLOT_BYTES: usize = 8;
unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
Self {
slot,
offset,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
self.get()
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
impl<'a> SimpleStorageType<'a> for StorageBlockNumber {
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
self.set(value);
}
}
impl Erase for StorageBlockNumber {
fn erase(&mut self) {
self.set(0);
}
}
impl From<StorageBlockNumber> for BlockNumber {
fn from(value: StorageBlockNumber) -> Self {
value.get()
}
}
#[derive(Clone, Debug)]
pub struct StorageBlockHash {
slot: U256,
__stylus_host: VM,
}
impl HostAccess for StorageBlockHash {
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
#[cfg(feature = "stylus-test")]
impl<T> From<&T> for StorageBlockHash
where
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
impl StorageBlockHash {
pub fn get(&self) -> BlockHash {
Storage::get_word(self.__stylus_host.clone(), self.slot)
}
pub fn set(&mut self, value: BlockHash) {
unsafe { Storage::set_word(self.__stylus_host.clone(), self.slot, value) }
}
}
impl StorageType for StorageBlockHash {
type Wraps<'a> = BlockHash;
type WrapsMut<'a> = StorageGuardMut<'a, Self>;
unsafe fn new(slot: U256, _offset: u8, host: VM) -> Self {
Self {
slot,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
self.get()
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
impl<'a> SimpleStorageType<'a> for StorageBlockHash {
fn set_by_wrapped(&mut self, value: Self::Wraps<'a>) {
self.set(value);
}
}
impl Erase for StorageBlockHash {
fn erase(&mut self) {
self.set(Self::Wraps::ZERO);
}
}
impl From<StorageBlockHash> for BlockHash {
fn from(value: StorageBlockHash) -> Self {
value.get()
}
}
impl<T> StorageType for PhantomData<T> {
type Wraps<'a>
= Self
where
Self: 'a;
type WrapsMut<'a>
= Self
where
Self: 'a;
const REQUIRED_SLOTS: usize = 0;
const SLOT_BYTES: usize = 0;
unsafe fn new(_slot: U256, _offset: u8, _host: VM) -> Self {
Self
}
fn load<'s>(self) -> Self::Wraps<'s>
where
Self: 's,
{
self
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s>
where
Self: 's,
{
self
}
}