This guide is for the people who integrate the Fuel Rust SDK at scale, operate CI that depends on it, contribute to it, or need to reason about its internals and security model. It documents the workspace architecture, every crate's responsibility, the abigen codegen pipeline, the transaction lifecycle, the node‑launching machinery, configuration surfaces, security considerations, and operational concerns.
It is derived from the SDK source at version 0.77.0 (commit b404424). For day‑to‑day usage, see the companion User Guide.
Note
fuels-rsis a library, not a deployable service. "Operations" here means: how it builds, how it talks to nodes, how to run its test suite and CI reliably, how to upgrade it safely, and how to contribute. There is no server to deploy — your application embeds the SDK.
1. Executive Summary
fuels-rs is the Rust SDK for the Fuel blockchain, published to crates.io as fuels. It is a Cargo workspace of 7 published library crates plus example crates, a WASM test crate, end‑to‑end tests, and four internal maintenance binaries.
| Fact | Value |
|---|---|
| Workspace version | 0.77.0 |
| Rust edition | 2024 (MSRV 1.93.0) |
| License | Apache-2.0 |
| Resolver | Cargo resolver 2 |
| Target node | fuel-core 0.48.x |
| VM / tx crates | fuel-vm / fuel-tx 0.66.x |
| ABI tooling | fuel-abi-types 0.15.3 |
| Published crates | fuels, fuels-core, fuels-accounts, fuels-programs, fuels-code-gen, fuels-macros, fuels-test-helpers |
| Primary consumers | Rust developers building apps & contract test harnesses on Fuel |
The SDK's value proposition is type‑safe, compile‑time‑checked interaction with Sway programs: a procedural macro (abigen!) turns a JSON ABI into Rust types and methods, so contract calls are checked by rustc rather than failing at runtime.
Important
This repository is the framework, not an application that uses it. Downstream users add
fuels = "0.x"to theirCargo.toml. "Installation" means adding the crate and installing the Fuel toolchain (forc,fuel-core) — not running a binary from this repo. The fourmain.rsfiles inscripts/are maintenance tooling, not the product.
2. Architecture
2.1 Workspace layout
fuels-rs/
├── packages/ ← THE PRODUCT (7 published crates)
│ ├── fuels ← umbrella facade + prelude
│ ├── fuels-core ← types, codec, traits, tx builders
│ ├── fuels-code-gen ← ABI → Rust code generation (compile-time)
│ ├── fuels-macros ← proc-macros (abigen!, derives, setup_program_test!)
│ ├── fuels-accounts ← wallets, signers, provider, predicate
│ ├── fuels-programs ← contracts, scripts, calls, executables
│ └── fuels-test-helpers ← launch fuel-core, build test wallets
├── examples/ ← reference crates (also doc-test sources)
│ ├── contracts, wallets, predicates, providers, codec,
│ │ types, macros, cookbook, debugging, rust_bindings
├── e2e/ ← integration tests + Sway test fixtures
│ ├── tests/*.rs ← the real cross-feature test suite
│ └── sway/** ← ~150 small Sway programs (test fixtures, NOT product)
├── wasm-tests/ ← WASM compatibility tests (cdylib)
├── scripts/ ← maintenance binaries (NOT shipped)
│ ├── change-log, check-docs, fuel-core-version, versions-replacer
└── docs/ ← mdbook user documentation
Note
The bulk of the repository's file count — roughly 150 of the 186 "important" folders — is
e2e/sway/**: tiny Sway contracts/scripts/predicates used as test fixtures. They are not part of the published SDK. The product surface is entirely inpackages/.
2.2 Crate dependency layers
The seven crates form a clean dependency stack. Document and reason about them by responsibility layer, not alphabetically:
┌──────────────────────────────────────────────┐
Layer 5│ fuels (facade) │ umbrella crate + prelude
└───────────────┬──────────────────────────────┘
│ re-exports everything below
┌───────────────┴───────────────┐
Layer 4│ fuels-programs │ Contract/Script/Predicate calls,
│ (contracts, scripts, calls) │ executables, loaders, tx debug
└───────────────┬───────────────┘
┌───────────────┴───────────────┐
Layer 3│ fuels-accounts │ Provider, Wallet, Predicate,
│ (provider, wallets, signers) │ signers, keystore, coin cache
└───────────────┬───────────────┘
┌───────────────┴───────────────┐
Layer 2│ fuels-core │ types, codec (ABI enc/dec),
│ (types, codec, traits) │ traits, transaction builders
└───────────────┬───────────────┘
┌───────────────┴───────────────────────────────┐
Layer 1│ fuels-code-gen + fuels-macros (compile-time) │ JSON ABI → Rust code
└────────────────────────────────────────────────┘
Cross-cutting: fuels-test-helpers (depends on accounts + core) launches nodes & wallets.
| Layer | Crate(s) | Responsibility | Who cares |
|---|---|---|---|
| 5 — Facade | fuels |
Re‑export everything; provide prelude |
End users |
| 4 — Programs | fuels-programs |
Deploy/call contracts, run scripts, predicate calls, loaders | End users |
| 3 — Accounts | fuels-accounts |
Node connection, wallets, signers, predicates | End users + ops |
| 2 — Core | fuels-core |
Types, ABI codec, traits, tx builders | Everyone (foundational) |
| 1 — Codegen | fuels-code-gen, fuels-macros |
Compile‑time ABI→Rust | Contributors |
| — Test | fuels-test-helpers |
Launch fuel-core, build wallets |
Test authors |
The dependency flow is strictly downward (no cycles): fuels-programs depends on fuels-accounts and fuels-core; fuels-accounts depends on fuels-core; fuels-core depends on fuels-code-gen and fuels-macros. The facade fuels depends on all of them.
2.3 The facade crate (fuels)
packages/fuels/src/lib.rs contains almost no logic — it is a re‑export hub organised into namespaced modules, plus a prelude:
| Module | Re‑exports |
|---|---|
fuels::tx |
fuel_tx types: ConsensusParameters, Receipt, TxParameters, StorageSlot, … |
fuels::client |
fuel_core_client FuelClient, pagination types (std only) |
fuels::macros |
fuels_macros::* (abigen!, derives, setup_program_test!) |
fuels::programs |
fuels_programs::* |
fuels::core |
fuels_core::{Configurable, Configurables, codec, constants, offsets, traits} |
fuels::crypto |
fuel_crypto::{Hasher, Message, PublicKey, SecretKey, Signature} |
fuels::accounts |
fuels_accounts::* |
fuels::types |
fuels_core::types::* |
fuels::test_helpers |
fuels_test_helpers::* (std only) |
fuels::prelude |
The curated "import‑everything" set used to bootstrap projects |
Tip
When tracing where a prelude symbol actually lives, follow the facade's
pub use. For example,Wallet→fuels_accounts::wallet::Wallet,abigen→fuels_macros,ABIEncoder→fuels_core::codec.
3. The Codegen Pipeline (Core IP)
The defining feature of fuels-rs is compile‑time generation of Rust bindings from a Sway program's JSON ABI. This is a two‑crate, compile‑time pipeline.
3.1 fuels-code-gen — the generator
fuels-code-gen turns an ABI into Rust token streams. Public surface (program_bindings):
pub use program_bindings::{Abi, Abigen, AbigenTarget, ProgramType};
ProgramType—Contract | Script | Predicate.AbigenTarget— one program to generate (name + ABI + type).Abigen— the engine that produces theTokenStream.- Sub‑modules:
custom_types(struct/enum generation),resolved_type(Sway→Rust type resolution),generated_code,utils.
It depends on fuel-abi-types (parses/validates the JSON ABI), proc-macro2, quote, syn, and Inflector (identifier casing). Its encode_fn_selector helper computes the length‑prefixed selector bytes:
pub fn encode_fn_selector(name: &str) -> Vec<u8> {
let bytes = name.as_bytes().to_vec();
let len = bytes.len() as u64;
[len.to_be_bytes().to_vec(), bytes].concat()
}
fuels-code-gen has its own lightweight Error(String) type with From impls for serde_json::Error, io::Error, proc_macro2::LexError, and fuel_abi_types::error::Error.
3.2 fuels-macros — the proc‑macro front end
fuels-macros is a proc-macro = true crate that wraps fuels-code-gen and exposes the user‑facing macros:
| Macro | Kind | Purpose |
|---|---|---|
abigen! |
function‑like | Generate bindings for one or more programs |
setup_program_test! |
function‑like | Reduce test boilerplate (wallets/abigen/deploy) |
#[derive(Tokenizable)] |
derive | Implement Tokenizable for a type |
#[derive(Parameterize)] |
derive | Implement Parameterize for a type |
#[derive(TryFrom)] |
derive | Decode bytes directly into a type |
Internally it is organised into abigen/, derive/, setup_program_test/, and parse_utils/. The setup_program_test! parser lives under setup_program_test/parsing/commands and recognises the Options, Wallets, Abigen, Deploy, and LoadScript commands.
3.3 Generated‑code shape
abigen! emits an abigen_bindings module containing one sub‑module per program (named after the binding's name), each holding that program's custom types and its binding struct. Types that are identical (same name and definition) across programs are lifted into a shared_types module and re‑exported back into each consumer.
Note
Type sharing only triggers when the name and the resolved definition match. Generating all programs in a single
abigen!call is therefore important: it is the only way the generator sees all programs at once and can deduplicate shared types and avoid name collisions.
3.4 Lifecycle
forc build ──► *.bin (FuelVM bytecode) + *-abi.json (JSON ABI)
│
abigen!(...) at compile time
│
fuels-macros ──► fuels-code-gen ──► fuel-abi-types (parse)
│
generated Rust types + bindings
│
rustc compiles your call sites
The generated code is purely a compile‑time artifact; there is no runtime codegen.
4. The Core Layer (fuels-core)
fuels-core is the foundational crate every other crate depends on. Modules: codec, traits, types, plus top‑level Configurable/Configurables and utilities.
4.1 Codec
fuels-core::codec implements the Fuel ABI argument encoding spec:
| Item | Role |
|---|---|
ABIEncoder |
Encode Tokens → bytes; configurable via EncoderConfig |
ABIDecoder |
Decode bytes + ParamType → Token; configurable via DecoderConfig |
ABIFormatter |
Human‑readable decoding from a JSON ABI (used in tx debugging) |
function_selector |
Compute function selectors |
logs |
LogDecoder, LogId, LogResult for decoding contract logs |
try_from_bytes<T> |
Decode bytes directly into T: Parameterize + Tokenizable |
EncoderConfig/DecoderConfig expose max_depth and max_tokens as resource‑exhaustion guards.
4.2 Traits
Three load‑bearing traits live in fuels-core::traits:
| Trait | Contract |
|---|---|
Tokenizable |
into_token() / from_token() — convert a Rust value ↔ ABI Token |
Parameterize |
param_type() — describe a type's ABI schema (ParamType) |
Signer |
sign(message) + address() — produce signatures |
All abigen!‑generated types implement Tokenizable + Parameterize. The SDK also implements them for built‑in and many foreign types.
4.3 Types
fuels-core::types re‑exports the primitive fuel types from fuel-types (Address, AssetId, Bytes32, ContractId, Nonce, Salt, SubAssetId, Word, …) and adds SDK‑owned wrappers, param_types, transaction_builders, tx_status, tx_response, errors, checksum_address, and method_descriptor.
4.4 Configurables
Configurable/Configurables model deploy‑time/runtime constant overrides. A Configurable is { offset: u64, data: Vec<u8> }; Configurables is a vector of these plus an update_constants_in(&mut [u8]) that patches a binary in place. with_shifted_offsets adjusts offsets (used when code is relocated, e.g. loaders), with checked arithmetic that errors on overflow/underflow.
4.5 The error model
The SDK's unified error type is an enum with five variants:
| Variant | Meaning |
|---|---|
Error::IO(String) |
Filesystem/network I/O failure |
Error::Codec(String) |
ABI encode/decode failure |
Error::Transaction(Reason) |
Transaction failed (Reason::Failure, Reason::Validation, …) |
Error::Provider(String) |
Node/provider error |
Error::Other(String) |
Everything else |
Result<T> is std::result::Result<T, Error>. The error! macro constructs typed errors; Context/with_context add human‑readable context. A prepend_msg combinator (in fuels-programs) prefixes messages while preserving the variant.
Tip
When matching on call failures, the most useful pattern is
Err(Error::Transaction(Reason::Failure { .. }))(the contract reverted/execution failed) vsReason::Validation(the node rejected the tx before execution — bad witnesses, insufficient inputs, etc.).
5. The Accounts Layer (fuels-accounts)
This crate owns everything to do with identity, node connection, and resource selection.
5.1 The account traits
pub trait ViewOnlyAccount: Send + Sync {
fn address(&self) -> Address;
fn try_provider(&self) -> Result<&Provider>;
async fn get_coins(&self, asset_id: AssetId) -> Result<Vec<Coin>>;
async fn get_asset_balance(&self, asset_id: &AssetId) -> Result<u128>;
async fn get_balances(&self) -> Result<HashMap<String, u128>>;
async fn get_spendable_resources(&self, asset_id, amount, excluded) -> Result<Vec<CoinType>>;
async fn get_asset_inputs_for_amount(...) -> Result<Vec<Input>>;
fn get_asset_outputs_for_amount(...) -> Vec<Output>;
async fn adjust_for_fee<Tb: TransactionBuilder>(&self, tb, used_base_amount) -> Result<()>;
// ...
}
pub trait Account: ViewOnlyAccount {
fn add_witnesses<Tb: TransactionBuilder>(&self, tb) -> Result<()>;
async fn transfer(&self, to, amount, asset_id, tx_policies) -> Result<TxResponse>;
async fn force_transfer_to_contract(&self, to, balance, asset_id, tx_policies) -> Result<TxResponse>;
async fn withdraw_to_base_layer(&self, to, amount, tx_policies) -> Result<WithdrawToBaseResponse>;
}
Both Wallet and Predicate implement these. adjust_for_fee is the linchpin of fee handling: it computes the missing base‑asset amount (max_fee + reserved − available), pulls additional base inputs if needed, and adds a base‑asset change output.
5.2 The Wallet typestate
Wallet<S = Unlocked<PrivateKeySigner>> is a typestate machine:
pub struct Wallet<S> { state: S, provider: Provider }
pub struct Unlocked<S> { signer: S } // has a signer → Account + ViewOnlyAccount
pub struct Locked { address: Address } // no signer → ViewOnlyAccount only
Wallet<Unlocked<S>>::new(signer, provider)— a signing wallet (generic over anyS: Signer).Wallet::random(rng, provider)— randomPrivateKeySigner.wallet.lock()→Wallet<Locked>— drops the signer, keeps the address.
This is a compile‑time guarantee: you cannot call signing operations on a Wallet<Locked> because the methods only exist on Wallet<Unlocked<S>>.
5.3 Signers
fuels-accounts::signers provides:
| Signer | Module | Feature flag |
|---|---|---|
PrivateKeySigner |
signers::private_key |
(always) |
| AWS KMS signer | signers::kms |
signer-aws-kms |
| Google KMS signer | signers::kms |
signer-google-kms |
FakeSigner |
signers::fake |
(always) |
Derivation constants live in signers::derivation: BIP44_PURPOSE = "44'", COIN_TYPE = "1179993420'", DEFAULT_DERIVATION_PATH = "m/44'/1179993420'/0'/0/0".
5.4 The Provider
Provider wraps a CachedClient<RetryableClient> (and, with coin-cache, an Arc<Mutex<CoinsCache>>). It is the read/submit gateway to a node, layered:
Provider
└── CachedClient (caches consensus_parameters / node_info via TtlConfig)
└── RetryableClient (retries on io::Error per RetryConfig/Backoff)
└── FuelClient (fuel-core-client; GraphQL over HTTP, cynic-typed)
Construction: Provider::connect(url), Provider::connect_with_fallbacks(&[urls]), Provider::from(socket_addr). The raw FuelClient is reachable via provider.client() for queries the SDK doesn't wrap.
Notable provider responsibilities:
- Transaction submission —
send_transaction,send_transaction_and_await_commit,send_transaction_and_await_status(streaming statuses incl. preconfirmation). Before sending,prepare_transaction_for_sendingprecomputes the tx, and for predicate‑using transactions estimates and validates predicates. - Dry runs —
dry_run,dry_run_opt,dry_run_multiple(and theDryRunnertrait impl used for gas/variable‑output estimation). - Resource queries —
get_coins,get_spendable_resources(viaResourceFilter),get_balances,get_contract_balances, paginated at 100 results/request. - Cost estimation —
estimate_transaction_costreturnsTransactionCost { gas_price, metered_bytes_size, total_fee, script_gas, total_gas }, applying a tolerance multiplier. - Chain/block/tx queries —
chain_info,consensus_parameters,latest_block_height,produce_blocks,get_transaction_by_id,get_message_proof. - Caching —
TtlConfig,set_cache_ttl,clear_cache. - Retry —
with_retry_config(RetryConfig).
ResourceFilter and TransactionCost are documented inline (anchored for the user docs):
pub struct ResourceFilter {
pub from: Address,
pub asset_id: Option<AssetId>,
pub amount: u128,
pub excluded_utxos: Vec<UtxoId>,
pub excluded_message_nonces: Vec<Nonce>,
}
5.5 The coin cache
With the coin-cache feature, the provider keeps a CoinsCache (default TTL 30 s) keyed by (Address, AssetId). On submit it records the spent coins; on a subsequent spend it excludes recently‑used coins and errors if you try to re‑spend one still in the cache. Failed/squeezed‑out transactions evict their coins.
Note
This is what protects you from accidentally double‑spending a UTXO across rapid back‑to‑back transactions. The cost is a 30‑second window where a just‑submitted coin appears unavailable. Tune via
CoinsCache::new(ttl)semantics; disable by building withoutcoin-cache.
5.6 Predicates
Predicate holds { address, code, data, provider }. Its address is derived purely from bytecode (fuel_tx::Input::predicate_owner(code)), so changing code or configurables changes the address (with_configurables re‑derives it). It implements ViewOnlyAccount and Account; its get_asset_inputs_for_amount produces Input::resource_predicate inputs (carrying code + data) rather than signed inputs.
5.7 Keystore
The Keystore (feature keystore) wraps eth-keystore to encrypt/decrypt SecretKeys with a password into a directory. save_key returns a UUID; load_key(uuid, password) recovers it. KeySaved is Zeroize/ZeroizeOnDrop.
5.8 GraphQL schema
fuels-accounts/src/schema/schema.sdl is the GraphQL SDL the cached client uses (via cynic). A test asserts it is byte‑identical to fuel_core_client::SCHEMA_SDL, catching schema drift between the SDK and the node.
6. The Programs Layer (fuels-programs)
This crate turns ABIs and bytecode into deployable/callable programs.
6.1 Contracts
Contract<Code> is generic over its code state (Regular vs Loader):
Contract::load_from(path, LoadConfiguration)— load a binary, auto‑load storage slots.Contract::regular(code, salt, storage_slots)— construct manually.contract_id()/state_root()/code_root()— computed without deploying, viacompute_contract_id_and_state_root(FuelContract::id(salt, code_root, state_root)).with_salt,with_storage_slots,with_configurables.deploy(&account, TxPolicies),deploy_if_not_exists(...),smart_deploy(...).convert_to_loader(max_words_per_blob)↔revert_to_regular().loader_from_blobs(...)/loader_from_blob_ids(...),upload_blobs(...).
LoadConfiguration and StorageConfiguration drive loading; storage autoload reads <name>-storage_slots.json and fails with an Error::Other if the file is missing and autoload is on.
6.2 Calls
fuels-programs::calls is the calling machinery:
| Item | Role |
|---|---|
CallHandler |
Builds, submits, simulates a call; new_multi_call for bundles |
contract_call / script_call |
The two call kinds |
receipt_parser |
Parse receipts into return values |
Execution |
Simulation context: realistic() vs state_read_only(), .at_height(h) |
traits, utils |
Shared call helpers |
Execution defaults to realistic(). state_read_only() disables most validation (fake witnesses, fake gas, extra variable outputs) so unfunded accounts can read state.
6.3 Executables (scripts/predicates as blobs)
Executable<State> (Regular | Loader) handles pre‑uploading script/predicate code:
Executable::<Regular>::load_from(path)/from_bytes(code),with_configurables(...).convert_to_loader()validates the code can become a loader, then yieldsExecutable<Loader>.Executable<Loader>::blob()extracts the code blob (minus data section);upload_blob(account)uploads it (idempotent — skips ifblob_exists).- Configurables are applied to the data section; the blob excludes the data section so the same blob is reused across configurations.
6.4 Transaction debugging
fuels-programs::debug decodes raw FuelVM script bytecode:
pub enum ScriptType {
ContractCall(Vec<ContractCallData>),
Loader { script: ScriptCallData, blob_id: [u8; 32] },
Other(ScriptCallData),
}
ScriptType::detect(script, data) disassembles the script (via fuel_asm), recognises a contract‑call instruction pattern, a loader pattern, or falls back to Other. Combined with ABIFormatter, this reconstructs human‑readable call signatures from on‑chain transactions.
6.5 Responses
fuels-programs::responses defines CallResponse<D> (value, receipts, gas_used, tx_id) and the submit‑response types for the submit/await‑later flow.
6.6 Assembly
fuels-programs::assembly (internal) builds the FuelVM instruction sequences: contract_call (the call‑opcode layout, gas‑forwarding variants), cursor, and script_and_predicate_loader (loader instruction generation, configurable‑section offset math).
7. The Test‑Helpers Layer (fuels-test-helpers)
fuels-test-helpers launches fuel-core and builds wallets/coins for tests.
7.1 Two node back ends
pub struct FuelService { /* ... */ }
impl FuelService {
pub async fn start(node_config, chain_config, state_config) -> Result<Self>;
pub async fn stop(&self) -> Result<State>;
pub fn bound_address(&self) -> SocketAddr;
}
- Default (binary) —
fuel_bin_servicespawns thefuel-corebinary found onPATH(viawhich), parses its stderr forBinding GraphQL provider to <addr>, and manages the child process (kill_on_drop). Times out after 30 s waiting for the bound address. fuel-core-libfeature —service.rsrunsfuel-corein‑process viaCoreFuelService::new_nodewith a fullServiceConfig(GraphQL query limits, combined DB config, gas‑price config, …).
The binary back end translates NodeConfig into CLI args (--db-type, --poa-instant/--poa-interval-period, --utxo-validation, --historical-execution, --debug, --starting-gas-price, --graphql-request-body-bytes-limit, --native-executor-version, …).
7.2 Node, chain, and state config
pub struct NodeConfig {
pub max_database_cache_size: Option<usize>,
pub database_type: DbType, // InMemory | RocksDb(Option<PathBuf>)
pub utxo_validation: bool, // default true
pub historical_execution: bool, // default false
pub debug: bool, // default true
pub block_production: Trigger, // default Instant
pub vm_backtrace: bool,
pub silent: bool, // default true
pub starting_gas_price: u64, // default 1
pub graphql_request_body_bytes_limit: u64, // default u64::MAX
}
Trigger = Instant | Never | Interval { block_time } | Open { period }. ChainConfig and StateConfig are re‑exported from fuel-core-chain-config. The default test chain raises tx size and contract size limits (10 MB tx, 1 MB contract) over the stock testnet config.
7.3 Wallet & coin helpers
| Helper | Produces |
|---|---|
launch_provider_and_get_wallet() |
1 funded Wallet + node |
launch_custom_provider_and_get_wallets(WalletsConfig, NodeConfig?, ChainConfig?) |
N wallets + node |
setup_test_provider(coins, messages, NodeConfig?, ChainConfig?) |
a Provider |
setup_single_asset_coins(owner, asset, n, amount) |
Vec<Coin> |
setup_multiple_assets_coins(owner, n_assets, coins_per, amount) |
(Vec<Coin>, Vec<AssetId>) |
setup_custom_assets_coins(owner, &[AssetConfig]) |
Vec<Coin> |
WalletsConfig defaults: DEFAULT_NUM_WALLETS = 10, DEFAULT_NUM_COINS = 1, DEFAULT_COIN_AMOUNT = 1_000_000_000. Generated wallet secret keys are deterministic (a padded counter), giving reproducible addresses.
Important
Deterministic test wallets are a feature (reproducibility), not a weakness — but it also means test keys are trivially predictable. Never reuse a test‑helper wallet on a real network.
8. System Requirements
| Concern | Requirement |
|---|---|
| Rust toolchain | stable, MSRV 1.93.0, edition 2024 |
forc |
To compile Sway programs and produce ABIs/binaries |
fuel-core |
On PATH (binary back end) or the fuel-core-lib feature |
| WASM (optional) | wasm32-unknown-unknown target + wasm-pack |
| OS | Any Tier‑1 Rust platform; fuel-core binaries for Linux/macOS |
| Network | Outbound HTTP/GraphQL to the target node; testnet faucet for funding |
9. Configuration Reference
There are no environment variables or config files the SDK reads at runtime in your application — configuration is via Rust builder types. The relevant surfaces:
9.1 Transaction policies (TxPolicies)
| Setter | Effect |
|---|---|
with_tip(u64) |
Block‑producer tip |
with_witness_limit(u64) |
Max witness data size |
with_maturity(u32) |
Earliest inclusion block |
with_expiration(u32) |
Latest inclusion block |
with_max_fee(u64) |
Fee ceiling |
with_script_gas_limit(u64) |
Script gas cap (auto‑estimated if unset) |
9.2 Call parameters (CallParameters)
| Setter | Effect |
|---|---|
with_amount(u64) |
Coins forwarded to the call |
with_asset_id(AssetId) |
Asset forwarded |
with_gas_forwarded(u64) |
Gas forwarded to the call (bounded by tx limit) |
9.3 Codec limits
| Type | Fields |
|---|---|
EncoderConfig |
max_depth, max_tokens |
DecoderConfig |
max_depth, max_tokens |
9.4 Node config (test helpers)
See §7.2 — NodeConfig, Trigger, DbType, plus ChainConfig/StateConfig from fuel-core-chain-config.
9.5 Retry config
RetryConfig::new(max_attempts, Backoff) where Backoff ∈ { Linear, Exponential, Fixed }(Duration).
9.6 Default constants (selected)
| Constant | Value | Crate |
|---|---|---|
DEFAULT_NUM_WALLETS |
10 |
test-helpers |
DEFAULT_NUM_COINS |
1 |
test-helpers |
DEFAULT_COIN_AMOUNT |
1_000_000_000 |
test-helpers |
DEFAULT_DERIVATION_PATH |
m/44'/1179993420'/0'/0/0 |
accounts |
DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE |
0.50 |
programs |
| Coin cache TTL | 30 s |
accounts |
| Pagination page size | 100 |
accounts |
10. Security Analysis
fuels-rs is a client library for a blockchain; its security surface is about key handling, transaction safety, and trust boundaries — not network‑exposed endpoints.
10.1 Key material
PrivateKeySignerholds the secret key in process memory.SecretKey(fromfuel-crypto) and the keystore'sKeySavedareZeroize/ZeroizeOnDrop, so they scrub on drop — but copies you make are your responsibility.- Keystore encrypts keys at rest with a password via
eth-keystore. The master password and the encrypted file together are sufficient to recover the key. - KMS signers (AWS/Google) keep the key in the HSM/KMS; the SDK never sees the private key. This is the recommended posture for production services.
- Locked wallets (
Wallet<Locked>) carry no signer — use them for read‑only paths to minimise the time keys are resident in memory. The docs explicitly recommend minimisingWallet<Unlocked<S>>usage.
Caution
Deterministic test wallets (predictable keys derived from a counter) and
FakeSigner(impersonation whenutxo_validation = false) are test‑only mechanisms. Using either against a real network is a critical mistake.
10.2 Transaction safety
force_transfer_to_contractcan permanently lose funds if the receiving contract doesn't account for them — flaggedCAUTIONin source.- Payability enforcement —
call_paramsreturns aResultand refuses to forward a non‑zero amount to a non‑#[payable]method, preventing a class of accidental loss. - Coin cache prevents accidental double‑spend of in‑flight UTXOs.
- Builder immutability — once a transaction is built, it can't be mutated, so the transaction ID can't silently change after signing.
- Predicate validation — the provider estimates and validates predicates before submitting predicate‑spending transactions.
10.3 Input parsing / untrusted data
The codec enforces max_depth/max_tokens limits to bound resource use when decoding untrusted ABI data. The tx‑debug decoder (ScriptType::detect, ContractCallData::decode) is written defensively: it returns typed errors (not panics) on malformed scripts, truncated data sections, invalid UTF‑8 function selectors, and bad call‑data offsets (see its extensive negative tests).
10.4 Schema/version pinning
The SDK pins a supported fuel-core version (SUPPORTED_FUEL_CORE_VERSION) and asserts its bundled GraphQL SDL matches fuel-core-client's. Running against a mismatched node can produce subtle consensus‑parameter or schema errors; pin SDK and node together.
10.5 Supply chain
The workspace pins exact dependency versions in the root Cargo.toml [workspace.dependencies]. KMS, keystore, and node‑lib dependencies are gated behind optional features, so a default build pulls a smaller surface. Audit the enabled feature set when integrating.
11. Operations
11.1 Building & testing the workspace
# Build all Sway test fixtures first (required for the Rust tests):
forc build --release --path e2e
# Run the whole suite:
cargo test
# All targets + features:
cargo test --all-targets --all-features
# WASM tests:
cd packages/wasm-tests && wasm-pack test --node
Warning
The Rust integration tests reference compiled Sway artifacts under
e2e/sway/**/out/release/. If you skipforc build --release --path e2e, tests fail to find*.bin/*-abi.json. After pulling changes, rebuild fixtures.
11.2 CI considerations
- Install
fuelupand a pinned toolchain soforcandfuel-coreare available and version‑matched to the SDK. - Alternatively enable
fuel-core-libto avoid needing afuel-corebinary (heavier build, no external process). - The repo ships a
scripts/check-docsbinary that verifies every{{#include …:anchor}}indocs/srcresolves to a real ANCHOR in the examples/tests, and that every doc file is listed inSUMMARY.md. Run it to catch documentation drift. scripts/fuel-core-version(aclapCLI) andscripts/versions-replacer(anarghCLI) keep the pinnedfuel-coreversion and workspace versions consistent.
11.3 Maintenance binaries
Binary (scripts/) |
Tooling | Purpose |
|---|---|---|
change-log |
octocrab, dialoguer |
Generate changelog entries from GitHub |
check-docs |
regex |
Validate mdbook include anchors & SUMMARY coverage |
fuel-core-version |
clap, toml |
Read/update the pinned fuel-core version |
versions-replacer |
argh, cargo_metadata |
Replace workspace version strings |
These are publish = false — not part of the released SDK.
11.4 Upgrading the SDK
- Bump the
fuelsversion in yourCargo.toml; runcargo update -p fuels. - Align your
fuel-core/forctoolchain with the SDK's supportedfuel-core(usefuelup). - Rebuild Sway artifacts (
forc build --release) since ABI/binary formats can change betweenforcversions. - Run your test suite; watch for
Reason::Validationerrors (consensus‑parameter changes) and codec changes.
Note
A
ScriptCallDatafield comment in source flags a planned rename (data_section_offset→data_section_offset/configurable_section_offset) tied to Sway binary‑format changes (sway 0.66.5 vs 0.66.6+). Expect such churn aroundforc/swayupgrades; pin toolchains in CI.
12. Repository / Module Reference (for contributors)
| Crate | Key modules | Notes |
|---|---|---|
fuels |
lib.rs (facade + prelude) |
~almost no logic; re‑exports |
fuels-core |
codec, traits, types, Configurables, utils::{constants, offsets} |
Foundation |
fuels-code-gen |
program_bindings::{abigen, custom_types, resolved_type, generated_code}, error, utils |
ABI→Rust |
fuels-macros |
abigen, derive, setup_program_test, parse_utils |
proc‑macros |
fuels-accounts |
account, wallet, provider, predicate, signers::{private_key, kms, fake, derivation}, keystore, coin_cache, schema |
Identity + node |
fuels-programs |
contract::{regular, loader, storage}, calls::{call_handler, contract_call, script_call, receipt_parser}, executable, responses, debug, assembly |
Programs |
fuels-test-helpers |
service, fuel_bin_service, accounts, node_types, wallets_config, utils |
Test infra |
Contribution flow
- Ensure an issue tracks the change; discuss large changes first.
- Branch from
master; write code + tests. - Run
forc build --release --path e2ethencargo test. - Add a
breakinglabel to the PR if it contains breaking changes. - Link the PR to its issue (
close #123/ref #456); CI (GitHub Actions) runs on authorized PRs.
Useful entry points for new contributors: Good First Issues and Help Wanted labels on the repo.
13. Improvements & Tech Debt (from the codebase & issue tracker)
These reflect open issues and in‑source TODOs at the documented commit.
| Area | Item | Source |
|---|---|---|
| Errors | Add context to errors throughout the SDK | #1597 |
| abigen | Renaming Sway imports breaks abigen!() |
#1703 |
| Errors | Convert a transaction error into contract errors when present | #1680 |
| Bindings | Defined Vec alias in Sway breaks the SDK harness |
#1603 |
| Provider | Support multiple RPCs in the provider | #1701 |
| Tx | Support unknown policies during tx deserialization | #1700 |
| Node | Set a static gas price via NodeConfig |
#1581 |
| Refactor | TransactionBuilder refactor |
#1661 |
| Refactor | Coin cache refactor | #1196 |
| API | Make max estimation fee tolerance configurable in call handlers | #1591 |
| Build | Remove include_bytes hack |
#1062 |
In‑source signals: e2e/Cargo.toml notes a planned removal (#1375) where ScriptTransaction/CreateTransaction use fuel_tx::Input without re‑exporting/converting it to a fuels‑owned type. The executable.rs loader has a #[deprecated] data_offset_in_code in favour of configurables_offset_in_code.
14. Quick Reference
Crate map (by responsibility)
fuels facade + prelude
├─ fuels-programs deploy/call contracts, scripts, predicates, loaders
├─ fuels-accounts Provider, Wallet, Predicate, signers, keystore, coin-cache
├─ fuels-core types, codec (ABI), traits, tx builders, Configurables, errors
├─ fuels-code-gen ABI → Rust (compile-time) ┐ codegen
├─ fuels-macros abigen!/derives/setup_program_test ┘
└─ fuels-test-helpers FuelService, wallet/coin setup
Transaction lifecycle
build inputs/outputs (account.get_asset_inputs/outputs_for_amount)
→ TransactionBuilder (Script/Create/Blob) [add_signer, with_tx_policies]
→ account.adjust_for_fee(&mut tb, used_base)
→ tb.build(&provider) (resolves witnesses, predicate offsets; immutable)
→ provider.prepare_transaction_for_sending (precompute, estimate/validate predicates)
→ provider.send_transaction[_and_await_commit]
→ TxStatus / receipts → CallHandler.get_response → CallResponse<D>
Error variants
Error::{ IO | Codec | Transaction(Reason::{Failure, Validation, …}) | Provider | Other }
Pinned versions
| Dependency | Version |
|---|---|
fuels (this SDK) |
0.77.0 |
fuel-core* |
0.48.0 |
fuel-vm / fuel-tx / fuel-types |
0.66.2 |
fuel-abi-types |
0.15.3 |
| Rust edition / MSRV | 2024 / 1.93.0 |
For the always‑current API, see docs.rs/fuels and the fuels‑rs book.