// File: @lambdalf-dev/ethereum-contracts/contracts/interfaces/IContractState.sol



// SPDX-License-Identifier: MIT

/**
* Author: Lambdalf the White
*/

pragma solidity 0.8.17;

interface IContractState {
  /**
  * @dev Thrown when a function is called with the wrong contract state.
  * 
  * @param currentState the current state of the contract
  */
  error ContractState_INCORRECT_STATE(uint8 currentState);
  /**
  * @dev Thrown when trying to set the contract state to an invalid value.
  * 
  * @param invalidState the invalid contract state
  */
  error ContractState_INVALID_STATE(uint8 invalidState);

  /**
  * @dev Emitted when the sale state changes
  * 
  * @param previousState the previous state of the contract
  * @param newState the new state of the contract
  */
  event ContractStateChanged(uint8 indexed previousState, uint8 indexed newState);

  /**
  * @dev Returns the current contract state.
  */
  function getContractState() external view returns (uint8);
}

// File: @lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC173.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

// import "./IERC165.sol";

/**
* @dev Required interface of an ERC173 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-173[EIP].
*/
interface IERC173 /* is IERC165 */ {
  /**
  * @dev This emits when ownership of a contract changes.
  * 
  * @param previousOwner the previous contract owner
  * @param newOwner the new contract owner
  */
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
  * @notice Set the address of the new owner of the contract.
  * @dev Set newOwner_ to address(0) to renounce any ownership.
  */
  function transferOwnership(address newOwner_) external; 

  /**
  * @notice Returns the address of the owner.
  */
  function owner() external view returns(address);
}

// File: @lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC173Errors.sol



// SPDX-License-Identifier: MIT

/**
* Author: Lambdalf the White
*/

pragma solidity 0.8.17;

interface IERC173Errors {
  /**
  * @dev Thrown when `operator` is not the contract owner.
  * 
  * @param operator address trying to use a function reserved to contract owner without authorization
  */
  error IERC173_NOT_OWNER(address operator);
}

// File: @lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC20.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

// import "./IERC165.sol";

/**
* @title ERC-20 Multi Token Standard
* @dev See https://eips.ethereum.org/EIPS/eip-20
*/
interface IERC20 /* is IERC165 */ {
  /**
  * @dev Emitted when the allowance of a `spender` for an `owner` is set by a call to {approve}.
  *   `value` is the new allowance.
  * 
  * @param owner address that owns the tokens
  * @param spender address allowed to spend the tokens
  * @param value the amount of tokens allowed to be spent
  */
  event Approval(address indexed owner, address indexed spender, uint256 value);
  /**
  * @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
  *   Note that `value` may be zero.
  * 
  * @param from address tokens are being transferred from
  * @param to address tokens are being transferred to
  * @param value amount of tokens being transferred
  */
  event Transfer(address indexed from, address indexed to, uint256 value);

  /**
  * @dev Sets `amount_` as the allowance of `spender_` over the caller's tokens.
  *
  * Returns a boolean value indicating whether the operation succeeded.
  *
  * IMPORTANT: Beware that changing an allowance with this method brings the risk
  *   that someone may use both the old and the new allowance by unfortunate transaction ordering.
  *   One possible solution to mitigate this race condition is to first reduce the spender"s allowance to 0,
  *   and set the desired value afterwards:
  *   https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
  *
  * Emits an {Approval} event.
  */
  function approve(address spender_, uint256 amount_) external returns (bool);
  /**
  * @dev Moves `amount_` tokens from the caller's account to `recipient_`.
  *
  * Returns a boolean value indicating whether the operation succeeded.
  *
  * Emits a {Transfer} event.
  */
  function transfer(address recipient_, uint256 amount_) external returns (bool);
  /**
  * @dev Moves `amount_` tokens from `sender_` to `recipient_` using the allowance mechanism.
  *   `amount_` is then deducted from the caller's allowance.
  *
  * Returns a boolean value indicating whether the operation succeeded.
  *
  * Emits a {Transfer} event.
  */
  function transferFrom(
    address sender_,
    address recipient_,
    uint256 amount_
  ) external returns (bool);

