Chamadas entre Contratos (Cross-Contract Calls)
Last updated
Was this helpful?
Last updated
Was this helpful?
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
e CallBuilder
).
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!.
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 em vez disso.
BasicContractRef
Passo a passoVamos seguir o exemplo básico 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
para BasicContractRef
.
Faça o upload de OtherContract
na on-chain.
Instancie OtherContract
usando BasicContractRef
.
Chame OtherContract
usando BasicContractRef
.
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:
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ções std
, não poderemos gerar os metadados do nosso contrato.
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!
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
.
Primeiro, precisaremos instanciar BasicContractRef
.
Se bem-sucedido, isso irá produzir um endereço de contrato paraBasicContractRef
semelhante a:
Finalmente, podemos chamar os métodos de OtherContract
por meio de BasicContractRef
da seguinte maneira:
O que resultará em algo como:
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.
Abaixo está um exemplo de como instanciar um contrato usando o CreateBuilder
. Faremos o seguinte:
para instanciar o contrato carregado com um code_hash
de 0x4242...
sem limite de gás especificado (0
significa ilimitado)
enviando 10
unidades de valor transferido para a instância do contrato
instanciando com o new
construtor
com os seguintes argumentos
um u8
com valor 42
um bool
com valor true
um array de 32 u8
com valor 0x10
gerar o endereço (AccountId
) usando os salt_bytes
especificados
e esperamos que retorne um valor do tipo MyContractRef
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.
Ao usar Chamadas Call
s o CallBuilder
requer um contrato já instanciado.
Abaixo está um exemplo de como chamar um contrato usando o CallBuilder
. Faremos o seguinte:
fazer uma Call
regular
para 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 contrato
chamando a mensagem flip
com os seguintes argumentos
um u8
com valor 42
um bool
com valor true
um array de 32 u8
com valor 0x10
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!
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!) de 0x4242...
sem limite de gás especificado (0
significa "automático")
enviando 10
unidades de valor transferido para a instância do contrato
chamando a mensagem flip
com os seguintes argumentos
um u8
com valor 42
um bool
com valor true
um array de 32 u8
com valor 0x10
e esperamos que retorne um i32
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
)
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.
Preparação OtherContract
Importação OtherContract
Conexão BasicContractRef
Carregando OtherContract
Você precisará do sendo executado em segundo plano para os próximos passos.
Instanciando OtherContract
por meio BasicContractRef
Chamando com OtherContract
por meio de BasicContractRef
Os Builders e 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.
Para instanciar um contrato, você precisa de uma referência ao seu contrato, assim como .
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 .
CallBuilder: Call
Vimos um exemplo de como usar oCreateBuilder
para instanciar contratos na .
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 a documentação para , , e para obter mais detalhes sobre o tratamento adequado de erros.