Audience: Compiler engineers, toolchain contributors, language designers, blockchain infrastructure operators, and advanced integrators building on the Fuel ecosystem.
Table of Contents
- Architecture Overview
- System Requirements
- Configuration Reference
- Deployment & Scaling
- Operations
- Performance
- Security
- Developer Internals
- Integration Points
1. Architecture Overview
Sway is a domain-specific programming language targeting the FuelVM — the virtual machine powering the Fuel blockchain. It is syntactically and semantically inspired by Rust, providing ownership semantics, type inference, pattern matching, and generics, while being specifically designed for the constraints and capabilities of a UTXO-based blockchain execution environment.
The repository is a large Rust workspace (Cargo.toml at root) containing the
complete Sway toolchain: compiler, formatter, language server, package manager,
documentation generator, debugger, and migration assistant. Version 0.71.0,
source commit e12e611, spanning 8,121 files.
Note
The entire Sway toolchain — compiler, package manager, LSP, formatter, and CLI — is developed as a single Rust workspace. All crates share a unified dependency tree, enabling cross-crate type sharing without serialization overhead. The
forcCLI binary is the single user-facing entry point into this ecosystem.
1.1 Workspace Members
| Crate | Path | Role |
|---|---|---|
forc |
forc/ |
CLI entry point, subcommand dispatcher |
forc-pkg |
forc-pkg/ |
Package manager, dependency resolution, build plans |
forc-test |
forc-test/ |
Test framework runner |
forc-util |
forc-util/ |
Shared CLI utilities, bytecode parsing |
forc-debug |
forc-plugins/forc-debug/ |
DAP debug server |
forc-doc |
forc-plugins/forc-doc/ |
HTML documentation generator (swaydoc) |
forc-fmt |
forc-plugins/forc-fmt/ |
Formatter CLI frontend |
forc-lsp |
forc-plugins/forc-lsp/ |
LSP server wrapper |
forc-migrate |
forc-plugins/forc-migrate/ |
Breaking-change migration assistant |
forc-publish |
forc-plugins/forc-publish/ |
Package registry publisher |
forc-tx |
forc-plugins/forc-tx/ |
Transaction builder |
sway-ast |
sway-ast/ |
AST data structures for all language constructs |
sway-core |
sway-core/ |
Core compiler: type checking, IR gen, ASM gen |
sway-error |
sway-error/ |
Error and warning type definitions, handler |
sway-features |
sway-features/ |
Experimental feature flag management |
sway-ir |
sway-ir/ |
SSA-form intermediate representation |
sway-ir/sway-ir-macros |
sway-ir/sway-ir-macros/ |
Proc-macros for IR construction |
sway-lsp |
sway-lsp/ |
LSP implementation (capabilities + server loop) |
sway-parse |
sway-parse/ |
Lexer and recursive-descent parser |
sway-types |
sway-types/ |
Shared types: spans, identifiers, source maps |
sway-utils |
sway-utils/ |
Utility functions, manifest discovery, constants |
swayfmt |
swayfmt/ |
Code formatter library |
test |
test/ |
E2E, snapshot, and in-language test harness |
mdbook-forc-documenter |
scripts/mdbook-forc-documenter/ |
mdBook plugin for forc docs |
1.2 High-Level Data Flow
┌─────────────────────────────────────────────────────────────────────┐
│ forc build │
│ (CLI entry point) │
└───────────────────────────┬─────────────────────────────────────────┘
│
┌───────▼────────┐
│ forc-pkg │
│ Manifest parse│
│ Dep resolve │
│ Build plan │
└───────┬────────┘
│ per-package source files
┌─────────────▼──────────────┐
│ sway-parse │
│ Lex → Token stream │
│ Parse → sway-ast nodes │
└─────────────┬──────────────┘
│ parsed AST
┌─────────────▼──────────────┐
│ sway-core │
│ Type checking │
│ Declaration engine │
│ Control flow analysis │
└─────────────┬──────────────┘
│ typed AST
┌─────────────▼──────────────┐
│ sway-ir │
│ IR generation (SSA) │
│ Optimization passes │
└─────────────┬──────────────┘
│ optimized IR
┌─────────────▼──────────────┐
│ sway-core │
│ ASM generation │
│ Virtual → Allocated → Final│
└─────────────┬──────────────┘
│
┌─────────────▼──────────────┐
│ Output │
│ Bytecode (.bin) │
│ ABI (.json) │
│ Debug info │
└────────────────────────────┘
1.3 Program Types
Sway supports four distinct program types, each with different execution semantics and structural requirements:
| Program Type | Entry Point | Return Type | Purpose |
|---|---|---|---|
contract |
ABI function dispatch | () per function |
Deployed smart contract; has storage, ABI |
script |
main() |
Any | Executed once per transaction; no persistent storage |
predicate |
main() |
bool |
Authorization; pure function; no storage |
library |
(module root) | N/A | Reusable code; no entry point |
The program type is declared at the top of every Sway file:
contract; // or: script; predicate; library;
1.4 Plugin System Architecture
The forc binary implements an extensible plugin system: any binary named
forc-<name> found on PATH is automatically discovered and exposed as
forc <name>. This allows third-party plugins without modifying forc itself.
forc <cmd>
├── Built-in subcommands (new, build, check, clean, deploy, run, ...)
└── forc-* on PATH (forc-debug, forc-doc, forc-fmt, forc-lsp, ...)
Plugin discovery uses which-style PATH scanning at runtime. Each plugin
binary receives forwarded arguments and inherits the calling environment.
1.5 Build Targets
| Target | Description |
|---|---|
fuel |
Default; targets FuelVM bytecode via fuel-asm / fuel-vm |
evm |
Targets EVM bytecode via the EVM ABI and code generation path |
2. System Requirements
2.1 Build Dependencies
| Dependency | Version | Purpose |
|---|---|---|
| Rust toolchain | Stable (pinned in rust-toolchain.toml) |
Compile the workspace |
just |
>= 1.0 | Task runner (project-wide recipes) |
cargo |
Bundled with Rust | Build and test |
git |
>= 2.0 | Source control, forc-pkg git dependencies |
fuel-core node |
0.48.x | Required for E2E and node-based tests |
forc (bootstrapped) |
0.71.0 | Building Sway example projects |
2.2 Optional Build Dependencies
| Dependency | Purpose |
|---|---|
mdbook |
Building the Sway book |
cargo-nextest |
Faster parallel test execution |
cargo-sort |
Enforcing sorted Cargo.toml dependencies (CI) |
cargo-audit |
Security advisory scanning |
sccache |
Cross-invocation Rust compilation caching |
2.3 Runtime Requirements (forc)
| Requirement | Notes |
|---|---|
git on PATH |
Required for git-sourced dependencies in Forc.toml |
| Network access | For fetching remote dependencies (disable with --offline) |
fuel-core node |
Required for forc run, forc deploy, forc test E2E |
Write access to ~/.forc/ |
Cache directory for downloaded packages |
2.4 Platform Support
| Platform | Status |
|---|---|
| Linux x86_64 | Primary CI target; fully supported |
| macOS Apple Silicon | Supported; CI tested |
| macOS Intel | Supported |
| Windows x86_64 | Community supported; some path-handling edge cases |
2.5 Key Dependency Versions
| Crate | Version | Role |
|---|---|---|
fuel-vm |
0.66 | FuelVM execution engine |
fuel-tx |
0.66 | Fuel transaction types |
fuel-asm |
0.66 | Fuel assembly instruction set |
fuels |
0.77 | Fuel Rust SDK (SDK harness tests) |
fuel-core-client |
0.48.0 | Fuel node GraphQL client |
clap |
4.5 | CLI argument parsing |
tokio |
latest stable | Async runtime |
petgraph |
latest | Dependency DAG |
dashmap |
latest | Concurrent HashMap for LSP |
serde / serde_json / toml |
latest | Serialization |
lsp-types |
0.94 | LSP protocol types |
tower-lsp |
0.20 | LSP server framework |
horrorshow |
latest | HTML generation (forc-doc) |
tikv-jemallocator |
latest | Memory allocator (performance) |
3. Configuration Reference
3.1 Forc.toml — Package Manifest
Forc.toml is the package manifest, analogous to Cargo.toml. It is located
at the project root. The constant MANIFEST_FILE_NAME = "Forc.toml" is
defined in sway-utils.
[project] Section
[project]
name = "my_contract"
version = "0.1.0"
authors = ["Alice <[email removed]>"]
entry = "main.sw" # Default: main.sw (scripts/contracts) or lib.sw (libraries)
license = "Apache-2.0"
implicit-std = true # Whether to implicitly depend on sway-std
| Field | Type | Default | Description |
|---|---|---|---|
name |
String | required | Package name |
version |
String | required | SemVer package version |
authors |
String[] | [] |
Author list |
entry |
String | main.sw / lib.sw |
Entry file relative to src/ |
license |
String | "" |
SPDX license identifier |
implicit-std |
bool | true |
Auto-add sway-std dependency |
[dependencies] Section
[dependencies]
# Path dependency
my_lib = { path = "../my_lib" }
# Git dependency (tag, branch, or rev)
sway_standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.4.0" }
# IPFS dependency (CID-based)
my_package = { ipfs = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG" }
| Source Type | Fields | Notes |
|---|---|---|
| Path | path |
Relative or absolute filesystem path |
| Git | git, tag / branch / rev |
Fetched via git clone; cached in ~/.forc/ |
| IPFS | ipfs |
CID-based; fetched from IPFS network |
[patch] Section
[patch."https://github.com/FuelLabs/sway-standards"]
sway_standards = { path = "../local-standards" }
Overrides remote dependencies with local versions for development. Follows the
same semantics as Cargo's [patch].
[build-profile] Section
[build-profile.release]
optimization_level = "1" # "0" = Opt0 (default), "1" = Opt1
print_dasm = false
print_finalized_asm = false
print_ir = false
| Field | Values | Default | Description |
|---|---|---|---|
optimization_level |
"0", "1" |
"0" |
IR optimization aggressiveness |
print_dasm |
bool | false |
Print desugared ASM to stdout |
print_finalized_asm |
bool | false |
Print final allocated ASM |
print_ir |
bool | false |
Print generated SSA IR |
[workspace] Section (Multi-package Projects)
[workspace]
members = [
"contracts/my_contract",
"libraries/my_lib",
"scripts/my_script",
]
3.2 Forc.lock — Lockfile
Forc.lock is auto-generated and must not be edited manually. It records exact
resolved versions and content hashes for all transitive dependencies, ensuring
reproducible builds across machines and CI environments.
[[package]]
name = "core"
source = "path+from-root-..."
version = "0.0.0"
[[package]]
name = "sway_standards"
source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.4.0#abc123def"
version = "0.4.0"
dependencies = ["core", "std"]
Commit Forc.lock to version control for all non-library packages. Libraries
may omit it to avoid conflict with downstream consumers.
3.3 swayfmt.toml — Formatter Configuration
swayfmt.toml is the formatter configuration file. swayfmt searches from
the project directory upward to the filesystem root. If absent, all defaults
apply.
Important
swayfmt.tomlis optional. The built-in defaults produce fully valid, community-standard formatting. Only add this file when your team needs to deviate from the defaults. Commit it to version control and enforceforc fmt --checkin CI to prevent formatting drift.
Whitespace Section
[whitespace]
tab_spaces = 4 # Spaces per indent level
newline_style = "Auto" # "Auto" | "Unix" | "Windows" | "Native"
indent_style = "Block" # "Block" | "Visual"
| Field | Default | Options | Description |
|---|---|---|---|
tab_spaces |
4 |
integer | Spaces per indent |
newline_style |
Auto |
Auto, Unix, Windows, Native |
Line ending style |
indent_style |
Block |
Block, Visual |
Indentation alignment mode |
Imports Section
[imports]
group_imports = "StdlibFirst" # "Preserve" | "One" | "StdlibFirst"
import_granularity = "Preserve" # "Preserve" | "Item" | "Crate" | "Module"
| Field | Default | Options | Description |
|---|---|---|---|
group_imports |
StdlibFirst |
Preserve, One, StdlibFirst |
Import group ordering |
import_granularity |
Preserve |
Preserve, Item, Crate, Module |
Merge/split use trees |
Expressions Section
[expressions]
expr_brace_style = "SameLineWhere"
| Field | Default | Description |
|---|---|---|
expr_brace_style |
SameLineWhere |
Brace and where-clause placement style |
Literals Section
[literals]
hex_literal_case = "Preserve" # "Preserve" | "Upper" | "Lower"
Structures Section
[structures]
field_alignment = "Off" # "Off" | "AlignFields"
| Field | Default | Description |
|---|---|---|
field_alignment |
Off |
When AlignFields, aligns struct field type annotations |
Heuristics Section
Controls character-width thresholds that determine when the formatter breaks a line vs. keeps it inline. Values are integers representing character counts.
[heuristics]
# (threshold fields — specific names are format-version dependent)
Comments Section
Controls how inline and doc comments are reflowed and positioned relative to the code they annotate.
Ordering Section
Controls ordering of items within modules (e.g., whether use declarations
are grouped before fn declarations).
3.4 Environment Variables
| Variable | Purpose | Example |
|---|---|---|
FORC_EXPERIMENTAL |
Enable experimental features (comma-separated) | FORC_EXPERIMENTAL=new_encoding,references |
FORC_NO_EXPERIMENTAL |
Disable experimental features | FORC_NO_EXPERIMENTAL=new_hashing |
FORC_PUB_TOKEN |
Auth token for forc publish |
FORC_PUB_TOKEN=<registry-token> |
FORC_IMPLICIT_STD_PATH |
Override path to sway-std standard library |
FORC_IMPLICIT_STD_PATH=/path/to/sway-std |
FORC_IMPLICIT_STD_GIT |
Override git URL for implicit std | FORC_IMPLICIT_STD_GIT=https://github.com/... |
FORC_IMPLICIT_STD_GIT_TAG |
Pin std to specific git tag | FORC_IMPLICIT_STD_GIT_TAG=v0.71.0 |
FORC_IMPLICIT_STD_GIT_BRANCH |
Pin std to specific branch | FORC_IMPLICIT_STD_GIT_BRANCH=master |
CARGO_MANIFEST_DIR |
Test harness: locates test assets | Set automatically by Cargo during cargo test |
RUST_BACKTRACE |
Enable Rust backtraces for ICE debugging | RUST_BACKTRACE=full |
3.5 Experimental Feature Flags
Managed by the sway-features crate. Features are toggled via environment
variables (future: Forc.toml support planned).
| Feature | Flag Name | Description |
|---|---|---|
| New encoding | new_encoding |
Revised ABI encoding scheme |
| References | references |
Reference types (&T, &mut T) in Sway |
| New hashing | new_hashing |
Updated hash primitive semantics |
| Str array no padding | str_array_no_padding |
Removes padding from string array encoding |
| Dynamic storage | dynamic_storage |
Dynamic storage key computation at runtime |
# Enable multiple features
FORC_EXPERIMENTAL=new_encoding,references forc build
# Disable a feature that would otherwise be on
FORC_NO_EXPERIMENTAL=dynamic_storage forc build
Important
Experimental features may introduce breaking changes between minor versions. Never enable experimental features in production contracts without thorough testing. The
new_encodingfeature changes the ABI wire format — enabling it on an already-deployed contract is a breaking change for all callers.
4. Deployment & Scaling
4.1 Building the Toolchain from Source
# Clone the repository
git clone https://github.com/FuelLabs/sway.git
cd sway
# Install Rust toolchain (respects rust-toolchain.toml)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# Build all workspace members (debug)
cargo build --workspace
# Build release binaries
cargo build --release --workspace
# Install forc and all plugins to ~/.cargo/bin
cargo install --path forc
cargo install --path forc-plugins/forc-fmt
cargo install --path forc-plugins/forc-lsp
cargo install --path forc-plugins/forc-debug
cargo install --path forc-plugins/forc-doc
cargo install --path forc-plugins/forc-migrate
cargo install --path forc-plugins/forc-publish
cargo install --path forc-plugins/forc-tx
4.2 Installing via fuelup (Recommended for End Users)
fuelup is the Fuel toolchain manager (analogous to rustup). It manages
multiple forc versions and the associated fuel-core node binary.
# Install fuelup
curl https://install.fuel.network | sh
# Install the latest stable Fuel toolchain
fuelup toolchain install latest
fuelup default latest
# List installed toolchains
fuelup toolchain list
# Install a specific version
fuelup toolchain install nightly
fuelup toolchain install v0.71.0
# Verify installation
forc --version
fuel-core --version
4.3 CI/CD Pipeline Integration
Full Toolchain CI (Contributing to Sway)
name: Sway Toolchain CI
on: [push, pull_request]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust (pinned via rust-toolchain.toml)
uses: dtolnay/rust-toolchain@stable
- name: Install just
uses: extractions/setup-just@v1
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Check sorted dependencies
run: cargo install cargo-sort && cargo sort --check --workspace
- name: Lint
run: cargo clippy --all-targets --workspace -- -D warnings
- name: Format check
run: cargo fmt --check --all
- name: Build
run: cargo build --release --workspace
- name: Test
run: cargo test --workspace
Sway Project CI (For Contract Developers)
name: Contract CI
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install fuelup
run: curl https://install.fuel.network | sh
- name: Install Fuel toolchain
run: |
fuelup toolchain install latest
fuelup default latest
- name: Format check
run: forc fmt --check
- name: Build (locked — no dependency updates)
run: forc build --locked
- name: Run tests
run: forc test --locked
4.4 Package Dependency Cache
forc-pkg caches downloaded packages under ~/.forc/:
~/.forc/
cache/
git/
<repo-hash>/ # Bare git clones of dependencies
HEAD
config
objects/
ipfs/
<cid>/ # IPFS-fetched package contents
Cache invalidation is keyed by content hash, so stale entries are never silently
used. In containerized CI, mount ~/.forc/cache as a named volume or use the
CI platform's cache action to persist across runs.
Note
Cache entries are never automatically pruned. In long-running CI systems, periodically clear
~/.forc/cache/git/if disk space becomes a concern. The next build will re-fetch and re-validate all dependencies.
4.5 Cross-Compilation (Compiler Binary)
The Sway compiler is a Rust program and cross-compiles using standard Rust tooling. The generated Sway bytecode always targets FuelVM or EVM regardless of the host architecture.
# Build forc for Linux ARM64 on Linux x86_64
rustup target add aarch64-unknown-linux-gnu
cargo build --release --target aarch64-unknown-linux-gnu -p forc
4.6 Workspace Builds (Multi-package Projects)
# Build all workspace members
forc build --path /workspace/root
# Check all members without codegen (fast)
forc check --path /workspace/root
# Build and test a specific member
forc build --path /workspace/root/contracts/token
forc test --path /workspace/root/contracts/token
# Locked offline build for reproducibility
forc build --path /workspace/root --locked --offline
4.7 Output Artifacts
forc build produces output in the out/ directory by default:
out/
debug/ # or release/ when --release is used
<name>.bin # FuelVM bytecode
<name>-abi.json # JSON ABI
<name>-storage_slots.json # Storage slot initializers
Override output directory:
forc build --output-dir ./artifacts/
forc build --output-bin-path ./artifacts/my_contract.bin
5. Operations
5.1 forc CLI Subcommands
The forc CLI (forc/src/main.rs → forc/src/cli/mod.rs) dispatches to
16 built-in subcommands plus any discovered forc-* plugins.
Note
Run
forc help <subcommand>orforc <subcommand> --helpfor complete argument documentation. All subcommands accept--path <dir>to specify the project root (defaults to current working directory).
| Subcommand | Purpose |
|---|---|
forc new |
Scaffold a new Sway project |
forc init |
Initialize Sway project in existing directory |
forc build |
Compile Sway project to bytecode + ABI |
forc check |
Type-check without codegen |
forc clean |
Remove build artifacts (out/ directory) |
forc deploy |
Deploy contract to a Fuel network |
forc run |
Run a script against a Fuel node |
forc test |
Run in-language unit tests |
forc update |
Update dependencies in Forc.lock |
forc add |
Add a dependency to Forc.toml |
forc remove |
Remove a dependency from Forc.toml |
forc contract-id |
Compute deterministic contract ID |
forc parse-bytecode |
Disassemble and display compiled bytecode |
forc completions |
Generate shell completion scripts |
forc plugins |
List all discovered forc-* plugins |
forc addr2line |
Resolve bytecode addresses to source locations |
5.2 forc build — Complete Flag Reference
forc build [OPTIONS]
Core Options:
--path <PATH> Project root [default: .]
--release Enable Opt1 IR optimizations
--profile <PROFILE> Use named build profile from Forc.toml
--target <TARGET> Build target: fuel (default) or evm
--locked Require Forc.lock to be current; fail if not
--offline No network access; use cache only
--ipfs-node <URL> Custom IPFS gateway URL
Output Options:
--output-dir <DIR> Override output directory [default: out/]
--output-bin-path <PATH> Override output .bin path
--json-abi Emit ABI as JSON to stdout
--json-abi-with-callpaths Emit ABI with fully-qualified call paths
Diagnostic Options:
--print-ast Print typed AST to stdout
--print-dasm Print desugared ASM
--print-finalized-asm Print final allocated ASM
--print-ir Print generated SSA IR
--time-phases Print wall-clock timing per compiler phase
--ir-opt <PASSES> Comma-separated IR optimization pass list
--error-on-warnings Treat all warnings as errors
Verbosity Options:
-v, --verbose Verbose output
-s, --silent Suppress all output
--terse Suppress warnings in output
5.3 forc test — Complete Flag Reference
forc test [OPTIONS] [TEST_FILTER]
Options:
--path <PATH> Project root
--release Optimize before testing (Opt1)
--logs Display VM execution logs during tests
--raw-log Display raw log bytes
--filter-exact Match test names exactly (substring match by default)
--count Print test count without running
--node-url <URL> Fuel node URL for integration tests
--default-salt Use deterministic salt for reproducible contract IDs
--locked Require lockfile to be current
--offline No network; cache only
5.4 forc-debug — DAP Server
forc-debug implements the Debug Adapter Protocol (DAP), enabling IDE-integrated
debugging of Sway contracts and scripts running against a live Fuel node.
# Start DAP server connecting to fuel-core at default address
forc debug --serve
# Connect to a remote or non-default node
forc debug --serve --api-url http://192.168.1.100:4000/graphql
| Option | Default | Description |
|---|---|---|
--api-url |
http://127.0.0.1:4000/graphql |
Fuel node GraphQL endpoint |
--serve |
off | Start DAP server on stdin/stdout |
The DAP server translates between the DAP protocol (VS Code, Neovim DAP, etc.) and the Fuel node's debug API, supporting:
- Breakpoints (by source line and bytecode offset)
- Step over / step into / step out
- Register and memory inspection
- Stack frame navigation
- Variable watches
5.5 forc-lsp — Language Server
# Start LSP server (editor launches this via stdin/stdout)
forc lsp
# Verbose LSP logging (useful for LSP client debugging)
forc lsp --verbose
LSP capabilities implemented in sway-lsp:
| Capability | Supported |
|---|---|
| Text document sync | Full (on save) |
| Publish diagnostics | Yes (errors + warnings) |
| Hover (type info, doc comments) | Yes |
| Go-to-definition | Yes |
| Find all references | Yes |
| Completion (items, keywords) | Yes |
| Code actions | Yes |
| Rename symbol | Yes |
| Semantic tokens | Yes |
| Document symbols | Yes |
| Workspace symbols | Yes |
| Signature help | Yes |
Formatting (forc fmt integration) |
Yes |
5.6 forc-doc — Documentation Generation
# Generate HTML docs for current package
forc doc
# Open in default browser after generation
forc doc --open
# Generate docs for all workspace members
forc doc --workspace
# Specify output directory
forc doc --out-dir ./docs/
Output is placed in out/doc/ relative to the project root. HTML is generated
using the horrorshow templating library following swaydoc conventions
(styled similar to rustdoc).
5.7 forc-publish — Package Registry
# Publish current package
FORC_PUB_TOKEN=<token> forc publish
# Dry run — validate without actually publishing
forc publish --dry-run
# Specify registry URL
forc publish --registry https://registry.fuel.network
# Verify your token is set
echo $FORC_PUB_TOKEN
Important
FORC_PUB_TOKENmust be set in the environment. Never hard-code registry tokens inForc.tomlor commit them to version control. Store them in CI secrets (GitHub Actions:Settings → Secrets and variables → Actions).
5.8 forc-migrate — Breaking Change Migration
forc-migrate automates source-level migrations when Sway introduces breaking
language changes. The plugin contains 10 migration definitions (~51K chars).
# List available migrations and which apply to current code
forc migrate list
# Apply all applicable migrations to current project
forc migrate
# Preview changes without modifying files
forc migrate --dry-run
# Apply a specific migration
forc migrate <migration-name>
Note
Always run
forc migrate --dry-runand review the diff before applying. Commit the un-migrated code first so the migration is a separate, reviewable commit.
5.9 forc-fmt — Code Formatter
# Format all .sw files in project
forc fmt
# Check formatting in CI (exit code 1 if any file needs changes)
forc fmt --check
# Format a specific file
forc fmt --path src/main.sw
# Use a specific config file
forc fmt --config-path /path/to/swayfmt.toml
The formatter is deterministic and idempotent: running it twice on any file
produces identical output. All contributions to the Sway repository must pass
forc fmt --check in CI.
5.10 forc-tx — Transaction Builder
# Build a transaction (subcommands vary by tx type)
forc tx create [OPTIONS]
forc tx script [OPTIONS]
forc-tx is used for constructing raw Fuel transactions programmatically,
particularly useful for testing contract interactions outside of the SDK.
5.11 Operational Runbooks
Diagnosing a Compilation Failure
# Step 1: Full verbose output
RUST_BACKTRACE=1 forc build --verbose 2>&1 | tee build.log
# Step 2: Check which phase failed
forc build --time-phases 2>&1 | tail -20
# Step 3: Inspect IR for type/codegen issues
forc build --print-ir 2>&1 | tee ir_dump.txt
# Step 4: Inspect typed AST for type checker issues
forc build --print-ast 2>&1 | tee ast_dump.txt
# Step 5: Inspect final ASM for code generation issues
forc build --print-finalized-asm 2>&1 | tee asm_dump.txt
Warning
If the compiler panics (ICE — Internal Compiler Error), this is always a bug in the compiler, not your code. File an issue at https://github.com/FuelLabs/sway/issues with the full panic message,
RUST_BACKTRACE=fulloutput, and the smallest possible reproducer.
Resolving Dependency Conflicts
# Update all dependencies to latest compatible versions
forc update
# Update a specific dependency
forc update sway_standards
# View the full dependency tree
# (if forc-tree plugin is installed)
forc tree
# Force re-resolve (delete lockfile and rebuild)
rm Forc.lock && forc build
Warning
Deleting
Forc.lockin a production project may pull in newer transitive dependency versions that introduce breaking changes. Preferforc update <specific-dep>for surgical dependency updates.
Clearing the Package Cache
# Clear all cached packages (will re-fetch on next build)
rm -rf ~/.forc/cache/
# Clear only git dependency caches
rm -rf ~/.forc/cache/git/
# Clear only IPFS caches
rm -rf ~/.forc/cache/ipfs/
Verifying a Build is Reproducible
# Build 1
forc build --locked --offline
sha256sum out/debug/my_contract.bin > hash1.txt
# Clean and rebuild
forc clean
forc build --locked --offline
sha256sum out/debug/my_contract.bin > hash2.txt
# Compare (should be identical)
diff hash1.txt hash2.txt
6. Performance
6.1 Compiler Performance Characteristics
The Sway compiler is designed for incremental workloads in LSP contexts.
sway-lsp maintains a persistent in-memory typed program state, recompiling
only on file save. For batch forc build, performance scales approximately
linearly with total source size.
Note
The LSP uses
dashmap(lock-free concurrent HashMap) throughoutsway-lspto allow parallel read-heavy IDE operations (hover, completion, references) while serializing write-heavy compilation updates. This provides sub-10ms response times for most IDE interactions on typical contract codebases.
6.2 Optimization Levels
| Level | forc build flag |
Forc.toml value |
Description |
|---|---|---|---|
| Opt0 | (default) | optimization_level = "0" |
No IR optimization; fastest compile time |
| Opt1 | --release |
optimization_level = "1" |
Full IR optimization suite |
Use Opt0 during development for fast iteration. Opt1 for production: the resulting bytecode is meaningfully smaller (fewer instructions) and faster (fewer redundant computations on-chain, lowering gas costs).
6.3 IR Optimization Passes
The sway-ir pass manager applies passes in a configured sequence. Individual
passes can be selected with --ir-opt <pass1,pass2,...>.
| Pass | Kebab Name | Description |
|---|---|---|
| Constant folding | const-folding |
Evaluate pure constant expressions at compile time |
| Constant demotion | const-demotion |
Spill large constants from registers to memory |
| Function inlining | fn-inline |
Inline small or single-call functions |
| Dead code elimination | dce |
Remove unreachable code and unused values |
| Scalar replacement of aggregates | sroa |
Decompose struct accesses into scalar operations |
| Simplify CFG | simplify-cfg |
Merge basic blocks, remove unreachable blocks |
| Memory to register | mem2reg |
Promote stack allocations to SSA registers |
| Loop invariant code motion | licm |
Hoist loop-invariant computations out of loops |
Custom pass sequence example:
forc build --release --ir-opt "const-folding,fn-inline,dce,simplify-cfg,mem2reg"
6.4 Performance Benchmarking with just
# End-to-end compilation performance benchmark
just perf-e2e
# In-language test execution performance
just perf-in-lang
# Run all benchmarks
just perf-all
# Diff current performance against the previous baseline
just perf-diff
# Diff against the latest published release
just perf-diff-latest
Performance data is collected with the --perf flag on the test harness,
measuring wall-clock time per compiler phase.
6.5 Memory Allocator
tikv-jemallocator is configured as the global Rust allocator for forc and
other binaries. jemalloc outperforms the system allocator for compiler workload
patterns (high rates of small, short-lived allocations during parsing and type
checking).
// Applied in forc/src/main.rs
#[cfg(feature = "jemalloc")]
#[global_allocator]
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
6.6 Build Caching with sccache
For large CI environments building the Sway compiler itself frequently:
# Install sccache
cargo install sccache
# Set sccache as the Rust compiler wrapper
export RUSTC_WRAPPER=sccache
# Build with caching
cargo build --release --workspace
# View cache statistics
sccache --show-stats
6.7 Parallel Package Builds
forc-pkg constructs a dependency DAG using petgraph and builds packages in
topological order. Independent packages are built in parallel (thread count
defaults to number of logical CPUs, configurable with --jobs/-j):
# Use 8 parallel jobs
forc build --jobs 8
# Use half the available CPUs
forc build --jobs $(nproc --ignore=1)
7. Security
7.1 Open Security Advisories
Caution
Two active RUSTSEC advisories affect the Sway toolchain dependency tree as of source commit e12e611 (version 0.71.0). Review these before deploying build infrastructure that processes untrusted Sway source code.
RUSTSEC-2025-0055 — ANSI Escape Sequence Log Poisoning (Issue #7589)
Affected: A logging/terminal display dependency in the toolchain.
Risk: Maliciously crafted content — such as a package name or compiler error message containing ANSI escape sequences — could manipulate terminal output. In CI systems displaying raw build logs, this could suppress or hide critical warnings from human reviewers.
Mitigation:
# Strip ANSI codes from forc output in automated pipelines
forc build 2>&1 | sed 's/\x1B\[[0-9;]*[mK]//g' > build.log
# Or use --terse to reduce styled output
forc build --terse 2>&1 > build.log
- Pin the affected transitive dependency to a patched version once available.
- Use
--json-abifor programmatic ABI consumption instead of parsing terminal output.
RUSTSEC-2026-0049 — CRL Matching Logic Flaw (Issue #7576)
Affected: TLS certificate validation in the HTTPS dependency-fetching path.
Risk: Certificate Revocation Lists (CRLs) may not be properly honored during git or IPFS dependency fetching. A revoked TLS certificate for a dependency host could be accepted, enabling potential man-in-the-middle attacks on dependency downloads.
Mitigation:
# Use --locked --offline for all production/CI builds
# This prevents dependency fetching entirely
forc build --locked --offline
- Fetch dependencies on a separate, network-isolated build step; then pass
--offlineto the actual compilation step. - Apply the patched dependency version once the upstream fix is merged and released.
Warning
Never run
forc buildagainst untrustedForc.tomlfiles without sandboxing. A maliciousForc.tomlcould causeforc-pkgto fetch and compile arbitrary Sway source from remote git repositories. Use--locked --offlineto prevent this, or run in an isolated container with--network none.
7.2 Supply Chain Security
Dependency Pinning
# Audit all Rust dependencies for known advisories
cargo install cargo-audit
cargo audit
# Generate a Software Bill of Materials
cargo install cargo-sbom
cargo sbom > sbom.json
# Ensure Forc.lock is committed and unchanged
git diff --exit-code Forc.lock || (echo "Forc.lock has uncommitted changes" && exit 1)
Reproducible Builds
# Always use --locked in CI to prevent accidental dependency updates
forc build --locked
# Verify output bytecode hash against a known-good value
EXPECTED="sha256:abc123..."
ACTUAL=$(sha256sum out/debug/my_contract.bin | awk '{print "sha256:"$1}')
[ "$EXPECTED" = "$ACTUAL" ] || (echo "Bytecode mismatch!" && exit 1)
7.3 Sandboxing Untrusted Builds
When compiling untrusted Sway source (e.g., third-party contract submissions or security research):
# Minimal Docker sandbox
docker run --rm \
--network none \
--read-only \
--tmpfs /tmp:exec,size=512m \
-v /path/to/project:/workspace:ro \
-v /path/to/output:/out:rw \
ghcr.io/fuellabs/fuel-toolchain:latest \
forc build \
--path /workspace \
--output-dir /out \
--locked \
--offline
# Even more restricted: drop all capabilities
docker run --rm \
--network none \
--read-only \
--tmpfs /tmp:exec,size=512m \
--cap-drop ALL \
--security-opt no-new-privileges \
-v /path/to/project:/workspace:ro \
-v /path/to/output:/out:rw \
ghcr.io/fuellabs/fuel-toolchain:latest \
forc build --path /workspace --output-dir /out --locked --offline
Key isolation measures:
--network none— Preventsforc-pkgfrom fetching remote dependencies--read-only— Source is read-only; builds go to a writable volume--locked --offline— Enforces pre-resolved lockfile; no network access--cap-drop ALL— No Linux capabilities (prevents privilege escalation)
7.4 CEI Pattern Analysis — Reentrancy Detection
The Sway compiler performs Checks-Effects-Interactions (CEI) pattern analysis to detect reentrancy vulnerability patterns at compile time. A CEI violation occurs when a storage write ("effect") is performed after an external contract call ("interaction").
contract;
abi VulnerableContract {
fn withdraw(amount: u64);
}
impl VulnerableContract for Contract {
fn withdraw(amount: u64) {
let sender = msg_sender().unwrap();
// INTERACTION: external call before state update
transfer(amount, AssetId::base(), sender);
// EFFECT: storage update after — CEI violation warning
storage.balance.write(storage.balance.read() - amount);
}
}
Correct CEI pattern:
fn withdraw(amount: u64) {
let sender = msg_sender().unwrap();
// EFFECT first: update state
storage.balance.write(storage.balance.read() - amount);
// INTERACTION after: now safe
transfer(amount, AssetId::base(), sender);
}
Warning
CEI warnings are currently nondeterministic (GitHub issue #7606). The same source file may or may not trigger the warning across different compilations. Do not rely solely on the CEI checker. Apply manual code review, test reentrancy scenarios explicitly, and treat any CEI warning as a definitive finding requiring immediate remediation.
To treat CEI violations as hard errors:
forc build --error-on-warnings
7.5 Compiler Security Properties
| Property | Implementation |
|---|---|
| No arbitrary OS syscalls | FuelVM is a sandboxed virtual machine; no OS access |
| Memory safety (compiler) | Rust ownership system throughout the compiler |
| Memory safety (contracts) | FuelVM has no raw pointer arithmetic |
| Integer overflow | Sway panics on overflow in debug; configurable in release |
| Reentrancy detection | CEI pattern analysis at compile time (with caveats) |
| Immutable storage keys | Storage key layout computed deterministically from ABI |
| Deterministic contract IDs | Contract ID = hash of bytecode + salt; fully deterministic |
7.6 ABI Encoding Security
The generated ABI JSON specifies the canonical encoding for all public contract functions. Incorrect encoding causes transaction revert (not silent data corruption). Key points:
- Function selectors are 4-byte SHA-256 prefixes of the canonical signature
- Parameter encoding is strict; mismatches revert
- The
new_encodingexperimental feature changes the wire format — enabling it on an already-deployed contract breaks all callers using the old format - Configurable constants (
configurables) are embedded in bytecode at deploy time and cannot be changed post-deployment
8. Developer Internals
8.1 Compiler Pipeline — Phase-by-Phase Analysis
Phase 1: Parsing (sway-parse)
Source size: 17 files, 121K characters
The parser is a hand-written recursive-descent parser operating in two stages:
Lexer tokenizes source text into a typed token stream:
- Keywords (
fn,let,match,if,contract, ...) - Identifiers, integer literals, string literals, boolean literals
- Operators, punctuation, delimiters
- Doc comments (
///) preserved as AST nodes - Full byte-offset span tracking per token
Parser consumes tokens and produces sway-ast node trees:
- Uses
ParseToEndandParseBracketcombinators for delimiter-bounded sequences - Recursive descent with direct precedence climbing for expressions
- Error recovery is minimal; most parse errors are immediately fatal
- All produced nodes carry
Spaninformation for downstream error reporting
Key public entry point:
pub fn parse_file(
handler: &Handler,
src: Arc<str>,
path: Option<Arc<PathBuf>>,
) -> Result<ParsedTree, ErrorEmitted>
Phase 2: AST Representation (sway-ast)
sway-ast defines the concrete syntax tree — a direct structural reflection
of the source text. All nodes carry Span. The AST is NOT yet type-annotated
at this stage.
Key node families:
| Family | Node Examples |
|---|---|
| Items (top-level declarations) | ItemFn, ItemImpl, ItemTrait, ItemStruct, ItemEnum, ItemStorage, ItemConst, ItemUse, ItemConfigurable |
| Expressions | ExprIf, ExprMatch, ExprBlock, ExprCall, ExprMethodCall, ExprBinary, ExprUnary, ExprIndex, ExprTuple |
| Type expressions | TyTuple, TyArray, TyPath, TyInfer, TyStr, TyPrimitive, TyPtr, TySlice |
| Patterns | PatLiteral, PatIdent, PatTuple, PatStruct, PatEnum, PatWildcard, PatOr |
| Statements | StatementLet, StatementExpr |
Phase 3: Type Checking (sway-core — type_system/)
Source size: 282K characters — the largest single subsystem
The type checker performs six major operations:
-
Name resolution — Resolves identifiers to declarations in the namespace, traversing module boundaries and handling
useimports. -
Type inference — Bidirectional type inference with constraint propagation. Types flow both top-down (from context) and bottom-up (from literals and calls).
-
Monomorphization — Instantiates generic functions and types with concrete type arguments. Memoized in the declaration engine to avoid duplicate work.
-
Trait resolution — Resolves which
impl Trait for Tblock satisfies each trait constraint, checking that all associated types and methods are provided. -
Type unification — Merges type variables with concrete types during inference. Failed unification produces type mismatch errors.
-
Coercion — Applies implicit coercions where permitted (e.g.,
b256toAddress).
The type system uses TypeId handles (integer indices) into the TypeEngine
to represent all types, avoiding recursive data structures:
// The TypeEngine maps TypeId -> TypeInfo
pub enum TypeInfo {
Unknown,
UnknownGeneric { name: Ident, trait_constraints: VecSet<TraitConstraint> },
Placeholder(TypeParameter),
TypeParam(usize),
StringSlice,
StringArray { length: Length },
UnsignedInteger(IntegerBits), // U8, U16, U32, U64, U256
Boolean,
Tuple(Vec<TypeArgument>),
B256,
Numeric, // Unresolved numeric literal
Contract,
ErrorRecovery(ErrorEmitted),
Array(TypeArgument, Length),
RawUntypedPtr,
RawUntypedSlice,
Ptr(TypeArgument),
Slice(TypeArgument),
Enum(DeclRefEnum),
Struct(DeclRefStruct),
ContractCaller { abi_name: AbiName, address: Box<TypedExpression> },
Custom { qualified_call_path: QualifiedCallPath, type_arguments: Option<Vec<TypeArgument>> },
SelfType,
Alias { name: Ident, ty: TypeArgument },
TraitType { name: Ident, trait_type_id: TraitTypeId },
}
Known active issues:
- #7603 — ICE from invalid type unification of arrays (type variable scope escape)
- #7604 — Compiler panic with type aliases + const generic arrays
- #7602 — ICE when indexing
self[i]with const generic index - #7605 — Confusing out-of-bounds error for const generic arrays
Phase 4: Declaration Engine (sway-core — decl_engine/)
Source size: 93K characters
The declaration engine manages the lifecycle of all named declarations using opaque integer handles, decoupling declaration identity from type-checking state. This enables incremental recompilation: unchanged declarations retain their handles and cached type information across rebuilds.
Core types:
pub struct DeclEngine {
// Stores all typed declarations by DeclId
// Supports: function, struct, enum, trait, impl, ...
}
// Typed handle to a specific declaration
pub struct DeclRef<T> {
pub name: Ident,
pub id: DeclId,
pub decl_span: Span,
_marker: PhantomData<T>,
}
pub struct DeclId(usize); // Opaque integer
The declaration engine also caches monomorphized instances, so
Vec<u64> and Vec<bool> are each generated once regardless of how
many times they appear in the codebase.
Phase 5: Control Flow Analysis
Two analysis passes run on the fully typed AST:
Dead Code Analysis
- Constructs a reachability graph from all entry points
- Marks unused functions, variables, struct fields, and
useimports - Emits
Warning::DeadCodevariants for each unused item - Respects visibility:
pubitems are considered reachable even if not locally called
CEI Pattern Analysis
- Scans each function body for the pattern: external contract call followed by storage write
- Constructs a partial temporal order of side-effecting operations
- Emits
Warning::StorageWriteAfterContractCallon violations - Known issue #7606: HashMap iteration order affects which violations are detected in which order; the set of reported violations can vary between invocations
Phase 6: IR Generation (sway-ir)
Source size: 481K characters, 21 files
The compiler lowers the typed AST to SSA-form (Static Single Assignment) intermediate representation. Every variable is assigned exactly once; phi nodes merge values at control flow join points.
IR container hierarchy:
Module
└── Function (one per Sway function)
├── Argument list
└── Block (basic block: sequential instructions, ends with branch/ret)
└── Instruction (SSA value-producing or effect-producing)
All IR instruction variants:
| Instruction | Description |
|---|---|
AsmBlock |
Inline assembly block (raw FuelASM) |
BitCast |
Bitwise reinterpretation between types of equal size |
BinaryOp |
Arithmetic (+, -, *, /, %) and bitwise (&, |
Branch |
Unconditional jump to a basic block |
Call |
Internal function call |
CastPtr |
Pointer type cast (changes pointee type, not bits) |
Cmp |
Comparison: eq, ne, lt, le, gt, ge |
ConditionalBranch |
Conditional jump: if cond goto true_bb else false_bb |
ContractCall |
External contract invocation via ABI |
ExtractElement |
Extract element from array or slice |
ExtractValue |
Extract field from struct/tuple by index path |
FuelVm(FuelVmInstruction) |
FuelVM-specific: Log, Revert, Smo, StateClear, StateLoadQuadWord, StateLoadWord, StateStoreQuadWord, StateStoreWord, WideUnaryOp, WideBinaryOp, WideCmpOp, WideModularOp |
GetElemPtr |
GEP-style: compute pointer to sub-element of aggregate |
GetLocal |
Get pointer to a local (stack) variable |
GetGlobal |
Get pointer to a module-level global |
InsertElement |
Insert element into array/slice (returns new value) |
InsertValue |
Insert field into struct/tuple (returns new value) |
IntToPtr |
Cast integer to raw pointer |
Load |
Dereference pointer |
MemCopyBytes |
Copy N bytes between two pointers |
MemCopyVal |
Typed copy between two pointers |
Nop |
No-operation (placeholder) |
PtrToInt |
Cast pointer to integer |
Ret |
Return from function |
Revert |
Revert the transaction with a code |
Store |
Write value through pointer |
IR text format for debugging:
// Example IR snippet (schematic)
fn my_function(a: u64, b: u64) -> u64 {
entry(a: u64, b: u64):
v0 = add a, b
ret u64 v0
}
# Dump IR to stdout during build
forc build --print-ir
# Parse IR text back to IR data structure (for testing)
# (internal tool; no direct forc command)
Phase 7: IR Optimization
The PassManager orchestrates optimization passes. Each pass implements:
pub trait Pass {
fn name(&self) -> &str;
fn descr(&self) -> &str;
fn run(
&self,
module: &mut Module,
analysis_results: &AnalysisResults,
) -> Result<bool, IrError>; // bool = whether the IR was modified
fn analyses_required(&self) -> Vec<AnalysisName>;
}
The pass manager tracks which analyses are valid after each pass and
re-runs invalidated analyses before dependent passes execute. The default
Opt1 pass sequence is configured internally in sway-core.
Phase 8: ASM Generation (sway-core — asm_generation/)
Source size: 196K characters
Three-stage lowering from IR to bytecode:
Stage 1 — Virtual ASM
- Architecture-agnostic assembly
- Unlimited virtual registers (VRs)
- IR instructions mapped to FuelASM instruction sequences
- Memory layout computed for all locals and aggregates
Stage 2 — Allocated ASM
- Virtual registers mapped to physical FuelVM registers (r0–r63) via linear scan
- Spill code inserted for registers that exceed the physical count
- Calling convention enforced (argument passing, return values)
Stage 3 — Final ASM
- Labels resolved to byte offsets
- Jump targets patched
- Inline assembly blocks integrated
- Final instruction stream ready for bytecode emission
Phase 9: ABI Generation
Fuel ABI generator (fuel_abi.rs): produces the standard Fuel JSON ABI
format, including:
- All public contract functions with canonical type encoding
- Function selector (4-byte SHA-256 prefix of canonical signature)
- Storage read/write attributes
payableattribute- Log types
- Configurable constants
EVM ABI generator (evm_abi.rs): produces EVM-compatible Solidity ABI
JSON for contracts targeting the EVM build target.
8.2 The Engines Architecture
sway-core uses a central Engines struct to thread shared mutable state
through the compiler without passing arguments everywhere:
pub struct Engines {
pub type_engine: TypeEngine,
pub decl_engine: DeclEngine,
pub parsed_decl_engine: ParsedDeclEngine,
pub query_engine: QueryEngine,
pub source_engine: SourceEngine,
}
impl Engines {
pub fn te(&self) -> &TypeEngine { &self.type_engine }
pub fn de(&self) -> &DeclEngine { &self.decl_engine }
pub fn se(&self) -> &SourceEngine { &self.source_engine }
}
This pattern:
- Makes engine access explicit and auditable (not global state)
- Enables testing with isolated engine instances
- Allows the query engine to track dependencies between engine accesses for incremental compilation invalidation
8.3 Module Reference — All Workspace Crates
sway-parse
| Component | Location | Description |
|---|---|---|
| Lexer | src/lexer/ |
Tokenizes source text; produces TokenStream |
| Parser entry | src/lib.rs |
parse_file() public API |
| Expression parser | src/expr/ |
All expression parsing rules |
| Item parser | src/item/ |
Top-level declaration parsing |
| Type parser | src/ty/ |
Type expression parsing |
| Pattern parser | src/pattern/ |
Pattern matching construct parsing |
| Combinators | src/priv_prelude.rs |
ParseToEnd, ParseBracket, Peek |
sway-ast
| Component | Location | Description |
|---|---|---|
| Expressions | src/expr/ |
All expression AST variants |
| Items | src/item/ |
All declaration AST variants |
| Types | src/ty.rs |
Type expression variants |
| Patterns | src/pattern.rs |
Pattern variants |
| Literals | src/literal.rs |
Literal value types |
sway-core
| Sub-module | Approx Size | Description |
|---|---|---|
type_system/ |
282K chars | Type inference, unification, monomorphization, trait resolution |
decl_engine/ |
93K chars | Declaration lifecycle, handle-based storage |
asm_generation/ |
196K chars | Virtual → Allocated → Final assembly |
ir_generation/ |
significant | Typed AST → sway-ir lowering |
control_flow_analysis/ |
moderate | Dead code, CEI analysis |
language/ |
significant | Typed AST node definitions (parallel to sway-ast) |
semantic_analysis/ |
significant | Name resolution, namespace management |
transform/ |
moderate | AST desugaring and normalization passes |
query_engine/ |
moderate | Query-based incremental compilation interface |
sway-ir
| Component | File | Description |
|---|---|---|
| Module | src/module.rs |
Top-level IR container |
| Function | src/function.rs |
IR function (args, return type, blocks) |
| Basic Block | src/block.rs |
Sequence of instructions + terminator |
| Instruction | src/instruction.rs |
All IR instruction variants |
| IR Types | src/irtype.rs |
IR type system (flat, no generics) |
| Values | src/value.rs |
SSA values: instructions + constants |
| Constants | src/constant.rs |
Compile-time constants |
| Pass Manager | src/pass_manager.rs |
Pass ordering and execution |
| Analyses | src/analysis/ |
Dominators, use-def chains, call graph |
| Passes | src/passes/ |
One file per optimization pass |
| Printer | src/printer.rs |
IR to text format |
| Parser | src/parser.rs |
Text format to IR (for tests) |
| IR Macros | sway-ir-macros/ |
Proc-macros for IR construction in tests |
sway-error
// Core types in sway-error
pub struct Handler {
inner: RefCell<HandlerInner>,
}
pub struct HandlerInner {
pub errors: Vec<(Span, CompileError)>,
pub warnings: Vec<CompileWarning>,
}
pub struct ErrorEmitted; // Zero-size type; returned from functions that emit errors
The Handler is passed through every compilation phase. Errors are collected
(not immediately fatal) allowing multiple errors to be reported per build. An
Err(ErrorEmitted) return means at least one error was emitted to the handler;
the caller can continue or propagate.
sway-features
pub struct ExperimentalFeatures {
pub new_encoding: bool,
pub references: bool,
pub new_hashing: bool,
pub str_array_no_padding: bool,
pub dynamic_storage: bool,
}
impl ExperimentalFeatures {
// Reads FORC_EXPERIMENTAL and FORC_NO_EXPERIMENTAL env vars
pub fn from_env() -> Self { ... }
// Construct with all features disabled
pub fn none() -> Self { ... }
}
sway-types
| Type | Description |
|---|---|
Span |
Source location: SourceId + byte start + byte end |
Ident |
Identifier string + Span |
SourceId |
Opaque file identifier (integer) |
SourceEngine |
Maps SourceId ↔ file path and full source content |
Spanned |
Trait: fn span(&self) -> Span |
Named |
Trait: fn name(&self) -> &Ident |
sway-utils
Key constants:
pub const MANIFEST_FILE_NAME: &str = "Forc.toml";
pub const MAIN_ENTRY: &str = "main.sw";
pub const LIB_ENTRY: &str = "lib.sw";
pub const DEFAULT_OUTPUT_DIRECTORY: &str = "out";
pub const LOCK_FILE_NAME: &str = "Forc.lock";
pub const SWAYFMT_CONFIG_FILE_NAME: &str = "swayfmt.toml";
Key functions:
// Search upward from starting_directory for Forc.toml
pub fn find_manifest_dir(starting_directory: &Path) -> Option<PathBuf>
// Search upward for workspace root (Forc.toml with [workspace])
pub fn find_workspace_dir(starting_directory: &Path) -> Option<PathBuf>
swayfmt
| Module | Description |
|---|---|
src/config/ |
All configuration type definitions and defaults |
src/formatter/ |
Core formatting state machine and entry points |
src/utils/comments/ |
Comment preservation and correct placement |
src/utils/expressions/ |
Expression formatting rules |
src/utils/heuristics/ |
Width-based line-break decisions |
src/utils/imports/ |
use statement formatting and grouping |
src/utils/items/ |
Item (declaration) formatting |
src/utils/literals/ |
Literal value formatting |
src/utils/ordering/ |
Item ordering within module scope |
src/utils/structures/ |
Struct and enum formatting |
src/utils/whitespace/ |
Indentation and newline normalization |
Public API:
pub fn format_text(src: &Arc<str>) -> Result<String, FormatterError>
pub fn format_text_with_config(
src: &Arc<str>,
config: &FormatterConfig,
) -> Result<String, FormatterError>
sway-lsp
| Component | Description |
|---|---|
src/server.rs |
tower-lsp LanguageServer trait implementation |
src/capabilities.rs |
LSP ServerCapabilities construction |
src/core/session.rs |
Per-workspace compilation session (holds typed program) |
src/core/document.rs |
Per-file text document state and version tracking |
src/handlers/ |
Individual LSP request handlers |
src/traverse/ |
Post-compilation AST traversal for IDE feature queries |
Concurrency model: dashmap::DashMap provides lock-free concurrent access to
the document store and session state. Read-heavy operations (hover, completion,
diagnostics) proceed without blocking; the compilation lock serializes write
access to the typed program state.
forc
forc/src/
main.rs Jemalloc setup, async runtime, CLI dispatch
cli/
mod.rs Subcommand enum and dispatch table
build.rs forc build
check.rs forc check
clean.rs forc clean
new.rs forc new / forc init
deploy.rs forc deploy
run.rs forc run
test.rs forc test
update.rs forc update
add.rs forc add
remove.rs forc remove
contract_id.rs forc contract-id
parse_bytecode.rs forc parse-bytecode
completions.rs forc completions
plugins.rs forc plugins
addr2line.rs forc addr2line
forc-pkg
| Component | Description |
|---|---|
src/manifest/ |
Forc.toml parsing, validation, workspace detection |
src/lock.rs |
Forc.lock reading and writing |
src/source/ |
Dependency fetching: path/, git/, ipfs/ |
src/pkg.rs |
Package compilation orchestration |
src/build_plan.rs |
Dependency DAG construction, topological sort |
src/compiled_package.rs |
Compiled package artifact type |
src/lib.rs |
Public API |
The dependency graph is a petgraph::Graph<Node, Edge>. Topological ordering
via petgraph::algo::toposort determines build order. Cycle detection uses
petgraph::algo::is_cyclic_directed.
forc-test
Implements the Sway unit test runner:
- Compiles the package with test functions enabled
- Deploys the contract to an in-process FuelVM instance
- Calls each
#[test]function as an ABI method - Reports pass/fail based on whether the call reverts
forc-util
| Component | Description |
|---|---|
| Bytecode parsing | Disassembly of FuelVM .bin bytecode |
| CLI utilities | Shared clap arg types, error formatting |
| Node connection | Shared fuel-core-client setup |
forc-debug
Implements the DAP server. Main loop:
- Start tokio async runtime
- Connect to Fuel node GraphQL endpoint (
--api-url) - Listen on stdin for DAP protocol messages (JSON-RPC)
- Translate DAP requests (set-breakpoint, step, continue) to Fuel node debug API calls
- Send DAP events (stopped, output) back on stdout
forc-doc
- Compiles the package with
forc-pkg - Walks the typed AST for all public items (functions, structs, enums, traits)
- Extracts doc comments (
///and//!) - Generates HTML pages using
horrorshowmacros - Writes output to
out/doc/
forc-migrate
For each defined migration:
- Scan source files for patterns matching the deprecated form
- Apply AST-aware or regex-based transforms
- Write modified files (or preview in
--dry-run) - Report what was changed
forc-publish
- Validate
Forc.toml(required fields) - Run
forc checkto ensure the package type-checks - Create a source tarball (excluding build artifacts and
.forc/) - Upload to the registry via HTTP using
FORC_PUB_TOKEN
8.4 Error Message Taxonomy
The compiler emits 295 distinct diagnostic types across all phases, split
between hard errors (CompileError) and warnings (CompileWarning).
Parse Error Categories
| Category | Approx Count | Representative Messages |
|---|---|---|
| Unexpected token | ~15 | UnexpectedToken, ExpectedToken, UnexpectedEndOfInput |
| Invalid literal | ~8 | InvalidIntegerLiteral, InvalidHexLiteral, InvalidStrEscape |
| Invalid syntax | ~20 | Misplaced keywords, invalid attribute syntax, bad annotation |
| Delimiter mismatch | ~5 | Unclosed braces, mismatched parentheses, extra closing delimiter |
Type Error Categories
| Category | Approx Count | Representative Messages |
|---|---|---|
| Type mismatch | ~25 | TypeError, MismatchedType, ArgumentParameterTypeMismatch |
| Unresolved type | ~15 | UnresolvedType, UnknownType, UnableToInferGeneric |
| Generic constraint violation | ~12 | TraitConstraintNotSatisfied, TraitNotImplemented |
| Array/slice errors | ~8 | InvalidArrayLength, ArrayOutOfBounds, ConstantEvaluationError |
| Integer range/cast | ~6 | IntegerTooLarge, IntegerOverflow, TypeCastNotAllowed |
| Self type errors | ~5 | InvalidSelfParam, SelfParameterNotAllowedForContract |
| Numeric type errors | ~6 | MultipleDefinitionsOfName, UnreachablePattern |
Declaration Error Categories
| Category | Approx Count | Representative Messages |
|---|---|---|
| Duplicate declaration | ~12 | DuplicateDeclaration, MultipleDefinitionsOfName |
| Missing declaration | ~10 | SymbolNotFound, FunctionNotFound, MethodNotFound |
| Visibility errors | ~8 | ModuleNotFound, PrivateField, ImportPrivateModule |
| Trait/impl errors | ~15 | MissingInterfaceSurfaceMethods, ImplSelfWithoutSelf, TraitImplNotAllowed |
| Storage errors | ~12 | InvalidStorageField, StorageAccessMismatch, StorageNotDeclared |
Control Flow Error Categories
| Category | Approx Count | Representative Messages |
|---|---|---|
| Unreachable code | ~5 | UnreachableCode, MatchPatternAlreadyCovered |
| Missing return | ~3 | MissingReturnStatement, ReturnStatementNotAllowed |
| CEI violation | ~4 | StorageWriteAfterContractCall |
| Loop issues | ~2 | BreakOutsideLoop, ContinueOutsideLoop |
ABI Error Categories
| Category | Approx Count | Representative Messages |
|---|---|---|
| Invalid ABI type | ~8 | InvalidABIType, ABIMethodNotFound, ABISupertraitNotABI |
| ABI encoding | ~6 | InvalidEncodingType, ABIHashTemplateError |
Code Generation Error Categories
| Category | Approx Count | Representative Messages |
|---|---|---|
| Register allocation | ~5 | TooManyRegisters |
| Unsupported operation | ~10 | OperationNotSupported, TargetNotSupported |
| IR errors | ~8 | IrGenError, IsizeTooBig |
Warning Categories
| Category | Approx Count | Representative Messages |
|---|---|---|
| Dead code | ~8 | DeadCode, UnusedVariable, UnusedImport, UnusedReturnValue |
| Deprecated usage | ~5 | Deprecated |
| Style | ~3 | NonSnakeCaseStructFieldName, NonCamelCaseTypeName |
| CEI pattern | ~4 | StorageWriteAfterContractCall |
| Shadow warning | ~3 | ShadowsOtherSymbol |
8.5 Storage Layout Reference
storage {
// Simple field: key = sha256("storage_domain:owner")
owner: Address = Address::zero(),
// Map: key for entry k = sha256(sha256("storage_domain:balances") ++ k)
balances: StorageMap<Address, u64> = StorageMap::<Address, u64> {},
// Vec: element i key = sha256(sha256("storage_domain:items") ++ i)
items: StorageVec<u64> = StorageVec {},
// Nested: StorageVec<StorageMap<...>> — see issue #6040 for limitations
}
Storage keys are deterministic and collision-resistant by construction:
- Each storage field has a unique domain derived from its name
- Map keys are hashed with the field domain, preventing aliasing
- Vec indices are hashed with the field domain
Active storage bugs:
- #6040 —
StorageVec::insert()with deeply nested generic types - #7559 — Complex generic
StorageKey<T>fails to resolve in certain patterns
8.6 Testing Infrastructure
Test Infrastructure Overview (test/ crate)
The test workspace member is the E2E test harness. Each test case is a
Sway project in tests/ with a test.toml controlling behavior.
test.toml Format
# Category controls what the harness does with this test
category = "run" # run | run_on_node | compile | unit_tests_pass | fail | disabled
# For "run" and "run_on_node" categories:
expected_output = "42\n" # Expected stdout from script execution
# For "fail" category:
# (errors are checked against .stderr expected files)
| Category | Description |
|---|---|
run |
Compile and execute; assert stdout matches expected_output |
run_on_node |
Compile and run against a live fuel-core node |
compile |
Compile only; assert success (or failure for expected_failure) |
unit_tests_pass |
Compile and run all #[test] functions; assert all pass |
fail |
Assert compilation fails with specific errors |
disabled |
Skip; comment in test.toml explains why |
Snapshot Tests
snapshot.toml drives compilation commands. Expected output is stored in
.expected files adjacent to each test project.
# Run snapshot tests
cargo test --package test -- snapshot
# Update snapshots (when output intentionally changes)
UPDATE_EXPECT=1 cargo test --package test -- snapshot
In-Language Unit Tests
contract;
#[test]
fn addition_works() {
assert_eq(1u64 + 1u64, 2u64);
}
#[test]
fn zero_division_reverts() {
let _ = 1u64 / 0u64; // should revert
}
#[test(should_revert)]
fn expected_revert() {
revert(42);
}
Run with forc test. Harness compiles the package with tests enabled,
deploys to an in-process VM, and calls each test function.
SDK Harness Tests (Rust)
use fuels::prelude::*;
// abigen! generates Rust bindings from the ABI JSON
abigen!(Contract(
name = "MyToken",
abi = "out/debug/my_token-abi.json"
));
#[tokio::test]
async fn test_mint() {
let config = WalletsConfig::new(Some(1), None, None);
let wallets = launch_custom_provider_and_get_wallets(config, None, None)
.await
.unwrap();
let wallet = wallets.into_iter().next().unwrap();
let id = Contract::load_from(
"out/debug/my_token.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&wallet, TxPolicies::default())
.await
.unwrap();
let contract = MyToken::new(id, wallet);
let result = contract
.methods()
.mint(100u64)
.call()
.await
.unwrap();
assert_eq!(result.value, 100u64);
}
Performance Test Recipes
just perf-e2e # Compile ~20 representative contracts; measure wall time
just perf-in-lang # Run in-language tests; measure execution time
just perf-all # Both of the above
just perf-diff # Compare vs saved baseline (stored in perf_baseline/)
just perf-diff-latest # Compare vs forc downloaded from latest GitHub release
8.7 Contributing — Development Workflow
First-Time Setup
git clone https://github.com/FuelLabs/sway.git && cd sway
rustup show # Installs pinned toolchain from rust-toolchain.toml
cargo install cargo-sort just # Required by CI
cargo build --workspace # Initial build
cargo test --workspace # Run full test suite
CI Requirements (All Must Pass)
cargo sort --check --workspace # Sorted [dependencies] in all Cargo.toml
cargo fmt --check --all # Rust formatting
cargo clippy --all-targets \
--workspace -- -D warnings # No clippy warnings
forc fmt --check # Sway example formatting
cargo build --workspace # Full build
cargo test --workspace # Full test suite
Adding a Language Feature (Step-by-Step)
- AST nodes (
sway-ast): Add newItem*,Expr*, orTy*variants withSpanfields - Parser (
sway-parse): Extend the relevant recursive descent rule to produce the new node; addParseToEnd/ParseBracketcombinators as needed - Typed AST (
sway-core/language/): Add corresponding typed variants; these holdTypeIdinstead of raw syntax types - Type checker (
sway-core/type_system/): Implement type-checking logic for the new node; use theEnginesstruct to accessTypeEngineandDeclEngine - IR lowering (
sway-core/ir_generation/): Emit appropriatesway-irinstructions for the new construct - Tests: Add test cases in
tests/covering both success and expected-failure scenarios; add atest.tomlfor each - Book: Update
docs/book/with user-facing documentation - Errors: If the feature introduces new error conditions, add variants to
sway-error
Adding an IR Optimization Pass
- Create
sway-ir/src/passes/my_pass.rs - Implement
Pass:pub struct MyPass; impl Pass for MyPass { fn name(&self) -> &str { "my-pass" } fn descr(&self) -> &str { "Describe what this pass does" } fn run(&self, module: &mut Module, _: &AnalysisResults) -> Result<bool, IrError> { // Modify IR; return Ok(true) if any change was made Ok(false) } fn analyses_required(&self) -> Vec<AnalysisName> { vec![] } } - Register in
sway-ir/src/pass_manager.rs - Add tests in
sway-ir/tests/using the text IR parser
Filing a High-Quality Bug Report
# Capture the panic with full backtrace
RUST_BACKTRACE=full forc build --path /path/to/reproducer 2>&1 > bug_report.txt
# Note exact versions
forc --version >> bug_report.txt
rustc --version >> bug_report.txt
# Minimize the reproducer
# Remove code until the panic stops, then restore the last line removed
Include in the GitHub issue:
forc --versionoutputRUST_BACKTRACE=fulloutput- Minimal self-contained Sway project that reproduces the issue
- Operating system and architecture
Active High-Priority Issues for Contributors
| Issue | Title | Severity |
|---|---|---|
| #7551 | Compiler infinite loop | Critical |
| #7603 | ICE from invalid type unification of arrays | High |
| #7604 | Compiler panic with type aliases + const generic arrays | High |
| #7602 | ICE when accessing self[i] with const generics |
High |
| #7606 | CEI warnings nondeterministic | Medium |
| #7605 | Confusing OOB error for const generic arrays | Medium |
| #6040 | StorageVec::insert() with nested types |
Medium |
| #7559 | Generic StorageKey resolution failure |
Medium |
| #7589 | RUSTSEC-2025-0055 ANSI escape log poisoning | Low |
| #7576 | RUSTSEC-2026-0049 CRL matching logic flaw | Low |
8.8 LSP Architecture Deep Dive
The sway-lsp server follows a session-based architecture to maintain
workspace state across editor interactions:
Editor (VS Code, Neovim, Emacs, ...)
↕ JSON-RPC 2.0 (stdin/stdout, per LSP spec)
↕ Managed by tower-lsp framework
LSP Server Process (forc lsp → sway-lsp)
├── Compilation loop (triggered by textDocument/didSave)
│ ├── forc-pkg: resolve dependencies
│ ├── sway-parse: parse changed files
│ ├── sway-core: type check, build typed program
│ └── Update session state
└── Request handlers (hover, completion, definition, references, ...)
└── Query session state (read-only, concurrent via DashMap)
Performance design:
- Compilation is triggered only on
textDocument/didSave, not on every keystroke. This keeps compile overhead from blocking typing. - Between saves, all IDE requests (hover, completion, go-to-definition) use cached session state. These complete in O(1) to O(log n) with no compilation.
DashMapallows multiple concurrent readers, so parallel LSP requests (e.g., hover + diagnostics publication) do not block each other.
Span-based query architecture: All IDE features are implemented by:
- Converting the cursor position (line, column) to a byte offset
- Finding the
Spanin the typed AST that covers the cursor offset - Querying the declaration engine for what that span refers to
- Returning the relevant information (type, documentation, location)
8.9 ABI Format Reference
Fuel JSON ABI (Full Structure)
{
"specVersion": "1",
"encodingVersion": "1",
"programType": "contract",
"concreteTypes": [
{
"type": "u64",
"concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
"metadataTypeId": 0,
"components": null,
"typeParameters": null
}
],
"metadataTypes": [
{
"type": "u64",
"metadataTypeId": 0,
"components": null,
"typeParameters": null
}
],
"functions": [
{
"name": "transfer",
"inputs": [
{ "name": "amount", "concreteTypeId": "..." },
{ "name": "recipient", "concreteTypeId": "..." }
],
"output": "...",
"attributes": ["storage(read, write)", "payable"]
}
],
"loggedTypes": [
{ "logId": "0", "concreteTypeId": "..." }
],
"messagesTypes": [...],
"configurables": [
{
"name": "OWNER",
"concreteTypeId": "...",
"offset": 2048
}
]
}
Function Selector Computation
selector = SHA-256( canonical_signature )[0..4]
Where canonical_signature follows the pattern:
function_name(param1_type,param2_type,...)
Example:
transfer(u64,b256) → SHA-256("transfer(u64,b256)")[0..4]
9. Integration Points
9.1 Fuel Node Integration
forc communicates with fuel-core nodes via the GraphQL API, using the
fuel-core-client (v0.48.0) crate:
| Operation | How |
|---|---|
forc deploy |
Submits a Create transaction containing bytecode + storage slots |
forc run |
Submits a Script transaction; prints output |
forc test (E2E) |
Launches ephemeral in-process node; submits transactions; asserts results |
forc debug |
Streams debug events via DAP over the node's debug interface |
Default node URL: http://127.0.0.1:4000/graphql
Override in any command:
forc deploy --node-url http://beta-5.fuel.network/graphql
forc run --node-url http://testnet.fuel.network/graphql
forc test --node-url http://127.0.0.1:4000/graphql
9.2 Fuel Rust SDK Integration (fuels 0.77)
The fuels crate is the primary SDK for writing Rust integration tests against
compiled Sway contracts. The workflow:
abigen!macro reads the ABI JSON and generates type-safe Rust bindingsContract::load_from()loads the compiled.binbytecode.deploy()submits aCreatetransaction to the node- Generated methods call contract functions via ABI-encoded
Scripttransactions - Return values are ABI-decoded back to Rust types
use fuels::prelude::*;
abigen!(Contract(
name = "MyContract",
abi = "out/debug/my_contract-abi.json"
));
#[tokio::test]
async fn integration_test() {
// Launch an in-process fuel-core node with a funded wallet
let config = WalletsConfig::new(Some(1), Some(1), Some(Amount::from(1_000_000)));
let wallets = launch_custom_provider_and_get_wallets(config, None, None)
.await
.unwrap();
let wallet = wallets.into_iter().next().unwrap();
// Deploy
let id = Contract::load_from(
"out/debug/my_contract.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&wallet, TxPolicies::default())
.await
.unwrap();
// Call
let contract_instance = MyContract::new(id, wallet);
let result = contract_instance
.methods()
.my_function(42u64)
.call()
.await
.unwrap();
assert_eq!(result.value, 42u64);
}
9.3 mdBook Integration
The Sway book is built with mdbook plus the custom mdbook-forc-documenter
preprocessor (in scripts/mdbook-forc-documenter/). This preprocessor
generates forc subcommand documentation pages directly from the clap
argument definitions, keeping the book in sync with the tool automatically.
# Install dependencies
cargo install mdbook
cargo install --path scripts/mdbook-forc-documenter
# Build the book
mdbook build docs/book
# Output: docs/book/book/
# Serve locally with hot-reload
mdbook serve docs/book --open
The book is also published to the Fuel documentation site on each release.
9.4 Editor Integration
Visual Studio Code
Install the official Sway extension (by FuelLabs) from the VS Code
marketplace. Configuration in settings.json:
{
"sway.server.path": "forc", // Uses forc lsp subcommand
"sway.trace.server": "off", // "off" | "messages" | "verbose"
"editor.formatOnSave": true,
"[sway]": {
"editor.defaultFormatter": "FuelLabs.sway-vscode-plugin"
}
}
Neovim (nvim-lspconfig)
require('lspconfig').sway_lsp.setup({
cmd = { 'forc', 'lsp' },
filetypes = { 'sway' },
root_dir = require('lspconfig').util.root_pattern('Forc.toml'),
settings = {},
on_attach = function(client, bufnr)
-- Standard LSP keybindings
local opts = { buffer = bufnr }
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
end,
})
Emacs (eglot or lsp-mode)
;; eglot
(add-to-list 'eglot-server-programs
'(sway-mode . ("forc" "lsp")))
;; lsp-mode
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection '("forc" "lsp"))
:major-modes '(sway-mode)
:server-id 'sway-lsp))
9.5 DAP Debugging Integration
forc-debug --serve implements the Debug Adapter Protocol over stdin/stdout.
The IDE launches forc debug --serve as a child process and communicates
via DAP JSON-RPC.
VS Code launch.json example:
{
"version": "0.2.0",
"configurations": [
{
"type": "fuel",
"request": "launch",
"name": "Debug Sway Script",
"program": "${workspaceFolder}/out/debug/my_script.bin",
"nodeUrl": "http://127.0.0.1:4000/graphql",
"stopAtEntry": true
}
]
}
Neovim (nvim-dap):
local dap = require('dap')
dap.adapters.fuel = {
type = 'executable',
command = 'forc',
args = { 'debug', '--serve' },
}
dap.configurations.sway = {
{
type = 'fuel',
request = 'launch',
name = 'Debug contract',
program = '${workspaceFolder}/out/debug/my_contract.bin',
nodeUrl = 'http://127.0.0.1:4000/graphql',
},
}
9.6 IPFS Dependency Integration
# Forc.toml
[dependencies]
my_library = { ipfs = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG" }
# Override IPFS gateway
forc build --ipfs-node https://ipfs.infura.io
# Use Cloudflare IPFS gateway
forc build --ipfs-node https://cloudflare-ipfs.com
IPFS packages are cached in ~/.forc/cache/ipfs/<cid>/ after first fetch.
Content is verified by CID (content-addressed, so integrity is implicit).
9.7 Downstream Tooling Compatibility
| Tool | Input | Description |
|---|---|---|
fuel-core |
.bin bytecode, ABI JSON |
Executes contracts and scripts |
fuels-rs |
ABI JSON | abigen! generates Rust bindings |
fuels-ts |
ABI JSON | Generates TypeScript/JavaScript bindings |
fuel-explorer |
ABI JSON, debug info | Block explorer contract verification |
| Sway Playground | Source code | Web-based Sway IDE (online) |
9.8 Troubleshooting Reference
Warning
The following ICE (Internal Compiler Error) patterns correspond to active bugs in version 0.71.0 (commit
e12e611). Track the linked issues for fix status.
| Symptom | Likely Cause | Issue | Workaround |
|---|---|---|---|
| Panic / ICE during type checking with type aliases | Type alias + const generic array | #7604 | Avoid const generics on type-aliased types |
ICE on self[i] access |
Const generic indexing edge case | #7602 | Use explicit index variable with known type |
| ICE "invalid type unification" message | Array type unification scope escape | #7603 | Simplify array type expressions |
forc build hangs indefinitely |
Infinite loop in type inference | #7551 | Kill process; file issue with minimal reproducer |
| CEI warning appears or disappears non-deterministically | Nondeterministic CEI analysis | #7606 | Treat any CEI occurrence as real; apply manual review |
| Confusing "out of bounds" error on const generic array | Incorrect error reporting path | #7605 | Check array size bounds carefully; compare type signatures |
StorageVec behaves incorrectly with nested types |
Nested type storage layout bug | #6040 | Avoid StorageVec<StorageMap<...>> and similar patterns |
Generic StorageKey<T> fails to compile in complex patterns |
Generic storage key resolution | #7559 | Simplify storage generic parameters |
| Terminal shows garbled output in CI | ANSI escape log poisoning | #7589 | Strip ANSI codes from output; see §7.1 |
forc build fetches dependencies despite --offline |
CRL validation dependency | #7576 | Use --locked --offline; verify Forc.lock is committed |
Note
For issues not in the table above, search https://github.com/FuelLabs/sway/issues before filing a new report. Always include
forc --version,RUST_BACKTRACE=fulloutput, and a minimal self-contained reproducer.