/**
 *Submitted for verification at Etherscan.io on 2022-11-24
*/

pragma solidity ^0.8.0;
 
/**
* @dev String operations.
*/
library Strings {
   bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
   uint8 private constant _ADDRESS_LENGTH = 20;
 
   /**
    * @dev Converts a `uint256` to its ASCII `string` decimal representation.
    */
   function toString(uint256 value) internal pure returns (string memory) {
       // Inspired by OraclizeAPI's implementation - MIT licence
       // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
 
       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);
   }
 
   /**
    * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
    */
   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);
   }
 
   /**
    * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed 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);
   }
 
   /**
    * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
    */
   function toHexString(address addr) internal pure returns (string memory) {
       return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
   }
}
 
// File: @openzeppelin/contracts/utils/Address.sol
 
 
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
 
pragma solidity ^0.8.1;
 
/**
* @dev Collection of functions related to the address type
*/
library Address {
   /**
    * @dev Returns true if `account` is a contract.
    *
    * [IMPORTANT]
    * ====
    * It is unsafe to assume that an address for which this function returns
    * false is an externally-owned account (EOA) and not a contract.
    *
    * Among others, `isContract` will return false for the following
    * types of addresses:
    *
    *  - an externally-owned account
    *  - a contract in construction
    *  - an address where a contract will be created
    *  - an address where a contract lived, but was destroyed
    * ====
    *
    * [IMPORTANT]
    * ====
    * You shouldn't rely on `isContract` to protect against flash loan attacks!
    *
    * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
    * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
    * constructor.
    * ====
    */
   function isContract(address account) internal view returns (bool) {
       // This method relies on extcodesize/address.code.length, which returns 0
       // for contracts in construction, since the code is only stored at the end
       // of the constructor execution.
 
       return account.code.length > 0;
   }
 
   /**
    * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
    * `recipient`, forwarding all available gas and reverting on errors.
    *
    * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
    * of certain opcodes, possibly making contracts go over the 2300 gas limit
    * imposed by `transfer`, making them unable to receive funds via
    * `transfer`. {sendValue} removes this limitation.
    *
    * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
    *
    * IMPORTANT: because control is transferred to `recipient`, care must be
    * taken to not create reentrancy vulnerabilities. Consider using
    * {ReentrancyGuard} or the
    * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
    */
   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");
   }
 
   /**
    * @dev Performs a Solidity function call using a low level `call`. A
    * plain `call` is an unsafe replacement for a function call: use this
    * function instead.
    *
    * If `target` reverts with a revert reason, it is bubbled up by this
    * function (like regular Solidity function calls).
    *
    * Returns the raw returned data. To convert to the expected return value,
    * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
    *
    * Requirements:
    *
    * - `target` must be a contract.
    * - calling `target` with `data` must not revert.
    *
    * _Available since v3.1._
    */
   function functionCall(address target, bytes memory data) internal returns (bytes memory) {
       return functionCall(target, data, "Address: low-level call failed");
   }
 
   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
    * `errorMessage` as a fallback revert reason when `target` reverts.
    *
    * _Available since v3.1._
    */
   function functionCall(
       address target,
       bytes memory data,
       string memory errorMessage
   ) internal returns (bytes memory) {
       return functionCallWithValue(target, data, 0, errorMessage);
   }
 
   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
    * but also transferring `value` wei to `target`.
    *
    * Requirements:
    *
    * - the calling contract must have an ETH balance of at least `value`.
    * - the called Solidity function must be `payable`.
    *
    * _Available since v3.1._
    */
   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");
   }
 
   /**
    * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
    * with `errorMessage` as a fallback revert reason when `target` reverts.
    *
    * _Available since v3.1._
    */
   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);
   }
 
   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
    * but performing a static call.
    *
    * _Available since v3.3._
    */
   function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
       return functionStaticCall(target, data, "Address: low-level static call failed");
   }
 
   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
    * but performing a static call.
    *
    * _Available since v3.3._
    */
   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);
   }
 
   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
    * but performing a delegate call.
    *
    * _Available since v3.4._
    */
   function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
       return functionDelegateCall(target, data, "Address: low-level delegate call failed");
   }
 
   /**
    * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
    * but performing a delegate call.
    *
    * _Available since v3.4._
    */
   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);
   }
 
   /**
    * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
    * revert reason using the provided one.
    *
    * _Available since v4.3._
    */
   function verifyCallResult(
       bool success,
       bytes memory returndata,
       string memory errorMessage
   ) internal pure returns (bytes memory) {
       if (success) {
           return returndata;
       } else {
           // Look for revert reason and bubble it up if present
           if (returndata.length > 0) {
               // The easiest way to bubble the revert reason is using memory via assembly
               /// @solidity memory-safe-assembly
               assembly {
                   let returndata_size := mload(returndata)
                   revert(add(32, returndata), returndata_size)
               }
           } else {
               revert(errorMessage);
           }
       }
   }
}
 
// File: @openzeppelin/contracts/security/ReentrancyGuard.sol
 
 
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
 
