Start by installing the Rust toolchain .
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then, install the Fuel toolchain .
curl --proto '=https' --tlsv1.2 -sSf https://install.fuel.network/fuelup-init.sh | sh
Make sure you have the latest version of fuelup
by running the following command:
fuelup self update
Fetching binary from https://github.com/FuelLabs/fuelup/releases/download/v0.19.5/fuelup-0.19.5-aarch64-apple-darwin.tar.gz
Downloading component fuelup without verifying checksum
Unpacking and moving fuelup to /var/folders/tp/0l8zdx9j4s9_n609ykwxl0qw0000gn/T/.tmpiNJQHt
Moving /var/folders/tp/0l8zdx9j4s9_n609ykwxl0qw0000gn/T/.tmpiNJQHt/fuelup to /Users/.fuelup/bin/fuelup
Next, install the beta-4
toolchain with:
fuelup toolchain install beta-4
Finally, set the beta-4
toolchain as your default distribution with the following command:
fuelup default beta-4
default toolchain set to 'beta-4'
You can check your current toolchain anytime by running fuelup show
.
Having problems with this part? Post your question on our forum https://forum.fuel.network/ . To help you as efficiently as possible, include the output of this command in your post:
fuelup show.
We'll build a simple counter contract with two functions: one to increment the counter, and one to return the value of the counter.
Start by creating a new, empty folder. We'll call it fuel-project
.
mkdir fuel-project
Move inside of your fuel-project
folder:
cd fuel-project
Then create a contract project using forc:
forc new counter-contract
You will get this output:
To compile, use `forc build`, and to run tests use `forc test`
----
Read the Docs:
- Sway Book: https://docs.fuel.network/docs/sway
- Forc Book: https://docs.fuel.network/docs/forc
- Rust SDK Book: https://docs.fuel.network/docs/fuels-rs
- TypeScript SDK: https://docs.fuel.network/docs/fuels-ts
Join the Community:
- Follow us @SwayLang: https://twitter.com/SwayLang
- Ask questions on Discourse: https://forum.fuel.network/
Report Bugs:
- Sway Issues: https://github.com/FuelLabs/sway/issues/new
Here is the project that Forc
has initialized:
$ tree counter-contract
counter-contract
āāā Forc.toml
āāā src
āāā main.sw
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.
Open your project in a code editor and delete everything in src/main.sw
apart from the first line.
Every Sway file must start with a declaration of what type of program the file contains; here, we've declared that this file is a contract.
contract;
Next, we'll define a storage value. In our case, we have a single counter that we'll call counter
of type 64-bit unsigned integer and initialize it to 0.
storage {
counter: u64 = 0,
}
An ABI defines an interface for a contract. A contract must either define or import an ABI declaration. It is considered best practice to define your ABI in a separate library and import it into your contract because this allows callers of the contract to import and use the ABI in scripts to call your contract
For simplicity, we will define the ABI directly in the contract file itself.
abi Counter {
#[storage(read, write)]
fn increment();
#[storage(read)]
fn count() -> u64;
}
Below your ABI definition, you will write the implementation of the functions defined in your ABI.
impl Counter for Contract {
#[storage(read)]
fn count() -> u64 {
storage.counter.read()
}
#[storage(read, write)]
fn increment() {
let incremented = storage.counter.read() + 1;
storage.counter.write(incremented);
}
}
Note: storage.counter.read()
is an implicit return and is equivalent to return storage.counter.read();
.
Here's what your code should look like so far:
File: ./counter-contract/src/main.sw
contract;
storage {
counter: u64 = 0,
}
abi Counter {
#[storage(read, write)]
fn increment();
#[storage(read)]
fn count() -> u64;
}
impl Counter for Contract {
#[storage(read)]
fn count() -> u64 {
storage.counter.read()
}
#[storage(read, write)]
fn increment() {
let incremented = storage.counter.read() + 1;
storage.counter.write(incremented);
}
}
Navigate to your contract folder:
cd counter-contract
changed directory into `counter-contract`
Then run the following command to build your contract:
forc build
Compiled library "core".
Compiled library "std".
Compiled contract "counter-contract".
Bytecode size is 232 bytes.
Let's have a look at the content of the counter-contract
folder after building:
$ tree .
.
āāā Forc.lock
āāā Forc.toml
āāā out
ā āāā debug
ā āāā counter-contract-abi.json
ā āāā counter-contract-storage_slots.json
ā āāā counter-contract.bin
āāā src
āāā main.sw
We now have an out
directory that contains our build artifacts such as the JSON representation of our ABI and the contract bytecode.
We will start by adding a Rust integration test harness using a Cargo generate template. If this is your first time going through this quickstart, you'll need to install the cargo generate
command. In the future, you can skip this step as it will already be installed.
Let's start by running the installation command:
cargo install cargo-generate
Updating crates.io index...
installed package `cargo-generate v0.17.3`
Note: You can learn more about cargo generate by visiting its repository .
Now, let's generate the default test harness with the following:
cargo generate --init fuellabs/sway templates/sway-test-rs --name counter-contract
ā ļø Favorite `fuellabs/sway` not found in config, using it as a git repository: https://github.com/fuellabs/sway.git
š§ Destination: /home/user/path/to/counter-contract ...
š§ project-name: counter-contract ...
š§ Generating template ...
š§ Moving generated files into: `/home/user/path/to/counter-contract`...
āØ Done! New project created /home/user/path/to/counter-contract
Let's have a look at the result:
$ tree .
.
āāā Cargo.toml
āāā Forc.lock
āāā Forc.toml
āāā out
ā āāā debug
ā āāā counter-contract-abi.json
ā āāā counter-contract-storage_slots.json
ā āāā counter-contract.bin
āāā src
ā āāā main.sw
āāā tests
āāā harness.rs
We have two new files!
Cargo.toml
is the manifest for our new test harness and specifies the required dependencies including fuels
the Fuel Rust SDK. tests/harness.rs
contains some boilerplate test code to get us started, though doesn't call any contract methods just yet. Now that we have our default test harness, let's add some useful tests to it.
At the bottom of test/harness.rs
below the can_get_contract_id()
test, add the test_increment
test function below to verify that the value of the counter gets incremented:
use fuels::{prelude::*, types::ContractId};
// Load abi from json
abigen!(Contract(
name = "MyContract",
abi = "out/debug/counter-contract-abi.json"
));
async fn get_contract_instance() -> (MyContract<WalletUnlocked>, 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 storage_config =
StorageConfiguration::load_from("out/debug/counter-contract-storage_slots.json").unwrap();
let load_config = LoadConfiguration::default().with_storage_configuration(storage_config);
let id = Contract::load_from("./out/debug/counter-contract.bin", load_config)
.unwrap()
.deploy(&wallet, TxParameters::default())
.await
.unwrap();
let instance = MyContract::new(id.clone(), wallet);
(instance, id.into())
}
#[tokio::test]
async fn can_get_contract_id() {
let (_instance, _id) = get_contract_instance().await;
// Now you have an instance of your contract you can use to test each function
}
#[tokio::test]
async fn test_increment() {
let (instance, _id) = get_contract_instance().await;
// Increment the counter
instance.methods().increment().call().await.unwrap();
// Get the current value of the counter
let result = instance.methods().count().call().await.unwrap();
// Check that the current value of the counter is 1.
// Recall that the initial value of the counter was 0.
assert_eq!(result.value, 1);
}
Here is what your file should look like:
File: ./counter-contract/tests/harness.rs
use fuels::{prelude::*, types::ContractId};
// Load abi from json
abigen!(Contract(
name = "MyContract",
abi = "out/debug/counter-contract-abi.json"
));
async fn get_contract_instance() -> (MyContract<WalletUnlocked>, 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 storage_config =
StorageConfiguration::load_from("out/debug/counter-contract-storage_slots.json").unwrap();
let load_config = LoadConfiguration::default().with_storage_configuration(storage_config);
let id = Contract::load_from("./out/debug/counter-contract.bin", load_config)
.unwrap()
.deploy(&wallet, TxParameters::default())
.await
.unwrap();
let instance = MyContract::new(id.clone(), wallet);
(instance, id.into())
}
#[tokio::test]
async fn can_get_contract_id() {
let (_instance, _id) = get_contract_instance().await;
// Now you have an instance of your contract you can use to test each function
}
#[tokio::test]
async fn test_increment() {
let (instance, _id) = get_contract_instance().await;
// Increment the counter
instance.methods().increment().call().await.unwrap();
// Get the current value of the counter
let result = instance.methods().count().call().await.unwrap();
// Check that the current value of the counter is 1.
// Recall that the initial value of the counter was 0.
assert_eq!(result.value, 1);
}
Run cargo test
in the terminal:
cargo test
If all goes well, the output should look as follows:
...
running 2 tests
test can_get_contract_id ... ok
test test_increment ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.25s
It's now time to deploy the contract to the testnet. We will show how to do this using forc
from the command line, but you can also do it using the Rust SDK or the TypeScript SDK .
In order to deploy a contract, you need to have a wallet to sign the transaction and coins to pay for gas. First, we'll create a wallet.
If you haven't deployed a contract before, you'll need to set up a local wallet with the forc-wallet plugin. forc-wallet
is packaged alongside the default distributed toolchains when installed using fuelup, so you should already have this installed.
You can initialize a new wallet with the command below:
forc wallet new
After typing in a password, be sure to save the mnemonic phrase that is output.
Next, create a new wallet account with:
forc wallet account new
With this, you'll get a fuel address that looks something like this: fuel1efz7lf36w9da9jekqzyuzqsfrqrlzwtt3j3clvemm6eru8fe9nvqj5kar8
. Save this address as you'll need it to sign transactions when we deploy the contract.
If you need to list your accounts, you can run the command below:
forc wallet accounts
With your account address in hand, head to the testnet faucet to get some coins sent to your wallet.
Now that you have a wallet, you can deploy with forc deploy
and adding the --testnet
to target the latest network:
forc deploy --testnet
The terminal will ask for the password of the wallet:
Please provide the password of your encrypted wallet vault at "~/.fuel/wallets/.wallet":
Once you unlocked the wallet, the terminal will show a list of the accounts of this wallet:
Account 0 -- fuel18caanqmumttfnm8qp0eq7u9yluydxtqmzuaqtzdjlsww5t2jmg9skutn8n:
Asset ID Amount
0000000000000000000000000000000000000000000000000000000000000000 499999940
Just below the list, you'll see this prompt:
Please provide the index of account to use for signing:
Then you'll enter the number of the account of preference and press Y
when prompted to accept the transaction.
Finally, you will get back the network endpoint where the contract was deployed, a Contract ID
and the block where the transaction was signed.
Save the Contract ID
, as you'll need this later to connect the frontend.
With this ID, you can also head to the block explorer to confirm the contract was deployed.
Contract deploy-to-beta-4 Deployed!
Network: https://beta-4.fuel.network
Contract ID: 0x8342d413de2a678245d9ee39f020795800c7e6a4ac5ff7daae275f533dc05e08
Deployed in block 0x4ea52b6652836c499e44b7e42f7c22d1ed1f03cf90a1d94cd0113b9023dfa636
Here is the repo for this project . If you run into any problems, a good first step is to compare your code to this repo and resolve any differences.
Tweet us @fuel_network letting us know you just built a dapp on Fuel, you might get invited to a private group of builders, be invited to the next Fuel dinner, get alpha on the project, or something š.
Get help from the team by posting your question in the Fuel Forum .
Was this page helpful?