From f88202be0ae1667cd193ab102b87e900e1a11093 Mon Sep 17 00:00:00 2001
From: Novus Nota <68142933+novusnota@users.noreply.github.com>
Date: Mon, 8 Jul 2024 18:05:35 +0200
Subject: [PATCH] feat/fix: update description of `coins` serialization format
(#305)
---
pages/book/integers.mdx | 77 +++++++++++++++++++++++++++++++---------
pages/ref/core-cells.mdx | 18 ++++++++--
2 files changed, 76 insertions(+), 19 deletions(-)
diff --git a/pages/book/integers.mdx b/pages/book/integers.mdx
index d14f68da..b37191a2 100644
--- a/pages/book/integers.mdx
+++ b/pages/book/integers.mdx
@@ -52,7 +52,7 @@ Therefore, the amount of $1.25$ Toncoin, which can be represented in Tact as [`t
## Serialization
-When encoding `Int{:tact}` values to persistent state (fields of [contracts](/book/contracts) and [traits](/book/types#traits)), it's usually better to use smaller representations than $257$-bits to reduce [storage costs](https://docs.ton.org/develop/smart-contracts/fees#storage-fee). Usage of such representations is also called "serialization" due to them representing the native [TL-B](https://docs.ton.org/develop/data-formats/tl-b-languagehttps://docs.ton.org/develop/data-formats/tl-b-language) types which TON Blockchain operates on.
+When encoding `Int{:tact}` values to persistent state (fields of [contracts](/book/contracts) and [traits](/book/types#traits)), it's usually better to use smaller representations than $257$-bits to reduce [storage costs](https://docs.ton.org/develop/smart-contracts/fees#storage-fee). Usage of such representations is also called "serialization" due to them representing the native [TL-B][tlb] types which TON Blockchain operates on.
The persistent state size is specified in every declaration of a state variable after the `as{:tact}` keyword:
@@ -88,25 +88,64 @@ Motivation is very simple:
### Serialization types
-Name | Inclusive range | Space taken
-:--------------- | :-------------------------: | :------------------------:
-`uint8{:tact}` | $0$ to $2^{8} - 1$ | 8 bit = 1 byte
-`uint16{:tact}` | $0$ to $2^{16} - 1$ | 16 bit = 2 bytes
-`uint32{:tact}` | $0$ to $2^{32} - 1$ | 32 bit = 4 bytes
-`uint64{:tact}` | $0$ to $2^{64} - 1$ | 64 bit = 8 bytes
-`uint128{:tact}` | $0$ to $2^{128} - 1$ | 128 bit = 16 bytes
-`uint256{:tact}` | $0$ to $2^{256} - 1$ | 256 bit = 32 bytes
-`int8{:tact}` | $-2^{7}$ to $2^{7} - 1$ | 8 bit = 1 byte
-`int16{:tact}` | $-2^{15}$ to $2^{15} - 1$ | 16 bit = 2 bytes
-`int32{:tact}` | $-2^{31}$ to $2^{31} - 1$ | 32 bit = 4 bytes
-`int64{:tact}` | $-2^{63}$ to $2^{63} - 1$ | 64 bit = 8 bytes
-`int128{:tact}` | $-2^{127}$ to $2^{127} - 1$ | 128 bit = 16 bytes
-`int256{:tact}` | $-2^{255}$ to $2^{255} - 1$ | 256 bit = 32 bytes
-`int257{:tact}` | $-2^{256}$ to $2^{256} - 1$ | 257 bit = 32 bytes + 1 bit
-`coins{:tact}` | $0$ to $2^{120} - 1$ | 120 bit = 15 bytes
+Name | [TL-B][tlb] | Inclusive range | Space taken
+:--------------: | :-------------------------: | :-------------------------: | :------------------------:
+`uint8{:tact}` | [`uint8`][tlb-builtin] | $0$ to $2^{8} - 1$ | $8$ bits = $1$ byte
+`uint16{:tact}` | [`uint16`][tlb-builtin] | $0$ to $2^{16} - 1$ | $16$ bits = $2$ bytes
+`uint32{:tact}` | [`uint32`][tlb-builtin] | $0$ to $2^{32} - 1$ | $32$ bits = $4$ bytes
+`uint64{:tact}` | [`uint64`][tlb-builtin] | $0$ to $2^{64} - 1$ | $64$ bits = $8$ bytes
+`uint128{:tact}` | [`uint128`][tlb-builtin] | $0$ to $2^{128} - 1$ | $128$ bits = $16$ bytes
+`uint256{:tact}` | [`uint256`][tlb-builtin] | $0$ to $2^{256} - 1$ | $256$ bits = $32$ bytes
+`int8{:tact}` | [`int8`][tlb-builtin] | $-2^{7}$ to $2^{7} - 1$ | $8$ bits = $1$ byte
+`int16{:tact}` | [`int16`][tlb-builtin] | $-2^{15}$ to $2^{15} - 1$ | $16$ bits = $2$ bytes
+`int32{:tact}` | [`int32`][tlb-builtin] | $-2^{31}$ to $2^{31} - 1$ | $32$ bits = $4$ bytes
+`int64{:tact}` | [`int64`][tlb-builtin] | $-2^{63}$ to $2^{63} - 1$ | $64$ bits = $8$ bytes
+`int128{:tact}` | [`int128`][tlb-builtin] | $-2^{127}$ to $2^{127} - 1$ | $128$ bits = $16$ bytes
+`int256{:tact}` | [`int256`][tlb-builtin] | $-2^{255}$ to $2^{255} - 1$ | $256$ bits = $32$ bytes
+`int257{:tact}` | [`int257`][tlb-builtin] | $-2^{256}$ to $2^{256} - 1$ | $257$ bits = $32$ bytes + $1$ bit
+`coins{:tact}` | [`VarUInteger 16`][varuint] | $0$ to $2^{120} - 1$ | between $4$ and $124$ bits, [see below](#serialization-coins)
+
+### Variable `coins` type [#serialization-coins]
+
+In Tact, `coins{:tact}` is an alias to [`VarUInteger 16`][varuint] in [TL-B][tlb] representation, i.e. it takes a variable bit length depending on the optimal number of bytes needed to store the given integer and is commonly used for storing [nanoToncoin](/book/integers#nanotoncoin) amounts.
+
+This serialization format consists of two [TL-B fields](https://docs.ton.org/develop/data-formats/tl-b-language#field-definitions):
+
+* `len`, a $4$-bit unsigned big-endian integer storing the byte length of the value provided
+* `value`, a $8 * len$-bit unsigned big-endian representation of the value provided
+
+That is, integers serialized as `coins{:tact}` occupy between $4$ and $124$ bits ($4$ bits for `len` and $0$ to $15$ bytes for `value`) and have values in the inclusive range from $0$ to $2^{120} - 1$.
+
+Examples:
+
+```tact
+struct Scrooge {
+ // len: 0000, 4 bits (always)
+ // value: none!
+ // in total: 4 bits
+ a: Int as coins = 0; // 0000
+
+ // len: 0001, 4 bits
+ // value: 00000001, 8 bits
+ // in total: 12 bits
+ b: Int as coins = 1; // 0001 00000001
+
+ // len: 0010, 4 bits
+ // value: 00000001 00000010, 16 bits
+ // in total: 20 bits
+ c: Int as coins = 258; // 0010 00000001 00000010
+
+ // len: 1111, 4 bits
+ // value: hundred twenty 1's in binary
+ // in total: 124 bits
+ d: Int as coins = pow(2, 120) - 1; // hundred twenty 1's in binary
+}
+```
+
Read more on serialization here: [Compatibility with FunC](/book/func#convert-serialization)
+
## Operations
@@ -139,3 +178,7 @@ Here, `oneByte` is serialized as a [`uint8`](#serialization-types), which occupi
Therefore, be **very** careful with numbers and always double-check calculations when using serialization.
+
+[tlb]: https://docs.ton.org/develop/data-formats/tl-b-languagehttps://docs.ton.org/develop/data-formats/tl-b-language
+[tlb-builtin]: https://docs.ton.org/develop/data-formats/tl-b-language#built-in-types
+[varuint]: https://docs.ton.org/develop/data-formats/msg-tlb#varuinteger-n
diff --git a/pages/ref/core-cells.mdx b/pages/ref/core-cells.mdx
index 1ad46d3c..0ebad2eb 100644
--- a/pages/ref/core-cells.mdx
+++ b/pages/ref/core-cells.mdx
@@ -125,7 +125,7 @@ extends fun storeCoins(self: Builder, value: Int): Builder;
Extension function for the [`Builder{:tact}`][p].
-Stores (serializes) an unsigned [`Int{:tact}`][int] `value` in the range $0 .. 2^{120} − 1$ into the copy of the [`Builder{:tact}`][p]. The serialization of `value` consists of a 4-bit unsigned big-endian integer $l$, which is the smallest integer $l ≥ 0$, such that `value` $< 2^{8 * l}$, followed by an $8 * l$-bit unsigned big-endian representation of `value`. If `value` does not belong to the supported range, a range check exception is thrown. Returns that copy of the [`Builder{:tact}`][p].
+Stores (serializes) an unsigned [`Int{:tact}`][int] `value` in the range $0 .. 2^{120} − 1$ into the copy of the [`Builder{:tact}`][p]. The serialization of `value` consists of a $4$-bit unsigned big-endian integer $l$, which is the smallest integer $l ≥ 0$, such that `value` $< 2^{8 * l}$, followed by an $8 * l$-bit unsigned big-endian representation of `value`. If `value` does not belong to the supported range, a range check exception is thrown. Returns that copy of the [`Builder{:tact}`][p].
Attempts to store an out-of-bounds `value` throw an exception with [exit code 5](/book/exit-codes#5): `Integer out of expected range`.
@@ -138,6 +138,13 @@ let b: Builder = beginCell();
let fizz: Builder = b.storeCoins(42);
```
+
+
+ **Useful links:**\
+ [Special `coins` serialization type](/book/integers#serialization-coins)
+
+
+
## Builder.storeAddress
```tact
@@ -470,7 +477,7 @@ extends mutates fun loadCoins(self: Slice): Int;
Extension mutation function for the [`Slice{:tact}`][p].
-Loads and returns [serialized](#builderstorecoins) an unsigned [`Int{:tact}`][int] value in the range $0 .. 2^{120} - 1$ from the [`Slice{:tact}`][p]. This values usually represents the amount in [nanoToncoins](/book/integers#nanotoncoin).
+Loads and returns [serialized](#builderstorecoins) an unsigned [`Int{:tact}`][int] value in the range $0 .. 2^{120} - 1$ from the [`Slice{:tact}`][p]. This value usually represents the amount in [nanoToncoins](/book/integers#nanotoncoin).
Attempts to load such [`Int{:tact}`][int] when [`Slice{:tact}`][p] doesn't contain it throw an exception with [exit code 8](/book/exit-codes#8): `Cell overflow`.
@@ -483,6 +490,13 @@ let s: Slice = beginCell().storeCoins(42).asSlice();
let fizz: Int = s.loadCoins();
```
+
+
+ **Useful links:**\
+ [Special `coins` serialization type](/book/integers#serialization-coins)
+
+
+
## Slice.loadAddress
```tact