// File: Base64.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}
// File: Context.sol



// SPDX-License-Identifier: MIT
// 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: ERC721A.sol



// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import './IERC721A.sol';

/**
 * @dev Interface of ERC721 token receiver.
 */
interface ERC721A__IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

/**
 * @title ERC721A
 *
 * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
 * Non-Fungible Token Standard, including the Metadata extension.
 * Optimized for lower gas during batch mints.
 *
 * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
 * starting from `_startTokenId()`.
 *
 * Assumptions:
 *
 * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
 * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
 */
contract ERC721A is IERC721A {
    // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
    struct TokenApprovalRef {
        address value;
    }

    // =============================================================
    //                           CONSTANTS
    // =============================================================

    // Mask of an entry in packed address data.
    uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;

    // The bit position of `numberMinted` in packed address data.
    uint256 private constant _BITPOS_NUMBER_MINTED = 64;

    // The bit position of `numberBurned` in packed address data.
    uint256 private constant _BITPOS_NUMBER_BURNED = 128;

    // The bit position of `aux` in packed address data.
    uint256 private constant _BITPOS_AUX = 192;

    // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
    uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;

    // The bit position of `startTimestamp` in packed ownership.
    uint256 private constant _BITPOS_START_TIMESTAMP = 160;

    // The bit mask of the `burned` bit in packed ownership.
    uint256 private constant _BITMASK_BURNED = 1 << 224;

    // The bit position of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;

    // The bit mask of the `nextInitialized` bit in packed ownership.
    uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;

    // The bit position of `extraData` in packed ownership.
    uint256 private constant _BITPOS_EXTRA_DATA = 232;

    // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
    uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;

    // The mask of the lower 160 bits for addresses.
    uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;

    // The maximum `quantity` that can be minted with {_mintERC2309}.
    // This limit is to prevent overflows on the address data entries.
    // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
    // is required to cause an overflow, which is unrealistic.
    uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;

    // The `Transfer` event signature is given by:
    // `keccak256(bytes("Transfer(address,address,uint256)"))`.
    bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    // =============================================================
    //                            STORAGE
    // =============================================================

    // The next token ID to be minted.
    uint256 private _currentIndex;

    // The number of tokens burned.
    uint256 private _burnCounter;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to ownership details
    // An empty struct value does not necessarily mean the token is unowned.
    // See {_packedOwnershipOf} implementation for details.
    //
    // Bits Layout:
    // - [0..159]   `addr`
    // - [160..223] `startTimestamp`
    // - [224]      `burned`
    // - [225]      `nextInitialized`
    // - [232..255] `extraData`
    mapping(uint256 => uint256) private _packedOwnerships;

    // Mapping owner address to address data.
    //
    // Bits Layout:
    // - [0..63]    `balance`
    // - [64..127]  `numberMinted`
    // - [128..191] `numberBurned`
    // - [192..255] `aux`
    mapping(address => uint256) private _packedAddressData;

    // Mapping from token ID to approved address.
    mapping(uint256 => TokenApprovalRef) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // =============================================================
    //                          CONSTRUCTOR
    // =============================================================

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _currentIndex = _startTokenId();
    }

    // =============================================================
    //                   TOKEN COUNTING OPERATIONS
    // =============================================================

    /**
     * @dev Returns the starting token ID.
     * To change the starting token ID, please override this function.
     */
    function _startTokenId() internal view virtual returns (uint256) {
        return 0;
    }

    /**
     * @dev Returns the next token ID to be minted.
     */
    function _nextTokenId() internal view virtual returns (uint256) {
        return _currentIndex;
    }

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than `_currentIndex - _startTokenId()` times.
        unchecked {
            return _currentIndex - _burnCounter - _startTokenId();
        }
    }

    /**
     * @dev Returns the total amount of tokens minted in the contract.
     */
    function _totalMinted() internal view virtual returns (uint256) {
        // Counter underflow is impossible as `_currentIndex` does not decrement,
        // and it is initialized to `_startTokenId()`.
        unchecked {
            return _currentIndex - _startTokenId();
        }
    }

    /**
     * @dev Returns the total number of tokens burned.
     */
    function _totalBurned() internal view virtual returns (uint256) {
        return _burnCounter;
    }

    // =============================================================
    //                    ADDRESS DATA OPERATIONS
    // =============================================================

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        if (owner == address(0)) revert BalanceQueryForZeroAddress();
        return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _numberMinted(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the number of tokens burned by or on behalf of `owner`.
     */
    function _numberBurned(address owner) internal view returns (uint256) {
        return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
    }

    /**
     * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getAux(address owner) internal view returns (uint64) {
        return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
    }

    /**
     * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setAux(address owner, uint64 aux) internal virtual {
        uint256 packed = _packedAddressData[owner];
        uint256 auxCasted;
        // Cast `aux` with assembly to avoid redundant masking.
        assembly {
            auxCasted := aux
        }
        packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
        _packedAddressData[owner] = packed;
    }

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        // The interface IDs are constants representing the first 4 bytes
        // of the XOR of all function selectors in the interface.
        // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
        // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
        return
            interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
            interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
            interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
    }

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

        string memory baseURI = _baseURI();
        return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, it can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return '';
    }

    // =============================================================
    //                     OWNERSHIPS OPERATIONS
    // =============================================================

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        return address(uint160(_packedOwnershipOf(tokenId)));
    }

    /**
     * @dev Gas spent here starts off proportional to the maximum mint batch size.
     * It gradually moves to O(1) as tokens get transferred around over time.
     */
    function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnershipOf(tokenId));
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct at `index`.
     */
    function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
        return _unpackedOwnership(_packedOwnerships[index]);
    }

    /**
     * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
     */
    function _initializeOwnershipAt(uint256 index) internal virtual {
        if (_packedOwnerships[index] == 0) {
            _packedOwnerships[index] = _packedOwnershipOf(index);
        }
    }

    /**
     * Returns the packed ownership data of `tokenId`.
     */
    function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
        uint256 curr = tokenId;

        unchecked {
            if (_startTokenId() <= curr)
                if (curr < _currentIndex) {
                    uint256 packed = _packedOwnerships[curr];
                    // If not burned.
                    if (packed & _BITMASK_BURNED == 0) {
                        // Invariant:
                        // There will always be an initialized ownership slot
                        // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                        // before an unintialized ownership slot
                        // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                        // Hence, `curr` will not underflow.
                        //
                        // We can directly compare the packed value.
                        // If the address is zero, packed will be zero.
                        while (packed == 0) {
                            packed = _packedOwnerships[--curr];
                        }
                        return packed;
                    }
                }
        }
        revert OwnerQueryForNonexistentToken();
    }

    /**
     * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
     */
    function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
        ownership.addr = address(uint160(packed));
        ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
        ownership.burned = packed & _BITMASK_BURNED != 0;
        ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
    }

    /**
     * @dev Packs ownership data into a single uint256.
     */
    function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
            result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
        }
    }

    /**
     * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
     */
    function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
        // For branchless setting of the `nextInitialized` flag.
        assembly {
            // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
            result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
        }
    }

    // =============================================================
    //                      APPROVAL OPERATIONS
    // =============================================================

    /**
     * @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) public payable virtual override {
        address owner = ownerOf(tokenId);

        if (_msgSenderERC721A() != owner)
            if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                revert ApprovalCallerNotOwnerNorApproved();
            }

        _tokenApprovals[tokenId].value = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

        return _tokenApprovals[tokenId].value;
    }

    /**
     * @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) public virtual override {
        _operatorApprovals[_msgSenderERC721A()][operator] = approved;
        emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
    }

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @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. See {_mint}.
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return
            _startTokenId() <= tokenId &&
            tokenId < _currentIndex && // If within bounds,
            _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
    }

    /**
     * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
     */
    function _isSenderApprovedOrOwner(
        address approvedAddress,
        address owner,
        address msgSender
    ) private pure returns (bool result) {
        assembly {
            // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
            owner := and(owner, _BITMASK_ADDRESS)
            // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
            msgSender := and(msgSender, _BITMASK_ADDRESS)
            // `msgSender == owner || msgSender == approvedAddress`.
            result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
        }
    }

    /**
     * @dev Returns the storage slot and value for the approved address of `tokenId`.
     */
    function _getApprovedSlotAndAddress(uint256 tokenId)
        private
        view
        returns (uint256 approvedAddressSlot, address approvedAddress)
    {
        TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
        // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
        assembly {
            approvedAddressSlot := tokenApproval.slot
            approvedAddress := sload(approvedAddressSlot)
        }
    }

    // =============================================================
    //                      TRANSFER OPERATIONS
    // =============================================================

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * 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
    ) public payable virtual override {
        uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

        if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        // The nested ifs save around 20+ gas over a compound boolean condition.
        if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
            if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();

        if (to == address(0)) revert TransferToZeroAddress();

        _beforeTokenTransfers(from, to, tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // We can directly increment and decrement the balances.
            --_packedAddressData[from]; // Updates: `balance -= 1`.
            ++_packedAddressData[to]; // Updates: `balance += 1`.

            // Updates:
            // - `address` to the next owner.
            // - `startTimestamp` to the timestamp of transfering.
            // - `burned` to `false`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] = _packOwnershipData(
                to,
                _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
            );

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, to, tokenId);
        _afterTokenTransfers(from, to, tokenId, 1);
    }

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable virtual override {
        safeTransferFrom(from, to, tokenId, '');
    }

    /**
     * @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 memory _data
    ) public payable virtual override {
        transferFrom(from, to, tokenId);
        if (to.code.length != 0)
            if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                revert TransferToNonERC721ReceiverImplementer();
            }
    }

    /**
     * @dev Hook that is called before a set of serially-ordered token IDs
     * are about to be transferred. This includes minting.
     * And also called before burning one token.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _beforeTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}

    /**
     * @dev Hook that is called after a set of serially-ordered token IDs
     * have been transferred. This includes minting.
     * And also called after one token has been burned.
     *
     * `startTokenId` - the first token ID to be transferred.
     * `quantity` - the amount to be transferred.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
     * transferred to `to`.
     * - When `from` is zero, `tokenId` has been minted for `to`.
     * - When `to` is zero, `tokenId` has been burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _afterTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual {}

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
     *
     * `from` - Previous owner of the given token ID.
     * `to` - Target address that will receive the token.
     * `tokenId` - Token ID to be transferred.
     * `_data` - Optional data to send along with the call.
     *
     * Returns whether the call correctly returned the expected magic value.
     */
    function _checkContractOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
            bytes4 retval
        ) {
            return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
        } catch (bytes memory reason) {
            if (reason.length == 0) {
                revert TransferToNonERC721ReceiverImplementer();
            } else {
                assembly {
                    revert(add(32, reason), mload(reason))
                }
            }
        }
    }

    // =============================================================
    //                        MINT OPERATIONS
    // =============================================================

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _mint(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (quantity == 0) revert MintZeroQuantity();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are incredibly unrealistic.
        // `balance` and `numberMinted` have a maximum limit of 2**64.
        // `tokenId` has a maximum limit of 2**256.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] = _packOwnershipData(
                to,
                _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
            );

            uint256 toMasked;
            uint256 end = startTokenId + quantity;

            // Use assembly to loop and emit the `Transfer` event for gas savings.
            // The duplicated `log4` removes an extra check and reduces stack juggling.
            // The assembly, together with the surrounding Solidity code, have been
            // delicately arranged to nudge the compiler into producing optimized opcodes.
            assembly {
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                toMasked := and(to, _BITMASK_ADDRESS)
                // Emit the `Transfer` event.
                log4(
                    0, // Start of data (0, since no data).
                    0, // End of data (0, since no data).
                    _TRANSFER_EVENT_SIGNATURE, // Signature.
                    0, // `address(0)`.
                    toMasked, // `to`.
                    startTokenId // `tokenId`.
                )

                // The `iszero(eq(,))` check ensures that large values of `quantity`
                // that overflows uint256 will make the loop run out of gas.
                // The compiler will optimize the `iszero` away for performance.
                for {
                    let tokenId := add(startTokenId, 1)
                } iszero(eq(tokenId, end)) {
                    tokenId := add(tokenId, 1)
                } {
                    // Emit the `Transfer` event. Similar to above.
                    log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                }
            }
            if (toMasked == 0) revert MintToZeroAddress();

            _currentIndex = end;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Mints `quantity` tokens and transfers them to `to`.
     *
     * This function is intended for efficient minting only during contract creation.
     *
     * It emits only one {ConsecutiveTransfer} as defined in
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
     * instead of a sequence of {Transfer} event(s).
     *
     * Calling this function outside of contract creation WILL make your contract
     * non-compliant with the ERC721 standard.
     * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
     * {ConsecutiveTransfer} event is only permissible during contract creation.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `quantity` must be greater than 0.
     *
     * Emits a {ConsecutiveTransfer} event.
     */
    function _mintERC2309(address to, uint256 quantity) internal virtual {
        uint256 startTokenId = _currentIndex;
        if (to == address(0)) revert MintToZeroAddress();
        if (quantity == 0) revert MintZeroQuantity();
        if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();

        _beforeTokenTransfers(address(0), to, startTokenId, quantity);

        // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
        unchecked {
            // Updates:
            // - `balance += quantity`.
            // - `numberMinted += quantity`.
            //
            // We can directly add to the `balance` and `numberMinted`.
            _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

            // Updates:
            // - `address` to the owner.
            // - `startTimestamp` to the timestamp of minting.
            // - `burned` to `false`.
            // - `nextInitialized` to `quantity == 1`.
            _packedOwnerships[startTokenId] = _packOwnershipData(
                to,
                _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
            );

            emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);

            _currentIndex = startTokenId + quantity;
        }
        _afterTokenTransfers(address(0), to, startTokenId, quantity);
    }

    /**
     * @dev Safely mints `quantity` tokens and transfers them to `to`.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
     * - `quantity` must be greater than 0.
     *
     * See {_mint}.
     *
     * Emits a {Transfer} event for each mint.
     */
    function _safeMint(
        address to,
        uint256 quantity,
        bytes memory _data
    ) internal virtual {
        _mint(to, quantity);

        unchecked {
            if (to.code.length != 0) {
                uint256 end = _currentIndex;
                uint256 index = end - quantity;
                do {
                    if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                        revert TransferToNonERC721ReceiverImplementer();
                    }
                } while (index < end);
                // Reentrancy protection.
                if (_currentIndex != end) revert();
            }
        }
    }

    /**
     * @dev Equivalent to `_safeMint(to, quantity, '')`.
     */
    function _safeMint(address to, uint256 quantity) internal virtual {
        _safeMint(to, quantity, '');
    }

    // =============================================================
    //                        BURN OPERATIONS
    // =============================================================

    /**
     * @dev Equivalent to `_burn(tokenId, false)`.
     */
    function _burn(uint256 tokenId) internal virtual {
        _burn(tokenId, false);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
        uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

        address from = address(uint160(prevOwnershipPacked));

        (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);

        if (approvalCheck) {
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
        }

        _beforeTokenTransfers(from, address(0), tokenId, 1);

        // Clear approvals from the previous owner.
        assembly {
            if approvedAddress {
                // This is equivalent to `delete _tokenApprovals[tokenId]`.
                sstore(approvedAddressSlot, 0)
            }
        }

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
        unchecked {
            // Updates:
            // - `balance -= 1`.
            // - `numberBurned += 1`.
            //
            // We can directly decrement the balance, and increment the number burned.
            // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
            _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;

            // Updates:
            // - `address` to the last owner.
            // - `startTimestamp` to the timestamp of burning.
            // - `burned` to `true`.
            // - `nextInitialized` to `true`.
            _packedOwnerships[tokenId] = _packOwnershipData(
                from,
                (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
            );

            // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
            if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                uint256 nextTokenId = tokenId + 1;
                // If the next slot's address is zero and not burned (i.e. packed value is zero).
                if (_packedOwnerships[nextTokenId] == 0) {
                    // If the next slot is within bounds.
                    if (nextTokenId != _currentIndex) {
                        // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                        _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                    }
                }
            }
        }

        emit Transfer(from, address(0), tokenId);
        _afterTokenTransfers(from, address(0), tokenId, 1);

        // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
        unchecked {
            _burnCounter++;
        }
    }

    // =============================================================
    //                     EXTRA DATA OPERATIONS
    // =============================================================

    /**
     * @dev Directly sets the extra data for the ownership data `index`.
     */
    function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
        uint256 packed = _packedOwnerships[index];
        if (packed == 0) revert OwnershipNotInitializedForExtraData();
        uint256 extraDataCasted;
        // Cast `extraData` with assembly to avoid redundant masking.
        assembly {
            extraDataCasted := extraData
        }
        packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
        _packedOwnerships[index] = packed;
    }

    /**
     * @dev Called during each token transfer to set the 24bit `extraData` field.
     * Intended to be overridden by the cosumer contract.
     *
     * `previousExtraData` - the value of `extraData` before transfer.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, `tokenId` will be burned by `from`.
     * - `from` and `to` are never both zero.
     */
    function _extraData(
        address from,
        address to,
        uint24 previousExtraData
    ) internal view virtual returns (uint24) {}

    /**
     * @dev Returns the next extra data for the packed ownership data.
     * The returned result is shifted into position.
     */
    function _nextExtraData(
        address from,
        address to,
        uint256 prevOwnershipPacked
    ) private view returns (uint256) {
        uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
        return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
    }

    // =============================================================
    //                       OTHER OPERATIONS
    // =============================================================

    /**
     * @dev Returns the message sender (defaults to `msg.sender`).
     *
     * If you are writing GSN compatible contracts, you need to override this function.
     */
    function _msgSenderERC721A() internal view virtual returns (address) {
        return msg.sender;
    }

    /**
     * @dev Converts a uint256 to its ASCII string decimal representation.
     */
    function _toString(uint256 value) internal pure virtual returns (string memory str) {
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
            let m := add(mload(0x40), 0xa0)
            // Update the free memory pointer to allocate.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 1)
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                // prettier-ignore
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }
}
// File: IERC4906.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/CHECKS721.sol)