  /**
  * @dev Returns the remaining number of tokens that `spender_` will be allowed to spend on behalf of `owner_`
  *   through {transferFrom}. This is zero by default.
  *
  * This value changes when {approve} or {transferFrom} are called.
  */
  function allowance(address owner_, address spender_) external view returns (uint256);
  /**
  * @dev Returns the amount of tokens owned by `account_`.
  */
  function balanceOf(address account_) external view returns (uint256);
  /**
  * @dev Returns the amount of tokens in existence.
  */
  function totalSupply() external view returns (uint256);
}

// File: @lambdalf-dev/ethereum-contracts/contracts/interfaces/IEtherErrors.sol



// SPDX-License-Identifier: MIT

/**
* Author: Lambdalf the White
*/

pragma solidity 0.8.17;

interface IEtherErrors {
  /**
  * @dev Thrown when an incorrect amount of eth is being sent for a payable operation.
  * 
  * @param amountReceived the amount the contract received
  * @param amountExpected the actual amount the contract expected to receive
  */
  error ETHER_INCORRECT_PRICE(uint256 amountReceived, uint256 amountExpected);
  /**
  * @dev Thrown when trying to withdraw from the contract with no balance.
  */
  error ETHER_NO_BALANCE();
  /**
  * @dev Thrown when contract fails to send ether to recipient.
  * 
  * @param to the recipient of the ether
  * @param amount the amount of ether being sent
  */
  error ETHER_TRANSFER_FAIL(address to, uint256 amount);
}

// File: @lambdalf-dev/ethereum-contracts/contracts/interfaces/INFTSupplyErrors.sol



// SPDX-License-Identifier: MIT

/**
* Author: Lambdalf the White
*/

pragma solidity 0.8.17;

interface INFTSupplyErrors {
  /**
  * @dev Thrown when trying to mint 0 token.
  */
  error NFT_INVALID_QTY();
  /**
  * @dev Thrown when trying to set max supply to an invalid amount.
  */
  error NFT_INVALID_SUPPLY();
  /**
  * @dev Thrown when trying to mint more tokens than the max allowed per transaction.
  * 
  * @param qtyRequested the amount of tokens requested
  * @param maxBatch the maximum amount that can be minted per transaction
  */
  error NFT_MAX_BATCH(uint256 qtyRequested, uint256 maxBatch);
  /**
  * @dev Thrown when trying to mint more tokens from the reserve than the amount left.
  * 
  * @param qtyRequested the amount of tokens requested
  * @param reserveLeft the amount of tokens left in the reserve
  */
  error NFT_MAX_RESERVE(uint256 qtyRequested, uint256 reserveLeft);
  /**
  * @dev Thrown when trying to mint more tokens than the amount left to be minted (except reserve).
  * 
  * @param qtyRequested the amount of tokens requested
  * @param remainingSupply the amount of tokens left in the reserve
  */
  error NFT_MAX_SUPPLY(uint256 qtyRequested, uint256 remainingSupply);
}

// File: @lambdalf-dev/ethereum-contracts/contracts/utils/ContractState.sol



// SPDX-License-Identifier: MIT

/**
* Author: Lambdalf the White
*/

pragma solidity 0.8.17;

import "../interfaces/IContractState.sol";

