contract Doggo is ERC721A, Ownable, Pausable, PreSaleAware, ReentrancyGuard {
	using Strings for uint256;

	uint256 public constant COST = 0.08 ether; // the cost per doggo

	// the merkle root used for verifying proofs during presale
	bytes32 public constant PRE_SALE_MERKLE_ROOT = 0x9f93df4f70710b1cc9fa86ee3c87cc30f77a6709363ccd6bdddeae109330be37;

	// packing the next variables as uint16 (max 65535, 2 bytes each, less than 32 bytes = 1 slot)
	uint16 public constant MAX_SUPPLY = 8888; // the max total supply
	uint16 public constant MAX_PUBLIC_SUPPLY = 8388; // the max public supply
	uint16 public constant MAX_MINT_AMOUNT = 10; // the max amount an address can mint
	uint16 public constant PRE_SALE_MAX_MINT_AMOUNT = 5; // the max amount an address can mint during presale

	// the address of the DAO treasury that will receive percentage of all withdrawls
	address public constant DAO_TREASURY_ADDRESS = 0x8C3b934E904b25455F53eFC2beCA8693406E15b1;

	string public hiddenMetadataUri;
	string public uriPrefix = "";

	bool public revealed = false; // determines whether the artwork is revealed

	constructor() ERC721A("Doggo Verse", "DV", 500) {
		setHiddenMetadataUri("ipfs://QmU3iVT84VGGY5T2jDfgTzqG8VtcRPdtND5Ma6568Pgfhs/unreaveled");
	}

	/*
	 * Base URI for computing {tokenURI}. The resulting URI for each token will be
	 * the concatenation of the `baseURI` and the `tokenId`.
	 */
	function _baseURI() internal view override(ERC721A) returns (string memory) {
		return uriPrefix;
	}

	function tokenURI(uint256 _tokenId) public view override(ERC721A) returns (string memory) {
		require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");

		if (revealed == false) {
			return hiddenMetadataUri;
		}

		string memory currentBaseURI = _baseURI();
		return bytes(currentBaseURI).length > 0 ? string(abi.encodePacked(currentBaseURI, _tokenId.toString())) : "";
	}

	// The following functions are available only to the owner

	/*
	 * @dev Pauses the contract.
	 */
	function pause() public onlyOwner {
		_pause();
	}

	/*
	 * @dev Resumes the contract.
	 */
	function resume() public onlyOwner {
		_resume();
	}

	/*
	 * @dev Disables presale.
	 */
	function disablePreSale() public onlyOwner {
		_disablePreSale();
	}

	/*
	 * @dev Enables presale.
	 */
	function enablePreSale() public onlyOwner {
		_enablePreSale();
	}

	/*
	 * @dev Withdraws the contracts balance to owner and DAO.
	 */
	function withdraw() public onlyOwner {
		// Reserve 30% for DAO treasury
		(bool hs, ) = payable(DAO_TREASURY_ADDRESS).call{ value: (address(this).balance * 30) / 100 }("");
		require(hs, "DAO tranfer failed");

		// owner only
		(bool os, ) = payable(owner()).call{ value: address(this).balance }("");
		require(os, "owner transfer failed");
	}

	/*
	 * @dev Reveals the artwork.
	 */
	function reveal() public onlyOwner {
		revealed = true;
	}

	/*
	 * @dev Hides the artwork.
	 */
	function hide() public onlyOwner {
		revealed = false;
	}

	/*
	 * @dev Sets uri prefix for revealed tokens
	 */
	function setUriPrefix(string memory _uriPrefix) public onlyOwner {
		uriPrefix = _uriPrefix;
	}

	/*
	 * @dev Sets uri prefix for hidden tokens
	 */
	function setHiddenMetadataUri(string memory _hiddenMetadataUri) public onlyOwner {
		hiddenMetadataUri = _hiddenMetadataUri;
	}

	/*
	 * @dev Mints a doggo to an address
	 */
	function _mintDoggo(address to, uint256 quantity) private {
		_safeMint(to, quantity);
	}

	/*
	 * @dev Mints doggos when presale is enabled.
	 */
	function presaleMintDoggo(uint256 quantity, bytes32[] calldata merkleProof)
		external
		payable
		whenNotPaused
		whenPreSale
		nonReentrant
	{
		require(totalSupply() + quantity < MAX_SUPPLY, "max supply reached");

		if (_msgSender() != owner()) {
			require(totalSupply() + quantity < MAX_PUBLIC_SUPPLY, "max public supply reached");
			require(
				MerkleProof.verify(merkleProof, PRE_SALE_MERKLE_ROOT, keccak256(abi.encodePacked(_msgSender()))),
				"Address not on the list"
			);
			require(_numberMinted(_msgSender()) + quantity <= PRE_SALE_MAX_MINT_AMOUNT, "presale mint limit reached");
			require(msg.value >= COST * quantity, "incorrect ether value");
		}

		_mintDoggo(_msgSender(), quantity);
	}

	/*
	 * @dev Mints doggos when presale is disabled.
	 */
	function mintDoggo(uint256 quantity) external payable whenNotPaused whenNotPreSale nonReentrant {
		require(totalSupply() + quantity < MAX_SUPPLY, "max supply reached");

		if (_msgSender() != owner()) {
			require(totalSupply() + quantity < MAX_PUBLIC_SUPPLY, "max public supply reached");
			require(_numberMinted(_msgSender()) + quantity <= MAX_MINT_AMOUNT, "mint limit reached");
			require(msg.value >= COST * quantity, "incorrect ether value");
		}

		_mintDoggo(_msgSender(), quantity);
	}

	/*
	 * @dev Returns the number of tokens minted by an address
	 */
	function numberMinted(address _owner) public view returns (uint256) {
		return _numberMinted(_owner);
	}

	// The following functions are overrides required by Solidity.

	function supportsInterface(bytes4 interfaceId) public view override(ERC721A) returns (bool) {
		return super.supportsInterface(interfaceId);
	}
}
/*
 `..:`.
 ``.-/.::/..```.-----:-`
 ```....````.-.`    `...-:+------..:.
 `.:-..``..---`             ./+//-`./`+
 `/.      ```                  :: /-`+`+
 `+`      .::+                   /. :+::`
 .:      `/ -+                   `+
 `/      /. `o                    /.`
 ::`    +  `o                    `:-:`
 `:---:- `+:                      ``::`
 ````` `o.                          `+
 -:                            :-
 +`                            :-
 -:                           `+`
 `/-                        `-:`
 `::.         `..-:---...-:-.`
 `-:-.....-+-..//`.....`
 `......+  -/
 `.::-.  .:-`
 `-:-`       .::`
 `./-            `:-`
 `://`             `+/`
 `/:/`               .++`
 `+-/                  .+/
 
 ________  ________  ________  ________  ________          ___      ___ _______   ________  ________  _______
 |\   ___ \|\   __  \|\   ____\|\   ____\|\   __  \        |\  \    /  /|\  ___ \ |\   __  \|\   ____\|\  ___ \
 \ \  \_|\ \ \  \|\  \ \  \___|\ \  \___|\ \  \|\  \       \ \  \  /  / | \   __/|\ \  \|\  \ \  \___|\ \   __/|
 \ \  \ \\ \ \  \\\  \ \  \  __\ \  \  __\ \  \\\  \       \ \  \/  / / \ \  \_|/_\ \   _  _\ \_____  \ \  \_|/__
 \ \  \_\\ \ \  \\\  \ \  \|\  \ \  \|\  \ \  \\\  \       \ \    / /   \ \  \_|\ \ \  \\  \\|____|\  \ \  \_|\ \
 \ \_______\ \_______\ \_______\ \_______\ \_______\       \ \__/ /     \ \_______\ \__\\ _\ ____\_\  \ \_______\
 \|_______|\|_______|\|_______|\|_______|\|_______|        \|__|/       \|_______|\|__|\|__|\_________\|_______|
 \|_________|
 */
function numberMinted(address _owner) public view returns (uint256) {
		return _numberMinted(_owner);
	}
/*
 	 * @dev Returns the number of tokens minted by an address
 	 */