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.
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
Importante
Edite o script demo.sh e defina o local apropriado do executável do Sandbox.
SANDBOX="$HOME/sandbox/sandbox"
Execute o script.
./demo.sh
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Top comments (0)