Created
October 1, 2024 07:13
-
-
Save AndreiD/71b25acde9cbd5379659b409599661e6 to your computer and use it in GitHub Desktop.
erc20staking.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.20; | |
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
import "@openzeppelin/utils/Pausable.sol"; | |
contract DogStaking is ReentrancyGuard, Ownable, Pausable { | |
IERC20 stakingToken; | |
struct stakeStruct { | |
uint256 amountStaked; | |
uint256 timeStaked; | |
} | |
uint256 public emissionRate = 1; | |
mapping(address => stakeStruct) public userStakeMap; | |
event Staked(address indexed user, uint256 amount); | |
event Unstaked(address indexed user, uint256 amount); | |
event RewardClaimed(address indexed user, uint256 amount); | |
event RewardsAdded(uint256 amount); | |
event EmissionRateChanged(uint256 newRate); | |
constructor(address _stakingToken, uint256 _emissionRate, address _initialOwner) Ownable(_initialOwner) { | |
stakingToken = IERC20(_stakingToken); | |
emissionRate = _emissionRate; | |
} | |
function calculateRewards(address _staker) public view returns (uint256) { | |
uint256 contractBalance = stakingToken.balanceOf(address(this)); | |
stakeStruct storage _stake = userStakeMap[_staker]; | |
uint256 stakingDuration = block.timestamp - _stake.timeStaked; | |
return (_stake.amountStaked * stakingDuration * emissionRate * (contractBalance / 1e18)) / 1e18; | |
} | |
function stake(uint256 _amount) external nonReentrant whenNotPaused { | |
uint256 balance = stakingToken.balanceOf(msg.sender); | |
require(balance >= _amount, "not enough tokens to stake"); | |
bool status = stakingToken.transferFrom(msg.sender, address(this), _amount); | |
require(status == true, "transfer failed"); | |
stakeStruct storage _stake = userStakeMap[msg.sender]; | |
_stake.amountStaked += _amount; | |
_stake.timeStaked = block.timestamp; | |
emit Staked(msg.sender, _amount); | |
} | |
function claim() external nonReentrant { | |
stakeStruct storage _stake = userStakeMap[msg.sender]; | |
require(_stake.amountStaked > 0, "no stake found"); | |
uint256 rewards = calculateRewards(msg.sender); | |
require(rewards > 0, "no rewards to claim"); | |
_stake.timeStaked = block.timestamp; | |
stakingToken.transfer(msg.sender, rewards); | |
emit RewardClaimed(msg.sender, rewards); | |
} | |
function unstake() external nonReentrant { | |
stakeStruct storage _stake = userStakeMap[msg.sender]; | |
require(_stake.amountStaked > 0, "no stake found"); | |
bool status = stakingToken.transfer(msg.sender, _stake.amountStaked); | |
require(status == true, "Transfer failed"); | |
uint256 rewards = calculateRewards(msg.sender); | |
if (rewards > 0) { | |
stakingToken.transfer(msg.sender, rewards); | |
} | |
_stake.amountStaked -= _stake.amountStaked; | |
_stake.timeStaked = block.timestamp; | |
emit Unstaked(msg.sender, _stake.amountStaked); | |
} | |
function addRewards(uint256 amount) external nonReentrant { | |
require(amount > 0, "Cannot add 0 rewards"); | |
stakingToken.transferFrom(msg.sender, address(this), amount); | |
emit RewardsAdded(amount); | |
} | |
function setEmissionRate(uint256 _emissionRate) external onlyOwner { | |
emissionRate = _emissionRate; | |
emit EmissionRateChanged(_emissionRate); | |
} | |
function pause() external onlyOwner { | |
_pause(); | |
} | |
// Function to unpause staking | |
function unpause() external onlyOwner { | |
_unpause(); | |
} | |
function airdropRewards( | |
address[] calldata recipients, | |
uint256[] calldata amounts | |
) external onlyOwner { | |
require(recipients.length == amounts.length, "Mismatched array lengths"); | |
for (uint256 i = 0; i < recipients.length; i++) { | |
(bool success, bytes memory data) = address(stakingToken).call( | |
abi.encodeWithSelector(IERC20.transfer.selector, recipients[i], amounts[i]) | |
); | |
require(success && (data.length == 0 || abi.decode(data, (bool))), "Token transfer failed"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment