From d100c751fbe7a71ef45077369fc9dd709f2a51da Mon Sep 17 00:00:00 2001 From: Daan van der Plas Date: Mon, 17 Apr 2023 18:36:29 +0200 Subject: [PATCH] delegator build_call example --- integration-tests/delegator/.gitignore | 9 + integration-tests/delegator/Cargo.toml | 43 ++ integration-tests/delegator/README.md | 6 + .../delegator/accumulator/.gitignore | 9 + .../delegator/accumulator/Cargo.toml | 33 ++ .../delegator/accumulator/lib.rs | 174 ++++++++ integration-tests/delegator/adder/Cargo.toml | 37 ++ integration-tests/delegator/adder/lib.rs | 119 ++++++ integration-tests/delegator/build-all.sh | 8 + integration-tests/delegator/clean-all.sh | 8 + integration-tests/delegator/lib.rs | 400 ++++++++++++++++++ integration-tests/delegator/subber/Cargo.toml | 37 ++ integration-tests/delegator/subber/lib.rs | 122 ++++++ integration-tests/delegator/test-all.sh | 22 + 14 files changed, 1027 insertions(+) create mode 100755 integration-tests/delegator/.gitignore create mode 100755 integration-tests/delegator/Cargo.toml create mode 100644 integration-tests/delegator/README.md create mode 100755 integration-tests/delegator/accumulator/.gitignore create mode 100755 integration-tests/delegator/accumulator/Cargo.toml create mode 100755 integration-tests/delegator/accumulator/lib.rs create mode 100644 integration-tests/delegator/adder/Cargo.toml create mode 100644 integration-tests/delegator/adder/lib.rs create mode 100755 integration-tests/delegator/build-all.sh create mode 100755 integration-tests/delegator/clean-all.sh create mode 100755 integration-tests/delegator/lib.rs create mode 100644 integration-tests/delegator/subber/Cargo.toml create mode 100644 integration-tests/delegator/subber/lib.rs create mode 100755 integration-tests/delegator/test-all.sh diff --git a/integration-tests/delegator/.gitignore b/integration-tests/delegator/.gitignore new file mode 100755 index 0000000000..8de8f877e4 --- /dev/null +++ b/integration-tests/delegator/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/integration-tests/delegator/Cargo.toml b/integration-tests/delegator/Cargo.toml new file mode 100755 index 0000000000..93ce2c5df0 --- /dev/null +++ b/integration-tests/delegator/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "delegator" +version = "4.0.1" +authors = ["[your_name] <[your_email]>"] +edition = "2021" +publish = false + +[dependencies] +ink = { version = "4.0.1", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } + +# Contracts that will be used for cross contract calls. +accumulator = { path = "accumulator", default-features = false, features = ["ink-as-dependency"] } +adder = { path = "adder", default-features = false, features = ["ink-as-dependency"] } +subber = { path = "subber", default-features = false, features = ["ink-as-dependency"] } + +[dev-dependencies] +ink_e2e = { version = "4.0.1" } + +[lib] +name = "delegator" +path = "lib.rs" +crate-type = ["cdylib"] +# crate-type = [ +# # Used for normal contract Wasm blobs. +# "cdylib", +# # Used for ABI generation. +# "rlib", +# ] + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + "accumulator/std", + "adder/std", + "subber/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/integration-tests/delegator/README.md b/integration-tests/delegator/README.md new file mode 100644 index 0000000000..1836eacb1d --- /dev/null +++ b/integration-tests/delegator/README.md @@ -0,0 +1,6 @@ +# `Delegator` Smart Contract + +The `Delegator` smart contract is our showcase for executing other smart +contracts on-chain. The key difference with the other `multi_contract_caller` contract is now the +"called" contracts are already instantiated, in stead of instantiating the contracts at the same +time as the `Delegator` contract. diff --git a/integration-tests/delegator/accumulator/.gitignore b/integration-tests/delegator/accumulator/.gitignore new file mode 100755 index 0000000000..8de8f877e4 --- /dev/null +++ b/integration-tests/delegator/accumulator/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/integration-tests/delegator/accumulator/Cargo.toml b/integration-tests/delegator/accumulator/Cargo.toml new file mode 100755 index 0000000000..1e739e7cef --- /dev/null +++ b/integration-tests/delegator/accumulator/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "accumulator" +version = "4.0.1" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +ink = { version = "4.0.1", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } + +[dev-dependencies] +ink_e2e = "4.0.1" + +[lib] +name = "accumulator" +path = "lib.rs" +crate-type = [ + # Used for normal contract Wasm blobs. + "cdylib", + # Used for ABI generation. + "rlib", +] + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/integration-tests/delegator/accumulator/lib.rs b/integration-tests/delegator/accumulator/lib.rs new file mode 100755 index 0000000000..d2b32e7ebd --- /dev/null +++ b/integration-tests/delegator/accumulator/lib.rs @@ -0,0 +1,174 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use self::accumulator::{Accumulator, AccumulatorRef}; + +#[ink::contract] +pub mod accumulator { + /// Holds a simple `i32` value that can be incremented and decremented. + #[ink(storage)] + pub struct Accumulator { + value: i32, + } + + impl Accumulator { + /// Initializes the value to the initial value. + #[ink(constructor)] + pub fn new(init_value: i32) -> Self { + Self { value: init_value } + } + + /// Mutates the internal value. + #[ink(message)] + pub fn inc(&mut self, by: i32) { + // Debug message to check whether the contract gets called by the + // `adder` or `subber` contract. + let caller = self.env().caller(); + let message = ink::prelude::format!("got a call from {:?}", caller); + ink::env::debug_println!("{}", &message); + + self.value += by; + } + + /// Returns the current state. + #[ink(message)] + pub fn get(&self) -> i32 { + self.value + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + fn get() { + let accumulator = Accumulator::new(10); + assert_eq!(accumulator.value, 10); + assert_eq!(accumulator.value, accumulator.get()); + } + + #[ink::test] + fn increase() { + let value = 10; + let mut accumulator = Accumulator::new(value); + assert_eq!(accumulator.value, accumulator.get()); + let increase = 10; + accumulator.inc(increase); + assert_eq!(accumulator.value, value + increase); + assert_eq!(accumulator.value, accumulator.get()); + } + + #[ink::test] + fn decrease() { + let value = 10; + let mut accumulator = Accumulator::new(value); + assert_eq!(accumulator.value, accumulator.get()); + let decrease = -10; + accumulator.inc(decrease); + assert_eq!(accumulator.value, value + decrease); + assert_eq!(accumulator.value, accumulator.get()); + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn get(mut client: ink_e2e::Client) -> E2EResult<()> { + let init_value = 10; + let constructor = AccumulatorRef::new(init_value); + let contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), constructor, 0, None) + .await + .expect("instantiation failed") + .account_id; + + // Build `get` message and execute + let get_message = ink_e2e::build_message::(contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + Ok(()) + } + + #[ink_e2e::test] + async fn increase(mut client: ink_e2e::Client) -> E2EResult<()> { + let init_value = 10; + let constructor = AccumulatorRef::new(init_value); + let contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), constructor, 0, None) + .await + .expect("instantiation failed") + .account_id; + + // Build `get` message and execute + let get_message = ink_e2e::build_message::(contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + + // Build `increase` message and execute + let increase = 10; + let increase_message = + ink_e2e::build_message::(contract_account_id.clone()) + .call(|accumulator| accumulator.inc(increase)); + let increase_result = client + .call(&ink_e2e::alice(), increase_message, 0, None) + .await; + assert!(increase_result.is_ok()); + + // Build `get` message and execute + let get_message = ink_e2e::build_message::(contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value + increase); + Ok(()) + } + + #[ink_e2e::test] + async fn decrease(mut client: ink_e2e::Client) -> E2EResult<()> { + let init_value = 10; + let constructor = AccumulatorRef::new(init_value); + let contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), constructor, 0, None) + .await + .expect("instantiation failed") + .account_id; + + // Build `get` message and execute + let get_message = ink_e2e::build_message::(contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + + // Build `increase` message and execute + let decrease = -10; + let increase_message = + ink_e2e::build_message::(contract_account_id.clone()) + .call(|accumulator| accumulator.inc(decrease)); + let increase_result = client + .call(&ink_e2e::alice(), increase_message, 0, None) + .await; + assert!(increase_result.is_ok()); + + // Build `get` message and execute + let get_message = ink_e2e::build_message::(contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value + decrease); + Ok(()) + } + } +} diff --git a/integration-tests/delegator/adder/Cargo.toml b/integration-tests/delegator/adder/Cargo.toml new file mode 100644 index 0000000000..6ae543b9e7 --- /dev/null +++ b/integration-tests/delegator/adder/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "adder" +version = "4.0.1" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +ink = { version = "4.0.1", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } + +# Contract that will be used for cross contract calls. +accumulator = { path = "../accumulator", default-features = false, features = ["ink-as-dependency"] } + +[dev-dependencies] +ink_e2e = "4.0.1" + +[lib] +name = "adder" +path = "lib.rs" +crate-type = [ + # Used for normal contract Wasm blobs. + "cdylib", + # Used for ABI generation. + "rlib", +] + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + "accumulator/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/integration-tests/delegator/adder/lib.rs b/integration-tests/delegator/adder/lib.rs new file mode 100644 index 0000000000..5f46e91d7f --- /dev/null +++ b/integration-tests/delegator/adder/lib.rs @@ -0,0 +1,119 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use self::adder::{Adder, AdderRef}; + +#[ink::contract] +mod adder { + use ink::env::{ + call::{build_call, ExecutionInput, Selector}, + CallFlags, DefaultEnvironment, + }; + + /// Increments the underlying `accumulator` value. + #[ink(storage)] + pub struct Adder { + /// The `accumulator` contract. + acc_contract: AccountId, + } + + impl Adder { + /// Creates a new `adder` from the given `accumulator`. + #[ink(constructor)] + pub fn new(acc_contract: AccountId) -> Self { + Self { acc_contract } + } + + /// Increases the `accumulator` value by some amount. + #[ink(message)] + pub fn inc(&mut self, by: i32) { + // Debug message to check whether the contract gets called by the delegator + let caller = self.env().caller(); + let message = ink::prelude::format!("got a call from {:?}", caller); + ink::env::debug_println!("{}", &message); + + let _result = build_call::() + .call(self.acc_contract) + .call_flags(CallFlags::default().set_tail_call(true)) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("inc"))) + .push_arg(by), + ) + .returns::<()>() + .try_invoke(); + unreachable!("set_tail_call = true"); + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use accumulator::AccumulatorRef; + type E2EResult = std::result::Result>; + + #[ink_e2e::test(additional_contracts = "../accumulator/Cargo.toml")] + async fn accumulator(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + Ok(()) + } + + #[ink_e2e::test] + async fn increase(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + + // Instantiate `adder` contract + let adder_constructor = AdderRef::new(acc_contract_account_id); + let adder_contract_account_id = client + .instantiate("adder", &ink_e2e::alice(), adder_constructor, 0, None) + .await + .expect("adder contract instantiation failed") + .account_id; + + // Build `increase` message of `adder` contract and execute + let increase = 10; + let inc_message = + ink_e2e::build_message::(adder_contract_account_id.clone()) + .call(|adder| adder.inc(increase)); + let inc_result = client.call(&ink_e2e::alice(), inc_message, 0, None).await; + assert!(inc_result.is_ok()); + + // Execute `get` message of `accumulator` contract + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value + increase); + Ok(()) + } + } +} diff --git a/integration-tests/delegator/build-all.sh b/integration-tests/delegator/build-all.sh new file mode 100755 index 0000000000..c6de737b5f --- /dev/null +++ b/integration-tests/delegator/build-all.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -eu + +cargo contract build --manifest-path accumulator/Cargo.toml +cargo contract build --manifest-path adder/Cargo.toml +cargo contract build --manifest-path subber/Cargo.toml +cargo contract build diff --git a/integration-tests/delegator/clean-all.sh b/integration-tests/delegator/clean-all.sh new file mode 100755 index 0000000000..140b2c348f --- /dev/null +++ b/integration-tests/delegator/clean-all.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -eu + +cargo clean --manifest-path accumulator/Cargo.toml +cargo clean --manifest-path adder/Cargo.toml +cargo clean --manifest-path subber/Cargo.toml +cargo clean diff --git a/integration-tests/delegator/lib.rs b/integration-tests/delegator/lib.rs new file mode 100755 index 0000000000..b7f5c6b0ef --- /dev/null +++ b/integration-tests/delegator/lib.rs @@ -0,0 +1,400 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[ink::contract] +mod delegator { + use ink::env::Result as EnvResult; + use ink::env::{ + call::{build_call, ExecutionInput, Selector}, + CallFlags, DefaultEnvironment, + }; + use ink::MessageResult; + + // Whether the contract calls the `adder` or `subber` contract. + #[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Decode, scale::Encode)] + #[cfg_attr( + feature = "std", + derive(ink::storage::traits::StorageLayout, scale_info::TypeInfo) + )] + pub enum Which { + Adder, + Subber, + } + + #[ink(storage)] + pub struct Delegator { + /// Says which of `adder` or `subber` is currently in use. + which: Which, + /// The `accumulator` contract. + acc_contract: AccountId, + /// The `adder` contract. + add_contract: AccountId, + /// The `subber` contract. + sub_contract: AccountId, + } + + impl Delegator { + #[ink(constructor)] + pub fn new( + acc_contract: AccountId, + add_contract: AccountId, + sub_contract: AccountId, + ) -> Self { + Delegator { + which: Which::Adder, + acc_contract, + add_contract, + sub_contract, + } + } + + #[ink(message)] + pub fn get(&self) -> i32 { + let result = build_call::() + .call(self.acc_contract) + .gas_limit(0) + .transferred_value(0) + .call_flags(CallFlags::default()) + .exec_input(ExecutionInput::new(Selector::new(ink::selector_bytes!( + "get" + )))) + .returns::() + .try_invoke(); + + match result { + EnvResult::Ok(MessageResult::Ok(result)) => result, + _ => unimplemented!(), + } + } + + #[ink(message)] + pub fn change(&self, by: i32) { + let (contract, method_selector) = match self.which { + Which::Adder => ( + self.add_contract, + Selector::new(ink::selector_bytes!("inc")), + ), + Which::Subber => ( + self.sub_contract, + Selector::new(ink::selector_bytes!("dec")), + ), + }; + let _result = build_call::() + .call(contract) + .call_flags(CallFlags::default().set_tail_call(true)) + .exec_input(ExecutionInput::new(method_selector).push_arg(by)) + .returns::<()>() + .try_invoke(); + unreachable!("set_tail_call = true"); + } + + #[ink(message)] + pub fn switch(&mut self) { + match self.which { + Which::Adder => { + self.which = Which::Subber; + } + Which::Subber => { + self.which = Which::Adder; + } + } + } + } + + #[cfg(test)] + mod test { + use super::*; + + #[ink::test] + fn new() { + let delegator = Delegator::new([0x06; 32].into(), [0x07; 32].into(), [0x08; 32].into()); + assert_eq!(delegator.acc_contract, [0x06; 32].into()); + assert_eq!(delegator.add_contract, [0x07; 32].into()); + assert_eq!(delegator.sub_contract, [0x08; 32].into()); + assert_eq!(delegator.which, Which::Adder); + } + + #[ink::test] + fn switch() { + let mut delegator = + Delegator::new([0x06; 32].into(), [0x07; 32].into(), [0x08; 32].into()); + assert_eq!(delegator.which, Which::Adder); + delegator.switch(); + assert_eq!(delegator.which, Which::Subber); + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use accumulator::AccumulatorRef; + use adder::AdderRef; + use subber::SubberRef; + type E2EResult = std::result::Result>; + + #[ink_e2e::test( + additional_contracts = "accumulator/Cargo.toml adder/Cargo.toml subber/Cargo.toml" + )] + async fn instantiate_other(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + + // Instantiate `subber` contract + let subber_constructor = SubberRef::new(acc_contract_account_id); + let _subber_contract_account_id = client + .instantiate("subber", &ink_e2e::alice(), subber_constructor, 0, None) + .await + .expect("subber contract instantiation failed") + .account_id; + + // Instantiate `adder` contract + let adder_constructor = AdderRef::new(acc_contract_account_id); + let _adder_contract_account_id = client + .instantiate("adder", &ink_e2e::alice(), adder_constructor, 0, None) + .await + .expect("adder contract instantiation failed"); + assert_eq!(get_result.return_value(), init_value); + Ok(()) + } + + #[ink_e2e::test] + async fn increase(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + + // Instantiate `adder` contract + let adder_constructor = AdderRef::new(acc_contract_account_id); + let adder_contract_account_id = client + .instantiate("adder", &ink_e2e::alice(), adder_constructor, 0, None) + .await + .expect("adder contract instantiation failed") + .account_id; + + // Build `increase` message of `adder` contract and execute + let increase = 10; + let inc_message = ink_e2e::build_message::(adder_contract_account_id.clone()) + .call(|adder| adder.inc(increase)); + let inc_result = client.call(&ink_e2e::alice(), inc_message, 0, None).await; + assert!(inc_result.is_ok()); + + // Execute `get` message of `accumulator` contract + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value + increase); + Ok(()) + } + + #[ink_e2e::test] + async fn decrease(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + + // Instantiate `subber` contract + let subber_constructor = SubberRef::new(acc_contract_account_id); + let subber_contract_account_id = client + .instantiate("subber", &ink_e2e::alice(), subber_constructor, 0, None) + .await + .expect("subber contract instantiation failed") + .account_id; + + // Build `decrease` message of `subber` contract and execute + let decrease = 10; + let dec_message = + ink_e2e::build_message::(subber_contract_account_id.clone()) + .call(|subber| subber.dec(decrease)); + let dec_result = client.call(&ink_e2e::alice(), dec_message, 0, None).await; + assert!(dec_result.is_ok()); + + // Execute `get` message of `accumulator` contract + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value - decrease); + Ok(()) + } + + #[ink_e2e::test] + async fn instantiate_delegator(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Instantiate `adder` contract + let adder_constructor = AdderRef::new(acc_contract_account_id); + let adder_contract_account_id = client + .instantiate("adder", &ink_e2e::alice(), adder_constructor, 0, None) + .await + .expect("adder contract instantiation failed") + .account_id; + + // Instantiate `subber` contract + let subber_constructor = SubberRef::new(acc_contract_account_id); + let subber_contract_account_id = client + .instantiate("subber", &ink_e2e::alice(), subber_constructor, 0, None) + .await + .expect("subber contract instantiation failed") + .account_id; + + // Instantiate `delegator` contract + let del_constructor = DelegatorRef::new( + acc_contract_account_id, + adder_contract_account_id, + subber_contract_account_id, + ); + let _del_contract_account_id = client + .instantiate("delegator", &ink_e2e::alice(), del_constructor, 0, None) + .await + .expect("delegator contract instantiation failed") + .account_id; + Ok(()) + } + + #[ink_e2e::test] + async fn delegate(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Instantiate `adder` contract + let adder_constructor = AdderRef::new(acc_contract_account_id); + let adder_contract_account_id = client + .instantiate("adder", &ink_e2e::alice(), adder_constructor, 0, None) + .await + .expect("adder contract instantiation failed") + .account_id; + + // Instantiate `subber` contract + let subber_constructor = SubberRef::new(acc_contract_account_id); + let subber_contract_account_id = client + .instantiate("subber", &ink_e2e::alice(), subber_constructor, 0, None) + .await + .expect("subber contract instantiation failed") + .account_id; + + // Instantiate `delegator` contract + let del_constructor = DelegatorRef::new( + acc_contract_account_id, + adder_contract_account_id, + subber_contract_account_id, + ); + let del_contract_account_id = client + .instantiate("delegator", &ink_e2e::alice(), del_constructor, 0, None) + .await + .expect("delegator contract instantiation failed") + .account_id; + + // Build `change` message of `delegator` contract and execute + // (Add 10) + let change = 10; + let change_message = + ink_e2e::build_message::(del_contract_account_id.clone()) + .call(|delegator| delegator.change(change)); + let change_result = client + .call(&ink_e2e::alice(), change_message, 0, None) + .await; + assert!(change_result.is_ok()); + + // Build `get` message of `delegator` contract and execute + let get_message = + ink_e2e::build_message::(del_contract_account_id.clone()) + .call(|delegator| delegator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value + change); + + // Build `switch` message of `delegator` contract and execute + let switch_message = + ink_e2e::build_message::(del_contract_account_id.clone()) + .call(|delegator| delegator.switch()); + let switch_result = client + .call(&ink_e2e::alice(), switch_message, 0, None) + .await; + assert!(switch_result.is_ok()); + + // Build `change` message of `delegator` contract and execute + // (Substract 20) + // + // value = 20 (init_value + 10, from previous `change` message) + let value = 20; + let change = 20; + let change_message = + ink_e2e::build_message::(del_contract_account_id.clone()) + .call(|delegator| delegator.change(change)); + let change_result = client + .call(&ink_e2e::alice(), change_message, 0, None) + .await; + assert!(change_result.is_ok()); + + // Build `get` message of `delegator` contract and execute + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), value - change); + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), value - change); + Ok(()) + } + } +} diff --git a/integration-tests/delegator/subber/Cargo.toml b/integration-tests/delegator/subber/Cargo.toml new file mode 100644 index 0000000000..1a43fdd14e --- /dev/null +++ b/integration-tests/delegator/subber/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "subber" +version = "4.0.1" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +ink = { version = "4.0.1", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } + +# Contract that will be used for cross contract calls. +accumulator = { path = "../accumulator", default-features = false, features = ["ink-as-dependency"] } + +[dev-dependencies] +ink_e2e = "4.0.1" + +[lib] +name = "subber" +path = "lib.rs" +crate-type = [ + # Used for normal contract Wasm blobs. + "cdylib", + # Used for ABI generation. + "rlib", +] + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + "accumulator/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/integration-tests/delegator/subber/lib.rs b/integration-tests/delegator/subber/lib.rs new file mode 100644 index 0000000000..a0acb6eaa4 --- /dev/null +++ b/integration-tests/delegator/subber/lib.rs @@ -0,0 +1,122 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use self::subber::{Subber, SubberRef}; + +#[ink::contract] +mod subber { + use ink::env::{ + call::{build_call, ExecutionInput, Selector}, + CallFlags, DefaultEnvironment, + }; + + /// Decreases the underlying `accumulator` value. + #[ink(storage)] + pub struct Subber { + /// The `accumulator` contract. + sub_contract: AccountId, + } + + impl Subber { + /// Creates a new `subber` from the given `accumulator`. + #[ink(constructor)] + pub fn new(sub_contract: AccountId) -> Self { + Self { sub_contract } + } + + /// Decreases the `accumulator` value by some amount. + #[ink(message)] + pub fn dec(&mut self, by: i32) { + // Debug message to check whether the contract gets called by the delegator + let caller = self.env().caller(); + let message = ink::prelude::format!("got a call from {:?}", caller); + ink::env::debug_println!("{}", &message); + + let _result = build_call::() + .call(self.sub_contract) + .call_flags( + CallFlags::default() + .set_tail_call(true) + .set_allow_reentry(true), + ) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("inc"))).push_arg(-by), + ) + .returns::<()>() + .try_invoke(); + unreachable!("set_tail_call = true"); + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use accumulator::AccumulatorRef; + type E2EResult = std::result::Result>; + + #[ink_e2e::test(additional_contracts = "../accumulator/Cargo.toml")] + async fn accumulator(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + Ok(()) + } + + #[ink_e2e::test] + async fn decrease(mut client: ink_e2e::Client) -> E2EResult<()> { + // Instantiate `accumulator` contract + let init_value = 10; + let acc_constructor = AccumulatorRef::new(init_value); + let acc_contract_account_id = client + .instantiate("accumulator", &ink_e2e::alice(), acc_constructor, 0, None) + .await + .expect("accumulator contract instantiation failed") + .account_id; + + // Build `get` message of `accumulator` contract and execute + let get_message = + ink_e2e::build_message::(acc_contract_account_id.clone()) + .call(|accumulator| accumulator.get()); + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value); + + // Instantiate `subber` contract + let subber_constructor = SubberRef::new(acc_contract_account_id); + let subber_contract_account_id = client + .instantiate("subber", &ink_e2e::alice(), subber_constructor, 0, None) + .await + .expect("subber contract instantiation failed") + .account_id; + + // Build `decrease` message of `subber` contract and execute + let decrease = 10; + let dec_message = + ink_e2e::build_message::(subber_contract_account_id.clone()) + .call(|subber| subber.dec(decrease)); + let dec_result = client.call(&ink_e2e::alice(), dec_message, 0, None).await; + assert!(dec_result.is_ok()); + + // Execute `get` message of `accumulator` contract + let get_result = client + .call_dry_run(&ink_e2e::alice(), &get_message, 0, None) + .await; + assert_eq!(get_result.return_value(), init_value - decrease); + Ok(()) + } + } +} diff --git a/integration-tests/delegator/test-all.sh b/integration-tests/delegator/test-all.sh new file mode 100755 index 0000000000..309a394452 --- /dev/null +++ b/integration-tests/delegator/test-all.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +./build-all.sh + +set -x + +ACCUMULATOR=$(cargo contract instantiate --manifest-path=accumulator/Cargo.toml --suri //Alice --skip-confirm --args 10 --output-json | jq -r .contract) +ADDER=$(cargo contract instantiate --manifest-path=adder/Cargo.toml --suri //Alice --skip-confirm --args $ACCUMULATOR --output-json | jq -r .contract) +SUBBER=$(cargo contract instantiate --manifest-path=subber/Cargo.toml --suri //Alice --skip-confirm --args $ACCUMULATOR --output-json | jq -r .contract) +DELEGATOR=$(cargo contract instantiate --manifest-path=./Cargo.toml --suri //Alice --skip-confirm --args $ACCUMULATOR $ADDER $SUBBER --output-json | jq -r .contract) + +# Calling `delegator` contract and adding 100 +cargo contract call --contract $DELEGATOR --message change --args 100 --suri //Alice --skip-confirm +cargo contract call --contract $DELEGATOR --message get --dry-run --suri //Alice --skip-confirm + +# Calling `delegator` contract and substracting 10 +cargo contract call --contract $DELEGATOR --message switch --suri //Alice --skip-confirm +cargo contract call --contract $DELEGATOR --message change --args 10 --suri //Alice --skip-confirm +cargo contract call --contract $DELEGATOR --message get --dry-run --suri //Alice --skip-confirm + +# Calling `accumulator` contract `get` message to compare with `delegator` contract `get` message +cargo contract call --manifest-path=accumulator/Cargo.toml --contract $ACCUMULATOR --message get --dry-run --suri //Alice --skip-confirm