abstract contract ContractState is IContractState {
  // Enum to represent the sale state, defaults to ``PAUSED``.
  uint8 public constant PAUSED = 0;

  // The current state of the contract
  uint8 private _contractState;

  // **************************************
  // *****          MODIFIER          *****
  // **************************************
    /**
    * @dev Ensures that contract state is `expectedState_`.
    * 
    * @param expectedState_ : the desirable contract state
    */
    modifier isState(uint8 expectedState_) {
      if (_contractState != expectedState_) {
        revert ContractState_INCORRECT_STATE(_contractState);
      }
      _;
    }
    /**
    * @dev Ensures that contract state is not `unexpectedState_`.
    * 
    * @param unexpectedState_ : the undesirable contract state
    */
    modifier isNotState(uint8 unexpectedState_) {
      if (_contractState == unexpectedState_) {
        revert ContractState_INCORRECT_STATE(_contractState);
      }
      _;
    }
  // **************************************

  // **************************************
  // *****          INTERNAL          *****
  // **************************************
    /**
    * @dev Internal function setting the contract state to `newState_`.
    * 
    * Note: Contract state defaults to ``PAUSED``.
    *   To maintain extendability, this value kept as uint8 instead of enum.
    *   As a result, it is possible to set the state to an incorrect value.
    *   To avoid issues, `newState_` should be validated before calling this function
    */
    function _setContractState(uint8 newState_) internal virtual {
      uint8 _previousState_ = _contractState;
      _contractState = newState_;
      emit ContractStateChanged(_previousState_, newState_);
    }
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
    /**
    * @dev Returns the current contract state.
    * 
    * @return uint8 : the current contract state
    */
    function getContractState() public virtual view override returns (uint8) {
      return _contractState;
    }
  // **************************************
}

// File: @lambdalf-dev/ethereum-contracts/contracts/utils/ERC173.sol



// SPDX-License-Identifier: MIT

/**
* Author: Lambdalf the White
*/

pragma solidity 0.8.17;

import "../interfaces/IERC173.sol";
import "../interfaces/IERC173Errors.sol";

/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract ERC173 is IERC173, IERC173Errors {
  // The owner of the contract
  address private _owner;

  // **************************************
  // *****          MODIFIER          *****
  // **************************************
    /**
    * @dev Throws if called by any account other than the owner.
    */
    modifier onlyOwner() {
      if (owner() != msg.sender) {
        revert IERC173_NOT_OWNER(msg.sender);
      }
      _;
    }
  // **************************************

  // **************************************
  // *****          INTERNAL          *****
  // **************************************
    /**
    * @dev Sets the contract owner.
    * 
    * Note: This function needs to be called in the contract constructor to initialize the contract owner, 
    * if it is not, then parts of the contract might be non functional
    * 
    * @param owner_ : address that owns the contract
    */
    function _setOwner(address owner_) internal {
      _owner = owner_;
    }
  // **************************************

  // **************************************
  // *****       CONTRACT OWNER       *****
  // **************************************
    /**
    * @dev Transfers ownership of the contract to `newOwner_`.
    * 
    * @param newOwner_ : address of the new contract owner
    * 
    * Requirements:
    * 
    * - Caller must be the contract owner.
    */
    function transferOwnership(address newOwner_) public virtual onlyOwner {
      address _oldOwner_ = _owner;
      _owner = newOwner_;
      emit OwnershipTransferred(_oldOwner_, newOwner_);
    }
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
    /**
    * @dev Returns the address of the current contract owner.
    * 
    * @return address : the current contract owner
    */
    function owner() public view virtual returns (address) {
      return _owner;
    }
  // **************************************
}

// File: @lambdalf-dev/ethereum-contracts/contracts/utils/Whitelist_ECDSA.sol



// SPDX-License-Identifier: MIT

/**
* Author: Lambdalf the White
*/

pragma solidity 0.8.17;

