diff --git a/Cargo.toml b/Cargo.toml index b301ce8b3de..11939e84b9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,12 @@ members = [ "sway-types", "sway-utils", "test", - "test-sig-gen-util" + "test-sig-gen-util", + + # These are standalone forc example projects, but because running `forc + # test` calls `cargo test` within this workspace, we must add them here. + "examples/hello_world", + "examples/subcurrency", ] [profile.dev.package.sway-server] diff --git a/docs/src/getting-started/forc_project.md b/docs/src/getting-started/forc_project.md index 128347a6bdc..a54131a679f 100644 --- a/docs/src/getting-started/forc_project.md +++ b/docs/src/getting-started/forc_project.md @@ -22,15 +22,7 @@ $ tree . `Forc.toml` is the _manifest file_ (similar to `Cargo.toml` for Cargo or `package.json` for Node), and defines project metadata such as the project name and dependencies. ```toml -[project] -name = "hello_world" -author = "user" -entry = "main.sw" -license = "Apache-2.0" - -[dependencies] -core = { git = "http://github.com/FuelLabs/sway-lib-core", version = "v0.0.1" } -std = { git = "http://github.com/FuelLabs/sway-lib-std", version = "v0.0.1" } +{{#include ../../../examples/hello_world/Forc.toml}} ``` Here are the contents of the only Sway file in the project, and the main entry point, `src/main.sw`: @@ -39,7 +31,7 @@ Here are the contents of the only Sway file in the project, and the main entry p script; fn main() { - + } ``` @@ -71,7 +63,14 @@ Bytecode size is 28 bytes. [Return { id: ContractId([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), val: 0, pc: 488, is: 464 }] ``` -Use `forc json-abi` to output the ABI of the contract. To write this to a `.json` file (which is necessary for running tests below), pipe it using something like `forc json-abi > my_contract.json`. There is currently not a convention for where ABI files should be placed; one common choice is loose in the root directory. +Use `forc json-abi` to output the ABI of the contract. To write this to a `.json` file (which is necessary for running tests below), pipe it using something like: + +```console +forc json-abi > my-contract-abi.json +``` + +There is currently not a convention for where ABI files should be placed; one +common choice is loose in the root directory. ## Testing a Sway Project with Forc @@ -96,78 +95,13 @@ These tests can be run using either `cargo test`, or `forc test` which will look For example, let's write tests against the following contract, written in Sway. This can be done in the pregenerated `src/main.sw` or in a new file in `src`. In the case of the latter, update the `entry` field in `Forc.toml` to point at the new contract. ```sway -contract; - -use std::storage::*; -use std::constants::*; - -abi TestContract { - fn initialize_counter(gas_: u64, amount_: u64, coin_: b256, value: u64) -> u64; - fn increment_counter(gas_: u64, amount_: u64, coin_: b256, amount: u64) -> u64; -} - -const SLOT = 0x0000000000000000000000000000000000000000000000000000000000000000; - -impl TestContract for Contract { - fn initialize_counter(gas_: u64, amount_: u64, color_: b256, value: u64) -> u64 { - store(SLOT, value); - value - } - - fn increment_counter(gas_: u64, amount_: u64, color_: b256, amount: u64) -> u64 { - let storedVal: u64 = get(SLOT); - let value = storedVal + amount; - store(SLOT, value); - value - } -} +{{#include ../../../examples/hello_world/src/main.sw}} ``` Our `tests/harness.rs` file could look like: ```rust -use fuel_tx::Salt; -use fuels_abigen_macro::abigen; -use fuels_contract::contract::Contract; -use rand::rngs::StdRng; -use rand::{Rng, SeedableRng}; - -// Generate Rust bindings from our contract JSON ABI -abigen!(MyContract, "./my-contract-abi.json"); - -#[tokio::test] -async fn harness() { - let rng = &mut StdRng::seed_from_u64(2322u64); - - // Build the contract - let salt: [u8; 32] = rng.gen(); - let salt = Salt::from(salt); - let compiled = Contract::compile_sway_contract("./", salt).unwrap(); - - // Launch a local network and deploy the contract - let (client, contract_id) = Contract::launch_and_deploy(&compiled).await.unwrap(); - - let contract_instance = MyContract::new(compiled, client); - - // Call `initialize_counter()` method in our deployed contract. - // Note that, here, you get type-safety for free! - let result = contract_instance - .initialize_counter(42) - .call() - .await - .unwrap(); - - assert_eq!(42, result); - - // Call `increment_counter()` method in our deployed contract. - let result = contract_instance - .increment_counter(10) - .call() - .await - .unwrap(); - - assert_eq!(52, result); -} +{{#include ../../../examples/hello_world/tests/harness.rs}} ``` Then, in the root of our project, running `forc test` or `cargo test` will run the test above, compiling and deploying the contract to a local Fuel network, and calling the ABI methods against the contract deployed in there: diff --git a/examples/hello_world/Cargo.toml b/examples/hello_world/Cargo.toml new file mode 100644 index 00000000000..034cff7fe3e --- /dev/null +++ b/examples/hello_world/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["mindtree"] +edition = "2021" +license = "Apache-2.0" +name = "hello_world" +version = "0.1.0" + +[dependencies] +fuel-gql-client = { version = "0.2", default-features = false } +fuel-tx = "0.3" +fuels-abigen-macro = "0.3" +fuels-contract = "0.3" +fuels-core = "0.3" +rand = "0.8" +tokio = { version = "1.12", features = ["rt", "macros"] } + +[[test]] +harness = true +name = "integration_tests" +path = "tests/harness.rs" diff --git a/examples/hello_world/Forc.toml b/examples/hello_world/Forc.toml new file mode 100644 index 00000000000..bde7e581f15 --- /dev/null +++ b/examples/hello_world/Forc.toml @@ -0,0 +1,9 @@ +[project] +author = "mindtree" +entry = "main.sw" +license = "Apache-2.0" +name = "examples/hello_world" + +[dependencies] +core = { git = "http://github.com/FuelLabs/sway-lib-core" } +std = { git = "http://github.com/FuelLabs/sway-lib-std" } diff --git a/examples/hello_world/my-contract-abi.json b/examples/hello_world/my-contract-abi.json new file mode 100644 index 00000000000..849d69e8308 --- /dev/null +++ b/examples/hello_world/my-contract-abi.json @@ -0,0 +1 @@ +[{"inputs":[{"components":null,"name":"gas_","type":"u64"},{"components":null,"name":"amount_","type":"u64"},{"components":null,"name":"color_","type":"b256"},{"components":null,"name":"value","type":"u64"}],"name":"initialize_counter","outputs":[{"components":null,"name":"","type":"u64"}],"type":"function"},{"inputs":[{"components":null,"name":"gas_","type":"u64"},{"components":null,"name":"amount_","type":"u64"},{"components":null,"name":"color_","type":"b256"},{"components":null,"name":"amount","type":"u64"}],"name":"increment_counter","outputs":[{"components":null,"name":"","type":"u64"}],"type":"function"}] diff --git a/examples/hello_world/src/main.sw b/examples/hello_world/src/main.sw new file mode 100644 index 00000000000..584c3901176 --- /dev/null +++ b/examples/hello_world/src/main.sw @@ -0,0 +1,25 @@ +contract; + +use std::storage::*; +use std::constants::*; + +abi TestContract { + fn initialize_counter(gas_: u64, amount_: u64, coin_: b256, value: u64) -> u64; + fn increment_counter(gas_: u64, amount_: u64, coin_: b256, amount: u64) -> u64; +} + +const SLOT = 0x0000000000000000000000000000000000000000000000000000000000000000; + +impl TestContract for Contract { + fn initialize_counter(gas_: u64, amount_: u64, color_: b256, value: u64) -> u64 { + store(SLOT, value); + value + } + + fn increment_counter(gas_: u64, amount_: u64, color_: b256, amount: u64) -> u64 { + let storedVal: u64 = get(SLOT); + let value = storedVal + amount; + store(SLOT, value); + value + } +} diff --git a/examples/hello_world/tests/harness.rs b/examples/hello_world/tests/harness.rs new file mode 100644 index 00000000000..2a602221f3c --- /dev/null +++ b/examples/hello_world/tests/harness.rs @@ -0,0 +1,43 @@ +use fuel_tx::Salt; +use fuels_abigen_macro::abigen; +use fuels_contract::contract::Contract; +use rand::rngs::StdRng; +use rand::{Rng, SeedableRng}; + +// Generate Rust bindings from our contract JSON ABI +// FIXME: Incorrect path, see https://github.com/FuelLabs/fuels-rs/issues/94 +abigen!(MyContract, "./examples/hello_world/my-contract-abi.json"); + +#[tokio::test] +async fn harness() { + let rng = &mut StdRng::seed_from_u64(2322u64); + + // Build the contract + let salt: [u8; 32] = rng.gen(); + let salt = Salt::from(salt); + let compiled = Contract::compile_sway_contract("./", salt).unwrap(); + + // Launch a local network and deploy the contract + let (client, _contract_id) = Contract::launch_and_deploy(&compiled).await.unwrap(); + + let contract_instance = MyContract::new(compiled, client); + + // Call `initialize_counter()` method in our deployed contract. + // Note that, here, you get type-safety for free! + let result = contract_instance + .initialize_counter(42) + .call() + .await + .unwrap(); + + assert_eq!(42, result.value); + + // Call `increment_counter()` method in our deployed contract. + let result = contract_instance + .increment_counter(10) + .call() + .await + .unwrap(); + + assert_eq!(52, result.value); +}