// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./libraries/SafeMath.sol"; import "./libraries/Address.sol"; import "./libraries/EnumerableSet.sol"; import "./libraries/EnumerableMap.sol"; import "./libraries/Strings.sol"; import "./utils/Context.sol"; interface ICosmoMasksERC721 { // IERC165 function supportsInterface(bytes4 interfaceId) external view returns (bool); // IERC721Enumerable function totalSupply() external view returns (uint256); function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); function tokenByIndex(uint256 index) external view returns (uint256); // IERC721Metadata function name() external view returns (string memory _name); function symbol() external view returns (string memory _symbol); function tokenURI(uint256 _tokenId) external view returns (string memory); // ERC721 function balanceOf(address owner) external view returns (uint256 balance); function ownerOf(uint256 tokenId) external view returns (address owner); function safeTransferFrom(address from, address to, uint256 tokenId) external; function transferFrom(address from, address to, uint256 tokenId) external; function approve(address to, uint256 tokenId) external; function getApproved(uint256 tokenId) external view returns (address operator); function setApprovalForAll(address operator, bool _approved) external; function isApprovedForAll(address owner, address operator) external view returns (bool); function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; 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); } interface IERC721Receiver { function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); } /** * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://eips.ethereum.org/EIPS/eip-721 */ abstract contract CosmoMasksERC721 is Context, ICosmoMasksERC721 { using SafeMath for uint256; using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableMap for EnumerableMap.UintToAddressMap; using Strings for uint256; // ERC165 bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; mapping(bytes4 => bool) private _supportedInterfaces; bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; bytes4 private constant _INTERFACE_ID_ERC721_METADATA_SHORT = 0x93254542; bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; mapping(address => EnumerableSet.UintSet) private _holderTokens; EnumerableMap.UintToAddressMap private _tokenOwners; mapping(uint256 => address) private _tokenApprovals; mapping(address => mapping(address => bool)) private _operatorApprovals; string private _name; string private _symbol; string private _baseURI; string private _url; constructor(string memory name_, string memory symbol_) internal { _name = name_; _symbol = symbol_; _registerInterface(_INTERFACE_ID_ERC165); _registerInterface(_INTERFACE_ID_ERC721); _registerInterface(_INTERFACE_ID_ERC721_METADATA); _registerInterface(_INTERFACE_ID_ERC721_METADATA_SHORT); _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); } function balanceOf(address owner) public view override returns (uint256) { require(owner != address(0), "CosmoMasks: balance query for the zero address"); return _holderTokens[owner].length(); } function ownerOf(uint256 tokenId) public view override returns (address) { return _tokenOwners.get(tokenId, "CosmoMasks: owner query for nonexistent token"); } function name() public view override returns (string memory) { return _name; } function symbol() public view override returns (string memory) { return _symbol; } function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "CosmoMasks: URI query for nonexistent token"); string memory base = baseURI(); return string(abi.encodePacked(base, tokenId.toString(), ".json")); } function baseURI() public view returns (string memory) { return _baseURI; } function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) { return _holderTokens[owner].at(index); } function totalSupply() public view override returns (uint256) { return _tokenOwners.length(); } function tokenByIndex(uint256 index) public view override returns (uint256) { (uint256 tokenId, ) = _tokenOwners.at(index); return tokenId; } function approve(address to, uint256 tokenId) public override { address owner = ownerOf(tokenId); require(to != owner, "CosmoMasks: approval to current owner"); require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()), "CosmoMasks: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } function getApproved(uint256 tokenId) public view override returns (address) { require(_exists(tokenId), "CosmoMasks: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } function setApprovalForAll(address operator, bool approved) public override { require(operator != _msgSender(), "CosmoMasks: approve to caller"); _operatorApprovals[_msgSender()][operator] = approved; emit ApprovalForAll(_msgSender(), operator, approved); } function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } function transferFrom(address from, address to, uint256 tokenId) public override { require(_isApprovedOrOwner(_msgSender(), tokenId), "CosmoMasks: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public override { safeTransferFrom(from, to, tokenId, ""); } function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public override { require(_isApprovedOrOwner(_msgSender(), tokenId), "CosmoMasks: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "CosmoMasks: transfer to non ERC721Receiver implementer"); } function _exists(uint256 tokenId) internal view returns (bool) { return _tokenOwners.contains(tokenId); } function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { require(_exists(tokenId), "CosmoMasks: operator query for nonexistent token"); address owner = ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); } function _safeMint(address to, uint256 tokenId) internal { _safeMint(to, tokenId, ""); } function _safeMint(address to, uint256 tokenId, bytes memory _data) internal { _mint(to, tokenId); require(_checkOnERC721Received(address(0), to, tokenId, _data), "CosmoMasks: transfer to non ERC721Receiver implementer"); } function _mint(address to, uint256 tokenId) internal { require(to != address(0), "CosmoMasks: mint to the zero address"); require(!_exists(tokenId), "CosmoMasks: token already minted"); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(address(0), to, tokenId); } function _burn(uint256 tokenId) internal { address owner = ownerOf(tokenId); _approve(address(0), tokenId); _holderTokens[owner].remove(tokenId); _tokenOwners.remove(tokenId); emit Transfer(owner, address(0), tokenId); } function _transfer(address from, address to, uint256 tokenId) internal { require(ownerOf(tokenId) == from, "CosmoMasks: transfer of token that is not own"); require(to != address(0), "CosmoMasks: transfer to the zero address"); _approve(address(0), tokenId); _holderTokens[from].remove(tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(from, to, tokenId); } function _setBaseURI(string memory baseURI_) internal { _baseURI = baseURI_; } function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) private returns (bool) { if (!to.isContract()) { return true; } bytes memory returndata = to.functionCall(abi.encodeWithSelector( IERC721Receiver(to).onERC721Received.selector, _msgSender(), from, tokenId, _data ), "CosmoMasks: transfer to non ERC721Receiver implementer"); bytes4 retval = abi.decode(returndata, (bytes4)); return (retval == _ERC721_RECEIVED); } function _approve(address to, uint256 tokenId) private { _tokenApprovals[tokenId] = to; emit Approval(ownerOf(tokenId), to, tokenId); } function _setURL(string memory newUrl) internal { _url = newUrl; } // ERC165 function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return _supportedInterfaces[interfaceId]; } function _registerInterface(bytes4 interfaceId) internal virtual { require(interfaceId != 0xffffffff, "CosmoMasks: invalid interface id"); _supportedInterfaces[interfaceId] = true; } }