Processing messages
Summary: In previous steps, we modified our smart contract interaction with
storage
,get methods
, and learned the basic smart contract development flow.
Now, we are ready to move on to the main functionality of smart contracts — sending and receiving messages. In TON, messages are used not only for sending currency but also as a data-exchange mechanism between smart contracts, making them crucial for smart contract development.
If you are stuck on some of the examples, you can find the original template project with all modifications performed during this guide here.
Internal messages
Before we proceed to implementation, let's briefly describe the main ways and patterns that we can use to process internal messages.
Actors and roles
Since TON implements the actor model, it's natural to think about smart contract relations in terms of roles
, determining who can access smart contract functionality or not. The most common examples of roles are:
anyone
: any contract that doesn't have a distinct role.owner
: a contract that has exclusive access to some crucial parts of the functionality.
Let's examine the recv_internal
function signature to understand how we could use that:
- FunC
- Tolk
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure
my_balance
- smart-contract balance at the beggining of the transaction.msg_value
- funds received with message.in_msg_full
-cell
containing "header" fields of the message.in_msg_body
- slice containg payload of the message.
fun onInternalMessage(myBalance: int, msgValue: int, msgFull: cell, msgBody: slice)
myBalance
- balance of smart contract at the beggining of the transaction.msgValue
- funds recieved with message.msgFull
-cell
containing "header" fields of message.msgBody
- slice containg payload pf the message.
You can find a comprehensive description of sending messages in this section.
What we are specifically interested in is the source address of the message, which we can extract from the msg_full
cell. By obtaining that address and comparing it to a stored one — we can conditionally allow access to crucial parts of our smart contract functionality. A common approach looks like this:
- FunC
- Tolk
;; This is NOT a part of the project, just an example.
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
;; Parse the sender address from in_msg_full
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
slice sender_address = cs~load_msg_addr();
;; check if message was send by owner
if (equal_slices_bits(sender_address, owner_address)) {
;;owner operations
return
} else if (equal_slices_bits(sender_address, other_role_address)){
;;other role operations
return
} else {
;;anyone else operations
return
}
;;no known operation were obtained for presented role
;;0xffff is not standard exit code, but is standard practice among TON developers
throw(0xffff);
}
// This is NOT a part of the project, just an example.
fun onInternalMessage(myBalance: int, msgValue: int, msgFull: cell, msgBody: slice) {
// Parse the sender address from in_msg_full
var cs: slice = msgFull.beginParse();
val flags = cs.loadMessageFlags();
var sender_address = cs~load_msg_address();
if (isSliceBitsEqual(sender_address, owner_address)) {
// owner operations
return
} else if (isSliceBitsEqual(sender_address, other_role_address)){
// other role operations
return
} else {
// anyone else operations
return
}
throw 0xffff; // if the message contains an op that is not known to this contract, we throw
}