# The Fuel TypeScript SDK (`fuels`) — User Guide

`fuels` is the official TypeScript SDK for the [Fuel](https://fuel.network/) blockchain. It is the toolkit you use from Node.js, the browser, or Bun to talk to a Fuel network: create wallets, deploy and call Sway smart **contracts**, run **scripts** and **predicates**, build and send transactions, and generate fully-typed TypeScript bindings from your Sway program ABIs.

If you have used `ethers.js` or `web3.js` on Ethereum, `fuels` fills the same role on Fuel — with first-class TypeScript types, a UTXO-based transaction model, and a command-line tool (`fuels`) that wires your Sway programs into your TypeScript project.

This guide shows you how to **use** the SDK. For architecture, the full configuration reference, internals, and contribution details, see the companion **Admin Guide**.

> [!NOTE]
> Everything in this guide comes from a single dependency: the `fuels` umbrella package. It re-exports every individual `@fuel-ts/*` package (`@fuel-ts/account`, `@fuel-ts/contract`, `@fuel-ts/abi-coder`, and so on), so you almost always import from `'fuels'` directly.

---

## 1. Quick Start

The fastest way to a working full-stack Fuel dApp is the `create-fuels` scaffolding tool. It creates a project that already contains Sway programs, generated TypeScript types, and a front-end wired to the SDK.

=== "npm"

    ```bash
    npm create fuels@latest
    ```

=== "pnpm"

    ```bash
    pnpm create fuels@latest
    ```

=== "bun"

    ```bash
    bun create fuels@latest
    ```

You will be prompted for a project name. After the files are copied and dependencies are installed, the tool prints next steps:

```text
⚡️ Success! Created a fullstack Fuel dapp at my-fuel-project

To get started:

- cd into the project directory: cd my-fuel-project
- Start a local Fuel dev server: npm run fuels:dev
- Run the frontend: npm run dev
```

```bash
cd my-fuel-project
npm run fuels:dev   # builds Sway programs, starts a local node, hot-reloads
npm run dev         # starts the front-end
```

> [!TIP]
> `create-fuels` also installs the Fuel toolchain (`forc` and `fuel-core`) for you via `fuelup`, pinned to the `testnet` channel. You do not need to install them separately when you scaffold this way.

### Minimal SDK usage

If you only want to talk to an existing network from a plain script, you need just three things — a `Provider`, a `Wallet`, and a balance query:

```ts
import { Provider, Wallet } from 'fuels';

const provider = new Provider('https://testnet.fuel.network/v1/graphql');
const wallet = Wallet.fromAddress(
  '0x767caf5b08eba21c561078a4d5be09bbd7f16b9eca22699a61f1edd9e456126f',
  provider
);

const { balances } = await wallet.getBalances();
console.log('Balances', balances);
```

```text
Balances [ { assetId: '0x...', amount: <BN: 0x...> } ]
```

---

## 2. Installation

### Requirements

| Requirement | Version |
|---|---|
| Node.js | `^20`, `^22`, or `^24` |
| Package manager | npm, pnpm `^9.4`, yarn, or bun |
| `forc` (Sway compiler) | Required only to **build** Sway programs |
| `fuel-core` (node binary) | Required only to run a **local node** |

> [!IMPORTANT]
> You do **not** need `forc` or `fuel-core` to use the SDK against a remote network (testnet/mainnet). You only need them locally when you compile Sway programs or run a local development node. Install both with [`fuelup`](https://docs.fuel.network/guides/installation/).

### Add the SDK to an existing project

=== "npm"

    ```bash
    npm install fuels
    ```

=== "pnpm"

    ```bash
    pnpm add fuels
    ```

=== "yarn"

    ```bash
    yarn add fuels
    ```

=== "bun"

    ```bash
    bun add fuels
    ```

The package ships CommonJS, ESM, and browser builds, so it works in Node.js, bundlers (Vite, Next.js, Webpack), and the browser without extra configuration.

> [!NOTE]
> The SDK is versioned in lock-step with a specific `forc` and `fuel-core` release. Mismatched versions are the most common source of confusing errors. Run `npx fuels versions` to check compatibility (see [§12](#12-the-fuels-cli)).

---

## 3. Core Concepts

A few Fuel-specific ideas make the rest of this guide click. Read this once; you can skip back later.

**The UTXO model.** Fuel does not track an account "balance" as a single number. Instead, every wallet owns a set of discrete **coins** (UTXOs — unspent transaction outputs), each with an amount and an asset ID. A transfer spends one or more coins and produces new ones (including a "change" coin back to you). The SDK handles coin selection automatically, but the model explains behaviors like the *"too many coins"* limit covered in [Troubleshooting](#15-troubleshooting).

**Assets are first-class.** Every coin has an `assetId`. The network's fee-paying asset is the **base asset**; fetch its ID with `provider.getBaseAssetId()`. Never hard-code it — it differs per network.

**Programs come in four flavors**, all written in [Sway](https://docs.fuel.network/docs/sway/) and compiled by `forc`:

| Program | What it is | SDK class |
|---|---|---|
| **Contract** | Stateful, deployed once, called by ID | `Contract` / `ContractFactory` |
| **Script** | Stateless executable logic run inside one transaction | `Script` |
| **Predicate** | A pure function guarding an address; returns true/false to authorize spends | `Predicate` |
| **Library** | Reusable Sway code (not deployable on its own) | — |

**Typegen bindings.** You rarely hand-write ABIs. The `fuels` CLI reads your compiled program's ABI JSON and generates typed classes — `MyContract`, `MyContractFactory`, `MyScript`, `MyPredicate` — so calls like `contract.functions.transfer(...)` are fully type-checked.

---

## 4. Connecting to a Network — the `Provider`

The `Provider` is your connection to a Fuel node. It is read-only by itself (it does not hold keys) and is required by almost everything else.

```ts
import { Provider } from 'fuels';

// Pick the network you want to talk to:
const local = new Provider('http://127.0.0.1:4000/v1/graphql');
const testnet = new Provider('https://testnet.fuel.network/v1/graphql');
const mainnet = new Provider('https://mainnet.fuel.network/v1/graphql');
```

> [!NOTE]
> Constructing a `Provider` is synchronous, but it fetches and caches chain information on first use. Network details (chain ID, base asset, gas configuration) are then available without extra round-trips.

### Querying the network

```ts
const provider = new Provider('https://mainnet.fuel.network/v1/graphql');

const baseAssetId = await provider.getBaseAssetId();
const chainId = await provider.getChainId();
const gasConfig = await provider.getGasConfig();
const { consensusParameters } = await provider.getChain();
```

| Method | Returns |
|---|---|
| `getBaseAssetId()` | The fee-paying asset ID for this network |
| `getChainId()` | Numeric chain ID |
| `getChain()` | Full chain info, including `consensusParameters` |
| `getGasConfig()` | Gas price factors and limits |
| `getCoins(address, assetId?, paginationArgs?)` | A wallet's coins (UTXOs), paginated |
| `getBlocks(...)` | A list of blocks |
| `dryRun(request)` | Simulate a transaction without submitting it |
| `assembleTx(params)` | Estimate fees and fund a transaction (see [§9](#9-building-transactions-the-utxo-model)) |
| `operations.getChain()` / `operations.getNodeInfo()` | Low-level GraphQL operations |

### Provider options

The second constructor argument tunes networking behavior:

```ts
new Provider(NETWORK_URL, {
  // Mutate every outgoing request (e.g. inject auth headers)
  requestMiddleware: (request) => {
    request.credentials = 'omit';
    return request;
  },
  // Abort a request after N milliseconds
  timeout: 3000,
  // Automatic retry policy for failed requests
  retryOptions: { maxRetries: 5, baseDelay: 100, backoff: 'linear' },
  // Cache fetched Coins/Messages for N ms to avoid double-spend races
  resourceCacheTTL: 5000,
});
```

`requestMiddleware` may be `async`, which is handy for fetching credentials before each call:

```ts
new Provider(NETWORK_URL, {
  requestMiddleware: async (request) => {
    const credentials = await fetchSomeExternalCredentials();
    request.headers ??= {};
    (request.headers as Record<string, string>).auth = credentials;
    return request;
  },
});
```

> [!TIP]
> Pass a custom `fetch` implementation through the options to integrate with proxies, instrumentation, or testing frameworks.

### Paginating results

List queries like `getCoins` use cursor pagination:

```ts
import type { CursorPaginationArgs } from 'fuels';

let args: CursorPaginationArgs = { first: 10 }; // first 10 coins
const { coins, pageInfo } = await provider.getCoins(WALLET_ADDRESS, baseAssetId, args);

if (pageInfo.hasNextPage) {
  args = { after: pageInfo.endCursor, first: 10 }; // next page
  await provider.getCoins(WALLET_ADDRESS, baseAssetId, args);
}
```

`CursorPaginationArgs` supports forward (`first` + `after`) and backward (`last` + `before`) paging. Calling `getCoins` with no pagination args returns the first 100 coins.

---

## 5. Wallets & Accounts

A wallet signs transactions and pays fees. The SDK distinguishes two states:

- **`WalletUnlocked`** — holds a private key and can sign and send transactions.
- **`WalletLocked`** — knows only an address; can read balances but cannot sign.

You create wallets through the `Wallet` factory.

```ts
import { Wallet } from 'fuels';
import type { WalletLocked, WalletUnlocked } from 'fuels';

// Generate a brand-new random wallet
const fresh: WalletUnlocked = Wallet.generate({ provider });

// From a known private key
const fromKey = Wallet.fromPrivateKey('0x...', provider);

// From just an address (read-only)
const readonly: WalletLocked = Wallet.fromAddress('0x...', provider);
```

### Locking and unlocking

```ts
const unlocked = Wallet.generate({ provider });
const privateKey = unlocked.privateKey;

// Lock it (drop the key, keep the address)
const locked: WalletLocked = unlocked.lock();

// Unlock again with the key
const reUnlocked: WalletUnlocked = locked.unlock(privateKey);
```

> [!NOTE]
> A wallet can exist without a provider (`Wallet.generate()` with no args). Key-only operations like `lock()` work, but any method that touches the network — `getCoins()`, `transfer()` — throws `Provider not set` until you attach one.

### Checking balances

```ts
import type { BN } from 'fuels';

// One asset — returns a BigNumber (BN)
const balance: BN = await wallet.getBalance(await provider.getBaseAssetId());

// All assets at once
const { balances } = await wallet.getBalances();
```

Amounts are always [`BN`](#13-numbers-and-the-bn-type) (big-number) instances — never plain JavaScript numbers — to safely represent on-chain `u64`/`u256` values.

### Encrypted JSON wallets

You can serialize a wallet to a password-encrypted JSON keystore and restore it later:

```ts
// Encrypt
const jsonWallet = await Wallet.generate({ provider }).encrypt('my-password');
// e.g. fs.writeFileSync('secure-path/my-wallet.json', jsonWallet);

// Decrypt
const wallet = await Wallet.fromEncryptedJson(jsonWallet, 'my-password', provider);
```

### Managing multiple keys — `WalletManager`

For apps that hold several keys behind one password, `WalletManager` wraps encrypted **vaults**:

```ts
import { WalletManager } from 'fuels';

const walletManager = new WalletManager();
await walletManager.unlock('my-password');

await walletManager.addVault({
  type: 'privateKey',
  secret: myWallet.privateKey,
  title: 'My first private key vault',
});

const vaults = walletManager.getVaults();
const wallet = walletManager.getWallet(myWallet.address);

await walletManager.lock(); // always lock when done
```

Useful `WalletManager` members:

| Member | Purpose |
|---|---|
| `unlock(password)` / `lock()` | Open / close the manager |
| `isLocked` | Current state |
| `addVault({ type, secret, title? })` | Add a `privateKey` or `mnemonic` vault |
| `getVaults()` / `getWallet(address)` | Enumerate vaults / retrieve a wallet |
| `updatePassphrase(old, new)` | Rotate the password |

> [!CAUTION]
> Always call `walletManager.lock()` once you are finished. Leaving the manager unlocked keeps decrypted secrets in memory.

### Connecting browser wallets — `Fuel` and connectors

In a dApp you usually connect to an installed browser wallet rather than holding keys yourself. The `Fuel` class is the connector entry point:

```ts
import { Fuel } from 'fuels';

const sdk = new Fuel();
await sdk.init(); // await init to avoid connector race conditions
```

You can pass connectors and storage:

```ts
import { Fuel, MemoryStorage } from 'fuels';

const sdk = await new Fuel({
  connectors: defaultConnectors({ devMode: true }),
  storage: new MemoryStorage(),
}).init();
```

For React apps, the `@fuels/react` and `@fuels/connectors` packages provide hooks (`useConnectUI`, `useIsConnected`, `useNetwork`) and a `FuelProvider`. To write a custom connector, extend `FuelConnector` and emit the standard events (`accounts`, `connection`, `currentAccount`, `networks`, `assets`, …).

---

## 6. Contracts

Contracts are stateful programs deployed to the network. After generating types (see [§11](#11-generating-typescript-types-typegen)), you get a `MyContractFactory` (for deploying) and a `MyContract` (for interacting).

### Deploying a contract

```ts
import { Provider, Wallet } from 'fuels';
import { EchoValuesFactory } from './typegend';

const provider = new Provider(LOCAL_NETWORK_URL);
const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);

const deploy = await EchoValuesFactory.deploy(wallet);
const { contract } = await deploy.waitForResult();
```

The two-step `deploy()` → `waitForResult()` pattern recurs throughout the SDK: the first call submits, the second waits for the network to finalize and returns the result.

### Calling contract methods

Every ABI function is exposed under `contract.functions`:

```ts
// State-changing call (submitted as a transaction)
const call = await contract.functions.return_input(1337).call();
const { value } = await call.waitForResult();

// Read-only — `.get()` runs the function without spending gas
const { value: count } = await contract.functions.get_count().get();

// Dry-run / simulate — execute against current state without submitting
const res = await contract.functions.echo_u8(10).simulate();
const sim = await contract.functions.echo_u8(10).dryRun();
```

| Terminator | Submits a tx? | Use it for |
|---|---|---|
| `.call()` | Yes | State changes; returns `{ waitForResult }` |
| `.get()` | No | Reading state cheaply |
| `.simulate()` | No | Validating a call against funded state |
| `.dryRun()` | No | Validating a call without resources |

### Connecting to an already-deployed contract

If you have a contract's ID and ABI, instantiate `Contract` directly — no redeploy needed:

```ts
import { Contract } from 'fuels';
import { EchoValues } from './typegend';

const contract = new Contract(contractId, EchoValues.abi, wallet);
const { value } = await contract.functions.echo_u8(10).simulate();
```

### Configurable constants

Sway programs can expose **configurables** — constants you override at deploy time:

```ts
const deploy = await EchoConfigurablesFactory.deploy(wallet, {
  configurableConstants: { age: 10 },
});
const { contract } = await deploy.waitForResult();

const { value: [age, tag] } = await contract.functions.echo_configurables().get();
// age === 10 (overridden); tag === 'fuel' (default)
```

> [!WARNING]
> Configurable values are validated against the Sway type. Supplying an incomplete struct throws *"Error setting configurable constants on contract: Invalid struct … Field … not present."*

### Forwarding assets and call parameters

Use `.callParams()` to forward coins into a contract call, and `.txParams()` to set transaction parameters:

```ts
const { waitForResult } = await contract.functions
  .transfer(amountToTransfer, asset, recipient.address.toB256())
  .callParams({ forward: [amountToForward, baseAssetId] })
  .call();

await waitForResult();
const contractBalance = await contract.getBalance(baseAssetId);
```

### Reading logs

Sway `log` statements surface as decoded values on the call result:

```ts
const { waitForResult } = await contract.functions.log_simple('ContractA').call();
const { logs } = await waitForResult();
// logs === ['ContractA']
```

For calls spanning multiple contracts, `groupedLogs` keys the logs by contract ID:

```ts
const { groupedLogs } = await waitForResult();
// { [contractA.id.toB256()]: ['ContractA'], [contractB.id.toB256()]: ['ContractB'] }
```

### Multi-calls

Batch several functions into one transaction with `multiCall`:

```ts
const { waitForResult } = await contractA
  .multiCall([
    contractA.functions.log_simple('Contract1'),
    contractB.functions.log_simple('Contract2'),
  ])
  .call();
```

### Inter-contract calls

When a contract calls another, register the external contract(s) with `.addContracts()` so the SDK includes the right inputs:

```ts
const call = await tokenDepositor.functions
  .deposit_to_simple_token(simpleToken.id.toB256(), 70)
  .addContracts([simpleToken])
  .call();
await call.waitForResult();
```

### Upgradeable (proxy) contracts

The SDK ships a ready-made SRC-14 owned-proxy contract (`Src14OwnedProxy` / `Src14OwnedProxyFactory`) for upgradeable deployments. The pattern: deploy your implementation, deploy the proxy with the implementation as its target, then always interact through the **proxy ID** (which stays stable across upgrades).

```ts
import { Src14OwnedProxy, Src14OwnedProxyFactory } from 'fuels';

// Combine implementation + proxy storage slots
const storageSlots = counterContractFactory.storageSlots.concat(
  Src14OwnedProxy.storageSlots
);

const proxyDeploy = await new Src14OwnedProxyFactory(wallet).deploy({
  storageSlots,
  configurableConstants: {
    INITIAL_TARGET: { bits: counterContract.id.toB256() },
    INITIAL_OWNER: { Initialized: { Address: { bits: wallet.address.toB256() } } },
  },
});
const { contract: proxyContract } = await proxyDeploy.waitForResult();
await (await proxyContract.functions.initialize_proxy().call()).waitForResult();

// Interact via the proxy ID, using your typed implementation class
const proxied = new Counter(proxyContract.id, wallet);
await (await proxied.functions.increment_count(1).call()).waitForResult();
```

To upgrade, deploy a new implementation and point the proxy at it with `set_proxy_target`. Existing code keeps using the same proxy ID.

---

## 7. Scripts

A **script** is stateless logic executed inside a single transaction — ideal for orchestrating several actions atomically. After typegen you get a `MyScript` class.

```ts
import { ScriptMainArgs } from './typegend';

const script = new ScriptMainArgs(wallet);
const { waitForResult } = await script.functions.main(3).call();
const { value, logs } = await waitForResult();
```

Scripts support configurables, just like contracts:

```ts
import { Script } from 'fuels';
import { ScriptSum } from './typegend/scripts';

const script = new Script(ScriptSum.bytecode, ScriptSum.abi, wallet);
script.setConfigurableConstants({ AMOUNT: 81 });

const { waitForResult } = await script.functions.main(10).call();
const { value } = await waitForResult(); // 10 + 81
```

You can also fetch the underlying transaction request before sending — useful for adding signatures or inspecting the tx ID:

```ts
const tx = script.functions.main(argument);
tx.callParams({ gasLimit: 7500 });
const txRequest = await tx.getTransactionRequest();
const txId = txRequest.getTransactionId(await provider.getChainId());
```

> [!TIP]
> Scripts can be **deployed** as loader bytecode with `fuels deploy`, then instantiated via the generated `MyScriptLoader` class. This keeps the on-chain blob reusable and your transactions small.

---

## 8. Predicates

A **predicate** is a pure, stateless Sway program that guards an address. Funds sent to a predicate's address can only be spent if the predicate returns `true` for the provided data — no private key involved. They power escrow, multisig, and conditional-spend patterns.

```ts
import { ConfigurablePin } from './typegend';

// Instantiate with the data the predicate expects
const predicate = new ConfigurablePin({ provider, data: [1337] });

// Fund the predicate's address
const fundTx = await sender.transfer(predicate.address, 300_000, assetId);
await fundTx.waitForResult();

// Spend from it — the predicate validates the transaction
const transferTx = await predicate.transfer(receiver.address, 1000, assetId);
const { isStatusSuccess } = await transferTx.waitForResult();
```

Predicates can carry **configurable constants** and be deployed as reusable loader bytecode, exactly like scripts (`ConfigurablePinLoader`). For full control, build the transaction by hand and let the predicate pay:

```ts
import { ScriptTransactionRequest } from 'fuels';

const request = new ScriptTransactionRequest();
request.addCoinOutput(receiver.address, amountToReceiver, assetId);

const { assembledRequest } = await provider.assembleTx({
  request,
  feePayerAccount: predicate,
  accountCoinQuantities: [
    { amount: amountToReceiver, assetId, account: predicate, changeOutputAccount: predicate },
  ],
});

const tx = await predicate.sendTransaction(assembledRequest);
await tx.waitForResult();
```

---

## 9. Building Transactions — the UTXO model

For everyday transfers you never touch a transaction object. For advanced flows you build one explicitly.

### Simple transfers

```ts
// Send 100 of an asset
const tx = await sender.transfer(receiver.address, 100, assetId);
await tx.waitForResult();
```

### Batch transfers

Send to many recipients (or split your own coins) in one transaction:

```ts
const transfers = [
  { amount: 200, assetId: baseAssetId, destination: walletA.address },
  { amount: 200, assetId: baseAssetId, destination: walletB.address },
];
const batchTx = await wallet.batchTransfer(transfers);
await batchTx.waitForResult();
```

### Transaction parameters

`TxParams` lets you control fees and lifetime. Apply it with `.txParams()` on a call, or pass it to a `ScriptTransactionRequest` constructor.

```ts
import type { TxParams } from 'fuels';
import { bn } from 'fuels';

const txParams: TxParams = {
  gasLimit: bn(70935),    // max gas the script may consume
  maxFee: bn(69242),      // max fee you will pay
  tip: bn(100),           // optional priority tip
  maturity: 1,            // earliest block the tx is valid
  witnessLimit: bn(5000), // max size of witness data
  expiration: 200,        // latest block the tx is valid
};

const { waitForResult } = await contract.functions
  .increment_count(15)
  .txParams(txParams)
  .call();
```

### Funding and estimating with `assembleTx`

`provider.assembleTx()` is the modern way to estimate fees and select coins for a hand-built request. You give it the request, a fee payer, and the coin quantities each account must supply; it returns a fully-funded `assembledRequest`.

```ts
const request = new ScriptTransactionRequest({ script: ScriptTransferToContract.bytecode });
request
  .setData(ScriptTransferToContract.abi, scriptArguments)
  .addContractInputAndOutput(contract.id);

const { assembledRequest } = await provider.assembleTx({
  request,
  feePayerAccount: wallet,
  accountCoinQuantities: [
    { amount: 1000, assetId: ASSET_A, account: wallet, changeOutputAccount: wallet },
  ],
});

const tx = await wallet.sendTransaction(assembledRequest);
await tx.waitForResult();
```

### Working with UTXOs directly

Because balances are sets of coins, you sometimes need to reshape them.

**Consolidate** many small coins into fewer (the fix for the "too many coins" limit):

```ts
const result = await wallet.consolidateCoins({
  assetId: baseAssetId,
  mode: 'parallel',  // or 'sequential'
  outputNum: 1,      // how many output coins to leave
});
console.log('Successful:', result.txResponses, 'Failed:', result.errors);
```

**Split** one large coin into several:

```ts
const splits = new Array(5).fill({ amount: 200, assetId: baseAssetId, destination: dest.address });
const batchTx = await wallet.batchTransfer(splits);
await batchTx.waitForResult();
```

> [!NOTE]
> `consolidateCoins` runs multiple transactions when needed. `mode: 'parallel'` is faster; `'sequential'` is gentler on the node. The default `outputNum` is `1`.

### Inspecting the chain's limits

```ts
const { maxInputs, maxOutputs } = (await provider.getChain())
  .consensusParameters.txParameters;
```

---

## 10. Signing transactions

When you need a signature on a custom transaction (for example, a script that verifies a signature on-chain):

```ts
const scope = script.functions.main(signer.address.toB256(), 0);
const request = await scope.getTransactionRequest();

let signature = await signer.signTransaction(request);
request.addWitness(signature);

const { assembledRequest } = await provider.assembleTx({ request, feePayerAccount: signer });

// assembleTx may modify the request, so re-sign and update the witness
signature = await signer.signTransaction(assembledRequest);
assembledRequest.updateWitness(0, signature);

const { waitForResult } = await scope.call({ skipAssembleTx: true });
const { transactionResult } = await waitForResult();
```

---

## 11. Generating TypeScript types (typegen)

Typegen turns a compiled Sway program's ABI JSON into typed TypeScript classes. You normally run it through the `fuels` CLI, which wraps the underlying `fuels-typegen` tool.

```bash
# Contracts (the default)
npx fuels typegen -i ./out/*-abi.json -o ./src/sway-api -c

# Scripts
npx fuels typegen -i ./out/my-script-abi.json -o ./src/sway-api -s

# Predicates
npx fuels typegen -i ./out/my-predicate-abi.json -o ./src/sway-api -p
```

| Flag | Meaning |
|---|---|
| `-i, --inputs <path\|glob...>` | ABI JSON file(s) — globs allowed (**required**) |
| `-o, --output <dir>` | Output directory for generated files (**required**) |
| `-c, --contract` | Generate contract types (default) |
| `-s, --script` | Generate script types |
| `-p, --predicate` | Generate predicate types |
| `-S, --silent` | Suppress output messages |

> [!CAUTION]
> `-c`, `-s`, and `-p` are mutually exclusive. Combining them fails with `error: option '-c, --contract' cannot be used with option '-s, --script'`.

The generated files give you, per program: a typed program class (`MyContract`), a factory (`MyContractFactory`) with `deploy()` and `storageSlots`, a `*Loader` variant for deployed scripts/predicates, plus a `contract-ids.json` you import for addresses. Using them:

```ts
import { Sample } from './sway-programs-api';
import contractsIds from './sway-programs-api/contract-ids.json';

const contract = new Sample(contractsIds.sample, wallet);
const { value } = await contract.functions.return_input(1337).dryRun();
```

---

## 12. The `fuels` CLI

The `fuels` command orchestrates the Sway-to-TypeScript workflow. Install it via the `fuels` package; run it with `npx fuels <command>` (or your scaffolded `fuels:dev` script).

### Global options

| Option | Effect |
|---|---|
| `-D, --debug` | Enable verbose logging |
| `-S, --silent` | Omit output messages |
| `-v, --version` | Print the version |
| `-h, --help` | Show help |

### Commands

| Command | Purpose |
|---|---|
| `fuels init` | Create a `fuels.config.ts` file |
| `fuels build` | Build Sway programs and generate TypeScript |
| `fuels deploy` | Deploy contracts/scripts/predicates to the network |
| `fuels dev` | Build + deploy + watch with hot-reload |
| `fuels node` | Start a local `fuel-core` node from your config |
| `fuels typegen` | Generate types from ABI JSON (see [§11](#11-generating-typescript-types-typegen)) |
| `fuels versions` | Check for version incompatibilities |

#### `fuels init`

Generates a config file. You must point it at your Sway programs, either as a Forc workspace or as individual paths, and give it an output directory:

```bash
npx fuels init --path . -w ./sway-programs -o ./src/sway-api
```

| Option | Meaning |
|---|---|
| `--path <path>` | Project root (defaults to current directory) |
| `-w, --workspace <path>` | Path to a Forc workspace |
| `-c, --contracts [paths...]` | Individual contract paths (conflicts with `--workspace`) |
| `-s, --scripts [paths...]` | Individual script paths (conflicts with `--workspace`) |
| `-p, --predicates [paths...]` | Individual predicate paths (conflicts with `--workspace`) |
| `-o, --output <path>` | TypeScript output directory (**required**) |
| `--forc-path <path>` | Path to the `forc` binary |
| `--fuel-core-path <path>` | Path to the `fuel-core` binary |
| `--auto-start-fuel-core` | Auto-start a node during `dev` |
| `--fuel-core-port <port>` | Port for the local node |

#### `fuels build`

```bash
npx fuels build            # compile Sway + generate types
npx fuels build --deploy   # also deploy (auto-starts a node if needed)
```

#### `fuels dev`

The everyday loop. It builds, deploys, and watches your `fuels.config.ts`, Forc workspace `*.sw`/`*.toml` files, and snapshot dir — rebuilding and redeploying on change.

```bash
npx fuels dev
```

> [!TIP]
> The development workflow is: edit Sway → `fuels dev` rebuilds and regenerates types → your TypeScript instantly sees the new ABI. Run `fuels node` separately if you only want a local node without the build/deploy loop.

The configuration file that drives these commands is documented in full in the **Admin Guide** (`§ Configuration Mastery`). The everyday essentials: `output` (required), one of `workspace`/`contracts`/`scripts`/`predicates`, and `autoStartFuelCore` (default `true`).

---

## 13. Numbers and the `BN` type

On-chain integers can exceed JavaScript's safe range, so the SDK uses a big-number type, `BN`, for all amounts.

```ts
import { bn, BN } from 'fuels';

const a = bn(70935);          // from a number
const b = bn('0x2710', 'hex'); // from a hex string
const sum: BN = a.add(b);

sum.toNumber();   // convert back (throws if too big)
sum.toString();   // decimal string
sum.toHex();      // hex string
```

> [!WARNING]
> Never do arithmetic on raw balances with `+`/`-`. Use `BN` methods (`add`, `sub`, `mul`, `div`) to avoid precision loss. Convert to `number` only for display, and only when you know the value is small.

Other common utilities exported from `fuels`:

| Helper | Purpose |
|---|---|
| `Address` | Parse/convert Fuel addresses (`toB256()`, `toAddress()`) |
| `hexlify` / `arrayify` | Convert between hex strings and byte arrays |
| `concat` | Concatenate byte arrays |
| `toHex` | Convert a value to a hex string |
| `ZeroBytes32` | The all-zero 32-byte constant |
| `Interface` | Encode/decode against an ABI directly |

---

## 14. Testing your integration

The SDK includes a test helper, exported from `fuels/test-utils`, that spins up a real ephemeral `fuel-core` node with funded wallets:

```ts
import { launchTestNode } from 'fuels/test-utils';
import { SampleFactory } from './sway-programs-api';

using launched = await launchTestNode();
const { wallets: [wallet] } = launched;

const deploy = await SampleFactory.deploy(wallet);
const { contract } = await deploy.waitForResult();

const { value } = await (await contract.functions.return_input(1337).call()).waitForResult();
```

> [!NOTE]
> The `using` keyword (TypeScript 5.2+ explicit resource management) automatically shuts the node down when the test scope ends. `launchTestNode` accepts `contractsConfigs` to auto-deploy contracts and gives you pre-funded `wallets` and a ready `provider`.

`fuels/test-utils` also exports helpers like `expectToThrowFuelError` (assert a specific `FuelError`) and asset constants (`ASSET_A`, `ASSET_B`).

---

## 15. Troubleshooting

Every error thrown by the SDK is a `FuelError` carrying a stable `code` (a member of the `ErrorCode` enum). Catch and branch on the code rather than the message:

```ts
import { FuelError, ErrorCode } from 'fuels';

try {
  await contract.functions.return_input(1337).simulate();
} catch (e) {
  if (e instanceof FuelError && e.code === ErrorCode.INSUFFICIENT_FUNDS_OR_MAX_COINS) {
    // handle insufficient funds
  }
}
```

### Error: `not-enough-funds-or-max-coins-reached`

**Message:** *"Insufficient funds or too many small value coins. Consider combining UTXOs. For the following asset ID: '0x…'."*
**Cause:** The wallet either lacks funds for the amount + fee, or owns more small coins than a transaction may reference (the coin-input limit). This is the same condition behind the community report *"No Coins Available or exceeding 255 Coin Limit"*.
**Solution:** Fund the wallet, or consolidate coins with `wallet.consolidateCoins({ assetId })` (see [§9](#9-building-transactions-the-utxo-model)).
**Prevention:** Periodically consolidate wallets that receive many small payments.

### Error: `Provider not set`

**Cause:** You called a network method on a wallet created without a provider.
**Solution:** Pass a `Provider` when constructing the wallet, or re-create it with `Wallet.fromPrivateKey(key, provider)`.

### Error: `config-file-not-found` — *"Config file not found!"*

**Cause:** A `fuels` CLI command ran where no `fuels.config.{ts,js,cjs,mjs}` exists.
**Solution:** Run `fuels init …` first, or `--path` to the directory that contains the config.

### Error: `Unable to find the following binaries on the filesystem`

**Cause:** `forc` or `fuel-core` could not be located at the configured path.
**Solution:** Install the toolchain via [`fuelup`](https://docs.fuel.network/guides/installation/), or set `forcPath`/`fuelCorePath` (or `--forc-path`/`--fuel-core-path`) to the binaries.

### Error: `workspace-not-detected` — *"Forc workspace not detected"*

**Cause:** You passed a single program directory to `workspace` instead of an actual Forc workspace.
**Solution:** Point `workspace` at a directory whose `Forc.toml` declares `[workspace]`, or use `contracts`/`scripts`/`predicates` for individual programs.

### Error: setting configurable constants — *"Invalid struct … Field … not present."*

**Cause:** A `configurableConstants` value does not fully match the Sway type.
**Solution:** Provide all required struct fields with correct types.

### Error: `invalid-configurable-constants`, `gas-limit-too-low`, `max-fee-too-low`

**Cause:** Gas limit or max fee below what the transaction needs, or invalid configurables.
**Solution:** Raise `gasLimit`/`maxFee` in `TxParams`, or rely on `assembleTx`/`simulate` to estimate them for you.

> [!TIP]
> When a contract call reverts, the SDK throws with `ErrorCode.SCRIPT_REVERTED` and surfaces any decoded `require`/`assert` log. Read the `logs`/`groupedLogs` on the result to see what the program reported.

---

## 16. Frequently Asked Questions

**Q: Do I have to install `forc` and `fuel-core`?**
Only to compile Sway programs or run a local node. To call an existing contract on testnet/mainnet you need just the `fuels` npm package.

**Q: What is the difference between `.call()`, `.get()`, `.simulate()`, and `.dryRun()`?**
`.call()` submits a real transaction (state changes). `.get()` reads state cheaply without a transaction. `.simulate()` and `.dryRun()` execute a function without submitting — `.simulate()` requires funded resources, `.dryRun()` does not.

**Q: Why are balances `BN` objects and not numbers?**
On-chain integers can exceed JavaScript's safe integer range. `BN` preserves precision; convert to a number only for display.

**Q: What is a predicate, and how is it different from a contract?**
A predicate is a stateless pure function that authorizes spends from its own address by returning true/false. A contract is stateful, has an ID, and is called to mutate state. Predicates need no private key.

**Q: How do I connect a browser wallet like the Fuel Wallet?**
Use the `Fuel` class with connectors, or the `@fuels/react` hooks (`useConnectUI`, `useIsConnected`) and `FuelProvider` in React apps.

**Q: My wallet has plenty of funds but transactions fail with a "max coins" error. Why?**
A transaction can only reference a limited number of coin inputs. Many tiny coins exceed that limit. Run `wallet.consolidateCoins({ assetId })`.

**Q: How do I run automated tests without a public network?**
Use `launchTestNode()` from `fuels/test-utils`. It starts an ephemeral node with funded wallets and tears it down automatically.

**Q: Can I use the SDK in the browser?**
Yes. The package ships a browser build and works with Vite, Next.js, and other bundlers out of the box.

**Q: How do I keep the SDK, `forc`, and `fuel-core` in sync?**
Run `npx fuels versions`. The scaffolded `fuel-toolchain` file pins compatible `forc` and `fuel-core` versions for you.

**Q: Where can I find more?**
The official docs at [docs.fuel.network/docs/fuels-ts](https://docs.fuel.network/docs/fuels-ts/), the Sway language docs, and the [Fuel forum](https://forum.fuel.network/).

---

## 17. Quick Reference

### Import map (everything comes from `fuels`)

| You need to… | Import |
|---|---|
| Connect to a network | `Provider` |
| Hold/sign keys | `Wallet`, `WalletUnlocked`, `WalletLocked`, `WalletManager` |
| Connect a browser wallet | `Fuel`, `FuelConnector` |
| Interact with a contract | `Contract`, `ContractFactory` (or generated classes) |
| Run a script | `Script`, `ScriptTransactionRequest` |
| Guard an address | `Predicate` |
| Numbers & encoding | `bn`, `BN`, `hexlify`, `arrayify`, `Address` |
| Errors | `FuelError`, `ErrorCode` |
| Testing | `launchTestNode` (from `fuels/test-utils`) |

### Common call pattern

```ts
const provider = new Provider(URL);
const wallet = Wallet.fromPrivateKey(KEY, provider);
const { contract } = await (await MyContractFactory.deploy(wallet)).waitForResult();
const { value } = await (await contract.functions.my_method(arg).call()).waitForResult();
```

### CLI cheat-sheet

```bash
npm create fuels@latest        # scaffold a full-stack dApp
npx fuels init -o ./src/api    # create fuels.config.ts
npx fuels build                # compile Sway + generate types
npx fuels dev                  # build + deploy + watch
npx fuels deploy               # deploy to the configured network
npx fuels typegen -i ./out/*-abi.json -o ./src/api -c
npx fuels versions             # check version compatibility
```

---

_Generated June 2026 from FuelLabs/fuels-ts at b3f37c9. 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._
