Autor: @Web3Mario

Resumen: Siguiendo el artículo anterior sobre la introducción de la tecnología TON, estudié en profundidad los documentos oficiales de desarrollo de TON durante este período. Siento que todavía existen algunas barreras para el aprendizaje. El contenido del documento actual parece ser más un desarrollo interno. El documento, para nuevos desarrollos de nivel básico, no es muy fácil de usar para los lectores, por lo que trato de ordenar una serie de artículos sobre el desarrollo del proyecto TON Chain en función de mi propia trayectoria de aprendizaje. comience rápidamente con el desarrollo de TON DApp. Si hay algún error en la escritura, pueden corregirme y aprender juntos.

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

Emitir un FT o NFT suele ser la necesidad más básica para los desarrolladores de DApp. Por eso también utilizo esto como punto de entrada para el aprendizaje. Primero, comprendamos las siguientes diferencias entre desarrollar una NFT en la pila de tecnología EVM y en TON Chain. Las NFT basadas en EVM suelen optar por heredar el estándar ERC-721. El llamado NFT se refiere a un tipo indivisible de activo cifrado, y cada activo es único, es decir, tiene ciertas características exclusivas. ERC-721 es un paradigma de desarrollo común para este tipo de activos. Veamos qué funciones debe implementar un contrato ERC721 común y qué información se registra. La siguiente imagen es una interfaz ERC721. Se puede ver que, a diferencia de FT, lo que se debe ingresar en la interfaz de transferencia es el tokenId que se transferirá en lugar del monto. Este tokenId es también la manifestación más básica de la singularidad de los activos NFT. Por supuesto, para llevar más atributos, generalmente se registra un metadato para cada tokenId. Estos metadatos son un enlace externo que guarda otros datos escalables de NFT. como enlaces a imágenes PFP, ciertos nombres de atributos, etc.

Para los desarrolladores que están familiarizados con Solidity o orientados a objetos, es fácil implementar un contrato inteligente de este tipo. Solo necesitan definir los tipos de datos requeridos en el contrato, como algunas relaciones de mapeo clave, y desarrollar los correspondientes de acuerdo con los requisitos. funciones La lógica de modificación de estos datos puede realizar una NFT.

Sin embargo, todo es diferente en TON Chain. Hay dos razones fundamentales para la diferencia:

l El almacenamiento de datos en TON se basa en Cell, y la Cell de la misma cuenta se implementa a través de un gráfico acíclico dirigido. Esto significa que los datos que deben almacenarse no pueden crecer sin límites, porque para un gráfico acíclico dirigido, el costo de la consulta está determinado por la profundidad de los datos. Cuando la profundidad se extiende infinitamente, el costo de la consulta puede ser demasiado alto, lo que resulta en. El contrato está estancado en un problema de punto muerto.

l Para lograr un alto rendimiento de concurrencia, TON abandonó la arquitectura de ejecución en serie y en su lugar adoptó un paradigma de desarrollo diseñado específicamente para el paralelismo, el modelo Actor, para reconstruir el entorno de ejecución. Esto tiene un impacto. Los contratos inteligentes solo se pueden llamar de forma asincrónica enviando los llamados mensajes internos. Tenga en cuenta que ya sea que se trate de un tipo de llamada de modificación de estado o de solo lectura, este principio también debe seguirse. Debe considerarse cuidadosamente cómo manejar la reversión de datos si falla una llamada asincrónica.

Por supuesto, otras diferencias técnicas se discutieron en detalle en el artículo anterior. Este artículo espera centrarse en el desarrollo de contratos inteligentes, por lo que no se discutirá. Los dos principios de diseño anteriores marcan una gran diferencia entre el desarrollo de contratos inteligentes y EVM en TON. En la discusión inicial, sabemos que un contrato NFT necesita definir algunas relaciones de mapeo, es decir, mapeo, para guardar datos relacionados con NFT. El más importante de ellos son los propietarios. Este mapeo almacena la relación de mapeo de la dirección del propietario del NFT correspondiente a un determinado tokenID, que determina la propiedad del NFT. La transferencia es una modificación de la propiedad. Dado que se trata de una estructura de datos que en teoría puede ser ilimitada, es necesario evitarla en la medida de lo posible. Por lo tanto, se recomienda oficialmente utilizar la existencia de estructuras de datos ilimitadas como estándar para la fragmentación. Es decir, cuando existen requisitos de almacenamiento de datos similares, se utiliza el paradigma del contrato maestro-esclavo y los datos correspondientes a cada clave se gestionan mediante la creación de subcontratos. Y administre los parámetros globales a través del contrato principal o ayude a manejar la interacción de información interna entre subcontratos.

