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,
CreateBuilder
eCallBuilder
).
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
OtherContract
para ser importado por outros contratos.Importe
OtherContract
paraBasicContractRef
.Faça o upload de
OtherContract
na on-chain.Instancie
OtherContract
usandoBasicContractRef
.Chame
OtherContract
usandoBasicContractRef
.
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-dependency
teremos erros de ligação.Se não habilitarmos a funcionalidade
std
para compilaçõesstd
, não poderemos gerar os metadados do nosso contrato.
Conexão BasicContractRef
Primeiro, iremos importar a referência de contrato de OtherContract
e 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_hash
de0x4242...
sem limite de gás especificado (
0
significa ilimitado)enviando
10
unidades de valor transferido para a instância do contratoinstanciando com o
new
construtorcom os seguintes argumentos
um
u8
com valor42
um
bool
com valortrue
um array de 32
u8
com valor0x10
gerar o endereço (
AccountId
) usando ossalt_bytes
especificadose 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 Call
s e DelegateCall
s. Vamos abordar brevemente ambas aqui.
CallBuilder: Call
Ao usar Chamadas Call
s 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
Call
regularpara um contrato no endereço
0x4242...
sem limite de gás especificado (
0
significa ilimitado)enviando
10
unidades de valor transferido para a instância do contratochamando a mensagem
flip
com os seguintes argumentos
um
u8
com valor42
um
bool
com valortrue
um array de 32
u8
com 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
DelegateCall
para um contrato com um
code_hash
(não endereço do contrato!) de0x4242...
sem limite de gás especificado (
0
significa "automático")enviando
10
unidades de valor transferido para a instância do contratochamando a mensagem
flip
com os seguintes argumentos
um
u8
com valor42
um
bool
com valortrue
um array de 32
u8
com 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?