编译器
0.8.10+commit.fc410830
文件 1 的 7: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 的 7:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 3 的 7:IERC721.sol
pragma solidity ^0.8.0;
import "../../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
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
文件 4 的 7:IERC721Enumerable.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 5 的 7:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 6 的 7: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);
}
}
文件 7 的 7:TombVault.sol
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
interface IKlub {
function mint(address to, uint256 amount) external;
}
contract TombVault is Ownable, IERC721Receiver {
struct Stake {
uint24 tokenId;
uint48 timestamp;
address owner;
}
uint public KLUBS_PER_TOMB_PER_DAY = 5 * 1 ether;
mapping(uint => Stake) public vault;
uint public totalStaked;
IERC721Enumerable public tomb = IERC721Enumerable(0x40f8719f2919a5DEDD2D5A67065df6EaC65c149C);
IKlub public klub = IKlub(0xa0DB234a35AaF919b51E1F6Dc21c395EeF2F959d);
event Staked(address owner, uint tokenId, uint value);
event Unstaked(address owner, uint tokenId, uint value);
event Claimed(address owner, uint amount);
constructor() {
}
function setTomb(address _tomb) external onlyOwner {
tomb = IERC721Enumerable(_tomb);
}
function setKlub(address _klub) external onlyOwner {
klub = IKlub(_klub);
}
function setKLUBS_PER_TOMB_PER_DAY(uint _KLUBS_PER_TOMB_PER_DAY) external onlyOwner {
KLUBS_PER_TOMB_PER_DAY = _KLUBS_PER_TOMB_PER_DAY * 1 ether;
}
function stake(uint[] calldata tokenIds) external {
totalStaked += tokenIds.length;
for (uint i = 0; i < tokenIds.length; i++) {
uint tokenId = tokenIds[i];
require(tomb.ownerOf(tokenId) == msg.sender, "TombVault: Only the owner of the tomb klub can stake it");
require(vault[tokenId].tokenId == 0, "TombVault: Token already staked");
tomb.transferFrom(msg.sender, address(this), tokenId);
emit Staked(msg.sender, tokenId, block.timestamp);
vault[tokenId] = Stake({
owner: msg.sender,
tokenId: uint24(tokenId),
timestamp: uint48(block.timestamp)
});
}
}
function _unstakeMany(address account, uint[] calldata tokenIds) internal {
totalStaked -= tokenIds.length;
for (uint i = 0; i < tokenIds.length; i++) {
uint tokenId = tokenIds[i];
Stake memory staked = vault[tokenId];
require(staked.owner == msg.sender, "TombVault: Only the Staker can unstake it");
delete vault[tokenId];
emit Unstaked(account, tokenId, block.timestamp);
tomb.transferFrom(address(this), account, tokenId);
}
}
function claim(uint[] calldata tokenIds) external {
_claim(msg.sender, tokenIds, false);
}
function claimForAddress(address account, uint[] calldata tokenIds) external {
_claim(account, tokenIds, false);
}
function unstake(uint[] calldata tokenIds) external {
_claim(msg.sender, tokenIds, true);
}
function _claim(address account, uint[] calldata tokenIds, bool _unstake) internal {
uint earned;
for (uint i = 0; i < tokenIds.length; i++) {
uint tokenId = tokenIds[i];
Stake memory staked = vault[tokenId];
require(staked.owner == account, "TombVault: Only the Staker can claim");
earned += KLUBS_PER_TOMB_PER_DAY * (block.timestamp - staked.timestamp) / 1 days;
vault[tokenId] = Stake({
owner: account,
tokenId: uint24(tokenId),
timestamp: uint48(block.timestamp)
});
}
if (earned > 0) {
klub.mint(account, earned);
}
if (_unstake) {
_unstakeMany(account, tokenIds);
}
emit Claimed(account, earned);
}
function unclamiedEarnings(uint[] calldata tokenIds) external view returns (uint) {
uint earned;
for (uint i = 0; i < tokenIds.length; i++) {
uint tokenId = tokenIds[i];
Stake memory staked = vault[tokenId];
uint stakedAt = staked.timestamp;
require(stakedAt > 0, "TombVault: Stake not found");
earned += KLUBS_PER_TOMB_PER_DAY * (block.timestamp - stakedAt) / 1 days;
}
return earned;
}
function balanceOf(address account) external view returns (uint) {
uint balance;
uint supply = tomb.totalSupply();
for(uint i = 0; i < supply; i++) {
if (vault[i].owner == account) {
balance += 1;
}
}
return balance;
}
function tokensOfOwner(address account) external view returns (uint[] memory ownerTokens) {
uint supply = tomb.totalSupply();
uint[] memory tmp = new uint[](supply);
uint index;
for(uint tokenId = 0; tokenId < supply; tokenId++) {
if (vault[tokenId].owner == account) {
tmp[index] = vault[tokenId].tokenId;
index +=1;
}
}
uint[] memory tokens = new uint[](index);
for(uint i = 0; i < index; i++) {
tokens[i] = tmp[i];
}
return tokens;
}
function onERC721Received(
address,
address from,
uint,
bytes calldata
) external pure override returns (bytes4) {
require(from == address(0x0), "TombVault: Can't receive tokens directly by minting");
return IERC721Receiver.onERC721Received.selector;
}
}
{
"compilationTarget": {
"contracts/TombVault.sol": "TombVault"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","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":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[],"name":"KLUBS_PER_TOMB_PER_DAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"claimForAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"klub","outputs":[{"internalType":"contract IKlub","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","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":"uint256","name":"_KLUBS_PER_TOMB_PER_DAY","type":"uint256"}],"name":"setKLUBS_PER_TOMB_PER_DAY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_klub","type":"address"}],"name":"setKlub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tomb","type":"address"}],"name":"setTomb","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"ownerTokens","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tomb","outputs":[{"internalType":"contract IERC721Enumerable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unclamiedEarnings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vault","outputs":[{"internalType":"uint24","name":"tokenId","type":"uint24"},{"internalType":"uint48","name":"timestamp","type":"uint48"},{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"}]