diff --git a/CHANGELOG.md b/CHANGELOG.md index 00eae1c7a..8ae6ff517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/docs/src/content/docs/book/contracts.mdx b/docs/src/content/docs/book/contracts.mdx index 7aae8237b..7130a39e3 100644 --- a/docs/src/content/docs/book/contracts.mdx +++ b/docs/src/content/docs/book/contracts.mdx @@ -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 { @@ -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{: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{: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. + +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). ::: @@ -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 } } ``` @@ -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 diff --git a/docs/src/content/docs/book/integers.mdx b/docs/src/content/docs/book/integers.mdx index 2109ebfc4..55a5c11ed 100644 --- a/docs/src/content/docs/book/integers.mdx +++ b/docs/src/content/docs/book/integers.mdx @@ -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 @@ -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