Esto significa que los NFT en TON también deben diseñarse utilizando una arquitectura similar. Cada NFT es un subcontrato independiente, que guarda datos exclusivos como la dirección del propietario, metadatos, etc., y gestiona la situación general a través de un contrato principal. como nombre de NFT, símbolo, suministro total, etc.

Después de aclarar la arquitectura, el siguiente paso es resolver los requisitos funcionales básicos. Debido a la adopción de este contrato maestro-esclavo,

Por lo tanto, es necesario aclarar qué funciones realiza el contrato principal y qué funciones realiza el subcontrato, y qué información interna se comunica entre los dos y cómo rodar cuando ocurre un error de ejecución. recuperar los datos anteriores. Por lo general, antes de desarrollar un proyecto complejo a gran escala, es necesario pasar un diagrama de clases y aclarar el flujo de información entre ellos, y pensar detenidamente en la lógica de reversión después de que falla la llamada interna. Por supuesto, aunque el NFT mencionado anteriormente. El desarrollo es simple y también se puede realizar una verificación similar.

Aprenda a desarrollar contratos inteligentes TON a partir del código fuente

TON eligió diseñar un lenguaje de tipo estático tipo C llamado Func como lenguaje de desarrollo de contratos inteligentes. Luego, aprendamos cómo desarrollar contratos inteligentes TON a partir del código fuente. Elegí el ejemplo de NFT en el documento oficial de TON. Los amigos interesados ​​pueden comprobarlo ellos mismos. En este caso, se implementa un ejemplo simple de TON NFT. Echemos un vistazo a la estructura del contrato, que se divide en dos contratos funcionales y tres bibliotecas necesarias.

Estos dos contratos funcionales principales están diseñados de acuerdo con los principios anteriores. Primero, veamos el código de la colección nft del contrato principal:

Esto presenta el primer punto de conocimiento, cómo almacenar datos de forma persistente en contratos inteligentes TON. Sabemos que EVM maneja automáticamente el almacenamiento persistente de datos en Solidity de acuerdo con el tipo de parámetros. Normalmente, las variables de estado de los contratos inteligentes serán. persiste y se almacena automáticamente según el último valor después de la ejecución, y los desarrolladores no necesitan considerar este proceso. Pero este no es el caso en Func. Los desarrolladores necesitan implementar la lógica de procesamiento correspondiente. Esta situación es algo similar a la necesidad de considerar el proceso GC en C y C ++, pero otros lenguajes de desarrollo nuevos generalmente automatizan esta parte. lógica. Echemos un vistazo al código. Primero, presentamos algunas bibliotecas requeridas y luego vemos que la primera función load_data se usa para leer los datos almacenados de forma persistente. Su lógica es devolver primero la celda de almacenamiento del contrato persistente a través de get_data. esto lo hace el estándar Implementado por la biblioteca stdlib.fc, algunas de estas funciones generalmente se pueden usar como funciones del sistema.

El tipo de valor de retorno de esta función es celda, que es el tipo de celda en TVM. En la introducción anterior, ya sabemos que todos los datos persistentes en la cadena de bloques TON se almacenan en el árbol de celdas. Cada celda tiene hasta 1023 bits de datos arbitrarios y hasta cuatro referencias a otras celdas. Las celdas se utilizan como memoria en TVM basado en pila. Lo que se almacena en la celda son datos codificados de forma compacta. Para obtener los datos de texto sin formato específicos, debe convertir la celda en un tipo llamado segmento. La celda se puede convertir al tipo de segmento mediante la función comenzar_parse, y luego los datos en la celda se pueden obtener cargando los bits de datos del segmento y las referencias a otras celdas. Tenga en cuenta que este método de llamada en la línea 15 es azúcar sintáctico en func y puede llamar directamente a la segunda función que devuelve el valor de la primera función. Y finalmente cargue los datos correspondientes en orden según el orden de persistencia de los datos. Tenga en cuenta que este proceso es diferente de la solidez y no se llama según el mapa hash, por lo que el orden de las llamadas no se puede alterar.

