增加nft stating合约
This commit is contained in:
parent
25b16ec862
commit
65a0d36702
21139
build/contracts/ERC721Staking.json
Normal file
21139
build/contracts/ERC721Staking.json
Normal file
File diff suppressed because one or more lines are too long
136
contracts/stake/ERC721Staking.sol
Normal file
136
contracts/stake/ERC721Staking.sol
Normal 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);
|
||||
}
|
||||
}
|
110
contracts/stake/TokenStaking.sol
Normal file
110
contracts/stake/TokenStaking.sol
Normal 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);
|
||||
}
|
||||
}
|
40
migrations/9_deploy_stake.js
Normal file
40
migrations/9_deploy_stake.js
Normal 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]}`);
|
||||
}
|
||||
};
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
@ -124,5 +124,11 @@
|
||||
"type": "logic",
|
||||
"json": "assets/contracts/BETokenMall.json",
|
||||
"address": "0x22557f3fe7fbA53C66a35a3D70527D014704fd4D"
|
||||
},
|
||||
{
|
||||
"name": "ERC721Staking",
|
||||
"type": "logic",
|
||||
"json": "assets/contracts/ERC721Staking.json",
|
||||
"address": "0xefe4b6b4C94b7514543A7ed912C877D6F560DdC2"
|
||||
}
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user