Resumo: Continuando do artigo anterior sobre a introdução da tecnologia TON, eu fiz uma pesquisa detalhada na documentação oficial de desenvolvimento da TON recentemente. Achei que o processo de aprendizado tem algumas dificuldades, pois o conteúdo atual da documentação parece mais voltado para desenvolvedores internos e não é muito amigável para desenvolvedores iniciantes. Por isso, decidi organizar uma série de artigos sobre o desenvolvimento do projeto TON Chain, baseado na minha própria trajetória de aprendizado, na esperança de ajudar os iniciantes a se familiarizarem rapidamente com o desenvolvimento de aplicativos TON. Se houver algum erro no meu texto, por favor, sinta-se à vontade para corrigir e aprender juntos.
Quais são as diferenças entre desenvolver NFT no EVM e desenvolver NFT na TON Chain
A emissão de um FT ou NFT é geralmente a necessidade mais básica para os desenvolvedores de DApp. Portanto, eu também o uso como ponto de partida para aprender. Primeiro, vamos entender as diferenças entre o desenvolvimento de um NFT na pilha de tecnologia EVM e na cadeia TON. Os NFTs baseados em EVM geralmente escolhem herdar o padrão ERC-721. NFT refere-se a um tipo de ativo criptográfico indivisível, e cada ativo possui singularidade, ou seja, possui algumas características exclusivas. ERC-721 é um paradigma de desenvolvimento comum para esse tipo de ativo. Vamos ver quais funções comuns um contrato ERC721 precisa implementar e quais informações ele precisa registrar. O diagrama a seguir mostra uma interface ERC721. É possível ver que, ao contrário dos FTs, na interface de transferência, é necessário inserir o tokenId a ser transferido, e não a quantidade. Esse tokenId também é a manifestação mais básica da singularidade do ativo NFT. Claro, para suportar mais propriedades, geralmente é gravado um metadado para cada tokenId, que é um link externo que armazena outros dados extensíveis desse NFT, como um link para a imagem de um PFP, alguns nomes de propriedades, etc.
Para os desenvolvedores familiarizados com Solidity ou programação orientada a objetos, implementar um contrato inteligente como este é fácil, desde que os tipos de dados necessários sejam definidos no contrato, como algumas relações de mapeamento críticas, e a lógica de modificação correspondente para esses dados seja desenvolvida de acordo com as funcionalidades necessárias, um NFT pode ser implementado.
No entanto, na TON Chain, tudo isso torna-se um pouco diferente, devido a duas razões principais diferentes:
No TON, o armazenamento de dados é implementado com base em Cells, e as Cells do mesmo conta são implementadas através de um gráfico acíclico dirigido. Isso leva a uma limitação no crescimento ilimitado dos dados que precisam ser armazenados, porque, em um gráfico acíclico dirigido, o custo de consulta é determinado pela profundidade dos dados. Após um crescimento ilimitado da profundidade, é possível que o custo da consulta se torne muito alto, levando assim a um problema de deadlock no contrato.
Para buscar um alto desempenho de concorrência, o TON abandonou a arquitetura de execução serial e adotou um paradigma de desenvolvimento projetado especificamente para paralelismo, o modelo de ator, para reestruturar o ambiente de execução. Isso resulta em uma consequência: os contratos inteligentes só podem ser invocados de forma assíncrona através do envio de mensagens internas. Observe que tanto as chamadas que modificam o estado quanto as chamadas somente leitura devem seguir esse princípio. Além disso, é necessário considerar cuidadosamente como lidar com o rollback dos dados no caso de falha na chamada assíncrona.
É claro que outras diferenças na tecnologia foram discutidas em detalhes no artigo anterior, e este artigo espera se concentrar no desenvolvimento contratos inteligentes, então não vou discuti-las. Os dois princípios de design acima tornam contratos inteligentes desenvolvimento em TON muito diferente de EVM. No início da discussão, sabemos que um contrato de NFT precisa definir algumas relações de mapeamento, ou seja, mapeamento, para armazenar dados relacionados à NFT. Um dos mais importantes são os proprietários, este mapeamento armazena o mapeamento do Endereço do proprietário do NFT correspondente a um tokenID, determina a propriedade do NFT, e a transferência é uma modificação da propriedade. Uma vez que esta é, teoricamente, uma estrutura de dados que pode ser sem fronteiras, deve ser evitada tanto quanto possível. Portanto, recomenda-se oficialmente usar a existência de estruturas de dados sem fronteiras como padrão para fragmentação. Ou seja, quando há requisitos semelhantes de armazenamento de dados, o paradigma do contrato mestre-escravo é usado para substituí-lo, e os dados correspondentes a cada chave são gerenciados criando um subcontrato. e gerenciar parâmetros globais através do contrato principal, ou ajudar a lidar com as informações internas exchange entre subcontratos.
Isso significa que também é necessário adotar uma arquitetura semelhante para projetar NFTs no TON, onde cada NFT é um contrato inteligente independente que armazena dados exclusivos, como endereço do proprietário, metadados, etc., e é gerenciado por um contrato inteligente principal que controla os dados globais, como nome do NFT, símbolo, oferta total, etc.
Depois de definir a arquitetura, o próximo passo é resolver os requisitos principais. Como estamos usando esse contrato principal e secundário, é necessário definir claramente quais funcionalidades são suportadas pelo contrato principal e quais funcionalidades são suportadas pelo contrato secundário, além de como essas duas partes se comunicam internamente. Também é importante considerar cuidadosamente a lógica de reversão dos dados anteriores em caso de erro de execução. Geralmente, antes de desenvolver projetos grandes e complexos, é necessário criar um diagrama de classes, definir o fluxo de informações entre eles e pensar cuidadosamente na lógica de reversão em caso de falha nas chamadas internas. Claro, embora o desenvolvimento de NFT mencionado acima seja simples, também pode ser usado para fins de validação semelhantes.
Aprender a desenvolver contratos inteligentes TON a partir do código-fonte
TON optou por projetar uma linguagem de desenvolvimento de contratos inteligentes chamada Func, que é semelhante a C e tem tipagem estática. Agora vamos aprender como desenvolver contratos inteligentes da TON a partir do código-fonte. Para este tutorial, escolhi o exemplo de NFT do documento oficial da TON para explicar. Se você estiver interessado, pode consultar por conta própria. Neste caso, foi implementado um exemplo simples de TON NFT. Vamos dar uma olhada na estrutura do contrato, que consiste em dois contratos de função e três bibliotecas necessárias.
Estes dois contratos inteligentes principais são projetados de acordo com os princípios acima. Primeiro, vamos dar uma olhada no código do contrato principal nft-collection:
Isso introduz o primeiro ponto de conhecimento, como persistir dados em contratos inteligentes TON. Sabemos que em Solidity, a persistência de dados é automaticamente tratada pelo EVM com base no tipo de parâmetro. Em geral, as variáveis de estado do contrato inteligente são automaticamente persistidas com base no valor mais recente após a execução, e os desenvolvedores não precisam se preocupar com esse processo. No entanto, isso não é o caso em Func, os desenvolvedores precisam implementar a lógica de tratamento correspondente por conta própria. Essa situação é um pouco semelhante ao processo de coleta de lixo (GC) em C e C++, mas outras linguagens de programação mais recentes geralmente automatizam essa parte da lógica. Vamos dar uma olhada no código. Primeiro, importe algumas bibliotecas necessárias e, em seguida, veja a primeira função load_data para ler os dados persistidos. A lógica aqui é obter a célula de armazenamento persistente do contrato por meio de get_data. Observe que isso é implementado pela biblioteca padrão stdlib.fc e, em geral, algumas de suas funções podem ser usadas como funções do sistema.
O tipo de retorno desta função é cell, que é o tipo de célula em TVM. Como já mencionado anteriormente, todos os dados persistentes da blockchain TON são armazenados em uma árvore de células. Cada célula pode ter no máximo 1023 bits de dados arbitrários e até quatro referências a outras células. Em TVM baseado em pilha, as células são usadas como memória. As células contêm os dados codificados compactados e para obter os dados em texto simples, elas precisam ser convertidas em um tipo chamado slice. As células podem ser convertidas em slice usando a função begin_parse e, em seguida, é possível carregar os dados e referências a outras células a partir do slice. Observe que a chamada na linha 15 é uma sintaxe de açúcar sintático que permite chamar diretamente a segunda função do valor de retorno da primeira função. Em seguida, os dados correspondentes são carregados de acordo com a ordem de persistência dos dados. Observe que esse processo é diferente do Solidity e não é baseado em chamadas de hashmap, portanto, a ordem das chamadas não pode ser alterada.
No save_data function, a lógica é semelhante, mas este é um processo de reversão, o que introduz um novo ponto de conhecimento, um novo tipo de construtor, que é o tipo de construtor de célula. Os bits de dados e as referências a outras células podem ser armazenados no construtor e, em seguida, o construtor pode ser finalizado como uma nova célula. Primeiro, crie um construtor com a função padrão begin_cell e depois armazene as funções relacionadas com a função store, observando que a ordem de chamada no texto anterior deve corresponder à ordem de armazenamento aqui. Por fim, termine a construção da nova célula com end_cell, momento em que a célula é gerida na memória, e, por último, com set_data no nível mais externo, a célula pode ser armazenada de forma persistente.
A seguir, vamos dar uma olhada nas funções relacionadas ao negócio. Primeiro, precisamos apresentar um ponto de conhecimento sobre como criar um novo contrato através de um contrato, o que será frequentemente usado na arquitetura mestre-escravo que foi apresentada anteriormente. Sabemos que, no TON, a chamada entre contratos inteligentes é realizada enviando mensagens internas. Isso é feito através de uma função chamada send_raw_message, onde o primeiro parâmetro é a célula codificada da mensagem e o segundo parâmetro é uma flag que indica a diferença entre as formas de execução dessa transação. No TON, existem três modos de envio de mensagens internas e três flags de mensagens. Você pode combinar um único modo com várias (talvez nenhuma) flags para obter o modo desejado. A combinação significa apenas somar os valores e preencher. Abaixo está uma tabela de descrição dos modos e flags:
Então, vamos dar uma olhada na primeira função principal, deploy_nft_item, como o nome sugere, esta é uma função para criar ou cunhar uma nova instância NFT, após codificar uma mensagem, enviá-la através de send_raw_message para o contrato interno, selecionando a flag 1 como identificador de envio, apenas usando a taxa especificada no código como taxa de gás para esta execução. Após a introdução acima, é fácil perceber que estas regras de codificação devem corresponder à forma de criar um novo contrato inteligente. Então, vamos ver como isso é implementado.
Vamos dar uma olhada direta na linha 51. As duas funções acima são funções auxiliares usadas para gerar as informações necessárias para criar a mensagem. Portanto, vamos ver mais tarde. Este é um processo de codificação para criar uma mensagem interna para o contrato inteligente. Alguns dos números no meio são, na verdade, identificadores usados para indicar as necessidades dessa mensagem interna. Aqui, é necessário introduzir outro ponto de conhecimento: TON escolheu uma linguagem binária chamada TL-B para descrever como as mensagens são executadas e usa diferentes identificadores para implementar mensagens internas com funções específicas. Os dois cenários de uso mais fáceis de pensar são a criação de um novo contrato e a chamada de uma função de contrato já implantado. A abordagem da linha 51 corresponde ao primeiro caso, que é criar um novo contrato de item NFT. Isso é principalmente especificado pelas linhas 55, 56 e 57. Primeiro, essa grande série de números na linha 55 é uma série de identificadores. Observe que o primeiro parâmetro de store_uint é um valor numérico e o segundo é o comprimento em bits. Entre eles, os últimos três identificadores determinam se a mensagem interna é a criação do contrato, e o valor binário correspondente é 111 (decimal é 4+2+1), dos quais os dois primeiros indicam que a mensagem incluirá os dados StateInit, que é o código-fonte do novo contrato e os dados necessários para inicializá-lo. O último identificador indica que a mensagem interna está anexada, ou seja, deseja-se executar a lógica relevante e os parâmetros necessários. Portanto, você verá que a linha 66 de código não define esses três dados, indicando que é uma chamada de função para um contrato já implantado. Consulte as regras de codificação específicas aqui.
Assim, as regras de codificação do StateInit correspondem ao código da linha 49, calculado através do calculate_nft_item_state_init. Note que a codificação dos dados do stateinit segue uma regra de codificação TL-B estabelecida, exceto por algumas flags, principalmente envolvendo duas partes: o novo código do contrato e os dados de inicialização. A ordem de codificação dos dados precisa ser consistente com a ordem de armazenamento das células de persistência especificadas no novo contrato. Na linha 36, pode-se ver que os dados de inicialização incluem item_index, semelhante ao tokenId no ERC721, e o endereço atual do contrato retornado pela função padrão my_address, que é o collection_address. A ordem desses dados deve ser consistente com a declaração do nft-item.
O próximo ponto de conhecimento é que no TON, todos os contratos inteligentes não gerados podem ser pré-calculados após o Endereço de sua geração, que é semelhante à função create2 em Solidity, no TON, a geração de novos Endereço consiste em duas partes, o bit identificador da cadeia de trabalho e o valor hash de stateinit, o primeiro que já conhecemos na introdução anterior precisa ser especificado em ordem para corresponder à arquitetura Fragmentação infinita TON, e atualmente é um valor uniforme. Obtido pela cadeia de trabalho da função padrão. Este último é obtido pela célula de função padrão_hash. Então, voltando ao exemplo, calculate_nft_item_address é a função que pré-calcula o Endereço do novo contrato. E codifice o valor gerado na mensagem na linha 53 como o Endereço de recebimento dessa mensagem interna. O NFT_content corresponde à chamada de inicialização para o contrato criado e a implementação específica é intermediário no próximo artigo.
Quanto ao envio_royalty_params, ele precisa ser uma resposta à mensagem interna de uma solicitação somente leitura, na introdução anterior, enfatizamos deliberadamente que no TON, a mensagem interna não só contém operações que podem modificar os dados, mas também operações somente leitura precisam ser implementadas dessa forma, então o contrato é uma operação desse tipo, em primeiro lugar, vale a pena notar que a linha 67 representa a marca da função de retração do solicitante após responder à solicitação, e os dados retornados são o item da solicitação e os dados de royalties correspondentes.
A seguir, vamos introduzir um novo ponto de conhecimento, os contratos inteligentes no TON têm apenas duas entradas unificadas, chamadas recv_internal e recv_external, sendo que a primeira é a entrada unificada para todas as mensagens internas, e a segunda é a entrada unificada para todas as mensagens externas. Os desenvolvedores precisam, dentro da função, responder a diferentes solicitações de forma semelhante a um switch, de acordo com os diferentes bits de marcação especificados pela mensagem, que é exatamente o bit de marcação da função de retorno na linha 67 acima. Voltando a este exemplo, primeiro verificamos se há espaços vazios na mensagem e, em seguida, analisamos as informações na mensagem. Primeiro, na linha 83, analisamos e obtemos o endereço do remetente, que será usado para verificações de permissão posteriores. Observe o operador ~ aqui, que é apenas um açúcar sintático diferente. Não vamos entrar em detalhes sobre isso agora. Em seguida, analisamos o bit de marcação da operação, e depois, de acordo com o bit de marcação, lidamos com solicitações correspondentes. Isso inclui chamar funções mencionadas anteriormente de acordo com alguma lógica, como responder a solicitações para o parâmetro de royalties, ou cunhar um novo NFT e aumentar o índice global.
O próximo ponto de conhecimento corresponde à linha 108, e é provável que todos consigam entender a lógica do funcionamento da função através do nome. Similar à função require em Solidity, na Func, a exceção é lançada através da função padrão throw_unless, em que o primeiro parâmetro é o código de erro e o segundo é um valor booleano de verificação. Se o valor for falso, uma exceção será lançada com o código de erro correspondente. Nesta linha, é feita uma verificação de permissões através da função equal_slices para verificar se o sender_address analisado anteriormente é igual ao owner_address armazenado de forma persistente no contrato.
Por fim, para tornar a estrutura do código mais clara, começou a criar uma série de funções auxiliares para obter informações de persistência, que não serão detalhadas aqui. Os desenvolvedores podem usar essa estrutura como referência para desenvolver seus próprios contratos inteligentes.
O desenvolvimento de DApp no ecossistema TON é realmente interessante, com grandes diferenças no paradigma de desenvolvimento do EVM, então vou apresentar uma série de artigos sobre como desenvolver DApp na cadeia TON. Aprenderemos juntos e aproveitaremos esta oportunidade. Também convido todos a interagir comigo no Twitter, trocar algumas novas e interessantes ideias de dapp, e desenvolver juntos.
Esta página pode conter conteúdo de terceiros, que é fornecido apenas para fins informativos (não para representações/garantias) e não deve ser considerada como um endosso de suas opiniões pela Gate nem como aconselhamento financeiro ou profissional. Consulte a Isenção de responsabilidade para obter detalhes.
Tutorial de desenvolvimento do projeto TON (1): Como criar um NFT na cadeia TON a partir da perspectiva do código-fonte
Autor: @Web3Mario (_mario)
Resumo: Continuando do artigo anterior sobre a introdução da tecnologia TON, eu fiz uma pesquisa detalhada na documentação oficial de desenvolvimento da TON recentemente. Achei que o processo de aprendizado tem algumas dificuldades, pois o conteúdo atual da documentação parece mais voltado para desenvolvedores internos e não é muito amigável para desenvolvedores iniciantes. Por isso, decidi organizar uma série de artigos sobre o desenvolvimento do projeto TON Chain, baseado na minha própria trajetória de aprendizado, na esperança de ajudar os iniciantes a se familiarizarem rapidamente com o desenvolvimento de aplicativos TON. Se houver algum erro no meu texto, por favor, sinta-se à vontade para corrigir e aprender juntos.
Quais são as diferenças entre desenvolver NFT no EVM e desenvolver NFT na TON Chain
A emissão de um FT ou NFT é geralmente a necessidade mais básica para os desenvolvedores de DApp. Portanto, eu também o uso como ponto de partida para aprender. Primeiro, vamos entender as diferenças entre o desenvolvimento de um NFT na pilha de tecnologia EVM e na cadeia TON. Os NFTs baseados em EVM geralmente escolhem herdar o padrão ERC-721. NFT refere-se a um tipo de ativo criptográfico indivisível, e cada ativo possui singularidade, ou seja, possui algumas características exclusivas. ERC-721 é um paradigma de desenvolvimento comum para esse tipo de ativo. Vamos ver quais funções comuns um contrato ERC721 precisa implementar e quais informações ele precisa registrar. O diagrama a seguir mostra uma interface ERC721. É possível ver que, ao contrário dos FTs, na interface de transferência, é necessário inserir o tokenId a ser transferido, e não a quantidade. Esse tokenId também é a manifestação mais básica da singularidade do ativo NFT. Claro, para suportar mais propriedades, geralmente é gravado um metadado para cada tokenId, que é um link externo que armazena outros dados extensíveis desse NFT, como um link para a imagem de um PFP, alguns nomes de propriedades, etc.
Para os desenvolvedores familiarizados com Solidity ou programação orientada a objetos, implementar um contrato inteligente como este é fácil, desde que os tipos de dados necessários sejam definidos no contrato, como algumas relações de mapeamento críticas, e a lógica de modificação correspondente para esses dados seja desenvolvida de acordo com as funcionalidades necessárias, um NFT pode ser implementado.
No entanto, na TON Chain, tudo isso torna-se um pouco diferente, devido a duas razões principais diferentes:
É claro que outras diferenças na tecnologia foram discutidas em detalhes no artigo anterior, e este artigo espera se concentrar no desenvolvimento contratos inteligentes, então não vou discuti-las. Os dois princípios de design acima tornam contratos inteligentes desenvolvimento em TON muito diferente de EVM. No início da discussão, sabemos que um contrato de NFT precisa definir algumas relações de mapeamento, ou seja, mapeamento, para armazenar dados relacionados à NFT. Um dos mais importantes são os proprietários, este mapeamento armazena o mapeamento do Endereço do proprietário do NFT correspondente a um tokenID, determina a propriedade do NFT, e a transferência é uma modificação da propriedade. Uma vez que esta é, teoricamente, uma estrutura de dados que pode ser sem fronteiras, deve ser evitada tanto quanto possível. Portanto, recomenda-se oficialmente usar a existência de estruturas de dados sem fronteiras como padrão para fragmentação. Ou seja, quando há requisitos semelhantes de armazenamento de dados, o paradigma do contrato mestre-escravo é usado para substituí-lo, e os dados correspondentes a cada chave são gerenciados criando um subcontrato. e gerenciar parâmetros globais através do contrato principal, ou ajudar a lidar com as informações internas exchange entre subcontratos.
Isso significa que também é necessário adotar uma arquitetura semelhante para projetar NFTs no TON, onde cada NFT é um contrato inteligente independente que armazena dados exclusivos, como endereço do proprietário, metadados, etc., e é gerenciado por um contrato inteligente principal que controla os dados globais, como nome do NFT, símbolo, oferta total, etc.
Depois de definir a arquitetura, o próximo passo é resolver os requisitos principais. Como estamos usando esse contrato principal e secundário, é necessário definir claramente quais funcionalidades são suportadas pelo contrato principal e quais funcionalidades são suportadas pelo contrato secundário, além de como essas duas partes se comunicam internamente. Também é importante considerar cuidadosamente a lógica de reversão dos dados anteriores em caso de erro de execução. Geralmente, antes de desenvolver projetos grandes e complexos, é necessário criar um diagrama de classes, definir o fluxo de informações entre eles e pensar cuidadosamente na lógica de reversão em caso de falha nas chamadas internas. Claro, embora o desenvolvimento de NFT mencionado acima seja simples, também pode ser usado para fins de validação semelhantes.
Aprender a desenvolver contratos inteligentes TON a partir do código-fonte
TON optou por projetar uma linguagem de desenvolvimento de contratos inteligentes chamada Func, que é semelhante a C e tem tipagem estática. Agora vamos aprender como desenvolver contratos inteligentes da TON a partir do código-fonte. Para este tutorial, escolhi o exemplo de NFT do documento oficial da TON para explicar. Se você estiver interessado, pode consultar por conta própria. Neste caso, foi implementado um exemplo simples de TON NFT. Vamos dar uma olhada na estrutura do contrato, que consiste em dois contratos de função e três bibliotecas necessárias.
Estes dois contratos inteligentes principais são projetados de acordo com os princípios acima. Primeiro, vamos dar uma olhada no código do contrato principal nft-collection:
Isso introduz o primeiro ponto de conhecimento, como persistir dados em contratos inteligentes TON. Sabemos que em Solidity, a persistência de dados é automaticamente tratada pelo EVM com base no tipo de parâmetro. Em geral, as variáveis de estado do contrato inteligente são automaticamente persistidas com base no valor mais recente após a execução, e os desenvolvedores não precisam se preocupar com esse processo. No entanto, isso não é o caso em Func, os desenvolvedores precisam implementar a lógica de tratamento correspondente por conta própria. Essa situação é um pouco semelhante ao processo de coleta de lixo (GC) em C e C++, mas outras linguagens de programação mais recentes geralmente automatizam essa parte da lógica. Vamos dar uma olhada no código. Primeiro, importe algumas bibliotecas necessárias e, em seguida, veja a primeira função load_data para ler os dados persistidos. A lógica aqui é obter a célula de armazenamento persistente do contrato por meio de get_data. Observe que isso é implementado pela biblioteca padrão stdlib.fc e, em geral, algumas de suas funções podem ser usadas como funções do sistema.
O tipo de retorno desta função é cell, que é o tipo de célula em TVM. Como já mencionado anteriormente, todos os dados persistentes da blockchain TON são armazenados em uma árvore de células. Cada célula pode ter no máximo 1023 bits de dados arbitrários e até quatro referências a outras células. Em TVM baseado em pilha, as células são usadas como memória. As células contêm os dados codificados compactados e para obter os dados em texto simples, elas precisam ser convertidas em um tipo chamado slice. As células podem ser convertidas em slice usando a função begin_parse e, em seguida, é possível carregar os dados e referências a outras células a partir do slice. Observe que a chamada na linha 15 é uma sintaxe de açúcar sintático que permite chamar diretamente a segunda função do valor de retorno da primeira função. Em seguida, os dados correspondentes são carregados de acordo com a ordem de persistência dos dados. Observe que esse processo é diferente do Solidity e não é baseado em chamadas de hashmap, portanto, a ordem das chamadas não pode ser alterada.
No save_data function, a lógica é semelhante, mas este é um processo de reversão, o que introduz um novo ponto de conhecimento, um novo tipo de construtor, que é o tipo de construtor de célula. Os bits de dados e as referências a outras células podem ser armazenados no construtor e, em seguida, o construtor pode ser finalizado como uma nova célula. Primeiro, crie um construtor com a função padrão begin_cell e depois armazene as funções relacionadas com a função store, observando que a ordem de chamada no texto anterior deve corresponder à ordem de armazenamento aqui. Por fim, termine a construção da nova célula com end_cell, momento em que a célula é gerida na memória, e, por último, com set_data no nível mais externo, a célula pode ser armazenada de forma persistente.
A seguir, vamos dar uma olhada nas funções relacionadas ao negócio. Primeiro, precisamos apresentar um ponto de conhecimento sobre como criar um novo contrato através de um contrato, o que será frequentemente usado na arquitetura mestre-escravo que foi apresentada anteriormente. Sabemos que, no TON, a chamada entre contratos inteligentes é realizada enviando mensagens internas. Isso é feito através de uma função chamada send_raw_message, onde o primeiro parâmetro é a célula codificada da mensagem e o segundo parâmetro é uma flag que indica a diferença entre as formas de execução dessa transação. No TON, existem três modos de envio de mensagens internas e três flags de mensagens. Você pode combinar um único modo com várias (talvez nenhuma) flags para obter o modo desejado. A combinação significa apenas somar os valores e preencher. Abaixo está uma tabela de descrição dos modos e flags:
Então, vamos dar uma olhada na primeira função principal, deploy_nft_item, como o nome sugere, esta é uma função para criar ou cunhar uma nova instância NFT, após codificar uma mensagem, enviá-la através de send_raw_message para o contrato interno, selecionando a flag 1 como identificador de envio, apenas usando a taxa especificada no código como taxa de gás para esta execução. Após a introdução acima, é fácil perceber que estas regras de codificação devem corresponder à forma de criar um novo contrato inteligente. Então, vamos ver como isso é implementado.
Vamos dar uma olhada direta na linha 51. As duas funções acima são funções auxiliares usadas para gerar as informações necessárias para criar a mensagem. Portanto, vamos ver mais tarde. Este é um processo de codificação para criar uma mensagem interna para o contrato inteligente. Alguns dos números no meio são, na verdade, identificadores usados para indicar as necessidades dessa mensagem interna. Aqui, é necessário introduzir outro ponto de conhecimento: TON escolheu uma linguagem binária chamada TL-B para descrever como as mensagens são executadas e usa diferentes identificadores para implementar mensagens internas com funções específicas. Os dois cenários de uso mais fáceis de pensar são a criação de um novo contrato e a chamada de uma função de contrato já implantado. A abordagem da linha 51 corresponde ao primeiro caso, que é criar um novo contrato de item NFT. Isso é principalmente especificado pelas linhas 55, 56 e 57. Primeiro, essa grande série de números na linha 55 é uma série de identificadores. Observe que o primeiro parâmetro de store_uint é um valor numérico e o segundo é o comprimento em bits. Entre eles, os últimos três identificadores determinam se a mensagem interna é a criação do contrato, e o valor binário correspondente é 111 (decimal é 4+2+1), dos quais os dois primeiros indicam que a mensagem incluirá os dados StateInit, que é o código-fonte do novo contrato e os dados necessários para inicializá-lo. O último identificador indica que a mensagem interna está anexada, ou seja, deseja-se executar a lógica relevante e os parâmetros necessários. Portanto, você verá que a linha 66 de código não define esses três dados, indicando que é uma chamada de função para um contrato já implantado. Consulte as regras de codificação específicas aqui.
Assim, as regras de codificação do StateInit correspondem ao código da linha 49, calculado através do calculate_nft_item_state_init. Note que a codificação dos dados do stateinit segue uma regra de codificação TL-B estabelecida, exceto por algumas flags, principalmente envolvendo duas partes: o novo código do contrato e os dados de inicialização. A ordem de codificação dos dados precisa ser consistente com a ordem de armazenamento das células de persistência especificadas no novo contrato. Na linha 36, pode-se ver que os dados de inicialização incluem item_index, semelhante ao tokenId no ERC721, e o endereço atual do contrato retornado pela função padrão my_address, que é o collection_address. A ordem desses dados deve ser consistente com a declaração do nft-item.
O próximo ponto de conhecimento é que no TON, todos os contratos inteligentes não gerados podem ser pré-calculados após o Endereço de sua geração, que é semelhante à função create2 em Solidity, no TON, a geração de novos Endereço consiste em duas partes, o bit identificador da cadeia de trabalho e o valor hash de stateinit, o primeiro que já conhecemos na introdução anterior precisa ser especificado em ordem para corresponder à arquitetura Fragmentação infinita TON, e atualmente é um valor uniforme. Obtido pela cadeia de trabalho da função padrão. Este último é obtido pela célula de função padrão_hash. Então, voltando ao exemplo, calculate_nft_item_address é a função que pré-calcula o Endereço do novo contrato. E codifice o valor gerado na mensagem na linha 53 como o Endereço de recebimento dessa mensagem interna. O NFT_content corresponde à chamada de inicialização para o contrato criado e a implementação específica é intermediário no próximo artigo.
Quanto ao envio_royalty_params, ele precisa ser uma resposta à mensagem interna de uma solicitação somente leitura, na introdução anterior, enfatizamos deliberadamente que no TON, a mensagem interna não só contém operações que podem modificar os dados, mas também operações somente leitura precisam ser implementadas dessa forma, então o contrato é uma operação desse tipo, em primeiro lugar, vale a pena notar que a linha 67 representa a marca da função de retração do solicitante após responder à solicitação, e os dados retornados são o item da solicitação e os dados de royalties correspondentes.
A seguir, vamos introduzir um novo ponto de conhecimento, os contratos inteligentes no TON têm apenas duas entradas unificadas, chamadas recv_internal e recv_external, sendo que a primeira é a entrada unificada para todas as mensagens internas, e a segunda é a entrada unificada para todas as mensagens externas. Os desenvolvedores precisam, dentro da função, responder a diferentes solicitações de forma semelhante a um switch, de acordo com os diferentes bits de marcação especificados pela mensagem, que é exatamente o bit de marcação da função de retorno na linha 67 acima. Voltando a este exemplo, primeiro verificamos se há espaços vazios na mensagem e, em seguida, analisamos as informações na mensagem. Primeiro, na linha 83, analisamos e obtemos o endereço do remetente, que será usado para verificações de permissão posteriores. Observe o operador ~ aqui, que é apenas um açúcar sintático diferente. Não vamos entrar em detalhes sobre isso agora. Em seguida, analisamos o bit de marcação da operação, e depois, de acordo com o bit de marcação, lidamos com solicitações correspondentes. Isso inclui chamar funções mencionadas anteriormente de acordo com alguma lógica, como responder a solicitações para o parâmetro de royalties, ou cunhar um novo NFT e aumentar o índice global.
O próximo ponto de conhecimento corresponde à linha 108, e é provável que todos consigam entender a lógica do funcionamento da função através do nome. Similar à função require em Solidity, na Func, a exceção é lançada através da função padrão throw_unless, em que o primeiro parâmetro é o código de erro e o segundo é um valor booleano de verificação. Se o valor for falso, uma exceção será lançada com o código de erro correspondente. Nesta linha, é feita uma verificação de permissões através da função equal_slices para verificar se o sender_address analisado anteriormente é igual ao owner_address armazenado de forma persistente no contrato.
Por fim, para tornar a estrutura do código mais clara, começou a criar uma série de funções auxiliares para obter informações de persistência, que não serão detalhadas aqui. Os desenvolvedores podem usar essa estrutura como referência para desenvolver seus próprios contratos inteligentes.
O desenvolvimento de DApp no ecossistema TON é realmente interessante, com grandes diferenças no paradigma de desenvolvimento do EVM, então vou apresentar uma série de artigos sobre como desenvolver DApp na cadeia TON. Aprenderemos juntos e aproveitaremos esta oportunidade. Também convido todos a interagir comigo no Twitter, trocar algumas novas e interessantes ideias de dapp, e desenvolver juntos.