Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Commit

Permalink
feat: explanation of remaining serialization format (#335)
Browse files Browse the repository at this point in the history
  • Loading branch information
novusnota authored Aug 14, 2024
1 parent 9655bac commit d6ec58e
Showing 1 changed file with 81 additions and 6 deletions.
87 changes: 81 additions & 6 deletions pages/book/cells.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,22 +125,95 @@ While you may use them for [manual parsing](#cnp-manually) of the cells, it's st

Similar to serialization options of [`Int{:tact}`](/book/integers) type, `Cell{:tact}`, `Builder{:tact}` and `Slice{:tact}` also have various representations for encoding their values in the following cases:

* as fields of [contracts](/book/contracts) and [traits](/book/types#traits),
* as fields of [Structs](/book/structs-and-messages#structs) and [Messages](/book/structs-and-messages#messages),
* and as key/value types of [maps](/book/maps).
* as [storage variables](/book/contracts#variables) of [contracts](/book/contracts) and [traits](/book/types#traits),
* and as fields of [Structs](/book/structs-and-messages#structs) and [Messages](/book/structs-and-messages#messages).

```tact
```tact {2-3}
contract SerializationExample {
someCell: Cell as remaining;
someSlice: Slice as bytes32;
// Constructor function,
// necessary for this example contract to compile
init() {
self.someCell = emptyCell();
self.someSlice = beginCell().storeUint(42, 256).asSlice();
}
}
```

### `remaining` [#serialization-remaining]

The `remaining{:tact}` serialization option can be applied to values of [`Cell{:tact}`](#cells), [`Builder{:tact}`](#builders) and [`Slice{:tact}`](#slices) types.

It affects the process of constructing and parsing cell values by causing them to be stored and loaded directly rather than as a reference. To draw parallels with [cell manipulation instructions](#cells-immutability), specifying `remaining{:tact}` is like using [`Builder.storeSlice(){:tact}`][b-5] and [`Slice.loadSlice(){:tact}`][s-5] instead of [`Builder.storeRef(){:tact}`][b-8] and [`Slice.loadRef(){:tact}`][s-8], which are to be used by default.

In addition, the [TL-B][tlb] representation produced by Tact changes too:

```tact {3-5, 8-10}
contract SerializationExample {
// By default
cRef: Cell; // ^cell in TL-B
bRef: Builder; // ^builder in TL-B
sRef: Slice; // ^slice in TL-B
// With `remaining`
cRem: Cell as remaining; // remainder<cell> in TL-B
bRem: Builder as remaining; // remainder<builder> in TL-B
sRem: Slice as remaining; // remainder<slice> in TL-B
// Constructor function,
// necessary for this example contract to compile
init() {
self.cRef = emptyCell();
self.bRef = beginCell();
self.sRef = emptySlice();
self.cRem = emptyCell();
self.bRem = beginCell();
self.sRem = emptySlice();
}
}
```

There, `^cell`, `^builder` and `^slice` in [TL-B][tlb] syntax mean the reference to [`Cell{:tact}`](#cells), [`Builder{:tact}`](#builders) and [`Slice{:tact}`](#slices) values respectively, while the `remainder<…>` of `cell`, `builder` or `slice` tells that the given value would be stored as a `Slice{:tact}` directly and not as a reference.

Now, to give a real-world example, imagine that you need to notice and react to inbound [jetton][jetton] transfers in your smart contract. The appropriate [Message][message] structure for doing so would look something like this:

```tact /remaining/
message(0x7362d09c) JettonTransferNotification {
queryId: Int as uint64; // arbitrary request number to prevent replay attacks
amount: Int as coins; // amount of jettons transferred
sender: Address; // address of the sender of the jettons
forwardPayload: Slice as remaining; // optional custom payload
}
```

And the [receiver][recv] in the contract would look like this:

```tact
receive(msg: JettonTransferNotification) {
// ... you do you ...
}
```

### `remaining` [#serialization-bytes64]
Upon receiving a [jetton][jetton] transfer notification message, its cell body is converted into a `Slice{tact}` and then parsed as a `JettonTransferNotification{:tact}` [Message][message]. At the end of this process, the `forwardPayload` will have all the remaining data of the original message cell.

If we were to violate the [jetton][jetton] standard and place the `forwardPayload: Slice as remaining` field in any other position in the `JettonTransferNotification{:tact}` [Message][message], the `forwardPayload` would have the remaining data of the message cell at the moment of parsing it, which would include all the subsequent fields of the [Message][message].

Therefore, to prevent misuse of the contract storage and reduce gas consumption, make sure to specify `remaining{:tact}` serialization option only on the last field of the given [Message][message], as it will store all the remaining data of the [`Slice{:tact}`](#slices) at the moment it was parsed into said [Message][message] in [receiver functions][recv].

<Callout>

To be resolved by [#26](https://github.com/tact-lang/tact-docs/issues/26).
Note, that the cell serialized via `as remaining{:tact}` cannot be [optional](/book/optional). That is, specifying something like `Cell? as remaining{:tact}`, `Builder{:tact}` or `Slice? as remaining{:tact}` would cause a compilation error.

Also note, that as of now, specifying `remaining{:tact}` for the `Cell{:tact}` as the [map](/book/maps) value type does nothing — the cell would still be stored as a reference:

```tact
struct Nope {
m: map<Int, Cell as remaining>; // dict<uint8, ^cell> in TL-B notation,
// despite the `remaining` specified
}
```

</Callout>

Expand Down Expand Up @@ -373,9 +446,11 @@ let areSlicesNotEqual = aSlice.hash() != bSlice.hash(); // false
[p]: /book/types#primitive-types
[struct]: /book/structs-and-messages#structs
[message]: /book/structs-and-messages#messages
[recv]: /book/contracts#receiver-functions

[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview
[tlb]: https://docs.ton.org/develop/data-formats/tl-b-language
[jetton]: https://docs.ton.org/develop/dapps/asset-processing/jettons
[sha-2]: https://en.wikipedia.org/wiki/SHA-2#Hash_standard

[quadtree]: https://en.wikipedia.org/wiki/Quadtree
Expand Down

0 comments on commit d6ec58e

Please sign in to comment.