EthereumEthereum
0x4c...3354
Staked Cyber Turtles

Staked Cyber Turtles

sCyber

收藏品
大小
4,694
收藏品
所有者
1,358
29% 独特的所有者
此合同的源代码已经过验证!
合同元数据
编译器
0.8.7+commit.e28d00a7
语言
Solidity
合同源代码
文件 1 的 1:sCyberTurtles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Open0x Ownable (by 0xInuarashi)
abstract contract Ownable {
    address public owner;
    event OwnershipTransferred(address indexed oldOwner_, address indexed newOwner_);
    constructor() { owner = msg.sender; }
    modifier onlyOwner {
        require(owner == msg.sender, "Ownable: caller is not the owner");
        _;
    }
    function _transferOwnership(address newOwner_) internal virtual {
        address _oldOwner = owner;
        owner = newOwner_;
        emit OwnershipTransferred(_oldOwner, newOwner_);    
    }
    function transferOwnership(address newOwner_) public virtual onlyOwner {
        require(newOwner_ != address(0x0), "Ownable: new owner is the zero address!");
        _transferOwnership(newOwner_);
    }
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0x0));
    }
}

interface iCT {

    struct ownerAndStake {
        address owner;
        uint40 timestamp;
    }
    
    function totalSupply() external view returns (uint256);
    function _ownerOf(uint256 tokenId_) external view returns (ownerAndStake memory);
    function ownerOf(uint256 tokenId_) external view returns (address);
    function isStaked(uint256 tokenId_) external view returns (bool);
    function tokenIdStartsAt() external view returns (uint256);

    function validateOwnershipOfTokens(address owner_, uint256[] calldata tokenIds_) 
    external view returns (bool);
    function validateOwnershipOfStakedTokens(address owner_,
    uint256[] calldata tokenIds_) external view returns (bool);

    function stakeTurtles(uint256[] calldata tokenIds_) external;
    function updateTurtles(uint256[] calldata tokenIds_) external;
    function unstakeTurtles(uint256[] calldata tokenIds_) external;

    function tokenURI(uint256 tokenId_) external view returns (string memory);
}

interface iShell {
    function mint(address to_, uint256 amount_) external;
}

