4

The title of the question is a bit open ended, but I am trying to make cross contract calling work right now and realised, that there is a lot of open question on how it exactly works and how the parts fit together.

I am using the directory structure recommended by openbrush (more or less). At the moment, my project looks like this:

  • contracts
    • contract_1
      • Cargo.toml
      • lib.rs
    • contract_2
      • Cargo.toml
      • lib.rs
  • traits
    • contract_1.rs
    • contract_2.rs
    • mod.rs
  • Cargo.toml
  • lib.rs

I want to call contract_1 in contract_2.

Defining the traits I have a wrapper for contract_1 like this:

#[openbrush::wrapper]
pub type ContractOneRef = dyn ContractOne;

Then I try to call on contract_1 in contract_2 like this:

#![cfg_attr(not(feature = "std"), no_std)]

use ink_lang as ink;

#[ink::contract]
mod contract_2 {
use c1_contract::contract_1::*;
use project::traits::contract_2::*;

#[ink(storage)]
pub struct Manager {
    contract_1: ContractOneRef
}

impl ContractTwo {
    
    #[ink(constructor)]
    pub fn new(
        code_hash: Hash
    ) -> Self {
        // Here I am not even sure what exactly to do.
        
    }

    ...
}

The idea is, that contract_1 is already deployed on the chain and I want to call on it with contract_2. I have found conflicting information online on how to reference the contract which I want to call on.

Here there are two examples on how to do it. The answer is, that the first example is deprecated and the second example does not really get any explanation. Just plain code. The wrapper feature of openbrush is also mentioned, but the only thing it seems to explain is how to do the trait definition.

Looking at the code example I am thinking, that the way to define/link contract_1 could look like this:

    let total_balance = Self::env().balance();
    let salt = version.to_le_bytes();
    let contract_1 = ContractOneRef::new(input_values)
        .endowment(total_balance / 4)
        .code_hash(accumulator_code_hash)
        .salt_bytes(salt)
        .instantiate()
        .unwrap_or_else(|error| {
            panic!(
                "failed at instantiating the Accumulator contract: {:?}",
                error
            )
        }

    );

But this just throws a lot of errors like:

33 |             let salt = version.to_le_bytes();
   |                        ^^^^^^^ not found in this scope

or

   |
37 |                 .instantiate()
   |                  ^^^^^^^^^^^ method not found in `CreateBuilder<DefaultEnvironment, Set<ink_env::Hash>, Unset<u64>, Set<u128>, Set<ExecutionInput<ArgumentList<ink_env::call::utils::Argument<String>, ArgumentList<ink_env::call::utils::Argument<String>, ArgumentList<ink_env::call::utils::Argument<ink_env::AccountId>, ArgumentList<ink_env::call::utils::Argument<Vec<u8>>, ArgumentList<ArgumentListEnd, ArgumentListEnd>>>>>>>, Unset<Salt>, c1_contract::contract_one::ContractOneRef>`
   |

Is this the right way to call on a contract which is using the wrapper of openbrush? And if yes, what am I doing wrong?

Also, what are the different values they are trying passing over there?

  • endowment
  • code_hash
  • isntantiate
  • salt

And if this is calling on a contract which should already be deployed on chain, why would I have to put call the constructor and pass in the arguments there?

1
  • Thanks for asking this question, just got stuck on the same problem. Have you figured out where the endowment, code_hash, salt_bytes and instantiate functions are defined? I can't find any documentation on what ContractRef is. Only link I could find is paritytech.github.io/ink/ink_lang/reflect/… which unfortunately is broken.
    – Jolow
    Sep 29, 2022 at 17:17

1 Answer 1

3

During work on your project, you may have two kinds of cross-contract interactions:

  • When one contract instantiates another contract.
  • When one contract calls the method of another already instantiated contract.

Wrapper from OpenBrush allows you to do the second kind of interaction(you need only knowledge of the trait). In the future, it will be improved, and you will be able to do the first kind, too.

The {}Ref(where {} is the name of the contract) allows you to do both kinds of interactions, but you need to import another contract as a dependency.

You can call the constructor of the {}Ref and pass arguments. During compilation ink! will expand it into a low-level call of host functions. But except for arguments, you also need to specify:

  • endowment - the amount of tokens that you want to transfer. Remember, it requires the constructor to be payable.
  • code_hash - the code hash of the deployed contract.
  • salt - if you want to deploy several contracts during one transaction, each contract should be deployed with some salt. It is needed to make the contract unique. It can be any sequence of bytes(an empty array also works). version in the example is a number that can be incremented each time after deploying a new contract.

OpenBrush use both {}Ref and wrapper in the example. {}Ref is used to instantiate the contracts. Wrapper is used to do cross contract calls

I hope it will answer your question. If not, you can ask in the comment, and I will edit the post.

2
  • Could you maybe supply an example on how to instantiate the contract to be called upon? I am unclear on the syntax. The examples that I found seem to require me for some reason to call on the constructor like ExampleRef::new(). If I have a contract and want to call on another contract in there. How would the syntax for that look like?
    – rajohs
    Sep 14, 2022 at 0:08
  • I think this answer covers my question. I will open a new question, for the detail of the implementation. I have an idea thx to your answer, but smth is still not working correctly.
    – rajohs
    Sep 15, 2022 at 5:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.