Skip to content

Commit

Permalink
Extract hello_world example from the book into a standalone project
Browse files Browse the repository at this point in the history
Related to FuelLabs#544.
Follows the example set in FuelLabs#731.
Tested with `mdbook serve`.

Also adds the `hello_world` and `subcurrency` examples to the Sway
workspace so that we can test both projects under CI.
  • Loading branch information
mitchmindtree committed Feb 10, 2022
1 parent 6332f9a commit 82a5a56
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 79 deletions.
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
90 changes: 12 additions & 78 deletions docs/src/getting-started/forc_project.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`:
Expand All @@ -39,7 +31,7 @@ Here are the contents of the only Sway file in the project, and the main entry p
script;
fn main() {
}
```

Expand Down Expand Up @@ -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

Expand All @@ -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:
Expand Down
20 changes: 20 additions & 0 deletions examples/hello_world/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
9 changes: 9 additions & 0 deletions examples/hello_world/Forc.toml
Original file line number Diff line number Diff line change
@@ -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" }
1 change: 1 addition & 0 deletions examples/hello_world/my-contract-abi.json
Original file line number Diff line number Diff line change
@@ -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"}]
25 changes: 25 additions & 0 deletions examples/hello_world/src/main.sw
Original file line number Diff line number Diff line change
@@ -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
}
}
43 changes: 43 additions & 0 deletions examples/hello_world/tests/harness.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 82a5a56

Please sign in to comment.