This project serves as an educational toolkit designed to explore Forge's property-based fuzz testing capabilities within the context of a Solidity-based staking contract. Fuzz testing, also known as fuzzing, is an automated testing technique that generates a large number of random inputs, or "fuzz," to identify programming errors and security vulnerabilities that might otherwise go unnoticed.
Forge is a Solidity testing framework that is part of the Foundry toolchain, well-known for its advanced fuzz testing features. These features help developers ensure their smart contracts are resilient against a diverse range of input values. By adhering to property-based testing principles, Forge allows developers to define properties (expected behaviors) that should always hold true for any valid inputs, and its fuzzing engine attempts to generate test cases that challenge these properties.
For additional details on fuzz testing and its applications in Forge, refer to the Foundry documentation: Forge Fuzz Testing.
The foundation of this project is PaulRBerg's foundry-template, which provides a robust Foundry-based template for developing Solidity smart contracts with sensible defaults.
The core functionality of the Staking Contract allows users to deposit a specific ERC-20 token into the contract to earn rewards based on the duration and amount of their stake. The contract encompasses functionalities for staking tokens, unstaking tokens along with accrued interest, and querying staked balances and earned rewards.
- Token Staking: Enables users to deposit (stake) tokens into the contract.
- Unstaking and Claiming Rewards: Allows users to withdraw their staked tokens along with any accrued rewards.
- Reward Calculation: Features a mechanism to calculate rewards based on the duration and amount of the stake.
Get started with this project by following these steps:
-
Clone the repository:
git clone https://github.com/luisvid/staking-forge-fuzzing.git
-
Install dependencies:
bun install
-
Compile the contract:
forge build
-
Run tests:
forge test
A simple ERC20 token utilized for testing the staking functionalities. This token:
- Initially mints 1 million tokens for the deployer.
- Includes functionality to mint new tokens to any address.
This contract allows users to stake ERC20 tokens (TestToken
) and earn rewards based on the duration of their stake.
Features:
- Staking and unstaking tokens.
- Calculating and claiming staking rewards.
- Emitting events for staking, unstaking, and claiming rewards to ensure transparency.
Tests are implemented using Forge and focus on fuzz testing for the staking and unstaking functionalities. Execute the tests with the following commands:
forge test --match-path test/StakingContract.t.sol
forge test --match-path test/StakingContractFuzz.t.sol
Descriptions for each fuzz test in the StakingContractFuzz.t.sol
test suite are as follows:
-
test_FuzzStake(uint256 amount)
:- Verifies that the staking function robustly handles random staking amounts ranging from 1 to 1,000,000 tokens, ensuring the contract's token balance is reduced accordingly.
-
test_FuzzUnstake(uint256 amount)
:- Initially stakes a random amount of tokens, then simulates 100 blocks to accrue rewards, and finally unstakes, verifying that the staked tokens and rewards are accurately returned, enhancing the final balance.
-
testFuzz_StakeAndImmediateUnstake(uint256 amount)
:- Ensures that immediate unstaking after staking a random valid amount correctly reverts token balances to their original states, affirming that no tokens are lost.
-
testFuzz_MultipleStakesSingleUnstake(uint256[] memory amounts)
:- Assesses the contract's capacity to manage multiple consecutive stakes and a single unstake, confirming the precise computation of accumulated rewards and the reflection of these in the final token balance.
-
testFuzz_StakeRedeemInterleaved(uint256[] memory stakes, uint16 rolledBlocks)
:- Simulates a scenario with multiple stakes at different intervals and intermittent reward redemptions, checking the precision of reward calculations and the correct adjustment of the final token balance after all operations.
- The contract and tests are intended for educational purposes and are not recommended for production use without further modifications, primarily due to their simplicity and the potential for security vulnerabilities.
- Consider further optimizing the reward calculation mechanism to better align with specific business models or operational criteria.
Explore various testing scenarios and refine the functionalities of your Staking Contract through this iterative process. This approach is essential for ensuring your contract's robustness and preparing it for practical deployment.
Diligence Fuzzing
is an advanced fuzz testing tool developed by ConsenSys that aims to identify potential
vulnerabilities in Ethereum smart contracts. By generating random inputs and testing them against the contract's logic,
it helps uncover hidden issues that might not be easily caught through normal testing.
Fuzzing is essential for ensuring the robustness of smart contracts by exposing them to a wide range of input conditions. Diligence Fuzzing offers:
- Automated discovery of vulnerabilities.
- Easy integration with Solidity and Foundry.
- Comprehensive documentation and support.
To integrate Diligence Fuzzing into your existing Foundry project and start fuzz testing your contracts, follow these detailed steps based on the official tutorial.
-
Install the CLI and Configure the API Key
- Ensure Foundry is installed and make sure you’re at least python 3.6 and node 16. You can add Diligence Fuzzing to
your project by running:
pip3 install diligence-fuzzing
- Ensure Foundry is installed and make sure you’re at least python 3.6 and node 16. You can add Diligence Fuzzing to
your project by running:
-
API Key
- With the tools installed, you will need to generate an API for the CLI and add it to the
.env
file. The API keys menu is accessible here.
- With the tools installed, you will need to generate an API for the CLI and add it to the
-
Running Fuzz Tests
-
Run your fuzz tests to see how your contract behaves under random conditions:
fuzz forge test
-
This sequence compiles unit tests, automatically detects and collects test contracts, and submits them for fuzzing:
$ fuzz forge test 🛠️ Parsing Foundry configuration 🛠️ Compiling tests 🛠️ Gathering test contracts 🛠️ Assembling and validating campaigns for submission 🛠️ Configuring the initial seed state ⚡️ Launching fuzzing campaigns You can track the progress of the campaign here: [Campaign Dashboard](https://fuzzing.diligence.tools/campaigns/cmp_ffcd3abf6b0640598c7cc7e436717xxx) Done 🎉
-
Visit the provided URL to access the Campaign Dashboard where you can monitor detailed statistics and results of the fuzzing process.
-
- Consult the Official Documentation: For comprehensive guidance and advanced techniques, visit the Diligence Fuzzing Documentation.
- Incorporate Regular Fuzz Testing: Include fuzz testing in your regular testing routines to continuously improve the security and reliability of your contracts.
This setup not only enhances your project’s testing capabilities but also deepens your understanding of smart contract security practices.