abstract contract Whitelist_ECDSA {
  // Errors
  /**
  * @dev Thrown when trying to query the whitelist while it"s not set
  */
  error Whitelist_NOT_SET();
  /**
  * @dev Thrown when `account` has consumed their alloted access and tries to query more
  * 
  * @param account address trying to access the whitelist
  */
  error Whitelist_CONSUMED(address account);
  /**
  * @dev Thrown when `account` does not have enough alloted access to fulfil their query
  * 
  * @param account address trying to access the whitelist
  */
  error Whitelist_FORBIDDEN(address account);

  /**
  * @dev A structure representing a signature proof to be decoded by the contract
  */
  struct Proof {
    bytes32 r;
    bytes32 s;
    uint8   v;
  }

  address private _adminSigner;
  mapping(uint8 => mapping(address => uint256)) private _consumed;

  // **************************************
  // *****          MODIFIER          *****
  // **************************************
    /**
    * @dev Ensures that `account_` has `qty_` alloted access on the `whitelistId_` whitelist.
    * 
    * @param account_ the address to validate access
    * @param whitelistId_ the identifier of the whitelist being queried
    * @param alloted_ the max amount of whitelist spots allocated
    * @param proof_ the signature proof to validate whitelist allocation
    * @param qty_ the amount of whitelist access requested
    */
    modifier isWhitelisted(address account_, uint8 whitelistId_, uint256 alloted_, Proof memory proof_, uint256 qty_) {
      uint256 _allowed_ = checkWhitelistAllowance(account_, whitelistId_, alloted_, proof_);
      if (_allowed_ < qty_) {
        revert Whitelist_FORBIDDEN(account_);
      }
      _;
    }
  // **************************************

  // **************************************
  // *****          INTERNAL          *****
  // **************************************
    /**
    * @dev Consumes `amount_` whitelist access passes from `account_`.
    * 
    * @param account_ the address to consume access from
    * @param whitelistId_ the identifier of the whitelist being queried
    * @param qty_ the amount of whitelist access consumed
    * 
    * Note: Before calling this function, eligibility should be checked through {Whitelistable-checkWhitelistAllowance}.
    */
    function _consumeWhitelist(address account_, uint8 whitelistId_, uint256 qty_) internal {
      unchecked {
        _consumed[ whitelistId_ ][ account_ ] += qty_;
      }
    }
    /**
    * @dev Sets the pass to protect the whitelist.
    * 
    * @param adminSigner_ : the address validating the whitelist signatures
    */
    function _setWhitelist(address adminSigner_) internal virtual {
      _adminSigner = adminSigner_;
    }
    /**
    * @dev Internal function to decode a signature and compare it with the `_adminSigner`.
    * 
    * @param account_ the address to validate access
    * @param whitelistId_ the identifier of the whitelist being queried
    * @param alloted_ the max amount of whitelist spots allocated
    * @param proof_ the signature proof to validate whitelist allocation
    * 
    * @return bool whether the signature is valid or not
    */ 
    function _validateProof(
      address account_,
      uint8 whitelistId_,
      uint256 alloted_,
      Proof memory proof_
    ) private view returns (bool) {
      bytes32 _digest_ = keccak256(abi.encode(whitelistId_, alloted_, account_));
      address _signer_ = ecrecover(_digest_, proof_.v, proof_.r, proof_.s);
      return _signer_ == _adminSigner;
    }
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
    /**
    * @dev Returns the amount that `account_` is allowed to access from the whitelist.
    * 
    * @param account_ the address to validate access
    * @param whitelistId_ the identifier of the whitelist being queried
    * @param alloted_ the max amount of whitelist spots allocated
    * @param proof_ the signature proof to validate whitelist allocation
    * 
    * @return uint256 : the total amount of whitelist allocation remaining for `account_`
    * 
    * Requirements:
    * 
    * - `_adminSigner` must be set.
    */
    function checkWhitelistAllowance(
      address account_,
      uint8 whitelistId_,
      uint256 alloted_,
      Proof memory proof_
    ) public view returns (uint256) {
      if (_adminSigner == address(0)) {
        revert Whitelist_NOT_SET();
      }

      if (_consumed[ whitelistId_ ][ account_ ] >= alloted_) {
        revert Whitelist_CONSUMED(account_);
      }

      if (! _validateProof(account_, whitelistId_, alloted_, proof_)) {
        revert Whitelist_FORBIDDEN(account_);
      }

      return alloted_ - _consumed[ whitelistId_ ][ account_ ];
    }
  // **************************************
}

