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

Commit

Permalink
feat/fix: update description of coins serialization format (#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
novusnota authored Jul 8, 2024
1 parent a4a29a7 commit f88202b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 19 deletions.
77 changes: 60 additions & 17 deletions pages/book/integers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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
}
```

<Callout>

Read more on serialization here: [Compatibility with FunC](/book/func#convert-serialization)

</Callout>

## Operations
Expand Down Expand Up @@ -139,3 +178,7 @@ Here, `oneByte` is serialized as a [`uint8`](#serialization-types), which occupi
<Callout type="warning" emoji="⚠️">
Therefore, be **very** careful with numbers and always double-check calculations when using serialization.
</Callout>

[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
18 changes: 16 additions & 2 deletions pages/ref/core-cells.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand All @@ -138,6 +138,13 @@ let b: Builder = beginCell();
let fizz: Builder = b.storeCoins(42);
```

<Callout>

**Useful links:**\
[Special `coins` serialization type](/book/integers#serialization-coins)

</Callout>

## Builder.storeAddress

```tact
Expand Down Expand Up @@ -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`.

Expand All @@ -483,6 +490,13 @@ let s: Slice = beginCell().storeCoins(42).asSlice();
let fizz: Int = s.loadCoins();
```

<Callout>

**Useful links:**\
[Special `coins` serialization type](/book/integers#serialization-coins)

</Callout>

## Slice.loadAddress

```tact
Expand Down

0 comments on commit f88202b

Please sign in to comment.