Agent skill
fuel-scanner
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.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/fuel-scanner
SKILL.md
Fuel Network Security Scanner
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
| Attribute | Value |
|---|---|
| Chain | Fuel (modular execution layer) |
| Language | Sway (Rust-inspired, purpose-built for FuelVM) |
| VM | FuelVM (register-based, not stack-based like EVM) |
| Transaction Model | UTXO-based (like Bitcoin, unlike Ethereum's account model) |
| Token Model | Native multi-asset (assets are first-class, not contract-based) |
| Program Types | Contract, Script, Predicate, Library |
| Toolchain | forc (Fuel Orchestrator), fuel-core |
| Testing | fuels-rs (Rust SDK) |
FuelVM vs EVM: Key Differences
| Feature | EVM (Ethereum) | FuelVM (Fuel) |
|---|---|---|
| Transaction model | Account-based | UTXO-based |
| Assets | ERC20 contracts | Native multi-asset |
| Parallelism | Sequential | Parallel (UTXO enables it) |
| State access | Any contract can read global state | State access declared upfront |
| Reentrancy | Possible (external calls) | Different model (no direct reentrancy) |
| Stack | Stack-based (256-bit words) | Register-based (64-bit words) |
| Programs | Smart contracts only | Contracts, Scripts, Predicates |
Detection Capabilities
| Category | Detection | Severity |
|---|---|---|
| UTXO | Same UTXO consumed in multiple paths | Critical |
| UTXO | Coin output not created for change | High |
| Predicates | Predicate logic bypass via crafted input | Critical |
| Predicates | Predicate gas limit exceeded (always fails) | High |
| Assets | Wrong AssetId used in transfer or balance check |
Critical |
| Assets | Missing AssetId validation on received funds |
High |
| Access Control | Missing msg_sender() validation on privileged functions |
Critical |
| Access Control | Identity type confusion (Address vs ContractId) |
High |
| Storage | Storage key collision in manual key assignment | High |
| Storage | Storage slot manipulation via asm blocks |
Medium |
| Math | Integer overflow (Sway u64 wraps in some contexts) | High |
| Math | Division by zero (panic) | Medium |
| Scripts | Incorrect script-to-contract call sequencing | Medium |
| Scripts | Script return value not validated by caller | Medium |
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
- Fuel Patterns
Workflows
- Fuel Audit
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 Code | Name | Meaning |
|---|---|---|
0x00 |
Success |
Normal execution |
0x01 |
Revert |
Explicit revert() or failed require() |
0x02 |
OutOfGas |
Transaction exceeded gas limit |
0x03 |
TransactionValidity |
Transaction failed validation rules |
0x04 |
MemoryOverflow |
Memory allocation exceeded limits |
0x05 |
ArithmeticOverflow |
Arithmetic operation overflow |
0x06 |
ContractNotFound |
Called contract ID does not exist |
0x07 |
MemoryOwnership |
Attempted write to read-only memory |
0x08 |
NotEnoughBalance |
Insufficient asset balance for transfer |
0x09 |
ExpectedInternalContext |
External call in internal-only context |
0x0A |
AssetIdNotFound |
Asset ID does not exist in transaction |
0x0B |
InputNotFound |
Transaction input not found |
0x0C |
OutputNotFound |
Transaction output not found |
0x0D |
WitnessNotFound |
Witness data not found at index |
Sway Standard Library Errors
| Error Type | Meaning | Audit Significance |
|---|---|---|
AuthError::SenderNotOwner |
Caller is not the contract owner | Access control — check ownership model |
AuthError::SenderNotAdmin |
Caller lacks admin role | Role-based access — check admin assignment |
AssetError::InsufficientBalance |
Insufficient asset balance | Financial operation — check for manipulation |
AssetError::InvalidAssetId |
Asset ID not recognized | Multi-asset — check asset ID validation |
PredicateError::InvalidSignature |
Predicate signature check failed | Auth bypass — check predicate logic |
InputError::InvalidInput |
Generic input validation failure | Check input bounds and type validation |
IdentityError::InvalidAddress |
Address validation failed | Check for zero/invalid address handling |
UTXO-Related Audit Errors
| Issue | Error Pattern | Audit Significance |
|---|---|---|
| Coin UTXO double-spend | TransactionValidity |
FuelVM prevents at protocol level — but check application logic for logical double-spend |
| Predicate evaluation failure | Revert in predicate context |
Predicates are stateless — verify all validation happens within single evaluation |
| Message proof invalid | MessageProofError |
L1→L2 bridge message not verified correctly |
| Variable output missing | OutputNotFound |
Transaction didn't include required output for asset transfer |
Troubleshooting
| Issue | Likely Cause | Solution |
|---|---|---|
| UTXO model vulnerabilities missed | Scanner uses account-model mental model | Analyze UTXO inputs/outputs explicitly; check coin selection and change handling |
| Predicate bypass not detected | Scanner doesn't analyze predicate scripts | Audit predicate logic separately — ensure all paths lead to true/false without side effects |
| Multi-asset handling errors missed | Scanner assumes single native asset | Flag all AssetId parameters; verify correct asset checking in every transfer |
| Storage slot collision not caught | Scanner doesn't map storage access in Sway | Map all storage block declarations; check for manual slot computation conflicts |
| Cross-contract call issues missed | Scanner treats inter-contract calls as trusted | Trace all abi(ContractId, ...) calls; verify called contract ID validation |
| Message-based bridge risks ignored | Scanner doesn't model Fuel L1→L2 bridge | Audit all input_message handlers and message proof verification logic |
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?