// File: contracts/FirstCoverSale.sol



// SPDX-License-Identifier: MIT

/**
* @team: Asteria Labs
* @author: Lambdalf the White
*/

pragma solidity 0.8.17;

import "@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC20.sol";
import "@lambdalf-dev/ethereum-contracts/contracts/interfaces/IEtherErrors.sol";
import "@lambdalf-dev/ethereum-contracts/contracts/interfaces/INFTSupplyErrors.sol";
import "@lambdalf-dev/ethereum-contracts/contracts/utils/ERC173.sol";
import "@lambdalf-dev/ethereum-contracts/contracts/utils/ContractState.sol";
import "@lambdalf-dev/ethereum-contracts/contracts/utils/Whitelist_ECDSA.sol";

interface ICover {
  function mintTo(uint256 id_, uint256 qty_, address recipient_) external;
  function balanceOf(address owner_, uint256 id_) external view returns (uint256);
}

contract FirstCoverSale is IEtherErrors, INFTSupplyErrors, ERC173, ContractState, Whitelist_ECDSA {
  // **************************************
  // *****    BYTECODE  VARIABLES     *****
  // **************************************
    uint8 public constant KYO_CLAIM = 1;
    uint8 public constant KPJ_CLAIM = 2;
    uint8 public constant POAP_CLAIM = 3;
    uint8 public constant KYO_SALE = 4;
    uint8 public constant WL_SALE = 5;
    uint8 public constant PUBLIC_SALE = 6;
    uint256 public constant VOLUME_1 = 100;
    address public immutable ASTERIA;
  // **************************************

  // **************************************
  // *****     STORAGE VARIABLES      *****
  // **************************************
    uint256 public publicPrice = 15000000; // 15 USDC
    uint256 public discountPrice = 11500000; // 11.5 USDC
    IERC20 public usdc;
    ICover public cover;
    address public treasury;
    bool public vaultClaimed;
    uint256 public maxSupply; // 3000;
    uint256 public mintedSuply;
    mapping(uint8 => uint256) private _volume;
  // **************************************

  // **************************************
  // *****           ERROR            *****
  // **************************************
    error USDC_TRANSFER_FAIL(address sender, address recipient, uint256 amount);
    error VAULT_CLAIM_DONE();
    error VAULT_CLAIM_INVALID();
  // **************************************

  // **************************************
  // *****           EVENT            *****
  // **************************************
  // **************************************

  constructor(address asteria_, address treasury_, address signer_, address usdcAddress_, address coverAddress_, uint256 maxSupply_) {
    ASTERIA = asteria_;
    treasury = treasury_;
    usdc = IERC20(usdcAddress_);
    cover = ICover(coverAddress_);
    _setWhitelist(signer_);
    _setOwner(msg.sender);
    _volume[KYO_CLAIM] = VOLUME_1 + KYO_CLAIM;
    _volume[KPJ_CLAIM] = VOLUME_1 + KPJ_CLAIM;
    _volume[POAP_CLAIM] = VOLUME_1;
    maxSupply = maxSupply_;
  }

  // **************************************
  // *****          MODIFIER          *****
  // **************************************
  // **************************************

  // **************************************
  // *****          INTERNAL          *****
  // **************************************
    /**
    * @dev Internal function processing an USDC payment.
    * 
    * @param sender_ the address sending the payment
    * @param recipient_ the address receiving the payment
    * @param amount_ the amount sent
    */
    function _processUSDCPayment(address sender_, address recipient_, uint256 amount_) internal {
      // solhint-disable-next-line
      bool _success_ = usdc.transferFrom(sender_, recipient_, amount_);
      if (! _success_) {
        revert USDC_TRANSFER_FAIL(sender_, recipient_, amount_);
      }
    }
  // **************************************

  // **************************************
  // *****           PUBLIC           *****
  // **************************************
    /**
    * @dev Claims a free cover.
    * 
    * @param proof_ the signature proving the wallet is allowed
    * 
    * Requirements:
    * 
    * - Contract state must be {KYO}, {KPJ}, or {POAP}
    * - Caller must be allowed to claim a cover
    */
    function claim(uint256 qty_, uint256 alloted_, Proof calldata proof_) external {
      uint8 _currentState_ = getContractState();
      if (_currentState_ == PAUSED || _currentState_ == KYO_SALE || _currentState_ == WL_SALE || _currentState_ == PUBLIC_SALE) {
        revert ContractState_INCORRECT_STATE(_currentState_);
      }
      uint256 _allowed_ = checkWhitelistAllowance(msg.sender, _currentState_, alloted_, proof_);
      if (_allowed_ < qty_) {
        revert Whitelist_FORBIDDEN(msg.sender);
      }
      _consumeWhitelist(msg.sender, _currentState_, qty_);
      if (_volume[_currentState_] == VOLUME_1) {
        uint256 _remainingSupply_;
        unchecked {
          _remainingSupply_ = maxSupply - mintedSuply;
        }
        if (qty_ > _remainingSupply_) {
          revert NFT_MAX_SUPPLY(qty_, _remainingSupply_);
        }
        unchecked {
          ++mintedSuply;
        }
      }
      try cover.mintTo(_volume[_currentState_], qty_, msg.sender) {}
      catch Error( string memory reason ) {
        revert(reason);
      }
    }
    /**
    * @dev Mints `qty_` first edition covers.
    * 
    * @param qty_ the number of covers purchased
    * @param alloted_ the max number allowed for the wallet
    * @param proof_ the signature proving the wallet is allowed
    * 
    * Requirements:
    * 
    * - Contract state must be {KYO_SALE} or {WL_SALE}
    * - Caller must have enough USDC to pay for `qty_` covers at discount price
    */
    function discountPurchase(uint256 qty_, uint256 alloted_, Proof calldata proof_) external {
      uint8 _currentState_ = getContractState();
      if (_currentState_ < KYO_SALE || _currentState_ == PUBLIC_SALE) {
        revert ContractState_INCORRECT_STATE(_currentState_);
      }
      uint256 _remainingSupply_;
      unchecked {
        _remainingSupply_ = maxSupply - mintedSuply;
      }
      if (qty_ > _remainingSupply_) {
        revert NFT_MAX_SUPPLY(qty_, _remainingSupply_);
      }
      uint256 _allowed_ = checkWhitelistAllowance(msg.sender, _currentState_, alloted_, proof_);
      if (_allowed_ < qty_) {
        revert Whitelist_FORBIDDEN(msg.sender);
      }
      unchecked {
        mintedSuply += qty_;
      }
      _consumeWhitelist(msg.sender, _currentState_, qty_);
      cover.mintTo(VOLUME_1, qty_, msg.sender);
      uint256 _totalPrice_ = discountPrice * qty_;
      uint256 _asteriaShare_ = _totalPrice_ * 5 / 100;
      _processUSDCPayment(msg.sender, ASTERIA, _asteriaShare_);
      _processUSDCPayment(msg.sender, treasury, _totalPrice_ - _asteriaShare_);
    }
    /**
    * @dev Mints `qty_` first edition covers.
    * 
    * @param qty_ the number of covers purchased
    * 
    * Requirements:
    * 
    * - Contract state must be {PUBLIC_SALE}
    * - Caller must have enough USDC to pay for `qty_` covers at full price
    */
    function purchase(uint256 qty_) external isState(PUBLIC_SALE) {
      uint256 _remainingSupply_;
      unchecked {
        _remainingSupply_ = maxSupply - mintedSuply;
      }
      if (qty_ > _remainingSupply_) {
        revert NFT_MAX_SUPPLY(qty_, _remainingSupply_);
      }
      unchecked {
        mintedSuply += qty_;
      }
      cover.mintTo(VOLUME_1, qty_, msg.sender);
      uint256 _totalPrice_ = publicPrice * qty_;
      uint256 _asteriaShare_ = _totalPrice_ * 5 / 100;
      _processUSDCPayment(msg.sender, ASTERIA, _asteriaShare_);
      _processUSDCPayment(msg.sender, treasury, _totalPrice_ - _asteriaShare_);
    }
  // **************************************

  // **************************************
  // *****       CONTRACT OWNER       *****
  // **************************************
    /**
    * @dev Mints 150 covers to project vault.
    * 
    * @param vault_ the address receiving the tokens
    * @param qty_ the number of covers minted
    * 
    * Requirements:
    * 
    * - Caller must be the contract owner.
    * - Function can only be called once.
    */
    function devMint(address vault_, uint256 qty_) external onlyOwner {
      if (vaultClaimed) {
        revert VAULT_CLAIM_DONE();
      }
      if (qty_ > 150) {
        revert VAULT_CLAIM_INVALID();
      }
      uint256 _remainingSupply_;
      unchecked {
        _remainingSupply_ = maxSupply - mintedSuply;
      }
      if (qty_ > _remainingSupply_) {
        revert NFT_MAX_SUPPLY(qty_, _remainingSupply_);
      }
      unchecked {
        mintedSuply += qty_;
      }
      vaultClaimed = true;
      cover.mintTo(VOLUME_1, qty_, vault_);
    }
    /**
    * @dev Updates the max supply for the Standard cover
    * 
    * @param newMaxSupply_ the new max supply
    *  
    * Requirements:
    * 
    * - Caller must be the contract owner.
    */
    function updateSupply(uint256 newMaxSupply_) external onlyOwner {
      maxSupply = newMaxSupply_;
    }
    /**
    * @dev Updates the contract state.
    * 
    * @param newState_ the new sale state
    * 
    * Requirements:
    * 
    * - Caller must be the contract owner.
    * - `newState_` must be a valid state.
    */
    function setContractState(uint8 newState_) external onlyOwner {
      if (newState_ > PUBLIC_SALE) {
        revert ContractState_INVALID_STATE(newState_);
      }
      _setContractState(newState_);
    }
    /**
    * @dev Sets the Cover contract address.
    * 
    * @param contractAddress_ the Cover contract address
    * 
    * Requirements:
    * 
    * - Caller must be the contract owner.
    */
    function setCover(address contractAddress_) external onlyOwner {
      cover = ICover(contractAddress_);
    }
    /**
    * @dev Updates the price of the purchase.
    * 
    * @param newPublicPrice_ the new public price
    * @param newDiscountPrice_ the new discount price
    *  
    * Requirements:
    * 
    * - Caller must be the contract owner.
    */
    function setPrice(uint256 newPublicPrice_, uint256 newDiscountPrice_) external onlyOwner {
      publicPrice = newPublicPrice_;
      discountPrice = newDiscountPrice_;
    }
    /**
    * @dev Updates the treasury addresses
    * 
    * @param newTreasury_ The new treasury address
    *  
    * Requirements:
    * 
    * - Caller must be the contract owner.
    */
    function setTreasury(address newTreasury_) external onlyOwner {
      treasury = newTreasury_;
    }
    /**
    * @dev Updates the whitelist signer.
    * 
    * @param newAdminSigner_ the new whitelist signer
    *  
    * Requirements:
    * 
    * - Caller must be the contract owner.
    */
    function setWhitelist(address newAdminSigner_) external onlyOwner {
      _setWhitelist(newAdminSigner_);
    }
  // **************************************
}