4

I'm writing chain extensions for my pallets and I have a few functions that require dynamic sized types, for example Vec<u8> for metadata and Vec<u64> for a list of asset ids.

The way I am approaching this right now is to make an array filled with zeroes in the size of the boundaries set for those cases in the runtime and fill the beginning with the actual data in the contract, then send it to the chain extension like that and removing the trailing zeroes.

Sure this works, but it cannot be the best possible approach and I'm struggling to find any resource on chain extensions. There's a short documentation page and an example that shows the very minimum, only deals with a single function that has a single fixed size argument.

So yeah, what I'm looking for here is a better approach to dynamic sized types in chain extensions but I'd really appreciate any resources and actual code for me to take a look.

1
  • If possible, storing a fixed sized hash of the value might be better?
    – Squirrel
    Apr 2, 2022 at 11:44

2 Answers 2

7

In order to read memory from a contract you should check out the different functions on the Environment type. As Green Baneling already pointed out correctly you should use SCALE in order to encode and decode types from and to contract memory.

As you already noticed the read_as function is only meant for types where the size is known at compile time. In your case you should use env.read_as_unbounded(len: u32). The len passed by the contract can be read by calling env.in_len().

4
  • Could you provide some examples of this usage and their counterparts in the contract side? I've been experimenting a lot with read() and read_as_unbounded() while also sending the lengths of the encoded types as the first arguments, but all my tries resulted in encoding failed errors when attempting to call those functions.
    – Gabe
    Apr 2, 2022 at 13:57
  • I mean "decoding" failed errors
    – Gabe
    Apr 2, 2022 at 15:09
  • 1
    Oh I see now, I don't have to encode things myself on the contract side. I was doing that and encoded data was being sent with double encoding. I managed to get things working, but I still believe documentation and proper example code is very lacking for this.
    – Gabe
    Apr 2, 2022 at 16:20
  • 1
    Documentation could always be better of course. But did you check out all the resources?: 1 2 3 Apr 3, 2022 at 11:19
2

Each type in ink! implements scale::Encode and scale::Decode. That serialization and deserialization are used across the substrate everywhere. To pass some dynamic types you can encode it on the contract side and decode it on the extension side.

1
  • 1
    But on the runtime side the chain_extension types need to implement MaxEncodedLen to be read from memory: let a: Vec<u8> = env.read_as()?; -> the trait 'MaxEncodedLen' is not implemented for 'hidden_include::dispatch::Vec<u8>'
    – Gabe
    Apr 1, 2022 at 14:47

Your Answer

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

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