EthereumEthereum
0x96...CDcc
Inventory V2

Inventory V2

ITEM

Colección
Precio de Piso
0,134 ETH
$2,345.34
Tamaño
1509
Coleccionables
Propietarios
602
40 % Propietarios Únicos
¡El código fuente de este contrato está verificado!
Metadatos del Contrato
Compilador
0.5.17+commit.d19bba13
Idioma
Solidity
Código Fuente del Contrato
Archivo 1 de 1: Inventory.sol
pragma solidity ^0.5.10;

/*
  _______                   ____  _____  
 |__   __|                 |___ \|  __ \ 
    | | ___  __ _ _ __ ___   __) | |  | |
    | |/ _ \/ _` | '_ ` _ \ |__ <| |  | |
    | |  __/ (_| | | | | | |___) | |__| |
    |_|\___|\__,_|_| |_| |_|____/|_____/ 

    https://team3d.io
    https://discord.gg/team3d
    
    NFT Inventory contract

*/

/**
 * @title IERC165
 * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
 */
contract IERC165 {
    /**
     * @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);
}

/**
 * @title ERC721 Non-Fungible Token Standard basic interface
 * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 */
contract IERC721 {
    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 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 memory data) public;
}

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
contract IERC721Receiver {
    /**
     * @notice Handle the receipt of an NFT
     * @dev The ERC721 smart contract calls this function on the recipient
     * after a `safeTransfer`. This function MUST return the function selector,
     * otherwise the caller will revert the transaction. The selector to be
     * returned can be obtained as `this.onERC721Received.selector`. This
     * function MAY throw to revert and reject the transfer.
     * Note: the ERC721 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 transferred
     * @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 memory data) public returns (bytes4);
}


/**
 * @title ERC165
 * @author Matt Condon (@shrugs)
 * @dev Implements ERC165 using a lookup table.
 */
contract ERC165 is IERC165 {
    bytes4 private 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) private _supportedInterfaces;

    /**
     * @dev A contract implementing SupportsInterfaceWithLookup
     * implement ERC165 itself
     */
    constructor () internal {
        _registerInterface(_InterfaceId_ERC165);
    }

    /**
     * @dev implement supportsInterface(bytes4) using a lookup table
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool) {
        return _supportedInterfaces[interfaceId];
    }

    /**
     * @dev internal method for registering an interface
     */
    function _registerInterface(bytes4 interfaceId) internal {
        require(interfaceId != 0xffffffff);
        _supportedInterfaces[interfaceId] = true;
    }
}


/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 */
contract IERC721Metadata {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function tokenURI(uint256 tokenId) external view returns (string memory);
}


/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 */
contract IERC721Enumerable {
    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);
}


contract ERC20Token {
    function balanceOf(address owner) public view returns (uint256);
    function transfer(address to, uint256 value) public returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool);
}


/**
 * @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 private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    constructor () internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @return the address of the owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner());
        _;
    }

    /**
     * @return true if `msg.sender` is the owner of the contract.
     */
    function isOwner() public view returns (bool) {
        return 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 OwnershipTransferred(_owner, address(0));
        _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;
    }
}


/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}


/**
 * Utility library of inline functions on addresses
 */
library Address {
    /**
     * 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 account address of the account to check
     * @return whether the target address is a contract
     */
    function isContract(address account) 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(account) }
        return size > 0;
    }
}


