StakingApp is a simple yet powerful Ethereum-based staking dApp that allows users to stake a fixed amount of ERC20 tokens in exchange for periodic ETH rewards. Designed with clear staking logic and robust test coverage using the Foundry framework, it highlights security patterns, owner controls, and real-world staking flows. This project consists of two Solidity smart contracts built on Ethereum:
StakingToken
: An ERC20 token with public minting capability.StakingApp
: A fixed-amount staking dApp that rewards users with ETH after a defined staking period.
- 💰 ERC20 Token: Customizable name and symbol and public minting functionality.
- 🔐 Fixed-Amount Staking: Users can only stake a predetermined token amount (e.g., 10 tokens).
- ⏱️ Reward Cycle: ETH rewards are distributed per period after staking duration elapses.
- 🧾 One-Time Staking: Only one active stake per user.
- 💰 ETH Rewards: Claimable rewards sent directly to users' wallets.
- 👑 Owner Controls:
- Set staking period
- Load contract with ETH via
receive()
function
- Ownable Pattern: Admin-only access for sensitive functions like
changeStakingPeriod
. - ERC20 Compliance: Works with any token implementing the
IERC20
interface. - CEI Pattern (Checks-Effects-Interactions): Proper use before external calls to prevent reentrancy attacks.
- Events Emitted: For deposits, withdrawals, reward transfers, and admin updates.
- Gas-efficient design: Minimalistic storage writes; uses timestamps to track staking.
Test Function | Key Verification |
---|---|
testStakingTokenMintsCorrectly() |
Token minting and balance update |
testStakingTokenCorrectlyDeployed() |
ERC20 contract deployment check |
testStakingAppCorrectlyDeployed() |
StakingApp contract deployment check |
testShouldRevertIfNotOwner() |
Only owner can update staking period |
testShouldChangeStakingPeriod() |
Admin updates staking period correctly |
testContractReceivesEtherCorrectly() |
StakingApp receives and stores ETH correctly |
Test Function | Key Verification |
---|---|
testIncorrectAmountShouldRevert() |
Rejects incorrect deposit amounts |
testDepositTokensCorrectly() |
Successful deposit updates balance & timestamp |
testUserCannotDepositMoreThanOnce() |
Prevents multiple deposits per user |
Test Function | Key Verification |
---|---|
testCanOnlyWithdraw0WithoutDeposit() |
No effect when no deposit made |
testWithdrawTokensCorrectly() |
Proper token refund and balance reset |
Test Function | Key Verification |
---|---|
testCanNotClaimIfNotStaking() |
Prevent reward claim without staking |
testCanNotClaimIfNotElapsedTime() |
Requires time to elapse before claiming |
testShouldRevertIfNoEther() |
Prevents reward claim if contract has no ETH |
testCanClaimRewards() |
Successfully claims ETH after staking period |
Fuzz Test | Coverage |
---|---|
testFuzzIncorrectAmountRevert() |
Ensures only fixed amount deposits are accepted |
Both contracts are fully covered by testing: every line, statement, branch and function:
forge coverage
File | % Lines | % Statements | % Branches | % Funcs |
---|---|---|---|---|
src/StakingApp.sol | 100.00% (29/29) | 100.00% (25/25) | 100.00% (10/10) | 100.00% (6/6) |
src/StakingToken.sol | 100.00% (2/2) | 100.00% (1/1) | 100.00% (0/0) | 100.00% (1/1) |
Total | 100.00% (31/31) | 100.00% (26/26) | 100.00% (10/10) | 100.00% (7/7) |
- Solidity:
^0.8.24
- Foundry: For testing, fuzzing and assertions
- OpenZeppelin Contracts:
Ownable
,ERC20
,IERC20
- Install Foundry
- Install OpenZeppelin
git clone https://github.com/your-repo/staking-dapp.git
cd staking-dapp
forge install
forge test
forge --match-test testExample -vvvv
This project is licensed under the MIT License.