Fuel Network Security Scanner

SkillFiles & storage

Use when the user wants to audit Fuel Network smart contracts written in Sway, scan FuelVM contracts for UTXO-model, predicate, or script vulnerabilities, review Fuel DeFi protocols for multi-asset handling issues, or analyze Sway-specific patterns including storage access and message passing.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Fuel Network Security Scanner skill

What this skill tells your AI

The instructions your AI receives, as published by 0x-shashi/web3-audit-skills in skills/fuel-scanner/SKILL.md and read by ahel’s review.

Security scanner for Fuel Network smart contracts written in Sway. Fuel uses a UTXO-based model with the FuelVM, fundamentally different from EVM account-based chains.


Language & Runtime

AttributeValue
ChainFuel (modular execution layer)
LanguageSway (Rust-inspired, purpose-built for FuelVM)
VMFuelVM (register-based, not stack-based like EVM)
Transaction ModelUTXO-based (like Bitcoin, unlike Ethereum's account model)
Token ModelNative multi-asset (assets are first-class, not contract-based)
Program TypesContract, Script, Predicate, Library
Toolchainforc (Fuel Orchestrator), fuel-core
Testingfuels-rs (Rust SDK)

FuelVM vs EVM: Key Differences

FeatureEVM (Ethereum)FuelVM (Fuel)
Transaction modelAccount-basedUTXO-based
AssetsERC20 contractsNative multi-asset
ParallelismSequentialParallel (UTXO enables it)
State accessAny contract can read global stateState access declared upfront
ReentrancyPossible (external calls)Different model (no direct reentrancy)
StackStack-based (256-bit words)Register-based (64-bit words)
ProgramsSmart contracts onlyContracts, Scripts, Predicates

Detection Capabilities

CategoryDetectionSeverity
UTXOSame UTXO consumed in multiple pathsCritical
UTXOCoin output not created for changeHigh
PredicatesPredicate logic bypass via crafted inputCritical
PredicatesPredicate gas limit exceeded (always fails)High
AssetsWrong AssetId used in transfer or balance checkCritical
AssetsMissing AssetId validation on received fundsHigh
Access ControlMissing msg_sender() validation on privileged functionsCritical
Access ControlIdentity type confusion (Address vs ContractId)High
StorageStorage key collision in manual key assignmentHigh
StorageStorage slot manipulation via asm blocksMedium
MathInteger overflow (Sway u64 wraps in some contexts)High
MathDivision by zero (panic)Medium
ScriptsIncorrect script-to-contract call sequencingMedium
ScriptsScript return value not validated by callerMedium

Program Types and Security Implications

Contract

Persistent state, deployed on-chain, callable by transactions and scripts:

contract;

storage {
    owner: Identity = Identity::Address(Address::zero()),
    balance: u64 = 0,
}

abi MyContract {
    #[storage(read, write)]
    fn deposit();

    #[storage(read, write)]
    fn withdraw(amount: u64);
}

impl MyContract for Contract {
    #[storage(read, write)]
    fn deposit() {
        // msg_amount() = forwarded base asset amount
        // msg_asset_id() = forwarded asset ID
        storage.balance.write(storage.balance.read() + msg_amount());
    }

    #[storage(read, write)]
    fn withdraw(amount: u64) {
        // MUST validate caller
        require(
            msg_sender().unwrap() == storage.owner.read(),
            "unauthorized"
        );
        storage.balance.write(storage.balance.read() - amount);
        transfer(msg_sender().unwrap(), AssetId::base(), amount);
    }
}

Predicate

Stateless UTXO spending conditions — returns true or false:

predicate;

// Predicate that allows spending only if multiple conditions met
fn main(expected_recipient: Address, min_amount: u64) -> bool {
    // Predicates have NO state and NO side effects
    // They validate whether a UTXO can be spent
    let tx_outputs = tx_outputs_count();

    // Check: output sends to expected recipient
    // Check: amount >= min_amount
    // Returns true only if conditions are met
    true // or false
}

Predicate Security: Predicates are pure functions evaluated at validation time. If the predicate returns true, the UTXO can be spent. Any logic error = funds at risk.

Script

Transaction-level orchestration (not deployed, executed once):

script;

use my_contract_abi::MyContract;

fn main(contract_id: ContractId, amount: u64) {
    let contract = abi(MyContract, contract_id.into());
    contract.deposit {  // Call parameters
        gas: 10_000,
        coins: amount,
        asset_id: AssetId::base(),
    }();
}

Native Multi-Asset Model

Unlike EVM where tokens are contract-based (ERC20), Fuel has native multi-asset support:

// Every contract can mint its own sub-assets
let sub_id = SubId::zero();
let asset_id = AssetId::new(ContractId::this(), sub_id);

// Mint native assets
mint(sub_id, amount);

// Transfer native assets
transfer(recipient, asset_id, amount);

// Check forwarded asset
let received_asset = msg_asset_id();
require(received_asset == expected_asset, "wrong asset");

Critical Check: Always validate msg_asset_id() matches the expected asset. Failing to do so allows an attacker to send a worthless asset and receive legitimate assets in return.


Resources

Workflows

Overview

Fuel is a modular execution layer with:

  • Sway language (Rust-inspired)
  • UTXO-based model (not account-based)
  • FuelVM (not EVM)
  • Native multi-asset support
  • Predicates (stateless UTXO conditions)
  • Parallel transaction processing via strict state access declarations

Error Code Reference

Common Sway/FuelVM errors encountered during audits. Fuel uses revert() with numeric codes and require() with custom enums.

FuelVM Runtime Errors

Error CodeNameMeaning
0x00SuccessNormal execution
0x01RevertExplicit revert() or failed require()
0x02OutOfGasTransaction exceeded gas limit
0x03TransactionValidityTransaction failed validation rules
0x04MemoryOverflowMemory allocation exceeded limits
0x05ArithmeticOverflowArithmetic operation overflow
0x06ContractNotFoundCalled contract ID does not exist
0x07MemoryOwnershipAttempted write to read-only memory
0x08NotEnoughBalanceInsufficient asset balance for transfer
0x09ExpectedInternalContextExternal call in internal-only context
0x0AAssetIdNotFoundAsset ID does not exist in transaction
0x0BInputNotFoundTransaction input not found
0x0COutputNotFoundTransaction output not found
0x0DWitnessNotFoundWitness data not found at index

Sway Standard Library Errors

Error TypeMeaningAudit Significance
AuthError::SenderNotOwnerCaller is not the contract ownerAccess control — check ownership model
AuthError::SenderNotAdminCaller lacks admin roleRole-based access — check admin assignment
AssetError::InsufficientBalanceInsufficient asset balanceFinancial operation — check for manipulation
AssetError::InvalidAssetIdAsset ID not recognizedMulti-asset — check asset ID validation
PredicateError::InvalidSignaturePredicate signature check failedAuth bypass — check predicate logic
InputError::InvalidInputGeneric input validation failureCheck input bounds and type validation
IdentityError::InvalidAddressAddress validation failedCheck for zero/invalid address handling

UTXO-Related Audit Errors

IssueError PatternAudit Significance
Coin UTXO double-spendTransactionValidityFuelVM prevents at protocol level — but check application logic for logical double-spend
Predicate evaluation failureRevert in predicate contextPredicates are stateless — verify all validation happens within single evaluation
Message proof invalidMessageProofErrorL1→L2 bridge message not verified correctly
Variable output missingOutputNotFoundTransaction didn't include required output for asset transfer

Troubleshooting

IssueLikely CauseSolution
UTXO model vulnerabilities missedScanner uses account-model mental modelAnalyze UTXO inputs/outputs explicitly; check coin selection and change handling
Predicate bypass not detectedScanner doesn't analyze predicate scriptsAudit predicate logic separately — ensure all paths lead to true/false without side effects
Multi-asset handling errors missedScanner assumes single native assetFlag all AssetId parameters; verify correct asset checking in every transfer
Storage slot collision not caughtScanner doesn't map storage access in SwayMap all storage block declarations; check for manual slot computation conflicts
Cross-contract call issues missedScanner treats inter-contract calls as trustedTrace all abi(ContractId, ...) calls; verify called contract ID validation
Message-based bridge risks ignoredScanner doesn't model Fuel L1→L2 bridgeAudit all input_message handlers and message proof verification logic

Signals

GitHub stars
60
Forks
10
Last commit
Feb 2026
Advanced
Catalog kind
skill
Gateway key
fuel-scanner
Source
github.com/0x-shashi/web3-audit-skills