WEB3DEV

Cover image for Usando um Contrato Inteligente para Gerar Contratos Inteligentes Adicionais - Algorand
Paulo Gio
Paulo Gio

Posted on • Atualizado em

Usando um Contrato Inteligente para Gerar Contratos Inteligentes Adicionais - Algorand

Com o TEAL versão 6, os contratos inteligentes da Algorand podem fazer chamadas de aplicativo para aplicativo usando transações internas. Este é um componente crítico em muitos aplicativos multicontratos e ter essa capacidade oferece aos desenvolvedores uma grande flexibilidade na forma como eles projetam seus aplicativos blockchain. Essa mudança permite que os desenvolvedores não apenas chamem outro contrato inteligente, mas também usem um contrato inteligente para criar um novo contrato, chamá-lo e até excluir o contrato. Isso efetivamente lhe dá a capacidade de gerar contratos de curta duração que podem ser usados e depois destruídos, talvez em um único grupo de transações atômicas.

Neste artigo, veremos um exemplo de contrato-pai que gera contratos-filho adicionais, chama o contrato-filho recém-criado e finalmente destrói o contrato-filho.

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2022/05/05%2014%3A20/parentchild.jpg

O exemplo neste artigo usa a Interface Binária de Aplicações (ABI). Para obter mais informações sobre como usar a ABI com seus contratos inteligentes, consulte este artigo, a especificação da ABI ou a documentação do desenvolvedor. Os desenvolvedores devem usar o Compositor de Transações Atômicas (ATC) para criar transações adequadas baseadas em ABI. Para obter mais informações sobre o ATC, consulte a documentação do desenvolvedor. Neste exemplo, um script de shell é usado em conjunto com a ferramenta de comando goal, pois estamos focando nos contratos.

Executando o exemplo

O código para este exemplo está localizado no repositório do github da Algorand DevRel. Para executar este exemplo rapidamente, primeiro verifique se você tem o Sandbox em execução no modo dev. Para entender como instalar e executar, veja o Readme do Sandbox. Depois que o Sandbox estiver instalado e em execução, faça o seguinte em um terminal.

git clone https://github.com/algorand-devrel/parent-child-contracts
cd parent-child-contracts
Enter fullscreen mode Exit fullscreen mode

Importante

Edite o script demo.sh e defina o local apropriado do executável do Sandbox.

SANDBOX="$HOME/sandbox/sandbox"
Enter fullscreen mode Exit fullscreen mode

Execute o script.

./demo.sh
Enter fullscreen mode Exit fullscreen mode

Esse script de shell de demonstração implanta o contrato-pai no Sandbox, financia o contrato, implanta um contrato-filho por meio do contrato-pai, chama o contrato-filho e destrói o contrato-filho por meio do contrato-pai.

O código do contrato-filho neste exemplo simplesmente permite chamá-lo com um argumento de string e reverterá e retornará a string.

Observação

Este exemplo não é um código de implantação e é apenas para fins educacionais. Muitas verificações de segurança não estão incluídas.

Explicação do código

O contrato-pai suporta três métodos de ABI (deploy, update e delete). Esses três métodos podem ser usados para primeiro criar o contrato-filho, possivelmente atualizar a origem do contrato-filho e, finalmente, excluir o contrato-filho. Seus respectivos descritores de métodos de ABI são mostrados abaixo.

Método Deploy da ABI

deploy(pay,byte[],byte[])uint64
Enter fullscreen mode Exit fullscreen mode

A chamada do método deploy usa uma transação de pagamento como o primeiro parâmetro, que é usado para cobrir o saldo mínimo exigido pelo contrato-pai para criar o contrato-filho. Observe que isso dependerá da quantidade de estado que o contrato-filho usa. Os próximos dois parâmetros contêm os bytes compilados para a aprovação e programas claros para o contrato inteligente. Por fim, o método deve retornar um inteiro contendo o ID do aplicativo do contrato-filho recém-criado.

