Skip to content

Commit

Permalink
extend header to 4 bytes for extension purpose; update doc and diagra…
Browse files Browse the repository at this point in the history
…m; add example code
  • Loading branch information
ws4charlie committed Oct 10, 2024
1 parent 4322e26 commit ed4a5cb
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 17 deletions.
123 changes: 106 additions & 17 deletions docs/rfc/002_standard_memo_format.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ The raw-byte representation of a standard non-EVM memo contains `9` sections (2
| | Section 0 | Section 1 | Section 2 | Section 3 | Section 4 | Section 5 | Section 6 | Section 7 | Section 8 |
|-----------------|------------|-------------|-------------|----------------|--------------|--------------|---------------|-------------|-------------|
| **Name** | Header | Receiver | Payload | RevertAddress | CallOnRevert | AbortAddress | RevertMessage | reserved | reserved |
| **Size (byte)** | 3 | 20 | variable | variable | 1 | 20 | variable | -- | -- |
| **Type** | [3]byte | ZEVM address| bytes | non-EVM address| bool | ZEVM address | bytes | -- | -- |
| **Size (byte)** | 4 | 20 | variable | variable | 1 | 20 | variable | -- | -- |
| **Type** | [4]byte | ZEVM address| bytes | non-EVM address| bool | ZEVM address | bytes | -- | -- |
| **Optional** | No | No | Yes | Yes | Yes | Yes | Yes | -- | -- |
<br><br>


## 3. The header

The `3-byte` header is a self-contained declaration of the memo properties and rules. The proposed header structure is designed in a flexible manner. The data fields are all declared as optional. With `reserved` flags, new features and fields can be added in the future without breaking compatibility.
The `4-byte` header is a self-contained declaration of the memo properties and rules. The proposed header structure is designed in a flexible manner. The data fields are all declared as optional. With `reserved` flags, new features and fields can be added in the future without breaking compatibility.

### `byte-0`: Memo Identifier

Expand All @@ -69,37 +69,49 @@ The ASCII code `0x5A` of letter `'Z'` is used as the identifier of the standard

### `byte-1`: Control Byte

| | bit 5 ~ 7 | bit 3 ~ 4 | bit 0 ~ 2 |
|-----------------|---------------------------------|--------------------------------|----------------------------------------------------------|
| **Name** | version # | encoding flag | operation code |
| **Optional** | No (0b000 for now) | No (0b00 or 0b01 or 0b10) | for non-contract chains; Ignored for contract chains. The operation code is already differentiated by method names in contract based |
| | bit 4 ~ 7 | bit 0 ~ 3 |
|-----------------|------------------------------------------------|--------------------------------------|
| **Name** | version # | encoding format |
| **Optional** | No (0b0000 for now) | No (0b0000 or 0b0001 or 0b0010) |
<br>


### `byte-2`: Control Byte

| | bit 4 ~ 7 | bit 0 ~ 3 |
|-----------------|------------------------------------------------|--------------------------------|
| **Name** | operation code | control bytes reserved |
| **Optional** | No (0b0000 or 0001 or 0010 for now) | -- |
<br>


### `byte-2`: Data Flags

### `byte-3`: Data Flags

| | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
|-----------------|---------------|--------------|---------------|-------------------|------------------|------------------|-------------------|------------------|
| **Name** | flag reserved | flag reserved| flag reserved | flag RevertMessage| flag AbortAddress| flag CallOnRevert| flag RevertAddress| flag Payload |
| **Name** | flag reserved | flag reserved| flag reserved | flag reserved | flag AbortAddress| flag CallOnRevert| flag RevertAddress| flag Payload |
| **Optional** | -- | -- | -- | Yes | Yes | Yes | Yes | Yes |
<br>

Supported `operation codes` :

The `operation code` is designed for the developers to explicitly specify the intention of an inbound transaction in non-contract chains (e.g. Bitcoin, Doge, …). A 3-bit operation code would allow `8` operation codes at maximum.
The `operation code` is designed for the developers to explicitly specify the intention of an inbound transaction in non-contract chains (e.g. Bitcoin, Doge, …).
The operation code is not necessary for non-contract chains, because the method name tells the intention of the transaction.

A 4-bit operation code would allow `16` operation codes at maximum.

| 0b000 | 0b001 | 0b010 |
|------------|------------------|------------|
| deposit | deposit_and_call | call |
| 0b0000 | 0b0001 | 0b0010 |
|-------------|-------------------|-------------|
| deposit | deposit_and_call | call |
<br>


Supported `encoding flags`:
This flag is to tell `zetaclient` how the memo fields should be decoded. A 2-bit weight flag seems good enough and won’t change in future.<br>
Note: the `compact *` encoding is to minimizes length of memo data (compared to ABI) and will be explained in following sections.

| 0b00 | 0b01 | 0b10 |
| 0b00 00 | 0b000 | 0b0010 |
|--------------|----------------|--------------|
| ABI encoded | compact short | compact long |
<br><br>
Expand All @@ -125,7 +137,84 @@ There are two ways to encode your the memo fields into raw bytes. The `ABI encod

| Decoding flag | Format | Layout | Description | Rule |
|---------------|---------------|-----------------------------------------|-----------------------------------------------|----------------------------------------------------------|
| 0b00 | ABI encoded | ABI encoded types | Types are packed/unpacked with ABI standard | [ABI Spec](https://docs.soliditylang.org/en/develop/abi-spec.html) |
| 0b01 | compact short | [1-byte length] + [data] | Carry up to 255 bytes of data. | A valid length must > 0 and match data length. |
| 0b10 | compact long | [2-byte length, little-endian] + [data] | Carry up to 65535 bytes (64KB) of data. | A valid length must > 0 and match data length. |
| 0b0000 | ABI encoded | ABI encoded types | Types are packed/unpacked with ABI standard | [ABI Spec](https://docs.soliditylang.org/en/develop/abi-spec.html) |
| 0b0001 | compact short | [1-byte length] + [data] | Carry up to 255 bytes of data. | A valid length (>= 0) must match data length. |
| 0b0010 | compact long | [2-byte length, little-endian] + [data] | Carry up to 65535 bytes (64KB) of data. | A valid length (>= 0) must match data length. |
|


## 6. How to pack your inbound memo

### Step-1: prepare your memo header

```go
func MakeHead() []byte {
header := make([]byte, 4)
header[0] = 'Z'
header[1] = 0<<4 | 0b0010 // version + encoding format (e.g. compact long)
header[2] = 0b0001 << 4 // operation code (e.g. DepositAndCall)
header[3] = 0b00000011 // payload and revertAddress are set
return header
}
```


### Step-2: pack your memo data

For use cases that requires compact transaction data, choose encoding format of `compact short` or `compact long`.

```go
func CompactPack(receiver common.Address, payload []byte, revertAddress string) []byte {
data := make([]byte, 0)

// pack receiver (fixed 20 bytes)
data = append(data, receiver.Bytes()...)

// pack payload (dynamic)
// encode length with 1 byte if 'compact short' is your choice
lenthPayload := make([]byte, 2)
binary.LittleEndian.PutUint16(lenthPayload, uint16(len(payload)))

data = append(data, lenthPayload...) // write length
data = append(data, payload...) // write payload

// pack revert address (dynamic).
// encode length with 1 byte if 'compact short' is your choice
lengthRevertAddr := make([]byte, 2)
binary.LittleEndian.PutUint16(lengthRevertAddr, uint16(len([]byte(revertAddress))))

data = append(data, lengthRevertAddr...) // write length
data = append(data, []byte(revertAddress)...) // write revert address

return data
}
```


Also, ABI encoding format is another option to consider.

```go
func ABIPack(receiver common.Address, payload []byte, revertAddress string) ([]byte, error) {
// define the ABI for encoding the types: address, bytes, string
abiString := `[{"type":"function","name":"encode","inputs":[
{"type":"address"},
{"type":"bytes"},
{"type":"string"}],
"outputs":[]}]`

// parse the ABI
parsedABI, err := abi.JSON(strings.NewReader(abiString))
if err != nil {
return nil, err
}

// pack the values using the ABI
data, err := parsedABI.Pack("encode", receiver, payload, revertAddress)
if err != nil {
return nil, err
}

// remove the 4-byte selector
return data[4:], nil
}
```
Binary file modified docs/rfc/images/002_layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ed4a5cb

Please sign in to comment.