contract BoredApeYachtClub is ERC721, Ownable {
using SafeMath for uint256;

string public BAYC_PROVENANCE = "";

uint256 public startingIndexBlock;

uint256 public startingIndex;

uint256 public constant apePrice = 80000000000000000; //0.08 ETH

uint public constant maxApePurchase = 20;

uint256 public MAX_APES;

bool public saleIsActive = false;

uint256 public REVEAL_TIMESTAMP;

constructor(string memory name, string memory symbol, uint256 maxNftSupply, uint256 saleStart) ERC721(name, symbol) {
MAX_APES = maxNftSupply;
REVEAL_TIMESTAMP = saleStart + (86400 * 9);
}

function withdraw() public onlyOwner {
uint balance = address(this).balance;
msg.sender.transfer(balance);
}

/**
 * Set some Bored Apes aside
 */
function reserveApes() public onlyOwner {
uint supply = totalSupply();
uint i;
for (i = 0; i < 30; i++) {
_safeMint(msg.sender, supply + i);
}
}

/**
 * DM Gargamel in Discord that you're standing right behind him.
 */
function setRevealTimestamp(uint256 revealTimeStamp) public onlyOwner {
REVEAL_TIMESTAMP = revealTimeStamp;
}

/*
* Set provenance once it's calculated
*/
function setProvenanceHash(string memory provenanceHash) public onlyOwner {
BAYC_PROVENANCE = provenanceHash;
}

function setBaseURI(string memory baseURI) public onlyOwner {
_setBaseURI(baseURI);
}

/*
* Pause sale if active, make active if paused
*/
function flipSaleState() public onlyOwner {
saleIsActive = !saleIsActive;
}

/**
* Mints Bored Apes
*/
function mintApe(uint numberOfTokens) public payable {
require(saleIsActive, "Sale must be active to mint Ape");
require(numberOfTokens <= maxApePurchase, "Can only mint 20 tokens at a time");
require(totalSupply().add(numberOfTokens) <= MAX_APES, "Purchase would exceed max supply of Apes");
require(apePrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");

for(uint i = 0; i < numberOfTokens; i++) {
uint mintIndex = totalSupply();
if (totalSupply() < MAX_APES) {
_safeMint(msg.sender, mintIndex);
}
}

// If we haven't set the starting index 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
if (startingIndexBlock == 0 && (totalSupply() == MAX_APES || block.timestamp >= REVEAL_TIMESTAMP)) {
startingIndexBlock = block.number;
}
}

/**
 * Set the starting index for the collection
 */
function setStartingIndex() public {
require(startingIndex == 0, "Starting index is already set");
require(startingIndexBlock != 0, "Starting index block must be set");

startingIndex = uint(blockhash(startingIndexBlock)) % MAX_APES;
// Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
if (block.number.sub(startingIndexBlock) > 255) {
startingIndex = uint(blockhash(block.number - 1)) % MAX_APES;
}
// Prevent default sequence
if (startingIndex == 0) {
startingIndex = startingIndex.add(1);
}
}

/**
 * Set the starting index block for the collection, essentially unblocking
 * setting starting index
 */
function emergencySetStartingIndexBlock() public onlyOwner {
require(startingIndex == 0, "Starting index is already set");

startingIndexBlock = block.number;
}
}
/**
 * @title BoredApeYachtClub contract
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function emergencySetStartingIndexBlock() public onlyOwner {
require(startingIndex == 0, "Starting index is already set");

startingIndexBlock = block.number;
}

/**
 * Set the starting index block for the collection, essentially unblocking
 * setting starting index
 */