编译器
0.8.17+commit.8df45f5f
文件 1 的 12:Context.sol
pragma solidity ^0.8.0;
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 的 12:ERC1155Holder.sol
pragma solidity ^0.8.0;
import "./ERC1155Receiver.sol";
contract ERC1155Holder is ERC1155Receiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
文件 3 的 12:ERC1155Receiver.sol
pragma solidity ^0.8.0;
import "../IERC1155Receiver.sol";
import "../../../utils/introspection/ERC165.sol";
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
}
文件 4 的 12:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 12:ERC721Holder.sol
pragma solidity ^0.8.0;
import "../IERC721Receiver.sol";
contract ERC721Holder is IERC721Receiver {
function onERC721Received(
address,
address,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
}
文件 6 的 12:IERC1155.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
文件 7 的 12:IERC1155Receiver.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC1155Receiver is IERC165 {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
文件 8 的 12:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 9 的 12:IERC721.sol
pragma solidity ^0.8.0;
import "../token/ERC721/IERC721.sol";
文件 10 的 12:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 11 的 12:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 12 的 12:VhilsStaking.sol
pragma solidity 0.8.17;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC721 } from "@openzeppelin/contracts/interfaces/IERC721.sol";
import { IERC1155 } from "@openzeppelin/contracts/interfaces/IERC1155.sol";
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
struct TokenInfo {
uint8 state;
bool locked;
uint8[4] tearIdUsed;
uint64 name;
string uri;
}
interface ILAYERS is IERC721 {
function getTokenInfo(uint16 _tokenId) external view returns (TokenInfo memory);
}
contract VhilsStaking is Ownable, ERC721Holder, ERC1155Holder {
struct TierTear {
uint8 level;
uint256 startTime;
}
struct TierMedal {
uint8 rank;
uint256 startTime;
}
struct StakingInfo {
address owner;
TierTear tierTear;
TierMedal tierMedal;
}
address public vhilsNFT;
address public vhilsTears;
mapping(address => uint16[]) stakedLayers;
mapping(uint16 => StakingInfo) public stakingInfo;
event Staked(uint16 indexed tokenId, address from, uint8 currentLevel, uint256 startTime, uint8 stakingType);
event UnStaked(uint16 indexed tokenId, address from, uint8 newLevel, uint256 startTime, uint256 endTime, uint8 stakingType);
constructor() {}
function getStakedLayers(address _owner) external view returns (uint16[] memory) {
return stakedLayers[_owner];
}
function stake(uint16 _tokenId) public {
require(ILAYERS(vhilsNFT).ownerOf(_tokenId) == msg.sender, "Require owner");
ILAYERS(vhilsNFT).transferFrom(msg.sender, address(this), _tokenId);
stakedLayers[msg.sender].push(_tokenId);
stakingInfo[_tokenId].owner = msg.sender;
TokenInfo memory tokenInfo = ILAYERS(vhilsNFT).getTokenInfo(_tokenId);
uint256 _currentTime = block.timestamp;
if (tokenInfo.state == 3) {
stakingInfo[_tokenId].tierMedal.startTime = _currentTime;
emit Staked(_tokenId, msg.sender, stakingInfo[_tokenId].tierMedal.rank, _currentTime, 1);
} else {
uint8 stakingLevel = stakingInfo[_tokenId].tierTear.level;
uint8 currentLevel = stakingLevel >= tokenInfo.state ? stakingLevel : tokenInfo.state;
require(currentLevel < 3, "Exceed state");
stakingInfo[_tokenId].tierTear.level = currentLevel;
stakingInfo[_tokenId].tierTear.startTime = _currentTime;
emit Staked(_tokenId, msg.sender, currentLevel, _currentTime, 0);
}
}
function withdraw(uint16 _tokenId) public {
require(msg.sender == stakingInfo[_tokenId].owner, "Require owner");
ILAYERS(vhilsNFT).transferFrom(address(this), msg.sender, _tokenId);
stakingInfo[_tokenId].owner = address(0);
TokenInfo memory tokenInfo = ILAYERS(vhilsNFT).getTokenInfo(_tokenId);
uint256 _currentTime = block.timestamp;
if (tokenInfo.state == 3) {
_withdrawForMedals(_tokenId);
emit UnStaked(_tokenId, msg.sender, stakingInfo[_tokenId].tierMedal.rank, stakingInfo[_tokenId].tierMedal.startTime, _currentTime, 1);
} else {
_withdrawForTears(_tokenId);
emit UnStaked(_tokenId, msg.sender, stakingInfo[_tokenId].tierTear.level, stakingInfo[_tokenId].tierTear.startTime, _currentTime, 0);
}
}
function _withdrawForMedals(uint16 _tokenId) internal {
uint24[5] memory stakingDays = [
10 * 24 * 60 * 60,
30 * 24 * 60 * 60,
60 * 24 * 60 * 60,
60 * 24 * 60 * 60,
116 * 24 * 60 * 60
];
uint256 durations = block.timestamp - stakingInfo[_tokenId].tierMedal.startTime;
uint8 i = stakingInfo[_tokenId].tierMedal.rank;
for (; i < stakingDays.length; i++) {
if (durations < stakingDays[i]) {
break;
}
durations -= stakingDays[i];
}
stakingInfo[_tokenId].tierMedal.rank = i;
_removeTokenFromStakedList(msg.sender, _tokenId);
}
function _withdrawForTears(uint16 _tokenId) internal {
uint24[3] memory stakingDays = [120 * 24 * 60 * 60, 70 * 24 * 60 * 60, 40 * 24 * 60 * 60];
uint256 durations = block.timestamp - stakingInfo[_tokenId].tierTear.startTime;
uint8 i = stakingInfo[_tokenId].tierTear.level;
for (; i < stakingDays.length; i++) {
if (durations < stakingDays[i]) {
break;
}
IERC1155(vhilsTears).safeTransferFrom(address(this), msg.sender, 2, 1, "");
durations -= stakingDays[i];
}
stakingInfo[_tokenId].tierTear.level = i;
_removeTokenFromStakedList(msg.sender, _tokenId);
}
function batchStake(uint16[] memory _tokenIds) external {
for (uint16 i = 0; i < _tokenIds.length; i++) {
stake(_tokenIds[i]);
}
}
function batchWithdraw(uint16[] memory _tokenIds) external {
for (uint16 i = 0; i < _tokenIds.length; i++) {
withdraw(_tokenIds[i]);
}
}
function bindVhilsNFT(address _contract) external onlyOwner {
vhilsNFT = _contract;
}
function bindVhilsTears(address _contract) external onlyOwner {
vhilsTears = _contract;
}
function forceWithdrawNFT(uint16[] memory _tokenIds) external onlyOwner {
uint16 size = uint16(_tokenIds.length);
for (uint16 i = 0; i < size; i++) {
ILAYERS(vhilsNFT).transferFrom(address(this), msg.sender, _tokenIds[i]);
}
}
function _removeTokenFromStakedList(address _owner, uint16 _tokenId) internal {
uint16 _index;
for (uint16 i = 0; i < stakedLayers[_owner].length; i++) {
if (stakedLayers[_owner][i] == _tokenId) {
_index = i;
break;
}
}
uint16 _finalIndex = uint16(stakedLayers[_owner].length - 1);
if (_index != _finalIndex) {
stakedLayers[_owner][_index] = stakedLayers[_owner][_finalIndex];
}
stakedLayers[_owner].pop();
}
}
{
"compilationTarget": {
"contracts/VhilsStaking.sol": "VhilsStaking"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint8","name":"currentLevel","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"stakingType","type":"uint8"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"tokenId","type":"uint16"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint8","name":"newLevel","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"stakingType","type":"uint8"}],"name":"UnStaked","type":"event"},{"inputs":[{"internalType":"uint16[]","name":"_tokenIds","type":"uint16[]"}],"name":"batchStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"_tokenIds","type":"uint16[]"}],"name":"batchWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"bindVhilsNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"bindVhilsTears","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"_tokenIds","type":"uint16[]"}],"name":"forceWithdrawNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"getStakedLayers","outputs":[{"internalType":"uint16[]","name":"","type":"uint16[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_tokenId","type":"uint16"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"","type":"uint16"}],"name":"stakingInfo","outputs":[{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"uint8","name":"level","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"}],"internalType":"struct VhilsStaking.TierTear","name":"tierTear","type":"tuple"},{"components":[{"internalType":"uint8","name":"rank","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"}],"internalType":"struct VhilsStaking.TierMedal","name":"tierMedal","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","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":[],"name":"vhilsNFT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vhilsTears","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_tokenId","type":"uint16"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]