编译器
0.8.28+commit.7893614a
文件 1 的 5:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 2 的 5:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 3 的 5:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 4 的 5:NFTStaking.sol
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract NFTStaking is Ownable {
constructor(address _nftCollection) Ownable(msg.sender) {
nftCollection = IERC721(_nftCollection);
}
IERC721 public nftCollection;
struct StakeInfo {
bool isStaked;
uint256 unlockTime;
}
mapping(address => mapping(uint256 => StakeInfo)) public stakedNFTs;
mapping(uint256 => bool) public stakingPeriods;
event NFTLocked(address indexed user, uint256 indexed tokenId, uint256 unlockTime);
event NFTUnlocked(address indexed user, uint256 indexed tokenId);
event NFTStakeExtended(address indexed user, uint256 indexed tokenId, uint256 newUnlockTime);
event StakingPeriodAdded(uint256 period);
event StakingPeriodRemoved(uint256 period);
function lockNFT(uint256 tokenId, uint256 period) external {
require(isValidStakingPeriod(period), "Invalid staking period");
uint256 unlockTime = block.timestamp + period;
stakedNFTs[msg.sender][tokenId] = StakeInfo(true, unlockTime);
emit NFTLocked(msg.sender, tokenId, unlockTime);
nftCollection.transferFrom(msg.sender, address(this), tokenId);
}
function unlockNFT(uint256 tokenId) external {
StakeInfo storage stake = stakedNFTs[msg.sender][tokenId];
require(stake.isStaked, "This NFT is not staked");
require(block.timestamp >= stake.unlockTime, "Staking period not yet ended");
stake.isStaked = false;
nftCollection.transferFrom(address(this), msg.sender, tokenId);
emit NFTUnlocked(msg.sender, tokenId);
}
function extendStake(uint256 tokenId, uint256 newPeriod) external {
require(isValidStakingPeriod(newPeriod), "Invalid staking period");
StakeInfo storage stake = stakedNFTs[msg.sender][tokenId];
require(stake.isStaked, "This NFT is not staked");
stake.unlockTime = block.timestamp + newPeriod;
emit NFTStakeExtended(msg.sender, tokenId, stake.unlockTime);
}
function getUnlockTime(address user, uint256 tokenId) external view returns (uint256) {
StakeInfo storage stake = stakedNFTs[user][tokenId];
require(stake.isStaked, "This NFT is not staked");
return stake.unlockTime;
}
function addStakingPeriod(uint256 period) external onlyOwner {
require(period > 0, "Period must be greater than 0");
stakingPeriods[period] = true;
emit StakingPeriodAdded(period);
}
function removeStakingPeriod(uint256 period) external onlyOwner {
require(stakingPeriods[period], "Staking period not found");
stakingPeriods[period] = false;
emit StakingPeriodRemoved(period);
}
function isValidStakingPeriod(uint256 period) internal view returns (bool) {
return stakingPeriods[period];
}
}
文件 5 的 5:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
{
"compilationTarget": {
"src/contracts/NFTStaking.sol": "NFTStaking"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_nftCollection","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockTime","type":"uint256"}],"name":"NFTLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newUnlockTime","type":"uint256"}],"name":"NFTStakeExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NFTUnlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"period","type":"uint256"}],"name":"StakingPeriodAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"period","type":"uint256"}],"name":"StakingPeriodRemoved","type":"event"},{"inputs":[{"internalType":"uint256","name":"period","type":"uint256"}],"name":"addStakingPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"newPeriod","type":"uint256"}],"name":"extendStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getUnlockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"}],"name":"lockNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nftCollection","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"period","type":"uint256"}],"name":"removeStakingPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakedNFTs","outputs":[{"internalType":"bool","name":"isStaked","type":"bool"},{"internalType":"uint256","name":"unlockTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakingPeriods","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"unlockNFT","outputs":[],"stateMutability":"nonpayable","type":"function"}]