🗣️Chamadas entre Contratos (Cross-Contract Calls)
Nos contratos ink!, é possível chamar mensagens e construtores de outros contratos na cadeia.
Existem algumas abordagens para realizar essas chamadas entre contratos no ink!:
Referências de contratos (ou seja,
ContractRef).Builders (ou seja,
CreateBuildereCallBuilder).
As referências de contratos só podem ser usadas para chamadas entre contratos (cross-contract) ink! a outros contratos ink!. Os builders podem ser usados para fazer chamadas entre contratos (cross-contract) para qualquer contrato Wasm, como os escritos em ink!, Solang ou ask!.
Referências de Contrato
As referências de contrato se referem a estruturas que são geradas pela geração de código ink! com o propósito de chamadas entre contratos.
Elas fornecem aos desenvolvedores uma maneira segura de interagir com um contrato.
Uma desvantagem de usá-las é que você precisa importar o contrato que deseja chamar como uma dependência do seu próprio contrato.
Se você quiser interagir com um contrato que já está na cadeia, você precisará usar a abordagem dos Builders em vez disso.
BasicContractRef Passo a passo
BasicContractRef Passo a passoVamos seguir o exemplo básico basic_contract_ref para demonstrar como as chamadas entre contratos usando referências de contrato funcionam.
O fluxo geral será:
Prepare
OtherContractpara ser importado por outros contratos.Importe
OtherContractparaBasicContractRef.Faça o upload de
OtherContractna on-chain.Instancie
OtherContractusandoBasicContractRef.Chame
OtherContractusandoBasicContractRef.
Preparação OtherContract
Precisamos garantir que a referência de contrato gerada pelo ink! para OtherContract esteja disponível para outras partes do código.
Fazemos isso reexportando a referência de contrato da seguinte forma:
Importação OtherContract
Em seguida, precisamos importar OtherContract para nosso contrato BasicContractRef.
Primeiro, adicionamos as seguintes linhas ao nosso arquivo Cargo.toml:
Duas coisas a serem observadas aqui:
Se não especificarmos a funcionalidade
ink-as-dependencyteremos erros de ligação.Se não habilitarmos a funcionalidade
stdpara compilaçõesstd, não poderemos gerar os metadados do nosso contrato.
Conexão BasicContractRef
Primeiro, iremos importar a referência de contrato de OtherContracte declarar a referência como parte da nossa estrutura de armazenamento.
Em seguida, precisamos adicionar uma maneira de instanciar OtherContract. Fazemos isso a partir do construtor do nosso contrato.
Observe que, para instanciar um contrato, precisamos ter acesso ao contrato carregado na cadeia code_hash. Voltaremos a isso mais tarde.
Uma vez que temos uma referência instanciada para OtherContract, podemos chamar suas mensagens como se fossem métodos normais do Rust!
Carregando OtherContract
Você precisará do substrate-contracts-node sendo executado em segundo plano para os próximos passos.
Podemos carregar OtherContract usando o cargo-contract da seguinte forma:
Se bem-sucedido, isso irá produzir um code_hash semelhante a:
Podemos então usar este code_hash para instanciar nosso contrato BasicContractRef.
Instanciando OtherContract por meio BasicContractRef
Primeiro, precisaremos instanciar BasicContractRef.
Se bem-sucedido, isso irá produzir um endereço de contrato paraBasicContractRef semelhante a:
Chamando com OtherContract por meio de BasicContractRef
Finalmente, podemos chamar os métodos de OtherContract por meio de BasicContractRef da seguinte maneira:
O que resultará em algo como:
Builders
Os BuildersCreateBuilder e CallBuilder oferecem interfaces de baixo nível e flexíveis para realizar chamadas entre contratos. O CreateBuilder permite instanciar contratos já carregados, e o CallBuilder permite chamar mensagens em contratos instanciados.
CreateBuilder
O CreateBuilder oferece uma maneira fácil de instanciar um contrato. Note que você ainda precisará ter carregado esse contrato previamente.
NOTA: Para uma revisão sobre a diferença entre upload e instantiate, consulte aqui.
Para instanciar um contrato, você precisa de uma referência ao seu contrato, assim como na seção anterior.
Abaixo está um exemplo de como instanciar um contrato usando o CreateBuilder. Faremos o seguinte:
para instanciar o contrato carregado com um
code_hashde0x4242...sem limite de gás especificado (
0significa ilimitado)enviando
10unidades de valor transferido para a instância do contratoinstanciando com o
newconstrutorcom os seguintes argumentos
um
u8com valor42um
boolcom valortrueum array de 32
u8com valor0x10
gerar o endereço (
AccountId) usando ossalt_bytesespecificadose esperamos que retorne um valor do tipo
MyContractRef
Como o método CreateBuilder::instantiate() retorna uma referência ao contrato, podemos usar essa referência para chamar mensagens da mesma forma que na seção anterior.
CallBuilder
O CallBuilder oferece algumas maneiras de chamar mensagens de outros contratos. Existem duas abordagens principais para isso:
Chamadas Calls e DelegateCalls. Vamos abordar brevemente ambas aqui.
CallBuilder: Call
Ao usar Chamadas Calls o CallBuilder requer um contrato já instanciado.
Vimos um exemplo de como usar oCreateBuilder para instanciar contratos na seção anterior.
Abaixo está um exemplo de como chamar um contrato usando o CallBuilder. Faremos o seguinte:
fazer uma
Callregularpara um contrato no endereço
0x4242...sem limite de gás especificado (
0significa ilimitado)enviando
10unidades de valor transferido para a instância do contratochamando a mensagem
flipcom os seguintes argumentos
um
u8com valor42um
boolcom valortrueum array de 32
u8com valor0x10
e esperamos que retorne um valor do tipo
bool
Observação: Os argumentos da mensagem serão codificados na ordem em que são fornecidos ao CallBuilder. Isso significa que eles devem corresponder à ordem (e tipo) em que aparecem na assinatura da função.
Você não será capaz de obter nenhum feedback sobre isso em tempo de compilação. Você só descobrirá que sua chamada falhou em tempo de execução!
CallBuilder: Delegate Call
Você também pode usar o CallBuilder para criar chamadas usando a mecânica DelegateCall. Se você precisa de um lembrete sobre o que são as chamadas de delegação, consulte este artigo.
No caso das DelegateCalls, não exigimos um contrato já instanciado. Apenas precisamos do code_hash de um contrato enviado.
Abaixo está um exemplo de como fazer uma chamada de delegação a um contrato usando o CallBuilder. Faremos o seguinte:
fazer uma chamada de delegação
DelegateCallpara um contrato com um
code_hash(não endereço do contrato!) de0x4242...sem limite de gás especificado (
0significa "automático")enviando
10unidades de valor transferido para a instância do contratochamando a mensagem
flipcom os seguintes argumentos
um
u8com valor42um
boolcom valortrueum array de 32
u8com valor0x10
e esperamos que retorne um
i32
Builder Error Handling
O CreateBuilder e o CallBuilder oferecem recursos de tratamento de erros com os métodos try_instantiate() e try_invoke(), respectivamente.
Isso permite que os desenvolvedores de contratos lidem com dois tipos de erros:
Erros do ambiente de execução subjacente (por exemplo, o pallet Contracts)
Erros da linguagem de programação (por exemplo,
LangErrors)
Consulte a documentação para try_instantiate, try_invoke, ink::env::Error e ink::LangError para obter mais detalhes sobre o tratamento adequado de erros.
DICA: Como o CallBuilder requer apenas o AccountId do contrato e o seletor de mensagem, podemos chamar contratos Solidity compilados usando o compilador Solang e implantados em uma cadeia que suporta o pallet-contracts. Veja aqui um exemplo de como fazer isso.
No entanto, o inverso, chamadas de Solidity para ink!, não é suportado pelo Solang, mas há planos para implementar isso no futuro.
Last updated
Was this helpful?