Messages and transactions
TON is an asynchronous blockchain with a complex structure that is very different from other blockchains. Because of this, new developers often have questions about low-level things in TON. In this article, we will examine one such question related to message delivery.
What is a message?
A message is a packet of data exchanged between actors (users, applications, or smart contracts). It typically contains information instructing the receiver on what action to perform, such as updating storage or sending a new message.


Working with this type of communication is reminiscent of launching a satellite into space. While we know the message we've created, observation after launch is required to determine the outcome.
What is a transaction?
A transaction in TON consists of the following:
- The incoming message that initially triggers the contract (special ways to trigger exist)
- Contract actions caused by the incoming message, such as an update to the contract's storage (optional)
- Outgoing messages generated and sent to other actors (optional)
Technically, a contract can be triggered through special functions such as Tick-Tock. Still, this function is more used for internal TON Blockchain core contracts.
Not every transaction results in outgoing messages or updates to the contract's storage - this depends on the actions defined by the contract's code.# Messages and transactions
TON is an asynchronous blockchain with a complex structure that is very different from other blockchains. Because of this, new developers often have questions about low-level things in TON. In this article, we will examine one such question related to message delivery.
What is a message?
A message is a packet of data exchanged between actors (users, applications, or smart contracts). It typically contains information instructing the receiver on what action to perform, such as updating storage or sending a new message.


If we look at Ethereum or almost any other synchronous blockchain, each transaction can contain several smart contract calls. For example, DEXs perform multiple exchanges in one transaction if there is no liquidity for the selected trading pair.
In an asynchronous system, you can't get a response from the destination smart contract in the same transaction. Depending on the length of the route between source and destination, a contract call may take a few blocks to process.
Achieving the infinite sharding paradigm requires full parallelization, ensuring that each transaction executes independently of others. Therefore, instead of transactions that affect and change the state of many contracts simultaneously, each transaction in TON is only executed on a single smart contract, and smart contracts communicate through messages. That way, smart contracts can only interact with each other by calling their functions with special messages and getting a response to them via other messages later.
See transaction layout for complete transaction details.
Transaction outcome
There is a TVM exit code for a transaction that had a compute phase. If it is not 0 or 1, then there was an error. Also, TVM compute phase may be skipped for reasons like lack of funds or state.
One should use tx.description.action to determine a successful transaction.success && tx.description.compute_ph.success:
"transactions": [
{
"description": {
. . . . . . . .
"action": {
"valid": true,
"success": true,
. . . . . . . .
},
. . . . . . . .
"destroyed": false,
"compute_ph": {
"mode": 0,
"type": "vm",
"success": true,
The transaction may have one of three results:
- Success, exit code 0 or 1
- Fail,
aborted: true
without execution - Fail, exit code,
aborted: true
aborted: true
is a toncenter field. Transaction has no such field.
What is a logical time?
In a system with asynchronous and parallel smart contract calls, it can be hard to define the order of actions to process. That's why each message in TON has its Logical time or Lamport time (later just lt). This time is used to understand which event caused another and what a validator needs to process first.
It is strictly guaranteed that the transaction resulting from a message will have a lt greater than the lt of the message. Likewise, the lt of a message sent in some transaction is strictly greater than the lt of the transaction that caused it. In addition, messages sent from one account and transactions on one account are strictly ordered.


For the case in the image, it turns out: in_msg_lt < tx0_lt < out_msg_lt
Thanks to this, we always know the order of transactions, received messages and sent messages for every account.
Moreover, if account A sends two messages to account B, it is guaranteed that the message with a lower lt will be processed earlier:
If msg1_lt < msg2_lt
=> tx1_lt < tx2_lt
.


Otherwise, an attempt to synchronize delivery would require knowing the state of all the others before processing one shard, thereby breaking parallelization and destroying efficient sharding.
For each block, we can define the lt span as starting from the first transaction and ending with the lt of the last event in the block (message or transaction). Blocks are ordered like other events in TON, so if one block depends on the other, it has a higher lt. The child block in a shard has a higher lt than its parent. A masterchain block's lt is higher than the lts of shard blocks that it lists since a master block depends on listed shard blocks. Each shard block contains an ordered reference to the latest (at the moment of shard block creation) master block, and thus, the shard block lt is higher than the referenced master block lt.
Message delivery
TON guarantees reliable delivery of all internal messages - the destination account will always receive them. The network ensures messages cannot be lost between sender and recipient during transit. Validators control external messages' initial acceptance into blocks. However, once an external message enters the incoming message queue, it receives the same delivery guarantee as internal messages.
Delivery order
Therefore, it seems like lt solves the issue about message delivery order because we know that a transaction with a lower lt will be processed first. But this doesn't work in every scenario.
Suppose there are two contracts - A and B. A receives an external message, which triggers it to send two internal messages to B. Let's call these messages 1 and 2. In this simple case, we can be 100% sure that 1 will be processed by B before 2 because it has a lower lt.


But this is just a simple case when we have only two contracts. How does our system work in more complex cases?
Several smart contracts
Consider three contracts - A, B, and C. When contract A sends two internal messages (1 to B and 2 to C) in a single transaction, the messages are created in strict order (1 first, then 2). However, their processing order at the destinations isn't guaranteed because:
- Network paths may differ: The routes to B and C might involve different validator sets
- Shard chain effects: If B and C are in separate shard chains, messages may traverse different numbers of blocks
- Asynchronous processing: Each shard chain progresses independently, potentially causing delivery timing variations
While the sending order is preserved at the source, TON's decentralized sharded architecture means the receiving order can't be predetermined.
For better clarity, suppose our contracts send back messages msg1'
and msg2'
after msg1
and msg2
executed by B
and C
contracts. As a result, it will apply tx2'
and tx1'
to contract A
.
We have two possible traces for these transactions,
- The first possible order is
tx1'_lt < tx2'_lt
:


- The second possible order is
tx2'_lt < tx1'_lt
:


The same happens in the reverse case, when two contracts, B and C, send a message to one contract, A. Even if message B -> A
was sent before C -> A
, we can't know which one will be delivered first. The B -> A
route may require more shard chain hops.


There can be many scenarios of smart contract interactions, and in any scenario with more than 2 contracts, the order of message delivery may be arbitrary. The only guarantee is that messages from contract A to contract B will be processed in order of their logical time. Some examples are below.






Conclusion
The TON blockchain's asynchronous structure creates challenges for message delivery guarantees. Logical time helps to establish event and transaction order but doesn't guarantee message delivery order between multiple smart contracts due to varying routes in shard chains. Despite these complexities, TON ensures internal message delivery, maintaining network reliability. Developers must adapt to these nuances to harness TON's full potential in building innovative decentralized applications.