Home  >  Article  >  TON project development tutorial (1): How to create an NFT on TON Chain from a source code perspective

TON project development tutorial (1): How to create an NFT on TON Chain from a source code perspective

WBOY
WBOYOriginal
2024-06-25 07:51:291086browse

Abstract: Following the previous article about the introduction of TON technology, I have studied the official TON development documents in depth during this period. I feel that there are still some barriers to learning. The current document content seems to be more like an internal development document, which is suitable for newbies. It is not very friendly to developers, so I try to sort out a series of articles about TON Chain project development based on my own learning trajectory. I hope it will be helpful for everyone to quickly get started with TON DApp development. If there are any errors in the writing, you are welcome to correct me and learn together.

What are the differences between developing NFT in EVM and developing NFT on TON Chain?

Issuing an FT or NFT is usually the most basic need for DApp developers. So I also use this as an entry point for learning. First, let us understand the following differences between developing an NFT in the EVM technology stack and in TON Chain. EVM-based NFTs usually choose to inherit the ERC-721 standard. The so-called NFT refers to an indivisible type of encrypted asset, and each asset is unique, that is, it has certain exclusive characteristics. ERC-721 is a common development paradigm for this type of assets. Let us look at what functions a common ERC721 contract needs to implement and what information is recorded. The picture below is an ERC721 interface. It can be seen that unlike FT, what needs to be entered in the transfer interface is the tokenId to be transferred rather than the amount. This tokenId is also the most basic manifestation of the uniqueness of NFT assets. Of course, in order to carry more attributes, a metadata is usually recorded for each tokenId. This metadata is an external link that saves other scalable data of the NFT, such as a Links to PFP images, certain attribute names, etc.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

For developers who are familiar with Solidity or familiar with object-oriented, it is easy to implement such a smart contract. As long as the data types required in the contract are defined, such as some key mapping relationships, and based on the required It requires functional development of corresponding modification logic to these data to implement an NFT.

However, this is not the same in TON Chain. There are two core reasons for the difference:

  • In TON, data storage is based on Cell, and the Cell of the same account is implemented through Directed Acyclic Figure to achieve. This means that the data that needs to be stored cannot grow without boundaries, because for a directed acyclic graph, the query cost is determined by the depth of the data. When the depth extends infinitely, the query cost may be too high, leading to The contract is stuck in a deadlock problem.
  • In order to pursue high concurrency performance, TON abandoned the serial execution architecture and instead adopted a development paradigm specifically designed for parallelism, the Actor model, to reconstruct the execution environment. This has an impact. Smart contracts can only be called asynchronously by sending so-called internal messages. Note that whether it is a state modification type or a read-only type of call, this principle needs to be followed. In addition, it also needs to be carefully considered. How to handle data rollback if an asynchronous call fails.

Of course, other technical differences have been discussed in detail in the previous article. This article hopes to focus on smart contract development, so it will not be discussed. The above two design principles make a big difference between smart contract development in TON and EVM. In the initial discussion, we know that an NFT contract needs to define some mapping relationships, that is, mapping, to save NFT-related data. The most important of them is owners. This mapping stores the mapping relationship of the owner address of the NFT corresponding to a certain tokenID, which determines the ownership of the NFT. The transfer is a modification of the ownership. Since this is a data structure that can be unbounded in theory, it needs to be avoided as much as possible. Therefore, it is officially recommended to use the existence of unbounded data structures as the standard for sharding. That is, when there are similar data storage requirements, the master-slave contract paradigm is used instead, and the data corresponding to each key is managed by creating sub-contracts. And manage global parameters through the main contract, or help handle internal information interaction between sub-contracts.

This means that NFTs in TON also need to be designed using a similar architecture. Each NFT is an independent sub-contract, which saves exclusive data such as owner address, metadata, etc., and is managed through a main contract. Manage global data such as NFT name, symbol, total supply, etc.

After clarifying the architecture, the next step is to solve the core functional requirements. Since this master-slave contract method is adopted, it is necessary to clarify which functions are carried by the main contract and which functions are carried by the sub-contract, and which functions are carried by the sub-contract, and which functions are carried by the sub-contract. What internal information is used to communicate between them, and how to roll back the previous data when an execution error occurs. Usually, before developing a complex large-scale project, it is necessary to pass a class diagram and clarify the information flow between each other, and carefully think about the rollback logic after the internal call fails. Of course, the above-mentioned NFT development is simple, but it can also Do similar verification.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Learn to develop TON smart contracts from the source code

