Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

chore: add sway app abi tests #1341

Merged
merged 2 commits into from
Sep 29, 2023
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
4 changes: 4 additions & 0 deletions packages/fuel-indexer-lib/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,12 @@ lazy_static! {
/// Generic Sway ABI types.
pub static ref IGNORED_GENERIC_METADATA: HashSet<&'static str> = HashSet::from([
"generic T",
"generic E",
"raw untyped ptr",
"struct RawVec",
"struct RawBytes",
"struct Bytes",
"enum Result"
]);

pub static ref GENERIC_STRUCTS: HashSet<&'static str> = HashSet::from([
Expand Down
7 changes: 7 additions & 0 deletions packages/fuel-indexer-macros/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub fn is_non_decodable_type(typ: &TypeDeclaration) -> bool {
is_tuple_type(typ)
|| is_unit_type(typ)
|| IGNORED_GENERIC_METADATA.contains(typ.type_field.as_str())
|| is_array_type(typ)
}

/// Derive Ident for decoded type
Expand Down Expand Up @@ -1116,6 +1117,12 @@ pub fn typed_path_components(
(name, tokens)
}

fn is_array_type(typ: &TypeDeclaration) -> bool {
typ.type_field.starts_with('[')
&& typ.type_field.ends_with(']')
&& typ.type_field.contains(';')
}

/// Determine whether or not the given type name is an unsupported type.
///
/// Since we allow unsupported types in the ABI JSON, this check is only
Expand Down
15 changes: 15 additions & 0 deletions packages/fuel-indexer-macros/src/indexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use syn::{parse_macro_input, FnArg, Item, ItemMod, PatType, Type};

fn additional_declarations() -> proc_macro2::TokenStream {
quote! {
// Miscellaneous types that can be included in ABI JSON
type b256 = [u8; 32];
type Bytes = Vec<u8>;
type B512 = [u8; 64];
}
}

fn process_fn_items(
manifest: &Manifest,
abi_path: Option<String>,
Expand Down Expand Up @@ -930,6 +939,8 @@ pub fn process_indexer_module(attrs: TokenStream, item: TokenStream) -> TokenStr
manifest.execution_source(),
);

let decl_tokens = additional_declarations();

