12

When building my pallet, I run into the error:

error[E0277]: the trait bound `T: parity_scale_codec::MaxEncodedLen` is not satisfied
  --> pallets/template/src/lib.rs:42:15
   |
42 |     #[pallet::pallet]
   |               ^^^^^^ the trait `parity_scale_codec::MaxEncodedLen` is not implemented for `T`
   |
note: required because of the requirements on the impl of `parity_scale_codec::MaxEncodedLen` for `Kitty<T>`
  --> pallets/template/src/lib.rs:25:69
   |
25 |     #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
   |                                                                        ^^^^^^^^^^^^^
26 |     #[scale_info(skip_type_params(T))]
27 |     pub struct Kitty<T: Config> {
   |                ^^^^^^^^^^^^^^^^
   = note: required because of the requirements on the impl of `StorageInfoTrait` for `frame_support::pallet_prelude::StorageMap<_GeneratedPrefixForStorageKitties<T>, frame_support::Twox64Concat, <T as frame_system::Config>::Hash, Kitty<T>>`
   = note: this error originates in the derive macro `MaxEncodedLen` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting this bound
   |
42 |     #[pallet::pallet + parity_scale_codec::MaxEncodedLen]
   |                      +++++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
error: could not compile `pallet-template` due to previous error
warning: build failed, waiting for other jobs to finish...
error: build failed

The custom type definitions are:

// Struct for holding Kitty information.
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct Kitty<T: Config> {
    pub dna: [u8; 16],   // Using 16 bytes to represent a kitty DNA
    pub price: Option<BalanceOf<T>>,
    pub gender: Gender,
    pub owner: AccountOf<T>,
}

// Enum declaration for Gender.
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum Gender {
    Male,
    Female,
}

As far as I know, MaxEncodedLen should work for Kitty since all of its fields are also bounded in length.

How do I resolve this issue?

2 Answers 2

11

The underlying issue here is that Rust macros are not super intelligent.

While you are correct that all the interior fields of Kitty is bounded in length, you are also using the generic type T: Config to help access some of those types.

The Rust macros for automatically deriving MaxEncodedLen naively thinks that T must also be bounded by MaxEncodedLen, even though T itself is not being used in the actual types.

We can fix this in one of two ways:

  1. Add a simple "hint" to the compiler in the form of #[codec(mel_bound())].

    // Struct for holding Kitty information.
    #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
    #[scale_info(skip_type_params(T))]
    #[codec(mel_bound())] // <---------------- Here
    pub struct Kitty<T: Config> {
        pub dna: [u8; 16],   // Using 16 bytes to represent a kitty DNA
        pub price: Option<BalanceOf<T>>,
        pub gender: Gender,
        pub owner: AccountOf<T>,
    }
    

    Oversimplifying, this will allow the compiler to skip trying to derive MaxEncodedLen for T, and just check the underlying types themselves all have MaxEncodedLen.

  2. Do not use <T: Config> when defining Kitties.

    Instead, make generic only the types you need:

    // Struct for holding Kitty information.
    #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
    pub struct Kitty<Account, Balance> {
        pub dna: [u8; 16],   // Using 16 bytes to represent a kitty DNA
        pub price: Option<Balance>,
        pub gender: Gender,
        pub owner: Account,
    }
    

    In this case, we have been able to remove T completely from the struct definition, and thus will have no complaints from the derive macro about trying to implement MaxEncodedLen for it. Note that we were also able to get rid of #[scale_info(skip_type_params(T))] from the definition aswell, since this basically doing the same thing as #[codec(mel_bound())] above.

2
  • 1
    That's a great explanation. I guess this must be in the tutorial as well. Mar 25, 2022 at 7:34
  • 2
    I was searching substrate crates to get more information around mel_bound(). Can you throw some document pointers to #[codec(mel_bound())]? Mar 25, 2022 at 22:42
1

In case of a struct that has Vec:

#[derive(Encode, Decode, Default, TypeInfo)]
    pub struct Nickname {
        pub first: Vec<u8>,
        pub last: Option<Vec<u8>>, // handles empty storage
}

I found as a solution to use the macro #[pallet::without_storage_info], this macro enables you to define pallet storage items that don't have a fixed size. This allows you to override the default behavior if you require unbounded storage for an entire pallet. To use it, add the #[pallet::without_storage_info] attribute to the pallet struct like so:

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);

I am posting this response in case it can be helpful to someone, but to store a Vec type is not the best practice, as it has no limit.

So in the case of haveing a Vec field, a better solution would be to come up with a size limit field (probably configured through your Config trait) and with it use BoundedVec instead of Vec:

pub struct Nickname<T: Config> {
    pub first: BoundedVec<u8, T::MaxLength>,
    pub last: Option<BoundedVec<u8, T::MaxLength>>,
}

And then use the solution Shawn proposed above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.