Método Update da ABI

update(application,byte[],byte[])bool
Enter fullscreen mode Exit fullscreen mode

O método update usa a aplicação do contrato-filho como primeiro parâmetro. Os próximos dois parâmetros contêm os bytes compilados para os programas de aprovação e limpeza atualizados. O método retornará true se a atualização for bem-sucedida.

Método Destroy da ABI

destroy(application)bool
Enter fullscreen mode Exit fullscreen mode

O método destroy leva apenas o ID do aplicativo do contrato-filho para excluir. Se for bem-sucedido, o método retornará true.

Implementando o Contrato-pai

A seção a seguir explica como o contrato-pai é implementado.

Configurando a Implementação

Antes de implementar cada um dos métodos, precisamos de algum código inicial para configurar o contrato-pai.

#pragma version 6

txn ApplicationID
bz handle_setup

txn OnCompletion
int UpdateApplication
==
bnz handle_update
Enter fullscreen mode Exit fullscreen mode

Primeiro, o ID do aplicativo do contrato-pai é verificado para ver se ele já foi criado. Isso apenas nos diz efetivamente se o contrato-pai já foi implantado. Se esta for a primeira vez que o código foi executado (ApplicationID será 0), ramificamos para o rótulo handle_setup.

Em seguida, a propriedade da transação OnCompletion é verificada para ver se a transação é uma atualização do contrato inteligente pai. Nesse caso, ramificamos para o rótulo handle_update.

Implementação de Roteamento

A próxima parte do contrato é usada especificamente para rotear métodos ABI específicos para o código apropriado a ser executado.

method "deploy(pay,byte[],byte[])uint64"
txn ApplicationArgs 0
==
bnz method_deploy

method "update(application,byte[],byte[])bool"
txn ApplicationArgs 0
==
bnz method_update

method "destroy(application)bool"
txn ApplicationArgs 0
==
bnz method_destroy

err

handle_setup:
    int 1
    return

handle_update:
    txn Sender
    global CreatorAddress
    ==
    return
Enter fullscreen mode Exit fullscreen mode

Ao usar a ABI, contamos com descritores de métodos, e para facilitar a verificação desses descritores, o método de código de operação TEAL pode ser usado. Com métodos ABI, o descritor é colocado no primeiro argumento do aplicativo. Isso permite comparar este argumento com o descritor exato definido na especificação do método ABI. O código acima está procurando ramificar em deploy, update, ou destroy. Finalmente, no código acima, a configuração ou atualização do código inicial é tratada. No caso de configuração (ou seja, quando implantamos o contrato-pai pela primeira vez), o contrato apenas retorna com 1 no topo da pilha, permitindo que a transação seja bem-sucedida. O código de atualização trata da situação quando o código do contrato-pai está sendo atualizado. Este código simplesmente verifica se o código só pode ser atualizado pela conta que originalmente criou o contrato.

A Implementação do Método Deploy

A implementação real do método deploy é implementada com o código a seguir. Esse código espera que a primeira transação no grupo seja uma transação de pagamento que financie o contrato-pai, para que ele possa criar o contrato-filho.

method_deploy:
    // Verifique se o remetente está financiando o aplicativo com
    // Algo suficiente para implantar um contrato inteligente.
    txn GroupIndex
    int 1
    -
    dup
    gtxns Receiver
    global CurrentApplicationAddress
    ==
    assert
    gtxns Amount
    int 100000
    >=
    assert
Enter fullscreen mode Exit fullscreen mode