TON has chosen to design a C-like, statically typed language named Func as the smart contract development language. Then let us learn how to develop TON smart contracts from the source code. I chose the NFT example in TON's official documentation for introduction. Interested friends can check it out by themselves. In this case, a simple TON NFT example is implemented. Let's take a look at the contract structure, which is divided into two functional contracts and three necessary libraries.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

The two main functional contracts are designed according to the above principles. First, let us look at the code of the main contract nft-collection:

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

This introduces the first knowledge point, how to Persistent storage of data in TON smart contracts. We know that the persistent storage of data in Solidity is automatically handled by the EVM based on the type of parameters. Normally, the state variables of the smart contract will be automatically persisted according to the latest value after execution. Storage, developers do not need to consider this process. But this is not the case in Func. Developers need to implement the corresponding processing logic themselves. This situation is somewhat similar to the need to consider the GC process in C and C++, but other new development languages ​​usually automate this part of the logic. Let's take a look at the code. First, we introduce some required libraries, and then we see that the first function load_data is used to read the persistently stored data. Its logic is to first return the persistent contract storage cell through get_data. Note that this is done by the standard Implemented by the library stdlib.fc, some of these functions can usually be used as system functions.

The return value type of this function is cell, which is the cell type in TVM. In the previous introduction, we already know that all persistent data in the TON blockchain is stored in the cell tree. Each cell has up to 1023 bits of arbitrary data and up to four references to other cells. Cells are used as memory in stack-based TVM. What is stored in the cell is compactly encoded data. To obtain the specific plaintext data, the cell needs to be converted into a type called slice. The cell can be converted to the slice type through the begin_parse function, and then the data in the cell can be obtained by loading the data bits from the slice and references to other cells. Note that this calling method in line 15 is syntactic sugar in func, and you can directly call the second function that returns the value of the first function. And finally load the corresponding data in order according to the data persistence order. Note that this process is different from solidity and is not called based on hashmap, so the order of calls cannot be messed up.

In the save_data function, the logic is similar, except that this is a reverse process, which introduces the next knowledge point, a new type builder, which is the type of cell builder. Data bits and references to other cells can be stored in builders, which can then be finalized into new cells. First create a builder through the standard function begin_cell, and store related functions through the store related functions in turn. Note that the calling order above needs to be consistent with the storage order here. Finally, the new cell construction is completed through end_cell. At this time, the cell is managed in the memory. Finally, through the outermost set_data, the persistent storage of the cell can be completed.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Next, let’s take a look at business-related functions. First, we need to introduce the next knowledge point, how to create a new contract through a contract, which will be frequently used in the master-slave architecture just introduced. We know that in TON, calls between smart contracts are implemented by sending internal messages. This is achieved through a message called send_raw_message. Note that the first parameter is the message-encoded cell, and the second parameter is the identification bit, which is used to indicate the difference in the execution method of the transaction. Different internal settings are set in TON. There are currently 3 message Modes and 3 message Flags for the execution mode of message sending. A single Mode can be combined with multiple (perhaps none) flags to obtain the desired mode. Combining just means filling in the sum of their values. The description table of Modes and Flags is given below:

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

So let’s look at the first main function, deploy_nft_item. As the name suggests, this is a function used to create or cast a new NFT instance. After some operations to encode a msg, the internal contract is sent through send_raw_message and selected The sending flag of flag 1 only uses the fee specified in the encoding as the gas fee for this execution. After the above introduction, we can easily realize that this coding rule should correspond to the way to create a new smart contract. So let's take a look at how it's implemented.