En la función save_data, la lógica es similar, excepto que se trata de un proceso inverso, que introduce el siguiente punto de conocimiento, un nuevo generador de tipos, que es el tipo de generador de celdas. Los bits de datos y las referencias a otras celdas se pueden almacenar en constructores, que luego se pueden finalizar en nuevas celdas. Primero cree un constructor a través de la función estándar begin_cell y almacene las funciones relacionadas a través de las funciones relacionadas con la tienda. Tenga en cuenta que el orden de llamada anterior debe ser coherente con el orden de almacenamiento aquí. Finalmente, la nueva celda se construye a través de end_cell. En este momento, la celda se administra en la memoria. Finalmente, a través del set_data más externo, se puede completar el almacenamiento persistente de la celda.

A continuación, echemos un vistazo a las funciones relacionadas con el negocio. Primero, debemos presentar el siguiente punto de conocimiento: cómo crear un nuevo contrato a través de un contrato, que se utilizará con frecuencia en la arquitectura maestro-esclavo que acabamos de presentar. Sabemos que en TON las llamadas entre contratos inteligentes se implementan mediante el envío de mensajes internos. Esto se logra a través de un mensaje llamado send_raw_message. Tenga en cuenta que el primer parámetro es la celda codificada del mensaje y el segundo parámetro es el bit de identificación, que se utiliza para indicar la diferencia en el método de ejecución de la transacción. En TON existen actualmente 3 modos de mensaje y 3 banderas de mensaje para el método de ejecución del envío de mensajes. Un único modo se puede combinar con varios indicadores (quizás ninguno) para obtener el modo deseado. Combinar simplemente significa completar la suma de sus valores. La tabla de descripción de modos y banderas se proporciona a continuación:

Entonces, veamos la primera función principal, implementar_nft_item. Como sugiere el nombre, esta es una función utilizada para crear o transmitir una nueva instancia de NFT. Después de codificar un mensaje, el contrato interno se envía a través de send_raw_message y se selecciona el indicador. El bit de bandera de 1 utiliza solo la tarifa especificada en la codificación como tarifa de gas para esta ejecución. Después de la introducción anterior, podemos darnos cuenta fácilmente de que esta regla de codificación debería corresponder a la forma de crear un nuevo contrato inteligente.

Así que echemos un vistazo a cómo se implementa.

Miremos directamente la línea 51. Las dos funciones anteriores son funciones auxiliares que se utilizan para generar la información requerida para el mensaje, por lo que lo veremos más adelante. Este es un proceso de codificación para crear mensajes internos del contrato inteligente. en el medio hay algunos bits de identificación que se utilizan para describir los requisitos del mensaje interno. Aquí se presenta el siguiente punto de conocimiento. TON eligió un lenguaje binario llamado TL-B para describir el método de ejecución del mensaje y lo implementa configurándolo. diferentes bits de bandera Para obtener información interna sobre ciertas funciones específicas, los dos escenarios de uso más fáciles de imaginar son la creación de nuevos contratos y las llamadas a funciones de contratos implementadas. El método de la línea 51 corresponde al anterior, creando un nuevo contrato de artículo nft, que se especifica principalmente a través de las líneas 55, 56 y 57. En primer lugar, la gran serie de números en la línea 55 es una serie de bits de identificación. Tenga en cuenta que el primer parámetro de entrada de store_uint es el valor y el segundo es la longitud del bit, que determina si el mensaje interno es creado por el contrato. , los últimos tres bits de marcado y el correspondiente El valor binario es 111 (4 + 2 + 1 en decimal), los dos primeros indican que el mensaje irá acompañado de datos StateInit. Estos datos son el código fuente del nuevo. contrato y los datos necesarios para la inicialización. El último bit de bandera indica el adjunto del mensaje interno, es decir, se espera que se ejecuten la lógica relevante y los parámetros requeridos. Por lo tanto, verá que los datos de tres dígitos no están configurados en la línea 66 del código, lo que indica una llamada de función al contrato implementado. Las reglas de codificación detalladas se pueden encontrar aquí.

Luego, las reglas de codificación de StateInit corresponden a 49 líneas de código, calculadas a través de calcular_nft_item_state_init. Tenga en cuenta que la codificación de los datos de stateinit también sigue una regla de codificación TL-B establecida, además de algunos bits de bandera, involucra principalmente dos partes del nuevo contrato. código y E inicializar datos. El orden de codificación de los datos debe ser coherente con el orden de almacenamiento de las celdas de persistencia especificadas en el nuevo contrato. Como puede ver en la línea 36, ​​los datos de inicialización incluyen item_index, que es similar al tokenId en ERC721, y la dirección del contrato actual devuelta por la función estándar my_address, que es collection_address. El orden de estos datos es coherente con la declaración en. artículo nft.