Este código obtém o índice de posição atual da chamada do aplicativo dentro do grupo de transações. Em seguida, subtrai 1 porque a transação de pagamento precede a chamada do aplicativo em 1 posição. O comando dup simplesmente faz uma cópia do número final, que deve deixar dois inteiros no topo da pilha. Ambos são iguais e representam a posição da transação de pagamento no grupo. O código então usa o número inteiro superior para fazer referência ao grupo de transações (usando o código de operação gtxns) e olhar para o receptor da transação anterior. Deve ser o endereço do contrato-pai. Caso contrário, o código falhará no código de operação assert. Este código deixará o inteiro final no topo da pilha que representa a transação anterior no grupo. Usando este índice, o código de operação gtxns é usado novamente para pesquisar o valor da última transação. Se for maior ou igual a 100.000 microalgos, o código continuará. Caso contrário, o código falhará no segundo assert.

Em seguida, a transação interna é criada para implantar o contrato-filho.

// Inicie a transação interna para implantar o novo contrato inteligente
// usando os programas de aprovação e limpeza passados como argumentos.
    itxn_begin

    int appl
    itxn_field TypeEnum

    int NoOp
    itxn_field OnCompletion

    // Obtenha o comprimento e extraia-o, removendo os primeiros 2 bytes.
    txn ApplicationArgs 1
    dup
    len
    int 2
    swap
    substring3
    itxn_field ApprovalProgram

    // Obtenha o comprimento e extraia-o, removendo os primeiros 2 bytes..
    txn ApplicationArgs 2
    dup
    len
    int 2
    swap
    substring3
    itxn_field ClearStateProgram

    int 0
    itxn_field Fee

    itxn_submit
Enter fullscreen mode Exit fullscreen mode

Este código usa os códigos de operação itxn para configurar e transmitir a transação interna. Consulte a documentação do desenvolvedor para obter mais detalhes sobre transações internas.

Este código primeiro define o tipo de transação como uma transação de aplicativo, define o subtipo de transação de aplicativo como um NoOp, que é uma chamada geral para um contrato inteligente, define a propriedade do programa de aprovação para os bytes armazenados no segundo argumento, define a propriedade do programa de limpeza para os bytes armazenados no terceiro argumento, define a taxa de transação para a transação interna como 0 (isso significa que a transação que chama esse método no contrato-pai deve pagar a taxa pela transação interna) e, em seguida, envia a transação.

Uma observação importante aqui é que quando a ABI codifica os parâmetros da matriz de bytes, o comprimento da matriz de bytes é armazenado nos primeiros dois bytes da matriz. Para implantar o contrato-filho, precisamos remover esses dois primeiros bytes. Este código trata dessa operação.

 txn ApplicationArgs 1
    dup
    len
    int 2
    swap
    substring3
Enter fullscreen mode Exit fullscreen mode

https://algorand-devloper-portal-app.s3.amazonaws.com/static/EditorImages/2022/05/05%2014%3A41/stringarg.jpg

Se o envio falhar, a chamada do aplicativo pai falhará. Se for bem-sucedido, o código manipula o tipo de retorno da ABI. Neste caso é um inteiro contendo o valor do contrato-filho criado.

// Usando a string de retorno ARC4, concatenando o appID recém-criado.
    byte 0x151f7c75
    itxn CreatedApplicationID
    itob
    concat
    log

    int 1
    return
Enter fullscreen mode Exit fullscreen mode

Ao usar a ABI, os tipos de retorno são prefixados com um conjunto de bytes 0x151f7c75. No código acima, esses bytes são colocados no topo da pilha e o código de operação itxn CreatedApplicationID é usado para obter o ID do aplicativo do contrato-filho. Isso é primeiro convertido em bytes usando itob e concatenado (código de operação concat) para os bytes acima. O código de operação log registra o resultado na transação concluída. Os SDKs ou a CLI goal procuram os bytes prefixados nos logs para extrair o resultado da chamada do método. Finalmente, um 1 é enviado para a pilha e o código de operação return é usado para sair do método com sucesso.

A Implementação do Método Update

