Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-call #399

Merged
merged 18 commits into from
Jun 28, 2022
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
27 changes: 27 additions & 0 deletions docs/src/getting-started/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,33 @@ If you already have a deployed contract and want to call its methods using the S
> {{#include ../../../examples/contracts/src/lib.rs:good_practice}}
> ```

## Multiple contract calls in a single transaction

With `ContractMultiCallHandler` you can execute multiple contract calls within a single transaction. To achieve this, you first prepare all the contract calls that you want to bundle:

```rust,ignore
{{#include ../../../examples/contracts/src/lib.rs:multi_call_prepare}}
```
At this point you can also choose to set call parameters, variable outputs or external contracts for every contract call, as long as you don't execute it with `call()` or `simulate()`.
Next, you provide the prepared calls to your `ContractMultiCallHandler` and optionally configure transaction parameters:

```rust,ignore
{{#include ../../../examples/contracts/src/lib.rs:multi_call_build}}
```
Note that any transaction parameters configured on separate contract calls are disregarded in favor of the parameters provided to `ContractMultiCallHandler`.

### Output values
To get the output values of the bundled calls, you need to provide explicit type annotations when saving the result of `call()` or `simulate()` to a variable:

```rust,ignore
{{#include ../../../examples/contracts/src/lib.rs:multi_call_values}}
```
You can also interact with the `CallResponse` by moving the type annotation to the invoked method:

```rust,ignore
{{#include ../../../examples/contracts/src/lib.rs:multi_call_response}}
```

## More examples

You can find runnable examples under [`fuels-abigen-macro/tests/harness.rs`](https://github.com/FuelLabs/fuels-rs/blob/master/packages/fuels-abigen-macro/tests/harness.rs).
48 changes: 48 additions & 0 deletions examples/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,51 @@ async fn call_params_gas() -> Result<(), Error> {
// ANCHOR_END: call_params_gas
Ok(())
}

#[tokio::test]
#[allow(unused_variables)]
async fn multi_call_example() -> Result<(), Error> {
use fuels::prelude::*;

abigen!(
MyContract,
"packages/fuels-abigen-macro/tests/test_projects/contract_test/out/debug/contract_test-abi.json"
);

let wallet = launch_provider_and_get_wallet().await;

let contract_id = Contract::deploy(
"../../packages/fuels-abigen-macro/tests/test_projects/contract_test/out/debug/contract_test.bin",
&wallet,
TxParameters::default(),
)
.await?;

// ANCHOR: multi_call_prepare
let contract_instance = MyContract::new(contract_id.to_string(), wallet.clone());

let call_handler_1 = contract_instance.initialize_counter(42);
let call_handler_2 = contract_instance.get_array([42; 2].to_vec());
// ANCHOR_END: multi_call_prepare

// ANCHOR: multi_call_build
let mut multi_call_handler = MultiContractCallHandler::new(wallet.clone());

multi_call_handler
.add_call(call_handler_1)
.add_call(call_handler_2);
// ANCHOR_END: multi_call_build

// ANCHOR: multi_call_values
let (counter, array): (u64, Vec<u64>) = multi_call_handler.call().await?.value;
// ANCHOR_END: multi_call_values

// ANCHOR: multi_call_response
let response = multi_call_handler.call::<(u64, Vec<u64>)>().await?;
// ANCHOR_END: multi_call_response

assert_eq!(counter, 42);
assert_eq!(array, [42; 2]);

Ok(())
}
81 changes: 78 additions & 3 deletions packages/fuels-abigen-macro/tests/harness.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use fuel_gql_client::fuel_tx::{AssetId, ContractId, Receipt};
use sha2::{Digest, Sha256};
use std::str::FromStr;

use fuels::contract::contract::MultiContractCallHandler;
use fuels::prelude::{
abigen, launch_provider_and_get_wallet, setup_multiple_assets_coins, setup_single_asset_coins,
setup_test_provider, CallParameters, Contract, Error, LocalWallet, Provider, ProviderError,
Expand All @@ -10,6 +8,8 @@ use fuels::prelude::{
use fuels_core::tx::Address;
use fuels_core::Tokenizable;
use fuels_core::{constants::BASE_ASSET_ID, Token};
use sha2::{Digest, Sha256};
use std::str::FromStr;

/// Note: all the tests and examples below require pre-compiled Sway projects.
/// To compile these projects, run `cargo run --bin build-test-projects`.
Expand Down Expand Up @@ -1861,3 +1861,78 @@ async fn nested_enums_are_correctly_encoded_decoded() -> Result<(), Error> {
assert_eq!(response.value, expected_none);
Ok(())
}

#[tokio::test]
async fn test_multi_call() -> Result<(), Error> {
abigen!(
MyContract,
"packages/fuels-abigen-macro/tests/test_projects/contract_test/out/debug/contract_test-abi.json"
);

let wallet = launch_provider_and_get_wallet().await;

let contract_id = Contract::deploy(
"tests/test_projects/contract_test/out/debug/contract_test.bin",
&wallet,
TxParameters::default(),
)
.await?;

let contract_instance = MyContract::new(contract_id.to_string(), wallet.clone());

let call_handler_1 = contract_instance.initialize_counter(42);
let call_handler_2 = contract_instance.get_array([42; 2].to_vec());

let mut multi_call_handler = MultiContractCallHandler::new(wallet.clone());

multi_call_handler
.add_call(call_handler_1)
.add_call(call_handler_2);

let (counter, array): (u64, Vec<u64>) = multi_call_handler.call().await?.value;

assert_eq!(counter, 42);
assert_eq!(array, [42; 2]);

Ok(())
}

#[tokio::test]
async fn test_multi_call_script_workflow() -> Result<(), Error> {
abigen!(
MyContract,
"packages/fuels-abigen-macro/tests/test_projects/contract_test/out/debug/contract_test-abi.json"
);

let wallet = launch_provider_and_get_wallet().await;
let client = &wallet.get_provider()?.client;

let contract_id = Contract::deploy(
"tests/test_projects/contract_test/out/debug/contract_test.bin",
&wallet,
TxParameters::default(),
)
.await?;

let contract_instance = MyContract::new(contract_id.to_string(), wallet.clone());

let call_handler_1 = contract_instance.initialize_counter(42);
let call_handler_2 = contract_instance.get_array([42; 2].to_vec());

let mut multi_call_handler = MultiContractCallHandler::new(wallet.clone());

multi_call_handler
.add_call(call_handler_1)
.add_call(call_handler_2);

let script = multi_call_handler.get_script().await;
let receipts = script.call(client).await.unwrap();
let (counter, array) = multi_call_handler
.get_response::<(u64, Vec<u64>)>(receipts)?
.value;

assert_eq!(counter, 42);
assert_eq!(array, [42; 2]);

Ok(())
}
Loading