// Contract: SolaStars
// Target function: function isWalletAuthorisedToMint(address _wallet, bytes memory _signature, string memory _id, string memory _timestamp, string memory _sale) public pure returns(bool)
 public view virtual returns (string memory) {
		return _baseURI;
	}

	/**
	 * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
	 */
	function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
		return _holderTokens[owner].at(index);
	}

	/**
	 * @dev See {IERC721Enumerable-totalSupply}.
	 */
	function totalSupply() public view virtual override returns (uint256) {
		// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
		return _tokenOwners.length();
	}

	/**
	 * @dev See {IERC721Enumerable-tokenByIndex}.
	 */
	function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
		(uint256 tokenId, ) = _tokenOwners.at(index);
		return tokenId;
	}

	/**
	 * @dev See {IERC721-approve}.
	 */
	function approve(address to, uint256 tokenId) public virtual override {
		address owner = ERC721.ownerOf(tokenId);
		require(to != owner, "ERC721: approval to current owner");

		require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
			"ERC721: approve caller is not owner nor approved for all"
		);

		_approve(to, tokenId);
	}

	/**
	 * @dev See {IERC721-getApproved}.
	 */
	function getApproved(uint256 tokenId) public view virtual override returns (address) {
		require(_exists(tokenId), "ERC721: approved query for nonexistent token");

		return _tokenApprovals[tokenId];
	}

	/**
	 * @dev See {IERC721-setApprovalForAll}.
	 */
	function setApprovalForAll(address operator, bool approved) public virtual override {
		require(operator != _msgSender(), "ERC721: approve to caller");

		_operatorApprovals[_msgSender()][operator] = approved;
		emit ApprovalForAll(_msgSender(), operator, approved);
	}

	/**
	 * @dev See {IERC721-isApprovedForAll}.
	 */
	function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
		return _operatorApprovals[owner][operator];
	}

	/**
	 * @dev See {IERC721-transferFrom}.
	 */
	function transferFrom(address from, address to, uint256 tokenId) public virtual override {
		//solhint-disable-next-line max-line-length
		require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

		_transfer(from, to, tokenId);
	}

	/**
	 * @dev See {IERC721-safeTransferFrom}.
	 */
	function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
		safeTransferFrom(from, to, tokenId, "");
	}

	/**
	 * @dev See {IERC721-safeTransferFrom}.
	 */
	function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
		require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
		_safeTransfer(from, to, tokenId, _data);
	}

	/**
	 * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
	 * are aware of the ERC721 protocol to prevent tokens from being forever locked.
	 *
	 * `_data` is additional data, it has no specified format and it is sent in call to `to`.
	 *
	 * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
	 * implement alternative mechanisms to perform token transfer, such as signature-based.
	 *
	 * Requirements:
	 *
	 * - `from` cannot be the zero address.
	 * - `to` cannot be the zero address.
	 * - `tokenId` token must exist and be owned by `from`.
	 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
	 *
	 * Emits a {Transfer} event.
	 */
	function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
		_transfer(from, to, tokenId);
		require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
	}

	/**
	 * @dev Returns whether `tokenId` exists.
	 *
	 * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
	 *
	 * Tokens start existing when they are minted (`_mint`),
	 * and stop existing when they are burned (`_burn`).
	 */
	function _exists(uint256 tokenId) internal view virtual returns (bool) {
		return _tokenOwners.contains(tokenId);
	}

	/**
	 * @dev Returns whether `spender` is allowed to manage `tokenId`.
	 *
	 * Requirements:
	 *
	 * - `tokenId` must exist.
	 */
	function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
		require(_exists(tokenId), "ERC721: operator query for nonexistent token");
		address owner = ERC721.ownerOf(tokenId);
		return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
	}

	/**
	 * @dev Safely mints `tokenId` and transfers it to `to`.
	 *
	 * Requirements:
	 d*
	 * - `tokenId` must not exist.
	 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
	 *
	 * Emits a {Transfer} event.
	 */
	function _safeMint(address to, uint256 tokenId) internal virtual {
		_safeMint(to, tokenId, "");
	}

	/**
	 * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
	 * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
	 */
	function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
		_mint(to, tokenId);
		require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
	}

	/**
	 * @dev Mints `tokenId` and transfers it to `to`.
	 *
	 * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
	 *
	 * Requirements:
	 *
	 * - `tokenId` must not exist.
	 * - `to` cannot be the zero address.
	 *
	 * Emits a {Transfer} event.
	 */
	function _mint(address to, uint256 tokenId) internal virtual {
		require(to != address(0), "ERC721: mint to the zero address");
		require(!_exists(tokenId), "ERC721: token already minted");

		_beforeTokenTransfer(address(0), to, tokenId);

		_holderTokens[to].add(tokenId);

		_tokenOwners.set(tokenId, to);

		emit Transfer(address(0), to, tokenId);
	}

	/**
	 * @dev Destroys `tokenId`.
	 * The approval is cleared when the token is burned.
	 *
	 * Requirements:
	 *
	 * - `tokenId` must exist.
	 *
	 * Emits a {Transfer} event.
	 */
	function _burn(uint256 tokenId) internal virtual {
		address owner = ERC721.ownerOf(tokenId); // internal owner

		_beforeTokenTransfer(owner, address(0), tokenId);

		// Clear approvals
		_approve(address(0), tokenId);

		// Clear metadata (if any)
		if (bytes(_tokenURIs[tokenId]).length != 0) {
			delete _tokenURIs[tokenId];
		}

		_holderTokens[owner].remove(tokenId);

		_tokenOwners.remove(tokenId);

		emit Transfer(owner, address(0), tokenId);
	}

	/**
	 * @dev Transfers `tokenId` from `from` to `to`.
	 *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
	 *
	 * Requirements:
	 *
	 * - `to` cannot be the zero address.
	 * - `tokenId` token must be owned by `from`.
	 *
	 * Emits a {Transfer} event.
	 */
	function _transfer(address from, address to, uint256 tokenId) internal virtual {
		require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
		require(to != address(0), "ERC721: transfer to the zero address");

		_beforeTokenTransfer(from, to, tokenId);

		// Clear approvals from the previous owner
		_approve(address(0), tokenId);

		_holderTokens[from].remove(tokenId);
		_holderTokens[to].add(tokenId);

		_tokenOwners.set(tokenId, to);

		emit Transfer(from, to, tokenId);
	}

	/**
	 * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
	 *
	 * Requirements:
	 *
	 * - `tokenId` must exist.
	 */
	function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
		require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
		_tokenURIs[tokenId] = _tokenURI;
	}

	/**
	 * @dev Internal function to set the base URI for all token IDs. It is
	 * automatically added as a prefix to the value returned in {tokenURI},
	 * or to the token ID if {tokenURI} is empty.
	 */
	function _setBaseURI(string memory baseURI_) internal virtual {
		_baseURI = baseURI_;
	}

	/**
	 * @dev Internal function to invoke {IERC721Receiver-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 bool whether the call correctly returned the expected magic value
	 */
	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
		), "ERC721: transfer to non ERC721Receiver implementer");
		bytes4 retval = abi.decode(returndata, (bytes4));
		return (retval == _ERC721_RECEIVED);
	}

	/**
	 * @dev Approve `to` to operate on `tokenId`
	 *
	 * Emits an {Approval} event.
	 */
	function _approve(address to, uint256 tokenId) internal virtual {
		_tokenApprovals[tokenId] = to;
		emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
	}

	/**
	 * @dev Hook that is called before any token transfer. This includes minting
	 * and burning.
	 *
	 * Calling conditions:
	 *
	 * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
	 * transferred to `to`.
	 * - When `from` is zero, `tokenId` will be minted for `to`.
	 * - When `to` is zero, ``from``'s `tokenId` will be burned.
	 * - `from` cannot be the zero address.
	 * - `to` cannot be the zero address.
	 *
	 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
	 */
	function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}


