Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): explain how storage variables get updated #1311

Merged
merged 9 commits into from
Jan 21, 2025
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added signatures for map methods, such as `.get()`, `.exists()`, `.set()`, `.replace()`, `.replaceGet()`, `.del()`, `.isEmpty()`, `.deepEquals()`, `.asCell()`: PR [#1352](https://github.com/tact-lang/tact/pull/1352)
- Added a compilation-related page with the description of the compilation report: PR [#1309](https://github.com/tact-lang/tact/pull/1309), PR [#1387](https://github.com/tact-lang/tact/pull/1387)
- Documented `BaseTrait` and methods in stdlib code: PR [#1296](https://github.com/tact-lang/tact/pull/1296)
- Document how storage variables get updated in relation to the `init()` function: PR [#1311](https://github.com/tact-lang/tact/pull/1311)

### Release contributors

Expand Down
62 changes: 59 additions & 3 deletions docs/src/content/docs/book/contracts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ In addition to that, [`tact.config.json`](/book/config) may still be used in [Bl

### Persistent state variables {#variables}

Contracts can define state variables that persist between contract calls. Contracts in TON [pay rent](https://docs.ton.org/develop/smart-contracts/fees#storage-fee) in proportion to the amount of persistent space they consume, so [compact representations via serialization](/book/integers#serialization) are encouraged.
Contracts can define state variables that persist between contract calls, and thus commonly referred to as _storage_ variables. Contracts in TON [pay rent](https://docs.ton.org/develop/smart-contracts/fees#storage-fee) in proportion to the amount of persistent space they consume, so [compact representations via serialization](/book/integers#serialization) are encouraged.

```tact
contract Example {
Expand All @@ -149,11 +149,62 @@ contract Example {
}
```

State variables must have a default value or initialized in [`init(){:tact}`](#init-function) function, that runs on deployment of the contract. The only exception is persistent state variables of type [`map<K, V>{:tact}`](/book/maps) since they are initialized empty by default.
State variables must have a default value or be initialized in the [`init(){:tact}`](#init-function) function that runs once on deployment of the contract. The only exception are persistent state variables of type [`map<K, V>{:tact}`](/book/maps), since they are initialized empty by default.

The default value of state variables is assigned before any values could be assigned in the [`init(){:tact}`](#init-function) function.

```tact
contract Example {
// persistent state variables
var1: Int = 0; // initialized with default value 0

// constructor function
init() {
self.var1 = 42; // overrides the default to 42
}
}
```

As the contract state is updated at the very end of the [compute phase][compute] of the transaction, intermediate assignments of [`Int{:tact}`][int] values that exceed the limits specified by [serialization formats](/book/integers#serialization) won't fail immediately. Instead, such assignments would cause an [exit code 5](/book/exit-codes#5) only after all statements have been executed.
novusnota marked this conversation as resolved.
Show resolved Hide resolved

This is to be expected because the integers in the temporary [TVM][tvm] memory, which is used to process the [compute phase][compute], always have $257$ bits and are capable of holding values in the inclusive range from $-2^{256}$ to $2^{256} - 1.$

```tact
contract DeRanged {
// Persistent state variables
var: Int as uint8; // cannot store values outside the 0-255 range

init() {
self.var = -1; // this won't fail immediately
self.var = 500; // and that won't fail right away either

} // only here, at the end of the compute phase,
// would there be an error thrown with an exit code 5: Integer out of range
}
```

In the end, this means that you cannot rely on intermediate out-of-bounds checks, because there are none at [TVM][tvm] runtime, and only the last assignment of each state variable is used to update its persistent state value.

```tact
contract Zero {
// Persistent state variables
var: Int as uint8; // cannot store values outside the 0-255 range
var2: Int as uint4; // cannot store values outside the 0-15 range

init() {
self.var = -1; // this won't fail
self.var = 0; // and this is in the range of `uint8`

self.var2 = -1; // this won't fail
self.var2 = 15; // and this is in the range of `uint4`

} // no errors, and now `self.var` is 0 and `self.var2` is 15
}
```

:::note

Note, that Tact supports local, non-persistent-state variables too, see: [Variable declaration](/book/statements#let).
Tact supports local, non-persistent-state variables too, see: [Variable declaration](/book/statements#let).

:::

Expand Down Expand Up @@ -203,10 +254,12 @@ contract Example {
// persistent state variables
var1: Int = 0; // initialized with default value 0
var2: Int; // must be initialized in the init() function
var3: Int = 7; // initialized with default value 7

// constructor function
init() {
self.var2 = 42;
self.var3 = 32; // overrides the default to 32
}
}
```
Expand Down Expand Up @@ -354,7 +407,10 @@ contract Functions {
:::

[p]: /book/types#primitive-types
[int]: /book/integers
[trait]: /book/types#traits

[compute]: https://docs.ton.org/learn/tvm-instructions/tvm-overview#compute-phase
[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview
[bp]: https://github.com/ton-org/blueprint
[bp-config]: https://github.com/ton-org/blueprint/tree/main?tab=readme-ov-file#configuration
9 changes: 9 additions & 0 deletions docs/src/content/docs/book/integers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ Motivation is very simple:
* Storing $1000$ $257$-bit integers in state [costs](https://docs.ton.org/develop/smart-contracts/fees#how-to-calculate-fees) about $0.184$ TON per year.
* Storing $1000$ $32$-bit integers only costs $0.023$ TON per year by comparison.

:::note

Serialization limits apply only to the contract state between transactions and are **not** imposed on the temporary [TVM][tvm] memory, which operates only on $257$-bit integers.

Attempts to assign out-of-bounds values will result in [exit code 5](/book/exit-codes#5) being thrown at the very end of the [compute phase](https://docs.ton.org/learn/tvm-instructions/tvm-overview#compute-phase): `Integer out of range`.

:::

### Common serialization types

Name | [TL-B][tlb] | Inclusive range | Space taken
Expand Down Expand Up @@ -195,6 +203,7 @@ Here, `oneByte` is serialized as a [`uint8`](#common-serialization-types), which
Therefore, be **very** careful with numbers and always double-check calculations when using serialization.
:::

[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview
[tlb]: https://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
Loading