Let’s look directly at line 51. The above two functions are auxiliary functions used to generate the information required for message, so we will look at it later. This is an encoding process for creating internal messages of smart contracts. Some numbers in the middle In fact, they are also some identification bits used to explain the requirements of the internal message. The next knowledge point is introduced here. TON chose a binary language called TL-B to describe the execution method of the message, and different flag bits are set according to To implement internal messages for certain specific functions, the two most common usage scenarios that come to mind are new contract creation and deployed contract function calls. The method on line 51 corresponds to the former, creating a new nft item contract, which is mainly specified through lines 55, 56, and 57. First of all, the large series of numbers in line 55 is a series of identification bits. Note that the first input parameter of store_uint is the value, and the second is the bit length, which determines whether the internal message is created by the contract, the last three marking bits, and the corresponding The binary value bit is 111 (decimal is 4+2+1), the first two of which indicate that the message will be accompanied by StateInit data. This data is the source code of the new contract and the data required for initialization. The latter flag bit indicates the internal message attachment, that is, the relevant logic and required parameters are expected to be executed. Therefore, you will see that the three-digit data is not set in line 66 of the code, which indicates a function call to the deployed contract. Detailed coding rules can be found here.

Then the encoding rules of StateInit correspond to 49 lines of code, calculated through calculate_nft_item_state_init. Note that the encoding of stateinit data also follows an established TL-B encoding rule. In addition to some flag bits, it mainly involves two parts of the new contract. code and initialized data. The encoding order of data needs to be consistent with the storage order of the persistence cells specified by the new contract. As you can see on line 36, the initialization data includes item_index, which is similar to the tokenId in ERC721, and the current contract address returned by the standard function my_address, which is collection_address. The order of this data is consistent with the declaration in nft-item.

The next knowledge point is that in TON, all ungenerated smart contracts can pre-calculate their generated addresses. This is similar to the create2 function in Solidity. The generation of new addresses in TON consists of two parts. The workchain identification bit is spliced ​​with the hash value of stateinit. As we already know in the previous introduction, the former needs to be specified in order to correspond to the TON infinite sharding architecture. It is currently a unified value. Obtained from the standard function workchain. The latter is obtained by the standard function cell_hash. So back to this example, calculate_nft_item_address is a function that pre-calculates the new contract address. And encode the generated value into the message on line 53 as the receiving address of the internal message. nft_content corresponds to the initialization call to the created contract. The specific implementation will be introduced in the next article.

As for send_royalty_params, it needs to be a response to the internal message of a read-only request. In the previous introduction, we specially emphasized that the internal message in TON not only contains operations that may modify data, but also the read-only operation needs to pass this It is implemented in a way, so the contract is such an operation. First of all, it is worth noting that line 67 indicates the mark of the requester's callback function after responding to the request. Write it down and it will return the data, which are the requested item index and the corresponding royalty data.

Let us introduce the next knowledge point. There are only two unified entrances to smart contracts in TON, named recv_internal and recv_external. The former is the unified calling entrance for all internal messages, and the latter is the unified calling entrance for all external messages. , developers need to use a switch-like method to respond to different requests based on the different flag bits specified by the message within the function according to the requirements. The flag bit here is the callback function flag in line 67 above. Back to this example, first perform a vacancy check on the message, and then parse the information in the message respectively. First, parse on line 83 to obtain the sender_address. This parameter will be used for subsequent permission checks. Note that the ~ operator here belongs to another syntax. sugar. I won’t expand on it here. Next, the op operation flag bits are parsed, and then the corresponding requests are processed according to different flag bits. Among them, the above functions are called respectively according to certain logic. For example, respond to a request for the royalty parameter, or cast a new nft and increment the global index.

The next knowledge point corresponds to line 108. I think you can also know the processing logic of this function by naming it. It is similar to the require function in Solidity. Exceptions are thrown in Func through the standard function throw_unless. The first input parameter is an error. code, the second is to check the bit Boolean value, if the bit is false, an exception will be thrown with the error code. In this line, equal_slices is used to determine whether the sender_address parsed above is equal to the owner_address of the contract's persistent storage, and permission judgment is made.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

Finally, in order to make the code structure clearer, a series of auxiliary functions have been developed to help obtain persistence information. They will not be introduced here. Developers can refer to this structure to develop their own smart contracts.

TON项目开发教程(一):源码角度看如何在TON Chain上创建一个NFT

DApp development in the TON ecosystem is really interesting. It is very different from the development paradigm of EVM, so I will introduce how to develop DApp in TON Chain through a series of articles. Learn together with everyone and seize this wave of opportunities. You are also welcome to interact with me on twitter to come up with some new and interesting dapp ideas and develop them together.

The above is the detailed content of TON project development tutorial (1): How to create an NFT on TON Chain from a source code perspective. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn