Some thoughts on Ton smart contract design

Author: Kojhliang, head of research at Kenetic Capital

When I was completing a Ton course topic recently, one such question: For Howard Peng’s NFT template contract (i.e. NFT Collection contract — NFT Item contract), add a function to the contract so that each user’s wallet can mint at most three NFTs.

Method one:

If it is a blockchain similar to EVM, the simplest method is to add an additional Map<user_address,mint_times> to the NFT collection contract to save the number of times each user has mint.

However, this method is not allowed to be implemented in Ton, because it will cause the gas fee to be unpredictable and does not comply with the Ton sharding idea. Ton needs to decompose all objects of variable length into contracts for storage and processing.

Method Two:

Split out a user information ( userInfo) contract to store the number of times this user has mint. Then in the NFT collection contract, send a query message to the userInfo contract to query the number of mints, and then decide whether to allow the user to continue minting NFTs based on the current number of mints contained in the message returned by the userInfo contract.

But this method is also not allowed in Ton. Because the messages of the Ton contract are all asynchronous, it is not synchronous like EVM calling other contracts to obtain the status of other contracts. Data consistency cannot be guaranteed using this approach.

For example:

In Ton, contract A sends a message to contract B, and contract B returns the state C1 of contract B to contract A. However, contract A cannot use this state C1 to make subsequent logical judgments, because when contract A receives the message sent back by contract B, state C1 may have changed to state C2! If you make a judgment based on this C1 status at this time, it may have undesirable consequences. For example, according to method two, when the NFT collection contract receives the number of mints returned by the userInfo contract, the number of mints for the current user may actually be three. At this time, if the collection contract still allows users to continue minting new NFTs, it will not meet business requirements.

According to the principle of data consistency and common solutions, in order to maintain the atomicity of a transaction in the Ton contract, it can only be solved by converting all related contracts with dependent logic into serial processing.

For example:

Contract A → Contract B (based on state b, deciding whether to change state b and call the next contract C) → Contract C (based on state c, deciding whether to change state c and call the next contract D) → ….

It can be seen that in this process, there is no need to query the status of other contracts like the second method. All the status that needs to be judged is stored and processed in the contract itself.

However, the problem that arises is that if the business logic that the contract wants to implement becomes more complex and more judgment conditions or variable variables (such as maps, arrays, etc.) are required, the more contracts that need to be decomposed. Dealing with the chain of smart contracts becomes more complex.

This is the price Ton pays to achieve complete sharding. The difficulty of developing complex contracts increases almost exponentially. Compared with traditional synchronous code contract design, which may only require a few lines of code, Ton requires a lot of additional message processing, including exception bounced processing and a very long processing chain to achieve it.

Attached: Two instructions from Ton official:

For FT contracts, there is no way to get the actual wallet balance on-chain because the wallet balance may not be up to date when the message with the balance arrives.

For NFT contracts, the actual owner of the NFT cannot be obtained on-chain because by the time the message with the NFT owner arrives, the owner may have changed.

Suggested solution:

The first option: We can consider adding a global state area to the Ton blockchain, whether within the chain or outside the chain, so that developers can safely execute corresponding logic based on the value of this global state. Of course, how to make this global state data conform to the idea of sharding requires more sophisticated design.

The second option: In order to reduce the difficulty and complexity of development for developers, develop more easy-to-use business models and development assistance tools, so that even if developers use the way of asynchronous message processing, they can develop the smart contract as easily as developing synchronous programs. Otherwise, according to the current design of Ton, it is indeed not suitable to develop and implement too complex business logic in the Ton contract.

1 Like