creation_sourcecode: // SPDX-License-Identifier: MIT
  
  // File: @openzeppelin/contracts/utils/introspection/IERC165.sol
  
  
  // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Interface of the ERC165 standard, as defined in the
   * https://eips.ethereum.org/EIPS/eip-165[EIP].
   *
   * Implementers can declare support of contract interfaces, which can then be
   * queried by others ({ERC165Checker}).
   *
   * For an implementation, see {ERC165}.
   */
  interface 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);
  }
  
  // File: @openzeppelin/contracts/token/ERC721/IERC721.sol
  
  
  // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Required interface of an ERC721 compliant contract.
   */
  interface IERC721 is IERC165 {
      /**
       * @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 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);
  }
  
  // File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
  
  
  // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @title ERC721 token receiver interface
   * @dev Interface for any contract that wants to support safeTransfers
   * from ERC721 asset contracts.
   */
  interface IERC721Receiver {
      /**
       * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
       * by `operator` from `from`, this function is called.
       *
       * It must return its Solidity selector to confirm the token transfer.
       * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
       *
       * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
       */
      function onERC721Received(
          address operator,
          address from,
          uint256 tokenId,
          bytes calldata data
      ) external returns (bytes4);
  }
  
  // File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol
  
  
  // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
   * @dev See https://eips.ethereum.org/EIPS/eip-721
   */
  interface IERC721Metadata is IERC721 {
      /**
       * @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);
  }
  
  // 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/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/utils/Strings.sol
  
  
  // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
  
  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/introspection/ERC165.sol
  
  
  // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Implementation of the {IERC165} interface.
   *
   * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
   * for the additional interface id that will be supported. For example:
   *
   * ```solidity
   * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
   *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
   * }
   * ```
   *
   * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
   */
  abstract contract ERC165 is IERC165 {
      /**
       * @dev See {IERC165-supportsInterface}.
       */
      function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
          return interfaceId == type(IERC165).interfaceId;
      }
  }
  
  // File: @openzeppelin/contracts/token/ERC721/ERC721.sol
  
  
  // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
   * the Metadata extension, but not including the Enumerable extension, which is available separately as
   * {ERC721Enumerable}.
   */
  contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
      using Address for address;
      using Strings for uint256;
  
      // Token name
      string private _name;
  
      // Token symbol
      string private _symbol;
  
      // Mapping from token ID to owner address
      mapping(uint256 => address) private _owners;
  
      // Mapping owner address to token count
      mapping(address => uint256) private _balances;
  
      // 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;
  
      /**
       * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
       */
      constructor(string memory name_, string memory symbol_) {
          _name = name_;
          _symbol = symbol_;
      }
  
      /**
       * @dev See {IERC165-supportsInterface}.
       */
      function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
          return
              interfaceId == type(IERC721).interfaceId ||
              interfaceId == type(IERC721Metadata).interfaceId ||
              super.supportsInterface(interfaceId);
      }
  
      /**
       * @dev See {IERC721-balanceOf}.
       */
      function balanceOf(address owner) public view virtual override returns (uint256) {
          require(owner != address(0), "ERC721: address zero is not a valid owner");
          return _balances[owner];
      }
  
      /**
       * @dev See {IERC721-ownerOf}.
       */
      function ownerOf(uint256 tokenId) public view virtual override returns (address) {
          address owner = _owners[tokenId];
          require(owner != address(0), "ERC721: invalid token ID");
          return owner;
      }
  
      /**
       * @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) {
          _requireMinted(tokenId);
  
          string memory baseURI = _baseURI();
          return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
      }
  
      /**
       * @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, can be overridden in child contracts.
       */
      function _baseURI() internal view virtual returns (string memory) {
          return "";
      }
  
      /**
       * @dev See {IERC721-approve}.
       */
      function approve(address to, uint256 tokenId) public virtual override {
          address owner = ERC721.ownerOf(tokenId);
          require(to != owner, "ERC721: approval to current owner");
  
          require(
              _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
              "ERC721: approve caller is not token owner nor approved for all"
          );
  
          _approve(to, tokenId);
      }
  
      /**
       * @dev See {IERC721-getApproved}.
       */
      function getApproved(uint256 tokenId) public view virtual override returns (address) {
          _requireMinted(tokenId);
  
          return _tokenApprovals[tokenId];
      }
  
      /**
       * @dev See {IERC721-setApprovalForAll}.
       */
      function setApprovalForAll(address operator, bool approved) public virtual override {
          _setApprovalForAll(_msgSender(), 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-transferFrom}.
       */
      function transferFrom(
          address from,
          address to,
          uint256 tokenId
      ) public virtual override {
          //solhint-disable-next-line max-line-length
          require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
  
          _transfer(from, to, tokenId);
      }
  
      /**
       * @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 {
          require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
          _safeTransfer(from, to, tokenId, data);
      }
  
      /**
       * @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.
       *
       * `data` is additional data, it has no specified format and it is sent in call to `to`.
       *
       * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
       * implement alternative mechanisms to perform token transfer, such as signature-based.
       *
       * Requirements:
       *
       * - `from` cannot be the zero address.
       * - `to` cannot be the zero address.
       * - `tokenId` token must exist and be owned by `from`.
       * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
       *
       * Emits a {Transfer} event.
       */
      function _safeTransfer(
          address from,
          address to,
          uint256 tokenId,
          bytes memory data
      ) internal virtual {
          _transfer(from, to, tokenId);
          require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
      }
  
      /**
       * @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`),
       * and stop existing when they are burned (`_burn`).
       */
      function _exists(uint256 tokenId) internal view virtual returns (bool) {
          return _owners[tokenId] != address(0);
      }
  
      /**
       * @dev Returns whether `spender` is allowed to manage `tokenId`.
       *
       * Requirements:
       *
       * - `tokenId` must exist.
       */
      function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
          address owner = ERC721.ownerOf(tokenId);
          return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
      }
  
      /**
       * @dev Safely mints `tokenId` and transfers it to `to`.
       *
       * Requirements:
       *
       * - `tokenId` must not exist.
       * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
       *
       * Emits a {Transfer} event.
       */
      function _safeMint(address to, uint256 tokenId) internal virtual {
          _safeMint(to, tokenId, "");
      }
  
      /**
       * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
       * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
       */
      function _safeMint(
          address to,
          uint256 tokenId,
          bytes memory data
      ) internal virtual {
          _mint(to, tokenId);
          require(
              _checkOnERC721Received(address(0), to, tokenId, data),
              "ERC721: transfer to non ERC721Receiver implementer"
          );
      }
  
      /**
       * @dev Mints `tokenId` and transfers it to `to`.
       *
       * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
       *
       * Requirements:
       *
       * - `tokenId` must not exist.
       * - `to` cannot be the zero address.
       *
       * Emits a {Transfer} event.
       */
      function _mint(address to, uint256 tokenId) internal virtual {
          require(to != address(0), "ERC721: mint to the zero address");
          require(!_exists(tokenId), "ERC721: token already minted");
  
          _beforeTokenTransfer(address(0), to, tokenId);
  
          _balances[to] += 1;
          _owners[tokenId] = to;
  
          emit Transfer(address(0), to, tokenId);
  
          _afterTokenTransfer(address(0), to, tokenId);
      }
  
      /**
       * @dev Destroys `tokenId`.
       * The approval is cleared when the token is burned.
       *
       * Requirements:
       *
       * - `tokenId` must exist.
       *
       * Emits a {Transfer} event.
       */
      function _burn(uint256 tokenId) internal virtual {
          address owner = ERC721.ownerOf(tokenId);
  
          _beforeTokenTransfer(owner, address(0), tokenId);
  
          // Clear approvals
          _approve(address(0), tokenId);
  
          _balances[owner] -= 1;
          delete _owners[tokenId];
  
          emit Transfer(owner, address(0), tokenId);
  
          _afterTokenTransfer(owner, address(0), tokenId);
      }
  
      /**
       * @dev Transfers `tokenId` from `from` to `to`.
       *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
       *
       * Requirements:
       *
       * - `to` cannot be the zero address.
       * - `tokenId` token must be owned by `from`.
       *
       * Emits a {Transfer} event.
       */
      function _transfer(
          address from,
          address to,
          uint256 tokenId
      ) internal virtual {
          require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
          require(to != address(0), "ERC721: transfer to the zero address");
  
          _beforeTokenTransfer(from, to, tokenId);
  
          // Clear approvals from the previous owner
          _approve(address(0), tokenId);
  
          _balances[from] -= 1;
          _balances[to] += 1;
          _owners[tokenId] = to;
  
          emit Transfer(from, to, tokenId);
  
          _afterTokenTransfer(from, to, tokenId);
      }
  
      /**
       * @dev Approve `to` to operate on `tokenId`
       *
       * Emits an {Approval} event.
       */
      function _approve(address to, uint256 tokenId) internal virtual {
          _tokenApprovals[tokenId] = to;
          emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
      }
  
      /**
       * @dev Approve `operator` to operate on all of `owner` tokens
       *
       * Emits an {ApprovalForAll} event.
       */
      function _setApprovalForAll(
          address owner,
          address operator,
          bool approved
      ) internal virtual {
          require(owner != operator, "ERC721: approve to caller");
          _operatorApprovals[owner][operator] = approved;
          emit ApprovalForAll(owner, operator, approved);
      }
  
      /**
       * @dev Reverts if the `tokenId` has not been minted yet.
       */
      function _requireMinted(uint256 tokenId) internal view virtual {
          require(_exists(tokenId), "ERC721: invalid token ID");
      }
  
      /**
       * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
       * The call is not executed if the target address is not a 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 _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.onERC721Received.selector;
              } catch (bytes memory reason) {
                  if (reason.length == 0) {
                      revert("ERC721: transfer to non ERC721Receiver implementer");
                  } else {
                      /// @solidity memory-safe-assembly
                      assembly {
                          revert(add(32, reason), mload(reason))
                      }
                  }
              }
          } else {
              return true;
          }
      }
  
      /**
       * @dev Hook that is called before any token transfer. This includes minting
       * and burning.
       *
       * 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, ``from``'s `tokenId` will be burned.
       * - `from` and `to` are never both zero.
       *
       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
       */
      function _beforeTokenTransfer(
          address from,
          address to,
          uint256 tokenId
      ) internal virtual {}
  
      /**
       * @dev Hook that is called after any transfer of tokens. This includes
       * minting and burning.
       *
       * Calling conditions:
       *
       * - when `from` and `to` are both non-zero.
       * - `from` and `to` are never both zero.
       *
       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
       */
      function _afterTokenTransfer(
          address from,
          address to,
          uint256 tokenId
      ) internal virtual {}
  }
  
  // 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);
      }
  }
  
  abstract contract CreatorOwnable {
      address private _owner;
  
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  
      /**
       * @dev Initializes the contract setting the deployer as the initial owner.
       */
      constructor() {
          _transferOwnership(msg.sender);
      }
  
      /**
       * @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() == msg.sender, "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);
      }
  }
  
  
  // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Interface of the ERC165 standard, as defined in the
   * https://eips.ethereum.org/EIPS/eip-165[EIP].
   *
   * Implementers can declare support of contract interfaces, which can then be
   * queried by others ({ERC165Checker}).
   *
   * For an implementation, see {ERC165}.
   */
  interface IERC165Upgradeable {
      /**
       * @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);
  }
  
  // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Required interface of an ERC721 compliant contract.
   */
  interface IERC721Upgradeable is IERC165Upgradeable {
      /**
       * @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 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);
  }
  
  // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @title ERC721 token receiver interface
   * @dev Interface for any contract that wants to support safeTransfers
   * from ERC721 asset contracts.
   */
  interface IERC721ReceiverUpgradeable {
      /**
       * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
       * by `operator` from `from`, this function is called.
       *
       * It must return its Solidity selector to confirm the token transfer.
       * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
       *
       * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
       */
      function onERC721Received(
          address operator,
          address from,
          uint256 tokenId,
          bytes calldata data
      ) external returns (bytes4);
  }
  
  // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
   * @dev See https://eips.ethereum.org/EIPS/eip-721
   */
  interface IERC721MetadataUpgradeable is IERC721Upgradeable {
      /**
       * @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);
  }
  
  // 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 AddressUpgradeable {
      /**
       * @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 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);
              }
          }
      }
  }
  
  
  // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
  
  pragma solidity ^0.8.2;
  
  /**
   * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
   * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
   * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
   * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
   *
   * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
   * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
   * case an upgrade adds a module that needs to be initialized.
   *
   * For example:
   *
   * [.hljs-theme-light.nopadding]
   * ```
   * contract MyToken is ERC20Upgradeable {
   *     function initialize() initializer public {
   *         __ERC20_init("MyToken", "MTK");
   *     }
   * }
   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
   *     function initializeV2() reinitializer(2) public {
   *         __ERC20Permit_init("MyToken");
   *     }
   * }
   * ```
   *
   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
   *
   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
   *
   * [CAUTION]
   * ====
   * Avoid leaving a contract uninitialized.
   *
   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
   *
   * [.hljs-theme-light.nopadding]
   * ```
   * /// @custom:oz-upgrades-unsafe-allow constructor
   * constructor() {
   *     _disableInitializers();
   * }
   * ```
   * ====
   */
  abstract contract Initializable {
      /**
       * @dev Indicates that the contract has been initialized.
       * @custom:oz-retyped-from bool
       */
      uint8 private _initialized;
  
      /**
       * @dev Indicates that the contract is in the process of being initialized.
       */
      bool private _initializing;
  
      /**
       * @dev Triggered when the contract has been initialized or reinitialized.
       */
      event Initialized(uint8 version);
  
      /**
       * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
       * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
       */
      modifier initializer() {
          bool isTopLevelCall = !_initializing;
          require(
              (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
              "Initializable: contract is already initialized"
          );
          _initialized = 1;
          if (isTopLevelCall) {
              _initializing = true;
          }
          _;
          if (isTopLevelCall) {
              _initializing = false;
              emit Initialized(1);
          }
      }
  
      /**
       * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
       * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
       * used to initialize parent contracts.
       *
       * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
       * initialization step. This is essential to configure modules that are added through upgrades and that require
       * initialization.
       *
       * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
       * a contract, executing them in the right order is up to the developer or operator.
       */
      modifier reinitializer(uint8 version) {
          require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
          _initialized = version;
          _initializing = true;
          _;
          _initializing = false;
          emit Initialized(version);
      }
  
      /**
       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
       * {initializer} and {reinitializer} modifiers, directly or indirectly.
       */
      modifier onlyInitializing() {
          require(_initializing, "Initializable: contract is not initializing");
          _;
      }
  
      /**
       * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
       * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
       * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
       * through proxies.
       */
      function _disableInitializers() internal virtual {
          require(!_initializing, "Initializable: contract is initializing");
          if (_initialized < type(uint8).max) {
              _initialized = type(uint8).max;
              emit Initialized(type(uint8).max);
          }
      }
  }
  
  // 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 ContextUpgradeable is Initializable {
      function __Context_init() internal onlyInitializing {
      }
  
      function __Context_init_unchained() internal onlyInitializing {
      }
      function _msgSender() internal view virtual returns (address) {
          return msg.sender;
      }
  
      function _msgData() internal view virtual returns (bytes calldata) {
          return msg.data;
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[50] private __gap;
  }
  
  
  // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev String operations.
   */
  library StringsUpgradeable {
      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);
      }
  }
  
  
  // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Implementation of the {IERC165} interface.
   *
   * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
   * for the additional interface id that will be supported. For example:
   *
   * ```solidity
   * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
   *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
   * }
   * ```
   *
   * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
   */
  abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
      function __ERC165_init() internal onlyInitializing {
      }
  
      function __ERC165_init_unchained() internal onlyInitializing {
      }
      /**
       * @dev See {IERC165-supportsInterface}.
       */
      function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
          return interfaceId == type(IERC165Upgradeable).interfaceId;
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[50] private __gap;
  }
  
  
  // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)
  
  pragma solidity ^0.8.0;
  
  /**
   * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
   * the Metadata extension, but not including the Enumerable extension, which is available separately as
   * {ERC721Enumerable}.
   */
  contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
      using AddressUpgradeable for address;
      using StringsUpgradeable for uint256;
  
      // Token name
      string private _name;
  
      // Token symbol
      string private _symbol;
  
      // Mapping from token ID to owner address
      mapping(uint256 => address) private _owners;
  
      // Mapping owner address to token count
      mapping(address => uint256) private _balances;
  
      // 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;
  
      /**
       * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
       */
      function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
          __ERC721_init_unchained(name_, symbol_);
      }
  
      function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
          _name = name_;
          _symbol = symbol_;
      }
  
      /**
       * @dev See {IERC165-supportsInterface}.
       */
      function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
          return
              interfaceId == type(IERC721Upgradeable).interfaceId ||
              interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
              super.supportsInterface(interfaceId);
      }
  
      /**
       * @dev See {IERC721-balanceOf}.
       */
      function balanceOf(address owner) public view virtual override returns (uint256) {
          require(owner != address(0), "ERC721: address zero is not a valid owner");
          return _balances[owner];
      }
  
      /**
       * @dev See {IERC721-ownerOf}.
       */
      function ownerOf(uint256 tokenId) public view virtual override returns (address) {
          address owner = _owners[tokenId];
          require(owner != address(0), "ERC721: invalid token ID");
          return owner;
      }
  
      /**
       * @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) {
          _requireMinted(tokenId);
  
          string memory baseURI = _baseURI();
          return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
      }
  
      /**
       * @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, can be overridden in child contracts.
       */
      function _baseURI() internal view virtual returns (string memory) {
          return "";
      }
  
      /**
       * @dev See {IERC721-approve}.
       */
      function approve(address to, uint256 tokenId) public virtual override {
          address owner = ERC721Upgradeable.ownerOf(tokenId);
          require(to != owner, "ERC721: approval to current owner");
  
          require(
              _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
              "ERC721: approve caller is not token owner nor approved for all"
          );
  
          _approve(to, tokenId);
      }
  
      /**
       * @dev See {IERC721-getApproved}.
       */
      function getApproved(uint256 tokenId) public view virtual override returns (address) {
          _requireMinted(tokenId);
  
          return _tokenApprovals[tokenId];
      }
  
      /**
       * @dev See {IERC721-setApprovalForAll}.
       */
      function setApprovalForAll(address operator, bool approved) public virtual override {
          _setApprovalForAll(_msgSender(), 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-transferFrom}.
       */
      function transferFrom(
          address from,
          address to,
          uint256 tokenId
      ) public virtual override {
          //solhint-disable-next-line max-line-length
          require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
  
          _transfer(from, to, tokenId);
      }
  
      /**
       * @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 {
          require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
          _safeTransfer(from, to, tokenId, data);
      }
  
      /**
       * @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.
       *
       * `data` is additional data, it has no specified format and it is sent in call to `to`.
       *
       * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
       * implement alternative mechanisms to perform token transfer, such as signature-based.
       *
       * Requirements:
       *
       * - `from` cannot be the zero address.
       * - `to` cannot be the zero address.
       * - `tokenId` token must exist and be owned by `from`.
       * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
       *
       * Emits a {Transfer} event.
       */
      function _safeTransfer(
          address from,
          address to,
          uint256 tokenId,
          bytes memory data
      ) internal virtual {
          _transfer(from, to, tokenId);
          require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
      }
  
      /**
       * @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`),
       * and stop existing when they are burned (`_burn`).
       */
      function _exists(uint256 tokenId) internal view virtual returns (bool) {
          return _owners[tokenId] != address(0);
      }
  
      /**
       * @dev Returns whether `spender` is allowed to manage `tokenId`.
       *
       * Requirements:
       *
       * - `tokenId` must exist.
       */
      function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
          address owner = ERC721Upgradeable.ownerOf(tokenId);
          return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
      }
  
      /**
       * @dev Safely mints `tokenId` and transfers it to `to`.
       *
       * Requirements:
       *
       * - `tokenId` must not exist.
       * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
       *
       * Emits a {Transfer} event.
       */
      function _safeMint(address to, uint256 tokenId) internal virtual {
          _safeMint(to, tokenId, "");
      }
  
      /**
       * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
       * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
       */
      function _safeMint(
          address to,
          uint256 tokenId,
          bytes memory data
      ) internal virtual {
          _mint(to, tokenId);
          require(
              _checkOnERC721Received(address(0), to, tokenId, data),
              "ERC721: transfer to non ERC721Receiver implementer"
          );
      }
  
      /**
       * @dev Mints `tokenId` and transfers it to `to`.
       *
       * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
       *
       * Requirements:
       *
       * - `tokenId` must not exist.
       * - `to` cannot be the zero address.
       *
       * Emits a {Transfer} event.
       */
      function _mint(address to, uint256 tokenId) internal virtual {
          require(to != address(0), "ERC721: mint to the zero address");
          require(!_exists(tokenId), "ERC721: token already minted");
  
          _beforeTokenTransfer(address(0), to, tokenId);
  
          _balances[to] += 1;
          _owners[tokenId] = to;
  
          emit Transfer(address(0), to, tokenId);
  
          _afterTokenTransfer(address(0), to, tokenId);
      }
  
      /**
       * @dev Destroys `tokenId`.
       * The approval is cleared when the token is burned.
       *
       * Requirements:
       *
       * - `tokenId` must exist.
       *
       * Emits a {Transfer} event.
       */
      function _burn(uint256 tokenId) internal virtual {
          address owner = ERC721Upgradeable.ownerOf(tokenId);
  
          _beforeTokenTransfer(owner, address(0), tokenId);
  
          // Clear approvals
          _approve(address(0), tokenId);
  
          _balances[owner] -= 1;
          delete _owners[tokenId];
  
          emit Transfer(owner, address(0), tokenId);
  
          _afterTokenTransfer(owner, address(0), tokenId);
      }
  
      /**
       * @dev Transfers `tokenId` from `from` to `to`.
       *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
       *
       * Requirements:
       *
       * - `to` cannot be the zero address.
       * - `tokenId` token must be owned by `from`.
       *
       * Emits a {Transfer} event.
       */
      function _transfer(
          address from,
          address to,
          uint256 tokenId
      ) internal virtual {
          require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
          require(to != address(0), "ERC721: transfer to the zero address");
  
          _beforeTokenTransfer(from, to, tokenId);
  
          // Clear approvals from the previous owner
          _approve(address(0), tokenId);
  
          _balances[from] -= 1;
          _balances[to] += 1;
          _owners[tokenId] = to;
  
          emit Transfer(from, to, tokenId);
  
          _afterTokenTransfer(from, to, tokenId);
      }
  
      /**
       * @dev Approve `to` to operate on `tokenId`
       *
       * Emits an {Approval} event.
       */
      function _approve(address to, uint256 tokenId) internal virtual {
          _tokenApprovals[tokenId] = to;
          emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
      }
  
      /**
       * @dev Approve `operator` to operate on all of `owner` tokens
       *
       * Emits an {ApprovalForAll} event.
       */
      function _setApprovalForAll(
          address owner,
          address operator,
          bool approved
      ) internal virtual {
          require(owner != operator, "ERC721: approve to caller");
          _operatorApprovals[owner][operator] = approved;
          emit ApprovalForAll(owner, operator, approved);
      }
  
      /**
       * @dev Reverts if the `tokenId` has not been minted yet.
       */
      function _requireMinted(uint256 tokenId) internal view virtual {
          require(_exists(tokenId), "ERC721: invalid token ID");
      }
  
      /**
       * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
       * The call is not executed if the target address is not a 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 _checkOnERC721Received(
          address from,
          address to,
          uint256 tokenId,
          bytes memory data
      ) private returns (bool) {
          if (to.isContract()) {
              try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                  return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;
              } catch (bytes memory reason) {
                  if (reason.length == 0) {
                      revert("ERC721: transfer to non ERC721Receiver implementer");
                  } else {
                      /// @solidity memory-safe-assembly
                      assembly {
                          revert(add(32, reason), mload(reason))
                      }
                  }
              }
          } else {
              return true;
          }
      }
  
      /**
       * @dev Hook that is called before any token transfer. This includes minting
       * and burning.
       *
       * 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, ``from``'s `tokenId` will be burned.
       * - `from` and `to` are never both zero.
       *
       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
       */
      function _beforeTokenTransfer(
          address from,
          address to,
          uint256 tokenId
      ) internal virtual {}
  
      /**
       * @dev Hook that is called after any transfer of tokens. This includes
       * minting and burning.
       *
       * Calling conditions:
       *
       * - when `from` and `to` are both non-zero.
       * - `from` and `to` are never both zero.
       *
       * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
       */
      function _afterTokenTransfer(
          address from,
          address to,
          uint256 tokenId
      ) internal virtual {}
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[44] private __gap;
  }
  
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev Collection of common custom errors used in multiple contracts
   *
   * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
   * It is recommended to avoid relying on the error API for critical functionality.
   */
  library Errors {
      /**
       * @dev The ETH balance of the account is not enough to perform the operation.
       */
      error InsufficientBalance(uint256 balance, uint256 needed);
  
      /**
       * @dev A call to an address target failed. The target may have reverted.
       */
      error FailedCall();
  
      /**
       * @dev The deployment failed.
       */
      error FailedDeployment();
  }
  
  // File: @openzeppelin/contracts/proxy/Clones.sol
  
  // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)
  
  pragma solidity ^0.8.20;
  
  /**
   * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
   * deploying minimal proxy contracts, also known as "clones".
   *
   * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
   * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
   *
   * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
   * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
   * deterministic method.
   */
  library Clones {
      /**
       * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
       *
       * This function uses the create opcode, which should never revert.
       */
      function clone(address implementation) internal returns (address instance) {
          return clone(implementation, 0);
      }
  
      /**
       * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
       * to the new contract.
       *
       * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
       * to always have enough balance for new deployments. Consider exposing this function under a payable method.
       */
      function clone(address implementation, uint256 value) internal returns (address instance) {
          if (address(this).balance < value) {
              revert Errors.InsufficientBalance(address(this).balance, value);
          }
          /// @solidity memory-safe-assembly
          assembly {
              // Stores the bytecode after address
              mstore(0x20, 0x5af43d82803e903d91602b57fd5bf3)
              // implementation address
              mstore(0x11, implementation)
              // Packs the first 3 bytes of the `implementation` address with the bytecode before the address.
              mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
              instance := create(value, 0x09, 0x37)
          }
          if (instance == address(0)) {
              revert Errors.FailedDeployment();
          }
      }
  
      /**
       * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
       *
       * This function uses the create2 opcode and a `salt` to deterministically deploy
       * the clone. Using the same `implementation` and `salt` multiple time will revert, since
       * the clones cannot be deployed twice at the same address.
       */
      function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
          return cloneDeterministic(implementation, salt, 0);
      }
  
      /**
       * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
       * a `value` parameter to send native currency to the new contract.
       *
       * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
       * to always have enough balance for new deployments. Consider exposing this function under a payable method.
       */
      function cloneDeterministic(
          address implementation,
          bytes32 salt,
          uint256 value
      ) internal returns (address instance) {
          if (address(this).balance < value) {
              revert Errors.InsufficientBalance(address(this).balance, value);
          }
          /// @solidity memory-safe-assembly
          assembly {
              // Stores the bytecode after address
              mstore(0x20, 0x5af43d82803e903d91602b57fd5bf3)
              // implementation address
              mstore(0x11, implementation)
              // Packs the first 3 bytes of the `implementation` address with the bytecode before the address.
              mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
              instance := create2(value, 0x09, 0x37, salt)
          }
          if (instance == address(0)) {
              revert Errors.FailedDeployment();
          }
      }
  
      /**
       * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
       */
      function predictDeterministicAddress(
          address implementation,
          bytes32 salt,
          address deployer
      ) internal pure returns (address predicted) {
          /// @solidity memory-safe-assembly
          assembly {
              let ptr := mload(0x40)
              mstore(add(ptr, 0x38), deployer)
              mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
              mstore(add(ptr, 0x14), implementation)
              mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
              mstore(add(ptr, 0x58), salt)
              mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
              predicted := keccak256(add(ptr, 0x43), 0x55)
          }
      }
  
      /**
       * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
       */
      function predictDeterministicAddress(
          address implementation,
          bytes32 salt
      ) internal view returns (address predicted) {
          return predictDeterministicAddress(implementation, salt, address(this));
      }
  }
  
  
  /**
   * @title Shared logic for managing templates and creating contract.
   */
  abstract contract ArtFactorySharedTemplate is
    Context,
    Ownable
  {
    using Clones for address;
  
    address public implementation;
    uint32 public templateVersion;
  
    error NFTCollectionFactorySharedTemplates_Collection_Requires_Symbol();
    error NFTCollectionFactorySharedTemplates_Invalid_Collection_Type();
    error NFTCollectionFactorySharedTemplates_Upgrade_Implementation_Already_Set();
    error NFTCollectionFactorySharedTemplates_Upgrade_Implementation_Not_A_Contract();
    error NFTCollectionFactorySharedTemplates_Upgrade_Inputs_Must_Be_The_Same_Length();
  
    event PRINT_ADDR(address addr);
    event PRINT_STR(string str);
  
    /**
     * @notice Emitted when the implementation used
     * @param implementation The new implementation contract address.
     * @param templateVersion The version of the new implementation, auto-incremented.
     */
    event TemplateUpdated(
      address indexed implementation,
      uint256 indexed templateVersion
    );
  
    /**
     * @notice Allows admins to update a template.
     * @param _implementation The new implementation contract address.
     * @dev New templates will start with version 1, others will auto-increment from their current version.
     */
    function updateTemplate(
      address _implementation
    ) external onlyOwner {
      if (implementation == _implementation) {
          revert NFTCollectionFactorySharedTemplates_Upgrade_Implementation_Already_Set();
      }
      implementation = _implementation;
      templateVersion = ++templateVersion;
  
      emit TemplateUpdated(implementation, templateVersion);
    }
  
    /**
     * @notice A helper for creating contract of a given type.
     */
    function _createContract(
      string memory _name,
      string memory _symbol,
      address _creator,
      address _operator,
      uint96 _nonce
    ) internal returns (address contract_, uint256 version_) {
      // This reverts if the NFT was previously created using this implementation version + msg.sender + nonce
      contract_ = Clones.cloneDeterministic(implementation, _getSalt(_creator, _nonce));
      ArtNft(contract_).init(_name, _symbol, _creator, _operator, templateVersion);
      version_ = templateVersion;
    }
  
    /**
     * @notice Returns the address of an ArtNft given the current
     * implementation version, creator, and nonce.
     * @param creator The creator of the ArtNft.
     * @param nonce An arbitrary value used to allow a creator to mint multiple contracts with a counterfactual address.
     * @return contractAddress_ The address of the contract that would be created by this nonce.
     * @dev This will return the same address whether the ArtNft has already been created or not.
     */
    function predictCollectionAddress(
      address creator,
      uint96 nonce
    ) public view returns (address contractAddress_) {
      contractAddress_ = implementation.predictDeterministicAddress(_getSalt(creator, nonce));
    }
  
    /**
     * @dev Salt is address + nonce packed.
     */
    function _getSalt(address creator, uint96 nonce) private pure returns (bytes32) {
      return bytes32((uint256(uint160(creator)) << 96) | uint256(nonce));
    }
  
    /**
     * @notice This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     * @dev This mixin is a total of 1,000 slots.
     */
    uint256[999] private __gap;
  }
  
  // File: contracts/ArtNft.sol
  
  pragma solidity ^0.8.0;
  
  contract ArtNft is ERC721Upgradeable, CreatorOwnable {
      address private operator;
      uint256 public totalMinted;
      uint256 public version;
      string public baseURI;
      uint256 public burned;
      event BaseURIChanged(string _baseURI);
      event SetOperator(address operator);
  
      modifier onlyOwnerOrOperator {
          require(msg.sender == owner() || msg.sender == operator, "Owner or artist only");
          _;
      }
  
      function init(
          string memory _name,
          string memory _symbol,
          address _artist,
          address _operator,
          uint256 _version
      ) initializer external {
          // require(!initialized, "Already Initialized");
          __ERC721_init(_name, _symbol);
          operator = _operator;
          transferOwnership(_artist);
          version = _version;
      }
      
      function mint(address _to) external onlyOwnerOrOperator {
          _mint(_to, ++totalMinted);
      }
      
      function mintBatch(address _to, uint256 _number) external onlyOwnerOrOperator {
          for (uint i; i < _number; i++)
              _mint(_to, ++totalMinted);
      }
  
      function burn(uint256 tokenId) public {
          require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
          _burn(tokenId);
          burned++;
      }
  
      function setBaseURI(string memory _uri) external {
          require(operator == msg.sender, "!operator");
          baseURI = _uri;
          emit BaseURIChanged(baseURI);
      }
  
      function _baseURI() internal view override returns (string memory) {
          return baseURI;
      }
  
      function transferOperator(address newOperator) external {
          require(operator == msg.sender, "!operator");
          require(newOperator != address(0), "!!0");
          operator = newOperator;
  
          emit SetOperator(operator);
      }
  
      function transferOwnership(address newOwner) public override onlyOwnerOrOperator {
          require(newOwner != address(0), "Ownable: new owner is the zero address");
          _transferOwnership(newOwner);
      }
  
      function totalSupply() public view returns (uint256) {
          return (totalMinted - burned);
      }
  }
  
  // File: contracts/ArtworkFactory.sol
  
  interface IFeeManager {
      struct FeeRecipients {
          address receiver;
          uint256 feeBips;
      }
      function setCreatorFee(FeeRecipients[] memory _splitInfo, address _tokenAddress, uint256 _tokenId) external;
      function getCreatorFee(address _tokenAddress, uint256 _tokenId) view external returns (FeeRecipients[] memory);
      function getTradeFeeBips(address _tokenAddress, uint256 _tokenId) view external returns (uint256 sum);
      function settleTradeFee(address _tokenAddress, uint256 _tokenId, address _referral) payable external;
      function withdrawBalance() external;
      function transferWithdrawFee() payable external;
  }
  
  interface IArtMarket {
      function listingAsset(address _payTo, address _tokenAddress, uint256 _tokenId, uint256 _price) external;
      function cancelListing(address _tokenAddress, uint256 _tokenId) external;
      function setListingPrice(address _tokenAddress, uint256 _tokenId, uint256 _price) external;
      function buyNow(address _tokenAddress, uint256 _tokenId, address _referral) payable external;
      function buyBatchNow(address[] calldata _tokenAddresses, uint256[] calldata _tokenIds, address[] calldata _referrals) payable external;
      function biddingAsset(address _tokenAddress, uint256 _tokenId, uint256 _expirationTime, address _referral) payable external;
      function cancelBidding(address _tokenAddress, uint256 _tokenId, uint256 _price, uint256 _expirationTime) external;
      function withdrawBalance() external;
  }
  
  interface IArtworkFactoryV2 {
      function migrationToV3(address _v3, uint256 _v1From, uint256 _v1To, uint256 _v2From, uint256 _v2To) external;
      function transferOwnership(address newOwner) external;
      function setOperator(address _tokenAddress, address _operator) external;
      function nftList(uint256 index) view external returns (ArtNft);
      function nftAddress(address _artist) view external returns (address nftAddr);
      function createContractAndArtwork(
          string memory _contractName,
          string memory _contractSymbol,
          IFeeManager.FeeRecipients[] memory _creatorFee,
          uint256[] memory _listingPrices
      ) external;
      function createContract(
          string memory _contractName,
          string memory _contractSymbol
      ) external;
      function createArtwork(
          IFeeManager.FeeRecipients[] memory _creatorFee,
          uint256[] memory _listingPrices   
      ) external;
  }
  
  pragma solidity ^0.8.0;
  
  contract ArtworkFactoryV3 is Ownable, ArtFactorySharedTemplate {
      IFeeManager public feeManager;
      IArtMarket public artMarket;
  
      ArtNft[] public nftList;
      mapping(address => address) public nftAddress;
      mapping(address => bool) public operators;
  
      bool isInitialized;
      string public baseURI; // "https://api.2r2.io/nft/"
  
      event ContractCreated(address indexed user);
      event ArtworkCreated(address indexed creator, address indexed nftAddress, uint256 nftId);
      event OperatorUpdated(address indexed operator, bool indexed status);
      event NftArtistMigrated(address indexed nftAddress, address oldArtist, address newArtist);
      event Migrated(address nftAddress, address newOperator);
      event catchErr(string _name, string _err);
  
      modifier onlyOperator() {
          require(operators[msg.sender] == true, "Operator account only");
          _;
      }
  
      function init(
          address payable _feeManager,
          address _artMarket,
          string memory _baseURI
      ) external {
          require(!isInitialized, "Already initialized");
          isInitialized = true;
  
          feeManager = IFeeManager(_feeManager);
          artMarket = IArtMarket(_artMarket);
          baseURI = _baseURI;
  
          _transferOwnership(msg.sender);
      }
  
      function migration(address _v1, uint256 _v1From, uint256 _v1To, address _v2, uint256 _v2From, uint256 _v2To) external onlyOwner {
          for (uint256 i = _v1From; i <= _v1To; i++) {
              ArtNft nft = IArtworkFactoryV2(_v1).nftList(i);
              nftList.push(nft);
              address artist = nft.owner();
              nftAddress[artist] = address(nft);
          }
          for (uint256 i = _v2From; i <= _v2To; i++) {
              ArtNft nft = IArtworkFactoryV2(_v2).nftList(i);
              nftList.push(nft);
              address artist = nft.owner();
              nftAddress[artist] = address(nft);
          }
      }
  
      function createContractAndArtwork(
          string memory _contractName,
          string memory _contractSymbol,
          IFeeManager.FeeRecipients[] memory _creatorFee,
          uint256[] memory _listingPrices
      ) external {
          createContract(_contractName, _contractSymbol);
          createArtwork(_creatorFee, _listingPrices);
      }
  
      function createContract(
          string memory _contractName,
          string memory _contractSymbol
      ) public {
          require(getNftAddress(msg.sender) == address(0), "Your contract has been created");
          uint96 nonce = 0;
          (address nftAddr, ) = _createContract(_contractName, _contractSymbol, address(msg.sender), address(this), nonce);
          ArtNft nft = ArtNft(nftAddr);
          nftAddress[msg.sender] = address(nft);
          nftList.push(nft);
          
          string memory tokenUri = string(abi.encodePacked(baseURI, Strings.toHexString(uint160(address(nft)), 20), "/"));
          nft.setBaseURI(tokenUri);
  
          emit ContractCreated(msg.sender);
      }
  
      function createArtwork(
          IFeeManager.FeeRecipients[] memory _creatorFee,
          uint256[] memory _listingPrices   
      ) public {
          _createArtwork(_creatorFee, _listingPrices, msg.sender, msg.sender);
      }
  
      function createArtwork(
          IFeeManager.FeeRecipients[] memory _creatorFee,
          uint256[] memory _listingPrices,
          address _creator,
          address _receiver
      ) external onlyOperator {
          _createArtwork(_creatorFee, _listingPrices, _creator, _receiver);
      }
  
      function _createArtwork(
          IFeeManager.FeeRecipients[] memory _creatorFee,
          uint256[] memory _listingPrices,
          address _creator,
          address _receiver
      ) internal {
          ArtNft nft;
          require(getNftAddress(_creator) != address(0), "Create your contract first");
          nft = ArtNft(getNftAddress(_creator));
  
          for (uint i = 0; i < _listingPrices.length; i++) {
              // listing
              if (_listingPrices[i] > 0) {
                  require(address(artMarket) != address(0), "Marketplace contract is not set");
                  nft.mint(address(this));
                  nft.approve(address(artMarket), nft.totalSupply());
                  artMarket.listingAsset(_receiver, address(nft), nft.totalSupply(), _listingPrices[i]);
              } else {
                  nft.mint(address(_receiver));
              }
  
              emit ArtworkCreated(_creator, address(nft), nft.totalSupply());
  
              // Creator fee 설정
              if (_creatorFee.length > 0) {
                  require(address(feeManager) != address(0), "FeeManager contract is not set");
                  feeManager.setCreatorFee(_creatorFee, address(nft), nft.totalSupply());
              }
          }
      }
  
      function getNftAddress(address _artist) public view returns (address) {
          if (nftAddress[_artist] != address(0))
              return nftAddress[_artist];
          return address(0);
      }
  
      function setFeeManager(address payable _feeManager) external onlyOwner {
          feeManager = IFeeManager(_feeManager);
      }
  
      function setArtMarket(address _artMarket) external onlyOwner {
          artMarket = IArtMarket(_artMarket);
      }
  
      function setArtNftOperator(address _tokenAddress, address _operator) public onlyOwner {
          ArtNft nft = ArtNft(_tokenAddress);
          nft.transferOperator(_operator);
      }
  
      function setNftBaseUri(string memory _baseURI) external onlyOwner {
          baseURI = _baseURI;
          ArtNft nft;
          for(uint i; i < nftList.length; i++) {
              nft = nftList[i];
              string memory tokenUri = string(abi.encodePacked(baseURI, Strings.toHexString(uint160(address(nft)), 20), "/"));
              nft.setBaseURI(tokenUri);
          }
      }
  
      function setArtistOfNft(address _oldArtist, address _newArtist) external onlyOwner {
          address nftAddr = getNftAddress(_oldArtist);
          Ownable(nftAddr).transferOwnership(_newArtist);
          nftAddress[_oldArtist] = address(0);
          nftAddress[_newArtist] = nftAddr;
  
          emit NftArtistMigrated(nftAddr, _oldArtist, _newArtist);
      }
  
      function updateOperator(address _operator, bool _status) external onlyOwner {
          operators[_operator] = _status;
          emit OperatorUpdated(_operator, _status);
      }
  }