pragma solidity ^0.8.0;
 
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
   // Booleans are more expensive than uint256 or any type that takes up a full
   // word because each write operation emits an extra SLOAD to first read the
   // slot's contents, replace the bits taken up by the boolean, and then write
   // back. This is the compiler's defense against contract upgrades and
   // pointer aliasing, and it cannot be disabled.
 
   // The values being non-zero value makes deployment a bit more expensive,
   // but in exchange the refund on every call to nonReentrant will be lower in
   // amount. Since refunds are capped to a percentage of the total
   // transaction's gas, it is best to keep them low in cases like this one, to
   // increase the likelihood of the full refund coming into effect.
   uint256 private constant _NOT_ENTERED = 1;
   uint256 private constant _ENTERED = 2;
 
   uint256 private _status;
 
   constructor() {
       _status = _NOT_ENTERED;
   }
 
   /**
    * @dev Prevents a contract from calling itself, directly or indirectly.
    * Calling a `nonReentrant` function from another `nonReentrant`
    * function is not supported. It is possible to prevent this from happening
    * by making the `nonReentrant` function external, and making it call a
    * `private` function that does the actual work.
    */
   modifier nonReentrant() {
       // On the first call to nonReentrant, _notEntered will be true
       require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
 
       // Any calls to nonReentrant after this point will fail
       _status = _ENTERED;
 
       _;
 
       // By storing the original value once again, a refund is triggered (see
       // https://eips.ethereum.org/EIPS/eip-2200)
       _status = _NOT_ENTERED;
   }
}
 
// File: @openzeppelin/contracts/utils/Context.sol
 
 
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
 
pragma solidity ^0.8.0;
 
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
   function _msgSender() internal view virtual returns (address) {
       return msg.sender;
   }
 
   function _msgData() internal view virtual returns (bytes calldata) {
       return msg.data;
   }
}
 
// File: @openzeppelin/contracts/access/Ownable.sol
 
 
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
 
pragma solidity ^0.8.0;
 
 
/**
* @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 Ownable is Context {
   address private _owner;
 
   event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
 
   /**
    * @dev Initializes the contract setting the deployer as the initial owner.
    */
   constructor() {
       _transferOwnership(_msgSender());
   }
 
   /**
    * @dev Throws if called by any account other than the owner.
    */
   modifier onlyOwner() {
       _checkOwner();
       _;
   }
 
   /**
    * @dev Returns the address of the current owner.
    */
   function owner() public view virtual returns (address) {
       return _owner;
   }
 
   /**
    * @dev Throws if the sender is not the owner.
    */
   function _checkOwner() internal view virtual {
       require(owner() == _msgSender(), "Ownable: caller is not the owner");
   }
 
   /**
    * @dev Leaves the contract without owner. It will not be possible to call
    * `onlyOwner` functions anymore. Can only be called by the current owner.
    *
    * NOTE: Renouncing ownership will leave the contract without an owner,
    * thereby removing any functionality that is only available to the owner.
    */
   function renounceOwnership() public virtual onlyOwner {
       _transferOwnership(address(0));
   }
 
   /**
    * @dev Transfers ownership of the contract to a new account (`newOwner`).
    * Can only be called by the current owner.
    */
   function transferOwnership(address newOwner) public virtual onlyOwner {
       require(newOwner != address(0), "Ownable: new owner is the zero address");
       _transferOwnership(newOwner);
   }
 
   /**
    * @dev Transfers ownership of the contract to a new account (`newOwner`).
    * Internal function without access restriction.
    */
   function _transferOwnership(address newOwner) internal virtual {
       address oldOwner = _owner;
       _owner = newOwner;
       emit OwnershipTransferred(oldOwner, newOwner);
   }
}
 
// File: erc721a/contracts/IERC721A.sol
 
 
// ERC721A Contracts v4.1.0
// Creator: Chiru Labs
 
pragma solidity ^0.8.4;
 
/**
* @dev Interface of an ERC721A compliant contract.
*/
interface IERC721A {
   /**
    * The caller must own the token or be an approved operator.
    */
   error ApprovalCallerNotOwnerNorApproved();
 
   /**
    * The token does not exist.
    */
   error ApprovalQueryForNonexistentToken();
 
   /**
    * The caller cannot approve to their own address.
    */
   error ApproveToCaller();
 
   /**
    * Cannot query the balance for the zero address.
    */
   error BalanceQueryForZeroAddress();
 
   /**
    * Cannot mint to the zero address.
    */
   error MintToZeroAddress();
 
   /**
    * The quantity of tokens minted must be more than zero.
    */
   error MintZeroQuantity();
 
   /**
    * The token does not exist.
    */
   error OwnerQueryForNonexistentToken();
 
   /**
    * The caller must own the token or be an approved operator.
    */
   error TransferCallerNotOwnerNorApproved();
 
   /**
    * The token must be owned by `from`.
    */
   error TransferFromIncorrectOwner();
 
   /**
    * Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
    */
   error TransferToNonERC721ReceiverImplementer();
 
   /**
    * Cannot transfer to the zero address.
    */
   error TransferToZeroAddress();
 
   /**
    * The token does not exist.
    */
   error URIQueryForNonexistentToken();
 
   /**
    * The `quantity` minted with ERC2309 exceeds the safety limit.
    */
   error MintERC2309QuantityExceedsLimit();
 
   /**
    * The `extraData` cannot be set on an unintialized ownership slot.
    */
   error OwnershipNotInitializedForExtraData();
 
   struct TokenOwnership {
       // The address of the owner.
       address addr;
       // Keeps track of the start time of ownership with minimal overhead for tokenomics.
       uint64 startTimestamp;
       // Whether the token has been burned.
       bool burned;
       // Arbitrary data similar to `startTimestamp` that can be set through `_extraData`.
       uint24 extraData;
   }
 
   /**
    * @dev Returns the total amount of tokens stored by the contract.
    *
    * Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
    */
   function totalSupply() external view returns (uint256);
 
   // ==============================
   //            IERC165
   // ==============================
 
   /**
    * @dev Returns true if this contract implements the interface defined by
    * `interfaceId`. See the corresponding
    * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
    * to learn more about how these ids are created.
    *
    * This function call must use less than 30 000 gas.
    */
   function supportsInterface(bytes4 interfaceId) external view returns (bool);
 
   // ==============================
   //            IERC721
   // ==============================
 
   /**
    * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
    */
   event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
 
   /**
    * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
    */
   event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
 
   /**
    * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
    */
   event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
 
