🇵🇹
DAO Lunes Labs - PT
  • 👋Seja Bem-vindo a DAO Lunes Labs
  • Overview
    • 🚪Introdução
    • 🚀Manifesto DAO Lunes Labs
    • 🍕Tokenomics
    • 👑Proposta de Valor
    • 🧑‍🚀Comunidade e Participação
    • 🗺️Roadmap
    • 💼Compromisso com Ética e Responsabilidade
    • 👾Programa de Bug Bounty: Lunes Security Initiative (LSI)
  • Developers
    • 💽Para Nodes
      • 🥾Instalar Node
        • 🏗️Rust toolchain
        • 🐧Linux
    • 🖥️Para Desenvolvedores
      • 📑Smart Contract Ink! 4.x
        • ✨Configuração
          • 📐Criando um projeto com ink!
          • 🖥️Compile Seu Contrato
          • ⛓️Executar um nó Lunes
          • ⬆️Implante seu contrato
          • 🔛Chame Seu Contrato
          • 🛠️Solução de problemas
        • 🪄Fundamentos
          • 👾Modelo de Contrato
          • 👨‍🚀Armazenando Valores
          • 🔭Lendo Valores do Armazenamento
          • 🦸‍♂️Alterando os Valores de Armazenamento
          • 🎡Eventos
          • 👨‍🔧Seletores
          • 🪶Definições de Trait
          • 🗣️Chamadas entre Contratos (Cross-Contract Calls)
          • 🦸Contratos Atualizáveis
          • 🤺Funções do Ambiente
          • 🏟️Tipos de Ambiente de Cadeia
          • 💠Metadados
          • 🧪Testes de Contrato
          • 🕵️‍♂️Depuração de Contratos
          • 🔬Verificação de Contrato
        • 🎙️Macros e Atributos
          • 📇#[ink::contract]
          • 👽#[ink(anonymous)]
          • 👷#[ink(constructor)]
          • 📏#[ink(default)]
          • 🎢#[ink(event)]
          • 🛩️#[ink(impl)]
          • 📧#[ink(message)]
          • 👨‍💼#[ink(namespace = "…")]
          • 💸#[ink(payable)]
          • ⚡#[ink(selector = S:u32)]
          • 💽#[ink(storage)]
          • 💣#[ink(topic)]
          • ⛓️#[ink::chain_extension]
        • 💽Storege & Data Structires
          • Working with Mapping
          • Storage Layout
          • Custom Data Structures
          • Metadata Format
        • 👾Frontend Development
          • Getting Started
          • Connect Wallet
          • Hooks
            • All Hooks
            • Contracts
              • useCall
              • useCallSubscription
              • useContract
              • useDryRun
              • useEventSubscription
              • useEvents
              • useTx
              • useTxPaymentInfo
            • Wallets
              • useWallet
              • useAllWallets
              • useInstalledWallets
              • useUninstalledWallets
            • API
              • useApi
              • useBalance
              • useBlockHeader
          • Configuration
          • useink / core
            • Contracts
              • Call
              • decodeCallResult
              • decodeError
              • getRegistryError
              • toAbiMessage
          • useink / chains
            • Getting Started
            • Chain Configurations
            • ChainId
          • useink / notifications
            • Getting Started
            • Configuration
            • useNotifications
            • toNotificationLevel
          • useink / utils
            • Getting Started
            • Pick Helpers
            • tx Helpers
            • Types
        • 💡Examples
          • 📔Smart Contracts
          • 📱Dapps
        • 🛠️Tools
          • 🖌️OpenBrush
      • 📒Smart Contract - EVM
        • Create ERC-20 Ink Token!
      • 💰Desenvolvendo uma Wallet Lunes
        • 👾Transações de Tokens PSP22
    • 🎨Para Designers
      • 🖌️Brand Lunes
Powered by GitBook
On this page
  • Referências de Contrato​
  • Builders​

Was this helpful?

  1. Developers
  2. Para Desenvolvedores
  3. Smart Contract Ink! 4.x
  4. Fundamentos

Chamadas entre Contratos (Cross-Contract Calls)

Last updated 1 year ago

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!:

  1. Referências de contratos (ou seja, ContractRef).

  2. 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!.

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 em vez disso.

BasicContractRef Passo a passo

Vamos seguir o exemplo básico para demonstrar como as chamadas entre contratos usando referências de contrato funcionam.

O fluxo geral será:

  1. Prepare OtherContract para ser importado por outros contratos.

  2. Importe OtherContract para BasicContractRef.

  3. Faça o upload de OtherContract na on-chain.

  4. Instancie OtherContract usando BasicContractRef.

  5. 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:

pub use self::other_contract::OtherContractRef;

Em seguida, precisamos importar OtherContract para nosso contrato BasicContractRef.

Primeiro, adicionamos as seguintes linhas ao nosso arquivo Cargo.toml:

# In `basic_contract_ref/Cargo.toml`

