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.