# Fuel Rust SDK (`fuels`) — User Guide

The Fuel Rust SDK is a collection of Rust crates, published on crates.io as **`fuels`**, for building applications and tests against the [Fuel](https://fuel.network/) blockchain and the FuelVM. With it you can launch Fuel nodes, generate type‑safe Rust bindings from a Sway contract's ABI, deploy and call contracts, run scripts, work with predicates, craft and sign custom transactions, and manage wallets and assets — all from ordinary, compile‑time‑checked Rust.

This guide is task‑oriented: it shows you how to *use* the SDK. It is built directly from the SDK's source code and its own documentation at version `0.77.0`. For deployment architecture, crate internals, and security hardening, see the companion **Admin Guide**.

> [!NOTE]
> The `s` in `fuels` is intentional. The SDK family (`fuels-rs`, `fuels-ts`, …) is modelled on [The Ethers Project](https://github.com/ethers-io/ethers.js), so the crate is `fuels`, not `fuel`.

---

## 1. Quick Start

The fastest way to see the SDK work is to launch a short‑lived in‑process Fuel node, deploy a compiled contract, and call one of its methods.

Add the dependency to your `Cargo.toml`:

```toml
[dev-dependencies]
fuels = "0.77.0"
tokio = { version = "1", features = ["full"] }
```

Then, in a test:

```rust
use fuels::prelude::*;

#[tokio::test]
async fn quick_start() -> Result<()> {
    // 1. Launch a local node and get a funded test wallet linked to it.
    let wallet = launch_provider_and_get_wallet().await?;

    // 2. Generate type-safe bindings from a contract's JSON ABI.
    abigen!(Contract(
        name = "MyContract",
        abi = "e2e/sway/contracts/contract_test/out/release/contract_test-abi.json"
    ));

    // 3. Deploy the compiled contract binary.
    let contract_id = Contract::load_from(
        "e2e/sway/contracts/contract_test/out/release/contract_test.bin",
        LoadConfiguration::default(),
    )?
    .deploy(&wallet, TxPolicies::default())
    .await?
    .contract_id;

    // 4. Bind an instance and call a method.
    let contract = MyContract::new(contract_id, wallet);
    let response = contract.methods().initialize_counter(42).call().await?;

    assert_eq!(42, response.value);
    Ok(())
}
```

```text
running 1 test
test quick_start ... ok
```

Everything you need is re‑exported from `fuels::prelude::*`. The rest of this guide unpacks each step.

> [!TIP]
> `launch_provider_and_get_wallet()`, `abigen!`, `Contract::load_from`, `TxPolicies`, and `Result` all come from the prelude. Importing `fuels::prelude::*` is the recommended way to bootstrap any project.

---

## 2. Installation & Prerequisites

The SDK is a *library*, not an application — you install it as a crate and write Rust against it. To compile and test Sway contracts you also need the Fuel toolchain binaries.

### 2.1 Prerequisites

| Tool | Purpose | Install |
|------|---------|---------|
| Rust (latest `stable`) | Build your project and the SDK | [Fuel installation guide](https://docs.fuel.network/guides/installation/#installing-rust) |
| `forc` | Sway compiler & package manager (Sway's `cargo`) | via [`fuelup`](https://docs.fuel.network/guides/installation/#installing-the-fuel-toolchain-using-fuelup) |
| `fuel-core` | A Fuel full‑node implementation | via `fuelup` |

> [!IMPORTANT]
> The SDK shells out to a `fuel-core` binary on your `PATH` when launching test nodes (unless you enable the `fuel-core-lib` feature — see [§2.3](#23-feature-flags)). Make sure `fuel-core` is installed and discoverable, or test setup will fail with `no 'fuel-core' in PATH`.

The current SDK targets **`fuel-core` 0.48.x** and **`fuel-vm` 0.66.x**. The exact supported `fuel-core` version is pinned in the SDK (`SUPPORTED_FUEL_CORE_VERSION`); using a mismatched node may cause schema or consensus‑parameter errors.

### 2.2 Adding the crate

For an application that talks to a live node, add `fuels` to `[dependencies]`:

```toml
[dependencies]
fuels = "0.77.0"
```

For a project that only *tests* contracts, `fuels` is typically a `[dev-dependencies]` entry, since the test helpers and node launching are test‑time concerns.

In your Rust code:

```rust
use fuels::prelude::*;
```

### 2.3 Feature flags

`fuels` exposes a number of Cargo features. The most commonly used:

| Feature | Default? | What it enables |
|---------|----------|-----------------|
| `std` | ✅ | Full standard‑library support (node client, test helpers). Disable for `no_std`/WASM. |
| `coin-cache` | ✅ | In‑memory cache that prevents re‑spending coins submitted in recent transactions. |
| `test-helpers` | — | The `fuels-test-helpers` surface (node launching, wallet setup) outside of dev builds. |
| `fuel-core-lib` | — | Run `fuel-core` *as a library* — no separate binary needed. Pulls in heavy deps. |
| `rocksdb` | — | Persistent on‑disk node storage (combine with `fuel-core-lib`). |
| `accounts-keystore` | — | Encrypted on‑disk key storage (`Keystore`). |
| `accounts-signer-aws-kms` | — | Sign transactions with AWS KMS. |
| `accounts-signer-google-kms` | — | Sign transactions with Google Cloud KMS. |

Enable features the usual way:

```toml
fuels = { version = "0.77.0", features = ["fuel-core-lib", "rocksdb"] }
```

> [!TIP]
> Use `fuel-core-lib` when you cannot guarantee a `fuel-core` binary on the machine (e.g. CI without `fuelup`). It downloads and compiles the node as a dependency instead.

### 2.4 Starting a project from a Sway template

If you are starting from a Sway contract, the official `cargo generate` template scaffolds a Rust integration‑test harness for you:

```shell
cargo install cargo-generate
cargo generate --init fuellabs/sway templates/sway-test-rs --name my_fuel_project --force
forc build
cargo test
```

> [!NOTE]
> `--force` preserves the casing of your `--name` for the `{{project-name}}` placeholder; without it, `cargo-generate` lowercases and kebab‑cases the name.

---

## 3. Core Concepts

A handful of concepts recur throughout the SDK. Understanding them up front makes everything else click.

| Concept | What it is |
|---------|-----------|
| **Provider** | A read‑only connection to a `fuel-core` node. Queries balances, coins, blocks, transactions; submits transactions. |
| **Signer** | Something that can produce a signature. Built‑ins: `PrivateKeySigner`, AWS/Google KMS signers, `FakeSigner`. |
| **Wallet** | An account backed by a key. `Wallet<Unlocked<S>>` has a signer (can sign/send); `Wallet<Locked>` knows only an address (read‑only). |
| **Account** | A trait (`Account` / `ViewOnlyAccount`) implemented by both `Wallet` and `Predicate`. The common interface for balances and transfers. |
| **Contract** | A program with persistent storage deployed on‑chain. The `abigen!` macro generates a type‑safe binding for it. |
| **Script** | A one‑shot executable run inside a transaction. Has no storage. |
| **Predicate** | A pure program that guards an address; it returns a boolean and can own assets. |
| **Asset / Coin / UTXO** | Fuel uses a UTXO model. Each *coin* is a UTXO with an `amount` of a given `AssetId`. The **base asset** (`AssetId` `0x000…0`) pays fees. |
| **abigen!** | A procedural macro that turns a JSON ABI into Rust structs and methods, checked at compile time. |

> [!IMPORTANT]
> Fuel is a UTXO chain, not an account‑balance chain. "Your balance" for an asset is the *sum of the amounts of all your unspent coins* of that asset. The SDK hides most of this, but it explains why you'll see `Coin`, `UtxoId`, and "spendable resources" in the API.

---

## 4. Connecting to a Fuel Node

Every interaction needs a `Provider` pointing at a `fuel-core` node. You have two broad options:

1. **Connect to a real node** (testnet, mainnet, or your own `fuel-core`) — for building applications.
2. **Launch a short‑lived local node** with the test helpers — for contract testing.

### 4.1 Connecting to the testnet or an external node

```rust
use std::str::FromStr;
use fuels::{crypto::SecretKey, prelude::*};

// Create a provider pointing to the testnet.
let provider = Provider::connect("testnet.fuel.network").await?;

// Build a wallet from a private key.
let secret = SecretKey::from_str(
    "a1447cd75accc6b71a976fd3401a1f6ce318d27ba660b0315ee6ac347bf39568",
)?;
let wallet = Wallet::new(PrivateKeySigner::new(secret), provider);

// Print the address so the faucet can fund it.
dbg!(wallet.address().to_string());
```

To connect to a local node started with `fuel-core` directly, just change the URL:

```rust
let provider = Provider::connect("127.0.0.1:4000").await?;
```

> [!NOTE]
> A fresh testnet wallet has no assets. Fund it via the [testnet faucet](https://faucet-testnet.fuel.network) by pasting the address printed above. Once funded, reuse the same private key across runs.

> [!TIP]
> `Provider::connect_with_fallbacks(&[url1, url2, …])` lets you supply several node URLs; the SDK uses the first reachable one.

### 4.2 Running a short‑lived node (for testing)

The test helpers spin up a local node and tear it down with the test. The simplest entry point gives you a single funded wallet:

```rust
let wallet = launch_provider_and_get_wallet().await?;
```

For several wallets, use `launch_custom_provider_and_get_wallets`:

```rust
// 10 wallets, each with 1 coin of 1_000_000_000 of the base asset (defaults).
let wallets =
    launch_custom_provider_and_get_wallets(WalletsConfig::default(), None, None).await?;
```

If you only need a `Provider` (no wallet), use `setup_test_provider`:

```rust
let provider = setup_test_provider(vec![], vec![], None, None).await?;
let wallet = Wallet::random(&mut rand::thread_rng(), provider);
```

> [!NOTE]
> Wallets produced by `launch_provider_and_get_wallet` and `launch_custom_provider_and_get_wallets` have **deterministic addresses** (their secret keys are derived from a counter). This makes tests reproducible.

### 4.3 Persistent storage with RocksDB

By default a test node keeps state in memory. To persist the chain locally, enable the `rocksdb` + `fuel-core-lib` features and point the node at a database path:

```rust
use std::path::PathBuf;

let provider_config = NodeConfig {
    database_type: DbType::RocksDb(Some(PathBuf::from("/tmp/.fuel/db"))),
    ..NodeConfig::default()
};

launch_custom_provider_and_get_wallets(Default::default(), Some(provider_config), None).await?;
```

> [!NOTE]
> If the database path does not exist, a new database is created there. You need either the `fuel-core` binary present, or both the `fuel-core-lib` and `rocksdb` features enabled.

### 4.4 Querying the blockchain

A `Provider` exposes a broad read API. Common queries:

```rust
// All unspent coins of an asset owned by an address.
let coins = provider.get_coins(&address, *consensus_parameters.base_asset_id()).await?;

// Spendable resources matching a filter (asset, amount, exclusions).
let filter = ResourceFilter { from: address, amount: 1, ..Default::default() };
let spendable = provider.get_spendable_resources(filter).await?;

// Per-asset balances (HashMap<asset_id_hex, amount>).
let balances = provider.get_balances(&address).await?;
```

Other useful provider calls include `chain_info()`, `consensus_parameters()`, `latest_block_height()`, `produce_blocks()`, `get_transaction_by_id()`, `get_message_proof()`, and `estimate_gas_price()`. See the [Admin Guide](#) and [docs.rs/fuels](https://docs.rs/fuels/latest/fuels/accounts/provider/struct.Provider.html) for the full surface.

### 4.5 Retrying failed requests

A `Provider` can automatically retry requests that fail with an `io::Error`:

```rust
use std::time::Duration;

let retry_config = RetryConfig::new(3, Backoff::Fixed(Duration::from_secs(2)))?;
let provider = setup_test_provider(coins, vec![], None, None)
    .await?
    .with_retry_config(retry_config);
```

`Backoff` controls the wait between attempts:

| Variant | Behaviour |
|---------|-----------|
| `Linear(Duration)` | *(default)* Wait grows linearly with each attempt. |
| `Exponential(Duration)` | Wait doubles with each attempt. |
| `Fixed(Duration)` | Constant wait between attempts. |

> [!WARNING]
> All node errors currently surface as `io::Error`, so a retry can happen even for non‑transient failures (e.g. a transaction that failed verification). Tune the attempt count accordingly.

---

## 5. Wallets & Accounts

A wallet is your interface to coins, balances, transfers, and signing. Every wallet needs a `Provider`.

### 5.1 Locked vs. unlocked wallets

The SDK encodes "can this wallet sign?" in the *type*:

| Type | Has signer? | Implements | Use for |
|------|-------------|-----------|---------|
| `Wallet<Unlocked<S>>` | ✅ | `Account` + `ViewOnlyAccount` | Signing, transfers, contract calls, deploys |
| `Wallet<Locked>` | ❌ | `ViewOnlyAccount` | Read‑only: balances, coins, transaction history |

Move from unlocked to locked with `lock()`:

```rust
let wallet_locked = wallet_unlocked.lock();
```

> [!TIP]
> When designing functions that accept a wallet, ask for the *least* access you need. Prefer `Wallet<Locked>` / `impl ViewOnlyAccount` for read‑only work so signers spend as little time in memory as possible.

### 5.2 Creating wallets from private keys

```rust
use std::str::FromStr;
use fuels::{crypto::SecretKey, prelude::*};

let provider = setup_test_provider(vec![], vec![], None, None).await?;

let secret = SecretKey::from_str(
    "5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1",
)?;
let wallet = Wallet::new(PrivateKeySigner::new(secret), provider);
```

A random wallet:

```rust
let wallet = Wallet::random(&mut rand::thread_rng(), provider);
```

### 5.3 Creating wallets from a mnemonic phrase

```rust
let phrase = "oblige salon price punch saddle immune slogan rare snap desert retire surprise";

// A specific derivation path.
let key = SecretKey::new_from_mnemonic_phrase_with_path(phrase, "m/44'/1179993420'/0'/0/0")?;
let wallet = Wallet::new(PrivateKeySigner::new(key), provider.clone());

// Or the SDK's default derivation path.
let key = SecretKey::new_from_mnemonic_phrase_with_path(phrase, DEFAULT_DERIVATION_PATH)?;
let wallet = Wallet::new(PrivateKeySigner::new(key), provider);
```

> [!NOTE]
> Fuel's default derivation path is `m/44'/1179993420'/0'/0/0` (`DEFAULT_DERIVATION_PATH`). The coin type `1179993420'` is Fuel's registered BIP‑44 value.

### 5.4 Encrypting and storing keys (Keystore)

Never store a raw private key or mnemonic on disk. The `Keystore` (feature `accounts-keystore`) encrypts a key with a master password:

```rust
use fuels::accounts::keystore::Keystore;

let dir = std::env::temp_dir();
let keystore = Keystore::new(&dir);

let key = SecretKey::new_from_mnemonic_phrase_with_path(phrase, DEFAULT_DERIVATION_PATH)?;
let uuid = keystore.save_key(key, "my_master_password", rand::thread_rng())?;

// Later, recover it with the same password.
let recovered_key = keystore.load_key(&uuid, "my_master_password")?;
```

> [!CAUTION]
> Anyone with the master password and the encrypted file can recover the key. Treat both as secrets. The recovered `SecretKey` is zeroized on drop, but your own copies are not.

### 5.5 Signing with a KMS

For production key management, delegate signing to AWS or Google Cloud KMS so the private key never touches your process. Enable `accounts-signer-aws-kms` or `accounts-signer-google-kms` and construct the corresponding signer, then build a wallet the usual way (`Wallet::new(kms_signer, provider)`). The signer types live under `fuels::accounts::signers::kms`.

### 5.6 Impersonating an account (FakeSigner)

For tests against a node with `utxo_validation = false`, `FakeSigner` lets you simulate ownership of an address's assets without its key:

```rust
let node_config = NodeConfig { utxo_validation: false, ..Default::default() };
let provider = setup_test_provider(coins, vec![], Some(node_config), None).await?;

let fake_signer = FakeSigner::new(some_address);
let impersonator = Wallet::new(fake_signer, provider.clone());
```

> [!WARNING]
> A `FakeSigner` only unlocks assets when the network does **not** enforce signature validation (`utxo_validation = false`). It is a testing tool, never a production one.

### 5.7 Checking balances and coins

```rust
use std::collections::HashMap;

// Sum of all spendable coins for one asset.
let asset_id = AssetId::zeroed();
let balance: u128 = wallet.get_asset_balance(&asset_id).await?;

// All balances, keyed by asset-id hex string.
let balances: HashMap<String, u128> = wallet.get_balances().await?;
let base_balance = balances.get(&asset_id.to_string()).unwrap();
```

### 5.8 Transferring assets

The `Account` trait provides three transfer methods:

| Method | Destination |
|--------|-------------|
| `transfer` | Another address (wallet or predicate) |
| `force_transfer_to_contract` | A contract's balance |
| `withdraw_to_base_layer` | An address on the base (L1) chain |

**Wallet → wallet:**

```rust
let _res = wallets[0]
    .transfer(wallets[1].address(), 1 /* amount */, AssetId::zeroed(), TxPolicies::default())
    .await?;
```

**Wallet → contract:**

```rust
let _res = wallet
    .force_transfer_to_contract(contract_id, 300, asset_id, TxPolicies::default())
    .await?;
```

**Wallet → base layer:**

```rust
let response = wallet
    .withdraw_to_base_layer(base_layer_address, 1000, TxPolicies::default())
    .await?;

// Verify with a message proof.
let proof = wallet
    .try_provider()?
    .get_message_proof(&response.tx_id, &response.nonce, None, Some(2))
    .await?;
assert_eq!(proof.amount, 1000);
```

> [!CAUTION]
> `force_transfer_to_contract` transfers coins *unconditionally*. If the receiving contract has no logic to account for the funds, they may be **permanently lost**. Use with care.

### 5.9 Signing messages and transactions

Sign an arbitrary message with any signer:

```rust
let message = Message::new([0; 32]);
let signature = wallet.signer().sign(message).await?;
```

When building a transaction, you usually add a *signer* to the builder rather than signing manually. The builder defers witness‑index resolution until `build()`:

```rust
tb.add_signer(signer.clone())?;
let tx = tb.build(&provider).await?;
```

> [!NOTE]
> Witness indexes (which tie a signed input to its signature) only become valid once input order is final. The SDK resolves them automatically at `build()` time, so add signers *before* building. To sign an *already‑built* transaction, use `tx.sign_with(...)` and ensure signature order matches signed‑input order.

---

## 6. Generating Contract Bindings with `abigen!`

To call a Sway program from Rust you first generate type‑safe bindings from its JSON ABI. The `abigen!` macro does this at compile time.

### 6.1 The JSON ABI file

When you compile a Sway program with `forc build`, you get two artifacts: the compiled binary (`*.bin`) and a JSON ABI (`*-abi.json`). The ABI describes the program's functions, inputs, and outputs — it is what tells the SDK how to encode calls and decode results.

```json
[
  {
    "type": "function",
    "inputs": [],
    "name": "test_function",
    "outputs": [{ "name": "", "type": "bool", "components": null }]
  }
]
```

### 6.2 The `abigen!` macro

`abigen!` accepts one or more program declarations of the form `ProgramType(name=…, abi=…)`:

- `ProgramType` is `Contract`, `Script`, or `Predicate`.
- `name` is the Rust name for the generated binding.
- `abi` is **either** a path to the JSON ABI file **or** the ABI's literal contents.

```rust
abigen!(
    Contract(
        name = "ContractA",
        abi = "e2e/sway/bindings/sharing_types/contract_a/out/release/contract_a-abi.json"
    ),
    Contract(
        name = "ContractB",
        abi = "e2e/sway/bindings/sharing_types/contract_b/out/release/contract_b-abi.json"
    ),
    Script(
        name = "MyScript",
        abi = "e2e/sway/scripts/arguments/out/release/arguments-abi.json"
    ),
    Predicate(
        name = "MyPredicateEncoder",
        abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
    ),
);
```

### 6.3 What gets generated

`abigen!` emits, under an `abigen_bindings` module, one sub‑module per program (named after `name`). Each contains the program's custom types (structs/enums) and the binding through which calls are made. Convenience `pub use` re‑exports are added so you don't have to fully qualify the types.

```rust
pub mod abigen_bindings {
    pub mod contract_a_mod {
        struct SomeCustomStruct { /* ... */ }
        struct ContractA { /* ... */ }
        // ...
    }
    pub mod shared_types { /* ... */ }
}
```

If two programs share an identical type (same name *and* definition), it is lifted into a generated `shared_types` module and reused, with re‑exports so each program's module can still reference it as its own.

> [!IMPORTANT]
> Generate **all** your bindings in a single `abigen!` call whenever possible. This enables type sharing and avoids name collisions you'd otherwise hit when calling `abigen!` multiple times in one namespace. If two unrelated types share a name, only uniquely‑named types get a `pub use`; qualify the rest as `abigen_bindings::some_contract_mod::TheType`.

> [!NOTE]
> Everything `abigen!` emits is generated code — you never write it. Predicate names get `Encoder` and `Configurables` suffixes appended automatically: `name = "MyPredicate"` yields `MyPredicateEncoder` and `MyPredicateConfigurables`.

---

## 7. Deploying Contracts

There are two ways to work with contracts: deploy one yourself, or connect to an already‑deployed one.

### 7.1 Deploying a compiled contract

Load the `.bin` produced by `forc build`, then `deploy()`:

```rust
let wallet = launch_provider_and_get_wallet().await?;

let contract_id = Contract::load_from(
    "e2e/sway/contracts/contract_test/out/release/contract_test.bin",
    LoadConfiguration::default(),
)?
.deploy(&wallet, TxPolicies::default())
.await?
.contract_id;

println!("Contract deployed @ {contract_id}");
```

### 7.2 Configuring the load with `LoadConfiguration`

`LoadConfiguration` controls how the binary is loaded before deployment. It lets you:

- Apply a `Salt` so the same binary yields a *new* `contract_id` (enables redeploying the same code).
- Override storage slots.
- Set `configurable` constants.

```rust
use rand::prelude::{Rng, SeedableRng, StdRng};

let salt: [u8; 32] = StdRng::seed_from_u64(2322).r#gen();

let storage_configuration =
    StorageConfiguration::default().add_slot_overrides([StorageSlot::new(key, value)]);

let configuration = LoadConfiguration::default()
    .with_storage_configuration(storage_configuration)
    .with_salt(salt);

let tx_policies = TxPolicies::default()
    .with_tip(1)
    .with_script_gas_limit(1_000_000)
    .with_maturity(0)
    .with_expiration(10_000);

let contract_id = Contract::load_from("....bin", configuration)?
    .deploy(&wallet, tx_policies)
    .await?
    .contract_id;
```

> [!WARNING]
> Redeploying an identical binary with the *same* salt produces the same `contract_id` and fails with a collision. Use `with_salt(...)` (or `LoadConfiguration::default().with_salt(...)`) to get a fresh ID each time.

### 7.3 Configurable constants

Sway `configurable` constants can be overridden at deploy time. Each constant gets a generated `with_<NAME>` method on a `Configurables` struct:

```rust
let configurables = MyContractConfigurables::default()
    .with_STR_4("FUEL".try_into()?)?
    .with_U8(42)?;

let contract_id = Contract::load_from("....bin", LoadConfiguration::default().with_configurables(configurables))?
    .deploy(&wallet, TxPolicies::default())
    .await?
    .contract_id;
```

### 7.4 Overriding storage slots

If your contract uses storage, `forc` generates a `*-storage_slots.json` of default values, which the SDK loads automatically. To override some defaults:

```rust
use fuels::{programs::contract::Contract, tx::StorageSlot};

let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into());
let storage_config = StorageConfiguration::default().add_slot_overrides([slot_override]);
let load_config = LoadConfiguration::default().with_storage_configuration(storage_config);
let _ = Contract::load_from("....bin", load_config);
```

To disable auto‑loading entirely (e.g. you don't have the slots file):

```rust
let storage_config = StorageConfiguration::default().with_autoload(false);
let load_config = LoadConfiguration::default().with_storage_configuration(storage_config);
```

> [!NOTE]
> With autoload enabled (the default) and no `*-storage_slots.json` next to the binary, `load_from` fails with *"could not autoload storage slots from file … Either provide the file or disable autoloading"*. Disable autoload or supply the file.

### 7.5 Connecting to an already‑deployed contract

You don't need to redeploy to call an existing contract — just bind its ID:

```rust
abigen!(Contract(name = "MyContract", abi = "....abi.json"));

let contract_id: ContractId =
    "0x65b6a3d081966040bbccbb7f79ac91b48c635729c59a4c02f15ae7da999b32d3".parse()?;

let contract = MyContract::new(contract_id, wallet);
```

### 7.6 Deploying large contracts (blobs & loaders)

If a contract exceeds the single‑transaction size limit, deploy it as a **loader**: the code is split into *blobs* uploaded via blob transactions, and a small loader contract loads them at runtime.

The simplest path — convert to a loader and deploy:

```rust
let max_words_per_blob = 10_000;
let contract_id = Contract::load_from("....bin", LoadConfiguration::default().with_salt(salt))?
    .convert_to_loader(max_words_per_blob)?
    .deploy(&wallet, TxPolicies::default())
    .await?
    .contract_id;
```

Let the SDK decide automatically (normal deploy if small enough, loader if not):

```rust
let contract_id = Contract::load_from("....bin", LoadConfiguration::default().with_salt(salt))?
    .smart_deploy(&wallet, TxPolicies::default(), max_words_per_blob)
    .await?
    .contract_id;
```

Estimate a safe blob size:

```rust
let max_blob_size = BlobTransactionBuilder::default()
    .estimate_max_blob_size(&provider)
    .await?;
```

Blob size is bounded by **(1)** the max transaction size, **(2)** the max gas per transaction, and **(3)** the node's max HTTP body size. The estimate above only considers (1), so make your blobs a few percent smaller than the estimate.

> [!TIP]
> For fine‑grained control you can upload blobs first (`upload_blobs`), split code into blobs manually (`Contract::loader_from_blobs` / `loader_from_blob_ids`), and deploy the loader separately. See the cookbook and Admin Guide for the low‑level flow.

---

## 8. Calling Contracts

Once you have a bound contract instance, call its ABI methods through `.methods()`.

### 8.1 A basic call

```rust
let contract = MyContract::new(contract_id, wallet);

let response = contract
    .methods()
    .initialize_counter(42) // build the call
    .call()                 // submit it
    .await?;                // await the result

assert_eq!(42, response.value);
```

> [!NOTE]
> You'll chain `.call().await?` constantly. That's because you choose `.call()` vs `.simulate()`, the call is async (so you `.await`), and it returns a `Result` you handle with `?` (or `.unwrap()`).

To separate submission from result retrieval:

```rust
let submitted = contract.methods().initialize_counter(42).submit().await?;
// ... do other work ...
let value = submitted.response().await?.value;
```

### 8.2 The call response

`call()` returns a `CallResponse<D>` (here `D` is the decoded return type):

```rust
pub struct CallResponse<D> {
    pub value: D,           // the decoded return value
    pub receipts: Vec<Receipt>,
    pub gas_used: u64,
    pub tx_id: Option<TxId>,
    // ...
}
```

- `value` is the method's return value, decoded to the matching Rust type (a Sway `u64` → `u64`, a Sway struct → a generated Rust struct, `b256` → `[u8; 32]`, …).
- `receipts` holds every [receipt](https://docs.fuel.network/docs/specs/abi/receipts/) the call produced.
- `gas_used` is the gas the call consumed.
- `tx_id` is the submitted transaction's ID.

### 8.3 Transaction policies

`TxPolicies` configures transaction‑level limits and pricing. Attach them with `with_tx_policies`:

```rust
let tx_policies = TxPolicies::default()
    .with_tip(1)
    .with_script_gas_limit(1_000_000)
    .with_maturity(0)
    .with_expiration(10_000);

let response = contract.methods().initialize_counter(42)
    .with_tx_policies(tx_policies)
    .call()
    .await?;
```

| Policy | Meaning |
|--------|---------|
| **Tip** | Amount paid to the block producer to prioritise the transaction. |
| **Witness Limit** | Maximum witness‑data size. If unset, the SDK sets it from the actual witnesses/signatures. |
| **Maturity** | Block height before which the transaction cannot be included. |
| **Expiration** | Block height after which the transaction cannot be included. |
| **Max Fee** | Maximum fee the transaction may pay. |
| **Script Gas Limit** | Max gas for executing the script. If unset, the SDK estimates it for you. |

`TxPolicies::default()` uses sensible defaults (the SDK estimates gas and witness limits automatically).

### 8.4 Call parameters (forwarding coins & gas)

`CallParameters` controls what is forwarded *to the contract call itself*: amount, asset ID, and forwarded gas. Attach with `call_params` (which returns a `Result`):

```rust
let call_params = CallParameters::default().with_amount(1_000_000);

let response = contract.methods()
    .get_msg_amount()
    .call_params(call_params)?   // returns Result — errors if forwarding to a non-payable method
    .call()
    .await?;
```

Forwarding a specific asset and gas:

```rust
let call_params = CallParameters::default()
    .with_amount(deposit_amount)
    .with_asset_id(base_asset_id)
    .with_gas_forwarded(4300);
```

> [!WARNING]
> `call_params` returns a `Result` so the SDK can stop you forwarding coins (amount ≠ 0) to a method **not** annotated `#[payable]` in Sway. Forwarding gas is always allowed regardless of payability. The default `CallParameters` forwards `0` of the base asset; if you don't set parameters, the whole transaction gas limit is forwarded.

### 8.5 Custom asset transfer alongside a call

Transfer an arbitrary asset to an address *within the same transaction* as a contract call:

```rust
let _ = contract.methods()
    .initialize_counter(42)
    .add_custom_asset(AssetId::zeroed(), 1000, Some(destination_addr))
    .call()
    .await?;
```

### 8.6 Custom inputs and outputs

Add specific transaction inputs/outputs to a call:

```rust
let _ = contract.methods()
    .initialize_counter(42)
    .with_inputs(custom_inputs)
    .with_outputs(custom_outputs)
    .add_signer(other_wallet.signer().clone())
    .call()
    .await?;
```

> [!NOTE]
> If your custom inputs include coins that must be signed, attach the matching signer with `add_signer`.

### 8.7 Variable outputs

If a contract method transfers funds out (e.g. minting tokens to an address), the transaction needs *variable outputs*. Set the policy with `with_variable_output_policy`:

```rust
let response = contract.methods()
    .transfer(1_000_000, asset_id, address.into())
    .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
    .call()
    .await?;
```

| `VariableOutputPolicy` | Behaviour |
|------------------------|-----------|
| `Exactly(n)` | Allocate exactly `n` variable outputs. |
| `EstimateMinimum` | Let the SDK estimate the minimum needed. |

> [!NOTE]
> Sway's `mint_to_address` calls `transfer_to_address` under the hood, so it needs variable outputs too.

### 8.8 Calling other contracts

If a method calls *another* contract, declare the external contract(s) so the right inputs/outputs are added:

```rust
// With instances (enables log & require-revert decoding from the external contract):
let response = contract.methods().my_method()
    .with_contracts(&[&other_contract_instance])
    .call().await?;

// With raw IDs (no log decoding):
let response = contract.methods().my_method()
    .with_contract_ids(&[external_contract_id])
    .call().await?;
```

### 8.9 Letting the SDK estimate dependencies

Rather than specifying external contracts and variable outputs by hand, chain `with_variable_output_policy(EstimateMinimum)` and `determine_missing_contracts()` to have the SDK figure them out (at the cost of extra simulated calls):

```rust
let response = contract.methods()
    .mint_then_increment_from_contract(called_contract_id, amount, address.into())
    .with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)
    .determine_missing_contracts()
    .await?
    .call()
    .await?;
```

> [!NOTE]
> `determine_missing_contracts()` does not enable *logging* from external contracts — for that you still need `with_contracts(&[…])`. The same helpers work for script and multi‑calls.

### 8.10 Multiple calls in one transaction (multicalls)

Bundle several calls into one transaction with `CallHandler::new_multi_call`:

```rust
let methods = MyContract::new(contract_id, wallet.clone()).methods();
let call_1 = methods.initialize_counter(42);
let call_2 = methods.get_array([42; 2]);

let multi = CallHandler::new_multi_call(wallet.clone())
    .add_call(call_1)
    .add_call(call_2)
    .with_tx_policies(TxPolicies::default());

// Supply explicit type annotations to decode the bundled return values.
let (counter, array): (u64, [u64; 2]) = multi.call().await?.value;
```

> [!NOTE]
> Per‑call `TxPolicies` are ignored in favour of the multi‑call's policies. Per‑call parameters, variable outputs, and external contracts *are* honoured, as long as you haven't already executed them. Custom input/output order follows the order calls are added.

### 8.11 Simulating calls

To execute a call without changing chain state, use `.simulate(...)` with an execution context:

```rust
// Realistic: behaves like a real call. Needs a wallet with base assets (none are consumed).
let counter = contract.methods().mint_coins(100)
    .simulate(Execution::realistic()).await?;

// State-read-only: disables most validation; read state with an unfunded account.
let balance = contract.methods().get_balance(contract_id, AssetId::zeroed())
    .simulate(Execution::state_read_only()).await?.value;
```

If the node supports historical execution (using `rocksdb` and started with `--historical-execution`), simulate at a specific height:

```rust
let balance = contract.methods().get_balance(contract_id, AssetId::zeroed())
    .simulate(Execution::state_read_only().at_height(block_height)).await?.value;
```

### 8.12 Estimating call cost

```rust
let transaction_cost = contract.methods()
    .initialize_counter(42)
    .estimate_transaction_cost(Some(0.0) /* tolerance */, Some(1) /* block_horizon */)
    .await?;

println!("total gas: {}", transaction_cost.total_gas);
```

`TransactionCost` exposes `gas_price`, `metered_bytes_size`, `total_fee`, `script_gas`, and `total_gas`. The same `estimate_transaction_cost` method exists on multi‑call handlers and script calls.

### 8.13 Calling with a different wallet

`with_account` returns a new instance bound to another wallet:

```rust
let response = contract
    .with_account(wallet_2)
    .methods()
    .get_msg_amount()
    .call()
    .await?;
```

> [!NOTE]
> Connecting a different wallet ignores *that wallet's* provider in favour of the provider used to deploy the contract. This only matters if you have multiple `fuel-core` instances in play.

### 8.14 Configuring the encoder/decoder for a call

You can tune the ABI encoder (for arguments) and decoder (for return values):

```rust
let _ = contract
    .with_encoder_config(EncoderConfig { max_depth: 10, max_tokens: 2_000 })
    .methods()
    .initialize_counter(42)
    .with_decoder_config(DecoderConfig { max_depth: 10, max_tokens: 2_000 })
    .call()
    .await?;
```

### 8.15 Low‑level calls

For dynamic, runtime‑specified calls (indirect calls through another contract), build an encoded function selector and calldata:

```rust
use fuels::core::codec::{calldata, encode_fn_selector};

let function_selector = encode_fn_selector("set_value_multiple_complex");
let call_data = calldata!(
    MyStruct { a: true, b: [1, 2, 3] },
    SizedAsciiString::<4>::try_from("fuel")?
)?;

caller_contract.methods()
    .call_low_level_call(target_id, Bytes(function_selector), Bytes(call_data))
    .determine_missing_contracts().await?
    .call().await?;
```

> [!NOTE]
> The `calldata!` macro uses the default `EncoderConfig`.

---

## 9. Running Scripts

A script is a one‑shot executable run inside a transaction. Generate its bindings with `abigen!(Script(...))`, then run it with arguments:

```rust
abigen!(Script(name = "MyScript", abi = "....abi.json"));

let script_instance = MyScript::new(wallet, "path/to/script.bin");
let response = script_instance.main(arg_1, arg_2).call().await?;
```

Scripts support the same building blocks as contract calls:

- **Transaction policies** — `with_tx_policies(...)`, identical to contracts.
- **Custom inputs/outputs** — `with_inputs` / `with_outputs` (add `add_signer` for signed coins).
- **Logs** — `decode_logs()` and `decode_logs_with_type::<T>()` (see [§10](#10-logs)).
- **Calling contracts** — `with_contracts(&[…])` / `with_contract_ids(&[…])`.
- **Configurable constants** — each `configurable` gets a `with_<NAME>` method, set via the generated configurables struct.
- **Cost estimation** — `estimate_transaction_cost(...)`.

```rust
let response = script_instance
    .main(arg)
    .with_tx_policies(TxPolicies::default())
    .with_contract_ids(&[external_id])
    .call()
    .await?;
```

---

## 10. Logs

When a Sway method logs a value, the SDK can decode it back into a Rust type. From a `CallResponse`:

```rust
// Decode all logs of a given type into a Vec<T>.
let logs: Vec<MyType> = response.decode_logs_with_type::<MyType>()?;

// Decode every log into a LogResult (each entry is a Result<String>).
let log_result = response.decode_logs();
```

> [!WARNING]
> `decode_logs()` can be expensive — reserve it for debugging, not hot paths. Sway string *slices* cannot be logged directly; convert them with `__to_str_array()` to a `str[N]` first.

The same two functions are available for script calls.

---

## 11. Predicates

A **predicate** is a pure Sway program that returns a boolean and can own assets. A predicate's address is derived from its bytecode (like a Bitcoin P2SH address). To spend a predicate's funds, you supply the original bytecode plus the *predicate data*; if the predicate validates against that data, the funds can move.

### 11.1 Instantiating a predicate

```rust
abigen!(Predicate(name = "MyPredicate", abi = "....abi.json"));

// Encode the predicate's main-function arguments.
let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;

let predicate: Predicate = Predicate::load_from("....bin")?
    .with_provider(provider)
    .with_data(predicate_data);
```

### 11.2 Funding and spending a predicate

A predicate implements the `Account` trait, so it transfers like any account:

```rust
// Fund the predicate from a wallet.
wallet.transfer(predicate.address(), 500, asset_id, TxPolicies::default()).await?;
assert_eq!(predicate.get_asset_balance(&asset_id).await?, 500);

// Spend predicate funds (predicate must validate against its data).
predicate.transfer(receiver.address(), 300, asset_id, TxPolicies::default()).await?;
```

### 11.3 A signature‑checking predicate

Predicates commonly validate signatures. For a predicate that requires 2‑of‑3 matching signatures:

```rust
let data_to_sign = Message::new([0; 32]);
let signature1: B512 = wallet.signer().sign(data_to_sign).await?.as_ref().try_into()?;
// ... signature2, signature3 ...

let predicate_data = MyPredicateEncoder::default().encode_data([signature1, signature2, signature3])?;
let predicate = Predicate::load_from("signatures.bin")?
    .with_provider(provider)
    .with_data(predicate_data);
```

### 11.4 Configurable constants in predicates

Like contracts and scripts, predicates support `configurable` constants. Each gets a `with_<NAME>` method on the generated `<Name>Configurables` struct, applied with `with_configurables`. Note that changing a predicate's configurables changes its bytecode and therefore its **address**.

---

## 12. Custom Transactions

When the high‑level helpers aren't enough, build transactions directly with the transaction builders.

### 12.1 The transaction builders

The SDK provides `CreateTransactionBuilder` and `ScriptTransactionBuilder` (plus the `TransactionBuilder` trait). Calling `build(&provider)` yields a `CreateTransaction` or `ScriptTransaction` ready to submit. The builders handle two fiddly concerns for you:

- **Predicate data offsets** — pointers inside dynamic predicate data stay valid because the builder resolves them only at `build()`.
- **Witness indexing** — signed inputs reference witnesses by index; the builder assigns correct indexes at finalisation.

They also make a built transaction immutable, so its transaction ID can't accidentally change.

### 12.2 Building a custom transaction

A worked example: use a hot wallet to pay a predicate's `ask_amount`, unlocking the predicate's bridged asset, which is sent to cold storage.

```rust
// Start with a ScriptTransactionBuilder (no contract deploy).
let tb = ScriptTransactionBuilder::default();

// Inputs/outputs of the base asset (paid to unlock the predicate).
let base_inputs = hot_wallet
    .get_asset_inputs_for_amount(*cp.base_asset_id(), ask_amount, None).await?;
let base_outputs = hot_wallet
    .get_asset_outputs_for_amount(receiver, *cp.base_asset_id(), ask_amount as u64);

// Inputs/outputs that move the predicate's bridged asset to cold storage.
let other_inputs = predicate
    .get_asset_inputs_for_amount(bridged_asset_id, locked_amount, None).await?;
let other_outputs =
    predicate.get_asset_outputs_for_amount(cold_wallet.address(), bridged_asset_id, 500);

let inputs = base_inputs.into_iter().chain(other_inputs).collect();
let outputs = base_outputs.into_iter().chain(other_outputs).collect();
let mut tb = tb.with_inputs(inputs).with_outputs(outputs);

// Sign with the hot wallet (signature created at build()).
tb.add_signer(hot_wallet.signer().clone())?;

// Cover the fee. Second arg = base asset we expect to spend regardless of fees.
hot_wallet.adjust_for_fee(&mut tb, ask_amount).await?;

// Optional policies.
let tb = tb.with_tx_policies(TxPolicies::default().with_maturity(64).with_expiration(128));

// Build & submit.
let tx = tb.build(&provider).await?;
let tx_id = provider.send_transaction(tx).await?;
```

> [!TIP]
> Add signers **before** calling `adjust_for_fee()` — the fee estimate includes witness sizes, so adding signers afterwards can under‑fund the transaction.

### 12.3 Building without signatures

For cost estimation or simulation you may want an unsigned transaction. Change the build strategy:

```rust
let tx = tb.with_build_strategy(ScriptBuildStrategy::NoSignatures).build(&provider).await?;
```

> [!NOTE]
> When signing a *built* transaction (rather than adding signers to the builder), signature order must match the order of signed inputs. Multiple signed inputs with the same owner share one witness index.

### 12.4 Customising a contract or script call's transaction

You can fetch the underlying builder from a `CallHandler`, tweak it, submit it, and still decode the result:

```rust
let call_handler = contract.methods().initialize_counter(counter);
let mut tb = call_handler.transaction_builder().await?;
// ... customise tb ...
wallet.adjust_for_fee(&mut tb, 0).await?;
wallet.add_witnesses(&mut tb)?;

let tx = tb.build(provider).await?;
let tx_id = provider.send_transaction(tx).await?;
let tx_status = provider.tx_status(&tx_id).await?;
let response = call_handler.get_response(tx_status)?;
```

---

## 13. Pre‑uploading Code (Blobs)

For a script or predicate that is large or called often, pre‑upload its code as a *blob* and run a small *loader* instead. The loader can still carry per‑run configurables, so you change configuration without re‑uploading the (large) code.

**Scripts** — high‑level:

```rust
let result = my_script
    .convert_into_loader()   // uploads the blob internally
    .await?
    .main(arg)
    .call()
    .await?;
```

**Predicates** — prepare without network requests, then upload the blob before executing:

```rust
let predicate = predicate.convert_into_loader()?;     // prepare (no network)
predicate.upload_blob(&wallet).await?;                // upload the code blob
// subsequent spends are cheaper
```

> [!TIP]
> Pre‑uploading amortises the cost of large code across many callers — every later call references the already‑uploaded blob instead of re‑embedding the code.

---

## 14. Working with Types

The SDK ships Rust equivalents for Fuel/Sway types. Many are re‑exported from `fuel-types`; all are reachable through `fuels::types`.

| Sway type | Rust type (in `fuels::types`) | Notes |
|-----------|-------------------------------|-------|
| `b256` | `Bits256` / `[u8; 32]` | 256‑bit value |
| `Address` | `Address` | 32‑byte account address |
| `ContractId` | `ContractId` | 32‑byte contract identifier |
| `AssetId` | `AssetId` | 32‑byte asset identifier (base asset = `AssetId::zeroed()`) |
| `b512` | `B512` | 512‑bit value (e.g. signatures) |
| `Bytes32` | `Bytes32` | 32 raw bytes |
| `Bytes` | `Bytes` | dynamic byte vector |
| `str[N]` | `SizedAsciiString<N>` | fixed‑length ASCII |
| `String` (std lib) | `AsciiString` / `String` | dynamic string |
| `Vec<T>` | `Vec<T>` | dynamic vector |
| `EvmAddress` | `EvmAddress` | 20‑byte Ethereum‑style address |
| structs / enums | generated by `abigen!` / `#[derive(...)]` | see Codec below |

Construct and convert as ordinary Rust:

```rust
let asset_id = AssetId::zeroed();                          // base asset
let addr = Address::from_str("0x4710…d8fe")?;             // from hex
let contract_id: ContractId = "0x65b6…32d3".parse()?;     // via FromStr
let sized: SizedAsciiString<4> = "fuel".try_into()?;
```

> [!NOTE]
> The base asset's `AssetId` is a 32‑byte zeroed value (`AssetId::zeroed()`, `0x000…0`). It is the only asset that can pay transaction fees and gas.

---

## 15. Encoding & Decoding (Codec)

Encoding/decoding follows the [Fuel ABI spec](https://docs.fuel.network/docs/specs/abi/argument-encoding/) via `ABIEncoder` and `ABIDecoder`.

### 15.1 The two traits

To encode a value you turn it into a `Token` (via the **`Tokenizable`** trait). To decode you also need a **`ParamType`** describing the type's schema (via the **`Parameterize`** trait). All `abigen!`‑generated types implement both, and the SDK implements them for built‑in and many foreign types (`u8`, `u16`, `Vec<T>`, …).

Derive them for your own structs/enums:

```rust
use fuels::macros::{Parameterize, Tokenizable};

#[derive(Parameterize, Tokenizable)]
struct MyStruct { field_a: u8 }

#[derive(Parameterize, Tokenizable)]
enum SomeEnum { A(MyStruct), B(Vec<u64>) }
```

> [!NOTE]
> Deriving `Tokenizable` on enums requires every variant to also implement `Parameterize`. If `fuels` isn't reachable as `::fuels`, point the derive at the right paths with `#[FuelsCorePath = "…"]` / `#[FuelsTypesPath = "…"]`. For `no_std` output, add `#[NoStd]`.

### 15.2 Encoding

```rust
use fuels::core::{codec::ABIEncoder, traits::Tokenizable};

let instance = MyStruct { field: 101 };
let encoded: Vec<u8> = ABIEncoder::default().encode(&[instance.into_token()])?;
```

The `calldata!` macro encodes several `Tokenizable` values at once:

```rust
let bytes: Vec<u8> = calldata!(MyStruct { field: 101 }, MyStruct { field: 102 })?;
```

Tune limits with `EncoderConfig`:

```rust
ABIEncoder::new(EncoderConfig { max_depth: 5, max_tokens: 100 });
```

### 15.3 Decoding

```rust
use fuels::core::{codec::ABIDecoder, traits::{Parameterize, Tokenizable}, };

let bytes: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 101];
let token = ABIDecoder::default().decode(&MyStruct::param_type(), bytes)?;
let value: MyStruct = MyStruct::from_token(token)?;
```

If a type derives `TryFrom`, you can decode bytes directly:

```rust
use fuels::macros::{Parameterize, Tokenizable, TryFrom};

#[derive(Parameterize, Tokenizable, TryFrom)]
struct MyStruct { field: u64 }

let value: MyStruct = bytes.try_into()?;   // calls try_from_bytes under the hood
```

Tune with `DecoderConfig`:

```rust
ABIDecoder::new(DecoderConfig { max_depth: 5, max_tokens: 100 });
```

> [!TIP]
> `DecoderConfig` and `EncoderConfig` are safety limits against pathological inputs (deeply nested or huge token streams). Raise them only when you genuinely have large/deep types.

---

## 16. Testing Your Contracts

The SDK is built for testing. Most patterns above run against a local node from the test helpers.

### 16.1 Test wallets

```rust
// 5 wallets, 3 coins each, 100 per coin.
let config = WalletsConfig::new(Some(5), Some(3), Some(100));
let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
```

Multiple custom assets per wallet:

```rust
let assets = vec![
    AssetConfig { id: AssetId::zeroed(), num_coins: 2, coin_amount: 4 },
    AssetConfig { id: asset_id_1,        num_coins: 6, coin_amount: 8 },
];
let config = WalletsConfig::new_multiple_assets(1, assets);
let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
```

Lower‑level coin builders (`setup_single_asset_coins`, `setup_multiple_assets_coins`, `setup_custom_assets_coins`) let you craft exact UTXO sets to feed `setup_test_provider`.

| Default constant | Value |
|------------------|-------|
| `DEFAULT_NUM_WALLETS` | `10` |
| `DEFAULT_NUM_COINS` | `1` |
| `DEFAULT_COIN_AMOUNT` | `1_000_000_000` |

> [!WARNING]
> Generating a very large number of coins/assets is slow. Keep `setup_multiple_assets_coins` under ~1,000 coins and assets combined (or ~1,000,000 coins for a single asset) until the helpers are optimised.

### 16.2 The `setup_program_test!` macro

`setup_program_test!` collapses the wallet → abigen → deploy boilerplate. It accepts commands of the form `COMMAND(ARG…)`:

```rust
setup_program_test!(
    Wallets("wallet"),
    Abigen(Contract(name = "TestContract", project = "e2e/sway/contracts/contract_test")),
    Deploy(name = "contract_instance", contract = "TestContract", wallet = "wallet"),
);

let response = contract_instance.methods().initialize_counter(42).call().await?;
assert_eq!(42, response.value);
```

| Command | Purpose | Cardinality |
|---------|---------|-------------|
| `Options(profile=…)` | Set the `cargo`/`forc` build profile (`"release"` default, `"debug"`). | 0–1 |
| `Wallets("a", "b", …)` | Launch a provider and create named wallets. | 0–1 |
| `Abigen(Contract/Script/Predicate(name=…, project=…))` | Generate bindings from a `forc` project (built in **release** mode). | 0–N |
| `Deploy(name=…, contract=…, wallet=…)` | Deploy a contract (with salt) and bind an instance. | 0–N |
| `LoadScript(name=…, script=…, wallet=…)` | Create a script instance. | 0–N |

> [!NOTE]
> Projects referenced by `Abigen` must be compiled in **release** mode (`forc build --release`). The macro deploys with salt, so the same contract can be deployed multiple times (different IDs). You can also create a wallet yourself and pass its name into the macro.

### 16.3 Tweaking the blockchain in tests

Advance the block height (useful for maturity tests):

```rust
// Requires `manual_blocks_enabled = true` on the node.
provider.produce_blocks(5, None).await?;

// With a custom block time:
provider.produce_blocks(5, Some(custom_time)).await?;
```

Customise consensus parameters via `ChainConfig`:

```rust
let tx_params = TxParameters::default().with_max_gas_per_tx(1_000).with_max_inputs(2);
let mut consensus_parameters = ConsensusParameters::default();
consensus_parameters.set_tx_params(tx_params);
let chain_config = ChainConfig { consensus_parameters, ..ChainConfig::default() };

let provider = setup_test_provider(coins, vec![], Some(NodeConfig::default()), Some(chain_config)).await?;
```

### 16.4 Running the SDK's own tests

The integration tests in `e2e/tests/` exercise nearly the whole SDK. To run them, first build the Sway test projects, then run `cargo test`:

```shell
forc build --release --path e2e
cargo test
```

```shell
# Run a subset and show output:
cargo test --test types in_vector -- --show-output

# Capture output even on pass:
cargo test -- --nocapture
```

WASM tests live in `packages/wasm-tests` and run with `wasm-pack test` (add the `wasm32-unknown-unknown` target and install `wasm-pack` first).

---

## 17. Cookbook

### 17.1 Deposit and withdraw (liquidity pool)

```rust
abigen!(Contract(name = "MyContract", abi = "....liquidity_pool-abi.json"));

let methods = MyContract::new(contract_id, wallet.clone()).methods();

// Deposit: forward base asset to the contract.
let call_params = CallParameters::default().with_amount(1_000_000).with_asset_id(base_asset_id);
methods.deposit(wallet.address().into())
    .call_params(call_params)?
    .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
    .call().await?;

// Withdraw: forward the LP token back.
let lp_asset_id = contract_id.asset_id(&SubAssetId::zeroed());
let lp_balance = wallet.get_asset_balance(&lp_asset_id).await?;
let call_params = CallParameters::default()
    .with_amount(lp_balance.try_into().unwrap())
    .with_asset_id(lp_asset_id);
methods.withdraw(wallet.address().into())
    .call_params(call_params)?
    .with_variable_output_policy(VariableOutputPolicy::Exactly(1))
    .call().await?;
```

### 17.2 Custom consensus parameters

```rust
use fuels::tx::{ConsensusParameters, FeeParameters, TxParameters};

let tx_params = TxParameters::default().with_max_gas_per_tx(1_000).with_max_inputs(2);
let fee_params = FeeParameters::default().with_gas_price_factor(10);

let mut consensus_parameters = ConsensusParameters::default();
consensus_parameters.set_tx_params(tx_params);
consensus_parameters.set_fee_params(fee_params);

let chain_config = ChainConfig { consensus_parameters, ..ChainConfig::default() };
let provider = setup_test_provider(coins, vec![], Some(NodeConfig::default()), Some(chain_config)).await?;
```

### 17.3 Transfer all assets in one transaction

Loop over a wallet's balances, building inputs/outputs per asset, then submit one transfer (keeping back half the base asset for fees):

```rust
let balances = wallet_1.get_balances().await?;
let mut inputs = vec![];
let mut outputs = vec![];
for (id_string, amount) in balances {
    let id = AssetId::from_str(&id_string)?;
    inputs.extend(wallet_1.get_asset_inputs_for_amount(id, amount, None).await?);
    let out_amount = if id == *cp.base_asset_id() { (amount / 2) as u64 } else { amount as u64 };
    outputs.extend(wallet_1.get_asset_outputs_for_amount(wallet_2.address(), id, out_amount));
}

let mut tb = ScriptTransactionBuilder::prepare_transfer(inputs, outputs, TxPolicies::default());
wallet_1.add_witnesses(&mut tb)?;
let tx = tb.build(&provider).await?;
provider.send_transaction_and_await_commit(tx).await?;
```

---

## 18. Debugging

### 18.1 Generating a function selector

The SDK computes a function selector for every contract call. To compute one yourself:

```rust
use fuels::core::codec::encode_fn_selector;
let selector = encode_fn_selector("my_function");
```

### 18.2 Decoding script transactions

The SDK can make a script transaction human‑readable — telling you whether it is a contract call, a loader script (with the blob ID), or neither. With the ABI you can decode the call's arguments via `ABIFormatter`:

```rust
use fuels::{core::codec::ABIFormatter, programs::debug::ScriptType};

let TransactionType::Script(tx) = provider.get_transaction_by_id(&tx_id).await?.unwrap().transaction
    else { panic!("not a script transaction") };

let ScriptType::ContractCall(calls) = ScriptType::detect(tx.script(), tx.script_data())?
    else { panic!("not a contract call") };

let abi = ABIFormatter::from_json_abi(std::fs::read_to_string("....abi.json")?)?;
let call = &calls[0];
let fn_selector = call.decode_fn_selector()?;
let decoded_args = abi.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

eprintln!("The script called: {fn_selector}({})", decoded_args.join(", "));
```

```text
The script called: initialize_counter(42)
```

### 18.3 Inspecting the compiled binary

`forc` can disassemble a compiled binary to FuelVM assembly:

```shell
forc parse-bytecode out/release/my-test.bin
```

```text
half-word   byte   op                raw           notes
        0   0      JI(4)             90 00 00 04   jump to byte 16
        1   4      NOOP              47 00 00 00
        ...
```

---

## 19. The `fuels-abi` CLI

`fuels-abi-cli` (`sway-abi-cli`) is a small utility to encode Sway function calls and decode their output.

```text
sway-abi-cli <SUBCOMMAND>

SUBCOMMANDS:
    codegen   Output Rust types file
    decode    Decode ABI call result
    encode    Encode ABI call
    help      Prints help
```

Encode raw parameters (`-v <type> <value>` per parameter):

```console
$ cargo run -- encode params -v bool true -v u32 42 -v u32 100
0000000000000001000000000000002a0000000000000064
```

Encode a full function call from a JSON ABI (the first 8 bytes carry the function selector):

```console
$ cargo run -- encode function examples/simple.json takes_u32_returns_bool -p 4
000000006355e6ee0000000000000004
```

Decode parameters or a function's output:

```console
$ cargo run -- decode params -t bool -t u32 -t u32 0000000000000001000000000000002a0000000000000064
Bool(true)
U32(42)
U32(100)
```

---

## 20. Troubleshooting

### Error: `no 'fuel-core' in PATH`

**Cause:** The test helpers launch the `fuel-core` binary, which isn't installed or isn't on your `PATH`.
**Solution:** Install the Fuel toolchain via `fuelup`, or enable the `fuel-core-lib` feature to run the node as a library instead.
**Prevention:** In CI, install `fuelup` (and run `fuelup toolchain install latest`) before tests, or use `fuel-core-lib`.

### Error: contract deployment fails with a contract‑ID collision

**Cause:** Deploying an identical binary with the same `Salt` produces the same `contract_id`, and the chain already has it.
**Solution:** Load with a unique salt: `LoadConfiguration::default().with_salt(random_salt)`.
**Prevention:** Always randomise the salt when redeploying the same code in tests.

### Error: `could not autoload storage slots from file …`

**Cause:** Autoloading is on (the default) but the `*-storage_slots.json` next to the binary is missing.
**Solution:** Provide the slots file, or disable autoload: `StorageConfiguration::default().with_autoload(false)`.
**Prevention:** Keep the `forc`‑generated storage‑slots JSON alongside the `.bin`.

### Error: forwarding coins to a non‑payable method (from `call_params`)

**Cause:** You set `CallParameters` with `amount != 0` for a method not annotated `#[payable]` in Sway.
**Solution:** Only forward coins to payable methods; for others use `CallParameters::default()` (amount `0`).
**Prevention:** Check the Sway ABI for the `#[payable]` annotation before forwarding.

### Error: `Error::Transaction(Reason::Failure { .. })` when a method mints/calls another contract

**Cause:** Missing variable outputs and/or external‑contract inputs.
**Solution:** Add `with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)` and `determine_missing_contracts()`, or specify them manually with `with_contract_ids`.
**Prevention:** Whenever a method transfers funds or calls another contract, set the appropriate dependencies.

### Error: "… was submitted recently in a transaction — attempting to spend it again will result in an error"

**Cause:** The `coin-cache` feature is preventing you from re‑spending a coin that's still in flight from a recent transaction.
**Solution:** Wait for the previous transaction to commit, or fetch fresh spendable resources before building the next transaction.
**Prevention:** Don't reuse the same UTXOs across rapid back‑to‑back transactions; let prior transactions settle.

### Error: tests fail unexpectedly on `master`

**Cause:** Stale build artifacts or a stale `Cargo.lock`.
**Solution:**

```shell
cargo clean
rm Cargo.lock
forc build --release --path e2e
cargo test
```

**Prevention:** Rebuild the Sway test projects after pulling changes.

### Error: schema / consensus‑parameter mismatch against a node

**Cause:** Your `fuel-core` version differs from the SDK's supported version (`SUPPORTED_FUEL_CORE_VERSION`).
**Solution:** Align `fuel-core` with the SDK's pinned version (`fuelup` can pin toolchains).
**Prevention:** Upgrade SDK and node together.

### Error: `abigen!()` fails after renaming a Sway import

**Cause:** A known issue ([#1703](https://github.com/FuelLabs/fuels-rs/issues/1703)) — renaming Sway imports can break `abigen!`.
**Solution:** Avoid renaming the imports involved, or regenerate the ABI after the rename.
**Prevention:** Keep import names stable across ABI regeneration.

---

## 21. FAQ

**Q: Why is the crate called `fuels` and not `fuel`?**
The SDK family is modelled on The Ethers Project, so it carries a trailing `s` to feel familiar to ethers.js users.

**Q: What's the difference between a contract, a script, and a predicate?**
A *contract* has persistent storage and lives at a `ContractId`. A *script* is a one‑shot executable run inside a transaction (no storage). A *predicate* is a pure program that guards an address and can own assets.

**Q: Do I need a running `fuel-core` to write tests?**
You need it available either as a binary on `PATH` or via the `fuel-core-lib` feature. The test helpers launch and tear it down for you.

**Q: What is the base asset?**
The asset with `AssetId` `0x000…0` (`AssetId::zeroed()`). It is the only asset that pays fees and gas.

**Q: How do I call a contract without redeploying it?**
Generate bindings with `abigen!`, then `MyContract::new(contract_id, wallet)` with the known ID.

**Q: When do I need variable outputs?**
Whenever a contract method transfers funds to an address during execution (e.g. minting). Use `with_variable_output_policy`.

**Q: How can I simulate a call without spending gas or changing state?**
Use `.simulate(Execution::realistic())` (validates as if real) or `.simulate(Execution::state_read_only())` (read state with an unfunded account).

**Q: How do I forward coins with a call?**
Use `CallParameters` via `call_params(...)`. The target Sway method must be `#[payable]` to receive a non‑zero amount.

**Q: Can I bundle several contract calls into one transaction?**
Yes — `CallHandler::new_multi_call(wallet).add_call(...).add_call(...)`. Provide explicit type annotations to decode the bundled return values.

**Q: How do I deploy a contract that's too big for one transaction?**
Convert it to a loader (`convert_to_loader`) or use `smart_deploy`, which uploads the code as blobs and deploys a small loader.

**Q: What's the difference between `TxPolicies` and `CallParameters`?**
`TxPolicies` are transaction‑level (tip, gas limit, maturity, expiration, max fee). `CallParameters` are call‑level (amount, asset, forwarded gas) for what the contract call receives.

**Q: Why are my test wallet addresses always the same?**
Wallets from `launch_*` helpers are deterministic by design, for reproducible tests.

**Q: How do I store keys safely?**
Use the `Keystore` (feature `accounts-keystore`) to encrypt with a master password, or sign via AWS/Google KMS so the key never enters your process.

**Q: Does the SDK work with `no_std` / WASM?**
Parts do. Disable default features for `no_std`, and use `#[NoStd]` on derives. The node client and test helpers require `std`. WASM tests live in `packages/wasm-tests`.

**Q: Where is the authoritative API reference?**
[docs.rs/fuels](https://docs.rs/fuels/latest/fuels/) — always synced with the released code.

**Q: How do I read a wallet's balance for a single asset?**
`wallet.get_asset_balance(&asset_id).await?` returns a `u128` (the sum of that asset's spendable coins).

**Q: My contract call needs another contract's logs decoded — what do I use?**
`with_contracts(&[&other_instance])` (instances enable log/require‑revert decoding). `with_contract_ids` does not decode logs.

**Q: Can I run a contract call at a past block height?**
Yes, for `simulate`, with `Execution::…​.at_height(h)`, if the node uses `rocksdb` and was started with `--historical-execution`.

---

## 22. Glossary

**Contract** — An abstraction representing a connection to a deployed smart contract. Usable as a regular Rust object whose methods mirror the on‑chain ABI methods.

**Provider** — A struct providing read‑only access to a Fuel node. Used directly or through a wallet.

**Signer** — Anything implementing the `Signer` trait that can produce a signature (`PrivateKeySigner`, KMS signers, `FakeSigner`).

**Account** — The `Account`/`ViewOnlyAccount` traits, implemented by `Wallet` and `Predicate`, providing balances and transfers.

**Coin / UTXO** — An unspent transaction output holding an `amount` of one `AssetId`. Fuel's balance model is the sum of a holder's unspent coins per asset.

**Base asset** — The fee‑paying asset, `AssetId::zeroed()` (`0x000…0`).

**Configurable** — A Sway constant that can be overridden at deploy/run time via a generated `with_<NAME>` method.

**Predicate data** — The arguments supplied to a predicate's `main` function when spending its funds.

**Loader / Blob** — A blob is a chunk of uploaded code; a loader is a small program that loads blobs at runtime, used to deploy/run code larger than one transaction allows.

---

## 23. Quick Reference

### Prelude essentials

| Symbol | What it gives you |
|--------|-------------------|
| `Provider` | Connection to a node |
| `Wallet`, `PrivateKeySigner`, `FakeSigner` | Accounts and signers |
| `abigen!`, `setup_program_test!` | Code generation & test setup |
| `Contract`, `LoadConfiguration`, `StorageConfiguration` | Deployment |
| `CallHandler`, `CallParameters`, `Execution` | Calling |
| `TxPolicies`, `VariableOutputPolicy` | Transaction configuration |
| `AssetId`, `Address`, `ContractId`, `Bytes`, `Salt` | Core types |
| `Result`, `Error` | Error handling |
| `launch_provider_and_get_wallet`, `launch_custom_provider_and_get_wallets`, `setup_test_provider` | Test node launching |

### Common chains

| Goal | Chain |
|------|-------|
| Basic call | `contract.methods().m(args).call().await?` |
| Forward coins | `.call_params(CallParameters::default().with_amount(n))?` |
| Set gas/tip | `.with_tx_policies(TxPolicies::default().with_script_gas_limit(n))` |
| Variable outputs | `.with_variable_output_policy(VariableOutputPolicy::EstimateMinimum)` |
| External contract | `.with_contracts(&[&other])` / `.with_contract_ids(&[id])` |
| Simulate | `.simulate(Execution::realistic())` |
| Estimate cost | `.estimate_transaction_cost(tolerance, horizon).await?` |
| Different wallet | `contract.with_account(w).methods()…` |

### Key default constants

| Constant | Value |
|----------|-------|
| `DEFAULT_NUM_WALLETS` | `10` |
| `DEFAULT_NUM_COINS` | `1` |
| `DEFAULT_COIN_AMOUNT` | `1_000_000_000` |
| `DEFAULT_DERIVATION_PATH` | `m/44'/1179993420'/0'/0/0` |
| Base asset `AssetId` | `0x000…0` (`AssetId::zeroed()`) |

For the complete, version‑synced API, see [docs.rs/fuels](https://docs.rs/fuels/latest/fuels/) and the [fuels‑rs book](https://fuellabs.github.io/fuels-rs/latest/).

---

_Generated June 2026 from FuelLabs/fuels-rs at b404424. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