Esse método é usado pelo contrato-pai para atualizar o código do contrato inteligente do contrato-filho. O método update é quase idêntico ao método deploy. As principais diferenças são que o ID do aplicativo do contrato-filho é colocado no segundo argumento e os programas de aprovação e limpeza estão no terceiro e quarto argumento. Esse método também retorna um valor true quando bem-sucedido, em vez do ID do aplicativo do contrato-filho. O tipo de transação de subaplicativo também é definido como UpdateApplication, que é uma transação de aplicativo para atualizar um contrato inteligente, em vez de NoOp.

method_update:
    itxn_begin

    int appl
    itxn_field TypeEnum

    int UpdateApplication
    itxn_field OnCompletion

    txn ApplicationArgs 1
    btoi
    txnas Applications
    itxn_field ApplicationID

    txn ApplicationArgs 2
    dup
    len
    int 2
    swap
    substring3
    itxn_field ApprovalProgram

    txn ApplicationArgs 3
    dup
    len
    int 2
    swap
    substring3
    itxn_field ClearStateProgram

    int 0
    itxn_field Fee

    itxn_submit

    byte 0x151f7c7580
    log

    int 1
    return
Enter fullscreen mode Exit fullscreen mode

Ao passar um ID de aplicativo como um argumento ABI, os SDKs e a CLI goal automaticamente colocam esse ID de aplicativo na matriz de aplicativos. Essa matriz limita quais contratos adicionais podem ser examinados ou chamados no aplicativo atual. Para obter mais informações sobre as matrizes de contratos inteligentes, consulte a documentação do desenvolvedor. Esse código lê a matriz do aplicativo da chamada para o contrato-pai e define a propriedade de transação interna ApplicationID.

    txn ApplicationArgs 1
    btoi
    txnas Applications
    itxn_field ApplicationID
Enter fullscreen mode Exit fullscreen mode

Essa propriedade determina qual contrato chamar na transação interna.

Esse código usa o mesmo código de retorno do método de implantação, mas em vez de concatenar o novo ID do aplicativo-filho, ele adiciona 0x80 aos bytes de retorno. Isso sinaliza efetivamente à ABI um tipo de retorno true para valores de retorno booleanos.

    byte 0x151f7c7580
    log

    int 1
    return
Enter fullscreen mode Exit fullscreen mode

A Implementação do Método Destroy

Esse método é chamado para excluir um contrato-filho. Este método tem muitos dos mesmos campos que os dois métodos anteriores. O subtipo de aplicativo é definido como DeleteApplication, que é usado para excluir um contrato inteligente. Depois de excluir o contrato-filho, o contrato-pai também emite uma transação de pagamento para retornar os 10.000 micro Algos originais depositados durante a chamada do método deploy. A propriedade TypeEnum da transação interna é definida para pay em vez de appl, para indicar uma transação de pagamento. O remetente assume como padrão o endereço do contrato-pai, o destinatário é definido como o remetente do chamador do método destroy e o valor é definido como 10.000 micro Algos.

method_destroy:
    itxn_begin

    int appl
    itxn_field TypeEnum

    int DeleteApplication
    itxn_field OnCompletion

    txn ApplicationArgs 1
    btoi
    txnas Applications
    itxn_field ApplicationID

    int 0
    itxn_field Fee

    Itxn_submit

    //devolver o depósito original
    itxn_begin

    int pay
    itxn_field TypeEnum

    txn Sender
    itxn_field Receiver

    int 100000
    itxn_field Amount

    int 0
    itxn_field Fee

    itxn_submit

    byte 0x151f7c7580
    log

    int 1
    return
Enter fullscreen mode Exit fullscreen mode

Após a emissão das duas transações, se elas forem concluídas com sucesso, um valor verdadeiro é retornado, semelhante ao método de atualização. Se uma dessas falhar, todas as transações, incluindo a chamada-pai original, falharão.

A versão original deste tutorial foi escrita por Jason Weathersby. Traduzido por Paulinho Giovannini

Latest comments (0)