other_contract = { path = "other_contract", default-features = false, features = ["ink-as-dependency"] }

# -- snip --

[features]
default = ["std"]
std = [
    "ink/std",
    # -- snip --
    "other_contract/std",
]

Duas coisas a serem observadas aqui:

  1. Se não especificarmos a funcionalidade ink-as-dependency teremos erros de ligação.

  2. 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 OtherContracte declarar a referência como parte da nossa estrutura de armazenamento.

// In `basic_contract_ref/lib.rs`

use other_contract::OtherContractRef;

#[ink(storage)]
pub struct BasicContractRef {
    other_contract: OtherContractRef,
}

Em seguida, precisamos adicionar uma maneira de instanciar OtherContract. Fazemos isso a partir do construtor do nosso contrato.

// In `basic_contract_ref/lib.rs`

#[ink(constructor)]
pub fn new(other_contract_code_hash: Hash) -> Self {
    let other_contract = OtherContractRef::new(true)
        .code_hash(other_contract_code_hash)
        .endowment(0)
        .salt_bytes([0xDE, 0xAD, 0xBE, 0xEF])
        .instantiate();

    Self { other_contract }
}

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!

// In `basic_contract_ref/lib.rs`

#[ink(message)]
pub fn flip_and_get(&mut self) -> bool {
    self.other_contract.flip();
    self.other_contract.get()
}

Podemos carregar OtherContract usando o cargo-contract da seguinte forma:

# In the `basic_contract_ref` directory
cargo contract build --manifest-path other_contract/Cargo.toml
cargo contract upload --manifest-path other_contract/Cargo.toml --suri //Alice

Se bem-sucedido, isso irá produzir um code_hash semelhante a:

Code hash "0x74a610235df4ff0161f0247e4c9d73934b70c1520d24ef843f9df9fcc3e63caa"

Podemos então usar este code_hash para instanciar nosso contrato BasicContractRef.

Primeiro, precisaremos instanciar BasicContractRef.

# In the `basic_contract_ref` directory
cargo contract build
cargo contract instantiate \
    --constructor new \
    --args 0x74a610235df4ff0161f0247e4c9d73934b70c1520d24ef843f9df9fcc3e63caa \
    --suri //Alice --salt $(date +%s)

Se bem-sucedido, isso irá produzir um endereço de contrato paraBasicContractRef semelhante a:

Contract 5CWz6Xnivp9PSoZq6qPRP8xVAShZgtNVGTCLCsq3qzqPV7Rq

Finalmente, podemos chamar os métodos de OtherContract por meio de BasicContractRef da seguinte maneira:

cargo contract call --contract 5CWz6Xnivp9PSoZq6qPRP8xVAShZgtNVGTCLCsq3qzqPV7Rq \
    --message flip_and_get --suri //Alice --dry-run

O que resultará em algo como:

Result Success!
Reverted false
Data Ok(true)

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

use contract::MyContractRef;
let my_contract: MyContractRef = build_create::<MyContractRef>()
    .code_hash(Hash::from([0x42; 32]))
    .gas_limit(0)
    .endowment(10)
    .exec_input(
        ExecutionInput::new(Selector::new(ink::selector_bytes!("new")))
            .push_arg(42)
            .push_arg(true)
            .push_arg(&[0x10u8; 32])
    )
    .salt_bytes(&[0xDE, 0xAD, 0xBE, 0xEF])
    .returns::<MyContractRef>()
    .instantiate();

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.

Ao usar Chamadas Calls 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

let my_return_value = build_call::<DefaultEnvironment>()
    .call(AccountId::from([0x42; 32]))
    .gas_limit(0)
    .transferred_value(10)
    .exec_input(
        ExecutionInput::new(Selector::new(ink::selector_bytes!("flip")))
            .push_arg(42u8)
            .push_arg(true)
            .push_arg(&[0x10u8; 32])
    )
    .returns::<bool>()
    .invoke();

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

let my_return_value = build_call::<DefaultEnvironment>()
    .delegate(ink::primitives::Hash::from([0x42; 32]))
    .exec_input(
        ExecutionInput::new(Selector::new(ink::selector_bytes!("flip")))
            .push_arg(42u8)
            .push_arg(true)
            .push_arg(&[0x10u8; 32])
    )
    .returns::<i32>()
    .invoke();

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:

  1. Erros do ambiente de execução subjacente (por exemplo, o pallet Contracts)

  2. 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

Builders

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.

CreateBuilder

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

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, .

Builder Error Handling

Consulte a documentação para , , e para obter mais detalhes sobre o tratamento adequado de erros.

🖥️
📑
🪄
🗣️
​
Builders
​
basic_contract_ref
​
​
​
​
substrate-contracts-node
​
​
​
CreateBuilder
CallBuilder
​
na seção anterior
seção anterior
​
​
seção anterior
​
consulte este artigo
​
try_instantiate
try_invoke
ink::env::Error
ink::LangError