Tutorial de desarrollo del proyecto TON (I): Cómo crear un NFT en TON Chain desde la perspectiva del código fuente

Autor: @Web3Mario (_mario)

Resumen: Siguiendo el artículo anterior sobre la introducción a la tecnología TON, he estado investigando a fondo la documentación oficial de desarrollo de TON en este período, y siento que todavía hay cierto umbral para aprender. El contenido actual de la documentación parece más como un documento de desarrollo interno, que no es muy amigable para los nuevos desarrolladores. Por lo tanto, intentaré organizar una serie de artículos sobre el desarrollo del proyecto TON Chain basándome en mi propio proceso de aprendizaje, con la esperanza de que pueda ayudar a los recién llegados a desarrollar TON DApp. Si hay errores en el texto, agradezco las correcciones y espero aprender juntos.

¿Cuáles son las diferencias entre desarrollar NFT en EVM y desarrollar NFT en TON Chain?

La emisión de un FT o NFT es generalmente la necesidad más básica para los desarrolladores de DApp. Por lo tanto, también lo considero como punto de entrada para el aprendizaje. Primero, vamos a entender las diferencias entre desarrollar un NFT en la pila tecnológica EVM y en la cadena TON. Los NFT basados en EVM generalmente eligen heredar el estándar ERC-721. Los NFT se refieren a activos criptográficos indivisibles, cada uno con su propia singularidad y características exclusivas. ERC-721 es un paradigma de desarrollo común para este tipo de activos. Veamos qué funciones y qué información se deben registrar en un contrato ERC721 común. La siguiente imagen muestra una interfaz ERC721. Como se puede ver, a diferencia de los FT, en la interfaz de transferencia se debe ingresar el tokenId que se va a transferir en lugar de la cantidad. Este tokenId es la manifestación básica de la singularidad de un activo NFT. Por supuesto, para llevar más atributos, generalmente se registra un metadata para cada tokenId. Este metadata es un enlace externo que contiene otros datos ampliables del NFT, como un enlace a una imagen de PFP o ciertos nombres de atributos.

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

Para los desarrolladores familiarizados con Solidity o con la programación orientada a objetos, implementar un contrato inteligente como este es algo sencillo. Solo es necesario definir los tipos de datos necesarios en el contrato, como algunas relaciones clave de mapeo, y desarrollar la lógica de modificación correspondiente a estos datos según la funcionalidad requerida, para así lograr un NFT.

Sin embargo, en TON Chain esto es diferente, y hay dos razones principales que lo causan:

  • En TON, el almacenamiento de datos se basa en las celdas, y las celdas de la misma cuenta se implementan a través de un grafo acíclico dirigido. Esto significa que los datos que necesitan ser almacenados durante mucho tiempo no pueden crecer sin límites, ya que en un grafo acíclico dirigido, la profundidad de los datos determina el costo de la consulta. Cuando la profundidad se extiende infinitamente, puede haber un costo de consulta demasiado alto, lo que puede provocar problemas de bloqueo del contrato.
  • Para lograr un alto rendimiento de concurrencia, TON ha abandonado la arquitectura de ejecución en serie y ha adoptado un paradigma de desarrollo diseñado específicamente para la concurrencia, el modelo de actores, para reconstruir el entorno de ejecución. Esto ha dado lugar a un impacto: los contratos inteligentes solo pueden invocarse de forma asíncrona mediante el envío de lo que se denomina mensajes internos. Ten en cuenta que tanto las llamadas de modificación de estado como las de solo lectura deben seguir este principio. Además, también es necesario considerar cuidadosamente cómo manejar el retroceso de datos en caso de que la llamada asíncrona falle.

Por supuesto, en el artículo anterior se ha discutido detalladamente sobre otras diferencias técnicas, y este artículo espera centrarse en el desarrollo de contratos inteligentes. Por lo tanto, no se discutirá más. Estos dos principios de diseño anteriores hacen que el desarrollo de contratos inteligentes en TON sea muy diferente al de EVM. En la discusión inicial, sabemos que un contrato NFT necesita definir algunas relaciones de mapeo, es decir, mapeo, para almacenar los datos relacionados con NFT. El más importante de ellos es el propietario, este mapeo almacena la relación de mapeo de la dirección del propietario de NFT correspondiente al tokenID, lo que determina la propiedad de NFT, y la transferencia es modificar esta propiedad. Debido a que teóricamente esta es una estructura de datos sin límites, debe evitarse tanto como sea posible. Por lo tanto, se recomienda oficialmente que se utilice la existencia de estructuras de datos sin límites como criterio de fragmentación. Es decir, cuando se tiene una necesidad similar de almacenamiento de datos, se reemplaza mediante el paradigma de contrato principal-subordinado, y se administran los datos correspondientes a cada clave mediante la creación de contratos secundarios. Y a través del contrato principal, se gestionan los parámetros globales o se ayuda a procesar la interacción de información interna entre los contratos secundarios.

