Home  >  Article  >  How to create an NFT on TON Chain from a source code perspective

How to create an NFT on TON Chain from a source code perspective

WBOY
WBOYOriginal
2024-06-25 18:45:02725browse

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 TON Chain. EVM-based NFTs often 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's 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, names of certain properties, etc.

从源码角度看如何在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, the storage of data is based on Cell, and the Cell of the same account is implemented through directed and undirected Ring diagram 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 one is owners. This mapping stores the mapping relationship of the NFT owner address 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 boundless 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 NFT development is simple, but it can also Do similar verification.

    从源码角度看如何在TON Chain上创建一个NFT

    Learn to develop TON smart contracts from the source code

    TON chose to design a C-like, statically typed language named Func as the smart contract development language, then let us learn how to develop it from the source code For the TON smart contract, I chose the NFT example in the TON official document to introduce it. 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 Chain上创建一个NFT

    These 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 Chain上创建一个NFT

    This introduces the first knowledge point, how to use TON Persistent storage of data in 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 how C and C++ need to consider the GC process, 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 a 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 and references to other cells from the slice. Note that the calling method in line 15 is syntactic sugar in a func that directly calls the second function on the return 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, end_cell is used to complete the construction of the new 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 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 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 new NFT instances. After some operations After encoding a msg, send the internal contract through send_raw_message, and select the sending flag of flag 1. Only the fee specified in the encoding will be used 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 of the numbers in the middle are actually They are also some identification bits used to describe 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 sets different flag bits to To implement internal messages for certain specific functions, the two most easily thought of usage scenarios 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 by 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 bits are 111 (4+2+1 in decimal), 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. In the previous introduction, we already know that 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 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 this way, so the contract is such an operation. First of all, it is worth noting that line 67 represents the mark of the requester's callback function after responding to the request. Write it down to get the returned 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 different flag bits specified by message within the function according to 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 mint 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. Similar to the require function in Solidity, exceptions are thrown in Func through the standard function throw_unless. The first input parameter is Error code. The second one is to check the Boolean value of the bit. 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 persistent storage of the contract, and permission judgment is made.

    从源码角度看如何在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, which will not be introduced here. Developers can refer to this structure to develop their own smart contracts.

    从源码角度看如何在TON Chain上创建一个NFT

    DApp development in the TON ecosystem is really interesting and 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.

    The above is the detailed content of 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