Smart Contract Fuzzing with Foundry: Finding Edge Cases Before Hackers Do
Salman Haider
TelGates Team
Traditional unit tests only check the scenarios you think of. Fuzzing checks the scenarios you didn't. Here is how we use Foundry to secure DeFi protocols.
What is Fuzzing?
Fuzzing involves providing random inputs to a function to see if it breaks. In Foundry, you simply define a test function that takes arguments. Foundry will automatically generate thousands of random inputs for those arguments.
```solidity function testFuzz_Withdraw(uint256 amount) public { vm.assume(amount > 0 && amount < type(uint128).max); // Setup state // Execute withdraw // Assert invariants } ```
Invariant Testing
While fuzzing tests single functions, invariant testing tests the entire system state over multiple random function calls. For a lending protocol, an invariant might be: `Total Borrowed <= Total Supplied`. Foundry will randomly call `deposit()`, `borrow()`, `repay()`, and `withdraw()` in arbitrary orders trying to break this invariant.
Advanced Techniques
- Bound Inputs: Use `vm.assume()` or `bound()` to restrict inputs to realistic ranges.
- Custom Handlers: For complex protocols, write handler contracts that dictate *how* Foundry interacts with your system (e.g., always approving tokens before depositing).
- Coverage: Aim for 100% branch coverage, but remember that coverage does not guarantee security.
Fuzzing has found vulnerabilities in our audits that human reviewers missed.