## Module frame_storage_derives

Source
Expand description

Learn about the details of what derives are needed for a type to be store-able in `frame` storage.

## §Frame storage derives

> Note:

> In all examples, a few lines of boilerplate have been hidden from each snippet for conciseness.

Let’s begin by starting to store a `NewType` in a storage item:

ⓘ

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	pub struct NewType(u32);
    	#[pezpallet::storage]
    	pub type Something<T> = StorageValue<_, NewType>;
    }

This raises a number of compiler errors, like:

    the trait `MaxEncodedLen` is not implemented for `NewType`, which is required by
    `pezframe::prelude::StorageValue<_GeneratedPrefixForStorageSomething<T>, NewType>:
    StorageInfoTrait`

This implies the following set of traits that need to be derived for a type to be stored in `frame` storage:

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
    	pub struct NewType(u32);

    	#[pezpallet::storage]
    	pub type Something<T> = StorageValue<_, NewType>;
    }

Next, let’s look at how this will differ if we are to store a type that is derived from `T` in storage, such as [`pezframe::prelude::BlockNumberFor`]:

ⓘ

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
    	pub struct NewType<T: Config>(BlockNumberFor<T>);

    	#[pezpallet::storage]
    	pub type Something<T: Config> = StorageValue<_, NewType<T>>;
    }

Surprisingly, this will also raise a number of errors, like:

    the trait `TypeInfo` is not implemented for `T`, which is required
    by`pezframe_support::pezpallet_prelude::StorageValue<pezpallet_2::_GeneratedPrefixForStorageSomething<T>,
    pezpallet_2::NewType<T>>:StorageEntryMetadataBuilder

Why is that? The underlying reason is that the `TypeInfo` `derive` macro will only work for `NewType` if all of `NewType`’s generics also implement `TypeInfo`. This is not the case for `T` in the example above.

If you expand an instance of the derive, you will find something along the lines of: `impl<T> TypeInfo for NewType<T> where T: TypeInfo { ... }`. This is the reason why the `TypeInfo` trait is required for `T`.

To fix this, we need to add a `#[scale_info(skip_type_params(T))]` attribute to `NewType`. This additional macro will instruct the `derive` to skip the bound on `T`.

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
    	#[scale_info(skip_type_params(T))]
    	pub struct NewType<T: Config>(BlockNumberFor<T>);

    	#[pezpallet::storage]
    	pub type Something<T: Config> = StorageValue<_, NewType<T>>;
    }

Next, let’s say we wish to store `NewType` as [`pezframe::prelude::ValueQuery`], which means it must also implement `Default`. This should be as simple as adding `derive(Default)` to it, right?

ⓘ

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	#[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo, Default)]
    	#[scale_info(skip_type_params(T))]
    	pub struct NewType<T: Config>(BlockNumberFor<T>);

    	#[pezpallet::storage]
    	pub type Something<T: Config> = StorageValue<_, NewType<T>, ValueQuery>;
    }

Under the hood, the expansion of the `derive(Default)` will suffer from the same restriction as before: it will only work if `T: Default`, and `T` is not `Default`. Note that this is an expected issue: `T` is merely a wrapper of many other types, such as `BlockNumberFor<T>`. `BlockNumberFor<T>` should indeed implement `Default`, but `T` implementing `Default` is rather meaningless.

To fix this, frame provides a set of macros that are analogous to normal rust derive macros, but work nicely on top of structs that are generic over `T: Config`. These macros are:

- [`pezframe::prelude::DefaultNoBound`]
- [`pezframe::prelude::DebugNoBound`]
- [`pezframe::prelude::PartialEqNoBound`]
- [`pezframe::prelude::EqNoBound`]
- [`pezframe::prelude::CloneNoBound`]
- [`pezframe::prelude::PartialOrdNoBound`]
- [`pezframe::prelude::OrdNoBound`]

The above traits are almost certainly needed for your tests - to print your type, assert equality or clone it.

We can fix the following example by using [`pezframe::prelude::DefaultNoBound`].

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	#[derive(
    		codec::Encode,
    		codec::Decode,
    		codec::MaxEncodedLen,
    		scale_info::TypeInfo,
    		DefaultNoBound
    	)]
    	#[scale_info(skip_type_params(T))]
    	pub struct NewType<T:Config>(BlockNumberFor<T>);

    	#[pezpallet::storage]
    	pub type Something<T: Config> = StorageValue<_, NewType<T>, ValueQuery>;
    }

Finally, if a custom type that is provided through `Config` is to be stored in the storage, it is subject to the same trait requirements. The following does not work:

ⓘ

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	use pezframe::prelude::*;
    	#[pezpallet::config]
    	pub trait Config: pezframe_system::Config {
    		type CustomType;
    	}
    	#[pezpallet::pezpallet]
    	pub struct Pezpallet<T>(_);
    	#[pezpallet::storage]
    	pub type Something<T: Config> = StorageValue<_, T::CustomType>;
    }

But adding the right trait bounds will fix it.

    #[pezframe::pezpallet]
    pub mod pezpallet {
    	use pezframe::prelude::*;
    	#[pezpallet::config]
    	pub trait Config: pezframe_system::Config {
    		type CustomType: codec::FullCodec
    			+ codec::MaxEncodedLen
    			+ scale_info::TypeInfo
    			+ Debug
    			+ Default;
    	}
    	#[pezpallet::pezpallet]
    	pub struct Pezpallet<T>(_);
    	#[pezpallet::storage]
    	pub type Something<T: Config> = StorageValue<_, T::CustomType>;
    }