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

fix/feat: More information on sending messages #145

Merged
merged 2 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion pages/book/cookbook.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:**\
Expand Down
38 changes: 20 additions & 18 deletions pages/book/message-mode.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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>
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
79 changes: 53 additions & 26 deletions pages/book/send.mdx
Original file line number Diff line number Diff line change
@@ -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()
});
```
Expand All @@ -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
});
```
```

[p]: /book/types#primitive-types
[int]: /book/integers
[opt]: /book/optionals
Loading