contract ASF is ERC721A, Ownable, ReentrancyGuard, ControlledAccess {
    using Strings for uint256; 
    /** Contract Functionality Variables */
    uint256 public constant maxPublicMintPerAddress = 10;
    uint256 public constant maxPresaleMintPerAddress = 5;
    uint256 public constant mintPrice = 0.042 ether;
    uint256 public constant whitelistMintPrice = 0.0169 ether;
    bool public publicSaleActive = false;
    bool public presaleActive = false;

    /** URI Variables */
    bytes32 public uriSuffix = ".json";
    string private _baseTokenURI = "";
    string public hiddenMetadataUri = "ipfs://QmVJf7dx8h9c7FPBN17LoSzRNNHjdgnvXqUG9KG3y8yT7F/hidden.json";

    /** Constructor - initialize the contract by setting the name, symbol, 
        max amount an address can mint, and the total collection size. */
    constructor() ERC721A( "Alien Scum Fleet", "ASF", maxPublicMintPerAddress, 6969) {
    }

    /** Modifier - ensures the function caller is the user */
    modifier callerIsUser() {
        require(tx.origin == msg.sender, "Caller is another contract");
        _;
    }

    /** Modifier - ensures all minting requirements are met, used in both public and presale 
        mint functions. Structure allows mintCompliance input values (_quantity, _maxPerAddress 
        and _startTime) to be function specific */
    modifier mintCompliance(uint256 _quantity, uint256 _maxPerAddress) {
        require(totalSupply() + _quantity <= collectionSize, "Max supply reached");
        require(_quantity >= 0 && _quantity <= _maxPerAddress, "Invalid mint amount"); 
        require(numberMinted(msg.sender) + _quantity <= _maxPerAddress, "Can not mint this many");
        _;
    }

    /** Public Mint Function */
    function mint(uint256 quantity)
        external
        payable
        callerIsUser
        nonReentrant
        mintCompliance(quantity, maxPublicMintPerAddress) /** Mint Compliance for Public Sale */
    {
        require(publicSaleActive, "Public sale is not live.");
        _safeMint(msg.sender, quantity);
        refundIfOver(quantity * mintPrice);
    }

    /** Presale Mint Function */
    function presaleMint(uint256 quantity, bytes32 _r, bytes32 _s, uint8 _v)
        external
        payable
        callerIsUser
        onlyValidAccess(_r, _s, _v) /** Whitelist */
        nonReentrant 
        mintCompliance(quantity, maxPresaleMintPerAddress) /** Mint Compliance for Presale */
    {
        require(presaleActive, "Presale is not live.");
        _safeMint(msg.sender, quantity);
        refundIfOver(quantity * whitelistMintPrice);
    }

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

    /** Total number of NFTs minted from the contract for a given address. Value can only increase and
        does not depend on how many NFTs are in your wallet */
    function numberMinted(address owner) public view returns (uint256) {
        return _numberMinted(owner);
    }

    /** Get the owner of a specific token from the tokenId */
    function getOwnershipData(uint256 tokenId)
        external
        view
        returns (TokenOwnership memory)
    {
        return ownershipOf(tokenId);
    }

    /**  Refund function which requires the minimum amount for the transaction and returns any extra payment to the sender */
    function refundIfOver(uint256 price) private {
        require(msg.value >= price, "Need to send more eth");
        if (msg.value > price) {
            payable(msg.sender).transfer(msg.value - price);
        }
    }

    /**  Standard TokenURI ERC721A function modified to return hidden metadata
         URI until the contract is revealed. */
    function tokenURI(uint256 _tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        require(_exists(_tokenId), "Nonexistent token!");

        if (keccak256(abi.encodePacked(_baseTokenURI)) == keccak256(abi.encodePacked(""))) {
            return hiddenMetadataUri;
        }

        return
            bytes(_baseTokenURI).length > 0
                ? string(
                    abi.encodePacked(_baseTokenURI, _tokenId.toString(), uriSuffix)
                )
                : "";
    }

/// OWNER FUNCTIONS ///

    /** Standard withdraw function for the owner to pull the contract */
    function withdraw() external onlyOwner nonReentrant {
        uint256 sendAmount = address(this).balance;

        address lawi = payable(0xdC2719e36B1028Ad840B21Cd08D4F395c09A7015); 
        address bunzo = payable(0x987F613dD3460bDCEd24Eeebfe13dE9Bf9D8d2B8); 
        address robatic = payable(0xDd730Aa1583396214Aa2D92Cfa92880b9d93E201); 
        address lashes = payable(0x141cA54Db6F8277917e0554b30F2B3270F65EB67); 
        address willy = payable(0x4Bd3BB6B1D03c8844476e525fF291627FbC3c0eA); 
        address yeti = payable(0x66c17Dcef1B364014573Ae0F869ad1c05fe01c89); 
        address dale = payable(0x64e293dd4BBA0756895cdB8FE3a2f8E2f4AD4071); 
        address marv = payable(0xEcf02b27e4f6Ff7E3C609652DE4B37F65A74d86B); 
        address community = payable(0xA868Ce0B203031B2E72330e8512c8729770c0759); 

        bool success;
        (success, ) = lawi.call{value: ((sendAmount * 1575) / 10000)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = bunzo.call{value: ((sendAmount * 1575) / 10000)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = robatic.call{value: ((sendAmount * 1575) / 10000)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = lashes.call{value: ((sendAmount * 1575) / 10000)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = willy.call{value: ((sendAmount * 3) / 100)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = yeti.call{value: ((sendAmount * 12) / 100)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = dale.call{value: ((sendAmount * 1) / 100)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = marv.call{value: ((sendAmount * 1) / 100)}("");
        require(success, "Transaction unsuccessful");
        (success, ) = community.call{value: ((sendAmount * 20) / 100)}("");
        require(success, "Transaction unsuccessful");
    }

    /** Mint Function only usable by contract owner. Use reserved for giveaways and promotions. */
    function ownerMint(address to, uint256 quantity) public callerIsUser onlyOwner {
        require(quantity + totalSupply() <= collectionSize, 'Max supply reached');
        _safeMint(to, quantity);
    }

    /** Function for updating the revealed token metadata URI. When setting this value,
        only replace _CID_ in the following:  ipfs://_CID_/                          */
    function setBaseURI(string memory baseURI) public onlyOwner {
        _baseTokenURI = baseURI;
    }

    /** Initialized in constructor - Hidden metadata value pointing to unrevealed token URI. */
    function setHiddenMetadataURI(string memory _hiddenMetadataURI) public onlyOwner {
        hiddenMetadataUri = _hiddenMetadataURI;
    }

    function togglePresaleActive() public onlyOwner {
        presaleActive = !presaleActive;
    }

    function togglePublicSaleActive() public onlyOwner {
        publicSaleActive = !publicSaleActive;
    }

   /** adding onlyOwner and nonReentrant modifiers to ERC721A setOwnersExplicit for enhanced security */
    function setOwnersExplicit(uint256 quantity)
        external
        onlyOwner
        nonReentrant
    {
        _setOwnersExplicit(quantity);
    }

}
function withdraw() external onlyOwner nonReentrant {
    uint256 sendAmount = address(this).balance;

    address lawi = payable(0xdC2719e36B1028Ad840B21Cd08D4F395c09A7015); 
    address bunzo = payable(0x987F613dD3460bDCEd24Eeebfe13dE9Bf9D8d2B8); 
    address robatic = payable(0xDd730Aa1583396214Aa2D92Cfa92880b9d93E201); 
    address lashes = payable(0x141cA54Db6F8277917e0554b30F2B3270F65EB67); 
    address willy = payable(0x4Bd3BB6B1D03c8844476e525fF291627FbC3c0eA); 
    address yeti = payable(0x66c17Dcef1B364014573Ae0F869ad1c05fe01c89); 
    address dale = payable(0x64e293dd4BBA0756895cdB8FE3a2f8E2f4AD4071); 
    address marv = payable(0xEcf02b27e4f6Ff7E3C609652DE4B37F65A74d86B); 
    address community = payable(0xA868Ce0B203031B2E72330e8512c8729770c0759); 

    bool success;
    (success, ) = lawi.call{value: ((sendAmount * 1575) / 10000)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = bunzo.call{value: ((sendAmount * 1575) / 10000)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = robatic.call{value: ((sendAmount * 1575) / 10000)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = lashes.call{value: ((sendAmount * 1575) / 10000)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = willy.call{value: ((sendAmount * 3) / 100)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = yeti.call{value: ((sendAmount * 12) / 100)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = dale.call{value: ((sendAmount * 1) / 100)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = marv.call{value: ((sendAmount * 1) / 100)}("");
    require(success, "Transaction unsuccessful");
    (success, ) = community.call{value: ((sendAmount * 20) / 100)}("");
    require(success, "Transaction unsuccessful");
}

/** Standard withdraw function for the owner to pull the contract */