Esto significa que los NFT en TON también deben ser diseñados con una arquitectura similar, donde cada NFT es un subcontrato independiente que almacena datos exclusivos como la dirección del propietario, metadatos, etc., y se gestiona a través de un contrato principal que administra datos globales como el nombre del NFT, símbolo, suministro total, etc.

Una vez que se haya definido la arquitectura, es necesario abordar los requisitos de las funciones principales. Dado que se utiliza el enfoque de contrato principal y secundario, es fundamental determinar qué funciones serán llevadas a cabo por el contrato principal y cuáles por el contrato secundario, así como establecer la comunicación interna entre ellos. Además, es crucial planificar cómo revertir los datos en caso de un error de ejecución. Por lo general, antes de desarrollar proyectos complejos a gran escala, es necesario crear un diagrama de clases para definir claramente el flujo de información entre ellos, y considerar cuidadosamente la lógica de reversión en caso de fallo en la llamada interna. Por supuesto, aunque el desarrollo de NFT mencionado anteriormente es sencillo, también se puede realizar una validación similar.

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

Aprender a desarrollar contratos inteligentes de TON desde el código fuente

TON ha elegido diseñar un lenguaje de programación de contrato inteligente de tipo C similar, llamado Func, como lenguaje de desarrollo de contrato inteligente de TON. A continuación, aprendamos cómo desarrollar contratos inteligentes de TON a partir del código fuente. He elegido el ejemplo de NFT en los documentos oficiales de TON para presentarlo. Los amigos interesados ​​pueden consultarlos por sí mismos. En este caso, se implementó un ejemplo simple de TON NFT. Veamos la estructura del contrato, que consta de dos contratos de función y tres bibliotecas necesarias.

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

Estos dos contratos inteligentes principales se diseñaron según los principios mencionados anteriormente. Primero, veamos el código del contrato principal nft-collection:

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

Esto introduce el primer punto de conocimiento, cómo almacenar datos de forma persistente en un contrato inteligente de TON. Sabemos que en Solidity, el almacenamiento persistente de datos es manejado automáticamente por el EVM según el tipo de parámetro. Por lo general, las variables de estado del contrato inteligente se almacenan de forma persistente automáticamente después de la ejecución, sin que el desarrollador tenga que preocuparse por este proceso. Pero en Func no es así, el desarrollador necesita implementar la lógica correspondiente por sí mismo. Esta situación es similar a la necesidad de considerar el proceso de GC en C y C++, pero otros nuevos lenguajes de programación suelen manejar esta parte de la lógica de forma automática. Veamos el código, primero se importan algunas bibliotecas necesarias, y luego vemos la primera función load_data que se utiliza para leer los datos almacenados de forma persistente. Su lógica es que primero devuelve la celda de almacenamiento persistente del contrato a través de get_data, tenga en cuenta que esto está implementado por la biblioteca estándar stdlib.fc, y generalmente se pueden utilizar algunas de sus funciones como funciones del sistema.

El tipo de valor devuelto de esta función es cell, que es el tipo de celda en TVM. En la introducción anterior, ya sabemos que todos los datos persistentes en el TON Cadena de bloques se almacenan en el árbol celular. Cada celda tiene como máximo 1023 bits largos de datos arbitrarios y cuatro referencias largas a otras celdas. La celda se utiliza como memoria en TVM basado en pilas. La celda contiene datos estrechamente codificados y, en orden, para obtener los datos de texto sin formato específicos que contiene, debe convertir la celda a un tipo llamado segmento. Una celda se puede convertir en un tipo de sector mediante la función begin_parse y, a continuación, los datos de la celda se pueden obtener cargando bits de datos y referencias a otras celdas del sector. Tenga en cuenta que este método de llamada en la línea 15 del código es un azúcar sintáctico en una función que puede llamar directamente a la segunda función del valor devuelto de la primera función. Al final, los datos correspondientes se cargan en el orden de persistencia de datos. Tenga en cuenta que este proceso es diferente de solidity, no se basa en hashmap, por lo que el orden de esta llamada no debe estropearse.

