Stateless Fuzzing
A fuzz testing approach where each test run is independent, with no state carried between iterations.
Stateless fuzzing is a testing technique where a fuzzer generates random inputs for a function and executes each test in isolation, without maintaining state between runs. Each fuzz iteration starts fresh, making it excellent for testing individual functions but limited for discovering vulnerabilities that require specific sequences of operations.
How Stateless Fuzzing Works
In stateless fuzzing, the testing framework generates random values for function parameters and calls the function repeatedly with different inputs. Each call is independent—the contract state resets between iterations, and previous calls don't influence subsequent ones.
In Foundry, stateless fuzz tests are simple to write. Any test function that accepts parameters automatically becomes a fuzz test:
1function testFuzz_Withdraw(uint256 amount) public {2 // Setup fresh state3 payable(address(safe)).transfer(amount);45 // Execute and verify6 uint256 preBalance = address(this).balance;7 safe.withdraw();8 uint256 postBalance = address(this).balance;910 assertEq(preBalance + amount, postBalance);11}
Foundry's fuzzer will call this function hundreds of times (256 by default) with different values for amount, testing the withdraw logic across a wide range of inputs.
Stateless vs Stateful Fuzzing
The key distinction between stateless and stateful fuzzing lies in how state is handled between test iterations:
Stateless fuzzing resets contract state between each run. This is ideal for testing:
- Individual function correctness
- Input validation and boundary conditions
- Arithmetic operations and edge cases
- Pure and view functions
Stateful fuzzing (also called invariant testing) maintains state across calls, allowing the fuzzer to build up complex contract states through sequences of operations. This catches vulnerabilities that only emerge after specific state transitions.
Many critical vulnerabilities require stateful testing to discover. A reentrancy bug might only trigger after deposits and partial withdrawals create specific balance conditions. A governance attack might need multiple proposals and votes to materialize. Stateless fuzzing alone cannot find these issues.
Strengths of Stateless Fuzzing
Despite its limitations, stateless fuzzing offers several advantages:
Speed: Without state management overhead, stateless fuzz tests run extremely fast. Foundry can execute thousands of iterations per second, exploring vast input spaces quickly.
Simplicity: Stateless tests are easier to write and understand. Each test is self-contained, making failures straightforward to debug and reproduce.
Targeted testing: When you know a specific function needs thorough testing, stateless fuzzing efficiently explores its input space. This is particularly valuable for complex mathematical operations or input parsing logic.
Deterministic reproduction: Failed test cases are easy to reproduce because they don't depend on previous state. The failing input alone is sufficient to recreate the bug.
Writing Effective Stateless Fuzz Tests
To maximize the value of stateless fuzzing, follow these practices:
Bound your inputs: Use Foundry's bound() function to constrain random values to realistic ranges. Unbounded inputs often hit trivial failures (like out-of-gas) rather than meaningful bugs.
1function testFuzz_Deposit(uint256 amount) public {2 amount = bound(amount, 1, 1000 ether);3 // Now amount is always between 1 wei and 1000 ether4}
Test boundary conditions explicitly: While fuzzing explores random values, explicitly test known boundaries like zero, maximum values, and threshold crossings.
Combine with unit tests: Use stateless fuzzing to complement, not replace, unit tests. Unit tests verify specific scenarios; fuzzing explores unexpected ones.
Increase run count for critical functions: Foundry's default 256 runs may not be enough for complex functions. Increase runs for critical code:
1[fuzz]2runs = 10000
Limitations and When to Use Stateful Testing
Stateless fuzzing cannot discover vulnerabilities that require:
- Specific sequences of function calls
- Accumulated state from multiple operations
- Interaction patterns between multiple users
- Time-dependent state changes
For these scenarios, invariant testing is essential. A comprehensive security testing strategy uses both approaches: stateless fuzzing for function-level correctness and stateful fuzzing for protocol-level invariants.
Real-World Application
In security audits, stateless fuzzing often serves as a first pass. Auditors write fuzz tests for individual functions to quickly identify input handling bugs, arithmetic errors, and boundary condition failures. These findings then inform deeper stateful testing and manual review.
The combination of stateless fuzzing's speed with stateful testing's depth provides thorough coverage of both individual function behavior and complex protocol interactions.
Articles Using This Term
Learn more about Stateless Fuzzing in these articles:
Related Terms
Fuzzing
Automated testing technique using randomly generated inputs to discover edge cases and vulnerabilities in smart contracts.
Invariant Testing
Property-based testing approach verifying that critical protocol conditions remain true across all possible execution paths.
Foundry
Fast, portable Ethereum development framework written in Rust, featuring advanced testing and debugging capabilities.
Edge Case
An unusual or extreme input condition that occurs at the boundaries of expected behavior, often revealing vulnerabilities in smart contracts.
Need expert guidance on Stateless Fuzzing?
Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.
Get a Quote

