pragma solidity 0.8.13;








contract Elev8 is ERC721A, Ownable, ReentrancyGuard {
    using Strings for uint256;

    uint256 public constant GENESIS_SUPPLY = 888;
    uint256 public constant MAX_PER_PUBLIC_GENESIS_TX = 5;
    uint256 public constant MAX_PER_GENESIS_ADDRESS = 2;
    uint256 public constant MAX_PER_PUBLIC_MINT_TX = 10;
    uint256 public constant RESERVE_QTY = 18;

    uint256 public publicSalePrice = 0.04 ether;
    uint256 public maxSupply = 8888;

    uint256 public genesisMintCount;
    uint256 public balanceAfterTeamPayments;

    bytes32 public genesisMerkleRoot;

    bool public genesisMintActive;
    bool public genesisPublicMintActive;
    bool public publicMintActive;

    bool public genesisMintsRevealed;
    bool public publicMintsRevealed;

    mapping(address => uint256) public genesisMintCounts;

    string private _baseUri;
    string private _contractURI;
    address[2] private _admins;

    address private constant _PROJECT =
        0x15B31F13D09420c0ADCB3A9AF7be29d6AdCF31BA;
    address private constant _PICO = 
        0x86190c50d1dD3Eeb2Ab1dbb8E7195F2195AF321f;

    bool private _adminsSet = false;
    uint256 private _qtyReserved = 0;

    event GenesisMintStateChanged(address indexed caller, bool indexed active);
    event GenesisPublicMintStateChanged(address indexed caller, bool indexed active);
    event PublicMintStateChanged(address indexed caller, bool indexed active);
    event PublicSalePriceChanged(address indexed caller, uint256 indexed newPrice);
    event MaxSupplyChanged(address indexed caller, uint256 newValue);
    event TeamMemberPayment(
        address indexed recipient,
        uint256 totalSold,
        uint256 baseAmount,
        uint256 paymentAmount
    );

    modifier canActivate(bool active) {
        if (active) {
            require(
                bytes(_baseUri).length > 0,
                "Metadata url has not been set"
            );
        }
        _;
    }

    modifier onlyOwnerOrAdmin() {
        require(
            _msgSender() == owner() ||
                (_adminsSet &&
                    (_msgSender() == _admins[0] || _msgSender() == _admins[1])),
            "Not owner or admin"
        );
        _;
    }

    modifier validProof(bytes32[] calldata merkleProof) {
        require(
            MerkleProof.verify(
                merkleProof, 
                genesisMerkleRoot, 
                keccak256(abi.encodePacked(_msgSender()))
            ), 
            "Invalid proof"
        );
        _;
    }

    constructor(string memory name, string memory symbol)
        ERC721A(name, symbol)
    {
        require(
            bytes(name).length > 0 && bytes(symbol).length > 0,
            "Name and Symbol"
        );
    }

    function genesisMint(uint256 qty, bytes32[] calldata merkleProof) external nonReentrant validProof(merkleProof) {
        require(genesisMintActive, "Genesis minting inactive");
        require(
            genesisMintCount + qty + RESERVE_QTY <= GENESIS_SUPPLY,
            "Exceeds available supply"
        );
        require(genesisMintCounts[_msgSender()] + qty <= MAX_PER_GENESIS_ADDRESS, "Exceeds address quantity limit");

        genesisMintCount += qty;
        genesisMintCounts[_msgSender()] += qty;

        _internalMint(_msgSender(), qty);
    }

    function genesisPublicMint(uint256 qty) external nonReentrant {
        require(
            genesisPublicMintActive, 
            "Genesis public minting inactive"
        );
        require(
            genesisMintCount + qty + RESERVE_QTY <= GENESIS_SUPPLY,
            "Exceeds available supply"
        );
        require(qty <= MAX_PER_PUBLIC_GENESIS_TX, "Exceeds tx quantity limit");

        genesisMintCount += qty;
        _internalMint(_msgSender(), qty);
    }

    function mint(uint256 qty) external payable nonReentrant {
        require(publicMintActive, "Public minting inactive");
        require(msg.value == (publicSalePrice * qty), "Incorrect payment amount");
        require(qty <= MAX_PER_PUBLIC_MINT_TX, "Exceeds tx quantity limit");

        _internalMint(_msgSender(), qty);
    }

    function distributeFromReserve(uint256 qty, address recipient)
        external
        nonReentrant
        onlyOwner
    {
        require(_qtyReserved + qty <= RESERVE_QTY, "Exceeds reserve quantity");

        _qtyReserved += qty;
        _internalMint(recipient, qty);
    }

    function payTeam() external onlyOwnerOrAdmin nonReentrant {
        require(
            address(this).balance > balanceAfterTeamPayments,
            "No payable balance"
        );
        uint256 payableBalance = address(this).balance -
            balanceAfterTeamPayments;

        uint256 paymentAmount = (payableBalance * 10) / 100; // 10%

        // Keep track of the balance after each time the team is paid so we don't pay the team
        // on amounts they've already been paid from
        balanceAfterTeamPayments += (payableBalance - paymentAmount);

        require(payable(_PICO).send(paymentAmount), "Team payment failed");

        emit TeamMemberPayment(
            _PICO,
            totalSupply() - _qtyReserved - genesisMintCount, // This is the non-free mint qty
            payableBalance,
            paymentAmount
        );
    }

    function withdrawToProjectAccount(uint256 amount)
        external
        onlyOwner
        nonReentrant
    {
        require(amount > 0, "Amount cannot be zero");
        require(
            balanceAfterTeamPayments >= amount,
            "Exceeds withdrawable amount"
        );

        balanceAfterTeamPayments -= amount;
        require(payable(_PROJECT).send(amount), "Project withdraw failed");
    }

    function setBaseURI(string calldata baseURI) external onlyOwnerOrAdmin {
        require(bytes(baseURI).length > 0, "baseURI is required");
        _baseUri = baseURI;
    }

    function setAdmins(address[2] calldata adminAddresses) external onlyOwner {
        require(
            adminAddresses[0] != address(0) && adminAddresses[1] != address(0),
            "Invalid address"
        );

        _admins = adminAddresses;
        _adminsSet = true;
    }

    function setGenesisSaleActive(bool active)
        external
        onlyOwnerOrAdmin
        canActivate(active)
    {
        genesisMintActive = active;
        emit GenesisMintStateChanged(_msgSender(), active);
    }

     function setGenesisPublicSaleActive(bool active)
        external
        onlyOwnerOrAdmin
        canActivate(active)
    {
        genesisPublicMintActive = active;
        emit GenesisPublicMintStateChanged(_msgSender(), active);
    }

    function setPublicSaleActive(bool active)
        external
        onlyOwnerOrAdmin
        canActivate(active)
    {
        publicMintActive = active;
        emit GenesisMintStateChanged(_msgSender(), active);
    }

    function setPublicSalePrice(uint256 newPrice) external onlyOwnerOrAdmin {
        // The public sale price cannot be increased after the first public
        // mint, but it can be decreased. Any changes will be communicated beforehand
        require(newPrice <= publicSalePrice || totalSupply() <= GENESIS_SUPPLY, "Cannot increase price");
        publicSalePrice = newPrice;
        emit PublicSalePriceChanged(_msgSender(), newPrice);
    }

    function setMaxSupply(uint256 newValue) external onlyOwnerOrAdmin {
        require(newValue < maxSupply, "Cannot increase supply");
        require(newValue >= totalSupply(), "Total minted exceeds new value");
        maxSupply = newValue;
        emit MaxSupplyChanged(_msgSender(), newValue);
    }

    function setGenesisMerkleRoot(bytes32 merkleRoot) external onlyOwnerOrAdmin {
        genesisMerkleRoot = merkleRoot;
    }

    function revealGenesisMints() external onlyOwnerOrAdmin {
        genesisMintsRevealed = true;
    }

    function revealPublicMints() external onlyOwnerOrAdmin {
        publicMintsRevealed = true;
    }

    /// @dev Returns the tokenIds of the address. O(totalSupply) in complexity.
    /// Inspired by https://github.com/chiru-labs/ERC721A/issues/115#issue-1140835245
    /// Modified to bail out of the loop once we've found the expected number of tokens
    function tokensOfOwner(address owner)
        external
        view
        returns (uint256[] memory)
    {
        unchecked {
            uint256 ownerBalance = balanceOf(owner);
            uint256[] memory a = new uint256[](ownerBalance);
            uint256 end = _currentIndex;
            uint256 tokenIdsIdx;
            address currOwnershipAddr;
            for (uint256 i; i < end; i++) {
                TokenOwnership memory ownership = _ownerships[i];
                if (ownership.burned) {
                    continue;
                }
                if (ownership.addr != address(0)) {
                    currOwnershipAddr = ownership.addr;
                }
                if (currOwnershipAddr == owner) {
                    a[tokenIdsIdx++] = i;
                }
                // No need to continue if we've already collected the expected number
                // of token ids for this owner, so let's bail
                if (tokenIdsIdx == ownerBalance) {
                    break;
                }
            }
            return a;
        }
    }

    function burn(uint256 tokenId) public {
        require(_ownerships[tokenId].addr == _msgSender(), "Not owner");
        _burn(tokenId);
    }

    function setContractURI(string memory newUri) public onlyOwner {
        _contractURI = newUri;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        require(_exists(tokenId), "Nonexistent tokenId");
        return
            string(
                abi.encodePacked(_baseUri, "/", tokenId.toString())
            );
    }

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

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

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

    /// @notice By default, ERC721A starts minting from 0, which is undesirable in our case, so we'll
    /// override that to start from 1
    function _startTokenId() internal view virtual override returns (uint256) {
        return 1;
    }

    function _internalMint(address recipient, uint256 qty) private {
        require(qty > 0, "Quantity cannot be zero");
        require(
            (totalSupply() + qty + (RESERVE_QTY - _qtyReserved)) <= maxSupply,
            "Exceeds total supply"
        );
        _safeMint(recipient, qty);
    }
}
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"bool","name":"active","type":"bool"}],"name":"GenesisMintStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"bool","name":"active","type":"bool"}],"name":"GenesisPublicMintStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"MaxSupplyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"bool","name":"active","type":"bool"}],"name":"PublicMintStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"PublicSalePriceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"TeamMemberPayment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"GENESIS_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PER_GENESIS_ADDRESS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PER_PUBLIC_GENESIS_TX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PER_PUBLIC_MINT_TX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESERVE_QTY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"balanceAfterTeamPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"qty","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"distributeFromReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesisMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"qty","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"genesisMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesisMintActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genesisMintCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"genesisMintCounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genesisMintsRevealed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"qty","type":"uint256"}],"name":"genesisPublicMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesisPublicMintActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"qty","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payTeam","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"publicMintActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMintsRevealed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicSalePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revealGenesisMints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revealPublicMints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[2]","name":"adminAddresses","type":"address[2]"}],"name":"setAdmins","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newUri","type":"string"}],"name":"setContractURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"setGenesisMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"active","type":"bool"}],"name":"setGenesisPublicSaleActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"active","type":"bool"}],"name":"setGenesisSaleActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"active","type":"bool"}],"name":"setPublicSaleActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"setPublicSalePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToProjectAccount","outputs":[],"stateMutability":"nonpayable","type":"function"}]