Continuando con el artículo anterior sobre la introducción a la tecnología TON, en este período he estado investigando a fondo la documentación oficial de desarrollo de TON. Me parece que aprenderlo tiene cierta dificultad, ya que el contenido actual de la documentación parece más un documento de desarrollo interno, lo cual no es muy amigable para los nuevos desarrolladores. Por lo tanto, he intentado organizar una serie de artículos sobre el desarrollo del proyecto TON Chain siguiendo 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 cualquier corrección 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 una necesidad básica para los desarrolladores de DApp. Por lo tanto, lo tomé como punto de partida para aprender. Primero, veamos la diferencia entre desarrollar un NFT en la pila tecnológica EVM y en TON Chain. Los NFT basados en EVM generalmente eligen heredar el estándar ERC-721. Los NFT, como tipo de activo criptográfico indivisible, tienen singularidad y características exclusivas. ERC-721 es un paradigma de desarrollo común para este tipo de activo. Veamos qué funciones y registros debe implementar un contrato ERC 721 común. La siguiente imagen es una interfaz ERC 721. Como se puede ver, a diferencia de FT, en la interfaz de transferencia se ingresa el tokenId que se va a transferir en lugar de la cantidad. Este tokenId es la manifestación básica de la singularidad de los activos NFT. Por supuesto, para contener más propiedades, generalmente se registra un metadato para cada tokenId. Este metadato es un enlace externo que guarda otros datos extensibles de este NFT, como el enlace de una imagen PFP, ciertos nombres de propiedades, etcétera.
Para desarrolladores familiarizados con Solidity o programación orientada a objetos, implementar un contrato inteligente como este es fácil, solo es necesario definir los tipos de datos necesarios en el contrato, como algunas relaciones de mapeo clave y desarrollar la lógica de modificación correspondiente para estos datos según la funcionalidad requerida, lo que permitirá implementar un NFT.
Sin embargo, en TON Chain, todo esto es diferente, y hay dos razones principales que lo causan:
En TON, el almacenamiento de datos se basa en Cells, y las Cells de la misma cuenta se implementan mediante un gráfico acíclico dirigido. Esto significa que los datos que necesitan ser almacenados indefinidamente no pueden crecer sin límites, ya que en un gráfico acíclico dirigido, la profundidad de los datos determina el costo de la consulta. Si la profundidad se extiende infinitamente, puede generar costos de consulta demasiado altos y provocar problemas de bloqueo en el contrato.
Para lograr un alto rendimiento en paralelo, TON abandonó la arquitectura de ejecución en serie y adoptó un paradigma de desarrollo especializado en paralelo, el modelo Actor, para reestructurar el entorno de ejecución. Esto ha tenido un impacto en el hecho de que los contratos inteligentes solo pueden invocarse de manera asíncrona entre sí a través del envío de lo que se llaman mensajes internos. Tenga en cuenta que tanto las llamadas de tipo modificación de estado como las de solo lectura deben seguir este principio, y también se debe considerar cuidadosamente cómo manejar el retroceso de datos en caso de falla en la llamada asíncrona.
Por supuesto, en el artículo anterior se detallaron las diferencias técnicas, pero este artículo se centra en el desarrollo de contratos inteligentes, por lo que no profundizaremos en ello. Estos dos principios de diseño hacen que el desarrollo de contratos inteligentes en TON sea muy diferente al de EVM. Como se mencionó al principio, sabemos que un contrato NFT necesita definir algunas relaciones de mapeo, es decir, mapping, para almacenar los datos relacionados con NFT. Lo más importante es owners, este mapeo almacena la relación de direcciones de los propietarios de NFT correspondientes a un tokenID, lo que determina la propiedad del NFT, y la transferencia modifica esta propiedad. Dado que teóricamente esta es una estructura de datos sin límites, se debe evitar en la medida de lo posible. Por lo tanto, se recomienda oficialmente utilizar la existencia de una estructura de datos sin límites como criterio de fragmentación. Es decir, cuando exista una necesidad similar de almacenamiento de datos, se debe usar el paradigma de contrato principal y secundario, gestionando cada dato correspondiente a una clave a través de la creación de subcontratos. Y a través del contrato principal se gestionan los parámetros globales o se ayuda a manejar la interacción de información interna entre subcontratos.
Esto también significa que en TON, los NFT también necesitan 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, y se gestiona a través de un contrato principal que administra datos globales como el nombre del NFT, símbolo, y la oferta total, entre otros.
Una vez que la arquitectura esté clara, el siguiente paso es abordar los requisitos de las funciones principales. Dado que se utiliza este contrato principal-subordinado, es importante determinar qué funciones serán llevadas a cabo por el contrato principal y cuáles serán llevadas a cabo por el contrato secundario, así como la comunicación interna entre ambos. Además, es necesario considerar cuidadosamente la lógica de reversión de los datos en caso de errores de ejecución. Por lo general, antes de desarrollar proyectos grandes y complejos, es necesario crear un diagrama de clases para aclarar el flujo de información entre ellos y pensar detenidamente en la lógica de reversión en caso de fallos en las llamadas internas. Por supuesto, aunque el desarrollo de NFT mencionado anteriormente es simple, también se pueden realizar verificaciones similares.
Aprender el desarrollo de contratos inteligentes TON desde el código fuente
TON ha optado por diseñar un lenguaje de programación estático similar a C llamado Func como lenguaje de desarrollo de contratos inteligentes. A continuación, aprenderemos a desarrollar contratos inteligentes TON desde el código fuente. He elegido el ejemplo de NFT del documento oficial de TON para la presentación. Los interesados pueden buscarlo por sí mismos. En este caso, se implementa un ejemplo simple de TON NFT. Echemos un vistazo a la estructura del contrato, que consta de dos contratos de funciones y tres bibliotecas necesarias.
Estos dos contratos inteligentes principales están diseñados según los principios anteriores. Primero, veamos el código del contrato inteligente principal nft-collection:
Esto introduce el primer punto de conocimiento, cómo persistir los datos en un contrato inteligente TON. Sabemos que en Solidity, la persistencia de los datos es manejada automáticamente por el EVM según el tipo de parámetro. En general, las variables de estado del contrato inteligente se persisten automáticamente después de la ejecución, según el valor más reciente, y los desarrolladores no necesitan preocuparse por este proceso. Sin embargo, en Func esto no es así, los desarrolladores deben implementar la lógica correspondiente por sí mismos. Esta situación es similar a la necesidad de tener en cuenta el GC en C y C++, pero otros nuevos lenguajes de desarrollo automatizan esta parte de la lógica. Echemos un vistazo al código, primero importamos algunas bibliotecas necesarias y luego vemos la primera función load_data que se utiliza para leer los datos persistidos. La lógica es que primero se obtiene la celda de almacenamiento persistente del contrato mediante get_data, tenga en cuenta que esto está implementado por la biblioteca estándar stdlib.fc, y generalmente se pueden considerar algunas de sus funciones como funciones del sistema para su uso.
El tipo de valor de retorno de esta función es cell, que es el tipo cell en TVM. Como se mencionó anteriormente, sabemos que todos los datos persistentes en la cadena de bloques TON se almacenan en el árbol de celdas. Cada celda puede tener hasta 1023 bits de datos arbitrarios y hasta cuatro referencias a otras celdas. Las celdas se utilizan como memoria en TVM basado en pilas. Las celdas contienen datos codificados de forma compacta, y para obtener datos en texto sin formato, es necesario convertir la celda en un tipo llamado slice. Las celdas se pueden convertir en el tipo slice mediante la función begin_parse, y luego se puede cargar los datos y las referencias a otras celdas desde el slice para obtener los datos de la celda. Tenga en cuenta que la llamada en la línea 15 es una forma de azúcar sintáctica en una función, y se puede llamar directamente a la segunda función del valor de retorno de la primera función. Finalmente, cargue los datos correspondientes en orden de persistencia. Tenga en cuenta que este proceso es diferente de Solidity y no se basa en llamadas hash map, por lo que el orden de las llamadas no debe ser alterado.
En la función save_data, la lógica es similar a la del operador lógico AND, pero esto 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 se pueden almacenar en el constructor y luego el constructor se puede finalizar en una nueva celda. Primero, se crea un constructor mediante la función estándar begin_cell, y luego se almacenan las funciones relacionadas con el almacenamiento a través de las funciones de almacenamiento correspondientes. Tenga en cuenta que el orden de llamada en el texto anterior y el orden de almacenamiento deben ser coherentes. Finalmente, se completa la construcción de la nueva celda mediante end_cell, y esta celda se administra en la memoria. Finalmente, mediante set_data en el nivel más externo, se puede completar el almacenamiento persistente de esta celda.
A continuación, veamos las funciones relacionadas con el negocio, primero necesitamos presentar un punto de conocimiento, cómo crear un nuevo contrato a través de un contrato, esto será utilizado 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 que muestra la diferencia en la forma en que se ejecuta la transacción. En TON, se establecen diferentes formas de envío de mensajes internos, actualmente hay 3 modos de mensajes y 3 banderas de mensajes. Un solo modo se puede combinar con múltiples (quizás ninguno) indicadores para obtener el modo deseado. La combinación simplemente implica sumar sus valores y completarlos. A continuación se muestra una tabla de descripción de modos y banderas:
Entonces, vamos a echar un vistazo a la primera función principal, deploy_nft_item, como su nombre lo indica, esta es una función para crear o acuñar una nueva instancia de NFT, después de codificar un msg a través de send_raw_message y seleccionar el indicador de envío flag 1, solo se utiliza 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 en concreto.
Veamos directamente la línea 51. Las dos funciones anteriores son auxiliares para generar la información necesaria del mensaje, por lo que las veremos más tarde. Este es el proceso de codificación de un mensaje interno para crear un contrato inteligente. Algunos de los números intermedios son en realidad indicadores que explican las necesidades del mensaje interno. Aquí se introduce otro punto de conocimiento: TON ha elegido un lenguaje binario llamado TL-B para describir la forma en que se ejecutan los mensajes, y según la configuración de diferentes indicadores, se implementan ciertas funciones específicas del mensaje interno. Los dos escenarios de uso más fáciles de imaginar son la creación de un nuevo contrato y la llamada a funciones de contratos ya desplegados. El enfoque de la línea 51 corresponde al primero, es decir, crear un nuevo contrato de elemento NFT, que principalmente se logra a través de las líneas 55, 56 y 57. Primero, la larga cadena de números de la línea 55 es una serie de indicadores, y es importante tener en cuenta que el primer argumento de store_uint es el valor numérico y el segundo es la longitud en bits, lo que determina que los últimos tres indicadores son para la creación del contrato, con el valor binario correspondiente a 111 (es decir, 4 + 2 + 1). Los dos primeros indican que el mensaje adjuntará los datos de StateInit, que son el código fuente del nuevo contrato y los datos necesarios para la inicialización. El indicador final indica que el mensaje es adjunto, es decir, se desea ejecutar la lógica relevante y se necesitan los parámetros correspondientes. Por lo tanto, verás que en la línea 66 el código no establece estos tres datos, lo que indica una llamada a 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 los datos de stateinit también sigue una regla de codificación TL-B establecida. Aparte de algunas marcas, 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 coincidir con el orden de almacenamiento de la celda de persistencia especificada en el nuevo contrato. En la línea 36, se puede ver que los datos de inicialización tienen item_index, similar a tokenId en ERC 721, y la dirección actual del contrato devuelta por la función estándar my_address, es decir, collection_address. El orden de estos datos coincide con la declaración en nft-item.
El siguiente punto importante es que en TON, todos los contratos inteligentes no generados pueden calcular previamente sus direcciones posteriores a la generación, similar a la función create 2 en Solidity. En TON, la generación de nuevas direcciones consta de dos partes: el bit de identificación de workchain y el valor hash de stateinit, el primero de los cuales ya sabemos, según la introducción anterior, está destinado a ser especificado para la arquitectura de fragmentación infinita de TON y es actualmente un valor unificado, obtenido por la función estándar workchain. El valor hash de stateinit se obtiene mediante la función estándar cell_hash. Por lo tanto, en este ejemplo, calculate_nft_item_address es la función para calcular previamente la nueva dirección del 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. Mientras que nft_content corresponde a la llamada de inicialización al contrato creado, cuya implementación se explicará 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 las operaciones de solo lectura también se implementan de esta manera. Por lo tanto, este contrato es para este tipo de operaciones. Lo primero que hay que tener en cuenta es que la línea 67 representa la marca de la función de retroceso 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ías correspondientes.
A continuación, introduzcamos el siguiente punto de conocimiento, en el TON contratos inteligentes solo hay dos entradas unificadas, denominadas recv_internal y recv_external, donde la primera es la entrada de llamada unificada de todos los mensajes internos y la segunda es la entrada de llamada unificada de todos los mensajes externos 67 líneas de marcado de función de retroceso. Volviendo al ejemplo, primero realice una verificación de coro bits en el mensaje, analice la información en el mensaje por separado después de pasar y primero analice el remitente_address en la línea 83, este parámetro se usará para verificaciones de permisos posteriores, tenga en cuenta que el operador ~ aquí pertenece a otro azúcar sintáctico. No me extenderé aquí. A continuación, se analizan los bits de etiqueta de operación OP y, a continuación, las solicitudes correspondientes se procesan por separado según los diferentes bits de etiqueta. Es decir, las funciones anteriores se llaman por separado de acuerdo con alguna lógica. Por ejemplo, en respuesta a una solicitud de un parámetro de regalías, o acuñando un nuevo NFT y autoincrementando el índice global.
El siguiente punto de conocimiento corresponde a la línea 108, y creo que todos pueden entender 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 excepciones. El primer argumento es el código de error y el segundo es un valor booleano de verificación. Si el valor es falso, se lanza una excepción con el código de error correspondiente. En esta línea, se utiliza equal_slices para verificar si la dirección del remitente obtenida anteriormente es igual a la owner_address almacenada de forma persistente en este contrato, lo que implica una verificación de permisos.
Finalmente, para hacer que la estructura del código sea más clara, comencé a crear una serie de funciones auxiliares para obtener información persistente. No entraré en detalles aquí, los desarrolladores pueden referirse a esta estructura para desarrollar sus propios contratos inteligentes.
El desarrollo de DApp en el ecosistema TON es realmente interesante, y difiere mucho del paradigma de desarrollo de EVM, por lo que voy a introducir cómo desarrollar DApp en TON Chain a través de una serie de artículos. Espero aprender junto con todos ustedes y aprovechar esta oportunidad. También los invito a interactuar conmigo en Twitter, compartir nuevas y emocionantes ideas de DApp y desarrollar juntos.
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.
Tutorial de desarrollo del proyecto TON (I): Cómo crear un NFT en la cadena TON desde el punto de vista del código fuente
El autor original: @Web3 Mario(_mario)
Continuando con el artículo anterior sobre la introducción a la tecnología TON, en este período he estado investigando a fondo la documentación oficial de desarrollo de TON. Me parece que aprenderlo tiene cierta dificultad, ya que el contenido actual de la documentación parece más un documento de desarrollo interno, lo cual no es muy amigable para los nuevos desarrolladores. Por lo tanto, he intentado organizar una serie de artículos sobre el desarrollo del proyecto TON Chain siguiendo 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 cualquier corrección 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 una necesidad básica para los desarrolladores de DApp. Por lo tanto, lo tomé como punto de partida para aprender. Primero, veamos la diferencia entre desarrollar un NFT en la pila tecnológica EVM y en TON Chain. Los NFT basados en EVM generalmente eligen heredar el estándar ERC-721. Los NFT, como tipo de activo criptográfico indivisible, tienen singularidad y características exclusivas. ERC-721 es un paradigma de desarrollo común para este tipo de activo. Veamos qué funciones y registros debe implementar un contrato ERC 721 común. La siguiente imagen es una interfaz ERC 721. Como se puede ver, a diferencia de FT, en la interfaz de transferencia se ingresa el tokenId que se va a transferir en lugar de la cantidad. Este tokenId es la manifestación básica de la singularidad de los activos NFT. Por supuesto, para contener más propiedades, generalmente se registra un metadato para cada tokenId. Este metadato es un enlace externo que guarda otros datos extensibles de este NFT, como el enlace de una imagen PFP, ciertos nombres de propiedades, etcétera.
Para desarrolladores familiarizados con Solidity o programación orientada a objetos, implementar un contrato inteligente como este es fácil, solo es necesario definir los tipos de datos necesarios en el contrato, como algunas relaciones de mapeo clave y desarrollar la lógica de modificación correspondiente para estos datos según la funcionalidad requerida, lo que permitirá implementar un NFT.
Sin embargo, en TON Chain, todo esto es diferente, y hay dos razones principales que lo causan:
Por supuesto, en el artículo anterior se detallaron las diferencias técnicas, pero este artículo se centra en el desarrollo de contratos inteligentes, por lo que no profundizaremos en ello. Estos dos principios de diseño hacen que el desarrollo de contratos inteligentes en TON sea muy diferente al de EVM. Como se mencionó al principio, sabemos que un contrato NFT necesita definir algunas relaciones de mapeo, es decir, mapping, para almacenar los datos relacionados con NFT. Lo más importante es owners, este mapeo almacena la relación de direcciones de los propietarios de NFT correspondientes a un tokenID, lo que determina la propiedad del NFT, y la transferencia modifica esta propiedad. Dado que teóricamente esta es una estructura de datos sin límites, se debe evitar en la medida de lo posible. Por lo tanto, se recomienda oficialmente utilizar la existencia de una estructura de datos sin límites como criterio de fragmentación. Es decir, cuando exista una necesidad similar de almacenamiento de datos, se debe usar el paradigma de contrato principal y secundario, gestionando cada dato correspondiente a una clave a través de la creación de subcontratos. Y a través del contrato principal se gestionan los parámetros globales o se ayuda a manejar la interacción de información interna entre subcontratos.
Esto también significa que en TON, los NFT también necesitan 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, y se gestiona a través de un contrato principal que administra datos globales como el nombre del NFT, símbolo, y la oferta total, entre otros.
Una vez que la arquitectura esté clara, el siguiente paso es abordar los requisitos de las funciones principales. Dado que se utiliza este contrato principal-subordinado, es importante determinar qué funciones serán llevadas a cabo por el contrato principal y cuáles serán llevadas a cabo por el contrato secundario, así como la comunicación interna entre ambos. Además, es necesario considerar cuidadosamente la lógica de reversión de los datos en caso de errores de ejecución. Por lo general, antes de desarrollar proyectos grandes y complejos, es necesario crear un diagrama de clases para aclarar el flujo de información entre ellos y pensar detenidamente en la lógica de reversión en caso de fallos en las llamadas internas. Por supuesto, aunque el desarrollo de NFT mencionado anteriormente es simple, también se pueden realizar verificaciones similares.
Aprender el desarrollo de contratos inteligentes TON desde el código fuente
TON ha optado por diseñar un lenguaje de programación estático similar a C llamado Func como lenguaje de desarrollo de contratos inteligentes. A continuación, aprenderemos a desarrollar contratos inteligentes TON desde el código fuente. He elegido el ejemplo de NFT del documento oficial de TON para la presentación. Los interesados pueden buscarlo por sí mismos. En este caso, se implementa un ejemplo simple de TON NFT. Echemos un vistazo a la estructura del contrato, que consta de dos contratos de funciones y tres bibliotecas necesarias.
Estos dos contratos inteligentes principales están diseñados según los principios anteriores. Primero, veamos el código del contrato inteligente principal nft-collection:
Esto introduce el primer punto de conocimiento, cómo persistir los datos en un contrato inteligente TON. Sabemos que en Solidity, la persistencia de los datos es manejada automáticamente por el EVM según el tipo de parámetro. En general, las variables de estado del contrato inteligente se persisten automáticamente después de la ejecución, según el valor más reciente, y los desarrolladores no necesitan preocuparse por este proceso. Sin embargo, en Func esto no es así, los desarrolladores deben implementar la lógica correspondiente por sí mismos. Esta situación es similar a la necesidad de tener en cuenta el GC en C y C++, pero otros nuevos lenguajes de desarrollo automatizan esta parte de la lógica. Echemos un vistazo al código, primero importamos algunas bibliotecas necesarias y luego vemos la primera función load_data que se utiliza para leer los datos persistidos. La lógica es que primero se obtiene la celda de almacenamiento persistente del contrato mediante get_data, tenga en cuenta que esto está implementado por la biblioteca estándar stdlib.fc, y generalmente se pueden considerar algunas de sus funciones como funciones del sistema para su uso.
El tipo de valor de retorno de esta función es cell, que es el tipo cell en TVM. Como se mencionó anteriormente, sabemos que todos los datos persistentes en la cadena de bloques TON se almacenan en el árbol de celdas. Cada celda puede tener hasta 1023 bits de datos arbitrarios y hasta cuatro referencias a otras celdas. Las celdas se utilizan como memoria en TVM basado en pilas. Las celdas contienen datos codificados de forma compacta, y para obtener datos en texto sin formato, es necesario convertir la celda en un tipo llamado slice. Las celdas se pueden convertir en el tipo slice mediante la función begin_parse, y luego se puede cargar los datos y las referencias a otras celdas desde el slice para obtener los datos de la celda. Tenga en cuenta que la llamada en la línea 15 es una forma de azúcar sintáctica en una función, y se puede llamar directamente a la segunda función del valor de retorno de la primera función. Finalmente, cargue los datos correspondientes en orden de persistencia. Tenga en cuenta que este proceso es diferente de Solidity y no se basa en llamadas hash map, por lo que el orden de las llamadas no debe ser alterado.
En la función save_data, la lógica es similar a la del operador lógico AND, pero esto 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 se pueden almacenar en el constructor y luego el constructor se puede finalizar en una nueva celda. Primero, se crea un constructor mediante la función estándar begin_cell, y luego se almacenan las funciones relacionadas con el almacenamiento a través de las funciones de almacenamiento correspondientes. Tenga en cuenta que el orden de llamada en el texto anterior y el orden de almacenamiento deben ser coherentes. Finalmente, se completa la construcción de la nueva celda mediante end_cell, y esta celda se administra en la memoria. Finalmente, mediante set_data en el nivel más externo, se puede completar el almacenamiento persistente de esta celda.
A continuación, veamos las funciones relacionadas con el negocio, primero necesitamos presentar un punto de conocimiento, cómo crear un nuevo contrato a través de un contrato, esto será utilizado 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 que muestra la diferencia en la forma en que se ejecuta la transacción. En TON, se establecen diferentes formas de envío de mensajes internos, actualmente hay 3 modos de mensajes y 3 banderas de mensajes. Un solo modo se puede combinar con múltiples (quizás ninguno) indicadores para obtener el modo deseado. La combinación simplemente implica sumar sus valores y completarlos. A continuación se muestra una tabla de descripción de modos y banderas:
Entonces, vamos a echar un vistazo a la primera función principal, deploy_nft_item, como su nombre lo indica, esta es una función para crear o acuñar una nueva instancia de NFT, después de codificar un msg a través de send_raw_message y seleccionar el indicador de envío flag 1, solo se utiliza 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 en concreto.
Veamos directamente la línea 51. Las dos funciones anteriores son auxiliares para generar la información necesaria del mensaje, por lo que las veremos más tarde. Este es el proceso de codificación de un mensaje interno para crear un contrato inteligente. Algunos de los números intermedios son en realidad indicadores que explican las necesidades del mensaje interno. Aquí se introduce otro punto de conocimiento: TON ha elegido un lenguaje binario llamado TL-B para describir la forma en que se ejecutan los mensajes, y según la configuración de diferentes indicadores, se implementan ciertas funciones específicas del mensaje interno. Los dos escenarios de uso más fáciles de imaginar son la creación de un nuevo contrato y la llamada a funciones de contratos ya desplegados. El enfoque de la línea 51 corresponde al primero, es decir, crear un nuevo contrato de elemento NFT, que principalmente se logra a través de las líneas 55, 56 y 57. Primero, la larga cadena de números de la línea 55 es una serie de indicadores, y es importante tener en cuenta que el primer argumento de store_uint es el valor numérico y el segundo es la longitud en bits, lo que determina que los últimos tres indicadores son para la creación del contrato, con el valor binario correspondiente a 111 (es decir, 4 + 2 + 1). Los dos primeros indican que el mensaje adjuntará los datos de StateInit, que son el código fuente del nuevo contrato y los datos necesarios para la inicialización. El indicador final indica que el mensaje es adjunto, es decir, se desea ejecutar la lógica relevante y se necesitan los parámetros correspondientes. Por lo tanto, verás que en la línea 66 el código no establece estos tres datos, lo que indica una llamada a 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 los datos de stateinit también sigue una regla de codificación TL-B establecida. Aparte de algunas marcas, 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 coincidir con el orden de almacenamiento de la celda de persistencia especificada en el nuevo contrato. En la línea 36, se puede ver que los datos de inicialización tienen item_index, similar a tokenId en ERC 721, y la dirección actual del contrato devuelta por la función estándar my_address, es decir, collection_address. El orden de estos datos coincide con la declaración en nft-item.
El siguiente punto importante es que en TON, todos los contratos inteligentes no generados pueden calcular previamente sus direcciones posteriores a la generación, similar a la función create 2 en Solidity. En TON, la generación de nuevas direcciones consta de dos partes: el bit de identificación de workchain y el valor hash de stateinit, el primero de los cuales ya sabemos, según la introducción anterior, está destinado a ser especificado para la arquitectura de fragmentación infinita de TON y es actualmente un valor unificado, obtenido por la función estándar workchain. El valor hash de stateinit se obtiene mediante la función estándar cell_hash. Por lo tanto, en este ejemplo, calculate_nft_item_address es la función para calcular previamente la nueva dirección del 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. Mientras que nft_content corresponde a la llamada de inicialización al contrato creado, cuya implementación se explicará 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 las operaciones de solo lectura también se implementan de esta manera. Por lo tanto, este contrato es para este tipo de operaciones. Lo primero que hay que tener en cuenta es que la línea 67 representa la marca de la función de retroceso 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ías correspondientes.
A continuación, introduzcamos el siguiente punto de conocimiento, en el TON contratos inteligentes solo hay dos entradas unificadas, denominadas recv_internal y recv_external, donde la primera es la entrada de llamada unificada de todos los mensajes internos y la segunda es la entrada de llamada unificada de todos los mensajes externos 67 líneas de marcado de función de retroceso. Volviendo al ejemplo, primero realice una verificación de coro bits en el mensaje, analice la información en el mensaje por separado después de pasar y primero analice el remitente_address en la línea 83, este parámetro se usará para verificaciones de permisos posteriores, tenga en cuenta que el operador ~ aquí pertenece a otro azúcar sintáctico. No me extenderé aquí. A continuación, se analizan los bits de etiqueta de operación OP y, a continuación, las solicitudes correspondientes se procesan por separado según los diferentes bits de etiqueta. Es decir, las funciones anteriores se llaman por separado de acuerdo con alguna lógica. Por ejemplo, en respuesta a una solicitud de un parámetro de regalías, o acuñando un nuevo NFT y autoincrementando el índice global.
El siguiente punto de conocimiento corresponde a la línea 108, y creo que todos pueden entender 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 excepciones. El primer argumento es el código de error y el segundo es un valor booleano de verificación. Si el valor es falso, se lanza una excepción con el código de error correspondiente. En esta línea, se utiliza equal_slices para verificar si la dirección del remitente obtenida anteriormente es igual a la owner_address almacenada de forma persistente en este contrato, lo que implica una verificación de permisos.
Finalmente, para hacer que la estructura del código sea más clara, comencé a crear una serie de funciones auxiliares para obtener información persistente. No entraré en detalles aquí, los desarrolladores pueden referirse a esta estructura para desarrollar sus propios contratos inteligentes.
El desarrollo de DApp en el ecosistema TON es realmente interesante, y difiere mucho del paradigma de desarrollo de EVM, por lo que voy a introducir cómo desarrollar DApp en TON Chain a través de una serie de artículos. Espero aprender junto con todos ustedes y aprovechar esta oportunidad. También los invito a interactuar conmigo en Twitter, compartir nuevas y emocionantes ideas de DApp y desarrollar juntos.