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

Commit

Permalink
feat: clarify outbound messages (#325)
Browse files Browse the repository at this point in the history
  • Loading branch information
novusnota authored Jul 17, 2024
1 parent a309027 commit da8a197
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 20 deletions.
69 changes: 56 additions & 13 deletions pages/book/send.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,14 @@ 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.
`value` | [`Int{:tact}`][int] | The amount of [nanoToncoins][nano] you want to send with the message. This value is usually used to cover [forward fees][fwdfee], unless the optional flag [`SendPayGasSeparately{:tact}`](/book/message-mode#optional-flags) is used.
`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.

<Callout>

Read more about all message sending functions in the Reference:
* [`send(){:tact}`](/ref/core-common#send)
* [`emit(){:tact}`](/ref/core-common#emit)
* [`self.notify(){:tact}`](/ref/core-base#self-notify)
* [`self.reply(){:tact}`](/ref/core-base#self-reply)
* [`self.forward(){:tact}`](/ref/core-base#self-forward)
* [`nativeSendMessage(){:tact}`](/ref/core-advanced#nativesendmessage)

</Callout>

## Send simple reply

The simplest message is a reply to the incoming message returning all excess value of a message:
Expand Down Expand Up @@ -109,6 +97,61 @@ send(SendParameters{
});
```

## Outbound message processing

Each transaction on TON Blockchain consists of [multiple phases][phases]. Outbound messages are evaluated in [compute phase][compute], but are **not** sent in that phase. Instead, they're queued in order of appearance for the [action phase][phases], where all actions listed in [compute phase][compute], like outbound messages or [reserve requests](/ref/core-advanced#nativereserve), are executed.

As all the values are computed in [compute phase][compute], all the fees computed by the end of it, and exceptions do not revert the transaction during [action phase][phases], outbound message sends can fail without bounce due to unsufficient [action fees](https://docs.ton.org/develop/howto/fees-low-level#action-fee) or [forward fees][fwdfee].

Consider the following example:

```tact
// This contract initially has 0 nanoToncoins on the balance
contract FailureIsNothingButAnotherStep {
// And all the funds it gets are obtained from inbound internal messages
receive() {
// 1st outbound message evaluated and queued (but not sent yet)
send(SendParameters{
to: sender(),
value: ton("0.042"), // plus forward fee due to SendPayGasSeparately
mode: SendIgnoreErrors | SendPayGasSeparately,
});
// 2nd outbound message evaluated and queued (but not sent yet, and never will be!)
send(SendParameters{
to: sender(),
value: 0,
mode: SendRemainingValue | SendIgnoreErrors,
});
}
}
```

There, the second message won't actually be sent:

* After finishing the [compute phase][compute], the remaining value $\mathrm{R}$ of the contract is computed.

* During the outbound message processing and assuming that there was enough value provided in the inbound message, the first message leaves $\mathrm{R} - (0.042 + \mathrm{forward-fees})$ [nanoToncoins](/book/integers#nanotoncoin) on the balance.

* When the second message is processed, contract tries to send $\mathrm{R}$ [nanoToncoins](/book/integers#nanotoncoin), but fails to do so because there is already a smaller amount left.

<Callout>

Read more about all message sending functions in the Reference:
* [`send(){:tact}`](/ref/core-common#send)
* [`emit(){:tact}`](/ref/core-common#emit)
* [`self.notify(){:tact}`](/ref/core-base#self-notify)
* [`self.reply(){:tact}`](/ref/core-base#self-reply)
* [`self.forward(){:tact}`](/ref/core-base#self-forward)
* [`nativeSendMessage(){:tact}`](/ref/core-advanced#nativesendmessage)

</Callout>

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

[phases]: https://docs.ton.org/learn/tvm-instructions/tvm-overview#transactions-and-phases
[compute]: https://docs.ton.org/learn/tvm-instructions/tvm-overview#compute-phase
[nano]: /book/integers#nanotoncoin
[fwdfee]: https://docs.ton.org/develop/howto/fees-low-level#forward-fees
2 changes: 1 addition & 1 deletion pages/ref/core-advanced.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Generates and returns a $256$-bit random number in the range from $0$ to `max` s
fun nativeSendMessage(cell: Cell, mode: Int);
```

Sends the message by specifying the complete `cell` and the [message `mode`](/book/message-mode).
[Queues the message](/book/send#outbound-message-processing) to be sent by specifying the complete `cell` and the [message `mode`](/book/message-mode).

<Callout>

Expand Down
10 changes: 6 additions & 4 deletions pages/ref/core-base.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,16 @@ self.notify("Beware, this is my reply to you!".asComment());
virtual fun forward(to: Address, body: Cell?, bounce: Bool, init: StateInit?);
```

Sends a bounceable or non-bounceable message to the specified address `to`. Optionally, you may provide a `body` of the message and the [`init` package](/book/expressions#initof).
[Queues the message](/book/send#outbound-message-processing) (bounceable or non-bounceable) to be sent to the specified address `to`. Optionally, you may provide a `body` of the message and the [`init` package](/book/expressions#initof).

When [`self.storageReserve{:tact}`](#self-storagereserve) constant is overwritten to be $> 0$, before sending a message it also tries to reserve the `self.storageReserve{:tact}` amount of [nanoToncoins](/book/integers#nanotoncoin) from the remaining balance before making the send in the [`SendRemainingBalance{:tact}`](https://docs.tact-lang.org/book/message-mode#base-modes) ($128$) mode.
When [`self.storageReserve{:tact}`](#self-storagereserve) constant is overwritten to be $> 0$, before sending a message it also tries to reserve the `self.storageReserve{:tact}` amount of [nanoToncoins][nano] from the remaining balance before making the send in the [`SendRemainingBalance{:tact}`](https://docs.tact-lang.org/book/message-mode#base-modes) ($128$) mode.

In case reservation attempt fails and in the default case without the attempt, the message is sent with the [`SendRemainingValue{:tact}`](https://docs.tact-lang.org/book/message-mode#base-modes) ($64$) mode instead.

<Callout>

Note, that `self.forward(){:tact}` never sends additional Toncoins on top of what's available on the balance.\
To be able to send more Toncoins with a single message, use the the [`send(){:tact}`](/ref/core-common#send) function.
Note, that `self.forward(){:tact}` never sends additional [nanoToncoins][nano] on top of what's available on the balance.\
To be able to send more [nanoToncoins][nano] with a single message, use the the [`send(){:tact}`](/ref/core-common#send) function.

</Callout>

Expand Down Expand Up @@ -123,3 +123,5 @@ contract Payout with Ownable {
}
}
```

[nano]: /book/integers#nanotoncoin
4 changes: 2 additions & 2 deletions pages/ref/core-common.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ let hereBeDragons: Address = contractAddressExt(0, initPkg.code, initPkg.data);
fun send(params: SendParameters);
```

Sends a message using a [`SendParameters{:tact}`](/book/send) [Struct](/book/structs-and-messages#structs).
[Queues the message](/book/send#outbound-message-processing) to be sent using a [`SendParameters{:tact}`](/book/send) [Struct](/book/structs-and-messages#structs).

Usage example:

Expand All @@ -225,7 +225,7 @@ send(SendParameters{
fun emit(body: Cell);
```

Sends a message `body` to the outer world with the purpose of logging and analyzing it later off-chain. The message does not have a recipient and is gas-efficient compared to using any other message sending functions of Tact.
[Queues the message](/book/send#outbound-message-processing) `body` to be sent to the outer world with the purpose of logging and analyzing it later off-chain. The message does not have a recipient and is gas-efficient compared to using any other message sending functions of Tact.

Usage example:

Expand Down

0 comments on commit da8a197

Please sign in to comment.