A simple time-based staking contract built with Solidity and Foundry. Users can stake ERC20 tokens and earn rewards proportional to their stake over a defined period.
This project includes two main contracts:
- RewardToken: A standard ERC20 token with mint/burn functionality
- StakingPool: A staking contract that distributes rewards to stakers over time
The staking pool implements the Synthetix staking rewards model, which uses a reward-per-token calculation to ensure fair distribution based on both stake amount and duration. Rewards are distributed continuously over a 30-day period (configurable).
Install dependencies:
forge installCopy the example environment file and add your private key:
cp .example.env .envRun tests:
forge testFor gas reporting:
forge test -vvRun specific tests:
forge test --match-contract StakingPoolTest
forge test --match-test testStakeDeploy to Sepolia testnet:
forge script script/Deploy.s.sol:DeployScript --rpc-url https://ethereum-sepolia-rpc.publicnode.com --broadcast -vvvvThe deploy script will:
- Deploy the RewardToken with an initial supply
- Deploy the StakingPool contract
- Log both contract addresses
Standard ERC20 with:
- Owner-controlled minting
- Public burn function
- Initial supply minted to deployer
Features:
- Stake tokens to earn rewards
- Withdraw staked tokens at any time
- Claim accumulated rewards
- Emergency withdraw (forfeit rewards)
- Owner can add rewards to extend/refresh the reward period
The pool tracks rewardPerToken which increases based on time elapsed and total staked amount. Each user's rewards are calculated by multiplying their stake by the difference between current and their last recorded rewardPerToken. This allows for efficient reward calculation without iterating through all stakers.
// Stake tokens
token.approve(address(stakingPool), amount);
stakingPool.stake(amount);
// Check earned rewards
uint256 earned = stakingPool.earned(userAddress);
// Claim rewards
stakingPool.getReward();
// Withdraw stake
stakingPool.withdraw(amount);- Uses OpenZeppelin's ReentrancyGuard for state-changing functions
- SafeERC20 for all token transfers
- Owner-only functions for privileged operations
- Emergency withdraw function for users in case of issues
MIT