becrypto/contracts/stake/TokenStaking.sol
2023-08-23 14:15:41 +08:00

111 lines
3.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
contract TokenStaking is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
uint256 public cooldownSeconds = 1 days;
uint256 orderIndex;
struct Staker {
address user;
address token;
uint256 amount;
uint64 start;
uint64 stakeTime;
}
mapping(uint256 => Staker) public stakingMap;
// User-selectable stake time, in seconds
mapping(uint64 => bool) public periods;
// User-selectable stake token
mapping(address => bool) public erc20Supported;
event Staked(address indexed user, Staker info);
event Redeem(address indexed user, Staker[] infos);
event EditTokenSuppout(address token, bool status);
constructor() {}
function stake(
address token,
uint256 amount,
uint64 staketime
) external nonReentrant whenNotPaused {
require(amount > 0, "TokenStaking: amount is 0");
require(
periods[staketime] == true,
"TokenStaking: staketime is not supported"
);
require(
erc20Supported[token] == true,
"TokenStaking: token is not supported"
);
address to = msg.sender;
require(
IERC20(token).balanceOf(_msgSender()) >= amount,
"TokenStaking: user doesn't have enough token to buy this item"
);
require(
IERC20(token).allowance(_msgSender(), address(this)) >= amount,
"TokenStaking: user doesn't approve TokenStaking to spend amount"
);
uint64 _begin = uint64(block.timestamp);
uint64 _staketime = _begin + staketime;
orderIndex += 1;
stakingMap[orderIndex] = Staker(to, token, amount, _begin, _staketime);
IERC20(token).safeTransferFrom(to, address(this), amount);
emit Staked(to, stakingMap[orderIndex]);
}
function redeem(
address[] calldata tokens,
uint256[] calldata ids
) external nonReentrant {
require(
tokens.length == ids.length,
"TokenStaking: tokens length != ids length"
);
address to = msg.sender;
uint256 amount = 0;
// check if ids are valid
Staker[] memory _infos = new Staker[](ids.length);
for (uint256 i = 0; i < ids.length; i++) {
require(stakingMap[ids[i]].amount > 0, "TokenStaking: id is not valid");
require(
stakingMap[ids[i]].start + cooldownSeconds <= block.timestamp,
"TokenStaking: cooldown time is not reached"
);
amount += stakingMap[ids[i]].amount;
_infos[i] = stakingMap[ids[i]];
IERC20(stakingMap[ids[i]].token).safeTransfer(to, amount);
delete stakingMap[ids[i]];
}
emit Redeem(to, _infos);
}
function updatePeriods(uint64 period, bool status) external onlyOwner {
periods[period] = status;
}
/**
* @dev update ERC721 support
*/
function updateERC20Support(address token, bool status) external onlyOwner {
erc20Supported[token] = status;
emit EditTokenSuppout(token, status);
}
}