Skip to content

Commit

Permalink
Add low level call validation test
Browse files Browse the repository at this point in the history
  • Loading branch information
IGI-111 committed Aug 2, 2023
1 parent c981087 commit d907a68
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 3 deletions.
2 changes: 1 addition & 1 deletion test/src/sdk-harness/Forc.lock
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ source = 'member'
dependencies = ['std']

[[package]]
name = 'test_script'
name = 'test_script_bytes'
source = 'member'
dependencies = ['std']

Expand Down
1 change: 1 addition & 0 deletions test/src/sdk-harness/test_projects/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod generics_in_abi;
mod hashing;
mod logging;
mod low_level_call;
mod low_level_call_bytes;
mod messages;
mod methods;
mod option_field_order;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ source = 'path+from-root-C9E1B0494AB5FDF1'
dependencies = ['core']

[[package]]
name = 'test_script'
name = 'test_script_bytes'
source = 'member'
dependencies = ['std']
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "test_script"
name = "test_script_bytes"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
13 changes: 13 additions & 0 deletions test/src/sdk-harness/test_projects/low_level_call_bytes/Forc.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = 'core'
source = 'path+from-root-C9E1B0494AB5FDF1'

[[package]]
name = 'std'
source = 'path+from-root-C9E1B0494AB5FDF1'
dependencies = ['core']

[[package]]
name = 'test_script'
source = 'member'
dependencies = ['std']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "test_script"

[dependencies]
std = { path = "../../../../../sway-lib-std" }
182 changes: 182 additions & 0 deletions test/src/sdk-harness/test_projects/low_level_call_bytes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
use fuel_vm::fuel_tx::{Bytes32, ContractId, Output, TxPointer, UtxoId};
use fuels::{
accounts::wallet::WalletUnlocked,
prelude::*,
types::{input::Input, Bits256, SizedAsciiString},
};

macro_rules! fn_selector {
( $fn_name: ident ( $($fn_arg: ty),* ) ) => {
::fuels::core::codec::resolve_fn_selector(stringify!($fn_name), &[$( <$fn_arg as ::fuels::core::traits::Parameterize>::param_type() ),*]).to_vec()
}
}
macro_rules! calldata {
( $($arg: expr),* ) => {
::fuels::core::codec::ABIEncoder::encode(&[$(::fuels::core::traits::Tokenizable::into_token($arg)),*]).unwrap().resolve(0)
}
}

// Load abi from json
abigen!(
Contract(
name = "TestContract",
abi = "test_artifacts/low_level_callee_contract/out/debug/test_contract-abi.json"
),
Script(
name = "TestScript",
abi = "test_projects/low_level_call_bytes/out/debug/test_script-abi.json"
)
);

async fn low_level_call(
id: ContractId,
wallet: WalletUnlocked,
function_selector: Vec<u8>,
calldata: Vec<u8>,
single_value_type_arg: bool,
) {
// Build the script instance
let script_instance = TestScript::new(
wallet,
"test_projects/low_level_call_bytes/out/debug/test_script.bin",
);

// Add the contract being called to the inputs and outputs
let contract_input = Input::Contract {
utxo_id: UtxoId::new(Bytes32::zeroed(), 0),
balance_root: Bytes32::zeroed(),
state_root: Bytes32::zeroed(),
tx_pointer: TxPointer::default(),
contract_id: id,
};

let contract_output = Output::Contract {
input_index: 0u8,
balance_root: Bytes32::zeroed(),
state_root: Bytes32::zeroed(),
};

// Run the script which will call the contract
let tx = script_instance
.main(
id,
fuels::types::Bytes(function_selector),
fuels::types::Bytes(calldata),
single_value_type_arg,
)
.with_inputs(vec![contract_input])
.with_outputs(vec![contract_output])
.tx_params(TxParameters::default().set_gas_limit(10_000_000));

tx.call().await.unwrap();
}

async fn get_contract_instance() -> (TestContract<WalletUnlocked>, ContractId, WalletUnlocked) {
// Launch a local network and deploy the contract
let mut wallets = launch_custom_provider_and_get_wallets(
WalletsConfig::new(
Some(1), /* Single wallet */
Some(1), /* Single coin (UTXO) */
Some(1_000_000_000), /* Amount per coin */
),
None,
None,
)
.await;
let wallet = wallets.pop().unwrap();

let id = Contract::load_from(
"test_artifacts/low_level_callee_contract/out/debug/test_contract.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&wallet, TxParameters::default())
.await
.unwrap();

let instance = TestContract::new(id.clone(), wallet.clone());

(instance, id.into(), wallet)
}

#[tokio::test]
async fn can_call_with_one_word_arg() {
let (instance, id, wallet) = get_contract_instance().await;

let function_selector = fn_selector!(set_value(u64));

let calldata = calldata!(42u64);

// Calling "set_value(u64)" with argument "42" should set the value to 42
low_level_call(id, wallet, function_selector, calldata, true).await;
let result = instance.methods().get_value().call().await.unwrap().value;
assert_eq!(result, 42);
}

#[tokio::test]
async fn can_call_with_multi_word_arg() {
let (instance, id, wallet) = get_contract_instance().await;

let function_selector = fn_selector!(set_b256_value(Bits256));

let calldata = calldata!(Bits256([1u8; 32]));

low_level_call(id, wallet, function_selector, calldata, false).await;
let result = instance
.methods()
.get_b256_value()
.call()
.await
.unwrap()
.value;
assert_eq!(result, Bits256([1u8; 32]));
}

#[tokio::test]
async fn can_call_with_multiple_args() {
let (instance, id, wallet) = get_contract_instance().await;

let function_selector = fn_selector!(set_value_multiple(u64, u64));
let calldata = calldata!(23u64, 42u64);

low_level_call(id, wallet, function_selector, calldata, false).await;
let result = instance.methods().get_value().call().await.unwrap().value;
assert_eq!(result, 23 + 42);
}

#[tokio::test]
async fn can_call_with_multiple_args_complex() {
let (instance, id, wallet) = get_contract_instance().await;

let function_selector =
fn_selector!(set_value_multiple_complex(MyStruct, SizedAsciiString::<4>));
let calldata = calldata!(
MyStruct {
a: true,
b: [1, 2, 3],
},
SizedAsciiString::<4>::try_from("fuel").unwrap()
);

low_level_call(id, wallet, function_selector, calldata, false).await;

let result_uint = instance.methods().get_value().call().await.unwrap().value;
let result_bool = instance
.methods()
.get_bool_value()
.call()
.await
.unwrap()
.value;
let result_str = instance
.methods()
.get_str_value()
.call()
.await
.unwrap()
.value;

assert_eq!(result_uint, 2);
assert!(result_bool);
assert_eq!(result_str, "fuel");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
script;

use std::bytes::Bytes;
use std::constants::BASE_ASSET_ID;
use std::low_level_call::{call_with_function_selector, call_with_function_selector_vec, CallParams};

fn main(
target: ContractId,
function_selector: Bytes,
calldata: Bytes,
single_value_type_arg: bool,
) {
let call_params = CallParams {
coins: 0,
asset_id: BASE_ASSET_ID,
gas: 10_000_000,
};

call_with_function_selector(target, function_selector, calldata, single_value_type_arg, call_params);
}

0 comments on commit d907a68

Please sign in to comment.