Attribute Macro ink::storage_item

#[storage_item]
Expand description

Prepares the type to be fully compatible and usable with the storage. It implements all necessary traits and calculates the storage key for types. Packed types don’t have a storage key, but non-packed types (like Mapping, Lazy etc.) require calculating the storage key during compilation.

Consider annotating structs and enums that are intended to be a part of the storage with this macro. If the type is packed then the usage of the macro is optional.

If the type is non-packed it is best to rely on automatic storage key calculation via ink::storage_item.

The usage of KEY: StorageKey generic allows to propagate the parent’s storage key to the type and offset the storage key of the type. It is helpful for non-packed types that can be used several times in the contract. Each field should have a unique storage key, so propagation of the parent’s storage key allows one to achieve it.

The macro should be called before derive macros because it can change the type.

All required traits can be:

  • Derived manually via #[derive(...)].
  • Derived automatically via deriving of scale::Decode and scale::Encode.
  • Derived via this macro.

Example

Trait implementation

use ink_prelude::vec::Vec;
use ink::storage::{
    Lazy,
    Mapping,
};
use ink::storage::traits::{
    StorageKey,
    StorableHint,
};
use ink::storage::traits::Storable;

// Deriving `scale::Decode` and `scale::Encode` also derives blanket implementation of all
// required traits to be storable.
#[derive(scale::Decode, scale::Encode)]
#[cfg_attr(
    feature = "std",
    derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
)]
#[derive(Default, Debug)]
struct Packed {
    s1: u128,
    s2: Vec<u128>,
    // Fails because `StorableHint` is only implemented for `Vec` where `T: Packed`.
    // s3: Vec<NonPacked>,
}

// Example of how to define the packed type with generic.
#[derive(scale::Decode, scale::Encode)]
#[cfg_attr(
    feature = "std",
    derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
)]
#[derive(Default, Debug)]
struct PackedGeneric<T: ink::storage::traits::Packed> {
    s1: (u128, bool),
    s2: Vec<T>,
    s3: String,
}

// Example of how to define the non-packed type.
#[ink::storage_item]
#[derive(Default, Debug)]
struct NonPacked {
    s1: Mapping<u32, u128>,
    s2: Lazy<u128>,
}

// Example of how to define the non-packed generic type.
#[ink::storage_item(derive = false)]
#[derive(Storable, StorableHint, StorageKey)]
#[cfg_attr(
    feature = "std",
    derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
)]
#[derive(Default, Debug)]
struct NonPackedGeneric<T>
where
    T: Default + core::fmt::Debug,
    T: ink::storage::traits::Packed,
{
    s1: u32,
    s2: T,
    s3: Mapping<u128, T>,
}

// Example of how to define a complex packed type.
#[derive(scale::Decode, scale::Encode)]
#[cfg_attr(
    feature = "std",
    derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
)]
#[derive(Default, Debug)]
struct PackedComplex {
    s1: u128,
    s2: Vec<u128>,
    s3: Vec<Packed>,
}

// Example of how to define a complex non-packed type.
#[ink::storage_item]
#[derive(Default, Debug)]
struct NonPackedComplex<KEY: StorageKey> {
    s1: (String, u128, Packed),
    s2: Mapping<u128, u128>,
    s3: Lazy<u128>,
    s4: Mapping<u128, Packed>,
    s5: Lazy<NonPacked>,
    s6: PackedGeneric<Packed>,
    s7: NonPackedGeneric<Packed>,
    // Fails because: the trait `ink::storage::traits::Packed` is not implemented for `NonPacked`
    // s8: Mapping<u128, NonPacked>,
}

Header Arguments

The #[ink::storage_item] macro can be provided with an additional comma-separated header argument:

  • derive: bool

    The derive configuration parameter is used to enable/disable auto deriving of all required storage traits.

    Usage Example:

    use ink::storage::Mapping;
    use ink::storage::traits::{
        StorableHint,
        StorageKey,
        Storable,
    };
    
    #[ink::storage_item(derive = false)]
    #[derive(StorableHint, Storable, StorageKey)]
    struct NonPackedGeneric<T: ink::storage::traits::Packed> {
        s1: u32,
        s2: Mapping<u128, T>,
    }

    Default value: true.