pragma solidity ^0.8.0;

import "./IERC721A.sol";

/// @title EIP-721 Metadata Update Extension
interface IERC4906 is IERC721A {
    /// @dev This event emits when the metadata of a token is changed.
    /// Third-party platforms such as NFT marketplaces can listen to
    /// the event and auto-update the tokens in their apps.
    event MetadataUpdate(uint256 _tokenId);
}
// File: IERC721A.sol



// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

/**
 * @dev Interface of ERC721A.
 */
interface IERC721A {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the
     * ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    /**
     * The `quantity` minted with ERC2309 exceeds the safety limit.
     */
    error MintERC2309QuantityExceedsLimit();

    /**
     * The `extraData` cannot be set on an unintialized ownership slot.
     */
    error OwnershipNotInitializedForExtraData();

    // =============================================================
    //                            STRUCTS
    // =============================================================

    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Stores the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
        // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
        uint24 extraData;
    }

    // =============================================================
    //                         TOKEN COUNTERS
    // =============================================================

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() external view returns (uint256);

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    // =============================================================
    //                            IERC721
    // =============================================================

    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables
     * (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`,
     * checking first that contract recipients are aware of the ERC721 protocol
     * to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move
     * this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external payable;

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Transfers `tokenId` 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 payable;

    /**
     * @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 payable;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom}
     * for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);

    // =============================================================
    //                           IERC2309
    // =============================================================

    /**
     * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
     * (inclusive) is transferred from `from` to `to`, as defined in the
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
     *
     * See {_mintERC2309} for more details.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}
// File: Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "./Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
// File: Segments.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "./Utilities.sol";
import "./Base64.sol";

library segments {

    function getBaseColorName(uint index) internal pure returns (string memory) {
        string[4] memory baseColorNames = ["White", "Red", "Green", "Blue"];
        return baseColorNames[index];
    }

    function getMetadata(uint tokenId, uint256 value, uint baseColor, bool burned, bytes memory _svgPrefix) internal pure returns (string memory) {
        uint[3] memory rgbs = utils.getRgbs(tokenId, baseColor);
        string memory json;

        if (burned) {
            json = string(abi.encodePacked(
            '{"name": "TIME ',
            utils.uint2str(tokenId),
            ' [BURNED]", "description": "Time is the ultimate currency.", "attributes":[{"trait_type": "Burned", "value": "Yes"}], "image": "data:image/svg+xml;base64,',
            Base64.encode(bytes(renderSvg(value, rgbs, _svgPrefix))),
            '"}'
        ));
        } else {
            json = string(abi.encodePacked(
            '{"name": "TIME ',
            utils.uint2str(tokenId),
            '", "description": "Time is the ultimate currency.", "attributes":[{"trait_type": "Balance", "max_value": 315359999999, "value": ',
            utils.uint2str(value),
            '},{"display_type": "number", "trait_type": "Mint Phase", "value": ',
            utils.uint2str(utils.getMintPhase(tokenId)),
            '},{"trait_type": "Burned", "value": "No"},{"trait_type": "Base Color", "value": "',
            getBaseColorName(baseColor),
            '"},{"trait_type": "Color", "value": "RGB(',
            utils.uint2str(rgbs[0]),
            ",",
            utils.uint2str(rgbs[1]),
            ",",
            utils.uint2str(rgbs[2]),
            ')"}], "image": "data:image/svg+xml;base64,',
            Base64.encode(bytes(renderSvg(value, rgbs, _svgPrefix))),
            '"}'
        ));
        }

        return string(abi.encodePacked(
            "data:application/json;base64,",
            Base64.encode(bytes(json))
        ));
    }

    function getNumberStyle(uint position, uint _value) internal pure returns (string memory) {
        string memory p = utils.uint2str(position);
        if (_value == 0) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"2,","#p",p,"3,","#p",p,"5,","#p",p,"6,","#p",p,"7 {fill-opacity:1}"
            ));
        } else if (_value == 1) {
            return string(abi.encodePacked(
                "#p",p,"3,","#p",p,"6 {fill-opacity:1}"
            ));
        } else if (_value == 2) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"3,","#p",p,"4,","#p",p,"5,","#p",p,"7 {fill-opacity:1}"
            ));
        } else if (_value == 3) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"3,","#p",p,"4,","#p",p,"6,","#p",p,"7 {fill-opacity:1}"
            ));
        } else if (_value == 4) {
            return string(abi.encodePacked(
                "#p",p,"2,","#p",p,"3,","#p",p,"4,","#p",p,"6 {fill-opacity:1}"
            ));
        } else if (_value == 5) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"2,","#p",p,"4,","#p",p,"6,","#p",p,"7 {fill-opacity:1}"
            ));
        } else if (_value == 6) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"2,","#p",p,"4,","#p",p,"5,","#p",p,"6,","#p",p,"7 {fill-opacity:1}"
            ));
        } else if (_value == 7) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"3,","#p",p,"6 {fill-opacity:1}"
            ));
        } else if (_value == 8) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"2,","#p",p,"3,","#p",p,"4,","#p",p,"5,","#p",p,"6,","#p",p,"7 {fill-opacity:1}"
            ));
        } else if (_value == 9) {
            return string(abi.encodePacked(
                "#p",p,"1,","#p",p,"2,","#p",p,"3,","#p",p,"4,","#p",p,"6,","#p",p,"7 {fill-opacity:1}"
            ));
        } else {
            return "error";
        }
    }

    function renderSvg(uint256 value,uint256[3] memory rgbs, bytes memory _svgPrefix) internal pure returns (string memory svg) {
        svg = string(_svgPrefix);

        string memory styles = string(
            abi.encodePacked(
                "path[id^='p']{fill:rgb(",
                utils.uint2str(rgbs[0]),
                ",",
                utils.uint2str(rgbs[1]),
                ",",
                utils.uint2str(rgbs[2]),
                ")}#bg{fill:#0C0C0C}"
            )
        );

        //bytes memory _number = bytes(utils.seconds2str(value));

        uint[13] memory _number = utils.seconds2strv2(value);
        
        styles = string(
            abi.encodePacked(styles, getNumberStyle(0, _number[0]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(1, _number[1]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(2, _number[2]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(3, _number[3]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(4, _number[4]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(5, _number[5]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(6, _number[6]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(7, _number[7]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(8, _number[8]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(9, _number[9]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(10, _number[10]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(11, _number[11]))
        );
        styles = string(
            abi.encodePacked(styles, getNumberStyle(12, _number[12]))
        );
    
        return string(abi.encodePacked(svg, styles, "</style></svg>"));
    }
}
// File: UltimateCurrency.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "./ERC721A.sol";
import "./Ownable.sol";
import "./Utilities.sol";
import "./Segments.sol";
import "./IERC4906.sol";

contract UltimateCurrency is ERC721A, Ownable, IERC4906 {
    
    event CountdownExtended(uint _finalBlock);

    uint public price = 3000000000000000; //.003 eth
    bool public isCombinable = false;
    uint public finalMintingBlock;

    SVGPrefix public _prefix;

    mapping(uint => uint) newValues;
    mapping(uint => uint) baseColors;
    mapping(address => uint) freeMints;

    constructor(address _address) ERC721A("Ultimate Currency", "TIME") {
        _prefix = SVGPrefix(_address);        
    }

    function mint(uint quantity) public payable {
        require(msg.value >= quantity * price, "not enough eth");
        handleMint(msg.sender, quantity);
    }

    function freeMint(uint quantity) public {
        require(quantity <= freeMints[msg.sender], "not enough free mints");
        handleMint(msg.sender, quantity);
        freeMints[msg.sender] -= quantity;
    }

    function handleMint(address recipient, uint quantity) internal {
        uint supply = _totalMinted();
        if (supply >= 5000) {
            require(utils.secondsRemaining(finalMintingBlock) > 0, "mint is closed");
            if (supply < 8000 && (supply + quantity) >= 8000) {
                finalMintingBlock = block.timestamp + 24 hours;
                emit CountdownExtended(finalMintingBlock);
            }
        } else if (supply + quantity >= 5000) {
            finalMintingBlock = block.timestamp + 24 hours;
            emit CountdownExtended(finalMintingBlock);
        }
        _mint(recipient, quantity);
    }

    function combine(uint[] memory tokens) public {
        require(isCombinable, "combining not active");
        uint256 sum;
        for (uint i = 0; i < tokens.length; i++) {
            require(ownerOf(tokens[i]) == msg.sender, "must own all tokens");
            sum = sum + getValue(tokens[i]);
        }
        if (sum > 315359999999) {
            revert("sum must be 9999:52:0:23:59:59 or less");
        }
        for (uint i = 1; i < tokens.length; i++) {
            _burn(tokens[i]);
            newValues[tokens[i]] = 0;
            baseColors[tokens[i]] = 0;
            emit MetadataUpdate(tokens[i]);
        }

        // Why was 6 afraid of 7? Because 7 8 9!
        newValues[tokens[0]] = sum;
        baseColors[tokens[0]] = utils.random(tokens[0], 1, 4);
        emit MetadataUpdate(tokens[0]);
    }

    function getValue(uint256 tokenId) public view returns (uint) {
        if (!_exists(tokenId)) {
            return 0;
        } else if (newValues[tokenId] > 0) {
            return newValues[tokenId];
        } else {
            return utils.initValue(tokenId);
        }
    }

    function tokenURI(uint256 tokenId) public view virtual override(ERC721A, IERC721A) returns (string memory) {
        bool burned;
        uint256 value;

        if (newValues[tokenId] > 0) {
            value = newValues[tokenId];
            burned = false;
        } else if (newValues[tokenId] == 0 && !_exists(tokenId)) {
            value = 0;
            burned = true;
        } else {
            if(tokenId <= 3) {
                //value = (tokenId < 2? 3153600000: 315360000);
                if(tokenId == 1) {
                    value = 31536000000;
                } else if(tokenId == 2) {
                    value = 3153600000;
                } else {
                    value = 315360000;
                }
            } else {
                value = utils.initValue(tokenId);
            }
            burned = false;
        }

        return segments.getMetadata(tokenId, value, baseColors[tokenId], burned, _prefix.prefixTxt());
    }

    function _startTokenId() internal view virtual override returns (uint256) {
        return 1;
    }

    function getMinutesRemaining() public view returns (uint) {
        return utils.minutesRemaining(finalMintingBlock);
    }

    function mintCount() public view returns (uint) {
        return _totalMinted();
    }

    function toggleCombinable() public onlyOwner {
        isCombinable = !isCombinable;
    }

    function withdraw() external onlyOwner {
        require(payable(msg.sender).send(address(this).balance));
    }

    function freeMintBalance(address addy) public view returns (uint) {
        return freeMints[addy];
    }

    function addFreeMints(address[] calldata addresses, uint quantity) public onlyOwner {
        for (uint i = 0; i < addresses.length; i++) {
            freeMints[addresses[i]] = quantity;
        }
    }

    function mint4Owner(uint quantity) public onlyOwner {
        handleMint(msg.sender, quantity);
    }
}


contract SVGPrefix {

    constructor() {

    }

    function prefixTxt() external pure returns (bytes memory) {
        
        return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 600"><defs><linearGradient id="_grad1" x1="124.8" y1="228.27" x2="124.8" y2="356.23" gradientTransform="matrix(1, 0, 0, 1, 0, 0)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffca3f"/><stop offset=".1" stop-color="#ca7c22"/><stop offset=".19" stop-color="#fddb7a"/><stop offset=".42" stop-color="#ffad45"/><stop offset=".55" stop-color="#a95f0d"/><stop offset=".66" stop-color="#ffad45"/><stop offset=".79" stop-color="#ffff9b"/><stop offset=".87" stop-color="#ffed7b"/><stop offset="1" stop-color="#ffca3f"/></linearGradient><linearGradient id="_grad2" y1="197.25" y2="357.47" xlink:href="#_grad1"/></defs><rect width="600" height="600" fill="white"/><path d="M540,431.58c0,10.18-8.22,18.42-18.36,18.42H78.36c-10.14,0-18.36-8.24-18.36-18.42V168.42c0-10.18,8.22-18.42,18.36-18.42h443.28c10.14,0,18.36,8.24,18.36,18.42V431.58Z" style="fill-rule:evenodd;"/><path d="M106.35,178.63c-.23-.8-.53-1.62-.8-2.07-.4-.67-.65-.85-2.02-.85h-2.5v12.17c0,1.95,.2,2.22,2.57,2.37v.7h-7.27v-.7c2.32-.15,2.52-.4,2.52-2.37v-12.17h-2.27c-1.37,0-1.77,.15-2.2,.9-.3,.5-.5,1.1-.82,2.02h-.75c.15-1.52,.3-3.1,.38-4.5h.57c.38,.6,.65,.57,1.35,.57h9.9c.7,0,.9-.1,1.27-.57h.6c0,1.17,.1,2.97,.22,4.42l-.75,.08Z" fill="white"/><path d="M107.88,190.95v-.7c1.57-.15,1.75-.27,1.75-1.97v-5.15c0-1.55-.08-1.65-1.58-1.9v-.6c1.3-.23,2.47-.55,3.55-1v8.65c0,1.7,.18,1.82,1.8,1.97v.7h-5.52Zm2.6-13.77c-.7,0-1.33-.62-1.33-1.32,0-.8,.62-1.38,1.35-1.38s1.28,.58,1.28,1.38c0,.7-.58,1.32-1.3,1.32Z" fill="white"/><path d="M128.4,190.95v-.7c1.47-.15,1.65-.25,1.65-2v-4.17c0-1.77-.6-2.9-2.15-2.9-.95,0-1.83,.52-2.77,1.3,.02,.3,.08,.6,.08,1.08v4.85c0,1.57,.22,1.7,1.6,1.85v.7h-5.35v-.7c1.55-.15,1.78-.25,1.78-1.9v-4.3c0-1.85-.58-2.87-2.1-2.87-1,0-1.97,.67-2.75,1.3v5.87c0,1.65,.18,1.75,1.6,1.9v.7h-5.37v-.7c1.65-.15,1.8-.25,1.8-1.9v-5.17c0-1.6-.1-1.7-1.5-1.95v-.62c1.17-.17,2.35-.5,3.47-1v2.02c.53-.4,1.05-.82,1.85-1.3,.62-.4,1.17-.65,1.97-.65,1.2,0,2.22,.75,2.72,2.05,.7-.55,1.35-.98,1.97-1.4,.55-.35,1.25-.65,1.97-.65,1.95,0,3.15,1.4,3.15,3.85v4.82c0,1.67,.15,1.75,1.57,1.9v.7h-5.2Z" fill="white"/><path d="M144.17,188.83c-1.6,2-3.35,2.42-4.15,2.42-3.05,0-4.9-2.5-4.9-5.37,0-1.7,.6-3.27,1.62-4.37,1.05-1.17,2.42-1.82,3.72-1.82,2.17,0,3.82,1.87,3.82,3.87-.02,.5-.1,.72-.5,.82-.5,.1-3.7,.32-6.67,.42-.08,3.35,1.97,4.72,3.75,4.72,1.02,0,1.97-.42,2.9-1.27l.4,.57Zm-4.3-8.2c-1.15,0-2.27,1.07-2.62,3.1,1.4,0,2.8,0,4.27-.08,.45,0,.6-.12,.6-.5,.02-1.32-.85-2.52-2.25-2.52Z" fill="white"/><path d="M166.92,186.96c-.35,1.2-1,3-1.38,3.77-.72,.15-2.67,.6-4.57,.6-5.97,0-9.02-3.97-9.02-8.35,0-5.1,3.87-8.62,9.47-8.62,2.15,0,3.9,.45,4.72,.58,.1,1.12,.27,2.62,.47,3.87l-.78,.17c-.5-1.67-1.1-2.7-2.37-3.22-.65-.3-1.67-.48-2.62-.48-4.12,0-6.3,3.05-6.3,7.17,0,4.82,2.5,7.92,6.55,7.92,2.55,0,3.8-1.17,5.07-3.67l.75,.25Z" fill="white"/><path d="M177.05,191.25c-.38,0-.92-.2-1.17-.47-.32-.33-.47-.67-.6-1.12-1,.67-2.22,1.6-3,1.6-1.77,0-3.05-1.47-3.05-3.07,0-1.22,.67-2.02,2.05-2.5,1.52-.52,3.4-1.17,3.95-1.62v-.5c0-1.77-.9-2.8-2.2-2.8-.58,0-.92,.28-1.17,.58-.28,.35-.45,.9-.67,1.62-.12,.4-.35,.57-.73,.57-.47,0-1.1-.5-1.1-1.1,0-.35,.33-.65,.83-1,.72-.52,2.17-1.45,3.6-1.75,.75,0,1.53,.23,2.1,.68,.88,.75,1.28,1.6,1.28,2.9v4.82c0,1.15,.42,1.5,.87,1.5,.3,0,.62-.12,.9-.27l.25,.7-2.12,1.25Zm-1.82-6.32c-.55,.27-1.75,.8-2.32,1.05-.95,.42-1.53,.9-1.53,1.82,0,1.32,1,1.92,1.8,1.92,.65,0,1.55-.4,2.05-.9v-3.9Z" fill="white"/><path d="M185.97,190.95h-5.75v-.7c1.57-.15,1.72-.27,1.72-1.9v-5.2c0-1.65-.1-1.72-1.55-1.9v-.62c1.22-.2,2.35-.5,3.52-1.02v2.75c.88-1.3,1.92-2.67,3.17-2.67,.92,0,1.45,.58,1.45,1.2,0,.58-.4,1.12-.85,1.38-.25,.15-.45,.12-.65-.05-.38-.37-.67-.62-1.12-.62-.53,0-1.45,.77-2,2.05v4.7c0,1.65,.12,1.77,2.05,1.92v.7Z" fill="white"/><path d="M201.47,190.33c-.53,.1-2.4,.4-3.87,.92v-1.62c-.45,.27-1.1,.62-1.55,.9-1,.57-1.67,.72-1.97,.72-2,0-4.65-2-4.65-5.5s3.05-6.07,6.47-6.07c.35,0,1.17,.05,1.7,.27v-3.67c0-1.6-.17-1.65-1.95-1.8v-.65c1.25-.18,3-.52,3.92-.85v15.07c0,1.3,.17,1.47,1.1,1.52l.8,.05v.7Zm-3.87-8.4c-.58-.87-1.65-1.27-2.62-1.27-1.2,0-3.32,.8-3.32,4.4,0,3.02,1.88,4.62,3.47,4.65,.9,0,1.87-.45,2.47-.9v-6.87Z" fill="white"/><path d="M507.39,267.29c-.11,7.26-2.86,13.31-8.37,18.08-.75,.65-1.79,.57-2.42-.16-.59-.69-.52-1.72,.22-2.36,2.01-1.73,3.68-3.74,4.91-6.1,3.87-7.38,2.92-16.21-2.46-22.57-.74-.88-1.6-1.66-2.44-2.45-.73-.68-.84-1.69-.22-2.42,.62-.71,1.66-.77,2.42-.12,4.63,4,7.37,9.02,8.21,15.08,.07,.47,.12,.95,.14,1.43,.02,.52,0,1.05,0,1.57Z" fill="white"/><path d="M499.74,267.92c-.12,4.81-2.18,9.33-6.29,12.89-.81,.7-1.86,.62-2.53-.16-.64-.74-.55-1.81,.24-2.5,2.15-1.86,3.72-4.11,4.47-6.85,1.51-5.59,.13-10.42-4-14.47-.15-.15-.32-.29-.48-.43-.75-.67-.85-1.75-.23-2.48,.66-.77,1.74-.85,2.53-.18,1.87,1.59,3.35,3.47,4.44,5.67,1.23,2.49,1.84,5.13,1.85,8.51Z" fill="white"/><path d="M486.91,275.48c-.7,0-1.32-.42-1.58-1.08-.26-.66-.09-1.39,.47-1.88,.82-.72,1.48-1.56,1.91-2.57,1.17-2.76,.44-5.81-1.88-7.87-.55-.49-.75-1.1-.54-1.8,.21-.68,.69-1.07,1.39-1.17,.49-.07,.93,.07,1.31,.39,4.25,3.52,4.85,10,1.31,14.24-.4,.48-.85,.93-1.34,1.33-.28,.23-.68,.34-1.02,.5l-.04-.09Z" fill="white"/><path d="M101.94,241.92h45.72c5.04,0,9.14,4.1,9.14,9.14v32.44c0,5.05-4.1,9.14-9.14,9.14h-45.71c-5.05,0-9.14-4.1-9.14-9.14v-32.43c0-5.05,4.1-9.14,9.14-9.14Z" style="fill:url(#_grad1);"/><path d="M156.8,257.9v-1.17h-11.72v-14.81h-1.17v14.81h-9.5c-1.25-3.97-4.75-6.9-9.02-7.3v-7.51h-1.17v7.43c-4.63,.08-8.5,3.17-9.82,7.38h-8.63v-14.81h-1.17v14.81h-11.8v1.17h11.8v16.59h-11.8v1.17h11.8v16.97h1.17v-16.97h8.38c1.06,4.62,5.15,8.08,10.06,8.17v8.8h1.17v-8.88c4.56-.43,8.27-3.73,9.27-8.09h9.25v16.97h1.17v-16.97h11.72v-1.17h-6.99v-16.59h6.99Zm-42.82,16.59h-8.21v-16.59h8.29c-.12,.64-.2,1.29-.2,1.96,0,2.46,.87,4.83,2.47,6.73-1.6,1.91-2.47,4.27-2.47,6.73,0,.4,.08,.78,.12,1.17Zm10.41,8.19c-5.16,0-9.36-4.2-9.36-9.36,0-2.33,.89-4.58,2.51-6.33l.36-.4-.36-.4c-1.62-1.75-2.51-4-2.51-6.33,0-5.16,4.2-9.36,9.36-9.36s9.36,4.2,9.36,9.36c0,2.34-.89,4.58-2.51,6.33l-.37,.4,.37,.4c1.62,1.75,2.51,4,2.51,6.33,0,5.16-4.2,9.36-9.36,9.36Zm24.24-8.19h-13.82c.04-.39,.12-.77,.12-1.17,0-2.46-.87-4.82-2.47-6.73,1.6-1.9,2.47-4.27,2.47-6.73,0-.67-.08-1.32-.2-1.96h13.9v16.59Z" style="fill:url(#_grad2); mix-blend-mode:multiply;"/><path id="p01" d="M110.21,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p02" d="M92.8,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p03" d="M110.86,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p04" d="M110.21,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p05" d="M92.8,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p06" d="M110.86,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p07" d="M110.21,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p11" d="M136.13,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p12" d="M118.73,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p13" d="M136.78,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p14" d="M136.13,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p15" d="M118.73,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p16" d="M136.78,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p17" d="M136.13,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/> <path id="p21" d="M162.06,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p22" d="M144.65,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p23" d="M162.7,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p24" d="M162.06,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p25" d="M144.65,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p26" d="M162.7,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p27" d="M162.06,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p31" d="M187.98,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p32" d="M170.57,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p33" d="M188.62,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p34" d="M187.98,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p35" d="M170.57,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p36" d="M188.62,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p37" d="M187.98,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p41" d="M230.16,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p42" d="M212.75,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p43" d="M230.81,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p44" d="M230.16,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p45" d="M212.75,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p46" d="M230.81,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p47" d="M230.16,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p51" d="M256.08,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p52" d="M238.67,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p53" d="M256.73,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p54" d="M256.08,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p55" d="M238.67,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p56" d="M256.73,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p57" d="M256.08,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p61" d="M298.3,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p62" d="M280.89,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p63" d="M298.94,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p64" d="M298.3,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p65" d="M280.89,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p66" d="M298.94,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p67" d="M298.3,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p71" d="M340.49,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p72" d="M323.08,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p73" d="M341.13,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p74" d="M340.49,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p75" d="M323.08,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p76" d="M341.13,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p77" d="M340.49,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p81" d="M366.41,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p82" d="M349,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p83" d="M367.05,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p84" d="M366.41,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p85" d="M349,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p86" d="M367.05,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p87" d="M366.41,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p91" d="M408.62,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p92" d="M391.22,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p93" d="M409.27,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p94" d="M408.62,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p95" d="M391.22,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p96" d="M409.27,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p97" d="M408.62,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p101" d="M434.55,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p102" d="M417.14,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p103" d="M435.19,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p104" d="M434.55,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p105" d="M417.14,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p106" d="M435.19,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p107" d="M434.55,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p111" d="M476.76,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p112" d="M459.35,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p113" d="M477.41,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p114" d="M476.76,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p115" d="M459.35,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p116" d="M477.41,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p117" d="M476.76,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p121" d="M502.68,332.41l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p122" d="M485.27,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p123" d="M503.33,336.93l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p124" d="M502.68,350.47l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="p125" d="M485.27,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p126" d="M503.33,354.98l1.93-1.93,1.93,1.93v12.9l-1.93,1.93-1.93-1.93v-12.9Z" fill="white" fill-opacity="0.05"/><path id="p127" d="M502.68,368.52l1.93,1.93-1.93,1.93h-12.9l-1.93-1.93,1.93-1.93h12.9Z" fill="white" fill-opacity="0.05"/><path id="pcolon1" d="M200.49,346.34v-4.35h4.29v4.35h-4.29Zm0,16.47v-4.35h4.29v4.35h-4.29Z" fill="white"/><path id="pcolon2" d="M268.6,346.34v-4.35h4.29v4.35h-4.29Zm0,16.47v-4.35h4.29v4.35h-4.29Z" fill="white"/><path id="pcolon3" d="M310.78,346.34v-4.35h4.29v4.35h-4.29Zm0,16.47v-4.35h4.29v4.35h-4.29Z" fill="white"/><path id="pcolon4" d="M378.92,346.34v-4.35h4.29v4.35h-4.29Zm0,16.47v-4.35h4.29v4.35h-4.29Z" fill="white"/><path id="pcolon5" d="M447.06,346.34v-4.35h4.29v4.35h-4.29Zm0,16.47v-4.35h4.29v4.35h-4.29Z" fill="white"/><style>';
    }

}
// File: Utilities.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

library utils {    
    function uint2str(
        uint256 _i
    ) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
            return "0";
        }
        uint256 j = _i;
        uint256 len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint256 k = len;
        while (_i != 0) {
            k = k - 1;
            uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }

    function seconds2strv2(uint256 _seconds) internal pure returns (uint[13] memory _numbers) {
        uint _years = _seconds / 31536000;
        _seconds -= _years * 31536000;

        uint _weeks = _seconds / 604800;
        _seconds -= _weeks * 604800;

        uint _days = _seconds / 86400;
        _seconds -= _days * 86400;

        uint _hours = _seconds / 3600;
        _seconds -= _hours * 3600;

        uint _minutes = _seconds / 60;
        _seconds -= _minutes * 60;

        uint _secondsRemaining = _seconds;

        if(_years < 10) {
            _numbers[0] = 0;
            _numbers[1] = 0;
            _numbers[2] = 0;
            _numbers[3] = _years;
        } else if(_years < 100) {
            _numbers[0] = 0;
            _numbers[1] = 0;
            _numbers[2] = (_years / 10);
            _numbers[3] = (_years % 10);
        } else if(_years < 1000) {
            _numbers[0] = 0;
            _numbers[1] = (_years / 100);
            _numbers[2] = ((_years % 100) / 10);
            _numbers[3] = (_years % 10);
        } else if(_years < 10000) {
            _numbers[0] = (_years / 1000);
            _numbers[1] = ((_years % 1000) / 100);
            _numbers[2] = ((_years % 100) / 10);
            _numbers[3] = (_years % 10);
        }

        if(_weeks < 10) {
            _numbers[4] = 0;
            _numbers[5] = _weeks;
        } else {
            _numbers[4] = (_weeks / 10);
            _numbers[5] = (_weeks % 10);
        }

        _numbers[6] = _days;

        if(_hours < 10) {
            _numbers[7] = 0;
            _numbers[8] = _hours;
        } else {
            _numbers[7] = (_hours / 10);
            _numbers[8] = (_hours % 10);
        }

        if(_minutes < 10) {
            _numbers[9] = 0;
            _numbers[10] = _minutes;
        } else {
            _numbers[9] = (_minutes / 10);
            _numbers[10] = (_minutes % 10);
        }


        if(_secondsRemaining < 10) {
            _numbers[11] = 0;
            _numbers[12] = _secondsRemaining;
        } else {
            _numbers[11] = (_secondsRemaining / 10);
            _numbers[12] = (_secondsRemaining % 10);
        }
        
        return _numbers;
    }

    // Get a pseudo random number
    function random(uint256 input, uint256 min, uint256 max) internal pure returns (uint256) {
        uint256 randRange = max - min;
        return max - (uint256(keccak256(abi.encodePacked(input + 2023))) % randRange) - 1;
    }

    function randomNumber(uint256 input, uint256 min, uint256 max) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input))) % (max - min + 1) + min;
    }

    function initValue(uint256 tokenId) internal pure returns (uint256 value) {
        if (tokenId <= 5000) {
            value = randomNumber(tokenId, 24 * 3600, 1000 * 3600);
        }  else if (tokenId <= 8000) {
            value = randomNumber(tokenId, 12 * 3600, 500 * 3600);
        }  else {
            value = randomNumber(tokenId, 6 * 3600, 250 * 3600);
        }
        return value;
    }

    function getRgbs(uint tokenId, uint baseColor) internal pure returns (uint256[3] memory rgbValues) {
        if (baseColor > 0) {
            for (uint i = 0; i < 3; i++) {
                if (baseColor == i + 1) {
                    rgbValues[i] = 255;
                } else {
                    rgbValues[i] = utils.random(tokenId + i, 0, 256);
                }
            }
        } else {
            for (uint i = 0; i < 3; i++) {
                rgbValues[i] = 255;
            }
        }
        return rgbValues;
    }

    function getMintPhase(uint tokenId) internal pure returns (uint mintPhase) {
        if (tokenId <= 5000) {
            mintPhase = 1;
        } else if (tokenId <= 8000) {
            mintPhase = 2;
        } else {
            mintPhase = 3;
        }
    }

    function secondsRemaining(uint end) internal view returns (uint) {
        if (block.timestamp <= end) {
            return end - block.timestamp;
        } else {
            return 0;
        }
    }

    function minutesRemaining(uint end) internal view returns (uint) {
        if (secondsRemaining(end) >= 60) {
            return (end - block.timestamp) / 60;
        } else {
            return 0;
        }
    }
}