2

I've been trying for a few days now to implement cross contract calls but somehow I can't seem to make it right. So here is my code and I hope one of you can point out my mistake to me.

I want to call on a smart contract which is already deployed on chain. Here is an example how that would look like, utilizing the openbrush wrapper.

As far as I understand it, works by importing the trait of the smart contract which is to be called on and then using the AccountId of the smart contract to point to the right instance, deployed on the chain.

Here is an example of how I tried to implement this myself. There are two contracts:

  • Incrementer Contract

    • has one value which is 0 at the start
    • inc() <- method to increase the value by 0.1
    • balance() <- method to check the value
  • Caller Contract

    • has the AccountId of Incrementer
    • call() <- method to call inc()

Code:

Incrementer trait:

#[openbrush::wrapper]
pub type IncRef = dyn Inc;

#[openbrush::trait_definition]
pub trait Inc {

    #[ink(message)]
    fn inc(&mut self);

    #[ink(message)]
    fn balance(&self) -> u128;
}

Incrementer code:

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

#[openbrush::contract]
mod inc {

    use project::traits::inc::*;

    #[ink(storage)]
    pub struct Inc {
        value: u128,
    }

    impl Inc {
        
        #[ink(constructor)]
        pub fn new() -> Self {
            Self { value: 0 }
        }


        #[ink(message)]
        pub fn inc(&mut self) {
            self.value += 1;
        }

        #[ink(message)]
        pub fn balance(&self) -> u128 {
            self.value
        }
    }
}

Caller Code:

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

#[openbrush::contract]
mod caller {
    use project::traits::inc::IncRef;
    
    #[ink(storage)]
    pub struct Caller {
        contract: AccountId
    }

    impl Caller {
        
        #[ink(constructor)]
        pub fn new(
            contract: AccountId
        ) -> Self {
            Self { contract: contract}
        }


        #[ink(message)]
        pub fn call(){
            IncRef::inc(&self.contract);
        }
    }
}

Incrementer Cargo.toml

[package]
name = "inc"
version = "0.1.0"
authors = ["[your_name] <[your_email]>"]
edition = "2021"

[dependencies]
ink_primitives = { version = "3", default-features = false }
ink_metadata = { version = "3", default-features = false, features = ["derive"], optional = true }
ink_env = { version = "3", default-features = false }
ink_storage = { version = "3", default-features = false }
ink_lang = { version = "3", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }

project = { path = "../..", default-features = false }
openbrush = { version="~2.1.0", default-features = false}

[lib]
name = "inc"
path = "lib.rs"
crate-type = [
    "cdylib",
    "rlib"
]

[features]
default = ["std"]
std = [
    "ink_metadata/std",
    "ink_env/std",
    "ink_storage/std",
    "ink_primitives/std",
    "scale/std",
    "scale-info/std",

    "project/std",
    "openbrush/std"
]
ink-as-dependency = []

Caller Cargo.toml

[package]
name = "caller"
version = "0.1.0"
authors = ["[your_name] <[your_email]>"]
edition = "2021"

[lib]
name = "caller"
path = "lib.rs"
crate-type = [
    "cdylib",
]


[dependencies]
ink_primitives = { version = "~3.3.0", default-features = false }
ink_metadata = { version = "~3.3.0", default-features = false, features = ["derive"], optional = true }
ink_env = { version = "~3.3.0", default-features = false }
ink_storage = { version = "~3.3.0", default-features = false }
ink_lang = { version = "~3.3.0", default-features = false }
ink_prelude = { version = "~3.3.0", default-features = false }
ink_engine = { version = "~3.3.0", default-features = false, optional = true }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }


project = { path = "../..", default-features = false }
openbrush = { version="~2.1.0", default-features = false}
inc = { path = "../inc", default-features = false, features = ["ink-as-dependency"] }

[features]
default = ["std"]
std = [
    "ink_primitives/std",
    "ink_metadata",
    "ink_metadata/std",
    "ink_env/std",
    "ink_storage/std",
    "ink_lang/std",
    "scale/std",
    "scale-info",
    "scale-info/std",

    "openbrush/std",
    "inc/std",
    "project/std",
]
ink-as-dependency = []

[profile.dev]
codegen-units = 16

[profile.release]
overflow-checks = false

Since there also seems to be no way to adequatly test smart contracts which utilize cross contract calls I am a bit lost at which could be the mistake. I tried testing this implementation by uploading the contracts to a private node. But the only info I got was the following, when the call() call failed:

9/15/2022, 12:25:17 AM call()

balances:Withdraw

system:ExtrinsicFailed contracts:ContractTrapped

Contract trapped during execution.

1

1 Answer 1

3

I could not get the openbrush apporach get to work by using a wrapper. However I found a low level approach (here) which works just fine for now.

let method_selector = [0xDE, 0xAD, 0xBE, 0xEF];
build_call::<DefaultEnvironment>()
    .call_type(Call::new().callee(self.contract))
    .exec_input(
         ExecutionInput::new(Selector::new(method_selector)))
    .call_flags(CallFlags::default()
    .returns::<()>()
    .fire()
    .unwrap();

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.