En la función save_data, la lógica es similar, pero es un proceso de reversión, lo que introduce el siguiente punto de conocimiento, un nuevo tipo de constructor, que es el tipo de constructor de celda. Los datos y las referencias a otras celdas pueden almacenarse en el constructor y luego el constructor puede finalizarse en una nueva celda. Primero, cree un constructor con la función estándar begin_cell y almacene las funciones relacionadas con la función store en orden. Preste atención a que el orden de llamada en el texto anterior debe mantenerse consistente con el orden de almacenamiento aquí. Finalmente, construya una nueva celda con end_cell, en este momento la celda se administrará en la memoria, y finalmente, con el set_data en el nivel más externo, se puede lograr el almacenamiento persistente de la celda.

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

A continuación, veamos las funciones relacionadas con el negocio. Primero, es necesario presentar un punto de conocimiento, cómo crear un nuevo contrato a través de un contrato, que se usará con frecuencia en la arquitectura maestro-esclavo recién presentada. Sabemos que en TON, la llamada entre contratos inteligentes se realiza enviando mensajes internos. Esto se logra a través de una función llamada send_raw_message, donde el primer parámetro es la celda codificada del mensaje, y el segundo parámetro es un indicador utilizado para especificar la forma en que se ejecutará la transacción. En TON, se han establecido diferentes formas de ejecución de mensajes internos, actualmente existen 3 modos de mensaje y 3 marcadores de mensaje. Se puede combinar un solo modo con varios (quizás ninguno) marcadores para obtener el modo deseado. La combinación simplemente implica ingresar la suma de sus valores. A continuación se presenta una tabla de descripción de los modos y marcadores:

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

Entonces, veamos la primera función principal, deploy_nft_item, como su nombre indica, esta es una función para crear o acuñar una nueva instancia de NFT, después de codificar un msg, enviará el contrato interno a través de send_raw_message y seleccionará la bandera 1 de envío, solo se utilizará la tarifa especificada en el código como tarifa de gas para esta ejecución. Después de la introducción anterior, es fácil darse cuenta de que esta regla de codificación debería corresponder a la forma de crear un nuevo contrato inteligente. Veamos cómo se implementa específicamente.

Vamos directamente a la línea 51. Las dos funciones anteriores son auxiliares para generar la información necesaria para el mensaje. Por lo tanto, las revisaremos más adelante. Este es un proceso de codificación para crear un mensaje interno de contrato inteligente. Algunos de los números en el medio son en realidad identificadores que indican los requisitos de este mensaje interno. Aquí vamos a introducir otro punto de conocimiento. TON ha elegido un lenguaje binario llamado TL-B para describir la forma en que se ejecuta el mensaje, y utiliza diferentes marcas para implementar mensajes internos con funcionalidades específicas. Los dos escenarios de uso más comunes son la creación de nuevos contratos y la llamada a funciones de contratos ya desplegados. El método en la línea 51 corresponde al primero, creando un nuevo contrato de item NFT, especificado principalmente por las líneas 55, 56 y 57. Primero, la larga cadena de números en la línea 55 es una serie de identificadores. Ten en cuenta que el primer argumento de store_uint es un valor numérico y el segundo es la longitud en bits. Entre estos, los últimos tres identificadores determinan que este mensaje interno es para la creación de contratos, y los tres valores binarios correspondientes son 111 en decimal (es decir, 4+2+1). Los dos primeros indican que el mensaje llevará datos StateInit, que es el código fuente del nuevo contrato y los datos necesarios para la inicialización. El último identificador indica que el mensaje llevará una carga útil, es decir, se espera ejecutar la lógica correspondiente y se necesitan parámetros. Por lo tanto, en la línea de código 66, no se establecen estos tres datos, lo que indica una llamada a una función de un contrato ya desplegado. Puedes consultar las reglas de codificación específicas aquí.

Entonces, la regla de codificación de StateInit corresponde a la línea de código 49, calculada a través de calculate_nft_item_state_init. Tenga en cuenta que la codificación de datos de stateinit también sigue una regla de codificación TL-B establecida. Aparte de algunas banderas de marcado, principalmente involucra dos partes: el nuevo código de contrato y los datos de inicialización. El orden de codificación de los datos debe mantenerse consistente con el orden de almacenamiento de la célula de persistencia especificada por el nuevo contrato. En la línea 36, se puede ver que los datos de inicialización incluyen item_index, similar a tokenId en ERC721, y la dirección de contrato actual devuelta por la función estándar my_address, es decir, collection_address, cuyo orden de datos se mantiene consistente con la declaración en el nft-item.

