Receive messages
TON is a distributed blockchain which means that communication between contracts is done by sending and receiving messages. The most common type of messages is the internal message - a message sent from one contract (or a wallet) to another.
Receive internal messages
To receive a message of the required type, you need to declare a receiver function, for example, receive("increment")
. This notation means the declaration of a receiver function that will be called when a text with the value "increment"
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.
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 will process the message:
receive()
- called when an empty message is sent to the contractreceive("message")
- called when a text message with a specific comment is sent to the contract (maximum"message"
length is 123 bytes)receive(str: String)
- called when an arbitrary text message is sent to the contractreceive(msg: MyMessage)
- called when a binary message of typeMyMessage
is sent to the contractreceive(msg: Slice)
- called when binary message of unknown type is sent to the contract
For example, an empty message gets processed by receive()
and not by receive(msg: Slice)
, because the former occurs before the latter in the above list. Similarly, a message with specific comment "message"
gets processed by receive("message")
and not by receive(str: String)
.
message MyMessage { value: Int;}
contract MyContract { receive() { // ... } receive("message") { // ... } receive(str: String) { // ... } receive(msg: MyMessage) { // ... } receive(msg: Slice) { // ... }}
In a contract, the order of declaration of receivers has no effect on how receivers process messages. Hence, changing the order of receivers in the above contract produces an equivalent contract.
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")
in the above contract, then if a message with comment "message"
arrives, it will get processed by receive(str: String)
.
Note that receiver receive(msg: Slice)
acts as a fallback that catches all messages that did not match the previous receivers in the execution order list.
If there is no receiver to process a message type and the fallback receiver receive(msg: Slice)
is not declared, the transaction will fail with exit code 130.
Naming a parameter of the receiver function with an underscore _
makes its value considered unused and discarded. This is useful when you don’t need to inspect the message received and you only want it to convey a specific opcode:
message(42) UniverseCalls {}
contract Example { receive(_: UniverseCalls) { // Got a Message with opcode 42 }}