// This is a proof-of-stake (token represents stake) contract
// Custom made with love by 0xInuarashi.eth
contract sCyberTurtles is Ownable {
    string public name = "Staked Cyber Turtles";
    string public symbol = "sCyber";

    // We largely interface with CyberTurtles
    iCT public CT = iCT(0x81BC389D02c3054649643E590ce57fAAAB3BF38B); // note: change
    function setCT(address address_) external onlyOwner {
        CT = iCT(address_);
    }

    iShell public SHELL = iShell(0x81BC389D02c3054649643E590ce57fAAAB3BF38B); // note: c
    function setShell(address address_) external onlyOwner {
        SHELL = iShell(address_);
    }

    // Yield Info
    uint256 public yieldStartTime = 1643670000; // 2021-01-31_18-00_EST
    uint256 public yieldEndTime = 1959202800; // 10 years
    uint256 public yieldRate = 100 ether;

    // Magic Events
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    // Magic Logic
    function totalSupply() public view returns (uint256) {
        uint256 _totalSupply;
        uint256 _startId = CT.tokenIdStartsAt();
        for (uint256 i = _startId; i <= CT.totalSupply() + _startId; i++) {
            if (CT.isStaked(i)) { _totalSupply++; }
        }
        return _totalSupply;
    }

    function ownerOf(uint256 tokenId_) public view returns (address) {
        iCT.ownerAndStake memory _ownerAndStake = CT._ownerOf(tokenId_);
        address _owner = _ownerAndStake.timestamp > 0 ?
             _ownerAndStake.owner : address(0);
        return _owner;
    }

    function balanceOf(address address_) public view returns (uint256) {
        uint256 _startId = CT.tokenIdStartsAt();
        uint256 _balance;
        for (uint256 i = _startId; i <= CT.totalSupply() + _startId; i++) {
            if (ownerOf(i) == address_) { _balance++; }
        }
        return _balance;
    }

    // Internal Claim Function
    function _getPendingTokens(uint256 tokenId_) internal view returns (uint256) {
        uint256 _timestamp = uint256(CT._ownerOf(tokenId_).timestamp);
        if (_timestamp == 0 || _timestamp > yieldEndTime) return 0;

        uint256 _timeCurrentOrEnded = yieldEndTime > block.timestamp ? 
            block.timestamp : yieldEndTime;
        uint256 _timeElapsed = _timeCurrentOrEnded - _timestamp;

        return (_timeElapsed * yieldRate) / 1 days;
    }
    function _getPendingTokensMany(uint256[] memory tokenIds_) internal view
    returns (uint256) {
        uint256 _pendingTokens;
        for (uint256 i = 0; i < tokenIds_.length; i++) {
            _pendingTokens += _getPendingTokens(tokenIds_[i]);
        }
        return _pendingTokens;
    }

    function getPendingTokens(uint256 tokenId_) public view returns (uint256) {
        return _getPendingTokens(tokenId_);
    }
    function getPendingTokensMany(uint256[] calldata tokenIds_) public view 
    returns (uint256) {
        return _getPendingTokensMany(tokenIds_);
    }
    function getPendingTokensOfAddress(address address_) public view 
    returns (uint256) {
        uint256[] memory _tokensOfAddress = walletOfOwner(address_);
        return _getPendingTokensMany(_tokensOfAddress);
    }

    function _claim(address to_, uint256[] memory tokenIds_) internal {
        uint256 _pendingTokens;
        for (uint256 i = 0; i < tokenIds_.length; i++) {
            _pendingTokens += _getPendingTokens(tokenIds_[i]);
        }
        SHELL.mint(to_, _pendingTokens);
    }

    function claim(uint256[] calldata tokenIds_) external {
        require(CT.validateOwnershipOfStakedTokens(msg.sender, tokenIds_),
            "You are not the owner or token is unstaked!");

        _claim(msg.sender, tokenIds_);
        CT.updateTurtles(tokenIds_); // This updates the timestamp
    }
    function stakeTurtles(uint256[] calldata tokenIds_) external {
        require(CT.validateOwnershipOfTokens(msg.sender, tokenIds_),
            "You are not the owner or token is already staked!");

        CT.stakeTurtles(tokenIds_); // Set timestamp to block.timestamp

        for (uint256 i = 0; i < tokenIds_.length; i++) {
            emit Transfer(address(0), msg.sender, tokenIds_[i]); // Mint sToken
        }
    }   
    function unstakeTurtles(uint256[] calldata tokenIds_) external {
        require(CT.validateOwnershipOfStakedTokens(msg.sender, tokenIds_),
            "You are not the owner or token is unstaked!");

        _claim(msg.sender, tokenIds_);
        CT.unstakeTurtles(tokenIds_); // Set timestamp to 0

        for (uint256 i = 0; i < tokenIds_.length; i++) {
            emit Transfer(msg.sender, address(0), tokenIds_[i]); // Burn sToken
        }
    }

    function mintStakedTokenAsCyberTurtles(address to_, uint256 tokenId_) external {
        require(msg.sender == address(CT), "You are not CT!");
        emit Transfer(address(0), to_, tokenId_);
    }

    function walletOfOwner(address address_) public virtual view 
    returns (uint256[] memory) {
        uint256 _balance = balanceOf(address_);
        if (_balance == 0) return new uint256[](0);

        uint256[] memory _tokens = new uint256[] (_balance);
        uint256 _index;
        uint256 _loopThrough = CT.totalSupply() + 1;
        for (uint256 i = 0; i < _loopThrough; i++) {
            if (ownerOf(i) == address(0x0) && _tokens[_balance - 1] == 0) {
                _loopThrough++; 
            }
            if (ownerOf(i) == address_) { 
                _tokens[_index] = i; _index++; 
            }
        }
        return _tokens;
    }

    // TokenURI Stuff
    string internal baseTokenURI; string internal baseTokenURI_EXT;
    function _toString(uint256 value_) internal pure returns (string memory) {
        if (value_ == 0) { return "0"; }
        uint256 _iterate = value_; uint256 _digits;
        while (_iterate != 0) { _digits++; _iterate /= 10; } // get digits in value_
        bytes memory _buffer = new bytes(_digits);
        while (value_ != 0) { _digits--; _buffer[_digits] = bytes1(uint8(
            48 + uint256(value_ % 10 ))); value_ /= 10; } // create bytes of value_
        return string(_buffer); // return string converted bytes of value_
    }
    function setBaseTokenURI(string memory uri_) external onlyOwner {
        baseTokenURI = uri_;
    }
    function setBaseTokenURI_EXT(string memory ext_) external onlyOwner {
        baseTokenURI_EXT = ext_;
    }
    function tokenURI(uint256 tokenId_) public view virtual returns (string memory) {
        require(ownerOf(tokenId_) != address(0), 
            "ERC721I: tokenURI() Token does not exist!");

        return string(abi.encodePacked(baseTokenURI, 
            _toString(tokenId_), baseTokenURI_EXT));
    }

    // OZ ERC721 Stuff
    function supportsInterface(bytes4 interfaceId_) public pure returns (bool) {
        return (interfaceId_ == 0x80ac58cd || interfaceId_ == 0x5b5e139f);
    }
}
设置
{
  "compilationTarget": {
    "sCyberTurtles.sol": "sCyberTurtles"
  },
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner_","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner_","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CT","outputs":[{"internalType":"contract iCT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHELL","outputs":[{"internalType":"contract iShell","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","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":"uint256","name":"tokenId_","type":"uint256"}],"name":"getPendingTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"}],"name":"getPendingTokensMany","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"getPendingTokensOfAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"mintStakedTokenAsCyberTurtles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri_","type":"string"}],"name":"setBaseTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ext_","type":"string"}],"name":"setBaseTokenURI_EXT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setCT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setShell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"}],"name":"stakeTurtles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId_","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","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":"unstakeTurtles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"walletOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]