-
Notifications
You must be signed in to change notification settings - Fork 45
Description
This issue is aimed at having a complete technical low-level overview of CompactBytesArray type with examples which combined should cover CompactBytesArray completely. LSP2 must have all valueTypes described and covered the same way Solidity documentation has covered its data types and respective ABI encoding/decoding.
TL;DR
Not enough documentation on how to encode/decode [CompactBytesArray]. We have explanations for bytes[CompactBytesArray] and bytesN[CompactBytesArray] in LSP2, but in LSP6 we use (bytes4,address,bytes4)[CompactBytesArray] and both the type and the encoding don't match what is described in LSP2.
The issue
Currently (06.02.2023) in LSP2 we have bytes[CompactBytesArray] and bytesN[CompactBytesArray] value types. These are covered and well understood. But that documentation comes short of the explanation required with the introduction of LSP6's AllowedCalls. Both of these were released with v0.8.0 see CHANGELOG8.pdf
LSP6's AllowedCalls scheme uses valueType set as (bytes4,address,bytes4)[CompactBytesArray], and it has the custom case of encoding [CompactBytesArray] of tuples described in LSP6 (not the right place, IMO). This custom case, first of all, must not be custom and it must be covered by LSP2, and it's not.
On top of that, since we have (bytes4,address,bytes4)[CompactBytesArray] in LSP6 and it doesn't match bytes[CompactBytesArray] and bytesN[CompactBytesArray] it implicitly allows developers to use any other type with [CompactBytesArray], like address[CompactBytesArray][CompactBytesArray] (2D CompactBytesArray). And the question is - how will it be encoded? There is no single source of truth that everyone can refer to at the moment to implement the correct encoding/decoding functions of CBAs.
LSP2 is a standard. We must define low-level specifications that others will build upon their custom structures the same way we use Solidity and its specifications to encode and decode ABI of types it provides. These custom structures will use a specific, known and well-documented set of small blocks (value types) and everyone will be able to refer to LSP2 instead to understand how to create and disassemble these small blocks (encode and decode ABI).
Additional question: if the compact bytes array has 2 or more levels of nested types do we compact only the first layer or all of them? My answer is - only the first layer, the type declared directly next to the left side of the [CompactBytesArray].
Example: (address[],(boolean,bytes4))[CompactBytesArray] (a type that doesn't make sense but is technically possible).
Do we encode ...,(boolean,bytes4)... tuple the default way or do we reduce/compact it as well?
My understanding is that we encode it the default way. The same applies in my opinion to the question above about arrays in CompactBytesArray.
Encoding example of (address[],(boolean,bytes4))[CompactBytesArray]:
// The `(address[],...)` part of `(address[],(boolean,bytes4))[CompactBytesArray]`
web3.eth.abi.encodeParameter('address[]', ['0x98d2fF3907A4a9dEb10B1F2F79EBF078984501BF'])
=>
0x0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000098d2ff3907a4a9deb10b1f2f79ebf078984501bf
// The `(...,(boolean,bytes4))` part of `(address[],(boolean,bytes4))[CompactBytesArray]`
web3.eth.abi.encodeParameter('(bool,bytes4)', [true, '0xffbbffbb'])
=>
0x0000000000000000000000000000000000000000000000000000000000000001
ffbbffbb00000000000000000000000000000000000000000000000000000000
// And instead of encoding these values as default array (with the total count of bytes = 256):
web3.eth.abi.encodeParameter('(address[],(bool,bytes4))[]', [[['0x98d2fF3907A4a9dEb10B1F2F79EBF078984501BF'], [true, '0xffbbffbb']]])
=>
0x0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000001
ffbbffbb00000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000098d2ff3907a4a9deb10b1f2f79ebf078984501bf
// We will encode these values as CompactBytesArray (with the total count of bytes = 196).
// Note: erc725js.encodeParameter is not a real function! Used just for explanation.
erc725js.encodeParameter('(address[],(bool,bytes4))[CompactBytesArray]', [[['0x98d2fF3907A4a9dEb10B1F2F79EBF078984501BF'], [true, '0xffbbffbb']]])
=>
00C0
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000001
ffbbffbb00000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000098d2ff3907a4a9deb10b1f2f79ebf078984501bf
After a discussion with @b00ste on Friday (04.02.2023) it also came to mind that [CompactBytesArray] could completely replace []. Any thoughts on that idea are welcome.
But [CompactBytesArray] has two downsides in comparison to [] one of which can be fixed:
- (fixable) no way to declare fixed size
[CompactBytesArray]. For the default array type, you can place a number between square brackets to define the array's size, e.g.[2]. As a solution it could be placed separated by a colon[CompactBytesArray:2]; - (non-fixable*) just having the ABI of
[CompactBytesArray]you have no way to tell if it's valid or not. The ABI of[](as of any other Solidity type) must have the number of bytes be a multiple of 32. One byte off and it's not valid. This is not the case with[CompactBytesArray]and cannot be.
* if the solution mentioned below for the fixed-size CBAs will be applied there is a certain set of conditions that will allow us to produce a type that can be validated before the encoding attempt has happened. The set of conditions is: (1) all subtypes have fixed size and (2) CBA is itself fix-sized.
Example: the expected bytes count forbytes8[CompactBytesArray:10]is(8 bytes + 2 bytes size prefix) * 10 = 100 bytes.