Sway bare-bones Development Environment.
In this guide we will setup a simple no fills Sway development environment, we will setup the whole thing from scratch: from installing Rust and Sways language server for VScode to the fuel tool chain. We will initialize a skeleton forc project that has a Sway main file and initialize a test harness for integration testing. A description of the conventions of basic fuel project development is given from square one and is targeted at developers getting to know the Sway landscape within Fuel.
Additionally as a simple example we will borrow Camiβs full-stack fuel example, adding in the counter contract code and an integration test. This section is short as the objective of this guide is to get acquainted with the forc toolchain and Sway program development. I encourage you to read the full-stack fuel example as it gives excellent detail into the contract code line-by-line. Feel free to leave comments below , this guide is also on github in markdown .
The TL/DR to TL/Suldβve-R:
As a new developer or even a seasoned one when coming to a new ecosystem and getting setup, its sometimes easy to get tripped up on something that doesnβt install correctly, or another dependency being out of date which usually adds to frustration and is a massive time sink. The team at Fuel have spent a lot of effort making the developer experience low friction to migration, rich in tooling and well documented.
Even with the above said, the in Fuel ecosystem, its tooling and code are under rapid development, as a beginner you may come across some code examples that are a little outdated or guides that need updating from version changes. If youβre finding all the links to docs, guides and how toβs a little overwhelming lets start with the bare minimum background information to form a foundation to understanding the Fuel ecosystem and get you up and running quickly and into a reasonably bare bones Sway development environment. This guide is based off forc
version 0.31.3
and is an aggregation of the important parts from a handful of setup guides in order to save you some time getting setup.
Prerequisites:
Rust:
We need Rust installed. Its not strictly necessary for the fuel toolchain to be installed but it is for the integration testing that we will be going over. Install Rust and verify the version. Im working on Ubuntu (Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-53-generic x86_64)) for which the set of commands to install Rust are below. Your system may be different, but follow the guide here if you need.
Ubuntu:
sudo apt update && sudo apt upgrade
sudo apt install curl build-essential gcc make -y
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then configure your current shell with:
source "$HOME/.cargo/env"
Check the installed version:
$ rustup --version
rustup 1.25.1 (bb60b1e89 2022-07-12)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active rustc version is rustc 1.65.0 (897e37553 2022-11-02)
So in this example at the time of writing we have rustup version 1.25.1 and the rust compiler version 1.65.0.
Get the Sway Visual Studio Code Extension:
Iβm going to assume any sane developer, presumably like yourself is going to use VScode to write the majority of their code. If youβre a glass eater and want to use vim
then I applaud you. For the rest of us normies lets just install Visual Studio code, you can get it here. Once installed add the Sway extension to VScode to make life easier:
A Little Background to the Fuel Toolchain.
To develop in Sway on the Fuel blockchain you want to have the Fuel toolchain installed. fuelup
is the official package manager for the Fuel toolchain. The take away here is what you get with fuelup is forc
(the f
uel orc
hestrator), fuel-core
as well as a bunch of other tools.
forc
provides a variety of tools and commands for developers working with the Fuel ecosystem, from a simple way to scaffold a new project to formatting, running scripts, deploying contracts, testing contracts, and more. forc
is to Fuel, what cargo
is to Rust or what pip
is to Python. fuel-core
lets you spin up an local Fuel node to run tests against during your Sway development.
Setting Up the Development Environment
Installing the Fuel toolchain can be done through the fuelup-init
script below. Copy and paste this into your terminal and run:
curl --proto '=https' --tlsv1.2 -sSf https://fuellabs.github.io/fuelup/fuelup-init.sh | sh
This will install forc
, forc-deploy
, forc-doc
, forc-explore
, forc-fmt
, forc-lsp
, forc-run
, forc-wallet
fuel-core
, fuel-indexer
and fuelup
in ~/.fuelup/bin
. The script will ask for permission to add ~/.fuelup/bin
to your PATH
. Enter y and the installation will run:
Would you like fuelup-init to modify your PATH variable for you? (N/y)
After the install you should now have a couple new hidden directories in home .fuelup
& .forc
, and at the time of writing this the output message from the install was:
Installed:
- forc 0.31.3
- forc-explore 0.28.1
- forc-wallet 0.1.2
- fuel-core 0.15.0
- fuel-indexer 0.1.10
Its a good idea at this point to check that everything installed correctly. Source your .bashrc
file in your current terminal window, or log out and log back in. What you should have in your ~/.fuelup/bin
directory is the below:
$ cd ~/.fuelup/bin
$ ls
forc forc-deploy forc-doc forc-explore forc-fmt forc-lsp forc-run forc-wallet fuel-core fuel-indexer fuelup
Heres a print out of my terminal when I check the version of each of the binaries (above) from my home directory:
$ fuelup --version
fuelup 0.14.0
$ forc --version
forc 0.31.3
$ forc-wallet --version
forc-wallet 0.1.2
$ forc-explore --version
forc-explore 0.28.1
$ fuel-core --version
fuel-core 0.15.0
...
A bare-bones Project:
Before we build our first project lets describe how a simple development cycle works for a Sway program. The whole project uses a few different parts from the toolchain we installed earlier to compile, test and deploy etc, these include: forc, Fuel-core, Sway and Rust.
What we have when we initialize a new forc project is a Sway main file main.sw
in the /src
directory, where you write the code for your Sway program (a smart contract, script, predicate or library - some info on the difference here). There will also be a Forc.toml
in the root directory which defines the project metadata like name, dependencies and other attributes of the project (Similar to a Cargo.toml
in Rust). Sway looks very similar syntactically to Rust, however we need to be aware of a clear differentiation when we go on to testing a Sway program, donβt let the build environments for forc
and cargo
confuse you. Once we have written some program code you may want to test. At the time of writing this Unit Testing is in development but we can write Integration tests using Fuels Sway tests template via Rusts Cargo generate crate (using the Fuel Rust-SDK). The best way to see all this in action is to do it, so lets generate a forc project and integration test skeleton project:
Move to your workspace in your development environment. To generate a new project we use the Fuel Orchestrator (forc
) with the "new"
command: forc new <name_of_project>
where <name_of_project>
is what you want to call your new forc project:
forc new project-1
Ok great, forc should have given you some output about how to compile & test, some links to docs, community and bug reporting, and you should now have a new directory /project-1
with the file structure:
βββ project-1
βββ Forc.toml
βββ src
βββ main.sw
Great! we have the bare bones of a new forc project with a skeleton Sway main file. lets build it using the forc build
command from the project root and see what changes in the project directory:
$ forc build
Creating a new Forc.lock file. (Cause: lock file did not exist)
Adding core
Adding std git+https://github.com/fuellabs/sway?tag=v0.31.3#12ad8423811d566972dd75fbb954cdb95afde8d8
Created new lock file at /home/catsper/fuel/workspace/project-1/Forc.lock
Compiled library "core".
Compiled library "std".
Compiled contract "project-1".
Bytecode size is 60 bytes.
Notice below now we have a /out
directory, this is where the abi.json
, the .bin
and storage_slots.json
file are output. We are going to reference these soon when we do some testing.
βββ Forc.lock
βββ Forc.toml
βββ out
β βββ debug
β βββ project-1-abi.json
β βββ project-1.bin
β βββ project-1-storage_slots.json
βββ src
βββ main.sw
Right now we have a program that is a skeleton, it can be built but it doesnβt do anything. Before we switch our attention from the project structure and go ahead and add some contract code to the Sway main file (which we will do in a little bit below). I want to run through the setup of the test harness so we have a complete program + testing
project. That way you have a real βbare-bonesβ project which you can test against a local fuel-core node.
Setting Up the Test Harness
Weβre going to add a Rust integration test harness using a cargo generate template. To do this we need to make sure we have the cargo generate command installed. Its great we already have Rust installed because now weβre going to use it. We need the developer tool cargo-generate which leverages the sway test template. In a terminal window run:
cargo install cargo-generate
Once Cargo is done installing we can generate a test hardness for integration tests. In the terminal in your project root run:
$ cargo generate --init fuellabs/sway templates/sway-test-rs --name project-1 --force
log output:
β οΈ Favorite fuellabs/sway not found in config, using it as a git repository: https://github.com/fuellabs/sway.git
π§ Destination: /home/catsper/fuel/workspace/project-1 ...
π§ project-name: project-1 ...
π§ Generating template ...
[1/3] Done: Cargo.toml
[2/3] Done: tests/harness.rs
[3/3] Done: tests
π§ Moving generated files into: [code single]/home/catsper/fuel/workspace/project-1...
β¨ Done! New project created /home/catsper/fuel/workspace/project-1
If you have changed the project name to something other than project-1
, then modify the --name project-1
part of the above.
Awesome! Now we have the project directory looking like the below, with the test harness.rs
in the /tests
sub directory:
βββ Cargo.toml
βββ Forc.lock
βββ Forc.toml
βββ out
β βββ debug
β βββ project-1-abi.json
β βββ project-1.bin
β βββ project-1-storage_slots.json
βββ src
β βββ main.sw
βββ tests
βββ harness.rs
Again so far the test harness is a just a skeleton and doesnβt do much except give us a simple starting point for a test with a contract instance. You can run the test(s) using the cargo test
command.
cargo test
With the output:
running 1 test
test can_get_contract_id ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.21s
Lets take account of what weβve done so far:
1. Weβve installed the requisite software (Rust, VScode).
2. installed the forc
toolchain.
3. Initialized a skeleton forc project with Sway main file.
4. Built the sway forc project.
5. Installed the cargo test cargo-generate tool.
6. Generated a Swat Rust test harness.
7. Built and Run the skeleton integration test.
Counter Contract Example:
If you havenβt already checked out Camiβs full-stack fuel example with the counter contract take a look. This is what we will be basing the contract code off below.
In the main.sw
file we have from out initialized forc project above, replace whats in main.sw
with the counter contract code below:
contract;
storage {
counter: u64 = 0,
}
abi Counter {
#[storage(write)]
fn initialize_counter(value: u64) -> u64;
#[storage(read, write)]
fn increment_counter(amount: u64) -> u64;
#[storage(read)]
fn count() -> u64;
}
impl Counter for Contract {
#[storage(write)]
fn initialize_counter(value: u64) -> u64 {
storage.counter = value;
value
}
#[storage(read, write)]
fn increment_counter(amount: u64) -> u64 {
let incremented = storage.counter + amount;
storage.counter = incremented;
incremented
}
#[storage(read)]
fn count() -> u64 {
return storage.counter;
}
}
build the contract with forc build
$ forc build
Compiled library "core".
Compiled library "std".
Compiled contract "project-1".
Bytecode size is 300 bytes.
Now copy and paste the integration tests, in the /tests/harness.rs
file, copy and past the below:
use fuels::{prelude::*, tx::ContractId};
// Load abi from json
abigen!(MyContract, "out/debug/project-1-abi.json");
async fn get_contract_instance() -> (MyContract, ContractId) {
// Launch a local network and deploy the contract
let mut wallets = launch_custom_provider_and_get_wallets(
WalletsConfig::new(
Some(1), /* Single wallet */
Some(1), /* Single coin (UTXO) */
Some(1_000_000_000), /* Amount per coin */
),
None,
None,
)
.await;
let wallet = wallets.pop().unwrap();
let id = Contract::deploy(
"./out/debug/project-1.bin",
&wallet,
TxParameters::default(),
StorageConfiguration::with_storage_path(Some(
"./out/debug/project-1-storage_slots.json".to_string(),
)),
)
.await
.unwrap();
let instance = MyContract::new(id.clone(), wallet);
(instance, id.into())
}
#[tokio::test]
async fn can_get_contract_id() {
let (_contract_instance, _id) = get_contract_instance().await;
}
#[tokio::test]
async fn can_inint_counter() {
let (contract_instance, _id) = get_contract_instance().await;
// Now you have an instance of your contract you can use to test each function
let result = contract_instance
.methods()
.initialize_counter(42)
.call()
.await
.unwrap();
assert_eq!(42, result.value);
}
#[tokio::test]
async fn can_inc_counter() {
let (contract_instance, _id) = get_contract_instance().await;
// if the above test fails can_inint_counter(), then this test will also fail
let result = contract_instance
.methods()
.initialize_counter(42)
.call()
.await
.unwrap();
assert_eq!(42, result.value);
// Call [code single]increment_counter()[/code] method in our deployed contract.
let result = contract_instance
.methods()
.increment_counter(10)
.call()
.await
.unwrap();
assert_eq!(52, result.value);
}
$ cargo test
Compiling project-1 v0.1.0 (/home/catsper/fuel/workspace/project-1)
Finished test [unoptimized + debuginfo] target(s) in 24.19s
Running tests/harness.rs (target/debug/deps/integration_tests-b18d1acdbb52c03e)
running 3 tests
test can_get_contract_id ... ok
test can_inint_counter ... ok
test can_inc_counter ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.39s
Congratulations, youβve just compiled and tested your first Sway contract.
Fuel-core Instance
Just as a last note to wrap this guide up. If you want to start a local fuel core instance use fuel-core run --db-type in-memory
.
$ fuel-core run --db-type in-memory
...(omitted)...
2022-12-13T03:24:39.632980Z INFO fuel_core::cli::run: 213: Fuel Core version v0.15.0
2022-12-13T03:24:39.637122Z INFO new_node: fuel_core::service::graph_api: 111: Binding GraphQL provider to 127.0.0.1:4000