Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cross-contract-calls-advanced/.gitignore
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions cross-contract-calls-advanced/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "cross-contract-calls"
version = "6.0.0-beta.1"
authors = ["Use Ink <ink@use.ink>"]
edition = "2024"
publish = false

[dependencies]
ink = { version = "6.0.0-beta.1", default-features = false }

# Note: We **need** to specify the `ink-as-dependency` feature.
#
# If we don't we will end up with linking errors!
other-contract = { path = "other-contract", default-features = false, features = ["ink-as-dependency"] }
pallet-revive-uapi = { version = "0.8.0", default-features = false }

[dev-dependencies]
ink_e2e = { version = "6.0.0-beta.1" }

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
# Note: The metadata generation step requires `std`. If we don't specify this the metadata
# generation for our contract will fail!
"other-contract/std",
]
ink-as-dependency = []
e2e-tests = []

[package.metadata.ink-lang]
abi = "ink"
143 changes: 143 additions & 0 deletions cross-contract-calls-advanced/e2e_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use super::cross_contract_calls::*;
use ink_e2e::ContractsBackend;

type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[ink_e2e::test]
async fn instantiate_with_insufficient_storage_deposit_limit<Client: E2EBackend>(
mut client: Client,
) -> E2EResult<()> {
// given
let other_contract_code = client
.upload("other-contract", &ink_e2e::alice())
.submit()
.await
.expect("other_contract upload failed");

const REF_TIME_LIMIT: u64 = 500;
const PROOF_SIZE_LIMIT: u64 = 100_000_000_000;
let storage_deposit_limit = ink::U256::from(100_000_000_000_000u64);

let mut constructor = CrossContractCallsRef::new_with_limits(
other_contract_code.code_hash,
REF_TIME_LIMIT,
PROOF_SIZE_LIMIT,
storage_deposit_limit,
);
let call_result = client
.instantiate("cross-contract-calls", &ink_e2e::alice(), &mut constructor)
.dry_run()
.await?;

assert!(call_result.did_revert());
let err_msg = String::from_utf8_lossy(call_result.return_data());
assert!(err_msg.contains(
"Cross-contract instantiation failed with ReturnError(OutOfResources)"
));

Ok(())
}

#[ink_e2e::test]
async fn instantiate_with_sufficient_limits<Client: E2EBackend>(
mut client: Client,
) -> E2EResult<()> {
// given
let other_contract_code = client
.upload("other-contract", &ink_e2e::alice())
.submit()
.await
.expect("other_contract upload failed");

const REF_TIME_LIMIT: u64 = 500_000_000_000_000;
const PROOF_SIZE_LIMIT: u64 = 100_000_000_000;
// todo remove the last group of `000` to get an `OutOfGas` error in
// `pallet-revive`. but they should throw an error about `StorageLimitExhausted`.
let storage_deposit_limit = ink::U256::from(100_000_000_000_000u64);

let mut constructor = CrossContractCallsRef::new_with_limits(
other_contract_code.code_hash,
REF_TIME_LIMIT,
PROOF_SIZE_LIMIT,
storage_deposit_limit,
);
let contract = client
.instantiate("cross-contract-calls", &ink_e2e::alice(), &mut constructor)
.submit()
.await;

assert!(contract.is_ok(), "{}", contract.err().unwrap());

Ok(())
}

#[ink_e2e::test]
async fn instantiate_no_limits<Client: E2EBackend>(mut client: Client) -> E2EResult<()> {
// given
let other_contract_code = client
.upload("other-contract", &ink_e2e::alice())
.submit()
.await
.expect("other_contract upload failed");

let mut constructor =
CrossContractCallsRef::new_no_limits(other_contract_code.code_hash);
let contract = client
.instantiate("cross-contract-calls", &ink_e2e::alice(), &mut constructor)
.submit()
.await;

assert!(contract.is_ok(), "{}", contract.err().unwrap());

Ok(())
}

