StakingContract
Overview
The StakingContract manages a staking system where users can stake POP, earn rewards, and accumulate points. It uses OpenZeppelin's upgradeable contracts for initialization, ownership, reentrancy protection, and upgradeability.
Contract Details
- License: MIT
- Solidity Version: ^0.8.24
- Inheritance: Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, UUPSUpgradeable
- Source Code: Staking
Data Structures
Constants
uint256 private constant PRECISION = 1e18;
This constant is used for precision in calculations to avoid rounding errors.
Structs
UserInfo
struct UserInfo {
uint256 amount; // Amount of POP staked by the user
uint256 rewardDebt; // Reward debt used for accurate reward calculation
uint256 pointsDebt; // Points debt used for accurate points calculation
uint256 earnedRewards; // Accumulated rewards not yet claimed
uint256 points; // Accumulated points
uint256 lastStaked; // Timestamp of last stake
uint256 lastClaimed; // Timestamp of last reward claim
uint256 totalRewards; // Total rewards claimed over time
}
This struct stores all staking-related information for a user.
Mappings
mapping(address => UserInfo) public userInfo;
Maps user addresses to their UserInfo structs.
State Variables
uint256 public totalStaked;
uint256 public accRewardPerShare;
uint256 public accPointsPerShare;
uint256 public lastUpdateTime;
uint256 public rewardPerSecond;
uint256 public pointsPerSecond;
totalStaked
: Total amount of POP staked in the contract.accRewardPerShare
: Accumulated rewards per share, used for reward calculations.accPointsPerShare
: Accumulated points per share, used for points calculations.lastUpdateTime
: Timestamp of the last update to the pool.rewardPerSecond
: Rate of reward distribution per second.pointsPerSecond
: Rate of points distribution per second.
Events
event Staked(address indexed user, uint256 amount);
Emitted when a user stakes POP.
event Unstaked(address indexed user, uint256 amount);
Emitted when a user unstakes POP.
event RewardPaid(address indexed user, uint256 reward);
Emitted when a user claims their rewards.
event PointsEarned(address indexed user, uint256 points);
Emitted when a user earns points.
event RewardPerSecondChanged(uint256 newRewardPerSecond);
Emitted when the reward rate is changed.
event PointsPerSecondChanged(uint256 newPointsPerSecond);
Emitted when the points rate is changed.
Function Signatures and Descriptions
initialize
function initialize(uint256 _rewardPerSecond, uint256 _pointsPerSecond, address _initialOwner) public initializer
Initializes the contract with initial reward and points rates, and sets the initial owner.
updatePool
function updatePool() public
Updates the pool's accumulated rewards and points based on the time elapsed since the last update.
stake
function stake() external payable nonReentrant
Allows a user to stake POP.
stakeOnBehalf
function stakeOnBehalf(address _user) external payable
Allows staking on behalf of another user.
unstake
function unstake(uint256 _amount) external nonReentrant
Allows a user to unstake their POP.
claimRewards
function claimRewards() external nonReentrant
Allows a user to claim their earned rewards and points.
earnedRewards
function earnedRewards(address _user) external view returns (uint256)
Calculates the earned rewards for a user.
earnedUserPoints
function earnedUserPoints(address _user) external view returns (uint256)
Calculates the earned points for a user.
changeRewardPerSecond
function changeRewardPerSecond(uint256 _newRewardPerSecond) external onlyOwner
Allows the owner to change the reward rate.
changePointsPerSecond
function changePointsPerSecond(uint256 _newPointsPerSecond) external onlyOwner
Allows the owner to change the points rate.
getStakedBalance
function getStakedBalance(address _user) external view returns (uint256)
Returns the staked balance of a user.
getCurrentRewardPerSecond
function getCurrentRewardPerSecond() external view returns (uint256)
Returns the current reward rate per second.
getCurrentPointsPerSecond
function getCurrentPointsPerSecond() external view returns (uint256)
Returns the current points rate per second.
getUserInfo
function getUserInfo(address _user) external view returns (UserInfo memory)
Returns the full UserInfo struct for a user.
emergencyWithdraw
function emergencyWithdraw() external onlyOwner
Allows the owner to withdraw all POP from the contract in case of emergency.
Internal Functions
updateUserEarnedStats
function updateUserEarnedStats() private
Updates the user's earned rewards and points.
recalculateUserDebt
function recalculateUserDebt() private
Recalculates the user's reward and points debt.
_stake
function _stake(address _user) private
Internal function to handle staking logic.
_authorizeUpgrade
function _authorizeUpgrade(address newImplementation) internal override onlyOwner
Function that should revert when msg.sender
is not authorized to upgrade the contract.
Key Features and Considerations
-
POP Staking: Users can stake POP directly, without the need for wrapped POP or other tokens.
-
Dual Reward System: Users earn both rewards (presumably in POP) and points.
-
Flexible Staking: Users can stake for themselves or on behalf of others.
-
Continuous Rewards: Rewards and points accrue continuously based on the time elapsed and amount staked.
-
Upgradeable: The contract is designed to be upgradeable using the UUPS pattern.
-
Reentrancy Protection: Uses OpenZeppelin's ReentrancyGuard to protect against reentrancy attacks.
-
Owner Controls: The contract owner can adjust reward and point rates, and perform emergency withdrawals.
Usage Example
// Initialize the contract
staking.initialize(1e15, 1e18, ownerAddress); // 0.001 POP reward per second, 1 point per second
// User stakes POP
staking.stake{value: 1 ether}();
// After some time, user claims rewards
staking.claimRewards();
// User unstakes some POP
staking.unstake(0.5 ether);
// Check user's earned rewards and points
uint256 rewards = staking.earnedRewards(userAddress);
uint256 points = staking.earnedUserPoints(userAddress);
// Owner changes reward rate
staking.changeRewardPerSecond(2e15); // 0.002 POP reward per second
The StakingContract provides a flexible and upgradeable staking system with dual rewards (POP and points). It's designed to integrate with other contracts in a larger ecosystem, potentially using the earned points for governance or other purposes. The contract includes safeguards against common vulnerabilities and allows for owner control of key parameters.