creation_sourcecode: /**
   *Submitted for verification at Etherscan.io on 2024-04-06
  */
  
  /**
   *Submitted for verification at Etherscan.io on 2022-03-24
   */
  
  /**
   *Submitted for verification at Etherscan.io on 2022-02-09
   */
  
  // SPDX-License-Identifier: MIT
  pragma solidity ^0.8.0;
  
  
  abstract contract ReentrancyGuard {
      uint256 private constant _NOT_ENTERED = 1;
      uint256 private constant _ENTERED = 2;
  
      uint256 private _status;
  
      constructor() {
          _status = _NOT_ENTERED;
      }
  
      modifier nonReentrant() {
          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
          _status = _ENTERED;
  
          _;
          _status = _NOT_ENTERED;
      }
  }
  
  /**
   * @dev Wrappers over Solidity's arithmetic operations.
   *
   * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
   * now has built in overflow checking.
   */
  library SafeMath {
      /**
       * @dev Returns the addition of two unsigned integers, with an overflow flag.
       *
       * _Available since v3.4._
       */
      function tryAdd(uint256 a, uint256 b)
          internal
          pure
          returns (bool, uint256)
      {
          unchecked {
              uint256 c = a + b;
              if (c < a) return (false, 0);
              return (true, c);
          }
      }
  
      /**
       * @dev Returns the substraction of two unsigned integers, with an overflow flag.
       *
       * _Available since v3.4._
       */
      function trySub(uint256 a, uint256 b)
          internal
          pure
          returns (bool, uint256)
      {
          unchecked {
              if (b > a) return (false, 0);
              return (true, a - b);
          }
      }
  
      /**
       * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
       *
       * _Available since v3.4._
       */
      function tryMul(uint256 a, uint256 b)
          internal
          pure
          returns (bool, uint256)
      {
          unchecked {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) return (true, 0);
              uint256 c = a * b;
              if (c / a != b) return (false, 0);
              return (true, c);
          }
      }
  
      /**
       * @dev Returns the division of two unsigned integers, with a division by zero flag.
       *
       * _Available since v3.4._
       */
      function tryDiv(uint256 a, uint256 b)
          internal
          pure
          returns (bool, uint256)
      {
          unchecked {
              if (b == 0) return (false, 0);
              return (true, a / b);
          }
      }
  
      /**
       * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
       *
       * _Available since v3.4._
       */
      function tryMod(uint256 a, uint256 b)
          internal
          pure
          returns (bool, uint256)
      {
          unchecked {
              if (b == 0) return (false, 0);
              return (true, a % b);
          }
      }
  
      /**
       * @dev Returns the addition of two unsigned integers, reverting on
       * overflow.
       *
       * Counterpart to Solidity's `+` operator.
       *
       * Requirements:
       *
       * - Addition cannot overflow.
       */
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
          return a + b;
      }
  
      /**
       * @dev Returns the subtraction of two unsigned integers, reverting on
       * overflow (when the result is negative).
       *
       * Counterpart to Solidity's `-` operator.
       *
       * Requirements:
       *
       * - Subtraction cannot overflow.
       */
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
          return a - b;
      }
  
      /**
       * @dev Returns the multiplication of two unsigned integers, reverting on
       * overflow.
       *
       * Counterpart to Solidity's `*` operator.
       *
       * Requirements:
       *
       * - Multiplication cannot overflow.
       */
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
          return a * b;
      }
  
      /**
       * @dev Returns the integer division of two unsigned integers, reverting on
       * division by zero. The result is rounded towards zero.
       *
       * Counterpart to Solidity's `/` operator.
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
          return a / b;
      }
  
      /**
       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
       * reverting when dividing by zero.
       *
       * Counterpart to Solidity's `%` operator. This function uses a `revert`
       * opcode (which leaves remaining gas untouched) while Solidity uses an
       * invalid opcode to revert (consuming all remaining gas).
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function mod(uint256 a, uint256 b) internal pure returns (uint256) {
          return a % b;
      }
  
      /**
       * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
       * overflow (when the result is negative).
       *
       * CAUTION: This function is deprecated because it requires allocating memory for the error
       * message unnecessarily. For custom revert reasons use {trySub}.
       *
       * Counterpart to Solidity's `-` operator.
       *
       * Requirements:
       *
       * - Subtraction cannot overflow.
       */
      function sub(
          uint256 a,
          uint256 b,
          string memory errorMessage
      ) internal pure returns (uint256) {
          unchecked {
              require(b <= a, errorMessage);
              return a - b;
          }
      }
  
      /**
       * @dev Returns the integer division of two unsigned integers, reverting with custom message on
       * division by zero. The result is rounded towards zero.
       *
       * Counterpart to Solidity's `/` operator. Note: this function uses a
       * `revert` opcode (which leaves remaining gas untouched) while Solidity
       * uses an invalid opcode to revert (consuming all remaining gas).
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function div(
          uint256 a,
          uint256 b,
          string memory errorMessage
      ) internal pure returns (uint256) {
          unchecked {
              require(b > 0, errorMessage);
              return a / b;
          }
      }
  
      /**
       * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
       * reverting with custom message when dividing by zero.
       *
       * CAUTION: This function is deprecated because it requires allocating memory for the error
       * message unnecessarily. For custom revert reasons use {tryMod}.
       *
       * Counterpart to Solidity's `%` operator. This function uses a `revert`
       * opcode (which leaves remaining gas untouched) while Solidity uses an
       * invalid opcode to revert (consuming all remaining gas).
       *
       * Requirements:
       *
       * - The divisor cannot be zero.
       */
      function mod(
          uint256 a,
          uint256 b,
          string memory errorMessage
      ) internal pure returns (uint256) {
          unchecked {
              require(b > 0, errorMessage);
              return a % b;
          }
      }
  }
  
  library Strings {
      bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
  
      function toString(uint256 value) internal pure returns (string memory) {
          if (value == 0) {
              return "0";
          }
          uint256 temp = value;
          uint256 digits;
          while (temp != 0) {
              digits++;
              temp /= 10;
          }
          bytes memory buffer = new bytes(digits);
          while (value != 0) {
              digits -= 1;
              buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
              value /= 10;
          }
          return string(buffer);
      }
  
      function toHexString(uint256 value) internal pure returns (string memory) {
          if (value == 0) {
              return "0x00";
          }
          uint256 temp = value;
          uint256 length = 0;
          while (temp != 0) {
              length++;
              temp >>= 8;
          }
          return toHexString(value, length);
      }
  
      function toHexString(uint256 value, uint256 length)
          internal
          pure
          returns (string memory)
      {
          bytes memory buffer = new bytes(2 * length + 2);
          buffer[0] = "0";
          buffer[1] = "x";
          for (uint256 i = 2 * length + 1; i > 1; --i) {
              buffer[i] = _HEX_SYMBOLS[value & 0xf];
              value >>= 4;
          }
          require(value == 0, "Strings: hex length insufficient");
          return string(buffer);
      }
  }
  
  abstract contract Context {
      function _msgSender() internal view virtual returns (address) {
          return msg.sender;
      }
  
      function _msgData() internal view virtual returns (bytes calldata) {
          return msg.data;
      }
  }
  
  abstract contract Ownable is Context {
      address private _owner;
  
      event OwnershipTransferred(
          address indexed previousOwner,
          address indexed newOwner
      );
  
      constructor() {
          _transferOwnership(_msgSender());
      }
  
      function owner() public view virtual returns (address) {
          return _owner;
      }
  
      modifier onlyOwner() {
          require(owner() == _msgSender(), "Ownable: caller is not the owner");
          _;
      }
  
      function renounceOwnership() public virtual onlyOwner {
          _transferOwnership(address(0));
      }
  
      function transferOwnership(address newOwner) public virtual onlyOwner {
          require(
              newOwner != address(0),
              "Ownable: new owner is the zero address"
          );
          _transferOwnership(newOwner);
      }
  
      function _transferOwnership(address newOwner) internal virtual {
          address oldOwner = _owner;
          _owner = newOwner;
          emit OwnershipTransferred(oldOwner, newOwner);
      }
  }
  
  library Address {
      function isContract(address account) internal view returns (bool) {
          uint256 size;
          assembly {
              size := extcodesize(account)
          }
          return size > 0;
      }
  
      function sendValue(address payable recipient, uint256 amount) internal {
          require(
              address(this).balance >= amount,
              "Address: insufficient balance"
          );
  
          (bool success, ) = recipient.call{value: amount}("");
          require(
              success,
              "Address: unable to send value, recipient may have reverted"
          );
      }
  
      function functionCall(address target, bytes memory data)
          internal
          returns (bytes memory)
      {
          return functionCall(target, data, "Address: low-level call failed");
      }
  
      function functionCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          return functionCallWithValue(target, data, 0, errorMessage);
      }
  
      function functionCallWithValue(
          address target,
          bytes memory data,
          uint256 value
      ) internal returns (bytes memory) {
          return
              functionCallWithValue(
                  target,
                  data,
                  value,
                  "Address: low-level call with value failed"
              );
      }
  
      function functionCallWithValue(
          address target,
          bytes memory data,
          uint256 value,
          string memory errorMessage
      ) internal returns (bytes memory) {
          require(
              address(this).balance >= value,
              "Address: insufficient balance for call"
          );
          require(isContract(target), "Address: call to non-contract");
  
          (bool success, bytes memory returndata) = target.call{value: value}(
              data
          );
          return verifyCallResult(success, returndata, errorMessage);
      }
  
      function functionStaticCall(address target, bytes memory data)
          internal
          view
          returns (bytes memory)
      {
          return
              functionStaticCall(
                  target,
                  data,
                  "Address: low-level static call failed"
              );
      }
  
      function functionStaticCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal view returns (bytes memory) {
          require(isContract(target), "Address: static call to non-contract");
  
          (bool success, bytes memory returndata) = target.staticcall(data);
          return verifyCallResult(success, returndata, errorMessage);
      }
  
      function functionDelegateCall(address target, bytes memory data)
          internal
          returns (bytes memory)
      {
          return
              functionDelegateCall(
                  target,
                  data,
                  "Address: low-level delegate call failed"
              );
      }
  
      function functionDelegateCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          require(isContract(target), "Address: delegate call to non-contract");
  
          (bool success, bytes memory returndata) = target.delegatecall(data);
          return verifyCallResult(success, returndata, errorMessage);
      }
  
      function verifyCallResult(
          bool success,
          bytes memory returndata,
          string memory errorMessage
      ) internal pure returns (bytes memory) {
          if (success) {
              return returndata;
          } else {
              if (returndata.length > 0) {
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert(errorMessage);
              }
          }
      }
  }
  
  interface IERC721Receiver {
      function onERC721Received(
          address operator,
          address from,
          uint256 tokenId,
          bytes calldata data
      ) external returns (bytes4);
  }
  
  interface IERC165 {
      function supportsInterface(bytes4 interfaceId) external view returns (bool);
  }
  
  abstract contract ERC165 is IERC165 {
      function supportsInterface(bytes4 interfaceId)
          public
          view
          virtual
          override
          returns (bool)
      {
          return interfaceId == type(IERC165).interfaceId;
      }
  }
  
  interface IERC721 is IERC165 {
      event Transfer(
          address indexed from,
          address indexed to,
          uint256 indexed tokenId
      );
      event Approval(
          address indexed owner,
          address indexed approved,
          uint256 indexed tokenId
      );
      event ApprovalForAll(
          address indexed owner,
          address indexed operator,
          bool approved
      );
  
      function balanceOf(address owner) external view returns (uint256 balance);
  
      function ownerOf(uint256 tokenId) external view returns (address owner);
  
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId
      ) external;
  
      function transferFrom(
          address from,
          address to,
          uint256 tokenId
      ) external;
  
      function approve(address to, uint256 tokenId) external;
  
      function getApproved(uint256 tokenId)
          external
          view
          returns (address operator);
  
      function setApprovalForAll(address operator, bool _approved) external;
  
      function isApprovedForAll(address owner, address operator)
          external
          view
          returns (bool);
  
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId,
          bytes calldata data
      ) external;
  }
  
  interface IERC721Enumerable is IERC721 {
      function totalSupply() external view returns (uint256);
  
      function tokenOfOwnerByIndex(address owner, uint256 index)
          external
          view
          returns (uint256 tokenId);
  
      function tokenByIndex(uint256 index) external view returns (uint256);
  }
  
  interface IERC721Metadata is IERC721 {
      function name() external view returns (string memory);
  
      function symbol() external view returns (string memory);
  
      function tokenURI(uint256 tokenId) external view returns (string memory);
  }
  
  contract ERC721A is
      Context,
      ERC165,
      IERC721,
      IERC721Metadata,
      IERC721Enumerable
  {
      using Address for address;
      using Strings for uint256;
  
      struct TokenOwnership {
          address addr;
          uint64 startTimestamp;
      }
  
      struct AddressData {
          uint128 balance;
          uint128 numberMinted;
      }
  
      uint256 private currentIndex = 1;
  
      uint256 internal immutable collectionSize;
      uint256 internal immutable maxBatchSize;
      string private _name;
      string private _symbol;
      mapping(uint256 => TokenOwnership) private _ownerships;
      mapping(address => AddressData) private _addressData;
      mapping(uint256 => address) private _tokenApprovals;
      mapping(address => mapping(address => bool)) private _operatorApprovals;
  
      constructor(
          string memory name_,
          string memory symbol_,
          uint256 maxBatchSize_,
          uint256 collectionSize_
      ) {
          require(
              collectionSize_ > 0,
              "ERC721A: collection must have a nonzero supply"
          );
          require(maxBatchSize_ > 0, "ERC721A: max batch size must be nonzero");
          _name = name_;
          _symbol = symbol_;
          maxBatchSize = maxBatchSize_;
          collectionSize = collectionSize_;
      }
  
      function totalSupply() public view override returns (uint256) {
          return currentIndex - 1;
      }
  
      function tokenByIndex(uint256 index)
          public
          view
          override
          returns (uint256)
      {
          require(index < totalSupply(), "ERC721A: global index out of bounds");
          return index;
      }
  
      function tokenOfOwnerByIndex(address owner, uint256 index)
          public
          view
          override
          returns (uint256)
      {
          require(index < balanceOf(owner), "ERC721A: owner index out of bounds");
          uint256 numMintedSoFar = totalSupply();
          uint256 tokenIdsIdx = 0;
          address currOwnershipAddr = address(0);
          for (uint256 i = 0; i < numMintedSoFar; i++) {
              TokenOwnership memory ownership = _ownerships[i];
              if (ownership.addr != address(0)) {
                  currOwnershipAddr = ownership.addr;
              }
              if (currOwnershipAddr == owner) {
                  if (tokenIdsIdx == index) {
                      return i;
                  }
                  tokenIdsIdx++;
              }
          }
          revert("ERC721A: unable to get token of owner by index");
      }
  
      function supportsInterface(bytes4 interfaceId)
          public
          view
          virtual
          override(ERC165, IERC165)
          returns (bool)
      {
          return
              interfaceId == type(IERC721).interfaceId ||
              interfaceId == type(IERC721Metadata).interfaceId ||
              interfaceId == type(IERC721Enumerable).interfaceId ||
              super.supportsInterface(interfaceId);
      }
  
      function balanceOf(address owner) public view override returns (uint256) {
          require(
              owner != address(0),
              "ERC721A: balance query for the zero address"
          );
          return uint256(_addressData[owner].balance);
      }
  
      function _numberMinted(address owner) internal view returns (uint256) {
          require(
              owner != address(0),
              "ERC721A: number minted query for the zero address"
          );
          return uint256(_addressData[owner].numberMinted);
      }
  
      function ownershipOf(uint256 tokenId)
          internal
          view
          returns (TokenOwnership memory)
      {
          require(_exists(tokenId), "ERC721A: owner query for nonexistent token");
  
          uint256 lowestTokenToCheck;
          if (tokenId >= maxBatchSize) {
              lowestTokenToCheck = tokenId - maxBatchSize + 1;
          }
  
          for (uint256 curr = tokenId; curr >= lowestTokenToCheck; curr--) {
              TokenOwnership memory ownership = _ownerships[curr];
              if (ownership.addr != address(0)) {
                  return ownership;
              }
          }
  
          revert("ERC721A: unable to determine the owner of token");
      }
  
      function ownerOf(uint256 tokenId) public view override returns (address) {
          return ownershipOf(tokenId).addr;
      }
  
      function name() public view virtual override returns (string memory) {
          return _name;
      }
  
      function symbol() public view virtual override returns (string memory) {
          return _symbol;
      }
  
      function tokenURI(uint256 tokenId)
          public
          view
          virtual
          override
          returns (string memory)
      {
          require(
              _exists(tokenId),
              "ERC721Metadata: URI query for nonexistent token"
          );
  
          string memory baseURI = _baseURI();
          return
              bytes(baseURI).length > 0
                  ? string(
                      abi.encodePacked(
                          baseURI,
                          tokenId.toString(),
                          _getUriExtension()
                      )
                  )
                  : "";
      }
  
      function _baseURI() internal view virtual returns (string memory) {
          return "";
      }
  
      function _getUriExtension() internal view virtual returns (string memory) {
          return "";
      }
  
      function approve(address to, uint256 tokenId) public override {
          address owner = ERC721A.ownerOf(tokenId);
          require(to != owner, "ERC721A: approval to current owner");
  
          require(
              _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
              "ERC721A: approve caller is not owner nor approved for all"
          );
  
          _approve(to, tokenId, owner);
      }
  
      function getApproved(uint256 tokenId)
          public
          view
          override
          returns (address)
      {
          require(
              _exists(tokenId),
              "ERC721A: approved query for nonexistent token"
          );
  
          return _tokenApprovals[tokenId];
      }
  
      function setApprovalForAll(address operator, bool approved)
          public
          override
      {
          require(operator != _msgSender(), "ERC721A: approve to caller");
  
          _operatorApprovals[_msgSender()][operator] = approved;
          emit ApprovalForAll(_msgSender(), operator, approved);
      }
  
      function isApprovedForAll(address owner, address operator)
          public
          view
          virtual
          override
          returns (bool)
      {
          return _operatorApprovals[owner][operator];
      }
  
      function transferFrom(
          address from,
          address to,
          uint256 tokenId
      ) public override {
          _transfer(from, to, tokenId);
      }
  
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId
      ) public override {
          safeTransferFrom(from, to, tokenId, "");
      }
  
      function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId,
          bytes memory _data
      ) public override {
          _transfer(from, to, tokenId);
          require(
              _checkOnERC721Received(from, to, tokenId, _data),
              "ERC721A: transfer to non ERC721Receiver implementer"
          );
      }
  
      function _exists(uint256 tokenId) internal view returns (bool) {
          return tokenId < currentIndex;
      }
  
      function _safeMint(address to, uint256 quantity) internal {
          _safeMint(to, quantity, "");
      }
  
      function _safeMint(
          address to,
          uint256 quantity,
          bytes memory _data
      ) internal {
          uint256 startTokenId = currentIndex;
          require(to != address(0), "ERC721A: mint to the zero address");
          require(!_exists(startTokenId), "ERC721A: token already minted");
          require(quantity <= maxBatchSize, "ERC721A: quantity to mint too high");
  
          _beforeTokenTransfers(address(0), to, startTokenId, quantity);
  
          AddressData memory addressData = _addressData[to];
          _addressData[to] = AddressData(
              addressData.balance + uint128(quantity),
              addressData.numberMinted + uint128(quantity)
          );
          _ownerships[startTokenId] = TokenOwnership(to, uint64(block.timestamp));
  
          uint256 updatedIndex = startTokenId;
  
          for (uint256 i = 0; i < quantity; i++) {
              emit Transfer(address(0), to, updatedIndex);
              require(
                  _checkOnERC721Received(address(0), to, updatedIndex, _data),
                  "ERC721A: transfer to non ERC721Receiver implementer"
              );
              updatedIndex++;
          }
  
          currentIndex = updatedIndex;
          _afterTokenTransfers(address(0), to, startTokenId, quantity);
      }
  
      function _transfer(
          address from,
          address to,
          uint256 tokenId
      ) private {
          TokenOwnership memory prevOwnership = ownershipOf(tokenId);
  
          bool isApprovedOrOwner = (_msgSender() == prevOwnership.addr ||
              getApproved(tokenId) == _msgSender() ||
              isApprovedForAll(prevOwnership.addr, _msgSender()));
  
          require(
              isApprovedOrOwner,
              "ERC721A: transfer caller is not owner nor approved"
          );
  
          require(
              prevOwnership.addr == from,
              "ERC721A: transfer from incorrect owner"
          );
          require(to != address(0), "ERC721A: transfer to the zero address");
  
          _beforeTokenTransfers(from, to, tokenId, 1);
          _approve(address(0), tokenId, prevOwnership.addr);
  
          _addressData[from].balance -= 1;
          _addressData[to].balance += 1;
          _ownerships[tokenId] = TokenOwnership(to, uint64(block.timestamp));
          uint256 nextTokenId = tokenId + 1;
          if (_ownerships[nextTokenId].addr == address(0)) {
              if (_exists(nextTokenId)) {
                  _ownerships[nextTokenId] = TokenOwnership(
                      prevOwnership.addr,
                      prevOwnership.startTimestamp
                  );
              }
          }
  
          emit Transfer(from, to, tokenId);
          _afterTokenTransfers(from, to, tokenId, 1);
      }
  
      function _approve(
          address to,
          uint256 tokenId,
          address owner
      ) private {
          _tokenApprovals[tokenId] = to;
          emit Approval(owner, to, tokenId);
      }
  
      uint256 public nextOwnerToExplicitlySet = 0;
  
      function _setOwnersExplicit(uint256 quantity) internal {
          uint256 oldNextOwnerToSet = nextOwnerToExplicitlySet;
          require(quantity > 0, "quantity must be nonzero");
          uint256 endIndex = oldNextOwnerToSet + quantity - 1;
          if (endIndex > collectionSize - 1) {
              endIndex = collectionSize - 1;
          }
          require(_exists(endIndex), "not enough minted yet for this cleanup");
          for (uint256 i = oldNextOwnerToSet; i <= endIndex; i++) {
              if (_ownerships[i].addr == address(0)) {
                  TokenOwnership memory ownership = ownershipOf(i);
                  _ownerships[i] = TokenOwnership(
                      ownership.addr,
                      ownership.startTimestamp
                  );
              }
          }
          nextOwnerToExplicitlySet = endIndex + 1;
      }
  
      function _checkOnERC721Received(
          address from,
          address to,
          uint256 tokenId,
          bytes memory _data
      ) private returns (bool) {
          if (to.isContract()) {
              try
                  IERC721Receiver(to).onERC721Received(
                      _msgSender(),
                      from,
                      tokenId,
                      _data
                  )
              returns (bytes4 retval) {
                  return retval == IERC721Receiver(to).onERC721Received.selector;
              } catch (bytes memory reason) {
                  if (reason.length == 0) {
                      revert(
                          "ERC721A: transfer to non ERC721Receiver implementer"
                      );
                  } else {
                      assembly {
                          revert(add(32, reason), mload(reason))
                      }
                  }
              }
          } else {
              return true;
          }
      }
  
      function _beforeTokenTransfers(
          address from,
          address to,
          uint256 startTokenId,
          uint256 quantity
      ) internal virtual {}
  
      function _afterTokenTransfers(
          address from,
          address to,
          uint256 startTokenId,
          uint256 quantity
      ) internal virtual {}
  }
  
  contract Archives is Ownable, ERC721A, ReentrancyGuard {
      using Strings for uint256;
      using SafeMath for uint256;
  
      uint8 public MAX_PER_Transaction = 10; // maximum amount that user can mint per transaction
      uint8 public MAX_PER_Address = 25;
      uint8 private reserveNFTs;
  
  
     
      uint256 public price = 0.033 ether; //0.08 WL price 0.1 eth public sale
  
      uint96 public royaltyFeesInBips;
      address public royaltyAddress;
  
      uint256 private constant TotalCollectionSize_ = 2222; // total number of nfts
      uint256 private constant MaxMintPerBatch_ = 25; //max that owner can mint per traction
  
      
  
      bool public revealed = false;
      bool public paused = true;
  
      
  
      string private baseTokenURI;
      string private notRevealedURI;
  
      
  
      constructor(uint96 _royaltyFeesInBips, address _royaltyAddress, string memory _notRevealedURI)
          ERC721A(
              "ARCHIVES",
              "AH!",
              MaxMintPerBatch_,
              TotalCollectionSize_
          )
      {
          setNotRevealedURI(
              _notRevealedURI
          );
          
          royaltyFeesInBips = _royaltyFeesInBips;
          royaltyAddress = _royaltyAddress;
      }
  
  
     
  
  
      function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
          external
          view
          virtual
          returns (address, uint256)
      {
          return (royaltyAddress, calculateRoyalty(_salePrice));
      }
  
      function calculateRoyalty(uint256 _salePrice)
          public
          view
          returns (uint256)
      {
          return (_salePrice / 10000) * royaltyFeesInBips;
      }
  
  
  
      function supportsInterface(bytes4 interfaceId)
          public
          view
          override(ERC721A)
          returns (bool)
      {
          return
              interfaceId == 0x2a55205a || super.supportsInterface(interfaceId);
      }
  
     
      function reserveNFT(uint8 quantity) external onlyOwner {
          require(
              totalSupply() + quantity <= collectionSize,
              "reached max supply"
          );
          require(reserveNFTs + quantity <= 25, "Reserve limit exceeded");
          reserveNFTs = reserveNFTs + quantity;
          _safeMint(msg.sender, quantity);
      }
  
      function mint(uint256 quantity) external payable {
          require(!paused, "mint is paused");
          require(
              totalSupply() + quantity <= TotalCollectionSize_,
              "reached max supply"
          );
          require(
              (numberMinted(msg.sender) + quantity <= MAX_PER_Address),
              "Quantity exceeds allowed Mints"
          );
          require(quantity <= MAX_PER_Transaction, "can not mint this many");
          require(msg.value >= price * quantity, "Need to send more ETH.");
          _safeMint(msg.sender, quantity);
      }
  
      
  
      function tokenURI(uint256 tokenId)
          public
          view
          virtual
          override
          returns (string memory)
      {
          require(
              _exists(tokenId),
              "ERC721Metadata: URI query for nonexistent token"
          );
          if (revealed) {
              string memory baseURI = _baseURI();
              return
                  bytes(baseURI).length > 0
                      ? string(
                          abi.encodePacked(baseURI, tokenId.toString(), ".json")
                      )
                      : "";
          } else {
              return notRevealedURI;
          }
      }
  
      function setNotRevealedURI(string memory URI) public onlyOwner {
          notRevealedURI = URI;
      }
  
      function setBaseURI(string memory baseURI) public onlyOwner {
          baseTokenURI = baseURI;
      }
  
      function _baseURI() internal view virtual override returns (string memory) {
          return baseTokenURI;
      }
  
      function numberMinted(address owner) public view returns (uint256) {
          return _numberMinted(owner);
      }
  
      function getOwnershipData(uint256 tokenId)
          external
          view
          returns (TokenOwnership memory)
      {
          return ownershipOf(tokenId);
      }
  
      function withdraw() external onlyOwner nonReentrant {
          uint256 balance = address(this).balance;
  
         (bool os, ) = payable(owner()).call{value: balance}("");
         require(os);
      }
  
      function reveal() external onlyOwner {
          revealed = !revealed;
      }
  
      function setPrice(uint256 _newPrice) external onlyOwner {
          price = _newPrice;
      }
  
     
      function changeMAX_PER_Transaction(uint8 q) external onlyOwner {
          MAX_PER_Transaction = q;
      }
  
      function changeMAX_PER_Address(uint8 q) external onlyOwner {
          MAX_PER_Address = q;
      }
  
  
       function setRoyaltyInfo(address _receiver, uint96 _royaltyFeesInBips)
          public
          onlyOwner
      {
          royaltyAddress = _receiver;
          royaltyFeesInBips = _royaltyFeesInBips;
      }
  
  
  
      
  
      function pause() external onlyOwner {
          paused = !paused;
      }
  
      
  
      function giveaway(address a, uint256 q) external onlyOwner {
          _safeMint(a, q);
      }
  }