137 lines
4.1 KiB
Solidity
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);
|
|
}
|
|
}
|