ETH Price: $2,935.59 (+0.08%)

Transaction Decoder

Block:
9131683 at Dec-19-2019 05:38:57 PM +UTC
Transaction Fee:
0.00029488 ETH $0.87
Gas Used:
147,440 Gas / 2 Gwei

Emitted Events:

48 Clovers.Transfer( _from=[Sender] 0x8ea243d05c3681bcfc4d2ef0f23ea60977b8cea2, _to=[Receiver] SecretSanta, _tokenId=198498371890339035486332032233347044694 )
49 SecretSanta.PrizeAdded( from=[Sender] 0x8ea243d05c3681bcfc4d2ef0f23ea60977b8cea2, tokens=[0xB55C5cAc5014C662fDBF21A2C59Cd45403C482Fd], tokensId=[198498371890339035486332032233347044694] )

Account State Difference:

  Address   Before After State Difference Code
(Spark Pool)
56.755738219492862346 Eth56.756033099492862346 Eth0.00029488
0x8496B5CE...0b4b94bFA
(Secreth Santa)
0x8eA243D0...977b8cEa2
0.13513285249257477 Eth
Nonce: 39
0.13483797249257477 Eth
Nonce: 40
0.00029488
0xB55C5cAc...403C482Fd

Execution Trace

SecretSanta.sendPrize( tokens=[0xB55C5cAc5014C662fDBF21A2C59Cd45403C482Fd], tokensId=[198498371890339035486332032233347044694] )
  • Clovers.transferFrom( _from=0x8eA243D05c3681bcFc4D2eF0F23eA60977b8cEa2, _to=0x8496B5CE2851B1DA7833e61B84043890b4b94bFA, _tokenId=198498371890339035486332032233347044694 )
    File 1 of 2: SecretSanta
    // File: @openzeppelin/contracts/GSN/Context.sol
    
    pragma solidity ^0.5.0;
    
    /*
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with GSN meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    contract Context {
        // Empty internal constructor, to prevent people from mistakenly deploying
        // an instance of this contract, which should be used via inheritance.
        constructor () internal { }
        // solhint-disable-previous-line no-empty-blocks
    
        function _msgSender() internal view returns (address payable) {
            return msg.sender;
        }
    
        function _msgData() internal view returns (bytes memory) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    
    // File: @openzeppelin/contracts/ownership/Ownable.sol
    
    pragma solidity ^0.5.0;
    
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor () internal {
            _owner = _msgSender();
            emit OwnershipTransferred(address(0), _owner);
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(isOwner(), "Ownable: caller is not the owner");
            _;
        }
    
        /**
         * @dev Returns true if the caller is the current owner.
         */
        function isOwner() public view returns (bool) {
            return _msgSender() == _owner;
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public onlyOwner {
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         */
        function _transferOwnership(address newOwner) internal {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    // File: contracts/SecretSanta.sol
    
    pragma solidity 0.5.13;
    
    
    
    contract ERC721 {
        function transferFrom(address from, address to, uint256 tokenId) public;
    }
    
    
    /**
     * @title Secret Santa with NFTs (www.secrethsanta.co)
     * @notice All the logic of the contract happens here
     * @author Clemlak
     */
    contract SecretSanta is Ownable {
        address public lastSecretSanta;
        bool public isPrizeClaimed;
    
        uint256 public lastPresentAt;
        uint256 public prizeDelay;
    
        address[] public prizeTokens;
        uint256[] public prizeTokensId;
    
        mapping (address => bool) public whitelist;
    
        event PresentSent(
            address indexed from,
            address indexed to,
            address token,
            uint256 tokenId
        );
    
        event PrizeAdded(
            address indexed from,
            address[] tokens,
            uint256[] tokensId
        );
    
        constructor(
            uint256 initialPrizeDelay
        ) public {
            lastSecretSanta = msg.sender;
            lastPresentAt = now;
            prizeDelay = initialPrizeDelay;
        }
    
        /**
         * @notice Send tokens to the prize
         * @param tokens An array with the address of the contracts
         * @param tokensId An array with the id of the tokens
         */
        function sendPrize(
            address[] calldata tokens,
            uint256[] calldata tokensId
        ) external {
            require(
                tokens.length == tokensId.length,
                "Invalid array"
            );
    
            require(
                lastPresentAt + prizeDelay > now,
                "Too late"
            );
    
            for (uint256 i = 0; i < tokens.length; i += 1) {
                require(
                    whitelist[tokens[i]],
                    "Token not whitelisted"
                );
    
                ERC721 token = ERC721(tokens[i]);
                token.transferFrom(
                    msg.sender,
                    address(this),
                    tokensId[i]
                );
    
                prizeTokens.push(tokens[i]);
                prizeTokensId.push(tokensId[i]);
            }
    
            emit PrizeAdded(
                msg.sender,
                tokens,
                tokensId
            );
        }
    
        /**
         * @notice Sends a present
         * @param tokenAddress The address of the contract
         * @param tokenId The id of the token
         */
        function sendPresent(
            address tokenAddress,
            uint256 tokenId
        ) external {
            require(
                lastPresentAt + prizeDelay > now,
                "Too late"
            );
    
            require(
                whitelist[tokenAddress],
                "Token not whitelisted"
            );
    
            ERC721 token = ERC721(tokenAddress);
    
            token.transferFrom(
                msg.sender,
                lastSecretSanta,
                tokenId
            );
    
            emit PresentSent(
                msg.sender,
                lastSecretSanta,
                tokenAddress,
                tokenId
            );
    
            lastSecretSanta = msg.sender;
            lastPresentAt = now;
        }
    
        /**
         * @notice Claims the prize
         */
        function claimPrize() external {
            require(
                now > lastPresentAt + prizeDelay,
                "Not yet"
            );
    
            require(
                msg.sender == lastSecretSanta,
                "Sender not last Santa"
            );
    
            for (uint256 i = 0; i < prizeTokens.length; i += 1) {
                ERC721 token = ERC721(prizeTokens[i]);
    
                token.transferFrom(
                    address(this),
                    msg.sender,
                    prizeTokensId[i]
                );
            }
    
            isPrizeClaimed = true;
        }
    
        function updateWhitelist(
            address[] calldata tokens,
            bool isApproved
        ) external onlyOwner() {
            for (uint256 i = 0; i < tokens.length; i += 1) {
                whitelist[tokens[i]] = isApproved;
            }
        }
    
        function getPrize() external view returns (
            address[] memory tokens,
            uint256[] memory tokensId
        ) {
            return (
                prizeTokens,
                prizeTokensId
            );
        }
    
        function isTooLate() external view returns (bool) {
            return now > lastPresentAt + prizeDelay;
        }
    }

    File 2 of 2: Clovers
    // File: zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
    
    pragma solidity ^0.4.24;
    
    
    /**
     * @title ERC20Basic
     * @dev Simpler version of ERC20 interface
     * See https://github.com/ethereum/EIPs/issues/179
     */
    contract ERC20Basic {
      function totalSupply() public view returns (uint256);
      function balanceOf(address who) public view returns (uint256);
      function transfer(address to, uint256 value) public returns (bool);
      event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    // File: zeppelin-solidity/contracts/token/ERC20/ERC20.sol
    
    pragma solidity ^0.4.24;
    
    
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender)
        public view returns (uint256);
    
      function transferFrom(address from, address to, uint256 value)
        public returns (bool);
    
      function approve(address spender, uint256 value) public returns (bool);
      event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
      );
    }
    
    // File: zeppelin-solidity/contracts/introspection/ERC165.sol
    
    pragma solidity ^0.4.24;
    
    
    /**
     * @title ERC165
     * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
     */
    interface ERC165 {
    
      /**
       * @notice Query if a contract implements an interface
       * @param _interfaceId The interface identifier, as specified in ERC-165
       * @dev Interface identification is specified in ERC-165. This function
       * uses less than 30,000 gas.
       */
      function supportsInterface(bytes4 _interfaceId)
        external
        view
        returns (bool);
    }
    
    // File: zeppelin-solidity/contracts/token/ERC721/ERC721Basic.sol
    
    pragma solidity ^0.4.24;
    
    
    
    /**
     * @title ERC721 Non-Fungible Token Standard basic interface
     * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
     */
    contract ERC721Basic is ERC165 {
      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) public view returns (uint256 _balance);
      function ownerOf(uint256 _tokenId) public view returns (address _owner);
      function exists(uint256 _tokenId) public view returns (bool _exists);
    
      function approve(address _to, uint256 _tokenId) public;
      function getApproved(uint256 _tokenId)
        public view returns (address _operator);
    
      function setApprovalForAll(address _operator, bool _approved) public;
      function isApprovedForAll(address _owner, address _operator)
        public view returns (bool);
    
      function transferFrom(address _from, address _to, uint256 _tokenId) public;
      function safeTransferFrom(address _from, address _to, uint256 _tokenId)
        public;
    
      function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes _data
      )
        public;
    }
    
    // File: zeppelin-solidity/contracts/token/ERC721/ERC721.sol
    
    pragma solidity ^0.4.24;
    
    
    
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
     * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
     */
    contract ERC721Enumerable is ERC721Basic {
      function totalSupply() public view returns (uint256);
      function tokenOfOwnerByIndex(
        address _owner,
        uint256 _index
      )
        public
        view
        returns (uint256 _tokenId);
    
      function tokenByIndex(uint256 _index) public view returns (uint256);
    }
    
    
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
     */
    contract ERC721Metadata is ERC721Basic {
      function name() external view returns (string _name);
      function symbol() external view returns (string _symbol);
      function tokenURI(uint256 _tokenId) public view returns (string);
    }
    
    
    /**
     * @title ERC-721 Non-Fungible Token Standard, full implementation interface
     * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
     */
    contract ERC721 is ERC721Basic, ERC721Enumerable, ERC721Metadata {
    }
    
    // File: zeppelin-solidity/contracts/token/ERC721/ERC721Receiver.sol
    
    pragma solidity ^0.4.24;
    
    
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers
     * from ERC721 asset contracts.
     */
    contract ERC721Receiver {
      /**
       * @dev Magic value to be returned upon successful reception of an NFT
       *  Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
       *  which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
       */
      bytes4 internal constant ERC721_RECEIVED = 0x150b7a02;
    
      /**
       * @notice Handle the receipt of an NFT
       * @dev The ERC721 smart contract calls this function on the recipient
       * after a `safetransfer`. This function MAY throw to revert and reject the
       * transfer. Return of other than the magic value MUST result in the 
       * transaction being reverted.
       * Note: the contract address is always the message sender.
       * @param _operator The address which called `safeTransferFrom` function
       * @param _from The address which previously owned the token
       * @param _tokenId The NFT identifier which is being transfered
       * @param _data Additional data with no specified format
       * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
       */
      function onERC721Received(
        address _operator,
        address _from,
        uint256 _tokenId,
        bytes _data
      )
        public
        returns(bytes4);
    }
    
    // File: zeppelin-solidity/contracts/math/SafeMath.sol
    
    pragma solidity ^0.4.24;
    
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
    
      /**
      * @dev Multiplies two numbers, throws on overflow.
      */
      function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
          return 0;
        }
    
        c = a * b;
        assert(c / a == b);
        return c;
      }
    
      /**
      * @dev Integer division of two numbers, truncating the quotient.
      */
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        // uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return a / b;
      }
    
      /**
      * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
      */
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      /**
      * @dev Adds two numbers, throws on overflow.
      */
      function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
        c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    // File: zeppelin-solidity/contracts/AddressUtils.sol
    
    pragma solidity ^0.4.24;
    
    
    /**
     * Utility library of inline functions on addresses
     */
    library AddressUtils {
    
      /**
       * Returns whether the target address is a contract
       * @dev This function will return false if invoked during the constructor of a contract,
       * as the code is not actually created until after the constructor finishes.
       * @param addr address to check
       * @return whether the target address is a contract
       */
      function isContract(address addr) internal view returns (bool) {
        uint256 size;
        // XXX Currently there is no better way to check if there is a contract in an address
        // than to check the size of the code at that address.
        // See https://ethereum.stackexchange.com/a/14016/36603
        // for more details about how this works.
        // TODO Check this again before the Serenity release, because all addresses will be
        // contracts then.
        // solium-disable-next-line security/no-inline-assembly
        assembly { size := extcodesize(addr) }
        return size > 0;
      }
    
    }
    
    // File: zeppelin-solidity/contracts/introspection/SupportsInterfaceWithLookup.sol
    
    pragma solidity ^0.4.24;
    
    
    
    /**
     * @title SupportsInterfaceWithLookup
     * @author Matt Condon (@shrugs)
     * @dev Implements ERC165 using a lookup table.
     */
    contract SupportsInterfaceWithLookup is ERC165 {
      bytes4 public constant InterfaceId_ERC165 = 0x01ffc9a7;
      /**
       * 0x01ffc9a7 ===
       *   bytes4(keccak256('supportsInterface(bytes4)'))
       */
    
      /**
       * @dev a mapping of interface id to whether or not it's supported
       */
      mapping(bytes4 => bool) internal supportedInterfaces;
    
      /**
       * @dev A contract implementing SupportsInterfaceWithLookup
       * implement ERC165 itself
       */
      constructor()
        public
      {
        _registerInterface(InterfaceId_ERC165);
      }
    
      /**
       * @dev implement supportsInterface(bytes4) using a lookup table
       */
      function supportsInterface(bytes4 _interfaceId)
        external
        view
        returns (bool)
      {
        return supportedInterfaces[_interfaceId];
      }
    
      /**
       * @dev private method for registering an interface
       */
      function _registerInterface(bytes4 _interfaceId)
        internal
      {
        require(_interfaceId != 0xffffffff);
        supportedInterfaces[_interfaceId] = true;
      }
    }
    
    // File: zeppelin-solidity/contracts/token/ERC721/ERC721BasicToken.sol
    
    pragma solidity ^0.4.24;
    
    
    
    
    
    
    
    /**
     * @title ERC721 Non-Fungible Token Standard basic implementation
     * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
     */
    contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic {
    
      bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd;
      /*
       * 0x80ac58cd ===
       *   bytes4(keccak256('balanceOf(address)')) ^
       *   bytes4(keccak256('ownerOf(uint256)')) ^
       *   bytes4(keccak256('approve(address,uint256)')) ^
       *   bytes4(keccak256('getApproved(uint256)')) ^
       *   bytes4(keccak256('setApprovalForAll(address,bool)')) ^
       *   bytes4(keccak256('isApprovedForAll(address,address)')) ^
       *   bytes4(keccak256('transferFrom(address,address,uint256)')) ^
       *   bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
       *   bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
       */
    
      bytes4 private constant InterfaceId_ERC721Exists = 0x4f558e79;
      /*
       * 0x4f558e79 ===
       *   bytes4(keccak256('exists(uint256)'))
       */
    
      using SafeMath for uint256;
      using AddressUtils for address;
    
      // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
      // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
      bytes4 private constant ERC721_RECEIVED = 0x150b7a02;
    
      // Mapping from token ID to owner
      mapping (uint256 => address) internal tokenOwner;
    
      // Mapping from token ID to approved address
      mapping (uint256 => address) internal tokenApprovals;
    
      // Mapping from owner to number of owned token
      mapping (address => uint256) internal ownedTokensCount;
    
      // Mapping from owner to operator approvals
      mapping (address => mapping (address => bool)) internal operatorApprovals;
    
      /**
       * @dev Guarantees msg.sender is owner of the given token
       * @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender
       */
      modifier onlyOwnerOf(uint256 _tokenId) {
        require(ownerOf(_tokenId) == msg.sender);
        _;
      }
    
      /**
       * @dev Checks msg.sender can transfer a token, by being owner, approved, or operator
       * @param _tokenId uint256 ID of the token to validate
       */
      modifier canTransfer(uint256 _tokenId) {
        require(isApprovedOrOwner(msg.sender, _tokenId));
        _;
      }
    
      constructor()
        public
      {
        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(InterfaceId_ERC721);
        _registerInterface(InterfaceId_ERC721Exists);
      }
    
      /**
       * @dev Gets the balance of the specified address
       * @param _owner address to query the balance of
       * @return uint256 representing the amount owned by the passed address
       */
      function balanceOf(address _owner) public view returns (uint256) {
        require(_owner != address(0));
        return ownedTokensCount[_owner];
      }
    
      /**
       * @dev Gets the owner of the specified token ID
       * @param _tokenId uint256 ID of the token to query the owner of
       * @return owner address currently marked as the owner of the given token ID
       */
      function ownerOf(uint256 _tokenId) public view returns (address) {
        address owner = tokenOwner[_tokenId];
        require(owner != address(0));
        return owner;
      }
    
      /**
       * @dev Returns whether the specified token exists
       * @param _tokenId uint256 ID of the token to query the existence of
       * @return whether the token exists
       */
      function exists(uint256 _tokenId) public view returns (bool) {
        address owner = tokenOwner[_tokenId];
        return owner != address(0);
      }
    
      /**
       * @dev Approves another address to transfer the given token ID
       * The zero address indicates there is no approved address.
       * There can only be one approved address per token at a given time.
       * Can only be called by the token owner or an approved operator.
       * @param _to address to be approved for the given token ID
       * @param _tokenId uint256 ID of the token to be approved
       */
      function approve(address _to, uint256 _tokenId) public {
        address owner = ownerOf(_tokenId);
        require(_to != owner);
        require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
    
        tokenApprovals[_tokenId] = _to;
        emit Approval(owner, _to, _tokenId);
      }
    
      /**
       * @dev Gets the approved address for a token ID, or zero if no address set
       * @param _tokenId uint256 ID of the token to query the approval of
       * @return address currently approved for the given token ID
       */
      function getApproved(uint256 _tokenId) public view returns (address) {
        return tokenApprovals[_tokenId];
      }
    
      /**
       * @dev Sets or unsets the approval of a given operator
       * An operator is allowed to transfer all tokens of the sender on their behalf
       * @param _to operator address to set the approval
       * @param _approved representing the status of the approval to be set
       */
      function setApprovalForAll(address _to, bool _approved) public {
        require(_to != msg.sender);
        operatorApprovals[msg.sender][_to] = _approved;
        emit ApprovalForAll(msg.sender, _to, _approved);
      }
    
      /**
       * @dev Tells whether an operator is approved by a given owner
       * @param _owner owner address which you want to query the approval of
       * @param _operator operator address which you want to query the approval of
       * @return bool whether the given operator is approved by the given owner
       */
      function isApprovedForAll(
        address _owner,
        address _operator
      )
        public
        view
        returns (bool)
      {
        return operatorApprovals[_owner][_operator];
      }
    
      /**
       * @dev Transfers the ownership of a given token ID to another address
       * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
       * Requires the msg sender to be the owner, approved, or operator
       * @param _from current owner of the token
       * @param _to address to receive the ownership of the given token ID
       * @param _tokenId uint256 ID of the token to be transferred
      */
      function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
      )
        public
        canTransfer(_tokenId)
      {
        require(_from != address(0));
        require(_to != address(0));
    
        clearApproval(_from, _tokenId);
        removeTokenFrom(_from, _tokenId);
        addTokenTo(_to, _tokenId);
    
        emit Transfer(_from, _to, _tokenId);
      }
    
      /**
       * @dev Safely transfers the ownership of a given token ID to another address
       * If the target address is a contract, it must implement `onERC721Received`,
       * which is called upon a safe transfer, and return the magic value
       * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
       * the transfer is reverted.
       *
       * Requires the msg sender to be the owner, approved, or operator
       * @param _from current owner of the token
       * @param _to address to receive the ownership of the given token ID
       * @param _tokenId uint256 ID of the token to be transferred
      */
      function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId
      )
        public
        canTransfer(_tokenId)
      {
        // solium-disable-next-line arg-overflow
        safeTransferFrom(_from, _to, _tokenId, "");
      }
    
      /**
       * @dev Safely transfers the ownership of a given token ID to another address
       * If the target address is a contract, it must implement `onERC721Received`,
       * which is called upon a safe transfer, and return the magic value
       * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
       * the transfer is reverted.
       * Requires the msg sender to be the owner, approved, or operator
       * @param _from current owner of the token
       * @param _to address to receive the ownership of the given token ID
       * @param _tokenId uint256 ID of the token to be transferred
       * @param _data bytes data to send along with a safe transfer check
       */
      function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes _data
      )
        public
        canTransfer(_tokenId)
      {
        transferFrom(_from, _to, _tokenId);
        // solium-disable-next-line arg-overflow
        require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
      }
    
      /**
       * @dev Returns whether the given spender can transfer a given token ID
       * @param _spender address of the spender to query
       * @param _tokenId uint256 ID of the token to be transferred
       * @return bool whether the msg.sender is approved for the given token ID,
       *  is an operator of the owner, or is the owner of the token
       */
      function isApprovedOrOwner(
        address _spender,
        uint256 _tokenId
      )
        internal
        view
        returns (bool)
      {
        address owner = ownerOf(_tokenId);
        // Disable solium check because of
        // https://github.com/duaraghav8/Solium/issues/175
        // solium-disable-next-line operator-whitespace
        return (
          _spender == owner ||
          getApproved(_tokenId) == _spender ||
          isApprovedForAll(owner, _spender)
        );
      }
    
      /**
       * @dev Internal function to mint a new token
       * Reverts if the given token ID already exists
       * @param _to The address that will own the minted token
       * @param _tokenId uint256 ID of the token to be minted by the msg.sender
       */
      function _mint(address _to, uint256 _tokenId) internal {
        require(_to != address(0));
        addTokenTo(_to, _tokenId);
        emit Transfer(address(0), _to, _tokenId);
      }
    
      /**
       * @dev Internal function to burn a specific token
       * Reverts if the token does not exist
       * @param _tokenId uint256 ID of the token being burned by the msg.sender
       */
      function _burn(address _owner, uint256 _tokenId) internal {
        clearApproval(_owner, _tokenId);
        removeTokenFrom(_owner, _tokenId);
        emit Transfer(_owner, address(0), _tokenId);
      }
    
      /**
       * @dev Internal function to clear current approval of a given token ID
       * Reverts if the given address is not indeed the owner of the token
       * @param _owner owner of the token
       * @param _tokenId uint256 ID of the token to be transferred
       */
      function clearApproval(address _owner, uint256 _tokenId) internal {
        require(ownerOf(_tokenId) == _owner);
        if (tokenApprovals[_tokenId] != address(0)) {
          tokenApprovals[_tokenId] = address(0);
        }
      }
    
      /**
       * @dev Internal function to add a token ID to the list of a given address
       * @param _to address representing the new owner of the given token ID
       * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
       */
      function addTokenTo(address _to, uint256 _tokenId) internal {
        require(tokenOwner[_tokenId] == address(0));
        tokenOwner[_tokenId] = _to;
        ownedTokensCount[_to] = ownedTokensCount[_to].add(1);
      }
    
      /**
       * @dev Internal function to remove a token ID from the list of a given address
       * @param _from address representing the previous owner of the given token ID
       * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
       */
      function removeTokenFrom(address _from, uint256 _tokenId) internal {
        require(ownerOf(_tokenId) == _from);
        ownedTokensCount[_from] = ownedTokensCount[_from].sub(1);
        tokenOwner[_tokenId] = address(0);
      }
    
      /**
       * @dev Internal function to invoke `onERC721Received` on a target address
       * The call is not executed if the target address is not a contract
       * @param _from address representing the previous owner of the given token ID
       * @param _to target address that will receive the tokens
       * @param _tokenId uint256 ID of the token to be transferred
       * @param _data bytes optional data to send along with the call
       * @return whether the call correctly returned the expected magic value
       */
      function checkAndCallSafeTransfer(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes _data
      )
        internal
        returns (bool)
      {
        if (!_to.isContract()) {
          return true;
        }
        bytes4 retval = ERC721Receiver(_to).onERC721Received(
          msg.sender, _from, _tokenId, _data);
        return (retval == ERC721_RECEIVED);
      }
    }
    
    // File: zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol
    
    pragma solidity ^0.4.24;
    
    
    
    
    
    /**
     * @title Full ERC721 Token
     * This implementation includes all the required and some optional functionality of the ERC721 standard
     * Moreover, it includes approve all functionality using operator terminology
     * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
     */
    contract ERC721Token is SupportsInterfaceWithLookup, ERC721BasicToken, ERC721 {
    
      bytes4 private constant InterfaceId_ERC721Enumerable = 0x780e9d63;
      /**
       * 0x780e9d63 ===
       *   bytes4(keccak256('totalSupply()')) ^
       *   bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
       *   bytes4(keccak256('tokenByIndex(uint256)'))
       */
    
      bytes4 private constant InterfaceId_ERC721Metadata = 0x5b5e139f;
      /**
       * 0x5b5e139f ===
       *   bytes4(keccak256('name()')) ^
       *   bytes4(keccak256('symbol()')) ^
       *   bytes4(keccak256('tokenURI(uint256)'))
       */
    
      // Token name
      string internal name_;
    
      // Token symbol
      string internal symbol_;
    
      // Mapping from owner to list of owned token IDs
      mapping(address => uint256[]) internal ownedTokens;
    
      // Mapping from token ID to index of the owner tokens list
      mapping(uint256 => uint256) internal ownedTokensIndex;
    
      // Array with all token ids, used for enumeration
      uint256[] internal allTokens;
    
      // Mapping from token id to position in the allTokens array
      mapping(uint256 => uint256) internal allTokensIndex;
    
      // Optional mapping for token URIs
      mapping(uint256 => string) internal tokenURIs;
    
      /**
       * @dev Constructor function
       */
      constructor(string _name, string _symbol) public {
        name_ = _name;
        symbol_ = _symbol;
    
        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(InterfaceId_ERC721Enumerable);
        _registerInterface(InterfaceId_ERC721Metadata);
      }
    
      /**
       * @dev Gets the token name
       * @return string representing the token name
       */
      function name() external view returns (string) {
        return name_;
      }
    
      /**
       * @dev Gets the token symbol
       * @return string representing the token symbol
       */
      function symbol() external view returns (string) {
        return symbol_;
      }
    
      /**
       * @dev Returns an URI for a given token ID
       * Throws if the token ID does not exist. May return an empty string.
       * @param _tokenId uint256 ID of the token to query
       */
      function tokenURI(uint256 _tokenId) public view returns (string) {
        require(exists(_tokenId));
        return tokenURIs[_tokenId];
      }
    
      /**
       * @dev Gets the token ID at a given index of the tokens list of the requested owner
       * @param _owner address owning the tokens list to be accessed
       * @param _index uint256 representing the index to be accessed of the requested tokens list
       * @return uint256 token ID at the given index of the tokens list owned by the requested address
       */
      function tokenOfOwnerByIndex(
        address _owner,
        uint256 _index
      )
        public
        view
        returns (uint256)
      {
        require(_index < balanceOf(_owner));
        return ownedTokens[_owner][_index];
      }
    
      /**
       * @dev Gets the total amount of tokens stored by the contract
       * @return uint256 representing the total amount of tokens
       */
      function totalSupply() public view returns (uint256) {
        return allTokens.length;
      }
    
      /**
       * @dev Gets the token ID at a given index of all the tokens in this contract
       * Reverts if the index is greater or equal to the total number of tokens
       * @param _index uint256 representing the index to be accessed of the tokens list
       * @return uint256 token ID at the given index of the tokens list
       */
      function tokenByIndex(uint256 _index) public view returns (uint256) {
        require(_index < totalSupply());
        return allTokens[_index];
      }
    
      /**
       * @dev Internal function to set the token URI for a given token
       * Reverts if the token ID does not exist
       * @param _tokenId uint256 ID of the token to set its URI
       * @param _uri string URI to assign
       */
      function _setTokenURI(uint256 _tokenId, string _uri) internal {
        require(exists(_tokenId));
        tokenURIs[_tokenId] = _uri;
      }
    
      /**
       * @dev Internal function to add a token ID to the list of a given address
       * @param _to address representing the new owner of the given token ID
       * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
       */
      function addTokenTo(address _to, uint256 _tokenId) internal {
        super.addTokenTo(_to, _tokenId);
        uint256 length = ownedTokens[_to].length;
        ownedTokens[_to].push(_tokenId);
        ownedTokensIndex[_tokenId] = length;
      }
    
      /**
       * @dev Internal function to remove a token ID from the list of a given address
       * @param _from address representing the previous owner of the given token ID
       * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
       */
      function removeTokenFrom(address _from, uint256 _tokenId) internal {
        super.removeTokenFrom(_from, _tokenId);
    
        uint256 tokenIndex = ownedTokensIndex[_tokenId];
        uint256 lastTokenIndex = ownedTokens[_from].length.sub(1);
        uint256 lastToken = ownedTokens[_from][lastTokenIndex];
    
        ownedTokens[_from][tokenIndex] = lastToken;
        ownedTokens[_from][lastTokenIndex] = 0;
        // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
        // be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping
        // the lastToken to the first position, and then dropping the element placed in the last position of the list
    
        ownedTokens[_from].length--;
        ownedTokensIndex[_tokenId] = 0;
        ownedTokensIndex[lastToken] = tokenIndex;
      }
    
      /**
       * @dev Internal function to mint a new token
       * Reverts if the given token ID already exists
       * @param _to address the beneficiary that will own the minted token
       * @param _tokenId uint256 ID of the token to be minted by the msg.sender
       */
      function _mint(address _to, uint256 _tokenId) internal {
        super._mint(_to, _tokenId);
    
        allTokensIndex[_tokenId] = allTokens.length;
        allTokens.push(_tokenId);
      }
    
      /**
       * @dev Internal function to burn a specific token
       * Reverts if the token does not exist
       * @param _owner owner of the token to burn
       * @param _tokenId uint256 ID of the token being burned by the msg.sender
       */
      function _burn(address _owner, uint256 _tokenId) internal {
        super._burn(_owner, _tokenId);
    
        // Clear metadata (if any)
        if (bytes(tokenURIs[_tokenId]).length != 0) {
          delete tokenURIs[_tokenId];
        }
    
        // Reorg all tokens array
        uint256 tokenIndex = allTokensIndex[_tokenId];
        uint256 lastTokenIndex = allTokens.length.sub(1);
        uint256 lastToken = allTokens[lastTokenIndex];
    
        allTokens[tokenIndex] = lastToken;
        allTokens[lastTokenIndex] = 0;
    
        allTokens.length--;
        allTokensIndex[_tokenId] = 0;
        allTokensIndex[lastToken] = tokenIndex;
      }
    
    }
    
    // File: zeppelin-solidity/contracts/ownership/Ownable.sol
    
    pragma solidity ^0.4.24;
    
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
      address public owner;
    
    
      event OwnershipRenounced(address indexed previousOwner);
      event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
      );
    
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      constructor() public {
        owner = msg.sender;
      }
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(msg.sender == owner);
        _;
      }
    
      /**
       * @dev Allows the current owner to relinquish control of the contract.
       * @notice Renouncing to ownership will leave the contract without an owner.
       * It will not be possible to call the functions with the `onlyOwner`
       * modifier anymore.
       */
      function renounceOwnership() public onlyOwner {
        emit OwnershipRenounced(owner);
        owner = address(0);
      }
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param _newOwner The address to transfer ownership to.
       */
      function transferOwnership(address _newOwner) public onlyOwner {
        _transferOwnership(_newOwner);
      }
    
      /**
       * @dev Transfers control of the contract to a newOwner.
       * @param _newOwner The address to transfer ownership to.
       */
      function _transferOwnership(address _newOwner) internal {
        require(_newOwner != address(0));
        emit OwnershipTransferred(owner, _newOwner);
        owner = _newOwner;
      }
    }
    
    // File: contracts/helpers/Admin.sol
    
    pragma solidity ^0.4.24;
    
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an admin address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Admin {
      mapping (address => bool) public admins;
    
    
      event AdminshipRenounced(address indexed previousAdmin);
      event AdminshipTransferred(
        address indexed previousAdmin,
        address indexed newAdmin
      );
    
    
      /**
       * @dev The Ownable constructor sets the original `admin` of the contract to the sender
       * account.
       */
      constructor() public {
        admins[msg.sender] = true;
      }
    
      /**
       * @dev Throws if called by any account other than the admin.
       */
      modifier onlyAdmin() {
        require(admins[msg.sender]);
        _;
      }
    
      function isAdmin(address _admin) public view returns(bool) {
        return admins[_admin];
      }
    
      /**
       * @dev Allows the current admin to relinquish control of the contract.
       * @notice Renouncing to adminship will leave the contract without an admin.
       * It will not be possible to call the functions with the `onlyAdmin`
       * modifier anymore.
       */
      function renounceAdminship(address _previousAdmin) public onlyAdmin {
        emit AdminshipRenounced(_previousAdmin);
        admins[_previousAdmin] = false;
      }
    
      /**
       * @dev Allows the current admin to transfer control of the contract to a newAdmin.
       * @param _newAdmin The address to transfer adminship to.
       */
      function transferAdminship(address _newAdmin) public onlyAdmin {
        _transferAdminship(_newAdmin);
      }
    
      /**
       * @dev Transfers control of the contract to a newAdmin.
       * @param _newAdmin The address to transfer adminship to.
       */
      function _transferAdminship(address _newAdmin) internal {
        require(_newAdmin != address(0));
        emit AdminshipTransferred(msg.sender, _newAdmin);
        admins[_newAdmin] = true;
      }
    }
    
    // File: contracts/helpers/strings.sol
    
    /*
     * @title String & slice utility library for Solidity contracts.
     * @author Nick Johnson <[email protected]>
     *
     * @dev Functionality in this library is largely implemented using an
     *      abstraction called a 'slice'. A slice represents a part of a string -
     *      anything from the entire string to a single character, or even no
     *      characters at all (a 0-length slice). Since a slice only has to specify
     *      an offset and a length, copying and manipulating slices is a lot less
     *      expensive than copying and manipulating the strings they reference.
     *
     *      To further reduce gas costs, most functions on slice that need to return
     *      a slice modify the original one instead of allocating a new one; for
     *      instance, `s.split(".")` will return the text up to the first '.',
     *      modifying s to only contain the remainder of the string after the '.'.
     *      In situations where you do not want to modify the original slice, you
     *      can make a copy first with `.copy()`, for example:
     *      `s.copy().split(".")`. Try and avoid using this idiom in loops; since
     *      Solidity has no memory management, it will result in allocating many
     *      short-lived slices that are later discarded.
     *
     *      Functions that return two slices come in two versions: a non-allocating
     *      version that takes the second slice as an argument, modifying it in
     *      place, and an allocating version that allocates and returns the second
     *      slice; see `nextRune` for example.
     *
     *      Functions that have to copy string data will return strings rather than
     *      slices; these can be cast back to slices for further processing if
     *      required.
     *
     *      For convenience, some functions are provided with non-modifying
     *      variants that create a new slice and return both; for instance,
     *      `s.splitNew('.')` leaves s unmodified, and returns two values
     *      corresponding to the left and right parts of the string.
     */
    
    pragma solidity ^0.4.14;
    
    library strings {
        struct slice {
            uint _len;
            uint _ptr;
        }
    
        function memcpy(uint dest, uint src, uint len) private pure{
            // Copy word-length chunks while possible
            for(; len >= 32; len -= 32) {
                assembly {
                    mstore(dest, mload(src))
                }
                dest += 32;
                src += 32;
            }
    
            // Copy remaining bytes
            uint mask = 256 ** (32 - len) - 1;
            assembly {
                let srcpart := and(mload(src), not(mask))
                let destpart := and(mload(dest), mask)
                mstore(dest, or(destpart, srcpart))
            }
        }
    
        /*
         * @dev Returns a slice containing the entire string.
         * @param self The string to make a slice from.
         * @return A newly allocated slice containing the entire string.
         */
        function toSlice(string self) internal pure returns (slice) {
            uint ptr;
            assembly {
                ptr := add(self, 0x20)
            }
            return slice(bytes(self).length, ptr);
        }
    
        /*
         * @dev Returns the length of a null-terminated bytes32 string.
         * @param self The value to find the length of.
         * @return The length of the string, from 0 to 32.
         */
        function len(bytes32 self) internal pure returns (uint) {
            uint ret;
            if (self == 0)
                return 0;
            if (self & 0xffffffffffffffffffffffffffffffff == 0) {
                ret += 16;
                self = bytes32(uint(self) / 0x100000000000000000000000000000000);
            }
            if (self & 0xffffffffffffffff == 0) {
                ret += 8;
                self = bytes32(uint(self) / 0x10000000000000000);
            }
            if (self & 0xffffffff == 0) {
                ret += 4;
                self = bytes32(uint(self) / 0x100000000);
            }
            if (self & 0xffff == 0) {
                ret += 2;
                self = bytes32(uint(self) / 0x10000);
            }
            if (self & 0xff == 0) {
                ret += 1;
            }
            return 32 - ret;
        }
    
        /*
         * @dev Returns a slice containing the entire bytes32, interpreted as a
         *      null-termintaed utf-8 string.
         * @param self The bytes32 value to convert to a slice.
         * @return A new slice containing the value of the input argument up to the
         *         first null.
         */
        function toSliceB32(bytes32 self) internal pure returns (slice ret) {
            // Allocate space for `self` in memory, copy it there, and point ret at it
            assembly {
                let ptr := mload(0x40)
                mstore(0x40, add(ptr, 0x20))
                mstore(ptr, self)
                mstore(add(ret, 0x20), ptr)
            }
            ret._len = len(self);
        }
    
        /*
         * @dev Returns a new slice containing the same data as the current slice.
         * @param self The slice to copy.
         * @return A new slice containing the same data as `self`.
         */
        function copy(slice self) internal pure returns (slice) {
            return slice(self._len, self._ptr);
        }
    
        /*
         * @dev Copies a slice to a new string.
         * @param self The slice to copy.
         * @return A newly allocated string containing the slice's text.
         */
        function toString(slice self) internal pure returns (string) {
            var ret = new string(self._len);
            uint retptr;
            assembly { retptr := add(ret, 32) }
    
            memcpy(retptr, self._ptr, self._len);
            return ret;
        }
    
        /*
         * @dev Returns the length in runes of the slice. Note that this operation
         *      takes time proportional to the length of the slice; avoid using it
         *      in loops, and call `slice.empty()` if you only need to know whether
         *      the slice is empty or not.
         * @param self The slice to operate on.
         * @return The length of the slice in runes.
         */
        function len(slice self) internal pure returns (uint l) {
            // Starting at ptr-31 means the LSB will be the byte we care about
            var ptr = self._ptr - 31;
            var end = ptr + self._len;
            for (l = 0; ptr < end; l++) {
                uint8 b;
                assembly { b := and(mload(ptr), 0xFF) }
                if (b < 0x80) {
                    ptr += 1;
                } else if(b < 0xE0) {
                    ptr += 2;
                } else if(b < 0xF0) {
                    ptr += 3;
                } else if(b < 0xF8) {
                    ptr += 4;
                } else if(b < 0xFC) {
                    ptr += 5;
                } else {
                    ptr += 6;
                }
            }
        }
    
        /*
         * @dev Returns true if the slice is empty (has a length of 0).
         * @param self The slice to operate on.
         * @return True if the slice is empty, False otherwise.
         */
        function empty(slice self) internal pure returns (bool) {
            return self._len == 0;
        }
    
        /*
         * @dev Returns a positive number if `other` comes lexicographically after
         *      `self`, a negative number if it comes before, or zero if the
         *      contents of the two slices are equal. Comparison is done per-rune,
         *      on unicode codepoints.
         * @param self The first slice to compare.
         * @param other The second slice to compare.
         * @return The result of the comparison.
         */
        function compare(slice self, slice other) internal pure returns (int) {
            uint shortest = self._len;
            if (other._len < self._len)
                shortest = other._len;
    
            var selfptr = self._ptr;
            var otherptr = other._ptr;
            for (uint idx = 0; idx < shortest; idx += 32) {
                uint a;
                uint b;
                assembly {
                    a := mload(selfptr)
                    b := mload(otherptr)
                }
                if (a != b) {
                    // Mask out irrelevant bytes and check again
                    uint mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
                    var diff = (a & mask) - (b & mask);
                    if (diff != 0)
                        return int(diff);
                }
                selfptr += 32;
                otherptr += 32;
            }
            return int(self._len) - int(other._len);
        }
    
        /*
         * @dev Returns true if the two slices contain the same text.
         * @param self The first slice to compare.
         * @param self The second slice to compare.
         * @return True if the slices are equal, false otherwise.
         */
        function equals(slice self, slice other) internal pure returns (bool) {
            return compare(self, other) == 0;
        }
    
        /*
         * @dev Extracts the first rune in the slice into `rune`, advancing the
         *      slice to point to the next rune and returning `self`.
         * @param self The slice to operate on.
         * @param rune The slice that will contain the first rune.
         * @return `rune`.
         */
        function nextRune(slice self, slice rune) internal pure returns (slice) {
            rune._ptr = self._ptr;
    
            if (self._len == 0) {
                rune._len = 0;
                return rune;
            }
    
            uint len;
            uint b;
            // Load the first byte of the rune into the LSBs of b
            assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) }
            if (b < 0x80) {
                len = 1;
            } else if(b < 0xE0) {
                len = 2;
            } else if(b < 0xF0) {
                len = 3;
            } else {
                len = 4;
            }
    
            // Check for truncated codepoints
            if (len > self._len) {
                rune._len = self._len;
                self._ptr += self._len;
                self._len = 0;
                return rune;
            }
    
            self._ptr += len;
            self._len -= len;
            rune._len = len;
            return rune;
        }
    
        /*
         * @dev Returns the first rune in the slice, advancing the slice to point
         *      to the next rune.
         * @param self The slice to operate on.
         * @return A slice containing only the first rune from `self`.
         */
        function nextRune(slice self) internal pure returns (slice ret) {
            nextRune(self, ret);
        }
    
        /*
         * @dev Returns the number of the first codepoint in the slice.
         * @param self The slice to operate on.
         * @return The number of the first codepoint in the slice.
         */
        function ord(slice self) internal pure returns (uint ret) {
            if (self._len == 0) {
                return 0;
            }
    
            uint word;
            uint length;
            uint divisor = 2 ** 248;
    
            // Load the rune into the MSBs of b
            assembly { word:= mload(mload(add(self, 32))) }
            var b = word / divisor;
            if (b < 0x80) {
                ret = b;
                length = 1;
            } else if(b < 0xE0) {
                ret = b & 0x1F;
                length = 2;
            } else if(b < 0xF0) {
                ret = b & 0x0F;
                length = 3;
            } else {
                ret = b & 0x07;
                length = 4;
            }
    
            // Check for truncated codepoints
            if (length > self._len) {
                return 0;
            }
    
            for (uint i = 1; i < length; i++) {
                divisor = divisor / 256;
                b = (word / divisor) & 0xFF;
                if (b & 0xC0 != 0x80) {
                    // Invalid UTF-8 sequence
                    return 0;
                }
                ret = (ret * 64) | (b & 0x3F);
            }
    
            return ret;
        }
    
        /*
         * @dev Returns the keccak-256 hash of the slice.
         * @param self The slice to hash.
         * @return The hash of the slice.
         */
        function keccak(slice self) internal pure returns (bytes32 ret) {
            assembly {
                ret := keccak256(mload(add(self, 32)), mload(self))
            }
        }
    
        /*
         * @dev Returns true if `self` starts with `needle`.
         * @param self The slice to operate on.
         * @param needle The slice to search for.
         * @return True if the slice starts with the provided text, false otherwise.
         */
        function startsWith(slice self, slice needle) internal pure returns (bool) {
            if (self._len < needle._len) {
                return false;
            }
    
            if (self._ptr == needle._ptr) {
                return true;
            }
    
            bool equal;
            assembly {
                let length := mload(needle)
                let selfptr := mload(add(self, 0x20))
                let needleptr := mload(add(needle, 0x20))
                equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
            }
            return equal;
        }
    
        /*
         * @dev If `self` starts with `needle`, `needle` is removed from the
         *      beginning of `self`. Otherwise, `self` is unmodified.
         * @param self The slice to operate on.
         * @param needle The slice to search for.
         * @return `self`
         */
        function beyond(slice self, slice needle) internal pure returns (slice) {
            if (self._len < needle._len) {
                return self;
            }
    
            bool equal = true;
            if (self._ptr != needle._ptr) {
                assembly {
                    let length := mload(needle)
                    let selfptr := mload(add(self, 0x20))
                    let needleptr := mload(add(needle, 0x20))
                    equal := eq(sha3(selfptr, length), sha3(needleptr, length))
                }
            }
    
            if (equal) {
                self._len -= needle._len;
                self._ptr += needle._len;
            }
    
            return self;
        }
    
        /*
         * @dev Returns true if the slice ends with `needle`.
         * @param self The slice to operate on.
         * @param needle The slice to search for.
         * @return True if the slice starts with the provided text, false otherwise.
         */
        function endsWith(slice self, slice needle) internal pure returns (bool) {
            if (self._len < needle._len) {
                return false;
            }
    
            var selfptr = self._ptr + self._len - needle._len;
    
            if (selfptr == needle._ptr) {
                return true;
            }
    
            bool equal;
            assembly {
                let length := mload(needle)
                let needleptr := mload(add(needle, 0x20))
                equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
            }
    
            return equal;
        }
    
        /*
         * @dev If `self` ends with `needle`, `needle` is removed from the
         *      end of `self`. Otherwise, `self` is unmodified.
         * @param self The slice to operate on.
         * @param needle The slice to search for.
         * @return `self`
         */
        function until(slice self, slice needle) internal pure returns (slice) {
            if (self._len < needle._len) {
                return self;
            }
    
            var selfptr = self._ptr + self._len - needle._len;
            bool equal = true;
            if (selfptr != needle._ptr) {
                assembly {
                    let length := mload(needle)
                    let needleptr := mload(add(needle, 0x20))
                    equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
                }
            }
    
            if (equal) {
                self._len -= needle._len;
            }
    
            return self;
        }
    
        // Returns the memory address of the first byte of the first occurrence of
        // `needle` in `self`, or the first byte after `self` if not found.
        function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private returns (uint) {
            uint ptr;
            uint idx;
    
            if (needlelen <= selflen) {
                if (needlelen <= 32) {
                    // Optimized assembly for 68 gas per byte on short strings
                    assembly {
                        let mask := not(sub(exp(2, mul(8, sub(32, needlelen))), 1))
                        let needledata := and(mload(needleptr), mask)
                        let end := add(selfptr, sub(selflen, needlelen))
                        ptr := selfptr
                        loop:
                        jumpi(exit, eq(and(mload(ptr), mask), needledata))
                        ptr := add(ptr, 1)
                        jumpi(loop, lt(sub(ptr, 1), end))
                        ptr := add(selfptr, selflen)
                        exit:
                    }
                    return ptr;
                } else {
                    // For long needles, use hashing
                    bytes32 hash;
                    assembly { hash := sha3(needleptr, needlelen) }
                    ptr = selfptr;
                    for (idx = 0; idx <= selflen - needlelen; idx++) {
                        bytes32 testHash;
                        assembly { testHash := sha3(ptr, needlelen) }
                        if (hash == testHash)
                            return ptr;
                        ptr += 1;
                    }
                }
            }
            return selfptr + selflen;
        }
    
        // Returns the memory address of the first byte after the last occurrence of
        // `needle` in `self`, or the address of `self` if not found.
        function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private returns (uint) {
            uint ptr;
    
            if (needlelen <= selflen) {
                if (needlelen <= 32) {
                    // Optimized assembly for 69 gas per byte on short strings
                    assembly {
                        let mask := not(sub(exp(2, mul(8, sub(32, needlelen))), 1))
                        let needledata := and(mload(needleptr), mask)
                        ptr := add(selfptr, sub(selflen, needlelen))
                        loop:
                        jumpi(ret, eq(and(mload(ptr), mask), needledata))
                        ptr := sub(ptr, 1)
                        jumpi(loop, gt(add(ptr, 1), selfptr))
                        ptr := selfptr
                        jump(exit)
                        ret:
                        ptr := add(ptr, needlelen)
                        exit:
                    }
                    return ptr;
                } else {
                    // For long needles, use hashing
                    bytes32 hash;
                    assembly { hash := sha3(needleptr, needlelen) }
                    ptr = selfptr + (selflen - needlelen);
                    while (ptr >= selfptr) {
                        bytes32 testHash;
                        assembly { testHash := sha3(ptr, needlelen) }
                        if (hash == testHash)
                            return ptr + needlelen;
                        ptr -= 1;
                    }
                }
            }
            return selfptr;
        }
    
        /*
         * @dev Modifies `self` to contain everything from the first occurrence of
         *      `needle` to the end of the slice. `self` is set to the empty slice
         *      if `needle` is not found.
         * @param self The slice to search and modify.
         * @param needle The text to search for.
         * @return `self`.
         */
        function find(slice self, slice needle) internal returns (slice) {
            uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
            self._len -= ptr - self._ptr;
            self._ptr = ptr;
            return self;
        }
    
        /*
         * @dev Modifies `self` to contain the part of the string from the start of
         *      `self` to the end of the first occurrence of `needle`. If `needle`
         *      is not found, `self` is set to the empty slice.
         * @param self The slice to search and modify.
         * @param needle The text to search for.
         * @return `self`.
         */
        function rfind(slice self, slice needle) internal returns (slice) {
            uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr);
            self._len = ptr - self._ptr;
            return self;
        }
    
        /*
         * @dev Splits the slice, setting `self` to everything after the first
         *      occurrence of `needle`, and `token` to everything before it. If
         *      `needle` does not occur in `self`, `self` is set to the empty slice,
         *      and `token` is set to the entirety of `self`.
         * @param self The slice to split.
         * @param needle The text to search for in `self`.
         * @param token An output parameter to which the first token is written.
         * @return `token`.
         */
        function split(slice self, slice needle, slice token) internal returns (slice) {
            uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
            token._ptr = self._ptr;
            token._len = ptr - self._ptr;
            if (ptr == self._ptr + self._len) {
                // Not found
                self._len = 0;
            } else {
                self._len -= token._len + needle._len;
                self._ptr = ptr + needle._len;
            }
            return token;
        }
    
        /*
         * @dev Splits the slice, setting `self` to everything after the first
         *      occurrence of `needle`, and returning everything before it. If
         *      `needle` does not occur in `self`, `self` is set to the empty slice,
         *      and the entirety of `self` is returned.
         * @param self The slice to split.
         * @param needle The text to search for in `self`.
         * @return The part of `self` up to the first occurrence of `delim`.
         */
        function split(slice self, slice needle) internal returns (slice token) {
            split(self, needle, token);
        }
    
        /*
         * @dev Splits the slice, setting `self` to everything before the last
         *      occurrence of `needle`, and `token` to everything after it. If
         *      `needle` does not occur in `self`, `self` is set to the empty slice,
         *      and `token` is set to the entirety of `self`.
         * @param self The slice to split.
         * @param needle The text to search for in `self`.
         * @param token An output parameter to which the first token is written.
         * @return `token`.
         */
        function rsplit(slice self, slice needle, slice token) internal returns (slice) {
            uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr);
            token._ptr = ptr;
            token._len = self._len - (ptr - self._ptr);
            if (ptr == self._ptr) {
                // Not found
                self._len = 0;
            } else {
                self._len -= token._len + needle._len;
            }
            return token;
        }
    
        /*
         * @dev Splits the slice, setting `self` to everything before the last
         *      occurrence of `needle`, and returning everything after it. If
         *      `needle` does not occur in `self`, `self` is set to the empty slice,
         *      and the entirety of `self` is returned.
         * @param self The slice to split.
         * @param needle The text to search for in `self`.
         * @return The part of `self` after the last occurrence of `delim`.
         */
        function rsplit(slice self, slice needle) internal returns (slice token) {
            rsplit(self, needle, token);
        }
    
        /*
         * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`.
         * @param self The slice to search.
         * @param needle The text to search for in `self`.
         * @return The number of occurrences of `needle` found in `self`.
         */
        function count(slice self, slice needle) internal returns (uint cnt) {
            uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len;
            while (ptr <= self._ptr + self._len) {
                cnt++;
                ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len;
            }
        }
    
        /*
         * @dev Returns True if `self` contains `needle`.
         * @param self The slice to search.
         * @param needle The text to search for in `self`.
         * @return True if `needle` is found in `self`, false otherwise.
         */
        function contains(slice self, slice needle) internal returns (bool) {
            return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr;
        }
    
        /*
         * @dev Returns a newly allocated string containing the concatenation of
         *      `self` and `other`.
         * @param self The first slice to concatenate.
         * @param other The second slice to concatenate.
         * @return The concatenation of the two strings.
         */
        function concat(slice self, slice other) internal pure returns (string) {
            var ret = new string(self._len + other._len);
            uint retptr;
            assembly { retptr := add(ret, 32) }
            memcpy(retptr, self._ptr, self._len);
            memcpy(retptr + self._len, other._ptr, other._len);
            return ret;
        }
    
        /*
         * @dev Joins an array of slices, using `self` as a delimiter, returning a
         *      newly allocated string.
         * @param self The delimiter to use.
         * @param parts A list of slices to join.
         * @return A newly allocated string containing all the slices in `parts`,
         *         joined with `self`.
         */
        function join(slice self, slice[] parts) internal pure returns (string) {
            if (parts.length == 0)
                return "";
    
            uint length = self._len * (parts.length - 1);
            for(uint i = 0; i < parts.length; i++)
                length += parts[i]._len;
    
            var ret = new string(length);
            uint retptr;
            assembly { retptr := add(ret, 32) }
    
            for(i = 0; i < parts.length; i++) {
                memcpy(retptr, parts[i]._ptr, parts[i]._len);
                retptr += parts[i]._len;
                if (i < parts.length - 1) {
                    memcpy(retptr, self._ptr, self._len);
                    retptr += self._len;
                }
            }
    
            return ret;
        }
    }
    
    // File: contracts/CloversMetadata.sol
    
    pragma solidity ^0.4.18;
    
    /**
    * CloversMetadata contract is upgradeable and returns metadata about Clovers
    */
    
    
    
    contract CloversMetadata {
        using strings for *;
    
        function tokenURI(uint _tokenId) public view returns (string _infoUrl) {
            string memory base = "https://api2.clovers.network/clovers/metadata/0x";
            string memory id = uint2hexstr(_tokenId);
            string memory suffix = "";
            return base.toSlice().concat(id.toSlice()).toSlice().concat(suffix.toSlice());
        }
        function uint2hexstr(uint i) internal pure returns (string) {
            if (i == 0) return "0";
            uint j = i;
            uint length;
            while (j != 0) {
                length++;
                j = j >> 4;
            }
            uint mask = 15;
            bytes memory bstr = new bytes(length);
            uint k = length - 1;
            while (i != 0){
                uint curr = (i & mask);
                bstr[k--] = curr > 9 ? byte(55 + curr) : byte(48 + curr); // 55 = 65 - 10
                i = i >> 4;
            }
            return string(bstr);
        }
    }
    
    // File: contracts/Clovers.sol
    
    pragma solidity ^0.4.18;
    
    /**
     * Digital Asset Registry for the Non Fungible Token Clover
     * with upgradeable contract reference for returning metadata.
     */
    
    
    
    
    
    
    
    contract Clovers is ERC721Token, Admin, Ownable {
    
        address public cloversMetadata;
        uint256 public totalSymmetries;
        uint256[5] symmetries; // RotSym, Y0Sym, X0Sym, XYSym, XnYSym
        address public cloversController;
        address public clubTokenController;
    
        mapping (uint256 => Clover) public clovers;
        struct Clover {
            bool keep;
            uint256 symmetries;
            bytes28[2] cloverMoves;
            uint256 blockMinted;
            uint256 rewards;
        }
    
        modifier onlyOwnerOrController() {
            require(
                msg.sender == cloversController ||
                owner == msg.sender ||
                admins[msg.sender]
            );
            _;
        }
    
    
        /**
        * @dev Checks msg.sender can transfer a token, by being owner, approved, operator or cloversController
        * @param _tokenId uint256 ID of the token to validate
        */
        modifier canTransfer(uint256 _tokenId) {
            require(isApprovedOrOwner(msg.sender, _tokenId) || msg.sender == cloversController);
            _;
        }
    
        constructor(string name, string symbol) public
            ERC721Token(name, symbol)
        { }
    
        function () public payable {}
    
        function implementation() public view returns (address) {
            return cloversMetadata;
        }
    
        function tokenURI(uint _tokenId) public view returns (string _infoUrl) {
            return CloversMetadata(cloversMetadata).tokenURI(_tokenId);
        }
        function getHash(bytes28[2] moves) public pure returns (bytes32) {
            return keccak256(moves);
        }
        function getKeep(uint256 _tokenId) public view returns (bool) {
            return clovers[_tokenId].keep;
        }
        function getBlockMinted(uint256 _tokenId) public view returns (uint256) {
            return clovers[_tokenId].blockMinted;
        }
        function getCloverMoves(uint256 _tokenId) public view returns (bytes28[2]) {
            return clovers[_tokenId].cloverMoves;
        }
        function getReward(uint256 _tokenId) public view returns (uint256) {
            return clovers[_tokenId].rewards;
        }
        function getSymmetries(uint256 _tokenId) public view returns (uint256) {
            return clovers[_tokenId].symmetries;
        }
        function getAllSymmetries() public view returns (uint256, uint256, uint256, uint256, uint256, uint256) {
            return (
                totalSymmetries,
                symmetries[0], //RotSym,
                symmetries[1], //Y0Sym,
                symmetries[2], //X0Sym,
                symmetries[3], //XYSym,
                symmetries[4] //XnYSym
            );
        }
    
    /* ---------------------------------------------------------------------------------------------------------------------- */
    
        /**
        * @dev Moves Eth to a certain address for use in the CloversController
        * @param _to The address to receive the Eth.
        * @param _amount The amount of Eth to be transferred.
        */
        function moveEth(address _to, uint256 _amount) public onlyOwnerOrController {
            require(_amount <= this.balance);
            _to.transfer(_amount);
        }
        /**
        * @dev Moves Token to a certain address for use in the CloversController
        * @param _to The address to receive the Token.
        * @param _amount The amount of Token to be transferred.
        * @param _token The address of the Token to be transferred.
        */
        function moveToken(address _to, uint256 _amount, address _token) public onlyOwnerOrController returns (bool) {
            require(_amount <= ERC20(_token).balanceOf(this));
            return ERC20(_token).transfer(_to, _amount);
        }
        /**
        * @dev Approves Tokens to a certain address for use in the CloversController
        * @param _to The address to receive the Token approval.
        * @param _amount The amount of Token to be approved.
        * @param _token The address of the Token to be approved.
        */
        function approveToken(address _to, uint256 _amount, address _token) public onlyOwnerOrController returns (bool) {
            return ERC20(_token).approve(_to, _amount);
        }
    
        /**
        * @dev Sets whether the minter will keep the clover
        * @param _tokenId The token Id.
        * @param value Whether the clover will be kept.
        */
        function setKeep(uint256 _tokenId, bool value) public onlyOwnerOrController {
            clovers[_tokenId].keep = value;
        }
        function setBlockMinted(uint256 _tokenId, uint256 value) public onlyOwnerOrController {
            clovers[_tokenId].blockMinted = value;
        }
        function setCloverMoves(uint256 _tokenId, bytes28[2] moves) public onlyOwnerOrController {
            clovers[_tokenId].cloverMoves = moves;
        }
        function setReward(uint256 _tokenId, uint256 _amount) public onlyOwnerOrController {
            clovers[_tokenId].rewards = _amount;
        }
        function setSymmetries(uint256 _tokenId, uint256 _symmetries) public onlyOwnerOrController {
            clovers[_tokenId].symmetries = _symmetries;
        }
    
        /**
        * @dev Sets total tallies of symmetry counts. For use by the controller to correct for invalid Clovers.
        * @param _totalSymmetries The total number of Symmetries.
        * @param RotSym The total number of RotSym Symmetries.
        * @param Y0Sym The total number of Y0Sym Symmetries.
        * @param X0Sym The total number of X0Sym Symmetries.
        * @param XYSym The total number of XYSym Symmetries.
        * @param XnYSym The total number of XnYSym Symmetries.
        */
        function setAllSymmetries(uint256 _totalSymmetries, uint256 RotSym, uint256 Y0Sym, uint256 X0Sym, uint256 XYSym, uint256 XnYSym) public onlyOwnerOrController {
            totalSymmetries = _totalSymmetries;
            symmetries[0] = RotSym;
            symmetries[1] = Y0Sym;
            symmetries[2] = X0Sym;
            symmetries[3] = XYSym;
            symmetries[4] = XnYSym;
        }
    
        /**
        * @dev Deletes data about a Clover.
        * @param _tokenId The Id of the clover token to be deleted.
        */
        function deleteClover(uint256 _tokenId) public onlyOwnerOrController {
            delete(clovers[_tokenId]);
            unmint(_tokenId);
        }
        /**
        * @dev Updates the CloversController contract address and approves that contract to manage the Clovers owned by the Clovers contract.
        * @param _cloversController The address of the new contract.
        */
        function updateCloversControllerAddress(address _cloversController) public onlyOwner {
            require(_cloversController != 0);
            cloversController = _cloversController;
        }
    
    
    
        /**
        * @dev Updates the CloversMetadata contract address.
        * @param _cloversMetadata The address of the new contract.
        */
        function updateCloversMetadataAddress(address _cloversMetadata) public onlyOwner {
            require(_cloversMetadata != 0);
            cloversMetadata = _cloversMetadata;
        }
    
        function updateClubTokenController(address _clubTokenController) public onlyOwner {
            require(_clubTokenController != 0);
            clubTokenController = _clubTokenController;
        }
    
        /**
        * @dev Mints new Clovers.
        * @param _to The address of the new clover owner.
        * @param _tokenId The Id of the new clover token.
        */
        function mint (address _to, uint256 _tokenId) public onlyOwnerOrController {
            super._mint(_to, _tokenId);
            setApprovalForAll(clubTokenController, true);
        }
    
    
        function mintMany(address[] _tos, uint256[] _tokenIds, bytes28[2][] memory _movess, uint256[] _symmetries) public onlyAdmin {
            require(_tos.length == _tokenIds.length && _tokenIds.length == _movess.length && _movess.length == _symmetries.length);
            for (uint256 i = 0; i < _tos.length; i++) {
                address _to = _tos[i];
                uint256 _tokenId = _tokenIds[i];
                bytes28[2] memory _moves = _movess[i];
                uint256 _symmetry = _symmetries[i];
                setCloverMoves(_tokenId, _moves);
                if (_symmetry > 0) {
                    setSymmetries(_tokenId, _symmetry);
                }
                super._mint(_to, _tokenId);
                setApprovalForAll(clubTokenController, true);
            }
        }
    
        /**
        * @dev Unmints Clovers.
        * @param _tokenId The Id of the clover token to be destroyed.
        */
        function unmint (uint256 _tokenId) public onlyOwnerOrController {
            super._burn(ownerOf(_tokenId), _tokenId);
        }
    
    
    }