Skip to main content

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​

  1. POP Staking: Users can stake POP directly, without the need for wrapped POP or other tokens.

  2. Dual Reward System: Users earn both rewards (presumably in POP) and points.

  3. Flexible Staking: Users can stake for themselves or on behalf of others.

  4. Continuous Rewards: Rewards and points accrue continuously based on the time elapsed and amount staked.

  5. Upgradeable: The contract is designed to be upgradeable using the UUPS pattern.

  6. Reentrancy Protection: Uses OpenZeppelin's ReentrancyGuard to protect against reentrancy attacks.

  7. 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.