Shawn Tabrizi
Shawn Tabrizi

Reputation: 12434

Problems using `u8` in Substrate and ink

I am trying to add a simple u8 to my Substrate Runtime Module:

decl_storage! {
    trait Store for Module<T: Trait> as TemplateModule {
        MyByte: u8;
    }
}

However, I get a compiler error that it does not implement Parity Codec's Encode or Decode:

error[E0277]: the trait bound `u8: _IMPL_DECODE_FOR_Event::_parity_codec::Encode` is not satisfied
  --> /Users/shawntabrizi/Documents/GitHub/substrate-package/substrate-node-template/runtime/src/template.rs:23:1
   |
23 | / decl_storage! {
24 | |     trait Store for Module<T: Trait> as TemplateModule {
25 | |         MyByte: u8;
26 | |     }
27 | | }
   | |_^ the trait `_IMPL_DECODE_FOR_Event::_parity_codec::Encode` is not implemented for `u8`

A similar problem occurs when I try to store a u8 in a Substrate Smart Contract using ink!:

contract! {
    struct MyContract {
        value: storage::Value<u8>,
    }
    ...
}

Error:

error[E0277]: the trait bound `u8: parity_codec::codec::Encode` is not satisfied
  --> src/lib.rs:26:1
   |
26 | / contract! {
27 | |     struct MyContract {
28 | |         value: storage::Value<u8>,
29 | |     }
...  |
49 | |     }
50 | | }
   | |_^ the trait `parity_codec::codec::Encode` is not implemented for `u8`

Why is that, and what can I do to resolve the issue?

Upvotes: 2

Views: 849

Answers (1)

Shawn Tabrizi
Shawn Tabrizi

Reputation: 12434

Today, the parity_codec does not support Encoding of u8 due to avoid a type collision, since Vec<u8> is a special case from Vec<T>.

See: https://github.com/paritytech/parity-codec/issues/47

gavofyork:

Because it would otherwise make the two encodings: Vec<u8> and Vec<T: Codec> clash.

It is possible this could be fixed in the future with additional Rust features, but for now, you will need to store your single bytes as [u8; 1] and work with that type.


Substrate Runtime Module

One hacky solution for a Substrate Runtime Module looks something like this:

use support::{decl_module, decl_storage, decl_event, StorageValue, dispatch::Result};
use system::ensure_signed;

pub trait Trait: system::Trait {
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}

type U8 = [u8; 1];

decl_storage! {
    trait Store for Module<T: Trait> as TemplateModule {
        MyByte get(my_byte): U8;
    }
}

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {

        fn deposit_event<T>() = default;

        pub fn set_my_byte(origin, input: U8) -> Result {
            let who = ensure_signed(origin)?;

            <MyByte<T>>::put(input);

            Self::deposit_event(RawEvent::MyByteStored(input, who));
            Ok(())
        }

        pub fn add_to_byte(origin, input: U8) -> Result {
            let who = ensure_signed(origin)?;

            let my_byte = Self::my_byte()[0];
            let my_new_byte = my_byte.checked_add(input[0]).ok_or("Overflow")?;

            <MyByte<T>>::put([my_new_byte]);
            Self::deposit_event(RawEvent::MyByteStored([my_new_byte], who));
            Ok(())
        }
    }
}

decl_event!(
    pub enum Event<T> where AccountId = <T as system::Trait>::AccountId {
        MyByteStored(U8, AccountId),
    }
);

Where we assign a new type type U8 = [u8; 1];. The choice of our new type name is important since it will trick the Polkadot UI to treat this value simply as a u8 for any input/output fields it generates. If you try to use a custom type like type Byte = [u8; 1], the UI will ask you to import the definition of that custom type. If you try to use [u8; 1] directly, the Polkadot UI will not know how to render the input/output of that value.

Furthermore, as of the time of writing this post, the decl_event! macro has an issue depositing a [u8; 1] directly due to pattern matching.

Note that you will need to treat this type like an array when you use it. add_to_byte() shows an example of that. So ultimately, you need to extract the first item of the array to extract the byte, and you need to wrap your byte in an array to set a U8:

let my_byte = Self::my_byte()[0];
...
<MyByte<T>>::put([my_new_byte]);

Other solutions may involve using other types which are natively supported like Vec<u8> or u16, and doing the appropriate checks on your runtime that this is treated like a single u8, but the UI will not know better.


Substrate Smart Contracts

I have not found a great solution for ink! yet, but you should be able to use [u8; 1] directly in all of your code. Again, you will need to treat it like an array for getters and setters. But when generating the ABI you will need to manually change the instances of [u8; 1] to u8 to trick the UI to do what you want.

Upvotes: 1

Related Questions