contract SniftieERC721Common is Context, AccessControlEnumerable, ERC721Enumerable, ERC721Burnable, ERC721Pausable, Ownable {
    using Counters for Counters.Counter;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    Counters.Counter private _tokenIdTracker;
    mapping(string => uint8) hashes;

    string private _baseTokenURI;
    string private _contractMetadataURI;
    mapping (uint256 => string) private _tokenURIs;

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * Token URIs will be autogenerated based on `baseURI` and their token IDs.
     * See {ERC721-tokenURI}.
     */
    constructor(string memory name, string memory symbol, string memory baseTokenURI, string memory contractMetadataURI, address sniftieAdmin) ERC721(name, symbol) {
        _baseTokenURI = baseTokenURI;
        _contractMetadataURI = contractMetadataURI;

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(DEFAULT_ADMIN_ROLE, sniftieAdmin);

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, sniftieAdmin);

        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, sniftieAdmin);
    }

    function contractURI() public view returns (string memory) {
        return _contractMetadataURI;
    }

    function setContractMetadataURI(string memory contractMetadataURI) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Must have DEFAULT_ADMIN_ROLE to set contract metadata uri");
        _contractMetadataURI = contractMetadataURI;
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function setBaseURI(string memory baseURI) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Must have DEFAULT_ADMIN_ROLE to set base uri");
        _baseTokenURI = baseURI;
    }

    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "URI query for nonexistent token");

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }

        if (bytes(_tokenURI).length > 0) {
            if (SniftieUtils.stringContains("://", _tokenURI)) return _tokenURI; // _tokenURI is absolute (e.g. http://domain.com/metadata/1)
            return string(abi.encodePacked(base, _tokenURI)); // _tokenURI is an identifier so concatenate with base uri
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(base, Strings.toString(tokenId)));
    }

    function setTokenURI(uint256 tokenId, string memory tokenUri) public {
        require(_exists(tokenId), "Cannot set metadata hash for nonexistent token");
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Cannot set metadata hash if not admin");

        _setTokenURI(tokenId, tokenUri);
    }

    // this function is needed in children contracts that do not use the standard mint function and
    // also do not set the media hash upon minting (a token's media hash must be unique)
    function setTokenMediaHash(uint tokenId, string memory mediaHashOrId) public {
        require(_exists(tokenId), "Cannot set media hash for nonexistent token");
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Cannot set media hash if not admin");
        require(hashes[mediaHashOrId] != 1, "Hash value has already been used");

        hashes[mediaHashOrId] = 1;
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC721Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC721Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause");
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721, ERC721Enumerable, ERC721Pausable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721, ERC721Enumerable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }
}
function pause() public virtual {
    require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause");
    _pause();
}

/**
 * @dev Pauses all token transfers.
 *
 * See {ERC721Pausable} and {Pausable-_pause}.
 *
 * Requirements:
 *
 * - the caller must have the `PAUSER_ROLE`.
 */