El siguiente punto de conocimiento es que en TON, todos los contratos inteligentes no generados pueden precalcular sus direcciones generadas. Esto es similar a la función create2 en Solidity. La generación de nuevas direcciones en TON consta de dos partes, el identificador de la cadena de trabajo Bit y el hash de stateinit. Los valores se unen. En la introducción anterior, ya sabemos que el primero debe especificarse para corresponder a la arquitectura de fragmentación infinita de TON. Actualmente es un valor unificado. Obtenido de la cadena de trabajo de funciones estándar. Este último se obtiene mediante la función estándar cell_hash. Volviendo a este ejemplo, calcular_nft_item_address es una función que calcula previamente la nueva dirección del contrato. Y codifique el valor generado en el mensaje en la línea 53 como la dirección de recepción del mensaje interno. nft_content corresponde a la llamada de inicialización del contrato creado. La implementación específica se presentará en el próximo artículo.

En cuanto a send_royalty_params, debe ser una respuesta al mensaje interno de una solicitud de solo lectura. En la introducción anterior, enfatizamos especialmente que el mensaje interno en TON no solo contiene operaciones que pueden modificar los datos, sino también la lectura. Solo la operación debe realizarse de esta manera. Por lo tanto, este contrato es una operación de este tipo. En primer lugar, vale la pena señalar que la línea 67 representa la marca de la función de devolución de llamada del solicitante después de responder a la solicitud. serán los datos devueltos, que son el índice del artículo solicitado y los datos de regalías correspondientes.

A continuación, introduzcamos el siguiente punto de conocimiento. Solo hay dos entradas unificadas para contratos inteligentes en TON, llamadas recv_internal.

y recv_external, donde el primero es la entrada de llamada unificada para todos los mensajes internos y el segundo es la entrada de llamada unificada para todos los mensajes externos. Los desarrolladores deben utilizar un método similar a un interruptor dentro de la función de acuerdo con los requisitos para responder a diferentes mensajes. De acuerdo con los diferentes bits de bandera especificados por la solicitud del mensaje, el bit de marca aquí es la marca de la función de devolución de llamada en la línea 67 anterior. Volviendo a este ejemplo, primero realice una verificación de vacantes en el mensaje y luego analice la información en el mensaje respectivamente. Primero, analice la línea 83 para obtener sender_address. Este parámetro se utilizará para comprobaciones de permisos posteriores. aquí pertenece a otra sintaxis de azúcar. No lo ampliaré aquí. A continuación, se analizan los bits de bandera de la operación operativa y luego las solicitudes correspondientes se procesan de acuerdo con diferentes bits de bandera. Entre ellas, las funciones anteriores se llaman respectivamente según cierta lógica. Por ejemplo, responda a una solicitud del parámetro de regalías o emita un nuevo nft e incremente el índice global.

El siguiente punto de conocimiento corresponde a la línea 108. Creo que también puede conocer la lógica de procesamiento de esta función nombrándola. Es similar a la función require en Solidity. Las excepciones se lanzan en Func a través de la función estándar throw_unless. es el código de error, el segundo es verificar el valor booleano del bit, si el bit es falso, se lanzará una excepción con el código de error. En esta línea, se utiliza equal_slices para determinar si la dirección_del_remitente analizada anteriormente es igual a la dirección_del_propietario del almacenamiento persistente del contrato y se determina el permiso.

Finalmente, para aclarar la estructura del código, se han desarrollado una serie de funciones auxiliares para ayudar a obtener información de persistencia. Los desarrolladores no se presentarán aquí y pueden consultar esta estructura para desarrollar sus propios contratos inteligentes.

El desarrollo de DApp en el ecosistema TON es realmente interesante y es muy diferente del paradigma de desarrollo de EVM, por lo que presentaré cómo desarrollar DApp en TON Chain a través de una serie de artículos. Aprende junto con todos y aprovecha esta ola de oportunidades. También puedes interactuar conmigo en Twitter para proponer algunas ideas nuevas e interesantes sobre dapp y desarrollarlas juntos.