   /**
    * @dev Returns the number of tokens in ``owner``'s account.
    */
   function balanceOf(address owner) external view returns (uint256 balance);
 
   /**
    * @dev Returns the owner of the `tokenId` token.
    *
    * Requirements:
    *
    * - `tokenId` must exist.
    */
   function ownerOf(uint256 tokenId) external view returns (address owner);
 
   /**
    * @dev Safely transfers `tokenId` token from `from` to `to`.
    *
    * Requirements:
    *
    * - `from` cannot be the zero address.
    * - `to` cannot be the zero address.
    * - `tokenId` token must exist and be owned by `from`.
    * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
    * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    *
    * Emits a {Transfer} event.
    */
   function safeTransferFrom(
       address from,
       address to,
       uint256 tokenId,
       bytes calldata data
   ) external;
 
   /**
    * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
    * are aware of the ERC721 protocol to prevent tokens from being forever locked.
    *
    * Requirements:
    *
    * - `from` cannot be the zero address.
    * - `to` cannot be the zero address.
    * - `tokenId` token must exist and be owned by `from`.
    * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
    * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    *
    * Emits a {Transfer} event.
    */
   function safeTransferFrom(
       address from,
       address to,
       uint256 tokenId
   ) external;
 
   /**
    * @dev Transfers `tokenId` token from `from` to `to`.
    *
    * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
    *
    * Requirements:
    *
    * - `from` cannot be the zero address.
    * - `to` cannot be the zero address.
    * - `tokenId` token must be owned by `from`.
    * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
    *
    * Emits a {Transfer} event.
    */
   function transferFrom(
       address from,
       address to,
       uint256 tokenId
   ) external;
 
   /**
    * @dev Gives permission to `to` to transfer `tokenId` token to another account.
    * The approval is cleared when the token is transferred.
    *
    * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
    *
    * Requirements:
    *
    * - The caller must own the token or be an approved operator.
    * - `tokenId` must exist.
    *
    * Emits an {Approval} event.
    */
   function approve(address to, uint256 tokenId) external;
 
   /**
    * @dev Approve or remove `operator` as an operator for the caller.
    * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
    *
    * Requirements:
    *
    * - The `operator` cannot be the caller.
    *
    * Emits an {ApprovalForAll} event.
    */
   function setApprovalForAll(address operator, bool _approved) external;
 
   /**
    * @dev Returns the account approved for `tokenId` token.
    *
    * Requirements:
    *
    * - `tokenId` must exist.
    */
   function getApproved(uint256 tokenId) external view returns (address operator);
 
   /**
    * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
    *
    * See {setApprovalForAll}
    */
   function isApprovedForAll(address owner, address operator) external view returns (bool);
 
   // ==============================
   //        IERC721Metadata
   // ==============================
 
   /**
    * @dev Returns the token collection name.
    */
   function name() external view returns (string memory);
 
   /**
    * @dev Returns the token collection symbol.
    */
   function symbol() external view returns (string memory);
 
   /**
    * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
    */
   function tokenURI(uint256 tokenId) external view returns (string memory);
 
   // ==============================
   //            IERC2309
   // ==============================
 
   /**
    * @dev Emitted when tokens in `fromTokenId` to `toTokenId` (inclusive) is transferred from `from` to `to`,
    * as defined in the ERC2309 standard. See `_mintERC2309` for more details.
    */
   event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}
 
// File: erc721a/contracts/ERC721A.sol
 
 
// ERC721A Contracts v4.1.0
// Creator: Chiru Labs
 
