From 9341285a787ca2770ce8b3d1857a4f3e2cfbdd33 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 19 Mar 2024 08:42:11 +0100 Subject: [PATCH] feat/fix: Large Book Update (#118) * feat: Operators Closes #59 Closes #62 Closes #64 * fix: Edit exit-codes (not a re-write yet) * Clean-up of the formatting * Move to the Book Closes #107 * fix: Small clean-up in "Compatibility with FunC" Closes #103 --- pages/book/_meta.js | 31 +- pages/book/composite-types.mdx | 6 +- pages/book/cookbook.mdx | 122 +++---- pages/book/exit-codes.mdx | 304 +++++++++++++++++ pages/book/func.mdx | 107 +++--- pages/book/index.mdx | 4 +- pages/book/integers.mdx | 10 +- pages/book/operators.mdx | 541 ++++++++++++++++++++++++++++++ pages/book/statements.mdx | 184 ++++++---- pages/book/types.mdx | 39 ++- pages/index.mdx | 2 +- pages/language/ref/_meta.js | 1 - pages/language/ref/exit-codes.mdx | 307 ----------------- 13 files changed, 1153 insertions(+), 505 deletions(-) create mode 100644 pages/book/exit-codes.mdx create mode 100644 pages/book/operators.mdx delete mode 100644 pages/language/ref/exit-codes.mdx diff --git a/pages/book/_meta.js b/pages/book/_meta.js index eaf2f54a..384fa907 100644 --- a/pages/book/_meta.js +++ b/pages/book/_meta.js @@ -3,28 +3,43 @@ export default { guides: 'Guides', cs: 'Cheatsheets', cookbook: 'Cookbook', - '--': { + '-- 1': { type: 'separator', + title: 'Fundamentals of Tact', }, types: 'Type system overview', integers: 'Integers', - functions: 'Functions', + operators: 'Operators', statements: 'Statements', constants: 'Constants', + functions: 'Functions', 'composite-types': 'Composite types', - receive: 'Receive Messages', - bounced: 'Bounced Messages', - external: 'External Messages', + // <- a place for https://github.com/tact-lang/tact-docs/issues/115 + '-- 2': { + type: 'separator', + title: 'Communication', + }, + // <- NOTE + // potential place for a rather short overview page describing asyncronous & actor-model nature + // of TON Blockchain with respect to Tact and exchanging messages with it + receive: 'Receive messages', + bounced: 'Bounced messages', + external: 'External messages', lifecycle: 'Message lifecycle', send: 'Sending messages', 'message-mode': 'Message mode', - deploy: 'Deploy contracts', + '-- 3': { + type: 'separator', + title: 'Going places', + }, + deploy: 'Deployment', debug: 'Debugging', upgrades: 'Contract upgrades', import: 'Importing code', - masterchain: 'Masterchain', - func: 'Compatibility with Func', config: 'Configuration', + masterchain: 'Using masterchain', + func: 'Compatibility with FunC', + 'exit-codes': 'Exit codes', programmatic: 'Programmatic API', '-- Community': { type: 'separator', diff --git a/pages/book/composite-types.mdx b/pages/book/composite-types.mdx index 37ed71f8..e898eaeb 100644 --- a/pages/book/composite-types.mdx +++ b/pages/book/composite-types.mdx @@ -112,11 +112,11 @@ As it was mentioned in [type system overview](/book/types), most [primitive type [Variables](/book/statements#variable-declaration) or fields of [Structs](#structs) and [Messages](#messages) that can hold `null{:tact}` are called "optionals". They're useful to reduce state size when the variable isn't necesserily used. -You can make any variable an optional by adding a question mark (`?`) after its type declaration. The only exceptions are [`map<>{:tact}`](/book/types#maps) and [`bounced<>{:tact}`](/book/bounced#bounced-messages-in-tact), where you can't make them, inner key/value type (in case of a map) or the inner [Message](#messages) (in case of a bounced) optional. +You can make any variable or a field an optional by adding a question mark (`?{:tact}`) after its type declaration. The only exceptions are [`map<>{:tact}`](/book/types#maps) and [`bounced<>{:tact}`](/book/bounced#bounced-messages-in-tact), where you can't make them, inner key/value type (in case of a map) or the inner [Message](#messages) (in case of a bounced) optional. -Optional variables that are not defined hold the `null{:tact}` value by default. You cannot access them without checking for `null{:tact}` first. But if you're certain the optional variable is not `null{:tact}`, use the non-null assertion operator (`!!`, also called double-bang or double exclamation mark operator) to access its value. +Optional variables or optional fields that are not defined hold the `null{:tact}` value by default. You cannot access them without checking for `null{:tact}` first. But if you're certain they are not `null{:tact}` at a given moment, use the non-null assertion operator (`!!{:tact}`) to access their value. -Trying to access the value of an optional variable without using `!!` or checking for `null{:tact}` beforehand will result in a compilation error. +Trying to access the value of an optional variable or an optional field without using `!!{:tact}` or without checking for `null{:tact}` beforehand will result in a compilation error. Example of optionals: diff --git a/pages/book/cookbook.mdx b/pages/book/cookbook.mdx index 0b9e908c..5b297feb 100644 --- a/pages/book/cookbook.mdx +++ b/pages/book/cookbook.mdx @@ -19,9 +19,9 @@ contract HelloWorld { } ``` -### How to write an 'if' statement in Tact +### How to write an `if` statement in Tact -Tact supports `if` statements in a similar syntax to most programming languages. The condition of the statement can be any boolean expression. +Tact supports `if...else{:tact}` statements in a similar syntax to most programming languages. The condition of the statement can be any boolean expression. ```tact let value: Int = 9001; @@ -47,9 +47,9 @@ if (value > 9000) { ## Loops -### How to write a repeat loop +### How to write a `repeat` loop -Please make sure the input number for the [repeat](/book/statements#repeat-loop) loop statement is within the range of an int32 data type, as an exception will be thrown otherwise. +Please make sure the input number for the [`repeat{:tact}`](/book/statements#repeat-loop) loop statement is within the range of an $32$-bit [`Int{:tact}`](/book/integers) data type, as an exception will be thrown otherwise. ```tact let sum: Int = 0; @@ -64,12 +64,12 @@ repeat (10) { // repeat exactly 10 times **Useful links:**\ - [`repeat` loop in the Book](/book/statements#repeat-loop)\ + [`repeat{:tact}` loop in the Book](/book/statements#repeat-loop)\ [Loops in Tact-By-Example](https://tact-by-example.org/04-loops) -### How to write a while loop +### How to write a `while` loop ```tact let i: Int = 0; @@ -82,15 +82,15 @@ while (i < 10) { **Useful links:**\ - [`while` loop in the Book](/book/statements#while-loop)\ + [`while{:tact}` loop in the Book](/book/statements#while-loop)\ [Loops in Tact-By-Example](https://tact-by-example.org/04-loops) -### How to write a do until loop +### How to write a `do...until` loop [#how-to-write-a-do-until-loop] -When we need the cycle to run at least once, we use the [do-until](/book/statements#until-loop) loop. +When we need the cycle to run at least once, we use the [`do...until{:tact}`](/book/statements#do-until-loop) loop. ```tact let num: Int; // A variable to store the random number @@ -106,7 +106,7 @@ dump("The loop is over!"); **Useful links:**\ - [`do-until` loop in the Book](/book/statements#until-loop)\ + [`do...until{:tact}` loop in the Book](/book/statements#do-until-loop)\ [`random()` in Language→Reference](/language/ref/random#random)\ [Loops in Tact-By-Example](https://tact-by-example.org/04-loops) @@ -115,20 +115,20 @@ dump("The loop is over!"); ## Map ```tact -// Create empty map +// Create an empty map let m: map = emptyMap(); -// Add key/value to the map +// Add the key/value pair to the map m.set(1, "a"); -// Get value by key from the map +// Get the value by the key from the map let first: String? = m.get(1); -// Check key is exists +// Check if the key exists if (first == null) { // do something... } else { - // Cast value if key exists + // Cast value if the key exists let firstStr: String = first!!; // do something... } @@ -137,20 +137,20 @@ if (first == null) { **Useful links:**\ - [Map type in the Book](/book/types#maps) + [`map{:tact}` type in the Book](/book/types#maps) -## Slice +## `Slice` -### How to determine if slice is empty +### How to determine if a `Slice` is empty -A Slice is considered *empty* if it has no stored `data` **and** no stored `references`. +A [`Slice{:tact}`](/book/types#primitive-types) is considered _empty_ if it has no stored `data` **and** no stored `references`. -Use `empty()` method to check if a Slice is empty. +Use `empty(){:tact}` method to check if a [`Slice{:tact}`](/book/types#primitive-types) is empty. ```tact -// Create an empty slice with no data and no refs +// Create an empty Slice with no data and no refs let empty_slice: Slice = emptyCell().asSlice(); // Returns `true`, because `empty_slice` doesn't have any data or refs empty_slice.empty(); @@ -178,7 +178,7 @@ let slice_with_data_and_refs: Slice = beginCell(). slice_with_data_and_refs.empty(); ``` -### How to determine if a slice has no refs (but may have bits) +### How to determine if a `Slice` has no refs (but may have bits) ```tact let slice_with_data: Slice = beginCell(). @@ -188,7 +188,7 @@ let refsCount: Int = slice_with_data.refs(); // 0 let hasNoRefs: Bool = slice_with_data.refsEmpty(); // true ``` -### How to determine if a Slice has no data (no bits, but may have refs) +### How to determine if a `Slice` has no data (no bits, but may have refs) ```tact let slice_with_data: Slice = beginCell(). @@ -198,7 +198,7 @@ let bitsCount: Int = slice_with_data.bits(); // 0 let hasNoData: Bool = slice_with_data.dataEmpty(); // true ``` -### How to determine if Slices are equal +### How to determine if two Slices are equal Direct comparison: @@ -232,15 +232,15 @@ dump(result) // true; **Useful links:**\ - [`String.asSlice()` in Language→Reference](/language/ref/strings#stringasslice) + [`String.asSlice(){:tact}` in Language→Reference](/language/ref/strings#stringasslice) -## Cell +## `Cell` -### How to determine if a Cell is empty +### How to determine if a `Cell` is empty -To check if there is any data in the Cell, we should first convert it to the Slice. If we are only interested in having bits, we should use `dataEmpty()`, if only refs - `refsEmpty()`. In case we want to check for the presence of any data, regardless of whether it is a bit or ref, we need to use `empty()`. +To check if there is any data in the [`Cell{:tact}`](/book/types#primitive-types), we should first convert it to the [`Slice{:tact}`](/book/types#primitive-types). If we are only interested in having bits, we should use `dataEmpty(){:tact}`, if only refs - `refsEmpty(){:tact}`. In case we want to check for the presence of any data, regardless of whether it is a bit or ref, we need to use `empty(){:tact}`. ```tact // Create an empty cell with no data and no refs @@ -264,16 +264,16 @@ slice.empty(); **Useful links:**\ - [`empty()` in Language→Reference](/language/ref/cells#sliceempty)\ - [`dataEmpty()` in Language→Reference](/language/ref/cells#slicedataempty)\ - [`refsEmpty()` in Language→Reference](/language/ref/cells#slicerefsempty)\ - [`emptyCell()` in Language→Reference](/language/ref/cells#emptycell)\ - [`beginCell()` in Language→Reference](/language/ref/cells#begincell)\ - [`endCell()` in Language→Reference](/language/ref/cells#builderendcell) + [`empty(){:tact}` in Language→Reference](/language/ref/cells#sliceempty)\ + [`dataEmpty(){:tact}` in Language→Reference](/language/ref/cells#slicedataempty)\ + [`refsEmpty(){:tact}` in Language→Reference](/language/ref/cells#slicerefsempty)\ + [`emptyCell(){:tact}` in Language→Reference](/language/ref/cells#emptycell)\ + [`beginCell(){:tact}` in Language→Reference](/language/ref/cells#begincell)\ + [`endCell(){:tact}` in Language→Reference](/language/ref/cells#builderendcell) -### How to determine if Cells are equal +### How to determine if two Cells are equal Direct comparison: @@ -310,7 +310,7 @@ let areCellsNotEqual: Bool = a.hash() != b.hash(); // false **Useful links:**\ - [`Cell.hash` in Language→Reference](/language/ref/cells#cellhash) + [`Cell.hash(){:tact}` in Language→Reference](/language/ref/cells#cellhash) @@ -328,7 +328,7 @@ send(SendParameters{ ### How to send a message with the entire balance -If we need to send the whole balance of the smart contract, then we should use the `SendRemainingBalance` send mode. Alternatively, we can use `mode 128`, which has the same meaning. +If we need to send the whole balance of the smart contract, then we should use the `SendRemainingBalance{:tact}` send mode. Alternatively, we can use `mode: 128{:tact}`, which has the same meaning. ```tact send(SendParameters{ @@ -347,7 +347,7 @@ send(SendParameters{ -### How to convert String to Int +### How to convert a `String` to an `Int` ```tact // Dangerously casts string as slice for parsing. Use it only if you know what you are doing. @@ -365,7 +365,7 @@ while (!string.empty()) { // A loop until slice has bytes dump(number); ``` -### How to convert Int to String +### How to convert an `Int` to a `String` ```tact let number: Int = 261119911; @@ -388,17 +388,17 @@ dump(coinsString); // "0.261119911" **Useful links:**\ - [`Int.toString` in Language→Reference](/language/ref/strings#inttostring)\ - [`Int.toFloatString` in Language→Reference](/language/ref/strings#inttofloatstring)\ - [`Int.toCoinsString` in Language→Reference](/language/ref/strings#inttocoinsstring) + [`Int.toString(){:tact}` in Language→Reference](/language/ref/strings#inttostring)\ + [`Int.toFloatString(){:tact}` in Language→Reference](/language/ref/strings#inttofloatstring)\ + [`Int.toCoinsString(){:tact}` in Language→Reference](/language/ref/strings#inttocoinsstring) -### How to get current time +### How to get the current time -Use the `now()` method to obtain the current standard [Unix time](https://en.wikipedia.org/wiki/Unix_time). +Use the `now(){:tact}` method to obtain the current standard [Unix time](https://en.wikipedia.org/wiki/Unix_time). -If you need to store the time in state or encode it in a message, use `Int as uint32`. +If you need to store the time in state or encode it in a message, use the following [serialization](/book/integers#serialization): `Int as uint32{:tact}`. ```tact let currentTime: Int = now(); @@ -411,7 +411,7 @@ if (currentTime > 1672080143) { **Useful links:**\ - [`now()` in Language→Reference](/language/ref/common#now)\ + [`now(){:tact}` in Language→Reference](/language/ref/common#now)\ ["Current Time" in Tact-By-Example](https://tact-by-example.org/04-current-time) @@ -432,14 +432,14 @@ number = random(1, 12); **Useful links:**\ - [`randomInt()` in Language→Reference](/language/ref/random#randomInt)\ - [`random()` in Language→Reference](/language/ref/random#random) + [`randomInt(){:tact}` in Language→Reference](/language/ref/random#randomInt)\ + [`random(){:tact}` in Language→Reference](/language/ref/random#random) ### How to throw errors -The throw function in a contract is useful when we don't know how often to perform a specific action. +The `throw(){:tact}` function in a contract is useful when we don't know how often to perform a specific action. It allows intentional exception or error handling, which leads to the termination of the current transaction and reverts any state changes made during that transaction. @@ -459,14 +459,14 @@ nativeThrowUnless(39, number == 198); **Useful links:**\ - [`throw()` in Language→Reference](/language/ref/advanced#throw)\ + [`throw(){:tact}` in Language→Reference](/language/ref/advanced#throw)\ [Errors in Tact-By-Example](https://tact-by-example.org/03-errors) ### How to send a message with a long text comment -If we need to send a message with a lengthy text comment, we should create a String that consists of more than `127` characters. To do this, we can utilize the StringBuilder primitive type and its methods called `beginComment()` and `append()`. Prior to sending, we should convert this string into a cell using the `toCell()` method. +If we need to send a message with a lengthy text comment, we should create a [`String{:tact}`](/book/types#primitive-types) that consists of more than $127$ characters. To do this, we can utilize the [`StringBuilder{:tact}`](/book/types#primitive-types) primitive type and its methods called `beginComment(){:tact}` and `append(){:tact}`. Prior to sending, we should convert this string into a cell using the `toCell(){:tact}` method. ```tact let comment: StringBuilder = beginComment(); @@ -485,17 +485,17 @@ send(SendParameters{ **Useful links:**\ - ["Sending messages" in docs](/language/guides/send#send-message)\ - [`StringBuilder` in docs](/language/guides/types#primitive-types)\ - [`Cell` in Language→Reference](/language/ref/cells) + ["Sending messages" in the Book](/book/send#send-message)\ + [`StringBuilder{:tact}` in the Book](/book/types#primitive-types)\ + [`Cell{:tact}` in Language→Reference](/language/ref/cells) -### How to calculate NFT item address by its index +### How to calculate the NFT item `Address` by its index -For Tacts' example, you should have the Tact code of the NFT item contract, placed in the same file. You can use the function as you wish, both inside and outside of a contract. +For Tact's example, you should have the Tact code of the NFT item contract, placed in the same file. You can use the function as you wish, both inside and outside of a contract. -For FunCs' example, you should have the code of item contract as a Cell. The function can be used outside of a contract by changing `self.nftItemCode` with a preassigned `nftItemCode`. +For FunC's example, you should have the code of item contract as a [`Cell{:tact}`](/book/types#primitive-types). The function can be used outside of a contract by changing `self.nftItemCode{:tact}` with a preassigned `nftItemCode`. Tact: @@ -511,7 +511,7 @@ let itemInit: StateInit = self.getNftItemInit(itemIndex); let itemAddress: Address = contractAddress(nft_init); ``` -FunC (may also vary depending on collection's `deploy_item()` function): +FunC (may also vary depending on collection's `deploy_item(){:tact}` function): ```func fun getNftItemInit(item_index: Int): StateInit { @@ -530,9 +530,9 @@ let itemAddress: Address = contractAddress(self.getNftItemInit(itemIndex)); **Useful links:**\ - [`initOf()` in the Book](/book/statements#initof)\ - [`contractAddress()` in Language→Reference](/language/ref/common#contractaddress)\ + [`initOf{:tact}` in the Book](/book/expressions#initof)\ + [`contractAddress(){:tact}` in Language→Reference](/language/ref/common#contractaddress)\ [Tact collection and item contracts example](https://github.com/howardpen9/nft-template-in-tact/blob/tutorial/sources/contract.tact)\ [FunC collection and item contracts example](https://github.com/Cosmodude/TAP/tree/main/contracts) - + \ No newline at end of file diff --git a/pages/book/exit-codes.mdx b/pages/book/exit-codes.mdx new file mode 100644 index 00000000..1afdcb9d --- /dev/null +++ b/pages/book/exit-codes.mdx @@ -0,0 +1,304 @@ +# Exit Codes + +import { Callout } from 'nextra/components' + + + THis page is under re-construction as per [#106](https://github.com/tact-lang/tact-docs/issues/106). All anchor links (`#`) may change in the future! + + +An exit code is a $16$-bit unsigned integer which ranges between $0$ to $65535$ (or $2_{16} - 1$). + +Codes from $0$ to $127$ are allocated for FunC (TVM), $128$ to $255$ for Tact. The range from $256$ to $65535$ is free for developer-defined exit codes. + +List of pre-allocated exit codes: + +Exit Code | phase | Description +:--------- | :----------------- | -------------------------------------------------------------------------------------------------------------------------- +$0$ | [Compute phase][c] | Standard successful execution exit code +$2$ | [Compute phase][c] | Stack underflow. Last op-code consumed more elements than there are on the stacks +$3$ | [Compute phase][c] | Stack overflow. More values have been stored on a stack than allowed by this version of TVM +$4$ | [Compute phase][c] | Integer overflow. Integer does not fit into −2256 ≤ x < 2256 or a division by zero has occurred +$5$ | [Compute phase][c] | Integer out of expected range +$6$ | [Compute phase][c] | Invalid opcode. Instruction is unknown in the current TVM version +$7$ | [Compute phase][c] | Type check error. An argument to a primitive is of an incorrect value type +$8$ | [Compute phase][c] | Cell overflow. Writing to builder is not possible since after operation there would be more than 1023 bits or 4 references +$9$ | [Compute phase][c] | Cell underflow. Read from slice primitive tried to read more bits or references than there are +$10$ | [Compute phase][c] | Dictionary error. Error during manipulation with dictionary (hashmaps) +$13$ | [Compute phase][c] | Out of gas error. Thrown by TVM when the remaining gas becomes negative +$-14$ | [Compute phase][c] | It means out of gas error, same as $13$. Negative, because it cannot be faked +$32$ | [Action phase][a] | Action list is invalid. Set during action phase if c5 register after execution contains unparsable object +$34$ | [Action phase][a] | Action is invalid or not supported. Set during action phase if current action cannot be applied +$37$ | [Action phase][a] | Not enough TON. Message sends too much TON (or there is not enough TON after deducting fees) +$38$ | [Action phase][a] | Not enough extra-currencies +$128$ | Tact (Compiler) | Null reference exception — compiler expects an integer or cell but a null value has been passed +$129$ | Tact (Compiler) | Invalid serialization prefix — if there is any inconsistency with the previous op-code check, this exit code will be thrown +$130$ | Tact (Compiler) | Invalid incoming message — no suitable operation is found +$131$ | Tact (Compiler) | Constraints error +$132$ | Tact (Compiler) | Access denied — someone other than the owner sent a message to the contract +$133$ | Tact (Compiler) | Contract stopped — a message has been sent to a stopped contract +$134$ | Tact (Compiler) | Invalid argument — invalid Base64 string +$135$ | Tact (Compiler) | Code of a contract was not found — false flag for a dictionary call +$136$ | Tact (Compiler) | Invalid Address — Non $267$-bit Address or invalid chain id (other than 0 or -1) +$137$ | Tact (Compiler) | Masterchain support is not enabled for this contract + +[c]: https://docs.ton.org/learn/tvm-instructions/tvm-overview#compute-phase +[a]: https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases + +Q: **Where to observe the list of all auto-generated exit codes in your project?**\ +A: The Tact Compiler collects all exit codes at the end of a *.md file and you can track them in the directory along +the path "./ProjectFolder/build/ProjectName/tact_ProjectName.md" + +Q: **How to observe a thrown exit code?**\ +A: In Tact, it's not wise to print the transactions to see the results because they are not easy to read. If you want to see the exit code of a transaction, +use the below template in your Typescript local tests: + +```typescript +const sender = await blockchain.treasury('sender'); +const result = await contractName.send(sender.getSender(), { value: toNano('0.05'), }, { transactionData }); + +expect(result.transactions).toHaveTransaction( + { from: sender.address, to: contractName.address, exitCode: YOUR_DESIRED_EXIT_CODE } +); +``` +* First line defines the sender. +* Second line sends the transaction. +* In the third line, you check if the result has a transaction from sender to your contract with your desired exit code. + +## Compute phase + +### $0$: Successful execution [#0] + +This exit code means that the Compute phase of the transaction was completed successfully. + +### $4$: Integer overflow [#4] + +In TVM, integer can be in the range -2256 < x < 2256. +If the value during the calculation went beyond this range, then 4 exit code is thrown. + +Example: + +```tact +self.id = 1; // force not to ignore it by using storage variables +repeat(256) { + self.id = 2 * self.id; +} +``` + +### $5$: Integer out of expected range [#5] + +If the integer value went beyond the expected range, then 5 exit code is thrown. +For example, if a negative value was used in the .store_uint() function. In Tact, there are some other new situations such as:\ +1- As you know, you can define more limited integers in Tact (integers with less than 257 bits). +If you try to store a number in this kind of integers and the number doesn't fit to this limited range, you will face this exit code.\ +2- according to ```storeUint(self: Builder, value: Int, bits: Int)``` function, it's not possible to use ```storeUint(0, 257)``` because ```0 ≤ bits ≤ 256```. + +Example: + +```tact +// option 1 -> id: Int as uint32 +self.id = 1; // force not to ignore it by using storage variables +repeat(32) { + self.id = 2 * self.id; +} + +// option 2 -> according to storeUint(self: Builder, value: Int, bits: Int) function, it's not possible to use storeUint(0, 1024) because 0 ≤ bits ≤ 256 +let s: Slice = beginCell().storeUint(0, 257).asSlice(); +``` + +### $8$: Cell overflow [#8] +A cell has the capacity to store 1023 bits of data and 4 references to other cells. +If you try to write more than 1023 bits or more than 4 references, 8 exit code is thrown. + +Example: + +```tact +// according to storeUint(self: Builder, value: Int, bits: Int) function, it's not possible to use storeUint(0, 1024) because 0 ≤ bits ≤ 256 +let s: Slice = beginCell().storeUint(0, 256).storeUint(0, 256).storeUint(0, 256).storeUint(0, 256).asSlice(); +``` + +### $9$: Cell underflow [#9] + +If you try to read more data from a slice than it contains, then 9 exit code is thrown. + +Example: + +```tact +let s: Slice = emptySlice(); +self.id = s.loadUint(1); // force not to ignore it by using storage variables +``` + +### $13$: Out of gas error [#13] + +If there isn't enough TON to handle compute phase, this error is thrown. + +During processing, the NOT operation is applied to this value, which changes this value to -14. This is done so that this exit code cannot be faked using the throw function, since all such functions accept only positive values for the exit code as it was discussed previously. + +Example: + +```tact +repeat(10000) { + self.id = self.id + 1; +} +``` + +## Action phase + +### $34$: Action is invalid or not supported [#34] + +This exit code is responsible for most of the errors when working with actions: invalid message, incorrect action, and so on. + +Example: + +```tact +nativeSendMessage(emptyCell(), 0); +``` + +### $37$: Not enough TON [#37] + +It means that there isn't enough TON to send the specified amount of it. + +Example: + +```tact +send(SendParameters{to: context().sender, value: ton("10")}); +``` + +## Tact (Compiler) + +### $130$: Invalid incoming message [#130] + +When you send a message to a contract, the first 32 bits of message body is the op code. It determines the operation that must be done. +In FunC, if no op code is found, 0xffff will be thrown. In Tact, 130 exit code will be thrown. + +Example: + +1. First, define an empty contract like below: + +```tact +contract Fireworks { + init() {} +} +``` + +2. Then, send a message to this contract. Because no suitable operation is found, you will get this exit code. + +### $132$: Access denied [#132] + +First, you should import and inherit from Ownable Trait. After it, your contract will have an owner. +You can ask for a check by calling ```self.requireOwner();``` in your functions. It will ensure that only the owner can send message to your contract. + +Example: + +```tact +import "@stdlib/deploy"; +import "@stdlib/ownable"; + +message FakeLaunch { + +} + +contract Fireworks with Deployable, Ownable { + owner: Address; + + init(){ + self.owner = sender(); + } + + receive(msg: FakeLaunch){ + self.requireOwner(); + } +} + +fun requireOwner() { + nativeThrowUnless(132, sender() == self.owner); +} +``` + +### $133$: Contract stopped [#133] + +The stoppable trait allows to stop the contract. +If you send a message to a stopped contract, and the contract asks for a check by running ```self.requireNotStopped();```, this exit code will be thrown. +In the current version of Tact, 40368 exit code will be thrown instead of 133. + +Example: + +```tact +import "@stdlib/deploy"; +import "@stdlib/ownable"; +import "@stdlib/stoppable"; + +message FakeLaunch {} + +contract Fireworks with Deployable, Ownable, Stoppable { + owner: Address; + stopped: Bool; + + init() { + self.owner = sender(); + self.stopped = false; + } + + receive(msg: FakeLaunch) { + self.stopped = true; + self.requireNotStopped(); + } +} + +fun requireNotStopped() { + require(!self.stopped, "Contract stopped"); +} +``` + +### $134$: Invalid argument [#134] + +This will be thrown by the below FunC function(in the last part of a bunch of if conditions). This function reads something from Base64. + +If the input characters don't fit into base64 chars, you will encounter this exit code. + +Example: + +```tact +let code: Slice = beginCell().storeUint(0, 8).asSlice().fromBase64(); +// 0 is not a valid ASCII code so it cannot be converted to Base64 +``` + +### $135$: Code of a contract was not found [#135] + +It will check the return flag of a search on the dictionary keys. + +Example: + +```tact +// copy & paste the below line in wrapper file(../build/ContractName/tact_ContractName.ts) instead of the second line of ContractName_init() function - this is a dictionary containing another smart contract code which leads to 135 exit code +// const __system = Cell.fromBase64('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'); +let ctx: Context = context(); +let fireworks_init: StateInit = initOf Fireworks(0); +``` + +### $136$: Invalid address [#136] + +In TON, all addresses are 267 bits. If you violate this rule, you will face this exit code. + +Currently, TON only supports two chain id. 0 for basechain and -1 for masterchain. If you address isn't from basechain, 136 exit code will be thrown. + +Example: + +```tact +// fun newAddress(chain: Int, hash: Int): Address; +// creates a new address from chain and hash values. +let zeroAddress: Address = newAddress(1, 0); // invalid chain zero address +``` + +### $137$: Masterchain support is not enabled for this contract [#137] + +Currently, TON only supports two chain id. 0 for basechain and -1 for masterchain. + +Tact only supports basechain and if you address is from masterchain, 137 exit code will be thrown. + +Example: + +```tact +// fun newAddress(chain: Int, hash: Int): Address; +// creates a new address from chain and hash values. +let zeroAddress: Address = newAddress(-1, 0); // masterchain zero address +``` \ No newline at end of file diff --git a/pages/book/func.mdx b/pages/book/func.mdx index 3b41d620..c44f8fb5 100644 --- a/pages/book/func.mdx +++ b/pages/book/func.mdx @@ -1,18 +1,22 @@ -# Compatibility with func +# Compatibility with FunC -Tact itself compiles to `func` and maps Tact entities directly to various `func` and `tl-b` types. +Tact itself compiles to FunC and maps all its entities directly to various FunC and TL-B types. ## Convert types -Primitive types in `tact` are directly mapped to `func` one. All rules about copying variables are the same. One of the big differences is that there are no visible mutation operators in `tact` and most `Slice` operations mutate variables in place. +[Primitive types](/book/types#primitive-types) in Tact are directly mapped to FunC ones. + +All rules about copying variables are the same. One of the big differences is that there are no visible mutation operators in Tact and most [`Slice{:tact}`](/book/types#primitive-types) operations mutate variables in place. ## Convert serialization -Tact structs and messages serialization is automatic unlike `func` where you need to define serialization logic manually. To build a `tact` type that serializes to specific `tl-b`. +Serialization of [Structs](/book/composite-types#structs) and [Messages](/book/composite-types#messages) in Tact is automatic, unlike FunC where you need to define serialization logic manually. + +Tact's auto-layout algorithm is greedy. This means that it takes the next variable, calculates its size, and tries to fit it into a current cell. If it doesn't fit, it creates a new cell and continues. All inner structs for auto-layout are flattened before allocation. -The tact auto-layout algorithm is greedy. This means that it takes the next variable, calculates its size, and tries to fit it into a current cell. If it doesn't fit, it creates a new cell and continues. All inner structs for auto-layout are flattened before allocation. +All optional types are serialized as `Maybe` in TL-B, except for [`Address{:tact}`](/book/types#primitive-types). -All, except `Address`, optional types are serialized as `Maybe` in `tl-b`. There is no support for `Either` since it does not define what to pick during serialization in some cases. +There is no support for `Either` since it does not define what to pick during serialization in some cases. ### Examples @@ -98,7 +102,8 @@ struct SomeValue { Tact generates a unique `op` for every received typed message, but it can be overwritten. -Code in `func`: +The following code in FunC: + ```func () recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure { ;; incoming message code... @@ -115,7 +120,9 @@ Code in `func`: } ``` -Will be in `tact`: + +Becomes this in Tact: + ```tact message MessageWithGeneratedOp { amount: Int as uint32; @@ -137,46 +144,47 @@ contract Contract { } } - ``` -## Convert get-methods +## Convert `get`-methods -You can express everything except `list-style-lists` in Tact that would be compatible with `func`'s `get`-methods. +You can express everything except `list-style-lists` in Tact that would be compatible with FunC's `get`-methods. ### Primitive return type -if `get-method` returns a primitive in `func`, you can implement it the same way in the `tact`. -Code in `func`: +If a `get`-method returns a primitive in FunC, you can implement it the same way in Tact. + +The following code in FunC: + ```func int seqno() method_id { return 0; } ``` -Will be in `tact`: +Becomes this in Tact: + ```tact -// Tact get fun seqno(): Int { return 0; } ``` - ### Tensor return types -In `func` there is a difference between tensor type `(int, int)` and `(int, (int))`, but for TVM there are no differences, they all represent a stack of two ints. +In FunC there is a difference between tensor type `(int, int){:func}` and `(int, (int)){:func}`, but for TVM there are no differences, they all represent a stack of two integers. -To convert the tensor that returned from the `func` get-method, you need to define a `struct` that has the same amount of fields as the tensor and in the same order. +To convert the tensor that returned from a FunC `get`-method, you need to define a [Struct](/book/composite-types#structs) that has the same field types as the tensor and in the same order. + +The following code in FunC: -Code in `func`: ```func (int, slice, slice, cell) get_wallet_data() method_id { return ...; } ``` -Will be in `tact`: +Becomes this in Tact: ```tact struct JettonWalletData { @@ -186,24 +194,26 @@ struct JettonWalletData { walletCode: Cell; } - -get fun get_wallet_data(): JettonWalletData { - return ...; +contract JettonWallet { + get fun get_wallet_data(): JettonWalletData { + return ...; + } } ``` ### Tuple return type -In `func` if you are returning a tuple, instead of a tensor you need to follow the process for tensor type, but define return type of a get method as optional. +In FunC if you are returning a tuple, instead of a tensor you need to follow the process for a tensor type, but define the return type of a `get`-method as optional. + +The following code in FunC: -Code in `func`: ```func [int, int] get_contract_state() method_id { return ...; } ``` -Will be in `tact`: +Becomes this in Tact: ```tact struct ContractState { @@ -211,53 +221,58 @@ struct ContractState { valueB: Int; } - -get fun get_contract_state(): ContractState? { - return ...; +contract StatefulContract { + get fun get_contract_state(): ContractState? { + return ...; + } } ``` ### Mixed tuple and tensor return types -When some of the tensors are a tuple, you need to define a struct as in previous steps and the tuple one must be defined as a separate struct. +When some of the tensors are a tuple, you need to define a struct as in previous steps and the tuple one must be defined as a separate [Struct](/book/composite-types#structs). + +The following code in FunC: -Code in `func`: ```func (int, [int, int]) get_contract_state() method_id { return ...; } ``` -Will be in `tact`: +Becomes this in Tact: ```tact struct ContractStateInner { valueA: Int; valueB: Int; } + struct ContractState { valueA: Int; valueB: ContractStateInner; } - -get fun get_contract_state(): ContractState { - return ...; +contract StatefulContract { + get fun get_contract_state(): ContractState { + return ...; + } } ``` ### Arguments mapping -Conversion of get-methods arguments is forward. Each argument is mapped as-is to `func` one, and each tuple is mapped to a struct. +Conversion of `get`-methods arguments is straightforward. Each argument is mapped _as-is_ to FunC one, and each tuple is mapped to a [Struct](/book/composite-types#structs). + +The following code in FunC: -Code in `func`: ```func (int, [int, int]) get_contract_state(int arg1, [int,int] arg2) method_id { return ...; } ``` -Will be in `tact`: +Becomes this in Tact: ```tact struct ContractStateArg2 { @@ -269,13 +284,21 @@ struct ContractStateInner { valueA: Int; valueB: Int; } + struct ContractState { valueA: Int; valueB: ContractStateInner; } - -get fun get_contract_state(arg1: Int, arg2: ContractStateArg2): ContractState { - return ...; +contract StatefulContract { + get fun get_contract_state(arg1: Int, arg2: ContractStateArg2): ContractState { + return ContractState{ + valueA: arg1, + valueB: ContractStateInner{ + valueA: arg2.valueA, + valueB: arg2.valueB + } + }; + } } -``` +``` \ No newline at end of file diff --git a/pages/book/index.mdx b/pages/book/index.mdx index 6093a450..faf04fcb 100644 --- a/pages/book/index.mdx +++ b/pages/book/index.mdx @@ -1,4 +1,4 @@ -# Overview +# Book Overview import { Cards, Steps } from 'nextra/components' @@ -60,4 +60,4 @@ Book also assumes that you’ve written code in another programming language but /> - + \ No newline at end of file diff --git a/pages/book/integers.mdx b/pages/book/integers.mdx index 62594b62..63acce82 100644 --- a/pages/book/integers.mdx +++ b/pages/book/integers.mdx @@ -29,17 +29,17 @@ You can use underscores (`_`) to improve readability: $123\_456\_789$ is equal t ### Hexadecimal -Represent numbers using [hexadecimal numeral system](https://en.wikipedia.org/wiki/Hexadecimal), denoted by the $\mathrm{0x}$ prefix: $\mathrm{0xFFFFFFFFF}.$\ +Represent numbers using [hexadecimal numeral system](https://en.wikipedia.org/wiki/Hexadecimal), denoted by the $\mathrm{0x}$ (or $\mathrm{0X}$) prefix: $\mathrm{0xFFFFFFFFF}.$\ Use underscores (`_`) to improve readability: $\mathrm{0xFFF\_FFF\_FFF}$ is equal to $\mathrm{0xFFFFFFFFF}.$ ### Octal -Represent numbers using [octal numeral system](https://en.wikipedia.org/wiki/Octal), denoted by the $\mathrm{0o}$ prefix: $\mathrm{0o777777777.}$\ +Represent numbers using [octal numeral system](https://en.wikipedia.org/wiki/Octal), denoted by the $\mathrm{0o}$ (or $\mathrm{0O}$) prefix: $\mathrm{0o777777777.}$\ Use underscores (`_`) to improve readability: $\mathrm{0o777\_777\_777}$ is equal to $\mathrm{0o777777777}.$ ### Binary -Represent numbers using [binary numeral system](https://en.wikipedia.org/wiki/Binary_number), denoted by the $\mathrm{0b}$ prefix: $\mathrm{0b111111111.}$\ +Represent numbers using [binary numeral system](https://en.wikipedia.org/wiki/Binary_number), denoted by the $\mathrm{0b}$ (or $\mathrm{0B}$) prefix: $\mathrm{0b111111111.}$\ Use underscores (`_`) to improve readability: $\mathrm{0b111\_111\_111}$ is equal to $\mathrm{0b111111111}.$ ### Nano-tons @@ -104,7 +104,7 @@ Name | Inclusive range | Space taken `coins{:tact}` | $0$ to $2^{120} - 1$ | 120 bit = 15 bytes - Read more on serialization here: [Compatibility with FunC](/book/func##convert-serialization) + Read more on serialization here: [Compatibility with FunC](/book/func#convert-serialization) ## Operations @@ -132,7 +132,7 @@ contract ComputeErrorsOhNo with Deployable { } ``` -Here, `oneByte` is serialized as a [`uint8`](#serialization-types), which occupies only one byte and ranges from $0$ to $2^{8} - 1$, which is $255$. And when used in runtime calculations no overflow happens and everything is calculated as a $257$-bit signed integers. But the very moment we decide to store the value of `tmp` back into `oneByte` we get an error with the [exit code](https://docs.ton.org/learn/tvm-instructions/tvm-exit-codes) 5, which states the following: `Integer out of the expected range`. +Here, `oneByte` is serialized as a [`uint8`](#serialization-types), which occupies only one byte and ranges from $0$ to $2^{8} - 1$, which is $255$. And when used in runtime calculations no overflow happens and everything is calculated as a $257$-bit signed integers. But the very moment we decide to store the value of `tmp` back into `oneByte` we get an error with the [exit code 5](/book/exit-codes#5), which states the following: `Integer out of the expected range`. Therefore, be **very** careful with numbers and always double-check calculations when using serialization. diff --git a/pages/book/operators.mdx b/pages/book/operators.mdx new file mode 100644 index 00000000..0f358ec9 --- /dev/null +++ b/pages/book/operators.mdx @@ -0,0 +1,541 @@ +# Operators + +import { Callout } from 'nextra/components' + +Almost every contract operates on data: transforms some values into another. Scope may vary, but operators lay in core of such modifications. + +This page lists all the operators in Tact in decreasing order of their [precedence](#precedence), with examples of usage. + + + + Note, that there are no implicit type conversions in Tact, so operators can't be used to, say, add values of different type or compare them in terms of equality without explicitly casting to the same type. That's done with certain functions from the standard library. See [`Int.toString(){:tact}`](https://docs.tact-lang.org/language/ref/strings#inttostring) for an example of such function. + + + +## Precedence + +All operators on this page are given in order of decreasing precedence, from highest to lowest. Precedence is used to choose which operator would be considered in a particular situation. Whenever any ambiguity arises, Tact would prefer operators with higher precedence over those with lower. + +For example, minus sign (`-{:tact}`) may be considered as a subtraction operator or as a negation operator, which reverses the sign of the expression from plus to minus, or vice-versa. As the latter has the higher precedence over the former in cases of ambiguity between the two Tact will first consider `-{:tact}` as a negation operator. And if that doesn't make sense for the given expression, only then it would consider it as a subtraction operator. + +Consider the following code: + +```tact +5 + -5; // here, the minus sign would be viewed as a negation operator +5 -5; // while here it would be viewed as a subtraction operator, despite formatting +``` + +Even though this example may be simple, neglection of precedence rules can often lead to confusing situations with operators. The correct order of operations can be ensured by wrapping every operation in [parentheses](#parentheses), since parentheses have the highest precedence of all expressions and operators there is. + +## Parentheses, `()` + +Parentheses (also can be called round brackets, `(){:tact}`) are more of a punctuation symbols than actual operators, but their [precedence](#precedence) is higher than precedence of any other operator. Use parentheses to override order of operations: + +```tact +5 * 5 - 2; // 23 +5 * (5 - 2); // 15 +``` + +## Unary + +Unary here means that they are applied only to one operand of the given expression. All unary operators, except for the non-null assertion, are of the same [precedence](#precedence). + +Unary operators can be one of the two types: + +* prefix — placed before the expression. +* postfix (or suffix) — placed after the expression. + +### Non-null assert, `!!` [#unary-non-null-assert] + +Unary double-exclamation mark (_non-null assertion_) operator `!!{:tact}` is a postfix operator, which enforces non-null values and allows direct access to the value of the optional variable if it's not `null{:tact}`. Raises a compilation error otherwise. Can be applied to any optional variable regardless of its non-null type. + + + + Read more about optional variables and fields here: [Book→Composite Types](https://docs.tact-lang.org/book/composite-types#optionals) + + + +### Plus, `+` [#unary-plus] + +Although unary plus sign operator `+{:tact}` is specified in the grammar of Tact compiler, it only exists as a [binary operator](#binary-plus). + +### Negate, `-` [#unary-negate] + +Unary minus sign (_negation_) operator `-{:tact}` is a prefix operator, which reverses the sign of the expression. Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let five: Int = 5; +five + -five; // here, the minus sign is a negation operator, not a subtraction operator +-(-1); // double application gives back the original value, which is 1 +--1; // SYNTAX ERROR! +``` + +### Inverse, `!` [#unary-inverse] + +Unary exclamation mark (_inversion_) operator `!{:tact}` is a prefix operator, which inverts the boolean value of the expression — changes `true{:tact}` to `false{:tact}`, and vice versa. Can only be applied to values of type [`Bool{:tact}`](/book/types#booleans): + +```tact +let iLikeTact: Bool = true; +!iLikeTact; // false +!false; // true +!!true; // SYNTAX ERROR! +!(!true); // true +``` + +## Binary + +Binary operators are split into several subsections, in order of decreasing [precedence](#precedence). Operators within each subsection have the same [precedence](#precedence) as the subsection itself. + +### Multiplication [#binary-multiplication] + +Multiply, divide or obtain a remainder. + +#### Multiply, `*` [#binary-multiply] + +Binary asterisk (_multiplication_) operator `*{:tact}` is used for multiplication of two values. Can cause [integer overflows](/book/integers#operations). + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two * two; // 4 +0 * 1_000_000_000; // 0 +-1 * 5; // -5 + +pow(2, 255) * pow(2, 255); // build error: integer overflow! +``` + +#### Divide, `/` [#binary-divide] + +Binary slash (_division_) operator `/{:tact}` is used for integer division of two values, which truncates towards zero. An attempt to divide by zero would result in an error with [exit code 4](/book/exit-codes#4): `Integer overflow`. + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two / 2; // 1 +two / 1; // 0 +-1 / 5; // -1 +``` + +#### Modulo, `%` [#binary-modulo] + +Binary percent sign (_modulo_) operator `%{:tact}` is used for getting the modulo of an integer division, which must not be confused with getting a remainder. For two values of the same sign, modulo and remainder operations are equivalent, but when the operands are of different signs, the modulo result always has the same sign as the _divisor_ (value on the right), while the remainder has the same sign as the _dividend_ (value on the left), which can make them differ by one unit of the _divisor_. + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two % 2; // 0 +two % 1; // 1 + +1 % 5; // 1 +-1 % 5; // 4 +1 % -5; // -4 +-1 % -5; // -1 +``` + +The simplest way to avoid confusion between the two is to prefer using positive values via [`abs(x: Int){:tact}`](https://docs.tact-lang.org/language/ref/math#abs): + +```tact +abs(-1) % abs(-5); // 1 +``` + + + + Did you know, that in JavaScript `%{:tact}` works as a _remainder_ operator, but not _modulo_ operator (like in Tact)?\ + [Remainder (%) - JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder#description)\ + [Modulo - Wikipedia](https://en.wikipedia.org/wiki/Modulo) + + + +### Addition [#binary-addition] + +Add or subtract. + +#### Add, `+` [#binary-add] + +Binary plus (_addition_) operator `+{:tact}` is used for adding numbers together. Going beyond the maximum value of an [`Int{:tact}`](/book/integers) will result in an error with [exit code 4](/book/exit-codes#4): `Integer overflow`. + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two + 2; // 4 +-1 + 1; // 0 + +pow(2, 254) + pow(2, 254); // 2 * 2^254 +pow(2, 255) + pow(2, 255); // build error: integer overflow! +pow(2, 255) - 1 + pow(2, 255); // 2^256 - 1, maximal value of any integer in Tact! +``` + +#### Subtract, `-` [#binary-subtract] + +Binary minus (_subtraction_) operator `-{:tact}` is used for subtracting numbers from each other. Going beyond the minimum value of an [`Int{:tact}`](/book/integers) will result in an error with [exit code 4](/book/exit-codes#4): `Integer overflow`. + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two - 2; // 0 +-1 - 1; // -2 + +pow(2, 254) - pow(2, 254); // 0 +pow(2, 255) - pow(2, 255); // 0 +pow(2, 256) - pow(2, 256); // build error: integer overflow! +``` + +### Bitwise [#binary-bitwise] + +Manipulate individual bits. + +#### Shift right, `>>` [#binary-bitwise-shift-right] + +Binary double greater than (_bitwise shift right_) operator `>>{:tact}` returns an integer which binary representation is the _left operand_ value shifted by the _right operand_ number of bits to the right. Excess bits shifted off to the right are discarded, and copies of the leftmost bit are shifted in from the left. This operation is also called "sign-propagating right shift" or "arithmetic right shift", because the sign of the resulting number is the same as the sign of the _left operand_. This is a more effective way to divide the _left operand_ by $2^n$, where $n$ is equal to the _right operand_. + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two >> 1; // 1 +4 >> 1; // 2 +5 >> 1; // 2, due to flooring of integer values + +pow(2, 254) >> 254; // 1 +``` + + + + [Bit shifts - Wikipedia](https://en.wikipedia.org/wiki/Bitwise_operation#Bit_shifts)\ + [Bit manipulation - Wikipedia](https://en.wikipedia.org/wiki/Bit_manipulation) + + + +#### Shift left, `<<` [#binary-bitwise-shift-left] + +Binary double greater than (_bitwise shift right_) operator `<<{:tact}` returns an integer which binary representation is the _left operand_ value shifted by the _right operand_ number of bits to the left. Excess bits shifted off to the left are discarded, and zero bits are shifted in from the right. This is a more effective way to multiply the _left operand_ by $2^n$, where $n$ is equal to the _right operand_. Going beyond the maximum value of an [`Int{:tact}`](/book/integers) will result in an error with [exit code 4](/book/exit-codes#4): `Integer overflow`. + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two << 1; // 4 +1 << 5; // 1 * 2^5, which is 32 +2 << 5; // 2 * 2^5, which is 64 + +pow(2, 254) == (1 << 254); // true +pow(2, 254) == 1 << 254; // true, no parentheses needed due to higher precedence of >> over == +pow(2, 255) == 1 << 255; // true, but we're very close to overflow here! +``` + + + + [Bit shifts - Wikipedia](https://en.wikipedia.org/wiki/Bitwise_operation#Bit_shifts)\ + [Bit manipulation - Wikipedia](https://en.wikipedia.org/wiki/Bit_manipulation) + + + +#### Bitwise AND, `&` [#binary-bitwise-and] + +Binary ampersand (_bitwise and_) operator `&{:tact}` applies a [bitwise AND](https://en.wikipedia.org/wiki/Bitwise_operation#AND), which performs the [logical AND](#binary-logical-and) operation on each pair of the corresponding bits of operands. This is useful when we want to clear selected bits off a number, where each bit represents an individual flag or a boolean state, which makes it possible to "store" up to $257$ boolean values per integer, as all integers in Tact are $257$-bit signed. + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two & 1; // 0 +4 & 1; // 0 +3 & 1; // 1 +1 & 1; // 1 +255 & 0b00001111; // 15 +``` + + + + [Bitwise AND - Wikipedia](https://en.wikipedia.org/wiki/Bitwise_operation#AND)\ + [Bit manipulation - Wikipedia](https://en.wikipedia.org/wiki/Bit_manipulation) + + + +#### Bitwise OR, `|` [#binary-bitwise-or] + +Binary bar (_bitwise or_) operator `|{:tact}` applies a [bitwise OR](https://en.wikipedia.org/wiki/Bitwise_operation#OR), which performs the [logical OR](#binary-logical-or) operation on each pair of the corresponding bits of operands. This is useful when we want to apply a specific [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)). + +Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two | 1; // 3 +4 | 1; // 5 +3 | 1; // 3 +1 | 1; // 1 + +255 | 0b00001111; // 255 +0b11110000 | 0b00001111; // 255 +``` + + + + [Bitwise OR - Wikipedia](https://en.wikipedia.org/wiki/Bitwise_operation#OR)\ + [Bit manipulation - Wikipedia](https://en.wikipedia.org/wiki/Bit_manipulation) + + + +### Comparison [#binary-comparison] + +Perform inequality and equality checks, find bigger, smaller or equal values. + +#### Not equal, `!=` [#binary-not-equal] + +Binary inequality (_not equal_) operator `!={:tact}` checks whether its two operands are _not equal_, returning a result of type [`Bool{:tact}`](/books/types#booleans). Doesn't perform implicit type conversions, always strict and requires both operands to be of the same type. + +Can be applied to values of following types: + +* [`Int{:tact}`](/book/integers) +* [`Bool{:tact}`](/book/types#booleans) +* [`Address{:tact}`](/book/types#primitive-types) +* [`Cell{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` +* [`Slice{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` +* [`String{:tact}`](/book/types#primitive-types) +* [`map{:tact}`](/book/types#maps), but only if their key and value types are identical + +```tact +// Int: +2 != 3; // true + +// Bool: +false != true; // true + +// Address: +myAddress() != myAddress(); // false + +// Cell: +emptyCell() != emptyCell(); // false + +// Slice: +"A".asSlice() != "A".asSlice(); // false + +// String: +"A" != "A"; // false + +// map: +let map1: map = emptyMap(); +let map2: map = emptyMap(); +map1 != map2; // false +``` + +Can also be used with `null` values: + +```tact +let nullable: Int? = null; +nullable != null; // false +null != null; // false +``` + +#### Equal, `==` [#binary-equal] + +Binary equality (_equal_) operator `!={:tact}` checks whether its two operands are _equal_, returning a result of type [`Bool{:tact}`](/books/types#booleans). Doesn't perform implicit type conversions, always strict and requires both operands to be of the same type. + +Can be applied to values of following types: + +* [`Int{:tact}`](/book/integers) +* [`Bool{:tact}`](/book/types#booleans) +* [`Address{:tact}`](/book/types#primitive-types) +* [`Cell{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` +* [`Slice{:tact}`](/book/types#primitive-types), implicitly compares via `.hash(){:tact}` +* [`String{:tact}`](/book/types#primitive-types) +* [`map{:tact}`](/book/types#maps), but only if their key and value types are identical + +```tact +// Int: +2 == 3; // false + +// Bool: +true == true; // true + +// Address: +myAddress() == myAddress(); // true + +// Cell: +emptyCell() == emptyCell(); // true + +// Slice: +"A".asSlice() == "A".asSlice(); // true + +// String: +"A" == "A"; // true + +// map: +let map1: map = emptyMap(); +let map2: map = emptyMap(); +map1 == map2; // true +``` + +Can also be used with `null` values: + +```tact +let nullable: Int? = null; +nullable == null; // true +null == null; // true +``` + +#### Greater than, `>` [#binary-greater] + +Binary _greater than_ operator `>{:tact}` returns `true{:tact}` if the left operand is greater than the right operand, and `false{:tact}` otherwise. Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two > 2; // false +-1 > -3; // true +``` + +#### Greater than or equal to, `>=` [#binary-greater-equal] + +Binary _greater than or equal to_ operator `>={:tact}` returns `true{:tact}` if the left operand is greater than or to the right operand, and `false{:tact}` otherwise. Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two >= 2; // true +-1 >= -3; // true +``` + +#### Less than, `<` [#binary-less] + +Binary _less than_ operator `<{:tact}` returns `true{:tact}` if the left operand is less than the right operand, and `false{:tact}` otherwise. Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two < 2; // false +-1 < -3; // false +``` + +#### Less than or equal to, `<=` [#binary-less-equal] + +Binary _less than or equal to_ operator `<={:tact}` returns `true{:tact}` if the left operand is less than or equal to the right operand, and `false{:tact}` otherwise. Can only be applied to values of type [`Int{:tact}`](/book/integers): + +```tact +let two: Int = 2; +two <= 2; // true +-1 <= -3; // false +``` + +### Logical AND, `&&` [#binary-logical-and] + +Binary logical AND ([logical conjunction](https://en.wikipedia.org/wiki/Logical_conjunction)) operator `&&{:tact}` returns `true{:tact}` if both operands are `true{:tact}`, and `false{:tact}` otherwise. It's short-circuited, meaning that it would immediately evaluate the whole expression as `false{:tact}` if the left operand is `false{:tact}`, without evaluating the right one. + +Can only be applied to values of type [`Bool{:tact}`](/book/types#booleans): + +```tact +let iLikeTact: Bool = true; +iLikeTact && true; // true +iLikeTact && false; // false, evaluated both operands +false && iLikeTact; // false, didn't evaluate iLikeTact +``` + +### Logical OR, `||` [#binary-logical-or] + +Binary logical OR ([logical disjunction](https://en.wikipedia.org/wiki/Logical_disjunction)) operator `||{:tact}` returns `false{:tact}` only if both operands are `false{:tact}`, and `true{:tact}` otherwise. It's short-circuited, meaning that it would immediately evaluate the whole expression as `true{:tact}` if the left operand is `true{:tact}`, without evaluating the right one. + +Can only be applied to values of type [`Bool{:tact}`](/book/types#booleans): + +```tact +let iLikeSnails: Bool = false; +iLikeSnails && true; // true +iLikeSnails && false; // false, evaluated both operands +true && iLikeSnails; // true, didn't evaluate iLikeSnails +``` + +## Ternary, `?:` [#ternary] + +Conditional (_ternary_) operator is the only Tact operator that takes three operands: a condition followed by a question mark (`?{:tact}`), then an expression to execute if the condition is evaluated to `true{:tact}` followed by a colon (`:{:tact}`), and finally the expression to execute if the condition is evaluated to `false{:tact}`. This operator is frequently used as an alternative to an [`if...else{:tact}`](/book/statements#if-else) statement. + +Condition must resolve to type [`Bool{:tact}`](/book/types#booleans): + +```tact +// condition +// ↓ +true ? "incredibly so" : "absolutely not"; // "incredibly so" +// --------------- ---------------- +// ↑ ↑ +// | alternative, when condition is false +// | +// consequence, when condition is true + +2 + 2 == 4 ? true : false; // true +``` + +Ternary operator is the only operator with right associativity, besides [assignment-related ones](#assignment). This means that in ambiguous situations Tact would prefer the longest matching sequence. In short, this makes bracket-less nesting of ternary operators possible, but only for alternative cases (the part that comes after the colon sign `:{:tact}`): + +```tact +// don't need additional parentheses for alternative cases +false ? 1 : (false ? 2 : 3); // 3 +false ? 1 : false ? 2 : 3; // also 3 +false ? 1 : true ? 2 : 3; // 2 + +// need additional parentheses for consequence cases (parts in-between ? and :) +false ? (false ? 1 : 2) : 3; // 3 +false ? false ? 1 : 2 : 3; // SYNTAX ERROR! +true ? (false ? 1 : 2) : 3; // 2 +``` + +## Assignment, `=` [#assignment] + +Assignment operator `={:tact}` is used to assign a value to a variable, or to a property of a [Message](/book/composite-types#messages) or a [Struct](/book/composite-types#structs). The assignent is a statement and it doesn't return a value. + +```tact +let someVar: Int = 5; // assignment operator = is used here... +someVar = 4; // ...and here +someVar = (someVar = 5); // SYNTAX ERROR! +``` + +### Augmented assignment + +Augmented (or compound) assignment operators such as `+={:tact}` combine an operation with an [assignment](#assignment). The augmented assignment is a statement and it doesn't return a value. + +Augmented assignments are semantically equivalent to regular assignments, but with an operation: + +```tact +let value: Int = 5; + +// this: +value += 5; +// is equivalent to this: +value = value + 5; +``` + +List of augmented assignment operators: + +* `+={:tact}`, which uses [addition operator `+{:tact}`](#binary-add). Can only be applied to values of type [`Int{:tact}`](/book/integers). +* `-={:tact}`, which uses [subtraction operator `-{:tact}`](#binary-subtract). Can only be applied to values of type [`Int{:tact}`](/book/integers). +* `*={:tact}`, which uses [multiplication operator `*{:tact}`](#binary-multiply). Can only be applied to values of type [`Int{:tact}`](/book/integers). +* `/={:tact}`, which uses [division operator `/{:tact}`](#binary-divide). Can only be applied to values of type [`Int{:tact}`](/book/integers). +* `%={:tact}`, which uses [modulo operator `%{:tact}`](#binary-modulo). Can only be applied to values of type [`Int{:tact}`](/book/integers). + +```tact +let value: Int = 5; + +// += +value + 5; // adds 5 +value = value + 5; // adds 5 and assigns result back +value += value; // also adds 5 and assigns result back + +// -= +value - 5; // subtracts 5 +value = value - 5; // subtracts 5 and assigns result back +value -= value; // also subtracts 5 and assigns result back + +// *= +value * 5; // multiplies by 5 +value = value * 5; // multiplies by 5 and assigns result back +value *= value; // also multiplies by 5 and assigns result back + +// /= +value / 5; // divides by 5 +value = value / 5; // divides by 5 and assigns result back +value /= value; // also divides by 5 and assigns result back + +// %= +value % 5; // gets modulo by 5 +value = value % 5; // gets modulo by 5 and assigns result back +value %= value; // also gets modulo by 5 and assigns result back +``` \ No newline at end of file diff --git a/pages/book/statements.mdx b/pages/book/statements.mdx index dd0e7e4d..c1395879 100644 --- a/pages/book/statements.mdx +++ b/pages/book/statements.mdx @@ -1,122 +1,174 @@ # Statements -## Variable declaration +import { Callout } from 'nextra/components' -Declaring variable always requires an initial value and an explicit type: +The following statements can appear anywhere in the [function](/book/functions) body. -```tact -let value: Int = 123; -``` +## `let` statement [#let] -## Static function call +The `let{:tact}` statement allows local and [block](#block)-scoped variable declaration. -Anywhere in the function body, a static function can be called: +In Tact, declaring a variable always requires an initial value and an explicit type: ```tact -let expiration: Int = now() + 1000; // now() is stdlib static function +let value: Int = 123; // declaration +let valueOptional: Int? = null; // Int or null + +let valueMap: map = emptyMap(); +let valueMapWithSerialization: map = emptyMap(); ``` -## Extension function call +## `return` statement [#return] -Some functions are defined only for specific types, they can be called this way: +The `return{:tact}` statement ends [function](/book/functions) execution and specifies a value to be returned to the [function](/book/functions) caller. ```tact -let some: String = 95.toString(); // toString() is a stdlib function that is defined on Int type +// Simple wrapper over stdlib function now() +fun getTimeFromNow(offset: Int): Int { + return now() + offset; +} ``` -## Operators +## Block -Tact supports operations: +A block statement is used to group zero or more statements. The block is delimited by a pair of braces ("curly braces", `{}{:tact}`) and contains a list of zero or more statements and declarations. -- `!!` suffix operator - enforces non-null value, defined only for nullable types. -- `!` - logical inversion, defined only for the `Bool` type. -- `/`, `*`, `%` - division and multiplication operations, defined only for the `Int` type -- `-`, `+` - arithmetic operations, defined only for `Int` type -- `!=`, `==` - equality operations -- `>`, `<`, `>=`, `<=` - compare operations, defined only for the `Int` type -- `&&`, `||` - logical `AND` and `OR` +```tact +{ // <- start of the block + // arbitrary statements: + let value: Int = 2 + 2; + dump(value); +} // <- end of the block -## Loops +{ dump(2 + 2); } // a block with only one statement +``` -### Repeat loop +## Expression -The Repeat Loop executes a block of code a specified number of times. In the example you provided, the code inside the loop will be executed 10 times. +An expression statement is an expression used in a place where a statement is expected. The expression is evaluated and its result is discarded — therefore, it makes sense only for expressions that have side effects, such as executing a function or updating a variable. ```tact -let a: Int = 1; -repeat(10) { - a = a * a; -} +dump(2 + 2); // stdlib function ``` -> **Note** -> Repeat number must be a 32-bit int or of range exception is thrown. Negative values are ignored. +## Assignment -### While loop - -The While Loop continues executing the block of code as long as the given condition is true. +Assignment statements use an [assignment operator](/book/operators#assignment) (`={:tact}`) or [augmented assignment operators](/book/operators#augmented-assignment) (assignments combined with an operation): ```tact -let x: Int = 10; -while(x > 0) { - x = x - 1; -} +let value: Int; // declaration +value = 5; // assignment +value += 5; // augmented assignment (one of the many, see below) ``` -**Explanation**: + -- The code inside the loop will execute as long as � > 0 x>0. -- In the example, the value of � x is decremented by 1 in each iteration, so the loop will run 10 times. + Read more about assignment and augmented assignment in their dedicated section: [assignment operators](/book/operators#assignment). -### Until loop + -The Until Loop is a post-test loop that executes the block of code at least once and then continues to execute it until the given condition becomes true. +## Branches -```tact -let x: Int = 10; -do { - x = x - 1; # do something no matter at least one time -} until (x <= 0); -``` +Control the flow of the code. + +### `if...else` [#if-else] -**Explanation**: + -- The code inside the loop will execute at least once, and then it will continue to execute until \( x \leq 0 \). -- In the example, the value of \( x \) is decremented by 1 in each iteration, so the loop will run 10 times. + Curly brackets (code blocks) are required! -## If Statements + -> **Warn** -> Curly brackets are required +When executing an `if...else{:tact}` statement, first, the specified condition gets evaluated. If the resulting value is `true{:tact}`, the `then{:tact}` statement block gets executed. Otherwise, if the condition evaluates to `false{:tact}`, the optional `else{:tact}` statement block will be executed. If the `else{:tact}` block is missing, nothing happens and execution continues further. + +Regular `if{:tact}`-statement: ```tact -if (condition) { - doSomething(); +// condition +// ↓ +if (true) { // consequence, when condition is true + dump(2 + 2); } ``` +With `else{:tact}` clause: + ```tact -if (condition) { - doSomething(); +// condition +// ↓ +if (2 + 2 == 4) { + // consequence, when condition is true + dump(true); } else { - doSomething2(); + // alternative, when condition is false + dump(false); } ``` +With nested `if...else{:tact}`: + ```tact -if (condition) { - doSomething(); -} else if (condition2) { - doSomething2(); +// condition +// ↓ +if (2 + 2 == 3) { + // consequence, when condition is true + dump("3?"); +// condition2 +// ↓ +} else if (2 + 2 == 4) { + // another consequence, when condition2 is true + dump(true); } else { - doSomething3(); + // alternative, when both condition and condition2 are false + dump(false); +} +``` + + + + Tact also has a ternary expression `?:{:tact}`, which is described earlier in the Book: [Ternary](/book/operators#ternary). + + + +## Loops + +Conditionally repeat certain blocks of code multiple times. + +### `repeat` [#repeat-loop] + +The `repeat{:tact}` loop executes a block of code a specified number of times. Number of repetitions must be a non-negative $32$-bit [`Int{:tact}`](/book/integers). Otherwise an error with the [exit code 5](/book/exit-codes#5), `Integer out of the expected range` would be thrown. + +In the following example, code inside the loop will be executed $10$ times: + +```tact +let a: Int = 1; +repeat (10) { + a *= a; } ``` -## initOf +### `while` [#while-loop] -Allows to compute init state for a contract: +While loop continues executing the block of code as long as the given condition is `true{:tact}`. + +In the following example, the value of `x` is decremented by $1$ on each iteration, so the loop will run $10$ times: ```tact -let state: StateInit = initOf Contract(123, 123); +let x: Int = 10; +while (x > 0) { + x -= 1; +} ``` + +### `do...until` [#do-until-loop] + +The `do...until{:tact}` loop is a post-test loop that executes the block of code at least once, and then continues to execute it until the given condition becomes `true{:tact}`. + +In the following example, the value of `x` is decremented by $1$ on each iteration, so the loop will run $10$ times: + +```tact +let x: Int = 10; +do { + x -= 1; // executes this code block at least once +} until (x <= 0); +``` \ No newline at end of file diff --git a/pages/book/types.mdx b/pages/book/types.mdx index 9643e747..80b54e07 100644 --- a/pages/book/types.mdx +++ b/pages/book/types.mdx @@ -13,7 +13,7 @@ Also, many of those types [can be made nullable](/book/composite-types#optionals ## Primitive types -* [`Int{:tact}`](/book/integers) — all numbers in Tact are $257$-bit signed integers, but [smaller representations](/book/integers#serialization) can be used to reduce storage costs. +* [`Int{:tact}`][int] — all numbers in Tact are $257$-bit signed integers, but [smaller representations](/book/integers#serialization) can be used to reduce storage costs. * [`Bool{:tact}`](#booleans) — classical boolean with `true{:tact}` and `false{:tact}` values. * `Address{:tact}` — standard [smart contract address](https://docs.ton.org/learn/overviews/addresses#address-of-smart-contract) in TON Blockchain. * `Slice{:tact}`, `Cell{:tact}`, `Builder{:tact}` — low-level primitives of TON VM. @@ -24,38 +24,59 @@ Also, many of those types [can be made nullable](/book/composite-types#optionals The primitive type `Bool{:tact}` can hold only the two values: `true{:tact}` and `false{:tact}`. It's convenient for boolean and logical operations, as well as for storing flags. -There are no implicit type conversions in Tact, so addition (`+`) of two boolean values isn't possible. Hovewer, many comparison [operators](/book/statements#operators) are available, such as: +There are no implicit type conversions in Tact, so addition (`+{:tact}`) of two boolean values isn't possible. Hovewer, many comparison [operators](/book/statements#operators) are available, such as: -* `&&` for logical `AND`, -* `||` for logical `OR`, -* and `!` for logical inversion. +* `&&{:tact}` for [logical AND](/book/operators#binary-logical-and), +* `||{:tact}` for [logical OR](/book/operators#binary-logical-or), +* `!{:tact}` for [logical inversion](/book/operators#unary-inverse), +* `=={:tact}` for checking [equality](/book/operators#binary-equal), +* `!={:tact}` for checking [inequality](/book/operators#binary-not-equal), +* and `!!{:tact}` for [non-null assertion](/book/composite-types#optionals). Persisting bools to state is very space-efficient, as they only take 1-bit. Storing 1000 bools in state [costs](https://ton.org/docs/develop/smart-contracts/fees#how-to-calculate-fees) about $0.00072$ TON per year. ## Maps -The type `map{:tact}` is used as a way to associate data with corresponding keys. +The type `map{:tact}` is used as a way to associate values of type `v` with corresponding keys of type `k`. Possible key types: -* [`Int{:tact}`](/book/integers) +* [`Int{:tact}`][int] * `Address{:tact}` Possible value types: -* [`Int{:tact}`](/book/integers) +* [`Int{:tact}`][int] * [`Bool{:tact}`](#booleans) * `Cell{:tact}` * `Address{:tact}` * [Struct](#structs-and-messages) * [Message](#structs-and-messages) +For example, `map{:tact}` uses [`Int{:tact}`][int] type for its keys and values: + ```tact -contract HelloWorld { +struct IntToInt { counters: map; } ``` +Additionally, maps allow [integer serialization](/book/integers#serialization-types) of its keys, values or both to [preserve space and reduce storage costs](/book/integers#serialization): + +```tact +struct SerializedMapInside { + countersButCompact: map; +} +``` + + + + Other serialization options are available too: [Compatibility with FunC](/book/func#convert-serialization) + + + +[int]: /book/integers + ## Structs and Messages [Structs][structs] and [Messages][messages] are two main ways of combining multiple [primitive types](#primitive-types) into a composite one. diff --git a/pages/index.mdx b/pages/index.mdx index 5eef4fbe..b5b01817 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -106,7 +106,7 @@ For custom plugins for your favorite editor and other tooling see the [Tools](/e Alternatively, take a look at the following broader sections: * [Book](/book) helps you learn the language step-by-step -* [Language](/language) provides a complete references of everething there is in it +* [Language](/language) provides a complete reference of the standard library, grammar and evolution process * Finally, [Ecosystem](/ecosystem) describes "what's out there" in the Tacts' and TONs' ecosystems diff --git a/pages/language/ref/_meta.js b/pages/language/ref/_meta.js index 1f621b8b..cd61e554 100644 --- a/pages/language/ref/_meta.js +++ b/pages/language/ref/_meta.js @@ -5,5 +5,4 @@ export default { math: 'Math', cells: 'Cells, Builders and Slices', advanced: 'Advanced', - 'exit-codes': 'Exit codes', } \ No newline at end of file diff --git a/pages/language/ref/exit-codes.mdx b/pages/language/ref/exit-codes.mdx deleted file mode 100644 index 49674af1..00000000 --- a/pages/language/ref/exit-codes.mdx +++ /dev/null @@ -1,307 +0,0 @@ -## 1. Exit Codes - -* An exit code is a 16-bit unsigned integer which ranges between 0 to 65535 (or 216-1). -* The best practice is to allocate 0 to 127 codes for Func Exit Codes, 128 to 255 for Tact Exit Codes, and 256 to 65535 for developer-defined Exit Codes. - -| Exit Code | Phase | Description | -|------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `0` | Compute Phase | Standard successful execution exit code | -| `2` | Compute Phase | Stack underflow. Last op-code consumed more elements than there are on the stacks | -| `3` | Compute Phase | Stack overflow. More values have been stored on a stack than allowed by this version of TVM | -| `4` | Compute Phase | Integer overflow. Integer does not fit into −2256 ≤ x < 2256 or a division by zero has occurred | -| `5` | Compute Phase | Integer out of expected range | -| `6` | Compute Phase | Invalid opcode. Instruction is unknown in the current TVM version | -| `7` | Compute Phase | Type check error. An argument to a primitive is of an incorrect value type | -| `8` | Compute Phase | Cell overflow. Writing to builder is not possible since after operation there would be more than 1023 bits or 4 references | -| `9` | Compute Phase | Cell underflow. Read from slice primitive tried to read more bits or references than there are | -| `10` | Compute Phase | Dictionary error. Error during manipulation with dictionary (hashmaps) | -| `13` | Compute Phase | Out of gas error. Thrown by TVM when the remaining gas becomes negative | -| `-14` | Compute Phase | It means out of gas error, same as `13`. Negative, because it cannot be faked | -| `32` | Action Phase | Action list is invalid. Set during action phase if c5 register after execution contains unparsable object | -| `34` | Action Phase | Action is invalid or not supported. Set during action phase if current action cannot be applied | -| `37` | Action Phase | Not enough TON. Message sends too much TON (or there is not enough TON after deducting fees) | -| `38` | Action Phase | Not enough extra-currencies | -| `128` | Tact (Compiler) | Compiler expects an integer or cell but a null value has been passed | -| `129` | Tact (Compiler) | If there is any inconsistency with the previous op-code check, this exit code will be thrown | -| `130` | Tact (Compiler) | No suitable operation is found | -| `131` | Tact (Compiler) | No info | -| `132` | Tact (Compiler) | Someone other than the owner sent a message to the contract | -| `133` | Tact (Compiler) | A message has been sent to a stopped contract | -| `134` | Tact (Compiler) | Invalid Base64 string | -| `135` | Tact (Compiler) | False flag for a dictionary call | -| `136` | Tact (Compiler) | Non 267-bit address or Invalid chain id (other than 0 or -1) | -| `137` | Tact (Compiler) | No support for Masterchain address | - -Where to observe the list of all auto-generated exit codes in your project? -* The Tact Compiler collects all exit codes at the end of a *.md file and you can track them in the directory along -the path "./ProjectFolder/build/ProjectName/tact_ProjectName.md" - -How to observe a thrown exit code? -* In TACT, it's not wise to print the transactions to see the results because they are not easy to read. If you want to see the exit code of a transaction, -use the below template in your Typescript local tests: - -```typescript -const sender = await blockchain.treasury('sender'); -const result = await contractName.send(sender.getSender(), { value: toNano('0.05'), }, { transactionData }); -expect(result.transactions).toHaveTransaction( - { from: sender.address, to: contractName.address, exitCode: YOUR_DESIRED_EXIT_CODE } - ) -``` --   First line defines the sender. --   Second line sends the transaction. --   In the third line, you check if the result has a transaction from sender to your contract with your desired exit code. - -Keywords: -- Compute phase: in this phase, TVM is executing the contract (see below) and the result of the contract execution is an aggregation of exit_code, -actions (serialized list of actions), gas_details, new_storage and some others. -- Action phase: if the compute phase was successful, in this phase, actions from the compute phase are processed. -In particular, actions may include sending messages, updating the smart contract code, updating the libraries, etc. -Note that some actions may fail during processing (for instance, if we try to send message with more TON than the contract has), -in that case the whole transaction may revert or this action may be skipped (it depends on the mode of the actions, in other words, -the contract may send a send-or-revert or try-send-if-no-ignore type of message). -- Tact (Compiler): It's a new keyword similar to normal compute phase but, it is checked in transpiled FunC code. - -## 2. Exit Code Examples - -### 2.1 Compute Phase - -**0: Successful execution** (compute phase)\ -This exit code means that the Compute Phase of the transaction was completed successfully. - -Example : -```tact -return ; -``` - ---- - -**4: Integer overflow** (compute phase)\ -In TVM, integer can be in the range -2256 < x < 2256. -If the value during the calculation went beyond this range, then 4 exit code is thrown. - -Example : -```tact -self.id = 1; // force not to ignore it by using storage variables -repeat(256) { - self.id = 2 * self.id; -} -``` - ---- - -**5: Integer out of expected range** (compute phase)\ -If the integer value went beyond the expected range, then 5 exit code is thrown. -For example, if a negative value was used in the .store_uint() function. In Tact, there are some other new situations such as:\ -1- As you know, you can define more limited integers in Tact (integers with less than 257 bits). -If you try to store a number in this kind of integers and the number doesn't fit to this limited range, you will face this exit code.\ -2- according to ```storeUint(self: Builder, value: Int, bits: Int)``` function, it's not possible to use ```storeUint(0, 257)``` because ```0 ≤ bits ≤ 256```. - -Example : -```tact -// option 1 -> id: Int as uint32 -self.id = 1; // force not to ignore it by using storage variables -repeat(32) { - self.id = 2 * self.id; -} -// option 2 -> according to storeUint(self: Builder, value: Int, bits: Int) function, it's not possible to use storeUint(0, 1024) because 0 ≤ bits ≤ 256 -let s: Slice = beginCell().storeUint(0, 257).asSlice(); -``` - ---- - -**8: Cell overflow** (compute phase)\ -A cell has the capacity to store 1023 bits of data and 4 references to other cells. -If you try to write more than 1023 bits or more than 4 references, 8 exit code is thrown. - -Example : -```tact -// according to storeUint(self: Builder, value: Int, bits: Int) function, it's not possible to use storeUint(0, 1024) because 0 ≤ bits ≤ 256 -let s: Slice = beginCell().storeUint(0, 256).storeUint(0, 256).storeUint(0, 256).storeUint(0, 256).asSlice(); -``` - ---- - -**9: Cell underflow** (compute phase)\ -If you try to read more data from a slice than it contains, then 9 exit code is thrown. - -Example : -```tact -let s: Slice = emptySlice(); -self.id = s.loadUint(1); // force not to ignore it by using storage variables -``` - ---- - -**13: Out of gas error** (compute phase)\ -If there isn't enough TON to handle compute phase, this error is thrown. -During processing, the NOT operation is applied to this value, which changes this value to -14. -This is done so that this exit code cannot be faked using the throw function, since all such functions accept only positive values for the exit code as it was discussed previously. - -Example : -```tact -repeat(10000) { - self.id = self.id + 1; -} -``` - ---- - -### 2.2 Action Phase - -**34: Action is invalid or not supported** (action phase)\ -This exit code is responsible for most of the errors when working with actions: invalid message, incorrect action, and so on. - -Example : -```tact -nativeSendMessage(emptyCell(), 0); -``` - ---- - -**37: Not enough TON** (action phase)\ -It means that there isn't enough TON to send the specified amount of it. - -Example : -```tact -send(SendParameters{to: context().sender, value: ton("10")}); -``` - ---- - -### 2.3 Tact (Compiler) - -**130: Invalid incoming message**\ -When you send a message to a contract, the first 32 bits of message body is the op code. It determines the operation that must be done. -In FunC, if no op code is found, 0xffff will be thrown. In Tact, 130 exit code will be thrown. - -Example :\ -First, define an empty contract like below: -```tact -contract Fireworks { - - init(){ - - } -} -``` -Then, send a message to this contract. Because no suitable operation is found, you will get this exit code. - ---- - -**132: Access denied**\ -First, you should import and inherit from Ownable Trait. After it, your contract will have an owner. -You can ask for a check by calling ```self.requireOwner();``` in your functions. It will ensure that only the owner can send message to your contract. - -Example : -```tact -import "@stdlib/deploy"; -import "@stdlib/ownable"; - -message FakeLaunch { - -} - -contract Fireworks with Deployable, Ownable { - owner: Address; - - init(){ - self.owner = sender(); - } - - receive(msg: FakeLaunch){ - self.requireOwner(); - } -} - -fun requireOwner() { - nativeThrowUnless(132, sender() == self.owner); -} -``` - ---- - -**133: Contract stopped**\ -The stoppable trait allows to stop the contract. -If you send a message to a stopped contract, and the contract asks for a check by running ```self.requireNotStopped();```, this exit code will be thrown. -In the current version of Tact, 40368 exit code will be thrown instead of 133. - -Example : -```tact -import "@stdlib/deploy"; -import "@stdlib/ownable"; -import "@stdlib/stoppable"; - -message FakeLaunch { - -} - -contract Fireworks with Deployable, Ownable, Stoppable { - owner: Address; - stopped: Bool; - - init(){ - self.owner = sender(); - self.stopped = false; - } - - receive(msg: FakeLaunch){ - self.stopped = true; - self.requireNotStopped(); - } -} - -fun requireNotStopped() { - require(!self.stopped, "Contract stopped"); -} -``` - ---- - -**134: Invalid argument**\ -This will be thrown by the below FunC function(in the last part of a bunch of if conditions). This function reads something from Base64. -If the input characters don't fit into base64 chars, you will encounter this exit code. - -Example : -```tact -let code: Slice = beginCell().storeUint(0, 8).asSlice().fromBase64(); -// 0 is not a valid ASCII code so it cannot be converted to Base64 -``` - ---- - -**135: Code of a contract was not found**\ -It will check the return flag of a search on the dictionary keys. - -Example : -```tact -// copy & paste the below line in wrapper file(../build/ContractName/tact_ContractName.ts) instead of the second line of ContractName_init() function - this is a dictionary containing another smart contract code which leads to 135 exit code -// const __system = Cell.fromBase64('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'); -let ctx: Context = context(); -let fireworks_init: StateInit = initOf Fireworks(0); -``` - ---- - -**136: Invalid address**\ -1- In TON, all addresses are 267 bits. If you violate this rule, you will face this exit code.\ -2- Currently, TON only supports two chain id. 0 for basechain and -1 for masterchain. If you address isn't from basechain, 136 exit code will be thrown. - -Example : -```tact -// fun newAddress(chain: Int, hash: Int): Address; -// creates a new address from chain and hash values. -let zeroAddress: Address = newAddress(1, 0); // invalid-chain zero address -``` - ---- - -**137: Masterchain support is not enabled for this contract**\ -Currently, TON only supports two chain id. 0 for basechain and -1 for masterchain. -Tact only supports basechain and if you address is from masterchain, 137 exit code will be thrown. - -Example : -```tact -// fun newAddress(chain: Int, hash: Int): Address; -// creates a new address from chain and hash values. -let zeroAddress: Address = newAddress(-1, 0); // master-chain zero address -``` \ No newline at end of file