becrypto/contracts/stake/ERC721Staking.sol
2023-09-06 13:54:51 +08:00

137 lines
4.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
contract ERC721Staking is Ownable, ReentrancyGuard, Pausable, ERC721Holder {
uint256 public cooldownSeconds = 1 days;
struct Staker {
address user;
address nft;
uint256 tokenId;
uint64 start;
uint64 stakeTime;
}
// nft address => token id => Staker
mapping(address => mapping(uint256 => Staker)) public stakingMap;
// User-selectable stake NFT
mapping(address => bool) public erc721Supported;
// User-selectable stake time, in seconds
mapping(uint64 => bool) public periods;
// event of stake
event Staked(address indexed user, Staker[] infos);
// event of redeem
event Redeem(address indexed user, Staker[] infos);
// event of update ERC721 support
event EditNFTSuppout(address nftToken, bool status);
// event of update periods
event EditPeriods(uint64 period, bool status);
// event of update cooldownSeconds
event EditCooldownSeconds(uint256 oldVal, uint256 newVal);
function stake(
address[] calldata nfts,
uint256[] calldata tokenIds,
uint64[] calldata staketimes
) external nonReentrant whenNotPaused {
require(
nfts.length == tokenIds.length,
"ERC721Staking: nfts length != tokenIds length"
);
require(
nfts.length == staketimes.length,
"ERC721Staking: nfts length != staketimes length"
);
address account = msg.sender;
uint64[] memory _beginTimes = new uint64[](nfts.length);
uint64[] memory _stakeTimes = new uint64[](nfts.length);
Staker[] memory _infos = new Staker[](nfts.length);
for (uint256 i = 0; i < nfts.length; i++) {
require(
erc721Supported[nfts[i]] == true,
"ERC721Staking: nft is not supported"
);
require(
periods[staketimes[i]] == true,
"ERC721Staking: staketime is not supported"
);
IERC721(nfts[i]).safeTransferFrom(account, address(this), tokenIds[i]);
_beginTimes[i] = uint64(block.timestamp);
_stakeTimes[i] = staketimes[i];
stakingMap[nfts[i]][tokenIds[i]] = Staker(
account,
nfts[i],
tokenIds[i],
_beginTimes[i],
_stakeTimes[i]
);
_infos[i] = stakingMap[nfts[i]][tokenIds[i]];
}
emit Staked(account, _infos);
}
function redeem(
address[] calldata nfts,
uint256[] calldata tokenIds
) external nonReentrant {
require(
nfts.length == tokenIds.length,
"ERC721Staking: nfts length != ids length"
);
address account = msg.sender;
// check if ids are valid
Staker[] memory _infos = new Staker[](tokenIds.length);
for (uint256 i = 0; i < tokenIds.length; i++) {
require(
stakingMap[nfts[i]][tokenIds[i]].user != address(0),
"ERC721Staking: tokenId is not valid"
);
require(
stakingMap[nfts[i]][tokenIds[i]].user == account,
"ERC721Staking: user is not the owner of this nft"
);
require(
stakingMap[nfts[i]][tokenIds[i]].start + cooldownSeconds <=
block.timestamp,
"ERC721Staking: cooldown time is not reached"
);
IERC721(nfts[i]).safeTransferFrom(address(this), account, tokenIds[i]);
_infos[i] = stakingMap[nfts[i]][tokenIds[i]];
delete stakingMap[nfts[i]][tokenIds[i]];
}
emit Redeem(account, _infos);
}
/**
* @dev update ERC721 support
*/
function updateERC721Support(
address nftToken,
bool status
) external onlyOwner {
erc721Supported[nftToken] = status;
emit EditNFTSuppout(nftToken, status);
}
function updatePeriods(uint64 period, bool status) external onlyOwner {
periods[period] = status;
emit EditPeriods(period, status);
}
function updateCooldownSeconds(uint256 _cooldownSeconds) external onlyOwner {
uint256 oldVal = cooldownSeconds;
cooldownSeconds = _cooldownSeconds;
emit EditCooldownSeconds(oldVal, _cooldownSeconds);
}
}