let output = match manifest.execution_source() {
ExecutionSource::Native => {
let (handler_block, fn_items) =
Expand All @@ -939,6 +950,8 @@ pub fn process_indexer_module(attrs: TokenStream, item: TokenStream) -> TokenStr

quote! {

#decl_tokens

#abi_tokens

#graphql_tokens
Expand All @@ -956,6 +969,8 @@ pub fn process_indexer_module(attrs: TokenStream, item: TokenStream) -> TokenStr
let handler_block = handler_block_wasm(handler_block);
quote! {

#decl_tokens

#abi_tokens

#graphql_tokens
Expand Down
10 changes: 4 additions & 6 deletions packages/fuel-indexer-macros/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ pub fn handler_block_native(
/// indexer module, not within the scope of the entire lib module.
fn native_prelude() -> proc_macro2::TokenStream {
quote! {
type B256 = [u8; 32];

static mut db: Option<Arc<Mutex<Database>>> = None;

use fuel_indexer_utils::plugin::types::*;
Expand All @@ -60,10 +58,10 @@ pub fn native_main() -> proc_macro2::TokenStream {


let config = args
.config
.as_ref()
.map(IndexerConfig::from_file)
.unwrap_or(Ok(IndexerConfig::from(args)))?;
.config
.as_ref()
.map(IndexerConfig::from_file)
.unwrap_or(Ok(IndexerConfig::from(args)))?;

init_logging(&config).await?;

Expand Down
2 changes: 0 additions & 2 deletions packages/fuel-indexer-macros/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ fn wasm_prelude() -> proc_macro2::TokenStream {
use alloc::{format, vec, vec::Vec};
use std::str::FromStr;

type B256 = [u8; 32];

use fuel_indexer_utils::plugin::types::*;
use fuel_indexer_utils::plugin::wasm::*;
use fuel_indexer_utils::plugin::{serde_json, serialize, deserialize, bincode};
Expand Down
60 changes: 60 additions & 0 deletions packages/fuel-indexer-tests/scripts/copy-swap-app-abi.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/bin/bash

# This script copies the JSON ABI for each Sway application into the indexer's
# trybuild tests.
#
# This helps ensure that the indexers can be built with Sway projects that more
# closely resemble "real-world" contracts, as oppposed to just indexer test
# contracts.
#
# This script should be run from the repository root, and might have to be updated
# to account for the addition/removal of Sway applications.

usage() {
echo "Usage: $0 [options] <directory_root_path>"
echo "Options:"
echo " -h, --help Show this help message and exit."
echo
echo "Arguments:"
echo " <directory_root_path> The root path of the sway-application repository."
echo
}

swayapps_root=$1

if [[ "$1" == "-h" || "$1" == "--help" ]]; then
usage
exit 0
fi

if [[ -z "$1" || ! -d "$1" ]]; then
echo "Error: Invalid or missing directory root path."
usage
exit 1
fi

testdir=$(realpath $(dirname $(dirname $0)))
abidir=$testdir/trybuild/abi
echo $abidir

paths=(
"AMM/project/contracts/AMM-contract"
"AMM/project/contracts/exchange-contract"
"DAO/project/contracts/DAO-contract"
"OTC-swap-predicate/project/predicates/swap-predicate"
"airdrop/project/contracts/asset-contract"
"airdrop/project/contracts/distributor-contract"
"multisig-wallet/project/contracts/multisig-contract"
"escrow/project/contracts/escrow-contract"
"timelock/project/contracts/timelock-contract"
"auctions/english-auction/project/contracts/auction-contract"
"name-registry/project/contracts/registry-contract"
"oracle/project/contracts/oracle-contract"
)

for path in "${paths[@]}"; do
cd $swayapps_root/$path
forc build
cp -fv $path/out/debug/*-abi.json $abidir/
done

130 changes: 109 additions & 21 deletions packages/fuel-indexer-tests/tests/trybuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ enum TestKind {
Fail,
}

#[test]
fn test_success_and_failure_macros() {
fn test_dirs() -> (String, String, String, String) {
let t = trybuild::TestCases::new();
let manifest_dir = env!("CARGO_MANIFEST_DIR");
std::env::set_var("COMPILE_TEST_PREFIX", manifest_dir);
Expand All @@ -21,7 +20,35 @@ fn test_success_and_failure_macros() {
let tests_root = project_root.join("packages").join("fuel-indexer-tests");
let tests_root_str = tests_root.to_str().unwrap();
let trybuild_root = tests_root.join("trybuild");
let abi_root = trybuild_root.join("abi");
let abi_root_str = abi_root.to_str().unwrap();

(
abi_root_str.to_string(),
tests_root_str.to_string(),
project_root_str.to_string(),
trybuild_root.to_string_lossy().to_string(),
)
}

fn manifest_with_contract_abi(contract_name: &str) -> String {
let (abi_root_str, tests_root_str, project_root_str, _) = test_dirs();
format!(
r#"
namespace: test_namespace
identifier: simple_wasm_executor
abi: {abi_root_str}/{contract_name}
graphql_schema: {tests_root_str}/indexers/simple-wasm/schema/simple_wasm.graphql
contract_id: ~
module:
wasm: {project_root_str}/target/wasm32-unknown-unknown/release/simple_wasm.wasm"#
)
}

#[test]
fn test_success_and_failure_macros() {
let t = trybuild::TestCases::new();
let (abi_root_str, tests_root_str, project_root_str, trybuild_root) = test_dirs();
let manifest_content = format!(
r#"
namespace: test_namespace
Expand All @@ -34,12 +61,16 @@ module:
"#
);

// IMPORTANT: Even though in theory we should be able to just re-use the same filename
// since we're writing and reading to each file for each test individually, in practice,
// these tests will error out if we use the same filename for each test.
//
// So, we simply change the manifest name according to each test to avoid these flaky errors.
let tests = vec![
(
"fail_if_attribute_manifest_schema_arg_is_invalid.rs",
"invalid_schema_simple_wasm.yaml",
TestKind::Fail,
// Using a custom manifest here
format!(
r#"
namespace: test_namespace
Expand Down Expand Up @@ -84,38 +115,27 @@ module:
),
(
"pass_if_indexer_is_valid_single_type.rs",
"simple_wasm.yaml",
"simple_wasm_single.yaml",
TestKind::Pass,
manifest_content.clone(),
),
(
"pass_if_indexer_is_valid_multi_type.rs",
"simple_wasm.yaml",
"simple_wasm_multi.yaml",
TestKind::Pass,
manifest_content.clone(),
),
(
"pass_if_unsupported_types_are_used.rs",
"simple_wasm.yaml",
"simple_wasm_unsupported.yaml",
TestKind::Pass,
// Using a custom manifest here
manifest_content.clone(),
),
(
"fail_if_abi_contains_reserved_fuel_type.rs",
"invalid_abi_type_simple_wasm.yaml",
TestKind::Fail,
// Using a custom manifest here
format!(
r#"
namespace: test_namespace
identifier: simple_wasm_executor
abi: {tests_root_str}/contracts/simple-wasm/out/debug/contracts-abi-reserved-name.json
graphql_schema: {tests_root_str}/indexers/simple-wasm/schema/simple_wasm.graphql
contract_id: ~
module:
wasm: {project_root_str}/target/wasm32-unknown-unknown/release/simple_wasm.wasm"#
),
manifest_with_contract_abi("contracts-abi-reserved-name.json"),
),
(
"fail_if_ident_not_defined_in_abi.rs",
Expand All @@ -135,19 +155,87 @@ module:
TestKind::Fail,
manifest_content.clone(),
),
(
"pass_if_using_sway_amm_abi.rs",
"sway_amm.yaml",
TestKind::Pass,
manifest_with_contract_abi("AMM-contract-abi.json"),
),
(
"pass_if_using_sway_dao_abi.rs",
"sway_dao.yaml",
TestKind::Pass,
manifest_with_contract_abi("DAO-contract-abi.json"),
),
(
"pass_if_using_sway_asset_contract_abi.rs",
"asset_contract.yaml",
TestKind::Pass,
manifest_with_contract_abi("asset-contract-abi.json"),
),
(
"pass_if_using_sway_distributor_contract_abi.rs",
"distributor_contract.yaml",
TestKind::Pass,
manifest_with_contract_abi("distributor-contract-abi.json"),
),
(
"pass_if_using_sway_escrow_contract_abi.rs",
"escrow_contract.yaml",
TestKind::Pass,
manifest_with_contract_abi("escrow-contract-abi.json"),
),
(
"pass_is_using_sway_exchange_contract_abi.rs",
"exchange_contract.yaml",
TestKind::Pass,
manifest_with_contract_abi("exchange-contract-abi.json"),
),
// NOTE: I don't think the ABI tokens are being properly generated from this contract JSON
Copy link
Contributor Author

@ra0x3 ra0x3 Sep 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will ask the Sway team and the SDK team why this particular contract presents an issue. There is a simple struct EvmAddress that for whatever reason is just not returned in the ABI tokens (so on the SDK side)

// (
// "pass_if_using_sway_multisig_contract_abi.rs",
// "multisig_contract.yaml",
// TestKind::Pass,
// manifest_with_contract_abi("multisig-contract-abi.json"),
// ),
(
"pass_if_using_sway_oracle_contract_abi.rs",
"oracle_contract.yaml",
TestKind::Pass,
manifest_with_contract_abi("oracle-contract-abi.json"),
),
(
"pass_if_using_sway_registry_contract_abi.rs",
"registry_contract.yaml",
TestKind::Pass,
manifest_with_contract_abi("registry-contract-abi.json"),
),
(
"pass_if_using_swap_predicate_abi.rs",
"predicate_abi.yaml",
TestKind::Pass,
manifest_with_contract_abi("swap-predicate-abi.json"),
),
(
"pass_if_using_timelock_contract_abi.rs",
"timelock_contract.yaml",
TestKind::Pass,
manifest_with_contract_abi("timelock-contract-abi.json"),
),
];

for (name, manifest_name, kind, manifest_content) in tests {
let manifest_path = trybuild_root.join(manifest_name);
let trybuild = Path::new(&trybuild_root);
let manifest_path = trybuild.join(manifest_name);
let mut f = std::fs::File::create(&manifest_path).unwrap();
f.write_all(manifest_content.as_bytes()).unwrap();

match kind {
TestKind::Pass => {
t.pass(trybuild_root.join(&name));
t.pass(trybuild.join(&name));
}
TestKind::Fail => {
t.compile_fail(trybuild_root.join(&name));
t.compile_fail(trybuild.join(&name));
}
}
}
Expand Down
Loading
Loading