增加nft stating合约

This commit is contained in:
CounterFire2023 2023-08-23 14:15:41 +08:00
parent 25b16ec862
commit 65a0d36702
6 changed files with 21444 additions and 1 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,136 @@
// 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] = _beginTimes[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);
}
}

View File

@ -0,0 +1,110 @@
// 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);
}
}

View File

@ -0,0 +1,40 @@
const base = require("../scripts/base");
const ERC721Staking = artifacts.require("stake/ERC721Staking");
module.exports = async function (deployer, network, accounts) {
await deployer.deploy(ERC721Staking);
const stakeInstance = await ERC721Staking.deployed();
if (stakeInstance) {
console.log("ERC721Staking successfully deployed.");
console.log("address: " + stakeInstance.address);
}
base.updateArray({
name: "ERC721Staking",
type: "logic",
json: "assets/contracts/ERC721Staking.json",
address: stakeInstance.address,
network,
});
const nfts = [
"0xae37bb7BcA26Bab9a11D8BaE8fdB97f63b82c189",
"0xEbC170185ad614C05Af38C820020b70E458717F5",
"0x2d3Afa678F777Df6b59186A54E5427c3527C637c",
];
for (let i = 0; i < nfts.length; i++) {
await stakeInstance.updateERC721Support(nfts[i], true);
console.log(`success add erc721 support for ${nfts[i]}`);
}
const DAYSECONDS = 24 * 60 * 60;
const periods = [
30 * DAYSECONDS, // 1 month 2592000
91 * DAYSECONDS, // 3 months 7862400
182 * DAYSECONDS, // 6 months 15724800
365 * DAYSECONDS, // 1 year 31536000
730 * DAYSECONDS, // 2 years 63072000
];
for (let i = 0; i < periods.length; i++) {
await stakeInstance.updatePeriods(periods[i], true);
console.log(`success add period ${periods[i]}`);
}
};

View File

@ -63,7 +63,7 @@
"name": "BEMultiSigWallet",
"type": "logic",
"json": "assets/contracts/BEMultiSigWallet.json",
"address": "0x3Cd052Cc25358DB844bcDDfFa514080d41a4c2cA"
"address": "0x62871e8e348FDC5bE370E92FF8d17cB04A248242"
},
{
"name": "Genesis",
@ -76,5 +76,17 @@
"type": "logic",
"json": "assets/contracts/UserMinterFactory.json",
"address": "0x24499444b2BE6A8E3F800D47E4Ed0c36B5c0747e"
},
{
"name": "Candy",
"type": "erc721",
"json": "assets/contracts/BEBadge.json",
"address": "0xefD4c863E73e7E9Cc33d46fB30CE51510FCFdeb0"
},
{
"name": "NftDistributor",
"type": "logic",
"json": "assets/contracts/NftDistributor.json",
"address": "0x41D79d900a0aD3017F4697FC19f9E2EE04D05F37"
}
]

View File

@ -124,5 +124,11 @@
"type": "logic",
"json": "assets/contracts/BETokenMall.json",
"address": "0x22557f3fe7fbA53C66a35a3D70527D014704fd4D"
},
{
"name": "ERC721Staking",
"type": "logic",
"json": "assets/contracts/ERC721Staking.json",
"address": "0xefe4b6b4C94b7514543A7ed912C877D6F560DdC2"
}
]