From 851c7504991f01d43983828910ad2639302b368e Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:09:16 +0100 Subject: [PATCH] fix/feat: More information on sending messages * Clean-up in `/book/message-mode` * Re-write of `/book/send` * More examples on sending messages in `/book/cookbook` --- pages/book/cookbook.mdx | 43 +++++++++++++++++++- pages/book/message-mode.mdx | 38 +++++++++--------- pages/book/send.mdx | 79 +++++++++++++++++++++++++------------ 3 files changed, 114 insertions(+), 46 deletions(-) diff --git a/pages/book/cookbook.mdx b/pages/book/cookbook.mdx index fed13fa2..631625a5 100644 --- a/pages/book/cookbook.mdx +++ b/pages/book/cookbook.mdx @@ -316,10 +316,19 @@ let areCellsNotEqual: Bool = a.hash() != b.hash(); // false ## Sending messages +### How to make a basic reply + +```tact +receive() { + self.reply("Hello, World!".asComment()); // asComment converts a String to a Cell with a comment +} +``` + ### How to send a simple message ```tact send(SendParameters{ + bounce: true, // default to: destinationAddress, value: ton("0.01"), // attached amount of Tons to send body: "Hello from Tact!".asComment() // comment (optional) @@ -335,10 +344,40 @@ send(SendParameters{ to: ctx.sender, value: 0, mode: SendRemainingBalance, // or mode: 128 - bounce: true + body: "Hello from Tact!".asComment() // comment (optional) }); ``` +### How to send a message with the remaining value + +If we want to make a reply to the same sender, we can use the mode `SendRemainingValue{:tact}` (i.e. `mode: 64{:tact}`), which carries all the remaining value of the inbound message in addition to the value initially indicated in the new message. + +```tact +receive() { + send(SendParameters{ + to: sender(), + value: 0, + mode: SendRemainingValue, + body: "Hello from Tact!".asComment() // comment (optional) + }); +} +``` + +It's often useful to add the `SendIgnoreErrors{:tact}` flag too, in order to ignore any errors arising while processing this message during the action phaseL + +```tact +receive() { + send(SendParameters{ + to: sender(), + value: 0, + mode: SendRemainingValue + SendIgnoreErrors, + body: "Hello from Tact!".asComment() // comment (optional) + }); +} +``` + +The latter example is identical to using a [`.reply(){:tact}` function](#how-to-make-a-basic-reply). + <Callout> **Useful links:**\ @@ -535,4 +574,4 @@ let itemAddress: Address = contractAddress(self.getNftItemInit(itemIndex)); [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) -</Callout> \ No newline at end of file +</Callout> diff --git a/pages/book/message-mode.mdx b/pages/book/message-mode.mdx index a1dc2b5f..3be31118 100644 --- a/pages/book/message-mode.mdx +++ b/pages/book/message-mode.mdx @@ -2,34 +2,34 @@ import { Callout } from 'nextra/components' # Message `mode` -As you might've noticed, messages are sent with the `mode` param of a struct `SendParameters`. It's an Int value, which is combined from base modes and optional flags, which are also Int values. +As it was previously mentioned, messages are sent with the `mode` param of a struct `SendParameters{:tact}`. It's an [`Int{:tact}`][int] value, which is combined from base modes and optional flags, which are also [`Int{:tact}`][int] values. -It's possible to use raw Int values and manually provide them for the `mode`, but for your convenience there's a set of constants which you may use to construct the compound `mode` with ease. Take a look at the following tables for more information on base modes and optional flags. +It's possible to use raw [`Int{:tact}`][int] values and manually provide them for the `mode`, but for your convenience there's a set of constants which you may use to construct the compound `mode` with ease. Take a look at the following tables for more information on base modes and optional flags. ## Base modes -Mode value | Constant name | Description ----------: | :------------------- | ----------------------------------------------------------------------------------------------------------------------- -$0$ | - | Ordinary message (default). -$64$ | SendRemainingValue | Carry all the remaining value of the inbound message in addition to the value initially indicated in the new message. -$128$ | SendRemainingBalance | Carry all the remaining balance of the current smart contract instead of the value originally indicated in the message. +Mode value | Constant name | Description +---------: | :---------------------------- | ----------- +$0$ | - | Ordinary message (default). +$64$ | `SendRemainingValue{:tact}` | Carry all the remaining value of the inbound message in addition to the value initially indicated in the new message. +$128$ | `SendRemainingBalance{:tact}` | Carry all the remaining balance of the current smart contract instead of the value originally indicated in the message. ## Optional flags -Flag value | Constant name | Description ----------: | :--------------------- | ---------------------------------------------------------------------------------------------------------------------- -$+1$ | SendPayGasSeparately | Pay forward fees separately from the message value. -$+2$ | SendIgnoreErrors | Ignore any errors arising while processing this message during the action phase. -$+16$ | SendBounceIfActionFail | Bounce transaction in case of any errors during action phase. Has no effect if flag $+2$, SendIgnoreErrors is used. -$+32$ | SendDestroyIfZero | Current account must be destroyed if its resulting balance is zero (often used with mode $128$, SendRemainingBalance). +Flag value | Constant name | Description +---------: | :------------------------------ | ----------- +$+1$ | `SendPayGasSeparately{:tact}` | Pay forward fees separately from the message value. +$+2$ | `SendIgnoreErrors{:tact}` | Ignore any errors arising while processing this message during the action phase. +$+16$ | `SendBounceIfActionFail{:tact}` | Bounce transaction in case of any errors during action phase. Has no effect if flag $+2$, `SendIgnoreErrors{:tact}` is used. +$+32$ | `SendDestroyIfZero{:tact}` | Current account must be destroyed if its resulting balance is zero (often used with mode $128$, `SendRemainingBalance{:tact}`). ## Combining modes with flags -To make the Int value for `mode` field of SendParameters, you just have to combine base modes with optional flags by adding them together as numbers. +To make the [`Int{:tact}`][int] value for `mode` field of SendParameters, you just have to combine base modes with optional flags by adding them together as numbers. -For example, if you want to send a regular message and pay transfer fees separately, use the mode $0$ (default) and a flag $+1$ to get `mode` $= 1$, which is equal to just use SendPayGasSeparately constant. +For example, if you want to send a regular message and pay transfer fees separately, use the mode $0$ (default) and a flag $+1$ to get `mode` $= 1$, which is equal to just use `SendPayGasSeparately{:tact}` constant. -Alternatively, if you want to send the whole contract balance and destroy it immediately, use the mode $128$ and flag $+32$ to get `mode` $= 160$, which is equal to SendRemainingBalance + SendDestroyIfZero. +Alternatively, if you want to send the whole contract balance and destroy it immediately, use the mode $128$ and flag $+32$ to get `mode` $= 160$, which is equal to `SendRemainingBalance{:tact}` + `SendDestroyIfZero{:tact}`. Here's how the latter example would look in code: @@ -45,5 +45,7 @@ send(SendParameters{ ``` <Callout> -NOTE: There can be only one base mode, but number of optional flags may vary: you can use them all, none or just some. -</Callout> \ No newline at end of file + NOTE: There can be only one [base mode](#base-modes), but number of [optional flags](#optional-flags) may vary: you can use them all, none or just some. +</Callout> + +[int]: /book/integers diff --git a/pages/book/send.mdx b/pages/book/send.mdx index 54103fd0..b122c4e5 100644 --- a/pages/book/send.mdx +++ b/pages/book/send.mdx @@ -1,54 +1,77 @@ # Sending messages -TON blockchain is a message-based one to communicate with other contracts you need to send messages. +TON blockchain is message-based — to communicate with other contracts and to deploy new ones you need to send messages. -The message consists of: -* `value` in TON - the amount of TON you want to send with the message. This value is used to cover gas fees on the receiver side. -* `bounce` - if set to `true` (default) then the message will be bounced back to the sender if the receiver contract doesn't exist or wasn't able to process the message. -* `code` and `data` - init package, useful for deployments -* `body` - message body as `Cell` -* `mode` - an 8-bit flag that configures how to send a message +Messages in Tact are composed using a built-in [Struct](/book/structs-and-messages#structs) `SendParameters{:tact}`, which consists of: + +Field | Type | Description +:------- | :-------------------- | :---------- +`bounce` | [`Bool{:tact}`][p] | When set to `true` (default) message bounces back to the sender if the receiver contract doesn't exist or wasn't able to process the message. +`to` | [`Address{:tact}`][p] | Receiver internal [`Address{:tact}`][p] in TON blockchain. +`value` | [`Int{:tact}`][int] | The amount of Toncoins you want to send with the message. This value is used to cover gas fees on the receiver side. +`mode` | [`Int{:tact}`][int] | An 8-bit value that configures how to send a message, defaults to $0$. See: [Message `mode`](/book/message-mode). +`body` | [`Cell?{:tact}`][p] | [Optional][opt] message body as a [`Cell{:tact}`][p] +`code` | [`Cell?{:tact}`][p] | [Optional][opt] initial code of the contract (the compiled bytecode) +`data` | [`Cell?{:tact}`][p] | [Optional][opt] initial data of the contract (arguments of [`init(){:tact}` function](/book/contracts#init-function) of the contract) + +Fields `code` and `data` are what's called an [init package](/book/expressions#initof), which is used in deployments of new contracts. ## Send simple reply The simplest message is a reply to the incoming message returning all excess value of a message: ```tact -self.reply("Hello, World!".asComment()); // asComment converts string to a Cell with a comment +receive() { + self.reply("Hello, World!".asComment()); // asComment converts a String to a Cell with a comment +} ``` ## Send message -If you need more advanced logic you can use the `send` function directly. +If you need more advanced logic you can use the `send(){:tact}` function and `SendParameters{:tact}` [Struct](/book/structs-and-messages#structs) directly. + +In fact, the previous example with [`.reply(){:tact}`](#send-simple-reply) can be made using the following call to `send(){:tact}` function: -This example sends a message to the `to` address with a `value` of 1 TON and the `body` of a comment with a string "Hello, World!". +```tact +receive() { + send(SendParameters{ + // bounce is set to true by default + to: sender(), // sending message back to the sender + value: 0, // don't add Toncoins to the message... + mode: SendRemainingValue + SendIgnoreErrors, // ...except for ones received from the sender due to SendRemainingValue + body: "Hello, World".asComment(); // asComment converts a String to a Cell with a comment + }); +} +``` -`SendIgnoreErrors` means that even when an error occurs during message sending next messages would be sent anyway. **No error during the sending phase would revert a transaction.** +Another example sends a message to the specified [`Address{:tact}`][p] with a `value` of $1$ TON and the `body` of a comment with a [`String{:tact}`][p] `"Hello, World!"{:tact}`: ```tact -let to: Address = ...; +let recipient: Address = ...; let value: Int = ton("1"); send(SendParameters{ - to: to, - value: value, - mode: SendIgnoreErrors, - bounce: true, + // bounce is set to true by default + to: recipient, + value: value, + mode: SendIgnoreErrors, // will send the message despite any errors body: "Hello, World!".asComment() }); ``` +The [optional flag](/book/message-mode#optional-flags) `SendIgnoreErrors{:tact}` means that even when an error occurs during message sending next messages would be sent anyway. **No error during the sending phase would revert a transaction.** + ## Send typed message To send a binary typed message you can use the following code: ```tact -let to: Address = ...; +let recipient: Address = ...; let value: Int = ton("1"); send(SendParameters{ - to: to, - value: value, - mode: SendIgnoreErrors, - bounce: true, + // bounce is set to true by default + to: recipient, + value: value, + mode: SendIgnoreErrors, // don't stop in case of errors body: SomeMessage{arg1: 123, arg2: 1234}.toCell() }); ``` @@ -62,12 +85,16 @@ let init: StateInit = initOf SecondContract(arg1, arg2); let address: Address = contractAddress(init); let value: Int = ton("1"); send(SendParameters{ + // bounce is set to true by default to: address, - value: value, - mode: SendIgnoreErrors, - bounce: true, + value: value, + mode: SendIgnoreErrors, // don't stop in case of errors code: init.code, data: init.data, - body: "Hello, World!".asComment() + body: "Hello, World!".asComment() // not necessary, can be omitted }); -``` \ No newline at end of file +``` + +[p]: /book/types#primitive-types +[int]: /book/integers +[opt]: /book/optionals