Forc stands for Fuel Orchestrator. Forc provides a variety of tools and commands for developers working with the Fuel ecosystem, such as scaffolding a new project, formatting, running scripts, deploying contracts, testing contracts, and more. If you're coming from a Rust background, forc
is similar to cargo
.
Creates a new project from scratch, setting up all necessary files for a complete Fuel project.
$ forc init my-fuel-project
$ cd my-fuel-project
$ tree
.
├── Cargo.toml
├── Forc.toml
├── src
│ └── main.sw
└── tests
└── harness.rs
Forc.toml
is the Forc manifest file, containing information about the project and dependencies. Cargo.toml
is the Rust project manifest file, used by the Rust-based tests package.
A src/
directory is created, with a single main.sw
Sway file in it.
A tests/
directory is also created. The Cargo.toml
in the root directory contains necessary Rust dependencies to enable you to write Rust-based tests using our Rust SDK (fuels-rs
). More on this in the Test
section down below.
Compile the sway files of the current project.
$ forc build
Compiled script "my-fuel-project".
Bytecode size is 28 bytes.
The output produced will depend on the project's program type. Building script, predicate and contract projects will produce their bytecode in binary format <project-name>.bin
. Building contracts and libraries will also produce the public ABI in JSON format <project-name>-abi.json
.
By default, these artifacts are placed in the out/
directory.
If a Forc.lock
file did not yet exist, it will be created in order to pin each of the dependencies listed in Forc.toml
to a specific commit or version.
Updates each of the dependencies so that they point to the latest suitable commit or version given their dependency declaration. The result is written to the Forc.lock
file.
You can write tests in Rust using our Rust SDK. These tests can be run using forc test
, which will look for Rust tests under the tests/
directory (which is created automatically with forc init
).
For example, let's write tests against this contract, written in Sway:
contract;
use std::storage::store_u64;
use std::storage::get_u64;
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 COUNTER_KEY = 0x0000000000000000000000000000000000000000000000000000000000000000;
impl TestContract for Contract {
fn initialize_counter(gas_: u64, amount_: u64, color_: b256, value: u64) -> u64 {
store_u64(COUNTER_KEY, value);
value
}
fn increment_counter(gas_: u64, amount_: u64, color_: b256, amount: u64) -> u64 {
let value = get_u64(COUNTER_KEY) + amount;
store_u64(COUNTER_KEY, value);
value
}
}
Our tests/harness.rs
file could look like:
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();
let client = Provider::launch(Config::local_node()).await.unwrap();
let contract_id = Contract::deploy(&compiled, &client).await.unwrap();
println!("Contract deployed @ {:x}", contract_id);
let contract_instance = MyContract::new(contract_id.to_string(), 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.unwrap());
// Call `increment_counter()` method in our deployed contract.
let result = contract_instance
.increment_counter(10)
.call()
.await
.unwrap();
assert_eq!(52, result.unwrap());
}
Then, in the root of our project, running forc 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:
$ forc test
running 1 test
test harness ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.64s
Alternatively, you could opt to write these tests in Typescript, using our Typescript SDK.
Run script project. Crafts a script transaction then sends it to a running node.
Deploy contract project. Crafts a contract deployment transaction then sends it to a running node.
Alternatively, you could deploy your contract programmatically using our SDK:
// 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 compiled = Contract::compile_sway_contract("./", salt).unwrap();
let client = Provider::launch(Config::local_node()).await.unwrap();
let contract_id = Contract::deploy(&compiled, &client).await.unwrap();
Format all Sway files of the current project.
Parse bytecode file into a debug format.
Example with the initial project created using forc init
:
$ forc build -o obj
Compiled script "my-fuel-project".
Bytecode size is 28 bytes.
my-second-project$ forc parse-bytecode obj
half-word byte op raw notes
0 0 JI(4) [144, 0, 0, 4] conditionally jumps to byte 16
1 4 NOOP [71, 0, 0, 0]
2 8 Undefined [0, 0, 0, 0] data section offset lo (0)
3 12 Undefined [0, 0, 0, 28] data section offset hi (28)
4 16 LW(46, 12, 1) [93, 184, 192, 1]
5 20 ADD(46, 46, 12) [16, 186, 227, 0]
6 24 RET(0) [36, 0, 0, 0]