Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Move book examples into standalone projects. Include them in book via mdbook preprocessor. #766

Merged
merged 10 commits into from
Feb 15, 2022
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
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ jobs:
command: run
args: --release --bin test

- name: Install forc
uses: actions-rs/cargo@v1
with:
command: install
args: --debug --path ./forc

- name: Build sway examples
uses: actions-rs/cargo@v1
with:
command: run
args: --bin build-all-examples

publish:
# Only do this job if publishing a release
needs: build
Expand Down
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
resolver = "2"
members = [
"docstrings",
"examples/build-all-examples",
"forc",
"parser",
"sway-core",
Expand All @@ -11,7 +12,13 @@ members = [
"sway-types",
"sway-utils",
"test",
"test-sig-gen-util"
"test-sig-gen-util",
]
exclude = [
"examples/fizzbuzz",
"examples/hello_world",
"examples/subcurrency",
"examples/wallet_smart_contract",
]

[profile.dev.package.sway-server]
Expand Down
28 changes: 1 addition & 27 deletions docs/src/examples/fizzbuzz.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,5 @@ and receive back its fizzbuzzability as an enum. Note that the deserialization s
so the caller knows what to do with the bytes.

```sway
contract;

enum FizzBuzzResult {
Fizz: (),
Buzz: (),
FizzBuzz: (),
Other: u64,
}

abi FizzBuzz {
fn fizzbuzz(gas: u64, coins: u64, asset_id: b256, input: u64) -> FizzBuzzResult;
}

impl FizzBuzz for Contract {
fn fizzbuzz(gas: u64, coins: u64, asset_id: b256, input: u64) -> FizzBuzzResult {
if input % 15 == 0 {
FizzBuzzResult::FizzBuzz
} else if input % 3 == 0 {
FizzBuzzResult::Fizz
} else if input % 5 == 0 {
FizzBuzzResult::Buzz
} else {
FizzBuzzResult::Other(input)
}
}
}

{{#include ../../../examples/fizzbuzz/src/main.sw}}
```
37 changes: 1 addition & 36 deletions docs/src/examples/wallet_smart_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,5 @@
_Contract storage in the language syntax is a work-in-progress feature, and the following example does not currently compile._

```sway
contract;

use std::*;

const OWNER_ADDRESS: b256 = 0x8900c5bec4ca97d4febf9ceb4754a60d782abbf3cd815836c1872116f203f861;
const ETH_ID: b256 = 0x0000000000000000000000000000000000000000000000000000000000000000;

storage {
balance: u64,
}

abi Wallet {
fn receive_funds(gas_to_forward: u64, coins_to_forward: u64, asset_id: b256, unused: ());
fn send_funds(gas_to_forward: u64, coins_to_forward: u64, asset_id: b256, req: SendFundsRequest);
}

impl Wallet for Contract {
fn receive_funds(gas_to_forward: u64, coins_to_forward: u64, asset_id: b256, unused: ()) {
if asset_id == ETH_ID {
let balance = storage.balance.write();
deref balance = balance + coins_to_forward;
};
}

fn send_funds(gas_to_forward: u64, coins_to_forward: u64, asset_id: b256, req: SendFundsRequest) {
assert(sender() == OWNER_ADDRESS);
assert(storage.balance > req.amount_to_send);
storage.balance = storage.balance - req.amount_to_send;
transfer_coins(asset_id, req.recipient_address, req.amount_to_send);
}
}

struct SendFundsRequest {
amount_to_send: u64,
recipient_address: b256,
}
{{#include ../../../examples/wallet_smart_contract/src/main.sw}}
```
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() {
Copy link
Contributor Author

@mitchmindtree mitchmindtree Feb 10, 2022

Choose a reason for hiding this comment

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

Woops, my editor's setup to remove trailing whitespace automatically when writing the buffer


}
```

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
6 changes: 6 additions & 0 deletions examples/build-all-examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "build-all-examples"
description = "Runs `forc build` for all projects under the Sway `examples` directory."
version = "0.0.0"
edition = "2021"
publish = false
88 changes: 88 additions & 0 deletions examples/build-all-examples/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Runs `forc build` for all projects under the Sway `examples` directory.
//!
//! NOTE: This expects both `forc` and `cargo` to be available in `PATH`.

use std::{
fs,
io::{self, Write},
path::{Path, PathBuf},
};

fn main() {
let proj_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let examples_dir = proj_dir
.parent()
.expect("failed to find examples directory");

// Track discovered projects and whether or not they were successful.
let mut summary: Vec<(PathBuf, bool)> = vec![];

for res in fs::read_dir(examples_dir).expect("failed to walk examples directory") {
let entry = match res {
Ok(entry) => entry,
_ => continue,
};
let path = entry.path();
if !path.is_dir() || !dir_contains_forc_manifest(&path) {
continue;
}

let output = std::process::Command::new("forc")
.args(["build", "--path"])
.arg(&path)
.output()
.expect("failed to run `forc build` for example project");

// Print output on failure so we can read it in CI.
let success = if !output.status.success() {
io::stdout().write_all(&output.stdout).unwrap();
io::stdout().write_all(&output.stderr).unwrap();
false
} else {
true
};

summary.push((path, success));
}

println!("\nBuild all examples summary:");
let mut successes = 0;
for (path, success) in &summary {
let (checkmark, status) = if *success {
("[✓]", "succeeded")
} else {
("[x]", "failed")
};
println!(" {}: {} {}!", checkmark, path.display(), status);
if *success {
successes += 1;
}
}
let failures = summary.len() - successes;
let successes_str = if successes == 1 {
"success"
} else {
"successes"
};
let failures_str = if failures == 1 { "failure" } else { "failures" };
println!(
"{} {}, {} {}",
successes, successes_str, failures, failures_str
);

if failures > 0 {
std::process::exit(1);
}
}

// Check if the given directory contains `Forc.toml` at its root.
fn dir_contains_forc_manifest(path: &Path) -> bool {
if let Ok(entries) = fs::read_dir(path) {
for entry in entries.flatten() {
if entry.path().file_name().and_then(|s| s.to_str()) == Some("Forc.toml") {
return true;
}
}
}
false
}
20 changes: 20 additions & 0 deletions examples/fizzbuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
authors = ["Fuel Labs <contact@fuel.sh>"]
edition = "2021"
license = "Apache-2.0"
name = "fizzbuzz"
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/fizzbuzz/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
author = "Fuel Labs <contact@fuel.sh>"
entry = "main.sw"
license = "Apache-2.0"
name = "fizzbuzz"

[dependencies]
core = { git = "http://github.com/FuelLabs/sway-lib-core" }
std = { git = "http://github.com/FuelLabs/sway-lib-std" }
26 changes: 26 additions & 0 deletions examples/fizzbuzz/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
contract;

enum FizzBuzzResult {
Fizz: (),
Buzz: (),
FizzBuzz: (),
Other: u64,
}

abi FizzBuzz {
fn fizzbuzz(gas: u64, coins: u64, asset_id: b256, input: u64) -> FizzBuzzResult;
}

impl FizzBuzz for Contract {
fn fizzbuzz(gas: u64, coins: u64, asset_id: b256, input: u64) -> FizzBuzzResult {
if input % 15 == 0 {
FizzBuzzResult::FizzBuzz
} else if input % 3 == 0 {
FizzBuzzResult::Fizz
} else if input % 5 == 0 {
FizzBuzzResult::Buzz
} else {
FizzBuzzResult::Other(input)
}
}
}
4 changes: 4 additions & 0 deletions examples/fizzbuzz/tests/harness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[tokio::test]
async fn harness() {
assert_eq!(true, true);
}
Loading