#[ink_e2e::test]
async fn flip_and_get<Client: E2EBackend>(mut client: Client) -> E2EResult<()> {
// given
let other_contract_code = client
.upload("other-contract", &ink_e2e::alice())
.submit()
.await
.expect("other_contract upload failed");

let mut constructor =
CrossContractCallsRef::new_no_limits(other_contract_code.code_hash);
let contract = client
.instantiate("cross-contract-calls", &ink_e2e::alice(), &mut constructor)
.submit()
.await
.expect("cross-contract-calls instantiate failed");
let mut call_builder = contract.call_builder::<CrossContractCalls>();

const REF_TIME_LIMIT: u64 = 500_000_000;
const PROOF_SIZE_LIMIT: u64 = 100_000;
let storage_deposit_limit = ink::U256::from(1_000_000_000);

// when
let call = call_builder.flip_and_get_invoke_with_limits(
REF_TIME_LIMIT,
PROOF_SIZE_LIMIT,
storage_deposit_limit,
);
let result = client
.call(&ink_e2e::alice(), &call)
.submit()
.await
.expect("Calling `flip_and_get_invoke_with_limits` failed")
.return_value();

assert!(!result);

let call = call_builder.flip_and_get_invoke_no_weight_limit();
let result = client
.call(&ink_e2e::alice(), &call)
.submit()
.await
.expect("Calling `flip_and_get_invoke_no_weight_limit` failed")
.return_value();

assert!(result);

Ok(())
}
90 changes: 90 additions & 0 deletions cross-contract-calls-advanced/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
mod cross_contract_calls {
use ink::codegen::TraitCallBuilder;
use other_contract::OtherContractRef;

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

impl CrossContractCalls {
/// Initializes the contract by instantiating the code at the given code hash via
/// `instantiate` host function with the supplied weight and storage
/// limits.
#[ink(constructor)]
pub fn new_with_limits(
other_contract_code_hash: ink::H256,
ref_time_limit: u64,
proof_size_limit: u64,
storage_deposit_limit: ink::U256,
) -> Self {
let other_contract = OtherContractRef::new(true)
.code_hash(other_contract_code_hash)
.endowment(0.into())
.salt_bytes(Some([1u8; 32]))
.ref_time_limit(ref_time_limit)
.proof_size_limit(proof_size_limit)
.storage_deposit_limit(storage_deposit_limit)
.instantiate();

Self { other_contract }
}

/// Initializes the contract by instantiating the code at the given code hash via
/// the `instantiate` host function with no weight or storage limits.
#[ink(constructor)]
pub fn new_no_limits(other_contract_code_hash: ink::H256) -> Self {
let other_contract = OtherContractRef::new(true)
.code_hash(other_contract_code_hash)
.endowment(0.into())
.salt_bytes(Some([1u8; 32]))
.instantiate();

Self { other_contract }
}

/// Use the `call` host function via the call builder to forward calls to
/// the other contract, initially calling `flip` and then `get` to return the
/// result.
///
/// This demonstrates how to set the new weight and storage limit parameters via
/// the call builder API.
#[ink(message)]
pub fn flip_and_get_invoke_with_limits(
&mut self,
ref_time_limit: u64,
proof_size_limit: u64,
storage_deposit_limit: ink::U256,
) -> bool {
let call_builder = self.other_contract.call_mut();

call_builder
.flip()
.ref_time_limit(ref_time_limit)
.proof_size_limit(proof_size_limit)
.storage_deposit_limit(storage_deposit_limit)
.invoke();

call_builder
.get()
.ref_time_limit(ref_time_limit)
.proof_size_limit(proof_size_limit)
.storage_deposit_limit(storage_deposit_limit)
.invoke()
}

/// Demonstrate that the `call` succeeds without having specified the weight
/// and storage limit parameters
#[ink(message)]
pub fn flip_and_get_invoke_no_weight_limit(&mut self) -> bool {
self.other_contract.flip();
self.other_contract.get()
}
}
}

#[cfg(all(test, feature = "e2e-tests"))]
mod e2e_tests;
27 changes: 27 additions & 0 deletions cross-contract-calls-advanced/other-contract/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "other-contract"
version = "6.0.0-beta.1"
authors = ["Use Ink <ink@use.ink>"]
edition = "2024"
publish = false

[dependencies]
ink = { version = "6.0.0-beta.1", default-features = false }
pallet-revive-uapi = { version = "0.8.0", default-features = false }

[dev-dependencies]
ink_e2e = { version = "6.0.0-beta.1" }

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
]
ink-as-dependency = []
e2e-tests = []

[package.metadata.ink-lang]
abi = "ink"
27 changes: 27 additions & 0 deletions cross-contract-calls-advanced/other-contract/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
mod other_contract {

#[ink(storage)]
pub struct OtherContract {
value: bool,
}

impl OtherContract {
#[ink(constructor)]
pub fn new(init_value: bool) -> Self {
Self { value: init_value }
}

#[ink(message)]
pub fn flip(&mut self) {
self.value = !self.value;
}

#[ink(message)]
pub fn get(&self) -> bool {
self.value
}
}
}
Loading
Loading