contract Inventory is ERC165, IERC721, IERC721Metadata, IERC721Enumerable, Ownable {
    using SafeMath for uint256;
    using Address for address;
    
    string private _name;
    string private _symbol;
    string private _pathStart;
    string private _pathEnd;
    bytes4 private constant InterfaceId_ERC721Metadata = 0x5b5e139f;
    bytes4 private constant _InterfaceId_ERC721Enumerable = 0x780e9d63;
    bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
    bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;

    // Treasure chest reward token (VIDYA)
    ERC20Token public constant treasureChestRewardToken = ERC20Token(0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30);
    
    // Uniswap token 
    ERC20Token public constant UNI_ADDRESS = ERC20Token(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984);
    
    // Unicorn's Head
    uint256 private constant UNICORN_TEMPLATE_ID = 11;
	uint256 public UNICORN_TOTAL_SUPPLY = 0;
	mapping (address => bool) public unicornClaimed;

    // Mapping from token ID to owner
    mapping (uint256 => address) private _tokenOwner;

    // Mapping from token ID to approved address
    mapping (uint256 => address) private _tokenApprovals;

    // Mapping from owner to number of owned tokens
    mapping (address => uint256) private _ownedTokensCount;

    // Mapping from owner to operator approvals
    mapping (address => mapping (address => bool)) private _operatorApprovals;
    
    // Mapping of contract addresses that are allowed to edit item features 
    mapping (address => bool) private _approvedGameContract;

    // Mapping from token ID to respective treasure chest rewards in VIDYA tokens
    mapping (uint256 => uint256) public treasureChestRewards;

    // Mapping to calculate how many treasure hunts an address has participated in
    mapping (address => uint256) public treasureHuntPoints;
    
    // Mapping for the different equipment items of each address/character
    // 0 - head, 1 - left hand, 2 - neck, 3 - right hand, 4 - chest, 5 - legs
    mapping (address => uint256[6]) public characterEquipment;
    
    // To check if a template exists
    mapping (uint256 => bool) _templateExists; 

    /* Item struct holds the templateId, a total of 4 additional features
    and the burned status */
    struct Item {
        uint256 templateId; // id of Template in the itemTemplates array
        uint8 feature1;
        uint8 feature2;
        uint8 feature3;
        uint8 feature4;
        uint8 equipmentPosition;
        bool burned;
    }
    
    /* Template struct holds the uri for each Item- 
    a reference to the json file which describes them */
    struct Template {
        string uri;
    }
    
    // All items created, ever, both burned and not burned 
    Item[] public allItems;
    
    // Admin editable templates for each item 
    Template[] public itemTemplates;
    
    modifier onlyApprovedGame() {
        require(_approvedGameContract[msg.sender], "msg.sender is not an approved game contract");
        _;
    }
    
    modifier tokenExists(uint256 _tokenId) {
        require(_exists(_tokenId), "Token does not exist");
        _;
    }
    
    modifier isOwnedByOrigin(uint256 _tokenId) {
        require(ownerOf(_tokenId) == tx.origin, "tx.origin is not the token owner");
        _;
    }
    
    modifier isOwnerOrApprovedGame(uint256 _tokenId) {
        require(ownerOf(_tokenId) == msg.sender || _approvedGameContract[msg.sender], "Not owner or approved game");
        _;
    }
    
    modifier templateExists(uint256 _templateId) {
        require(_templateExists[_templateId], "Template does not exist");
        _;
    }

    constructor() 
        public  
    {
        _name = "Inventory";
        _symbol = "ITEM";
        _pathStart = "https://team3d.io/inventory/json/";
        _pathEnd = ".json";
        _registerInterface(InterfaceId_ERC721Metadata);
        _registerInterface(_InterfaceId_ERC721Enumerable);
        _registerInterface(_InterfaceId_ERC721);
        
        // Add the "nothing" item to msg.sender
        // This is a dummy item so that valid items in allItems start with 1
        addNewItem(0,0);
    }
    
    // Get the Unicorn's head item
    function mintUnicorn()
        external
    {
        uint256 id;
        
        require(UNICORN_TOTAL_SUPPLY < 100, "Unicorns are now extinct");
		require(!unicornClaimed[msg.sender], "You have already claimed a unicorn");
        require(UNI_ADDRESS.balanceOf(msg.sender) >= 1000 * 10**18, "Min balance 1000 UNI");
        require(_templateExists[UNICORN_TEMPLATE_ID], "Unicorn template has not been added yet");
        checkAndTransferVIDYA(1000 * 10**18); // Unicorn's head costs 1000 VIDYA 
        
        id = allItems.push(Item(UNICORN_TEMPLATE_ID,0,0,0,0,0,false)) -1;
		
		UNICORN_TOTAL_SUPPLY = UNICORN_TOTAL_SUPPLY.add(1);
		unicornClaimed[msg.sender] = true;
        
        // Materialize 
        _mint(msg.sender, id);
    }
    
    function checkAndTransferVIDYA(uint256 _amount) private {
        require(treasureChestRewardToken.transferFrom(msg.sender, address(this), _amount) == true, "transfer must succeed");
    }
    
    function equip(
        uint256 _tokenId, 
        uint8 _equipmentPosition
    ) 
        external
        tokenExists(_tokenId)
    {
        require(_equipmentPosition < 6);
        require(allItems[_tokenId].equipmentPosition == _equipmentPosition, 
            "Item cannot be equipped in this slot");
            
        characterEquipment[msg.sender][_equipmentPosition] = _tokenId;
    }

    function unequip(
        uint8 _equipmentPosition
    ) 
        external
    {
        require(_equipmentPosition < 6);
        characterEquipment[msg.sender][_equipmentPosition] = 0;
    }

    function getEquipment(
        address player
    ) 
        public 
        view 
        returns(uint256[6] memory)
    {
        return characterEquipment[player];
    }

    // The total supply of any one item
    // Ask for example how many of "Torch" item exist
    function getIndividualCount(
        uint256 _templateId
    ) 
        public 
        view 
        returns(uint256) 
    {
        uint counter = 0;
        
        for (uint i = 0; i < allItems.length; i++) {
            // If match found & is not burned 
            if (allItems[i].templateId == _templateId && !allItems[i].burned) {
                counter++;
            }
        }
        
        // Total supply of item using the _templateId
        return counter;
    }
    
    // Total supply of any one item owned by _owner
    // Ask for example how many of "Torch" item does the _owner have 
    function getIndividualOwnedCount(
        uint256 _templateId,
        address _owner
    )
        public 
        view 
        returns(uint256)
    {
        uint counter = 0;
        uint[] memory ownedItems = getItemsByOwner(_owner);
        
        for(uint i = 0; i < ownedItems.length; i++) {
            
            /* If ownedItems[i]'s templateId matches the one in allItems[] */
            if(allItems[ownedItems[i]].templateId == _templateId) {
                counter++;
            }
        }
        
        // Total supply of _templateId that _owner owns 
        return counter;
    }
    
    // Given a _tokenId returns how many other tokens exist with 
    // the same _templateId
    function getIndividualCountByID(
        uint256 _tokenId
    )
        public
        view
        tokenExists(_tokenId)
        returns(uint256)
    {
        uint256 counter = 0;
        uint256 templateId = allItems[_tokenId].templateId; // templateId we are looking for 
        
        for(uint i = 0; i < allItems.length; i++) {
            if(templateId == allItems[i].templateId && !allItems[i].burned) {
                counter++;
            }
        }
        
        return counter;
    }
    
    // Given a _tokenId returns how many other tokens the _owner has 
    // with the same _templateId
    function getIndividualOwnedCountByID(
        uint256 _tokenId,
        address _owner 
    )
        public
        view
        tokenExists(_tokenId)
        returns(uint256)
    {
        uint256 counter = 0;
        uint256 templateId = allItems[_tokenId].templateId; // templateId we are looking for 
        uint[] memory ownedItems = getItemsByOwner(_owner);
        
        for(uint i = 0; i < ownedItems.length; i++) {
            // The item cannot be burned because of getItemsByOwner(_owner), no need to check 
            if(templateId == allItems[ownedItems[i]].templateId) {
                counter++;
            }
        }
        
        return counter;
    }
    
    /*  Given an array of _tokenIds return the corresponding _templateId count 
        for each of those _tokenIds */
    function getTemplateCountsByTokenIDs(
        uint[] memory _tokenIds
    )
        public
        view
        returns(uint[] memory)
    {
        uint[] memory counts = new uint[](_tokenIds.length);
        
        for(uint i = 0; i < _tokenIds.length; i++) {
            counts[i] = getIndividualCountByID(_tokenIds[i]);
        }
        
        return counts;
    }
    
    /*  Given an array of _tokenIds return the corresponding _templateId count 
        for each of those _tokenIds that the _owner owns */
    function getTemplateCountsByTokenIDsOfOwner(
        uint[] memory _tokenIds,
        address _owner 
    )
        public
        view
        returns(uint[] memory)
    {
        uint[] memory counts = new uint[](_tokenIds.length);
        
        for(uint i = 0; i < _tokenIds.length; i++) {
            counts[i] = getIndividualOwnedCountByID(_tokenIds[i], _owner);
        }
        
        return counts;
    }
    
    /*  Given an array of _tokenIds return the corresponding _templateIds 
        for each of those _tokenIds 
        
        Useful for cross referencing / weeding out duplicates to populate the UI */
    function getTemplateIDsByTokenIDs(
        uint[] memory _tokenIds
    )
        public
        view
        returns(uint[] memory)
    {
        uint[] memory templateIds = new uint[](_tokenIds.length);
        
        for(uint i = 0; i < _tokenIds.length; i++) {
            templateIds[i] = allItems[_tokenIds[i]].templateId;
        }
        
        return templateIds;
    }

    // Get all the item id's by owner 
    function getItemsByOwner(
        address _owner
    ) 
        public 
        view 
        returns(uint[] memory) 
    {
        uint[] memory result = new uint[](_ownedTokensCount[_owner]);
        uint counter = 0;
        
        for (uint i = 0; i < allItems.length; i++) {
            // If owner is _owner and token is not burned 
            if (_tokenOwner[i] == _owner && !allItems[i].burned) {
                result[counter] = i;
                counter++;
            }
        }
        
        // Array of ID's in allItems that _owner owns 
        return result;
    }

    // Function to withdraw any ERC20 tokens
    function withdrawERC20Tokens(
        address _tokenContract
    ) 
        external 
        onlyOwner 
        returns(bool) 
    {
        ERC20Token token = ERC20Token(_tokenContract);
        uint256 amount = token.balanceOf(address(this));
        return token.transfer(msg.sender, amount);
    }
    
    // Admin can approve (or disapprove) game contracts 
    function approveGameContract(
        address _game,
        bool _status
    )
        external 
        onlyOwner
    {
        _approvedGameContract[_game] = _status;
    }
    
    // Admin function to set _pathStart and _pathEnd
    function setPaths(
        string calldata newPathStart,
        string calldata newPathEnd
    )
        external
        onlyOwner
        returns(bool)
    {
        bool success;
        
        if(keccak256(abi.encodePacked(_pathStart)) != keccak256(abi.encodePacked(newPathStart))) {
            _pathStart = newPathStart;
            success = true;
        }
        
        if(keccak256(abi.encodePacked(_pathEnd)) != keccak256(abi.encodePacked(newPathEnd))) {
            _pathEnd = newPathEnd;
            success = true;
        }
        
        return success;
    }

    /* Admin can add new item template
    The _templateId is a reference to Template struct in itemTemplates[] */
    function addNewItem(
        uint256 _templateId,
        uint8 _equipmentPosition
    )
        public 
        onlyOwner
    {
        uint256 id;
        
        // Does the _templateId exist or do we need to add it?
        if(!_templateExists[_templateId]) {
            // Add template id for this item as reference
            itemTemplates.push(Template(uint2str(_templateId)));
            _templateExists[_templateId] = true;
        }
        
        id = allItems.push(Item(_templateId,0,0,0,0,_equipmentPosition,false)) -1;
        
        // Materialize 
        _mint(msg.sender, id);
    }
    
    /* Admin can add new item template and send it to receiver in 
    one call */
    function addNewItemAndTransfer(
        uint256 _templateId,
        uint8 _equipmentPosition,
        address receiver 
    )
        public 
        onlyOwner
    {
        uint256 id;
        
        // Does the _templateId exist or do we need to add it?
        if(!_templateExists[_templateId]) {
            // Add template id for this item as reference 
            itemTemplates.push(Template(uint2str(_templateId)));
            _templateExists[_templateId] = true;
        }
        
        id = allItems.push(Item(_templateId,0,0,0,0,_equipmentPosition,false)) -1;
        
        // Materialize 
        _mint(receiver, id);
    }
    
    /*  Allows approved game contracts to create new items from 
        already existing templates (added by admin)
        
        In other words this function allows a game to spawn more 
        of ie. "Torch" and set its default features etc */
    function createFromTemplate(
        uint256 _templateId,
        uint8 _feature1,
        uint8 _feature2,
        uint8 _feature3,
        uint8 _feature4,
        uint8 _equipmentPosition
    )
        public
        templateExists(_templateId)
        onlyApprovedGame
        returns(uint256)
    {
        uint256 id; 
        address player = tx.origin;
        
        id = allItems.push(
            Item(
                _templateId,
                _feature1,
                _feature2,
                _feature3,
                _feature4,
                _equipmentPosition,
                false
            )
        ) -1;
        
        // Materialize to tx.origin (and not msg.sender aka. the game contract)
        _mint(player, id);
        
        // id of the new item 
        return id;
    }

    /*
    Change feature values of _tokenId
    
    Only succeeds when:
        the tx.origin (a player) owns the item 
        the msg.sender (game contract) is a manually approved game address 
    */
    function changeFeaturesForItem(
        uint256 _tokenId,
        uint8 _feature1,
        uint8 _feature2,
        uint8 _feature3,
        uint8 _feature4,
        uint8 _equipmentPosition
    )
        public 
        onlyApprovedGame // msg.sender has to be a manually approved game address 
        tokenExists(_tokenId) // check if _tokenId exists in the first place 
        isOwnedByOrigin(_tokenId) // does the tx.origin (player in a game) own the token?
        returns(bool)
    {
        return (
            _changeFeaturesForItem(
                _tokenId,
                _feature1,
                _feature2,
                _feature3,
                _feature4,
                _equipmentPosition
            )
        );
    }

    function _changeFeaturesForItem(
        uint256 _tokenId,
        uint8 _feature1,
        uint8 _feature2,
        uint8 _feature3,
        uint8 _feature4,
        uint8 _equipmentPosition
    )
        internal
        returns(bool)
    {
        Item storage item = allItems[_tokenId];

        if(item.feature1 != _feature1) {
            item.feature1 = _feature1;
        }
        
        if(item.feature2 != _feature2) {
            item.feature2 = _feature2;
        }
        
        if(item.feature3 != _feature3) {
            item.feature3 = _feature3;
        }
        
        if(item.feature4 != _feature4) {
            item.feature4 = _feature4;
        }
        
        if(item.equipmentPosition != _equipmentPosition) {
            item.equipmentPosition = _equipmentPosition;
        }
        
        return true;
    }
    
    /*
    Features of _tokenId 
    Useful in various games where the _tokenId should 
    have traits etc.
    
    Example: a "Torch" could start with 255 and degrade 
    during gameplay over time 
    
    Note: maximum value for uint8 type is 255 
    */
    function getFeaturesOfItem(
        uint256 _tokenId 
    )
        public 
        view 
        returns(uint8[] memory)
    {
        Item storage item = allItems[_tokenId];
        uint8[] memory features = new uint8[](4);
        
        features[0] = item.feature1;
        features[1] = item.feature2;
        features[2] = item.feature3;
        features[3] = item.feature4;
        
        return features;
    }

    /*
    Turn uint256 into a string
    
    Reason: ERC721 standard needs token uri to return as string,
    but we don't want to store long urls to the json files on-chain.
    Instead we use this returned string (which is actually just an ID)
    and say to front ends that the token uri can be found at 
    somethingsomething.io/tokens/<id>.json 
    */
    function uint2str(
        uint256 i
    ) 
        internal 
        pure 
        returns(string memory) 
    {
        if (i == 0) return "0";
        
        uint256 j = i;
        uint256 length;
        while (j != 0) {
            length++;
            j /= 10;
        }
        bytes memory bstr = new bytes(length);
        uint256 k = length - 1;
        while (i != 0) {
            bstr[k--] = byte(uint8(48 + i % 10)); 
            i /= 10;
        }
        return string(bstr);
    }
    
    function append(
        string memory a, 
        string memory b, 
        string memory c
    ) 
        internal 
        pure 
        returns(string memory) 
    {
        return string(
            abi.encodePacked(a, b, c)
        );
    }
    
    /*
     * Adds an NFT and the corresponding reward for whoever finds it and burns it.
     */
    function addTreasureChest(uint256 _tokenId, uint256 _rewardsAmount) 
    external
    tokenExists(_tokenId)
    onlyApprovedGame 
    {
        treasureChestRewards[_tokenId] = _rewardsAmount;
    }

    /*  Burn the _tokenId
        Succeeds when:
            token exists 
            msg.sender is either direct owner of the token OR 
            msg.sender is a manually approved game contract 
        
        If tx.origin and msg.sender are different, burn the 
        _tokenId of the tx.origin (the player, not the game contract)
    */
    function burn(
        uint256 _tokenId
    )
        public
        tokenExists(_tokenId)
        isOwnerOrApprovedGame(_tokenId)
        returns(bool)
    {
        if (tx.origin == msg.sender) {
            return _burn(_tokenId, msg.sender);
        } else {
            return _burn(_tokenId, tx.origin);
        }
    }
    
    // Burn owner's tokenId 
    function _burn(
        uint256 _tokenId,
        address owner
    )
        internal
        returns(bool)
    {
        // Set burned status on token
        allItems[_tokenId].burned = true;
        
        // Set new owner to 0x0 
        _tokenOwner[_tokenId] = address(0);
        
        // Remove from old owner 
        _ownedTokensCount[owner] = _ownedTokensCount[owner].sub(1);

        // Check if it's a treasure hunt token
        uint256 treasureChestRewardsForToken = treasureChestRewards[_tokenId];
        if (treasureChestRewardsForToken > 0) {
            treasureChestRewardToken.transfer(msg.sender, treasureChestRewardsForToken);
            treasureHuntPoints[owner]++;
        }

        // Fire event 
        emit Transfer(owner, address(0), _tokenId);
        
        return true;
    }

    function getLevel(address player) public view returns(uint256) {
        return treasureHuntPoints[player];
    }

    // Return the total supply
    function totalSupply() 
        public 
        view 
        returns(uint256)
    {
        uint256 counter;
        for(uint i = 0; i < allItems.length; i++) {
            if(!allItems[i].burned) {
                counter++;
            }
        }
        
        // All tokens which are not burned 
        return counter;
    }
    
    // Return the templateId of _index token
    function tokenByIndex(
        uint256 _index
    ) 
        public 
        view 
        returns(uint256) 
    {
        require(_index < totalSupply());
        return allItems[_index].templateId;
    }
    
    // Return The token templateId for the index'th token assigned to owner
    function tokenOfOwnerByIndex(
        address owner,
        uint256 index
    ) 
        public 
        view 
        returns 
        (uint256 tokenId) 
    {
        require(index < balanceOf(owner));
        return getItemsByOwner(owner)[index];
    }

    /**
     * @dev Gets the token name
     * @return string representing the token name
     */
    function name() 
        external 
        view 
        returns(string memory) 
    {
        return _name;
    }

    /**
     * @dev Gets the token symbol
     * @return string representing the token symbol
     */
    function symbol() 
        external 
        view 
        returns(string memory) 
    {
        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
    ) 
        external 
        view 
        returns(string memory) 
    {
        require(_exists(tokenId));
        uint256 tokenTemplateId = allItems[tokenId].templateId;
        
        string memory id = uint2str(tokenTemplateId);
        return append(_pathStart, id, _pathEnd);
    }

    /**
     * @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));
        require(!allItems[tokenId].burned, "This token is burned"); // Probably useless require at this point 
        
        return owner;
    }

    /**
     * @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
     * Reverts if the token ID does not exist.
     * @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)
    {
        require(_exists(tokenId));
        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 
    {
        require(_isApprovedOrOwner(msg.sender, tokenId));
        _transferFrom(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 
    {
        // 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 memory _data
    ) 
        public 
    {
        transferFrom(from, to, tokenId);
        // solium-disable-next-line arg-overflow
        require(_checkOnERC721Received(from, to, tokenId, _data));
    }

    /**
     * @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
    ) 
        internal 
        view 
        returns(bool) 
    {
        address owner = _tokenOwner[tokenId];
        return owner != address(0);
    }

    /**
     * @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
     */
    function _mint(
        address to, 
        uint256 tokenId
    ) internal {
        require(to != address(0));
        require(!_exists(tokenId));

        _tokenOwner[tokenId] = to;
        _ownedTokensCount[to] = _ownedTokensCount[to].add(1);

        emit Transfer(address(0), to, tokenId);
    }

    /**
     * @dev Internal function to transfer ownership of a given token ID to another address.
     * As opposed to transferFrom, this imposes no restrictions on msg.sender.
     * @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
    ) 
        internal 
    {
        require(ownerOf(tokenId) == from);
        require(to != address(0));

        _clearApproval(tokenId);

        _ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
        _ownedTokensCount[to] = _ownedTokensCount[to].add(1);

        _tokenOwner[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    /**
     * @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 _checkOnERC721Received(
        address from, 
        address to, 
        uint256 tokenId, 
        bytes memory _data
    ) 
        internal 
        returns(bool) 
    {
        if (!to.isContract()) {
            return true;
        }

        bytes4 retval = IERC721Receiver(to).onERC721Received(
            msg.sender, 
            from, 
            tokenId, 
            _data
        );
        
        return (retval == _ERC721_RECEIVED);
    }

    /**
     * @dev Private function to clear current approval of a given token ID
     * @param tokenId uint256 ID of the token to be transferred
     */
    function _clearApproval(
        uint256 tokenId
    ) 
        private 
    {
        if (_tokenApprovals[tokenId] != address(0)) {
            _tokenApprovals[tokenId] = address(0);
        }
    }
}
Configuraciones
{
  "compilationTarget": {
    "Inventory.sol": "Inventory"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":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"},{"constant":true,"inputs":[],"name":"UNICORN_TOTAL_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UNI_ADDRESS","outputs":[{"internalType":"contract ERC20Token","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_templateId","type":"uint256"},{"internalType":"uint8","name":"_equipmentPosition","type":"uint8"}],"name":"addNewItem","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_templateId","type":"uint256"},{"internalType":"uint8","name":"_equipmentPosition","type":"uint8"},{"internalType":"address","name":"receiver","type":"address"}],"name":"addNewItemAndTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_rewardsAmount","type":"uint256"}],"name":"addTreasureChest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allItems","outputs":[{"internalType":"uint256","name":"templateId","type":"uint256"},{"internalType":"uint8","name":"feature1","type":"uint8"},{"internalType":"uint8","name":"feature2","type":"uint8"},{"internalType":"uint8","name":"feature3","type":"uint8"},{"internalType":"uint8","name":"feature4","type":"uint8"},{"internalType":"uint8","name":"equipmentPosition","type":"uint8"},{"internalType":"bool","name":"burned","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_game","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"approveGameContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint8","name":"_feature1","type":"uint8"},{"internalType":"uint8","name":"_feature2","type":"uint8"},{"internalType":"uint8","name":"_feature3","type":"uint8"},{"internalType":"uint8","name":"_feature4","type":"uint8"},{"internalType":"uint8","name":"_equipmentPosition","type":"uint8"}],"name":"changeFeaturesForItem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"characterEquipment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_templateId","type":"uint256"},{"internalType":"uint8","name":"_feature1","type":"uint8"},{"internalType":"uint8","name":"_feature2","type":"uint8"},{"internalType":"uint8","name":"_feature3","type":"uint8"},{"internalType":"uint8","name":"_feature4","type":"uint8"},{"internalType":"uint8","name":"_equipmentPosition","type":"uint8"}],"name":"createFromTemplate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint8","name":"_equipmentPosition","type":"uint8"}],"name":"equip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getEquipment","outputs":[{"internalType":"uint256[6]","name":"","type":"uint256[6]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getFeaturesOfItem","outputs":[{"internalType":"uint8[]","name":"","type":"uint8[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_templateId","type":"uint256"}],"name":"getIndividualCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getIndividualCountByID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_templateId","type":"uint256"},{"internalType":"address","name":"_owner","type":"address"}],"name":"getIndividualOwnedCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_owner","type":"address"}],"name":"getIndividualOwnedCountByID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"getItemsByOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getLevel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"getTemplateCountsByTokenIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"address","name":"_owner","type":"address"}],"name":"getTemplateCountsByTokenIDsOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"getTemplateIDsByTokenIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"itemTemplates","outputs":[{"internalType":"string","name":"uri","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"mintUnicorn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"newPathStart","type":"string"},{"internalType":"string","name":"newPathEnd","type":"string"}],"name":"setPaths","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"treasureChestRewardToken","outputs":[{"internalType":"contract ERC20Token","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"treasureChestRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"treasureHuntPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint8","name":"_equipmentPosition","type":"uint8"}],"name":"unequip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unicornClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_tokenContract","type":"address"}],"name":"withdrawERC20Tokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]