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

Support for typed return values and output with custom types #24

Merged
merged 1 commit into from
Dec 23, 2021
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
2 changes: 1 addition & 1 deletion fuels-abigen-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ strum = "0.21"
strum_macros = "0.21"
syn = "1.0.12"
thiserror = { version = "1.0.26", default-features = false }
tokio = "1.12.0"
tokio = "1.15.0"
184 changes: 176 additions & 8 deletions fuels-abigen-macro/tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ async fn compile_bindings_single_param() {
"name": "takes_ints_returns_bool",
"outputs": [
{
"name": "",
"name": "ret",
"type": "bool"
}
]
Expand All @@ -128,7 +128,7 @@ async fn compile_bindings_single_param() {

let contract_instance = SimpleContract::new(Default::default(), fuel_client);

let contract_call = contract_instance.takes_ints_returns_bool(42 as u32);
let contract_call = contract_instance.takes_ints_returns_bool(42);

let encoded = format!(
"{}{}",
Expand Down Expand Up @@ -375,11 +375,11 @@ async fn compile_bindings_struct_input() {
"components": [
{
"name": "foo",
"type": "u8"
"type": "u8[2]"
},
{
"name": "bar",
"type": "bool"
"type": "str[4]"
}
]
}
Expand All @@ -395,7 +395,10 @@ async fn compile_bindings_struct_input() {

// Because of the abigen! macro, `MyStruct` is now in scope
// and can be used!
let input = MyStruct { foo: 10, bar: true };
let input = MyStruct {
foo: vec![10, 2],
bar: "fuel".to_string(),
};

let contract_instance = SimpleContract::new(Default::default(), fuel_client);

Expand All @@ -407,7 +410,10 @@ async fn compile_bindings_struct_input() {
hex::encode(contract_call.encoded_args)
);

assert_eq!("00000000cb0b2f05000000000000000a0000000000000001", encoded);
assert_eq!(
"00000000f427d499000000000000000a00000000000000026675656c00000000",
encoded
);
}

#[tokio::test]
Expand Down Expand Up @@ -749,13 +755,175 @@ async fn example_workflow() {
.await
.unwrap();

assert_eq!(42, result.unwrap());
assert_eq!(42, result);

let result = contract_instance
.increment_counter(10)
.call()
.await
.unwrap();

assert_eq!(52, result.unwrap());
assert_eq!(52, result);
}

#[tokio::test]
async fn type_safe_output_values() {
let rng = &mut StdRng::seed_from_u64(2322u64);

// Generates the bindings from the an ABI definition inline.
// The generated bindings can be accessed through `SimpleContract`.
abigen!(
MyContract,
r#"
[
{
"type":"contract",
"inputs":[
{
"components": null,
"name": "gas",
"type": "u64"
},
{
"components": null,
"name": "coin",
"type": "u64"
},
{
"components": null,
"name": "color",
"type": "b256"
},
{
"name":"value",
"type":"u64",
"components": []
}
],
"name":"is_even",
"outputs":[
{
"name":"ret",
"type":"bool",
"components": []
}
]
},
{
"type":"contract",
"inputs":[
{
"components": null,
"name": "gas",
"type": "u64"
},
{
"components": null,
"name": "coin",
"type": "u64"
},
{
"components": null,
"name": "color",
"type": "b256"
},
{
"name":"value",
"type":"str[4]",
"components": []
}
],
"name":"return_my_string",
"outputs":[
{
"name":"ret",
"type":"str[4]",
"components": []
}
]
},
{
"type":"contract",
"inputs":[
{
"components": null,
"name": "gas",
"type": "u64"
},
{
"components": null,
"name": "coin",
"type": "u64"
},
{
"components": null,
"name": "color",
"type": "b256"
},
{
"name":"value",
"type":"struct MyStruct",
"components": [
{
"name": "foo",
"type": "u8"
},
{
"name": "bar",
"type": "bool"
}
]
}
],
"name":"return_my_struct",
"outputs":[
{
"name":"ret",
"type":"struct MyStruct",
"components": [
{
"name": "foo",
"type": "u8"
},
{
"name": "bar",
"type": "bool"
}
]
}
]
}
]
"#
);

// Build the contract
let salt: [u8; 32] = rng.gen();
let salt = Salt::from(salt);

let compiled =
Contract::compile_sway_contract("tests/test_projects/contract_output_test", salt).unwrap();

let (client, contract_id) = Contract::launch_and_deploy(&compiled).await.unwrap();

println!("Contract deployed @ {:x}", contract_id);

let contract_instance = MyContract::new(compiled, client);

// `response`'s type matches the return type of `is_event()`
let response = contract_instance.is_even(10).call().await.unwrap();
assert!(response);

// `response`'s type matches the return type of `return_my_string()`
let response = contract_instance
.return_my_string("fuel".to_string())
.call()
.await
.unwrap();

assert_eq!(response, "fuel");

let my_struct = MyStruct { foo: 10, bar: true };

let _response = contract_instance.return_my_struct(my_struct).call().await;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[project]
author = "Rodrigo Araujo"
license = "MIT"
name = "contract_test"
entry = "main.sw"

[dependencies]
increment_abi = { path = "../library_test" }
std = { path = "../lib-std" }
core = { path = "../lib-core" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
contract;

use std::*;
use core::*;
use std::storage::*;

struct MyStruct {
foo: u8,
bar: bool,
}

abi TestContract {
fn is_even(gas: u64, coin: u64, color: b256, value: u64) -> bool;
fn return_my_string(gas: u64, coin: u64, color: b256, value: str[4]) -> str[4];
fn return_my_struct(gas: u64, coin: u64, color: b256, value: MyStruct) -> MyStruct;

}

impl TestContract for Contract {
fn is_even(gas: u64, coin: u64, color: b256, value: u64) -> bool {
if (value / 2) * 2 == value {
true
} else {
false
}
}
fn return_my_string(gas: u64, coin: u64, color: b256, value: str[4]) -> str[4] {
value
}


fn return_my_struct(gas: u64, coin: u64, color: b256, value: MyStruct) -> MyStruct {
value
}

}
34 changes: 30 additions & 4 deletions fuels-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::fmt;
use fuel_types::bytes::padded_len;
use fuel_types::Word;
use strum_macros::{EnumString, ToString};
use strum_macros::EnumString;

pub mod abi_decoder;
#[cfg(not(feature = "no-std"))]
Expand All @@ -14,7 +14,7 @@ pub type Bits256 = [u8; 32];
pub type EnumSelector = (u8, Token);
pub const WORD_SIZE: usize = core::mem::size_of::<Word>();

#[derive(Debug, Clone, EnumString, ToString, PartialEq, Eq)]
#[derive(Debug, Clone, EnumString, PartialEq, Eq)]
#[strum(ascii_case_insensitive)]
pub enum ParamType {
U8,
Expand All @@ -27,8 +27,6 @@ pub enum ParamType {
Array(Box<ParamType>, usize),
#[strum(serialize = "str")]
String(usize),
// Disabling EnumString on these 2 types because
// they are more complex to parse
#[strum(disabled)]
Struct(Vec<ParamType>),
#[strum(disabled)]
Expand All @@ -41,6 +39,34 @@ impl Default for ParamType {
}
}

impl fmt::Display for ParamType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParamType::String(size) => {
let t = format!("String({})", size);
write!(f, "{}", t)
}
ParamType::Array(t, size) => {
let boxed_type_str = format!("Box::new(ParamType::{})", t.to_string());
let arr_str = format!("Array({},{})", boxed_type_str, size);
write!(f, "{}", arr_str)
}
ParamType::Struct(inner) => {
let inner_strings: Vec<String> = inner
.iter()
.map(|p| format!("ParamType::{}", p.to_string()))
.collect();

let s = format!("Struct(vec![{}])", inner_strings.join(","));
write!(f, "{}", s)
}
_ => {
write!(f, "{:?}", self)
}
}
}
}

// Sway types
#[derive(Debug, Clone, PartialEq, EnumString)]
#[strum(ascii_case_insensitive)]
Expand Down
Loading