Após o último artigo sobre a introdução à tecnologia TON, passei um tempo aprofundando-me na documentação oficial de desenvolvimento da TON. Senti que havia algumas barreiras na aprendizagem, uma vez que o conteúdo atual parece mais uma documentação de desenvolvimento interna e não é muito amigável para novos desenvolvedores. Por isso, tentei organizar uma série de artigos sobre o desenvolvimento do projeto TON Chain, seguindo minha própria trajetória de aprendizado, na esperança de ajudar rapidamente os desenvolvedores iniciantes na criação de aplicativos TON. Se houver erros no texto, agradeço as correções, pois todos estamos aprendendo juntos.
Quais são as diferenças entre desenvolver NFTs na EVM e desenvolver NFTs na TON Chain?
Emitir um FT ou NFT é geralmente a necessidade mais básica para os desenvolvedores de DApp. Portanto, eu também o escolhi como ponto de partida para aprender. Primeiro, vamos entender as diferenças entre desenvolver um NFT no EVM stack e no TON Chain. Os NFTs baseados em EVM geralmente escolhem herdar o padrão ERC-721. O chamado NFT refere-se a um tipo de ativo criptografado indivisível, e cada ativo tem singularidade e algumas propriedades exclusivas. ERC-721 é um paradigma de desenvolvimento comum para esse tipo de ativo. Vamos dar uma olhada em quais funções um contrato ERC 721 comum precisa implementar e quais informações ele precisa registrar. A seguir está uma interface ERC 721 comum. Você pode ver que, diferente do FT, a interface de transferência requer o tokenId a ser transferido em vez da quantidade. O tokenId é a expressão mais básica da singularidade dos ativos NFTs. Claro, para incorporar mais propriedades, geralmente é registrado um metadata para cada tokenId. Este metadata é um link externo que contém dados adicionais extensíveis do NFT, como um link para uma imagem de PFP (perfil) e alguns nomes de propriedades.
Para os desenvolvedores familiarizados com Solidity ou orientados a objetos, implementar um contrato inteligente como este é uma tarefa fácil, desde que os tipos de dados necessários no contrato sejam definidos, como alguns relacionamentos de mapeamento-chave, e a lógica de modificação dos dados correspondentes às funções necessárias seja desenvolvida.
No entanto, no TON Chain, tudo isso é um pouco diferente, com duas razões principais que levam a essa diferença no núcleo:
No TON, o armazenamento de dados é baseado em células, e as células de uma mesma conta são implementadas por meio de um gráfico acíclico dirigido. Isso significa que os dados que precisam ser armazenados indefinidamente não podem crescer ilimitadamente, pois, para um grafo acíclico dirigido, a profundidade dos dados determina o custo das consultas. Quando a profundidade se estende infinitamente, pode ocorrer um alto custo das consultas, levando ao problema de deadlock do contrato.
Para buscar um alto desempenho de concorrência, o TON abandonou a arquitetura de execução serial e adotou um paradigma de desenvolvimento feito especialmente para a concorrência, o modelo de ator, para reconstruir o ambiente de execução. Isso causou um impacto, pois os contratos inteligentes só podem ser chamados de forma assíncrona entre si, através do envio de chamadas de mensagem interna. Note que tanto as chamadas de tipo de modificação de estado quanto as de tipo apenas leitura precisam seguir esse princípio. Além disso, é necessário considerar cuidadosamente como lidar com a reversão de dados caso as chamadas assíncronas falhem.
Claro, houve uma discussão detalhada sobre outras diferenças técnicas no artigo anterior, mas este artigo pretende focar no desenvolvimento de contratos inteligentes. Portanto, não vamos entrar em detalhes. Os dois princípios de design acima mencionados fazem com que o desenvolvimento de contratos inteligentes no TON seja muito diferente do EVM. Como mencionado no início, sabemos que um contrato NFT precisa definir algumas relações de mapeamento, ou seja, mapping, para armazenar os dados relacionados ao NFT. O mais importante é o owners, este mapping armazena as relações de mapeamento dos endereços dos proprietários do NFT correspondentes a um determinado tokenID, determinando a propriedade do NFT, e a transferência é uma modificação dessa propriedade. Como essa é uma estrutura de dados potencialmente sem limites, é necessário evitá-la o máximo possível. Portanto, é recomendado pelo oficial usar a existência de estrutura de dados sem limites como critério para fragmentação. Ou seja, ao ter uma necessidade semelhante de armazenamento de dados, substitua-a pelo paradigma do contrato principal-secundário, gerenciando os dados correspondentes a cada chave por meio da criação de contratos secundários. E gerencie os parâmetros globais através do contrato principal ou ajude a lidar com a interação de informações internas entre os contratos secundários.
Isto também significa que os NFTs no TON precisam de ser concebidos com uma arquitetura semelhante, onde cada NFT é um subcontrato independente que armazena dados exclusivos, como o endereço do proprietário, metadados, etc., e é gerido por um contrato principal que controla os dados globais, como o nome do NFT, o símbolo, o fornecimento total, etc.
Depois de estabelecer a arquitetura, o próximo passo é resolver os requisitos das funções principais. Devido ao uso deste contrato principal-secundário, é necessário especificar quais funções são realizadas pelo contrato principal e quais são realizadas pelo contrato secundário, e como a comunicação interna entre eles é feita. Além disso, é importante considerar a lógica de reversão dos dados anteriores quando ocorrem erros de execução. Geralmente, antes de desenvolver projetos complexos de grande escala, é necessário criar um diagrama de classes para esclarecer o fluxo de informações e pensar cuidadosamente na lógica de reversão após falhas de chamada interna. Claro, embora o desenvolvimento de NFT mencionado acima seja simples, também pode ser realizado um processo semelhante de verificação.
Aprendendo a desenvolver contratos inteligentes TON a partir do código-fonte
TON escolheu projetar uma linguagem de programação de tipo estático semelhante a C, chamada Func, como a linguagem de desenvolvimento de contrato inteligente. Então vamos aprender a desenvolver contratos inteligentes TON a partir do código-fonte. Escolhi o exemplo NFT da documentação oficial do TON para apresentar. Os interessados podem consultar por si próprios. Neste caso, foi implementado um exemplo simples de TON NFT. Vamos dar uma olhada na estrutura do contrato, que é composta por dois contratos de função e três bibliotecas necessárias.
Os dois contratos principais de funcionalidade 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 um contrato inteligente TON. Sabemos que em Solidity, a persistência de dados é tratada automaticamente pelo EVM com base no tipo de parâmetro. Normalmente, as variáveis de estado do contrato inteligente serão persistidas automaticamente com base no valor mais recente após a conclusão da execução, e os desenvolvedores não precisam se preocupar com esse processo. No entanto, a situação é diferente em Func, os desenvolvedores precisam implementar a lógica de processamento correspondente por conta própria. Essa situação é um pouco semelhante a C e C++, onde é necessário considerar o processo de GC, mas outras novas linguagens de programação geralmente automatizam essa parte da lógica. Vamos dar uma olhada no código, primeiro importamos algumas bibliotecas necessárias e, em seguida, vemos a primeira função load_data que é usada para ler os dados persistidos. Sua lógica é retornar a célula de armazenamento persistente do contrato por meio da função get_data, observe que isso é implementado pela biblioteca padrão stdlib.fc e, geralmente, algumas funções nela podem ser usadas como funções do sistema.
O tipo de retorno desta função é cell, que é o tipo cell em TVM. Como introduzido anteriormente, sabemos que todos os dados persistentes na Blockchain TON estão armazenados em árvores de células. Cada célula pode conter 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 dados codificados de forma compacta e para obter os dados em texto simples específicos, é necessário converter a célula para o tipo chamado slice. A célula pode ser convertida em tipo slice através da função begin_parse e, em seguida, os dados e as referências a outras células podem ser carregados a partir do slice para obter os dados da célula. Note que a chamada na linha 15 é uma sintaxe de func em que é possível chamar diretamente a segunda função do valor de retorno da primeira função. E, por fim, carregar os dados correspondentes de forma persistente de acordo com a ordem. Note que este processo é diferente do Solidity e não é baseado em chamadas de hashmap, portanto, a ordem das chamadas deve ser estrita.
Na função save_data, a lógica é semelhante, apenas que 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élulas. Os bits de dados e as referências a outras células podem ser armazenados no construtor, e depois o construtor pode ser finalizado como uma nova célula. Primeiro, crie um construtor usando a função padrão begin_cell e armazene as funções relacionadas sequencialmente, observe que a ordem de chamada no texto anterior deve ser mantida consistente com a ordem de armazenamento aqui. Finalmente, conclua a construção da nova célula através de end_cell, momento em que a célula é gerenciada na memória. Por fim, usando a função set_data mais externa, você pode concluir o armazenamento persistente da célula.
A seguir, vamos dar uma olhada nas funções relacionadas ao negócio. Primeiro, precisamos introduzir um ponto de conhecimento sobre como criar um novo contrato por meio de um contrato, o que será frequentemente usado na arquitetura mestre-escravo que acabamos de apresentar. Sabemos que na TON, as chamadas entre contratos inteligentes são realizadas por meio do envio de mensagens internas. Isso é feito por meio de uma função chamada send_raw_message, observe que o primeiro parâmetro é a célula codificada da mensagem, o segundo parâmetro é uma flag que indica a diferença na forma de execução dessa transação. Na TON, diferentes modos de envio de mensagens internas foram estabelecidos, atualmente existem 3 modos de mensagens e 3 flags de mensagens. Um único modo pode ser combinado com vários (talvez nenhum) flags para obter o modo desejado. A combinação significa apenas somar os valores e preenchê-los. A tabela a seguir descreve os 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 de NFT, após codificar uma mensagem, envia o contrato interno através do send_raw_message e seleciona a flag 1 para 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. Vamos ver como isso é implementado.
Vamos dar uma olhada direta na linha 51. As duas funções acima são auxiliares para gerar as informações necessárias para a mensagem. Portanto, vamos voltar a olhar mais tarde. Este é um processo de codificação para criar uma mensagem interna de contrato inteligente. Alguns dos números intermediários também são identificadores usados para indicar os requisitos dessa mensagem interna. Aqui, vamos introduzir outro ponto de conhecimento. A TON escolheu uma linguagem binária chamada TL-B para descrever a forma de execução das mensagens e utiliza diferentes marcadores para implementar funcionalidades específicas nas mensagens internas. As duas cenas de uso mais comuns são a criação de novos contratos e a chamada de funções de contratos já implantados. A forma na linha 51 corresponde à primeira situação, a criação de um novo contrato de item NFT, especificada pelas linhas 55, 56 e 57. Primeiramente, a sequência de números na linha 55 é uma série de marcadores. Observe que o primeiro parâmetro do store_uint é um valor e o segundo é o tamanho em bits. Aqui, eles determinam que a mensagem interna é a criação de um contrato e os três últimos marcadores são definidos como 111 (que em decimal é 4 + 2 + 1). Os dois primeiros indicam que a mensagem é acompanhada pelos dados StateInit, que são o código-fonte do novo contrato e os dados de inicialização necessários. O último marcador indica o envio da mensagem interna, ou seja, a execução da lógica relacionada e os parâmetros necessários. Portanto, você verá que a linha de código 66 não define esses três dados, o que indica uma chamada de função em um contrato já implantado. Você pode verificar as regras de codificação específicas aqui.
Portanto, as regras de codificação do StateInit correspondem à linha de código 49, calculadas através do calculate_nft_item_state_init. Observe que a codificação dos dados do stateinit também segue uma regra de codificação TL-B estabelecida, envolvendo principalmente duas novas partes de contrato, ou seja, code e dados de inicialização. A ordem de codificação dos dados precisa ser consistente com a ordem de armazenamento de células persistentes especificadas pelo novo contrato. Na linha 36, pode-se ver que os dados de inicialização incluem item_index, semelhante ao tokenId em ERC 721, e o endereço de contrato atual retornado pela função padrão my_address, ou seja, collection_address, a ordem desses dados é consistente com as declarações em nft-item.
O próximo ponto a ser considerado no TON é que todos os contratos inteligentes não gerados podem ter seus endereços calculados antecipadamente, o que é semelhante à função create2 em Solidity. A geração de um novo endereço no TON é composta por duas partes: o identificador workchain e o hash do stateinit concatenados. O primeiro já foi explicado na introdução anterior como um valor uniforme necessário para a arquitetura de fragmentação ilimitada do TON. Ele é obtido pela função padrão workchain. O último é obtido pela função padrão cell_hash. Portanto, no exemplo dado, calculate_nft_item_address é a função que calcula antecipadamente o novo endereço do contrato. E o valor gerado é codificado na linha 53 como o endereço de recebimento da mensagem interna. O nft_content corresponde à chamada de inicialização para o contrato criado, cuja implementação será explicada no próximo artigo.
Quanto ao send_royalty_params, deve ser uma resposta a uma mensagem interna de uma solicitação somente leitura. Como mencionado anteriormente, as mensagens internas no TON não apenas contêm operações que podem modificar dados, mas também operações somente leitura também são implementadas dessa maneira. Portanto, este contrato é usado para esse tipo de operação. Primeiro, é importante observar que a linha 67 representa a marcação da função de retorno para o solicitante após a resposta a essa solicitação. Anote isso como os dados de retorno, que são o índice do item solicitado e os dados de royalties correspondentes.
A seguir, vamos introduzir o próximo ponto de conhecimento: os contratos inteligentes do TON têm apenas duas entradas unificadas, denominadas recv_internal e recv_external. O primeiro é a entrada unificada para todas as mensagens internas e o segundo é a entrada unificada para todas as mensagens externas. Os desenvolvedores precisam responder a diferentes solicitações dentro da função com base nas diferentes marcas especificadas pela mensagem, semelhante a um switch. Essas marcas são os marcadores de retorno na linha 67 mencionada acima. Voltando para o exemplo, primeiro verifique se há espaços vazios no message, e então analise as informações em message separadamente. Primeiro, na linha 83, analise o endereço do remetente, que será usado para verificar as permissões posteriormente. Observe o operador ~ aqui, que é outro açúcar sintático. Em seguida, analise o marcador de operação op e processe a solicitação correspondente com base em diferentes marcas. O exemplo inclui a resposta à solicitação do parâmetro de royalties ou a cunhagem de um novo NFT e o incremento do índice global.
O próximo ponto de conhecimento corresponde à linha 108. Pelo nome, podemos entender a lógica de processamento dessa função. Semelhante à função require em Solidity, a função Func utiliza throw_unless para lançar exceções. O primeiro argumento é o código de erro e o segundo é um valor booleano de verificação. Se o valor for falso, uma exceção é lançada com o código de erro correspondente. Nesta linha, equal_slices é usado para verificar se o sender_address analisado anteriormente é igual ao owner_address armazenado persistentemente no contrato, para fins de verificação de permissão.
Por fim, para tornar a estrutura do código mais clara, comecei a criar uma série de funções auxiliares para obter informações de persistência. Não vou entrar em detalhes aqui, mas os desenvolvedores podem usar essa estrutura como referência para desenvolver seus próprios contratos inteligentes.
O desenvolvimento de DApp na ecologia TON é realmente interessante, com grandes diferenças em relação ao paradigma de desenvolvimento EVM. Portanto, vou apresentar uma série de artigos sobre como desenvolver DApp na TON Chain. Vamos aprender juntos e aproveitar esta oportunidade. Também convido todos a interagirem comigo no Twitter, discutindo novas ideias interessantes de DApp e desenvolvendo juntos.
Esta página pode conter conteúdos de terceiros, que são fornecidos apenas para fins informativos (sem representações/garantias) e não devem ser considerados como uma aprovação dos seus pontos de vista pela Gate, nem como aconselhamento financeiro ou profissional. Consulte a Declaração de exoneração de responsabilidade para obter mais informações.
Tutorial de Desenvolvimento do Projeto TON (Parte 1): Como criar um NFT na TON Chain do ponto de vista do código-fonte
O autor original: @Web3 Mario(_mario)
Após o último artigo sobre a introdução à tecnologia TON, passei um tempo aprofundando-me na documentação oficial de desenvolvimento da TON. Senti que havia algumas barreiras na aprendizagem, uma vez que o conteúdo atual parece mais uma documentação de desenvolvimento interna e não é muito amigável para novos desenvolvedores. Por isso, tentei organizar uma série de artigos sobre o desenvolvimento do projeto TON Chain, seguindo minha própria trajetória de aprendizado, na esperança de ajudar rapidamente os desenvolvedores iniciantes na criação de aplicativos TON. Se houver erros no texto, agradeço as correções, pois todos estamos aprendendo juntos.
Quais são as diferenças entre desenvolver NFTs na EVM e desenvolver NFTs na TON Chain?
Emitir um FT ou NFT é geralmente a necessidade mais básica para os desenvolvedores de DApp. Portanto, eu também o escolhi como ponto de partida para aprender. Primeiro, vamos entender as diferenças entre desenvolver um NFT no EVM stack e no TON Chain. Os NFTs baseados em EVM geralmente escolhem herdar o padrão ERC-721. O chamado NFT refere-se a um tipo de ativo criptografado indivisível, e cada ativo tem singularidade e algumas propriedades exclusivas. ERC-721 é um paradigma de desenvolvimento comum para esse tipo de ativo. Vamos dar uma olhada em quais funções um contrato ERC 721 comum precisa implementar e quais informações ele precisa registrar. A seguir está uma interface ERC 721 comum. Você pode ver que, diferente do FT, a interface de transferência requer o tokenId a ser transferido em vez da quantidade. O tokenId é a expressão mais básica da singularidade dos ativos NFTs. Claro, para incorporar mais propriedades, geralmente é registrado um metadata para cada tokenId. Este metadata é um link externo que contém dados adicionais extensíveis do NFT, como um link para uma imagem de PFP (perfil) e alguns nomes de propriedades.
Para os desenvolvedores familiarizados com Solidity ou orientados a objetos, implementar um contrato inteligente como este é uma tarefa fácil, desde que os tipos de dados necessários no contrato sejam definidos, como alguns relacionamentos de mapeamento-chave, e a lógica de modificação dos dados correspondentes às funções necessárias seja desenvolvida.
No entanto, no TON Chain, tudo isso é um pouco diferente, com duas razões principais que levam a essa diferença no núcleo:
Claro, houve uma discussão detalhada sobre outras diferenças técnicas no artigo anterior, mas este artigo pretende focar no desenvolvimento de contratos inteligentes. Portanto, não vamos entrar em detalhes. Os dois princípios de design acima mencionados fazem com que o desenvolvimento de contratos inteligentes no TON seja muito diferente do EVM. Como mencionado no início, sabemos que um contrato NFT precisa definir algumas relações de mapeamento, ou seja, mapping, para armazenar os dados relacionados ao NFT. O mais importante é o owners, este mapping armazena as relações de mapeamento dos endereços dos proprietários do NFT correspondentes a um determinado tokenID, determinando a propriedade do NFT, e a transferência é uma modificação dessa propriedade. Como essa é uma estrutura de dados potencialmente sem limites, é necessário evitá-la o máximo possível. Portanto, é recomendado pelo oficial usar a existência de estrutura de dados sem limites como critério para fragmentação. Ou seja, ao ter uma necessidade semelhante de armazenamento de dados, substitua-a pelo paradigma do contrato principal-secundário, gerenciando os dados correspondentes a cada chave por meio da criação de contratos secundários. E gerencie os parâmetros globais através do contrato principal ou ajude a lidar com a interação de informações internas entre os contratos secundários.
Isto também significa que os NFTs no TON precisam de ser concebidos com uma arquitetura semelhante, onde cada NFT é um subcontrato independente que armazena dados exclusivos, como o endereço do proprietário, metadados, etc., e é gerido por um contrato principal que controla os dados globais, como o nome do NFT, o símbolo, o fornecimento total, etc.
Depois de estabelecer a arquitetura, o próximo passo é resolver os requisitos das funções principais. Devido ao uso deste contrato principal-secundário, é necessário especificar quais funções são realizadas pelo contrato principal e quais são realizadas pelo contrato secundário, e como a comunicação interna entre eles é feita. Além disso, é importante considerar a lógica de reversão dos dados anteriores quando ocorrem erros de execução. Geralmente, antes de desenvolver projetos complexos de grande escala, é necessário criar um diagrama de classes para esclarecer o fluxo de informações e pensar cuidadosamente na lógica de reversão após falhas de chamada interna. Claro, embora o desenvolvimento de NFT mencionado acima seja simples, também pode ser realizado um processo semelhante de verificação.
Aprendendo a desenvolver contratos inteligentes TON a partir do código-fonte
TON escolheu projetar uma linguagem de programação de tipo estático semelhante a C, chamada Func, como a linguagem de desenvolvimento de contrato inteligente. Então vamos aprender a desenvolver contratos inteligentes TON a partir do código-fonte. Escolhi o exemplo NFT da documentação oficial do TON para apresentar. Os interessados podem consultar por si próprios. Neste caso, foi implementado um exemplo simples de TON NFT. Vamos dar uma olhada na estrutura do contrato, que é composta por dois contratos de função e três bibliotecas necessárias.
Os dois contratos principais de funcionalidade 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 um contrato inteligente TON. Sabemos que em Solidity, a persistência de dados é tratada automaticamente pelo EVM com base no tipo de parâmetro. Normalmente, as variáveis de estado do contrato inteligente serão persistidas automaticamente com base no valor mais recente após a conclusão da execução, e os desenvolvedores não precisam se preocupar com esse processo. No entanto, a situação é diferente em Func, os desenvolvedores precisam implementar a lógica de processamento correspondente por conta própria. Essa situação é um pouco semelhante a C e C++, onde é necessário considerar o processo de GC, mas outras novas linguagens de programação geralmente automatizam essa parte da lógica. Vamos dar uma olhada no código, primeiro importamos algumas bibliotecas necessárias e, em seguida, vemos a primeira função load_data que é usada para ler os dados persistidos. Sua lógica é retornar a célula de armazenamento persistente do contrato por meio da função get_data, observe que isso é implementado pela biblioteca padrão stdlib.fc e, geralmente, algumas funções nela podem ser usadas como funções do sistema.
O tipo de retorno desta função é cell, que é o tipo cell em TVM. Como introduzido anteriormente, sabemos que todos os dados persistentes na Blockchain TON estão armazenados em árvores de células. Cada célula pode conter 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 dados codificados de forma compacta e para obter os dados em texto simples específicos, é necessário converter a célula para o tipo chamado slice. A célula pode ser convertida em tipo slice através da função begin_parse e, em seguida, os dados e as referências a outras células podem ser carregados a partir do slice para obter os dados da célula. Note que a chamada na linha 15 é uma sintaxe de func em que é possível chamar diretamente a segunda função do valor de retorno da primeira função. E, por fim, carregar os dados correspondentes de forma persistente de acordo com a ordem. Note que este processo é diferente do Solidity e não é baseado em chamadas de hashmap, portanto, a ordem das chamadas deve ser estrita.
Na função save_data, a lógica é semelhante, apenas que 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élulas. Os bits de dados e as referências a outras células podem ser armazenados no construtor, e depois o construtor pode ser finalizado como uma nova célula. Primeiro, crie um construtor usando a função padrão begin_cell e armazene as funções relacionadas sequencialmente, observe que a ordem de chamada no texto anterior deve ser mantida consistente com a ordem de armazenamento aqui. Finalmente, conclua a construção da nova célula através de end_cell, momento em que a célula é gerenciada na memória. Por fim, usando a função set_data mais externa, você pode concluir o armazenamento persistente da célula.
A seguir, vamos dar uma olhada nas funções relacionadas ao negócio. Primeiro, precisamos introduzir um ponto de conhecimento sobre como criar um novo contrato por meio de um contrato, o que será frequentemente usado na arquitetura mestre-escravo que acabamos de apresentar. Sabemos que na TON, as chamadas entre contratos inteligentes são realizadas por meio do envio de mensagens internas. Isso é feito por meio de uma função chamada send_raw_message, observe que o primeiro parâmetro é a célula codificada da mensagem, o segundo parâmetro é uma flag que indica a diferença na forma de execução dessa transação. Na TON, diferentes modos de envio de mensagens internas foram estabelecidos, atualmente existem 3 modos de mensagens e 3 flags de mensagens. Um único modo pode ser combinado com vários (talvez nenhum) flags para obter o modo desejado. A combinação significa apenas somar os valores e preenchê-los. A tabela a seguir descreve os 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 de NFT, após codificar uma mensagem, envia o contrato interno através do send_raw_message e seleciona a flag 1 para 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. Vamos ver como isso é implementado.
Vamos dar uma olhada direta na linha 51. As duas funções acima são auxiliares para gerar as informações necessárias para a mensagem. Portanto, vamos voltar a olhar mais tarde. Este é um processo de codificação para criar uma mensagem interna de contrato inteligente. Alguns dos números intermediários também são identificadores usados para indicar os requisitos dessa mensagem interna. Aqui, vamos introduzir outro ponto de conhecimento. A TON escolheu uma linguagem binária chamada TL-B para descrever a forma de execução das mensagens e utiliza diferentes marcadores para implementar funcionalidades específicas nas mensagens internas. As duas cenas de uso mais comuns são a criação de novos contratos e a chamada de funções de contratos já implantados. A forma na linha 51 corresponde à primeira situação, a criação de um novo contrato de item NFT, especificada pelas linhas 55, 56 e 57. Primeiramente, a sequência de números na linha 55 é uma série de marcadores. Observe que o primeiro parâmetro do store_uint é um valor e o segundo é o tamanho em bits. Aqui, eles determinam que a mensagem interna é a criação de um contrato e os três últimos marcadores são definidos como 111 (que em decimal é 4 + 2 + 1). Os dois primeiros indicam que a mensagem é acompanhada pelos dados StateInit, que são o código-fonte do novo contrato e os dados de inicialização necessários. O último marcador indica o envio da mensagem interna, ou seja, a execução da lógica relacionada e os parâmetros necessários. Portanto, você verá que a linha de código 66 não define esses três dados, o que indica uma chamada de função em um contrato já implantado. Você pode verificar as regras de codificação específicas aqui.
Portanto, as regras de codificação do StateInit correspondem à linha de código 49, calculadas através do calculate_nft_item_state_init. Observe que a codificação dos dados do stateinit também segue uma regra de codificação TL-B estabelecida, envolvendo principalmente duas novas partes de contrato, ou seja, code e dados de inicialização. A ordem de codificação dos dados precisa ser consistente com a ordem de armazenamento de células persistentes especificadas pelo novo contrato. Na linha 36, pode-se ver que os dados de inicialização incluem item_index, semelhante ao tokenId em ERC 721, e o endereço de contrato atual retornado pela função padrão my_address, ou seja, collection_address, a ordem desses dados é consistente com as declarações em nft-item.
O próximo ponto a ser considerado no TON é que todos os contratos inteligentes não gerados podem ter seus endereços calculados antecipadamente, o que é semelhante à função create2 em Solidity. A geração de um novo endereço no TON é composta por duas partes: o identificador workchain e o hash do stateinit concatenados. O primeiro já foi explicado na introdução anterior como um valor uniforme necessário para a arquitetura de fragmentação ilimitada do TON. Ele é obtido pela função padrão workchain. O último é obtido pela função padrão cell_hash. Portanto, no exemplo dado, calculate_nft_item_address é a função que calcula antecipadamente o novo endereço do contrato. E o valor gerado é codificado na linha 53 como o endereço de recebimento da mensagem interna. O nft_content corresponde à chamada de inicialização para o contrato criado, cuja implementação será explicada no próximo artigo.
Quanto ao send_royalty_params, deve ser uma resposta a uma mensagem interna de uma solicitação somente leitura. Como mencionado anteriormente, as mensagens internas no TON não apenas contêm operações que podem modificar dados, mas também operações somente leitura também são implementadas dessa maneira. Portanto, este contrato é usado para esse tipo de operação. Primeiro, é importante observar que a linha 67 representa a marcação da função de retorno para o solicitante após a resposta a essa solicitação. Anote isso como os dados de retorno, que são o índice do item solicitado e os dados de royalties correspondentes.
A seguir, vamos introduzir o próximo ponto de conhecimento: os contratos inteligentes do TON têm apenas duas entradas unificadas, denominadas recv_internal e recv_external. O primeiro é a entrada unificada para todas as mensagens internas e o segundo é a entrada unificada para todas as mensagens externas. Os desenvolvedores precisam responder a diferentes solicitações dentro da função com base nas diferentes marcas especificadas pela mensagem, semelhante a um switch. Essas marcas são os marcadores de retorno na linha 67 mencionada acima. Voltando para o exemplo, primeiro verifique se há espaços vazios no message, e então analise as informações em message separadamente. Primeiro, na linha 83, analise o endereço do remetente, que será usado para verificar as permissões posteriormente. Observe o operador ~ aqui, que é outro açúcar sintático. Em seguida, analise o marcador de operação op e processe a solicitação correspondente com base em diferentes marcas. O exemplo inclui a resposta à solicitação do parâmetro de royalties ou a cunhagem de um novo NFT e o incremento do índice global.
O próximo ponto de conhecimento corresponde à linha 108. Pelo nome, podemos entender a lógica de processamento dessa função. Semelhante à função require em Solidity, a função Func utiliza throw_unless para lançar exceções. O primeiro argumento é o código de erro e o segundo é um valor booleano de verificação. Se o valor for falso, uma exceção é lançada com o código de erro correspondente. Nesta linha, equal_slices é usado para verificar se o sender_address analisado anteriormente é igual ao owner_address armazenado persistentemente no contrato, para fins de verificação de permissão.
Por fim, para tornar a estrutura do código mais clara, comecei a criar uma série de funções auxiliares para obter informações de persistência. Não vou entrar em detalhes aqui, mas os desenvolvedores podem usar essa estrutura como referência para desenvolver seus próprios contratos inteligentes.
O desenvolvimento de DApp na ecologia TON é realmente interessante, com grandes diferenças em relação ao paradigma de desenvolvimento EVM. Portanto, vou apresentar uma série de artigos sobre como desenvolver DApp na TON Chain. Vamos aprender juntos e aproveitar esta oportunidade. Também convido todos a interagirem comigo no Twitter, discutindo novas ideias interessantes de DApp e desenvolvendo juntos.