El siguiente punto de conocimiento es que en TON, todos los contratos inteligentes no generados pueden calcular previamente su dirección después de su generación. Esto es similar a la función create2 en Solidity. En TON, la generación de una nueva dirección consta de dos partes: un identificador de workchain y la concatenación del valor hash de stateinit. El primero, como mencionamos anteriormente, es necesario para la arquitectura de fragmentación infinita de TON y actualmente es un valor uniforme obtenido mediante la función estándar workchain. El segundo se obtiene mediante la función estándar cell_hash. Por lo tanto, en este ejemplo, calculate_nft_item_address es la función que calcula previamente la dirección del nuevo contrato. Y el valor generado se codifica en el mensaje en la línea 53 como la dirección de recepción de este mensaje interno. Por otro lado, nft_content corresponde a la llamada de inicialización al contrato creado. Los detalles de la implementación se explicarán en el próximo artículo.

En cuanto a send_royalty_params, debe ser una respuesta a un mensaje interno de una solicitud de solo lectura. En la introducción anterior, enfatizamos que en TON, los mensajes internos no solo contienen operaciones que pueden modificar datos, sino que también se requiere este método para operaciones de solo lectura. Por lo tanto, este contrato es de este tipo de operaciones. Lo primero que hay que tener en cuenta es la marca en la línea 67, que indica la devolución de llamada a la función del solicitante después de responder a esta solicitud. Anote los datos devueltos, que son el índice del elemento solicitado y los datos de regalía correspondientes.

A continuación, introducimos otro punto de conocimiento: en TON, los contratos inteligentes solo tienen dos entradas unificadas, llamadas recv_internal y recv_external. El primero es la entrada unificada para todos los mensajes internos, mientras que el segundo es la entrada unificada para todos los mensajes externos. Los desarrolladores deben responder a diferentes solicitudes dentro de la función de manera similar a un switch, según las diferentes marcas especificadas por message. Aquí, la marca es la marca de la función de retroceso en la línea 67 anterior. Volviendo al ejemplo, primero se verifica si hay espacios vacíos en message. Si se pasa la verificación, se analizan la información en message en dos pasos. En primer lugar, en la línea 83, el parámetro sender_address se analiza y se utiliza para la verificación de permisos posterior. Tenga en cuenta el operador ~ aquí, que es otro azúcar sintáctico. No lo ampliaremos aquí. A continuación, se analiza la marca de la operación op y se procesan las solicitudes correspondientes según la marca. Llamamos a diferentes funciones según la lógica. Por ejemplo, para responder a la solicitud del parámetro de regalía o para acuñar un nuevo NFT y aumentar el índice global.

El siguiente punto de conocimiento corresponde a la línea 108. Supongo que todos ustedes pueden comprender la lógica de procesamiento de esta función a través de su nombre. Es similar a la función ‘require’ en Solidity. En ‘Func’, se utiliza la función estándar ‘throw_unless’ para lanzar una excepción. El primer parámetro es el código de error y el segundo es un valor booleano de verificación. Si el valor es ‘false’, se lanzará una excepción con el código de error correspondiente. En esta línea, se utiliza ‘equal_slices’ para verificar si ‘sender_address’, que se analizó anteriormente, es igual a ‘owner_address’, que se encuentra almacenada permanentemente en este contrato, con el fin de realizar una verificación de permisos.

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

Finalmente, para hacer que la estructura del código sea más clara, comencé a desarrollar una serie de funciones auxiliares para ayudar a obtener información persistente. No entraré en detalles aquí, los desarrolladores pueden consultar esta estructura para desarrollar sus propios contratos inteligentes.

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

El desarrollo de DApp en el ecosistema de TON es realmente interesante, y difiere mucho del paradigma de desarrollo de EVM. Por lo tanto, voy a presentar una serie de artículos sobre cómo desarrollar DApp en la cadena de TON. Aprendamos juntos y aprovechemos esta oportunidad. También te invito a interactuar conmigo en Twitter y compartir ideas interesantes de DApp para desarrollar juntos.

TON1.02%
Ver originales
Esta página puede contener contenido de terceros, que se proporciona únicamente con fines informativos (sin garantías ni declaraciones) y no debe considerarse como un respaldo por parte de Gate a las opiniones expresadas ni como asesoramiento financiero o profesional. Consulte el Descargo de responsabilidad para obtener más detalles.
  • Recompensa
  • Comentar
  • Republicar
  • Compartir
Comentar
0/400
Sin comentarios
  • Anclado
Opera con criptomonedas en cualquier momento y lugar
qrCode
Escanee para descargar la aplicación Gate
Comunidad
Español
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)