// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract Staking {
// Token used for staking
IERC20 public stakingToken;
// Token used for rewards
IERC20 public rewardToken;
// Mapping of user addresses to their stakes
mapping(address => uint256) public stakes;
// Mapping of user addresses to their accumulated reward points
mapping(address => uint256) public rewardPoints;
// Mapping of user addresses to the last time they staked
mapping(address => uint256) public lastStakeTime;
// Reward rate per second (adjustable)
uint256 public rewardRate = 100; // reward rate per second
// Total amount of tokens staked in the contract
uint256 public totalStaked;
// Events to log staking actions
event Staked(address indexed user, uint256 amount);
event Unstaked(address indexed user, uint256 amount);
event RewardsClaimed(address indexed user, uint256 reward);
// Constructor to set the staking and reward tokens
constructor(IERC20 _stakingToken, IERC20 _rewardToken) {
stakingToken = _stakingToken;
rewardToken = _rewardToken;
}
// Function to stake tokens
function stake(uint256 amount) external {
require(amount > 0, "Amount must be greater than zero");
// Update rewards for previous stake before new staking
_updateRewards(msg.sender);
// Transfer tokens from the user to the contract
stakingToken.transferFrom(msg.sender, address(this), amount);
// Increase user's stake and total staked amount
stakes[msg.sender] += amount;
totalStaked += amount;
// Record the current time as the last stake time
lastStakeTime[msg.sender] = block.timestamp;
emit Staked(msg.sender, amount);
}
// Function to unstake tokens
function unstake(uint256 amount) external {
require(amount > 0 && amount <= stakes[msg.sender], "Invalid amount");
// Update rewards before unstaking
_updateRewards(msg.sender);
// Decrease user's stake and total staked amount
stakes[msg.sender] -= amount;
totalStaked -= amount;
// Transfer tokens back to the user
stakingToken.transfer(msg.sender, amount);
emit Unstaked(msg.sender, amount);
}
// Function to claim rewards
function claimRewards() external {
_updateRewards(msg.sender);
uint256 reward = rewardPoints[msg.sender];
require(reward > 0, "No rewards available");
// Reset reward points after claiming
rewardPoints[msg.sender] = 0;
// Transfer reward tokens to the user
rewardToken.transfer(msg.sender, reward);
emit RewardsClaimed(msg.sender, reward);
}
// Internal function to update rewards for a user
function _updateRewards(address user) internal {
// Calculate the time the user has staked
uint256 stakedTime = block.timestamp - lastStakeTime[user];
// Calculate rewards based on the staked amount and time
uint256 rewards = stakedTime * rewardRate * stakes[user] / 1e18;
// Add calculated rewards to user's reward points
rewardPoints[user] += rewards;
// Update the last stake time to the current time
lastStakeTime[user] = block.timestamp;
}
// Function to get the total rewards for a user
function getRewards(address user) external view returns (uint256) {
uint256 stakedTime = block.timestamp - lastStakeTime[user];
uint256 rewards = stakedTime * rewardRate * stakes[user] / 1e18;
return rewardPoints[user] + rewards;
}
}