When declaring types, with generic parameters, to be stored in runtime storage, it’s common to see this pattern
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo)]
pub struct MyItem<TypeA, TypeB> {
pub item_a: TypeA,
pub item_b: TypeB,
}
type MyItemOf<T> = MyItem<<T as Config>::TypeA, <T as Config>::TypeB>;
However, this seems a bit clunky, and I have tried the following
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo)]
pub struct MyItem<T: Config> {
pub item_a: T::TypeA,
pub item_b: T::TypeB,
}
only to get an error saying that T
doesn’t implement TypeInfo
. This is what I ran into in the past, so I've used the first pattern above for some time.
There’s a way to get the second approach above to work by using #[scale_info(skip_type_params(..))]
to tell scale_info
to ignore that T
doesn't implement TypeInfo
.
Still, I run into the error that MyItem<T>
doesn't implement parity_scale_codec::MaxEncodedLen
. I've looked around and found that the Substrate Kitties tutorial uses the #[codec(mel_bound)]
attribute on its Kitty<T>
struct. I found that it also works in my case and I ended up with
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo)]
#[scale_info(skip_type_params(T))]
#[codec(mel_bound())]
pub struct MyItem<T: Config> {
pub item_a: T::TypeA,
pub item_b: T::TypeB,
}
However, I don't quite understand how #[codec(mel_bound())]
works and what are the tradeoffs involved with it. I couldn't find much documentation around it. Would anyone be kind enough to explain how it works?