|
6 | 6 | label: Functions |
7 | 7 | --- |
8 | 8 |
|
9 | | -TON is a distributed blockchain, which means that communication between contracts is performed by sending and receiving messages. The most common type of message is the internal message - a message sent from one contract (or a wallet) to another. |
| 9 | +import { Badge } from '@astrojs/starlight/components'; |
| 10 | + |
| 11 | +TON is a distributed blockchain, which means that communication between contracts is performed by sending and receiving messages. The most common type of message is the _internal message_ — a message sent from one contract (or a wallet) to another. |
10 | 12 |
|
11 | 13 | ## Receive internal messages |
12 | 14 |
|
13 | 15 | To receive a message of the required type, you need to declare a receiver function. For example, `receive("increment"){:tact}`. This notation means the declaration of a receiver function that will be called when a text with the value `"increment"{:tact}` is sent to the contract. The function body can modify the state of the contract and send messages to other contracts. It is impossible to call a receiver directly. If you need to reuse some logic, you can declare a function and call it from the receiver. |
14 | 16 |
|
15 | | -There are several receiver functions. All receiver functions are processed in the order they are listed below. The first receiver that matches the message type processes the message: |
| 17 | +### Empty receiver |
| 18 | + |
| 19 | +This receiver specifically handles internal messages with no contents, i.e., the `null{:tact}` body. Note that as a function, it's own body doesn't have to be empty. |
| 20 | + |
| 21 | +```tact |
| 22 | +contract Emptiness() { |
| 23 | + // This receiver handles `null` (empty) message bodies of internal messages. |
| 24 | + receive() { |
| 25 | + // Although you can use this receiver for anything, |
| 26 | + // it's most common to utilize it for deployments and forward excess funds |
| 27 | + // from the incoming message back to the sender's address. |
| 28 | + cashback(sender()); |
| 29 | + } |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +### Text receivers |
| 34 | + |
| 35 | +<Badge text="500+ gas" title="Uses 500 gas units or more" variant="danger" size="medium"/><p/> |
| 36 | + |
| 37 | +There are two kinds of text receivers: |
| 38 | + |
| 39 | +* `receive("..."){:tact}` — the exact text receiver that handles specific string comments with the maximum length of 123 bytes |
| 40 | +* `receive(str: String){:tact}` — the catch-all string receiver that handles arbitrary string comments |
| 41 | + |
| 42 | +Processing and distinguishing text receivers, e.g., the comment receiver `receive("..."){:tact}` and the string receiver `receive(str: String){:tact}`, costs significantly more gas than [processing binary ones](#binary-receivers), such as `receive(){:tact}` or `receive(msg: MyMessage){:tact}`. Thus, it is recommended to [prefer binary receivers to text receivers](/book/gas-best-practices#prefer-binary-receivers-to-text-receivers). |
| 43 | + |
| 44 | +```tact |
| 45 | +contract CertainMD() { |
| 46 | + receive("time changes everything") { |
| 47 | + message(MessageParameters { |
| 48 | + to: sender(), |
| 49 | + value: 0, |
| 50 | + mode: SendRemainingValue, |
| 51 | + body: "Doing things changes things. Not doing things leaves things exactly as they were".asComment(), |
| 52 | + }); |
| 53 | + } |
| 54 | +
|
| 55 | + receive(str: String) { |
| 56 | + message(MessageParameters { |
| 57 | + to: sender(), |
| 58 | + value: 0, |
| 59 | + mode: SendRemainingValue, |
| 60 | + body: "Do I get bonus points if I act like I care?".asComment(), |
| 61 | + }); |
| 62 | + } |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +### Binary receivers |
| 67 | + |
| 68 | +If the message body starts with a recognized [opcode](/book/structs-and-messages#message-opcodes) of 4 non-zero bytes, that internal message will be handled with a corresponding binary receiver `receive(msg: MessageStruct){:tact}` or the catch-all slice receiver `receive(msg: Slice){:tact}`, if there is no binary receiver for that opcode. |
| 69 | + |
| 70 | +```tact |
| 71 | +// This message struct overrides its unique id (opcode) with 411, |
| 72 | +// which allows message bodies that start with such opcodes |
| 73 | +// to be recognized and handled by the respective binary receiver function. |
| 74 | +message(411) HandleMe {} |
| 75 | +
|
| 76 | +contract Handler() { |
| 77 | + receive(msg: HandleMe) { |
| 78 | + let body = inMsg(); |
| 79 | + body.preloadUint(32) == HandleMe.opcode(); // true |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +## Processing order |
| 85 | + |
| 86 | +All receiver functions are processed in the order they are listed below. The first receiver that matches the message type processes the message: |
16 | 87 |
|
17 | 88 | * `receive(){:tact}` - called when an empty message is sent to the contract |
18 | 89 | * `receive("message"){:tact}` - called when a text message with a specific comment is sent to the contract (maximum `"message"{:tact}` length is 123 bytes) |
@@ -50,13 +121,7 @@ In a contract, the order of declaration of receivers has no effect on how receiv |
50 | 121 |
|
51 | 122 | Contracts are not required to declare receivers for all possible message types. If a contract does not have a receiver for a specific message type, the message will be processed by the next receiver that matches the message type in the receiver execution order list. For example, if we remove the receiver `receive("message"){:tact}` in the above contract, then when a message with the comment `"message"{:tact}` arrives, it will be processed by `receive(str: String){:tact}`. yet, the message with an empty comment `""{:tact}` will be processed by the empty body receiver, `receive(){:tact}`. |
52 | 123 |
|
53 | | -Processing and distinguishing the text receivers, like the comment receiver `receive("..."){:tact}` and the string receiver `receive(str: String){:tact}`, costs significantly more gas than processing binary ones, such as `receive(){:tact}` or `receive(msg: MyMessage){:tact}`. Thus, it is recommended to [prefer binary receivers to text receivers](/book/gas-best-practices#prefer-binary-receivers-to-text-receivers). |
54 | | - |
55 | | -Note that the receiver `receive(msg: Slice){:tact}` acts as a fallback that catches all messages that did not match previous receivers in the execution order list. |
56 | | - |
57 | | -If there is no receiver to process a message type and the fallback receiver `receive(msg: Slice){:tact}` is not declared, the transaction will fail with exit code [130](/book/exit-codes/#130). |
58 | | - |
59 | | -Receivers accept all incoming funds by default. That is, without explicitly sending a message that will refund spare Toncoin with the [`cashback(){:tact}`](/ref/core-send#cashback) function, all the incoming message value will be kept by the contract. |
| 124 | +Note that the receiver `receive(msg: Slice){:tact}` acts as a fallback that catches all messages that did not match previous receivers in the execution order list. If there is no receiver to process a message type and the fallback receiver `receive(msg: Slice){:tact}` is not declared, the transaction will fail with exit code [130](/book/exit-codes/#130). |
60 | 125 |
|
61 | 126 | Receivers accept all incoming funds by default. That is, without explicitly sending a message that will refund spare Toncoin with the [`cashback(){:tact}`](/ref/core-send#cashback) function, the contract will keep all the incoming message value. |
62 | 127 |
|
|
0 commit comments