pragma solidity ^0.8.4;
 
 
/**
* @dev ERC721 token receiver interface.
*/
interface ERC721A__IERC721Receiver {
   function onERC721Received(
       address operator,
       address from,
       uint256 tokenId,
       bytes calldata data
   ) external returns (bytes4);
}
 
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard,
* including the Metadata extension. Built to optimize for lower gas during batch mints.
*
* Assumes serials are sequentially minted starting at `_startTokenId()`
* (defaults to 0, e.g. 0, 1, 2, 3..).
*
* Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
*
* Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
*/
contract ERC721A is IERC721A {
   // Mask of an entry in packed address data.
   uint256 private constant BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
 
   // The bit position of `numberMinted` in packed address data.
   uint256 private constant BITPOS_NUMBER_MINTED = 64;
 
   // The bit position of `numberBurned` in packed address data.
   uint256 private constant BITPOS_NUMBER_BURNED = 128;
 
   // The bit position of `aux` in packed address data.
   uint256 private constant BITPOS_AUX = 192;
 
   // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
   uint256 private constant BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
 
   // The bit position of `startTimestamp` in packed ownership.
   uint256 private constant BITPOS_START_TIMESTAMP = 160;
 
   // The bit mask of the `burned` bit in packed ownership.
   uint256 private constant BITMASK_BURNED = 1 << 224;
 
   // The bit position of the `nextInitialized` bit in packed ownership.
   uint256 private constant BITPOS_NEXT_INITIALIZED = 225;
 
   // The bit mask of the `nextInitialized` bit in packed ownership.
   uint256 private constant BITMASK_NEXT_INITIALIZED = 1 << 225;
 
   // The bit position of `extraData` in packed ownership.
   uint256 private constant BITPOS_EXTRA_DATA = 232;
 
   // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
   uint256 private constant BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
 
   // The mask of the lower 160 bits for addresses.
   uint256 private constant BITMASK_ADDRESS = (1 << 160) - 1;
 
   // The maximum `quantity` that can be minted with `_mintERC2309`.
   // This limit is to prevent overflows on the address data entries.
   // For a limit of 5000, a total of 3.689e15 calls to `_mintERC2309`
   // is required to cause an overflow, which is unrealistic.
   uint256 private constant MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
 
   // The tokenId of the next token to be minted.
   uint256 private _currentIndex;
 
   // The number of tokens burned.
   uint256 private _burnCounter;
 
   // Token name
   string private _name;
 
   // Token symbol
   string private _symbol;
 
   // Mapping from token ID to ownership details
   // An empty struct value does not necessarily mean the token is unowned.
   // See `_packedOwnershipOf` implementation for details.
   //
   // Bits Layout:
   // - [0..159]   `addr`
   // - [160..223] `startTimestamp`
   // - [224]      `burned`
   // - [225]      `nextInitialized`
   // - [232..255] `extraData`
   mapping(uint256 => uint256) private _packedOwnerships;
 
   // Mapping owner address to address data.
   //
   // Bits Layout:
   // - [0..63]    `balance`
   // - [64..127]  `numberMinted`
   // - [128..191] `numberBurned`
   // - [192..255] `aux`
   mapping(address => uint256) private _packedAddressData;
 
   // Mapping from token ID to approved address.
   mapping(uint256 => address) private _tokenApprovals;
 
   // Mapping from owner to operator approvals
   mapping(address => mapping(address => bool)) private _operatorApprovals;
 
   constructor(string memory name_, string memory symbol_) {
       _name = name_;
       _symbol = symbol_;
       _currentIndex = _startTokenId();
   }
 
   /**
    * @dev Returns the starting token ID.
    * To change the starting token ID, please override this function.
    */
   function _startTokenId() internal view virtual returns (uint256) {
       return 0;
   }
 
   /**
    * @dev Returns the next token ID to be minted.
    */
   function _nextTokenId() internal view returns (uint256) {
       return _currentIndex;
   }
 
   /**
    * @dev Returns the total number of tokens in existence.
    * Burned tokens will reduce the count.
    * To get the total number of tokens minted, please see `_totalMinted`.
    */
   function totalSupply() public view override returns (uint256) {
       // Counter underflow is impossible as _burnCounter cannot be incremented
       // more than `_currentIndex - _startTokenId()` times.
       unchecked {
           return _currentIndex - _burnCounter - _startTokenId();
       }
   }
 
   /**
    * @dev Returns the total amount of tokens minted in the contract.
    */
   function _totalMinted() internal view returns (uint256) {
       // Counter underflow is impossible as _currentIndex does not decrement,
       // and it is initialized to `_startTokenId()`
       unchecked {
           return _currentIndex - _startTokenId();
       }
   }
 
   /**
    * @dev Returns the total number of tokens burned.
    */
   function _totalBurned() internal view returns (uint256) {
       return _burnCounter;
   }
 
   /**
    * @dev See {IERC165-supportsInterface}.
    */
   function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
       // The interface IDs are constants representing the first 4 bytes of the XOR of
       // all function selectors in the interface. See: https://eips.ethereum.org/EIPS/eip-165
       // e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`
       return
           interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
           interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
           interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
   }
 
   /**
    * @dev See {IERC721-balanceOf}.
    */
   function balanceOf(address owner) public view override returns (uint256) {
       if (owner == address(0)) revert BalanceQueryForZeroAddress();
       return _packedAddressData[owner] & BITMASK_ADDRESS_DATA_ENTRY;
   }
 
   /**
    * Returns the number of tokens minted by `owner`.
    */
   function _numberMinted(address owner) internal view returns (uint256) {
       return (_packedAddressData[owner] >> BITPOS_NUMBER_MINTED) & BITMASK_ADDRESS_DATA_ENTRY;
   }
 
   /**
    * Returns the number of tokens burned by or on behalf of `owner`.
    */
   function _numberBurned(address owner) internal view returns (uint256) {
       return (_packedAddressData[owner] >> BITPOS_NUMBER_BURNED) & BITMASK_ADDRESS_DATA_ENTRY;
   }
 
   /**
    * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
    */
   function _getAux(address owner) internal view returns (uint64) {
       return uint64(_packedAddressData[owner] >> BITPOS_AUX);
   }
 
   /**
    * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
    * If there are multiple variables, please pack them into a uint64.
    */
   function _setAux(address owner, uint64 aux) internal {
       uint256 packed = _packedAddressData[owner];
       uint256 auxCasted;
       // Cast `aux` with assembly to avoid redundant masking.
       assembly {
           auxCasted := aux
       }
       packed = (packed & BITMASK_AUX_COMPLEMENT) | (auxCasted << BITPOS_AUX);
       _packedAddressData[owner] = packed;
   }
 
   /**
    * Returns the packed ownership data of `tokenId`.
    */
   function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
       uint256 curr = tokenId;
 
       unchecked {
           if (_startTokenId() <= curr)
               if (curr < _currentIndex) {
                   uint256 packed = _packedOwnerships[curr];
                   // If not burned.
                   if (packed & BITMASK_BURNED == 0) {
                       // Invariant:
                       // There will always be an ownership that has an address and is not burned
                       // before an ownership that does not have an address and is not burned.
                       // Hence, curr will not underflow.
                       //
                       // We can directly compare the packed value.
                       // If the address is zero, packed is zero.
                       while (packed == 0) {
                           packed = _packedOwnerships[--curr];
                       }
                       return packed;
                   }
               }
       }
       revert OwnerQueryForNonexistentToken();
   }
 
   /**
    * Returns the unpacked `TokenOwnership` struct from `packed`.
    */
   function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
       ownership.addr = address(uint160(packed));
       ownership.startTimestamp = uint64(packed >> BITPOS_START_TIMESTAMP);
       ownership.burned = packed & BITMASK_BURNED != 0;
       ownership.extraData = uint24(packed >> BITPOS_EXTRA_DATA);
   }
 
   /**
    * Returns the unpacked `TokenOwnership` struct at `index`.
    */
   function _ownershipAt(uint256 index) internal view returns (TokenOwnership memory) {
       return _unpackedOwnership(_packedOwnerships[index]);
   }
 
   /**
    * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
    */
   function _initializeOwnershipAt(uint256 index) internal {
       if (_packedOwnerships[index] == 0) {
           _packedOwnerships[index] = _packedOwnershipOf(index);
       }
   }
 
   /**
    * Gas spent here starts off proportional to the maximum mint batch size.
    * It gradually moves to O(1) as tokens get transferred around in the collection over time.
    */
   function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
       return _unpackedOwnership(_packedOwnershipOf(tokenId));
   }
 
   /**
    * @dev Packs ownership data into a single uint256.
    */
   function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
       assembly {
           // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
           owner := and(owner, BITMASK_ADDRESS)
           // `owner | (block.timestamp << BITPOS_START_TIMESTAMP) | flags`.
           result := or(owner, or(shl(BITPOS_START_TIMESTAMP, timestamp()), flags))
       }
   }
 
   /**
    * @dev See {IERC721-ownerOf}.
    */
   function ownerOf(uint256 tokenId) public view override returns (address) {
       return address(uint160(_packedOwnershipOf(tokenId)));
   }
 
   /**
    * @dev See {IERC721Metadata-name}.
    */
   function name() public view virtual override returns (string memory) {
       return _name;
   }
 
   /**
    * @dev See {IERC721Metadata-symbol}.
    */
   function symbol() public view virtual override returns (string memory) {
       return _symbol;
   }
 
   /**
    * @dev See {IERC721Metadata-tokenURI}.
    */
   function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
       if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
 
       string memory baseURI = _baseURI();
       return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
   }
 
   /**
    * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
    * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
    * by default, it can be overridden in child contracts.
    */
   function _baseURI() internal view virtual returns (string memory) {
       return '';
   }
 
   /**
    * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
    */
   function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
       // For branchless setting of the `nextInitialized` flag.
       assembly {
           // `(quantity == 1) << BITPOS_NEXT_INITIALIZED`.
           result := shl(BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
       }
   }
 
   /**
    * @dev See {IERC721-approve}.
    */
   function approve(address to, uint256 tokenId) public override {
       address owner = ownerOf(tokenId);
 
       if (_msgSenderERC721A() != owner)
           if (!isApprovedForAll(owner, _msgSenderERC721A())) {
               revert ApprovalCallerNotOwnerNorApproved();
           }
 
       _tokenApprovals[tokenId] = to;
       emit Approval(owner, to, tokenId);
   }
 
   /**
    * @dev See {IERC721-getApproved}.
    */
   function getApproved(uint256 tokenId) public view override returns (address) {
       if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
 
       return _tokenApprovals[tokenId];
   }
 
   /**
    * @dev See {IERC721-setApprovalForAll}.
    */
   function setApprovalForAll(address operator, bool approved) public virtual override {
       if (operator == _msgSenderERC721A()) revert ApproveToCaller();
 
       _operatorApprovals[_msgSenderERC721A()][operator] = approved;
       emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
   }
 
   /**
    * @dev See {IERC721-isApprovedForAll}.
    */
   function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
       return _operatorApprovals[owner][operator];
   }
 
   /**
    * @dev See {IERC721-safeTransferFrom}.
    */
   function safeTransferFrom(
       address from,
       address to,
       uint256 tokenId
   ) public virtual override {
       safeTransferFrom(from, to, tokenId, '');
   }
 
   /**
    * @dev See {IERC721-safeTransferFrom}.
    */
   function safeTransferFrom(
       address from,
       address to,
       uint256 tokenId,
       bytes memory _data
   ) public virtual override {
       transferFrom(from, to, tokenId);
       if (to.code.length != 0)
           if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
               revert TransferToNonERC721ReceiverImplementer();
           }
   }
 
   /**
    * @dev Returns whether `tokenId` exists.
    *
    * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
    *
    * Tokens start existing when they are minted (`_mint`),
    */
   function _exists(uint256 tokenId) internal view returns (bool) {
       return
           _startTokenId() <= tokenId &&
           tokenId < _currentIndex && // If within bounds,
           _packedOwnerships[tokenId] & BITMASK_BURNED == 0; // and not burned.
   }
 
   /**
    * @dev Equivalent to `_safeMint(to, quantity, '')`.
    */
   function _safeMint(address to, uint256 quantity) internal {
       _safeMint(to, quantity, '');
   }
 
   /**
    * @dev Safely mints `quantity` tokens and transfers them to `to`.
    *
    * Requirements:
    *
    * - If `to` refers to a smart contract, it must implement
    *   {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
    * - `quantity` must be greater than 0.
    *
    * See {_mint}.
    *
    * Emits a {Transfer} event for each mint.
    */
   function _safeMint(
       address to,
       uint256 quantity,
       bytes memory _data
   ) internal {
       _mint(to, quantity);
 
       unchecked {
           if (to.code.length != 0) {
               uint256 end = _currentIndex;
               uint256 index = end - quantity;
               do {
                   if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                       revert TransferToNonERC721ReceiverImplementer();
                   }
               } while (index < end);
               // Reentrancy protection.
               if (_currentIndex != end) revert();
           }
       }
   }
 
   /**
    * @dev Mints `quantity` tokens and transfers them to `to`.
    *
    * Requirements:
    *
    * - `to` cannot be the zero address.
    * - `quantity` must be greater than 0.
    *
    * Emits a {Transfer} event for each mint.
    */
   function _mint(address to, uint256 quantity) internal {
       uint256 startTokenId = _currentIndex;
       if (to == address(0)) revert MintToZeroAddress();
       if (quantity == 0) revert MintZeroQuantity();
 
       _beforeTokenTransfers(address(0), to, startTokenId, quantity);
 
       // Overflows are incredibly unrealistic.
       // `balance` and `numberMinted` have a maximum limit of 2**64.
       // `tokenId` has a maximum limit of 2**256.
       unchecked {
           // Updates:
           // - `balance += quantity`.
           // - `numberMinted += quantity`.
           //
           // We can directly add to the `balance` and `numberMinted`.
           _packedAddressData[to] += quantity * ((1 << BITPOS_NUMBER_MINTED) | 1);
 
           // Updates:
           // - `address` to the owner.
           // - `startTimestamp` to the timestamp of minting.
           // - `burned` to `false`.
           // - `nextInitialized` to `quantity == 1`.
           _packedOwnerships[startTokenId] = _packOwnershipData(
               to,
               _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
           );
 
           uint256 tokenId = startTokenId;
           uint256 end = startTokenId + quantity;
           do {
               emit Transfer(address(0), to, tokenId++);
           } while (tokenId < end);
 
           _currentIndex = end;
       }
       _afterTokenTransfers(address(0), to, startTokenId, quantity);
   }
 
   /**
    * @dev Mints `quantity` tokens and transfers them to `to`.
    *
    * This function is intended for efficient minting only during contract creation.
    *
    * It emits only one {ConsecutiveTransfer} as defined in
    * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
    * instead of a sequence of {Transfer} event(s).
    *
    * Calling this function outside of contract creation WILL make your contract
    * non-compliant with the ERC721 standard.
    * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
    * {ConsecutiveTransfer} event is only permissible during contract creation.
    *
    * Requirements:
    *
    * - `to` cannot be the zero address.
    * - `quantity` must be greater than 0.
    *
    * Emits a {ConsecutiveTransfer} event.
    */
   function _mintERC2309(address to, uint256 quantity) internal {
       uint256 startTokenId = _currentIndex;
       if (to == address(0)) revert MintToZeroAddress();
       if (quantity == 0) revert MintZeroQuantity();
       if (quantity > MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
 
       _beforeTokenTransfers(address(0), to, startTokenId, quantity);
 
       // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
       unchecked {
           // Updates:
           // - `balance += quantity`.
           // - `numberMinted += quantity`.
           //
           // We can directly add to the `balance` and `numberMinted`.
           _packedAddressData[to] += quantity * ((1 << BITPOS_NUMBER_MINTED) | 1);
 
           // Updates:
           // - `address` to the owner.
           // - `startTimestamp` to the timestamp of minting.
           // - `burned` to `false`.
           // - `nextInitialized` to `quantity == 1`.
           _packedOwnerships[startTokenId] = _packOwnershipData(
               to,
               _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
           );
 
           emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
 
           _currentIndex = startTokenId + quantity;
       }
       _afterTokenTransfers(address(0), to, startTokenId, quantity);
   }
 
   /**
    * @dev Returns the storage slot and value for the approved address of `tokenId`.
    */
   function _getApprovedAddress(uint256 tokenId)
       private
       view
       returns (uint256 approvedAddressSlot, address approvedAddress)
   {
       mapping(uint256 => address) storage tokenApprovalsPtr = _tokenApprovals;
       // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId]`.
       assembly {
           // Compute the slot.
           mstore(0x00, tokenId)
           mstore(0x20, tokenApprovalsPtr.slot)
           approvedAddressSlot := keccak256(0x00, 0x40)
           // Load the slot's value from storage.
           approvedAddress := sload(approvedAddressSlot)
       }
   }
 
   /**
    * @dev Returns whether the `approvedAddress` is equals to `from` or `msgSender`.
    */
   function _isOwnerOrApproved(
       address approvedAddress,
       address from,
       address msgSender
   ) private pure returns (bool result) {
       assembly {
           // Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
           from := and(from, BITMASK_ADDRESS)
           // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
           msgSender := and(msgSender, BITMASK_ADDRESS)
           // `msgSender == from || msgSender == approvedAddress`.
           result := or(eq(msgSender, from), eq(msgSender, approvedAddress))
       }
   }
 
   /**
    * @dev Transfers `tokenId` from `from` to `to`.
    *
    * Requirements:
    *
    * - `to` cannot be the zero address.
    * - `tokenId` token must be owned by `from`.
    *
    * Emits a {Transfer} event.
    */
   function transferFrom(
       address from,
       address to,
       uint256 tokenId
   ) public virtual override {
       uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
 
       if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
 
       (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedAddress(tokenId);
 
       // The nested ifs save around 20+ gas over a compound boolean condition.
       if (!_isOwnerOrApproved(approvedAddress, from, _msgSenderERC721A()))
           if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
 
       if (to == address(0)) revert TransferToZeroAddress();
 
       _beforeTokenTransfers(from, to, tokenId, 1);
 
       // Clear approvals from the previous owner.
       assembly {
           if approvedAddress {
               // This is equivalent to `delete _tokenApprovals[tokenId]`.
               sstore(approvedAddressSlot, 0)
           }
       }
 
       // Underflow of the sender's balance is impossible because we check for
       // ownership above and the recipient's balance can't realistically overflow.
       // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
       unchecked {
           // We can directly increment and decrement the balances.
           --_packedAddressData[from]; // Updates: `balance -= 1`.
           ++_packedAddressData[to]; // Updates: `balance += 1`.
 
           // Updates:
           // - `address` to the next owner.
           // - `startTimestamp` to the timestamp of transfering.
           // - `burned` to `false`.
           // - `nextInitialized` to `true`.
           _packedOwnerships[tokenId] = _packOwnershipData(
               to,
               BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
           );
 
           // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
           if (prevOwnershipPacked & BITMASK_NEXT_INITIALIZED == 0) {
               uint256 nextTokenId = tokenId + 1;
               // If the next slot's address is zero and not burned (i.e. packed value is zero).
               if (_packedOwnerships[nextTokenId] == 0) {
                   // If the next slot is within bounds.
                   if (nextTokenId != _currentIndex) {
                       // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                       _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                   }
               }
           }
       }
 
       emit Transfer(from, to, tokenId);
       _afterTokenTransfers(from, to, tokenId, 1);
   }
 
   /**
    * @dev Equivalent to `_burn(tokenId, false)`.
    */
   function _burn(uint256 tokenId) internal virtual {
       _burn(tokenId, false);
   }
 
   /**
    * @dev Destroys `tokenId`.
    * The approval is cleared when the token is burned.
    *
    * Requirements:
    *
    * - `tokenId` must exist.
    *
    * Emits a {Transfer} event.
    */
   function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
       uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
 
       address from = address(uint160(prevOwnershipPacked));
 
       (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedAddress(tokenId);
 
       if (approvalCheck) {
           // The nested ifs save around 20+ gas over a compound boolean condition.
           if (!_isOwnerOrApproved(approvedAddress, from, _msgSenderERC721A()))
               if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
       }
 
       _beforeTokenTransfers(from, address(0), tokenId, 1);
 
       // Clear approvals from the previous owner.
       assembly {
           if approvedAddress {
               // This is equivalent to `delete _tokenApprovals[tokenId]`.
               sstore(approvedAddressSlot, 0)
           }
       }
 
       // Underflow of the sender's balance is impossible because we check for
       // ownership above and the recipient's balance can't realistically overflow.
       // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
       unchecked {
           // Updates:
           // - `balance -= 1`.
           // - `numberBurned += 1`.
           //
           // We can directly decrement the balance, and increment the number burned.
           // This is equivalent to `packed -= 1; packed += 1 << BITPOS_NUMBER_BURNED;`.
           _packedAddressData[from] += (1 << BITPOS_NUMBER_BURNED) - 1;
 
           // Updates:
           // - `address` to the last owner.
           // - `startTimestamp` to the timestamp of burning.
           // - `burned` to `true`.
           // - `nextInitialized` to `true`.
           _packedOwnerships[tokenId] = _packOwnershipData(
               from,
               (BITMASK_BURNED | BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
           );
 
           // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
           if (prevOwnershipPacked & BITMASK_NEXT_INITIALIZED == 0) {
               uint256 nextTokenId = tokenId + 1;
               // If the next slot's address is zero and not burned (i.e. packed value is zero).
               if (_packedOwnerships[nextTokenId] == 0) {
                   // If the next slot is within bounds.
                   if (nextTokenId != _currentIndex) {
                       // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                       _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                   }
               }
           }
       }
 
       emit Transfer(from, address(0), tokenId);
       _afterTokenTransfers(from, address(0), tokenId, 1);
 
       // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
       unchecked {
           _burnCounter++;
       }
   }
 
   /**
    * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
    *
    * @param from address representing the previous owner of the given token ID
    * @param to target address that will receive the tokens
    * @param tokenId uint256 ID of the token to be transferred
    * @param _data bytes optional data to send along with the call
    * @return bool whether the call correctly returned the expected magic value
    */
   function _checkContractOnERC721Received(
       address from,
       address to,
       uint256 tokenId,
       bytes memory _data
   ) private returns (bool) {
       try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
           bytes4 retval
       ) {
           return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
       } catch (bytes memory reason) {
           if (reason.length == 0) {
               revert TransferToNonERC721ReceiverImplementer();
           } else {
               assembly {
                   revert(add(32, reason), mload(reason))
               }
           }
       }
   }
 
   /**
    * @dev Directly sets the extra data for the ownership data `index`.
    */
   function _setExtraDataAt(uint256 index, uint24 extraData) internal {
       uint256 packed = _packedOwnerships[index];
       if (packed == 0) revert OwnershipNotInitializedForExtraData();
       uint256 extraDataCasted;
       // Cast `extraData` with assembly to avoid redundant masking.
       assembly {
           extraDataCasted := extraData
       }
       packed = (packed & BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << BITPOS_EXTRA_DATA);
       _packedOwnerships[index] = packed;
   }
 
   /**
    * @dev Returns the next extra data for the packed ownership data.
    * The returned result is shifted into position.
    */
   function _nextExtraData(
       address from,
       address to,
       uint256 prevOwnershipPacked
   ) private view returns (uint256) {
       uint24 extraData = uint24(prevOwnershipPacked >> BITPOS_EXTRA_DATA);
       return uint256(_extraData(from, to, extraData)) << BITPOS_EXTRA_DATA;
   }
 
   /**
    * @dev Called during each token transfer to set the 24bit `extraData` field.
    * Intended to be overridden by the cosumer contract.
    *
    * `previousExtraData` - the value of `extraData` before transfer.
    *
    * Calling conditions:
    *
    * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
    * transferred to `to`.
    * - When `from` is zero, `tokenId` will be minted for `to`.
    * - When `to` is zero, `tokenId` will be burned by `from`.
    * - `from` and `to` are never both zero.
    */
   function _extraData(
       address from,
       address to,
       uint24 previousExtraData
   ) internal view virtual returns (uint24) {}
 
   /**
    * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred.
    * This includes minting.
    * And also called before burning one token.
    *
    * startTokenId - the first token id to be transferred
    * quantity - the amount to be transferred
    *
    * Calling conditions:
    *
    * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
    * transferred to `to`.
    * - When `from` is zero, `tokenId` will be minted for `to`.
    * - When `to` is zero, `tokenId` will be burned by `from`.
    * - `from` and `to` are never both zero.
    */
   function _beforeTokenTransfers(
       address from,
       address to,
       uint256 startTokenId,
       uint256 quantity
   ) internal virtual {}
 
   /**
    * @dev Hook that is called after a set of serially-ordered token ids have been transferred.
    * This includes minting.
    * And also called after one token has been burned.
    *
    * startTokenId - the first token id to be transferred
    * quantity - the amount to be transferred
    *
    * Calling conditions:
    *
    * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
    * transferred to `to`.
    * - When `from` is zero, `tokenId` has been minted for `to`.
    * - When `to` is zero, `tokenId` has been burned by `from`.
    * - `from` and `to` are never both zero.
    */
   function _afterTokenTransfers(
       address from,
       address to,
       uint256 startTokenId,
       uint256 quantity
   ) internal virtual {}
 
   /**
    * @dev Returns the message sender (defaults to `msg.sender`).
    *
    * If you are writing GSN compatible contracts, you need to override this function.
    */
   function _msgSenderERC721A() internal view virtual returns (address) {
       return msg.sender;
   }
 
   /**
    * @dev Converts a `uint256` to its ASCII `string` decimal representation.
    */
   function _toString(uint256 value) internal pure returns (string memory ptr) {
       assembly {
           // The maximum value of a uint256 contains 78 digits (1 byte per digit),
           // but we allocate 128 bytes to keep the free memory pointer 32-byte word aliged.
           // We will need 1 32-byte word to store the length,
           // and 3 32-byte words to store a maximum of 78 digits. Total: 32 + 3 * 32 = 128.
           ptr := add(mload(0x40), 128)
           // Update the free memory pointer to allocate.
           mstore(0x40, ptr)
 
           // Cache the end of the memory to calculate the length later.
           let end := ptr
 
           // We write the string from the rightmost digit to the leftmost digit.
           // The following is essentially a do-while loop that also handles the zero case.
           // Costs a bit more than early returning for the zero case,
           // but cheaper in terms of deployment and overall runtime costs.
           for {
               // Initialize and perform the first pass without check.
               let temp := value
               // Move the pointer 1 byte leftwards to point to an empty character slot.
               ptr := sub(ptr, 1)
               // Write the character to the pointer. 48 is the ASCII index of '0'.
               mstore8(ptr, add(48, mod(temp, 10)))
               temp := div(temp, 10)
           } temp {
               // Keep dividing `temp` until zero.
               temp := div(temp, 10)
           } {
               // Body of the for loop.
               ptr := sub(ptr, 1)
               mstore8(ptr, add(48, mod(temp, 10)))
           }
 
           let length := sub(end, ptr)
           // Move the pointer 32 bytes leftwards to make room for the length.
           ptr := sub(ptr, 32)
           // Store the length.
           mstore(ptr, length)
       }
   }
}
 
 
 
pragma solidity ^0.8.0;
 
 
 
 
 
 
contract HongKongPunks is ERC721A, Ownable, ReentrancyGuard {
 using Address for address;
 using Strings for uint;
 
 string  public  baseTokenURI = "ipfs://QmZE5MAvmyC4joiZj36tb6o9KVRo2SZeNtGKcvDRaJWcsD/";
 
 uint256 public  maxSupply = 7777;
 uint256 public  MAX_MINTS_PER_TX = 25;
 uint256 public  FREE_MINTS_PER_TX = 25;
 uint256 public  PUBLIC_SALE_PRICE = 0.003 ether;
 uint256 public  TOTAL_FREE_MINTS = 0;
 bool public isPublicSaleActive = true;
 
 constructor() ERC721A("HongKongPunks", "HONGKONG") {
 
 }
 
 function mint(uint256 numberOfTokens)
     external
     payable
 {
   require(isPublicSaleActive, "Public sale is not open");
   require(
     totalSupply() + numberOfTokens <= maxSupply,
     "Maximum supply exceeded"
   );
   if(totalSupply() + numberOfTokens > TOTAL_FREE_MINTS || numberOfTokens > FREE_MINTS_PER_TX){
       require(
           (PUBLIC_SALE_PRICE * numberOfTokens) <= msg.value,
           "Incorrect ETH value sent"
       );
   }
   _safeMint(msg.sender, numberOfTokens);
 }
 
 function setBaseURI(string memory baseURI)
   public
   onlyOwner
 {
   baseTokenURI = baseURI;
 }
 
 function _startTokenId() internal view virtual override returns (uint256) {
       return 1;
   }
 
 function treasuryMint(uint quantity, address user)
   public
   onlyOwner
 {
   require(
     quantity > 0,
     "Invalid mint amount"
   );
   require(
     totalSupply() + quantity <= maxSupply,
     "Maximum supply exceeded"
   );
   _safeMint(user, quantity);
 }
 
 function withdraw()
   public
   onlyOwner
   nonReentrant
 {
   Address.sendValue(payable(msg.sender), address(this).balance);
 }
 
 function tokenURI(uint _tokenId)
   public
   view
   virtual
   override
   returns (string memory)
 {
   require(
     _exists(_tokenId),
     "ERC721Metadata: URI query for nonexistent token"
   );
   return string(abi.encodePacked(baseTokenURI, "/", _tokenId.toString(), ".json"));
 }
 
 function _baseURI()
   internal
   view
   virtual
   override
   returns (string memory)
 {
   return baseTokenURI;
 }
 
 function setIsPublicSaleActive(bool _isPublicSaleActive)
     external
     onlyOwner
 {
     isPublicSaleActive = _isPublicSaleActive;
 }
 
 function setNumFreeMints(uint256 _numfreemints)
     external
     onlyOwner
 {
     TOTAL_FREE_MINTS = _numfreemints;
 }
 
 function setSalePrice(uint256 _price)
     external
     onlyOwner
 {
     PUBLIC_SALE_PRICE = _price;
 }
 
 function setMaxLimitPerTransaction(uint256 _limit)
     external
     onlyOwner
 {
     MAX_MINTS_PER_TX = _limit;
 }
 
}