/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
	/**
	 * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
	 * by `operator` from `from`, this function is called.
	 *
	 * It must return its Solidity selector to confirm the token transfer.
	 * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
	 *
	 * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
	 */
	function onERC721Received(
		address operator,
		address from,
		uint256 tokenId,
		bytes calldata data
	) external returns (bytes4);
}


/**
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 *
 */
contract SolaStars is ERC721, ReentrancyGuard, Ownable
{
	using SafeMath for uint256;

	uint256 public max_tokens_per_transaction = 2;
	uint256 public token_price = 200000000000000000;
	uint256 public max_token_supply = 2000;
	uint256 public starting_index_block;
	uint256 public starting_index;

	bool public whitelist_sale_active = false;
	bool public public_raffle_sale_active = false;
	bool public general_sale_active = false;

	string public provenance_hash = "";


	/**
	 * @dev Nothing to see here.
	 */
	constructor() ERC721("The SolaVerse: SOLA-STARS", "SOLA-STAR") {}


	/**
	 * @dev Whitelisted method to mint a free token.
	 *
	 * @param _signature bytes memory
	 * @param _id string memory
	 * @param _timestamp string memory
	 */
	function mintStarWhitelist(bytes memory _signature, string memory _id, string memory _timestamp) public nonReentrant
	{
		require(whitelist_sale_active, "Whitelist not open");
		require(isWalletAuthorisedToMint(msg.sender, _signature, _id, _timestamp, "Whitelist"), "Not authorised to mint");
		require(IERC721(address(this)).balanceOf(msg.sender) == 0, "Wallet has already minted");

		maybeMintTokens(1);
	}


	/**
	 * @dev Whitelisted method to mint a free token and one additional token at normal price.
	 *
	 * @param _signature bytes memory
	 * @param _id string memory
	 * @param _timestamp string memory
	 */
	function mintStarsWhitelist(bytes memory _signature, string memory _id, string memory _timestamp) public payable nonReentrant
	{
		require(whitelist_sale_active, "Whitelist not open");
		require(msg.value >= token_price, "Not enough ETH");
		require(isWalletAuthorisedToMint(msg.sender, _signature, _id, _timestamp, "Whitelist"), "Not authorised to mint");
		require(IERC721(address(this)).balanceOf(msg.sender) == 0, "Wallet has already minted");

		maybeMintTokens(2);
	}


	/**
	 * @dev Public payable method to mint the number of tokens requested based on the results of the public raffle.
	 *
	 * @param _num_tokens uint256
	 * @param _signature bytes memory
	 * @param _id string memory
	 * @param _timestamp string memory
	 */
	function mintStarRaffle(uint256 _num_tokens, bytes memory _signature, string memory _id, string memory _timestamp) public payable nonReentrant
	{
		require(public_raffle_sale_active, "Public Raffle not open");
		require(msg.value >= (token_price * _num_tokens), "Not enough ETH");
		require(isWalletAuthorisedToMint(msg.sender, _signature, _id, _timestamp, "Public-Raffle"), "Not authorised to mint");
		require(IERC721(address(this)).balanceOf(msg.sender) == 0, "Wallet has already minted");

		maybeMintTokens(_num_tokens);
	}


	/**
	 * @dev Public payable method to mint the number of tokens requested.
	 *
	 * @param _num_tokens uint256
	 */
	function mintStarPublic(uint256 _num_tokens) public payable nonReentrant
	{
		require(general_sale_active, "General sale not open");
		require(msg.value >= (token_price * _num_tokens), "Not enough ETH");

		maybeMintTokens(_num_tokens);
	}


	/**
	 * @dev Mint the requested number of tokens if there's enough available.

	 * @dev If we haven't set the starting index yet and this is either 1) the last saleable token or 2) the first token to be sold after the end of pre-sale, set the starting index block.
	 */
	function maybeMintTokens(uint _num_tokens) internal
	{
		require((_num_tokens > 0) && (_num_tokens <= max_tokens_per_transaction), "Cannot mint that many tokens");
		require(totalSupply().add(_num_tokens) <= max_token_supply, "Exceeds max tokens available");

		for (uint256 i=0; i<_num_tokens; i++)
		{
			uint256 new_token_id = totalSupply();
			_safeMint(msg.sender, new_token_id);
		}

		if (starting_index_block == 0 && (totalSupply() == max_token_supply))
		{
			starting_index_block = block.number;
		}
	}