Attribute Macro frame_support::pallet_macros::storage
source · #[storage]
Expand description
Declares a type alias as a storage item.
Storage items are pointers to data stored on-chain (the blockchain state), under a specific key. The exact key is dependent on the type of the storage.
From the perspective of this pallet, the entire blockchain state is abstracted behind a key-value api, namely
sp_io::storage
.
§Storage Types
The following storage types are supported by the #[storage]
macro. For specific
information about each storage type, refer to the documentation of the respective type.
§Storage Type Usage
The following details are relevant to all of the aforementioned storage types. Depending on the exact storage type, it may require the following generic parameters:
Prefix
- Used to give the storage item a unique key in the underlying storage.Key
- Type of the keys used to store the values,Value
- Type of the value being stored,Hasher
- Used to ensure the keys of a map are uniformly distributed,QueryKind
- Used to configure how to handle queries to the underlying storage,OnEmpty
- Used to handle missing values when querying the underlying storage,MaxValues
- not currently used.
Each Key
type requires its own designated Hasher
declaration, so that
StorageDoubleMap
needs two of
each, and StorageNMap
needs N
such
pairs. Since StorageValue
only stores
a single element, no configuration of hashers is needed.
§Syntax
Two general syntaxes are supported, as demonstrated below:
- Named type parameters, e.g.,
type Foo<T> = StorageValue<Value = u32>
. - Positional type parameters, e.g.,
type Foo<T> = StorageValue<_, u32>
.
In both instances, declaring the generic parameter <T>
is mandatory. Optionally, it
can also be explicitly declared as <T: Config>
. In the compiled code, T
will
automatically include the trait bound Config
.
Note that in positional syntax, the first generic type parameter must be _
.
§Example
#[frame_support::pallet]
mod pallet {
/// Positional syntax, without bounding `T`.
#[pallet::storage]
pub type Foo<T> = StorageValue<_, u32>;
/// Positional syntax, with bounding `T`.
#[pallet::storage]
pub type Bar<T: Config> = StorageValue<_, u32>;
/// Named syntax.
#[pallet::storage]
pub type Baz<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
}
§Value Trait Bounds
To use a type as the value of a storage type, be it StorageValue
, StorageMap
or
anything else, you need to meet a number of trait bound constraints.
Notably, all value types need to implement Encode
, Decode
, MaxEncodedLen
and
TypeInfo
, and possibly Default
, if
ValueQuery
is used, explained in the
next section.
§QueryKind
Every storage type mentioned above has a generic type called
QueryKind
that determines its
“query” type. This refers to the kind of value returned when querying the storage, for
instance, through a ::get()
method.
There are three types of queries:
OptionQuery
: The default query type. It returnsSome(V)
if the value is present, orNone
if it isn’t, whereV
is the value type.ValueQuery
: Returns the value itself if present; otherwise, it returnsDefault::default()
. This behavior can be adjusted with theOnEmpty
generic parameter, which defaults toOnEmpty = GetDefault
.ResultQuery
: ReturnsResult<V, E>
, whereV
is the value type.
See QueryKind
for further examples.
§Optimized Appending
All storage items — such as
StorageValue
,
StorageMap
, and their variants—offer an
::append()
method optimized for collections. Using this method avoids the
inefficiency of decoding and re-encoding entire collections when adding items. For
instance, consider the storage declaration type MyVal<T> = StorageValue<_, Vec<u8>, ValueQuery>
. With MyVal
storing a large list of bytes, ::append()
lets you
directly add bytes to the end in storage without processing the full list. Depending on
the storage type, additional key specifications may be needed.
§Example
#[test]
pub fn example_storage_value_append() {
type MyVal = StorageValue<Prefix, Vec<u8>, ValueQuery>;
TestExternalities::default().execute_with(|| {
MyVal::set(vec![42, 43]);
assert_eq!(MyVal::get(), vec![42, 43]);
// Append a single u32 to Vec stored in `MyVal`
MyVal::append(40);
assert_eq!(MyVal::get(), vec![42, 43, 40]);
});
}
Similarly, there also exists a ::try_append()
method, which can be used when handling
types where an append operation might fail, such as a
BoundedVec
.
§Example
#[test]
pub fn example_storage_value_try_append() {
type MyVal = StorageValue<Prefix, BoundedVec<u8, ConstU32<10>>, ValueQuery>;
TestExternalities::default().execute_with(|| {
MyVal::set(BoundedVec::try_from(vec![42, 43]).unwrap());
assert_eq!(MyVal::get(), vec![42, 43]);
// Try to append a single u32 to BoundedVec stored in `MyVal`
assert_ok!(MyVal::try_append(40));
assert_eq!(MyVal::get(), vec![42, 43, 40]);
});
}
§Optimized Length Decoding
All storage items — such as
StorageValue
,
StorageMap
, and their counterparts —
incorporate the ::decode_len()
method. This method allows for efficient retrieval of
a collection’s length without the necessity of decoding the entire dataset.
§Example
#[test]
pub fn example_storage_value_decode_len() {
type MyVal = StorageValue<Prefix, BoundedVec<u8, ConstU32<10>>, ValueQuery>;
TestExternalities::default().execute_with(|| {
MyVal::set(BoundedVec::try_from(vec![42, 43]).unwrap());
assert_eq!(MyVal::decode_len().unwrap(), 2);
});
}
§Hashers
For all storage types, except
StorageValue
, a set of hashers needs
to be specified. The choice of hashers is crucial, especially in production chains. The
purpose of storage hashers in maps is to ensure the keys of a map are
uniformly distributed. An unbalanced map/trie can lead to inefficient performance.
In general, hashers are categorized as either cryptographically secure or not. The
former is slower than the latter. Blake2
and Twox
serve as examples of each,
respectively.
As a rule of thumb:
- If the map keys are not controlled by end users, or are cryptographically secure by
definition (e.g.,
AccountId
), then the use of cryptographically secure hashers is NOT required. - If the map keys are controllable by the end users, cryptographically secure hashers should be used.
For more information, look at the types that implement
frame_support::StorageHasher
.
Lastly, it’s recommended for hashers with “concat” to have reversible hashes. Refer to
the implementors section of
hash::ReversibleStorageHasher
.
§Prefixes
Internally, every storage type generates a “prefix”. This prefix serves as the initial
segment of the key utilized to store values in the on-chain state (i.e., the final key
used in sp_io::storage
). For all storage types, the following rule
applies:
The storage prefix begins with
twox128(pallet_prefix) ++ twox128(STORAGE_PREFIX)
, wherepallet_prefix
is the name assigned to the pallet instance inframe_support::construct_runtime
, andSTORAGE_PREFIX
is the name of thetype
aliased to a particular storage type, such asFoo
intype Foo<T> = StorageValue<..>
.
For StorageValue
, no additional key is
required. For map types, the prefix is extended with one or more keys defined by the
map.
§Example
#[test]
pub fn example_storage_value_map_prefixes() {
type MyVal = StorageValue<Prefix1, u32, ValueQuery>;
type MyMap = StorageMap<Prefix2, Blake2_128Concat, u16, u32, ValueQuery>;
TestExternalities::default().execute_with(|| {
// This example assumes `pallet_prefix` to be "test"
// Get storage key for `MyVal` StorageValue
assert_eq!(
MyVal::hashed_key().to_vec(),
[twox_128(b"test"), twox_128(b"MyVal")].concat()
);
// Get storage key for `MyMap` StorageMap and `key` = 1
let mut k: Vec<u8> = vec![];
k.extend(&twox_128(b"test"));
k.extend(&twox_128(b"MyMap"));
k.extend(&1u16.blake2_128_concat());
assert_eq!(MyMap::hashed_key_for(1).to_vec(), k);
});
}
§Related Macros
The following attribute macros can be used in conjunction with the #[storage]
macro:
getter
: Creates a custom getter function.storage_prefix
: Overrides the default prefix of the storage item.unbounded
: Declares the storage item as unbounded.disable_try_decode_storage
: Declares that try-runtime checks should not attempt to decode the storage item.
§Example
#[frame_support::pallet]
mod pallet {
/// A kitchen-sink StorageValue, with all possible additional attributes.
#[pallet::storage]
#[pallet::getter(fn foo)]
#[pallet::storage_prefix = "OtherFoo"]
#[pallet::unbounded]
#[pallet::disable_try_decode_storage]
pub type Foo<T> = StorageValue<_, u32, ValueQuery>;
}
§Note on deprecation of storage items
- Usage of
deprecated
attribute will propagate deprecation information to the pallet metadata where the storage item was declared. - For general usage examples of
deprecated
attribute please refer to https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-deprecated-attribute
Documentation for this macro can be found at frame_support::pallet_macros::storage
.