ETH Price: $2,328.87 (+0.93%)

Transaction Decoder

Block:
22532398 at May-21-2025 04:16:11 PM +UTC
Transaction Fee:
0.002362194482905313 ETH $5.50
Gas Used:
170,737 Gas / 13.835281649 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
14.29608372392663447 Eth14.29625446092663447 Eth0.000170737
0xC67d7BCb...51aEEC6cD
0.029506048989317349 Eth
Nonce: 101
0.017142735284132176 Eth
Nonce: 102
0.012363313705185173
0xEf0cd0F1...1e7d96856 19.695099803163047457 Eth19.705100922385327317 Eth0.01000111922227986

Execution Trace

ETH 0.010101130414502658 MegaRabbit.batchMint( amount=1, mintId=1 ) => ( totalCostWithFee=10001119222279860 )
  • 0x65db9966492c0a5ac0ef15c018c19ee383f7a8cf.STATICCALL( )
    • ERC1967Proxy.a4ae35e0( )
      • 0xdd24f84d36bf92c65f92307595335bdfab5bbd21.a4ae35e0( )
      • ETH 0.000100011192222798 0xc67d7bcb9b05c83aeab73d07f119e4851aeec6cd.CALL( )
        File 1 of 2: MegaRabbit
        // Sources flattened with hardhat v2.22.19 https://hardhat.org
        
        // SPDX-License-Identifier: Apache-2.0 AND CC0-1.0 AND MIT AND UNLICENSED
        
        // File @openzeppelin/contracts/utils/Context.sol@v5.1.0
        
        // Original license: SPDX_License_Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
        
        pragma solidity ^0.8.20;
        
        /**
         * @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;
            }
        
            function _contextSuffixLength() internal view virtual returns (uint256) {
                return 0;
            }
        }
        
        
        // File @openzeppelin/contracts/access/Ownable.sol@v5.1.0
        
        // Original license: SPDX_License_Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
        
        pragma solidity ^0.8.20;
        
        /**
         * @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.
         *
         * The initial owner is set to the address provided by the deployer. 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;
        
            /**
             * @dev The caller account is not authorized to perform an operation.
             */
            error OwnableUnauthorizedAccount(address account);
        
            /**
             * @dev The owner is not a valid owner account. (eg. `address(0)`)
             */
            error OwnableInvalidOwner(address owner);
        
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
            /**
             * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
             */
            constructor(address initialOwner) {
                if (initialOwner == address(0)) {
                    revert OwnableInvalidOwner(address(0));
                }
                _transferOwnership(initialOwner);
            }
        
            /**
             * @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 {
                if (owner() != _msgSender()) {
                    revert OwnableUnauthorizedAccount(_msgSender());
                }
            }
        
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby disabling 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 {
                if (newOwner == address(0)) {
                    revert OwnableInvalidOwner(address(0));
                }
                _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 @openzeppelin/contracts/utils/introspection/IERC165.sol@v5.1.0
        
        // Original license: SPDX_License_Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
        
        pragma solidity ^0.8.20;
        
        /**
         * @dev Interface of the ERC-165 standard, as defined in the
         * https://eips.ethereum.org/EIPS/eip-165[ERC].
         *
         * Implementers can declare support of contract interfaces, which can then be
         * queried by others ({ERC165Checker}).
         *
         * For an implementation, see {ERC165}.
         */
        interface IERC165 {
            /**
             * @dev Returns true if this contract implements the interface defined by
             * `interfaceId`. See the corresponding
             * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
             * to learn more about how these ids are created.
             *
             * This function call must use less than 30 000 gas.
             */
            function supportsInterface(bytes4 interfaceId) external view returns (bool);
        }
        
        
        // File @openzeppelin/contracts/interfaces/IERC2981.sol@v5.1.0
        
        // Original license: SPDX_License_Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC2981.sol)
        
        pragma solidity ^0.8.20;
        
        /**
         * @dev Interface for the NFT Royalty Standard.
         *
         * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
         * support for royalty payments across all NFT marketplaces and ecosystem participants.
         */
        interface IERC2981 is IERC165 {
            /**
             * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
             * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
             *
             * NOTE: ERC-2981 allows setting the royalty to 100% of the price. In that case all the price would be sent to the
             * royalty receiver and 0 tokens to the seller. Contracts dealing with royalty should consider empty transfers.
             */
            function royaltyInfo(
                uint256 tokenId,
                uint256 salePrice
            ) external view returns (address receiver, uint256 royaltyAmount);
        }
        
        
        // File @openzeppelin/contracts/utils/introspection/ERC165.sol@v5.1.0
        
        // Original license: SPDX_License_Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
        
        pragma solidity ^0.8.20;
        
        /**
         * @dev Implementation of the {IERC165} interface.
         *
         * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
         * for the additional interface id that will be supported. For example:
         *
         * ```solidity
         * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
         *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
         * }
         * ```
         */
        abstract contract ERC165 is IERC165 {
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                return interfaceId == type(IERC165).interfaceId;
            }
        }
        
        
        // File @openzeppelin/contracts/token/common/ERC2981.sol@v5.1.0
        
        // Original license: SPDX_License_Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.1.0) (token/common/ERC2981.sol)
        
        pragma solidity ^0.8.20;
        
        
        /**
         * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
         *
         * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
         * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
         *
         * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
         * fee is specified in basis points by default.
         *
         * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
         * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the ERC. Marketplaces are expected to
         * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
         */
        abstract contract ERC2981 is IERC2981, ERC165 {
            struct RoyaltyInfo {
                address receiver;
                uint96 royaltyFraction;
            }
        
            RoyaltyInfo private _defaultRoyaltyInfo;
            mapping(uint256 tokenId => RoyaltyInfo) private _tokenRoyaltyInfo;
        
            /**
             * @dev The default royalty set is invalid (eg. (numerator / denominator) >= 1).
             */
            error ERC2981InvalidDefaultRoyalty(uint256 numerator, uint256 denominator);
        
            /**
             * @dev The default royalty receiver is invalid.
             */
            error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);
        
            /**
             * @dev The royalty set for an specific `tokenId` is invalid (eg. (numerator / denominator) >= 1).
             */
            error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);
        
            /**
             * @dev The royalty receiver for `tokenId` is invalid.
             */
            error ERC2981InvalidTokenRoyaltyReceiver(uint256 tokenId, address receiver);
        
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
                return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
            }
        
            /**
             * @inheritdoc IERC2981
             */
            function royaltyInfo(
                uint256 tokenId,
                uint256 salePrice
            ) public view virtual returns (address receiver, uint256 amount) {
                RoyaltyInfo storage _royaltyInfo = _tokenRoyaltyInfo[tokenId];
                address royaltyReceiver = _royaltyInfo.receiver;
                uint96 royaltyFraction = _royaltyInfo.royaltyFraction;
        
                if (royaltyReceiver == address(0)) {
                    royaltyReceiver = _defaultRoyaltyInfo.receiver;
                    royaltyFraction = _defaultRoyaltyInfo.royaltyFraction;
                }
        
                uint256 royaltyAmount = (salePrice * royaltyFraction) / _feeDenominator();
        
                return (royaltyReceiver, royaltyAmount);
            }
        
            /**
             * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
             * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
             * override.
             */
            function _feeDenominator() internal pure virtual returns (uint96) {
                return 10000;
            }
        
            /**
             * @dev Sets the royalty information that all ids in this contract will default to.
             *
             * Requirements:
             *
             * - `receiver` cannot be the zero address.
             * - `feeNumerator` cannot be greater than the fee denominator.
             */
            function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
                uint256 denominator = _feeDenominator();
                if (feeNumerator > denominator) {
                    // Royalty fee will exceed the sale price
                    revert ERC2981InvalidDefaultRoyalty(feeNumerator, denominator);
                }
                if (receiver == address(0)) {
                    revert ERC2981InvalidDefaultRoyaltyReceiver(address(0));
                }
        
                _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
            }
        
            /**
             * @dev Removes default royalty information.
             */
            function _deleteDefaultRoyalty() internal virtual {
                delete _defaultRoyaltyInfo;
            }
        
            /**
             * @dev Sets the royalty information for a specific token id, overriding the global default.
             *
             * Requirements:
             *
             * - `receiver` cannot be the zero address.
             * - `feeNumerator` cannot be greater than the fee denominator.
             */
            function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
                uint256 denominator = _feeDenominator();
                if (feeNumerator > denominator) {
                    // Royalty fee will exceed the sale price
                    revert ERC2981InvalidTokenRoyalty(tokenId, feeNumerator, denominator);
                }
                if (receiver == address(0)) {
                    revert ERC2981InvalidTokenRoyaltyReceiver(tokenId, address(0));
                }
        
                _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
            }
        
            /**
             * @dev Resets royalty information for the token id back to the global default.
             */
            function _resetTokenRoyalty(uint256 tokenId) internal virtual {
                delete _tokenRoyaltyInfo[tokenId];
            }
        }
        
        
        // File @pythnetwork/pyth-sdk-solidity/IPythEvents.sol@v4.0.0
        
        // Original license: SPDX_License_Identifier: Apache-2.0
        pragma solidity ^0.8.0;
        
        /// @title IPythEvents contains the events that Pyth contract emits.
        /// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
        interface IPythEvents {
            /// @dev Emitted when the price feed with `id` has received a fresh update.
            /// @param id The Pyth Price Feed ID.
            /// @param publishTime Publish time of the given price update.
            /// @param price Price of the given price update.
            /// @param conf Confidence interval of the given price update.
            event PriceFeedUpdate(
                bytes32 indexed id,
                uint64 publishTime,
                int64 price,
                uint64 conf
            );
        }
        
        
        // File @pythnetwork/pyth-sdk-solidity/PythStructs.sol@v4.0.0
        
        // Original license: SPDX_License_Identifier: Apache-2.0
        pragma solidity ^0.8.0;
        
        contract PythStructs {
            // A price with a degree of uncertainty, represented as a price +- a confidence interval.
            //
            // The confidence interval roughly corresponds to the standard error of a normal distribution.
            // Both the price and confidence are stored in a fixed-point numeric representation,
            // `x * (10^expo)`, where `expo` is the exponent.
            //
            // Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
            // to how this price safely.
            struct Price {
                // Price
                int64 price;
                // Confidence interval around the price
                uint64 conf;
                // Price exponent
                int32 expo;
                // Unix timestamp describing when the price was published
                uint publishTime;
            }
        
            // PriceFeed represents a current aggregate price from pyth publisher feeds.
            struct PriceFeed {
                // The price ID.
                bytes32 id;
                // Latest available price
                Price price;
                // Latest available exponentially-weighted moving average price
                Price emaPrice;
            }
        }
        
        
        // File @pythnetwork/pyth-sdk-solidity/IPyth.sol@v4.0.0
        
        // Original license: SPDX_License_Identifier: Apache-2.0
        pragma solidity ^0.8.0;
        
        
        /// @title Consume prices from the Pyth Network (https://pyth.network/).
        /// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
        /// @author Pyth Data Association
        interface IPyth is IPythEvents {
            /// @notice Returns the price of a price feed without any sanity checks.
            /// @dev This function returns the most recent price update in this contract without any recency checks.
            /// This function is unsafe as the returned price update may be arbitrarily far in the past.
            ///
            /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
            /// sufficiently recent for their application. If you are considering using this function, it may be
            /// safer / easier to use `getPriceNoOlderThan`.
            /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
            function getPriceUnsafe(
                bytes32 id
            ) external view returns (PythStructs.Price memory price);
        
            /// @notice Returns the price that is no older than `age` seconds of the current time.
            /// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
            /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
            /// recently.
            /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
            function getPriceNoOlderThan(
                bytes32 id,
                uint age
            ) external view returns (PythStructs.Price memory price);
        
            /// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
            /// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
            /// However, if the price is not recent this function returns the latest available price.
            ///
            /// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
            /// the returned price is recent or useful for any particular application.
            ///
            /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
            /// sufficiently recent for their application. If you are considering using this function, it may be
            /// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
            /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
            function getEmaPriceUnsafe(
                bytes32 id
            ) external view returns (PythStructs.Price memory price);
        
            /// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
            /// of the current time.
            /// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
            /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
            /// recently.
            /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
            function getEmaPriceNoOlderThan(
                bytes32 id,
                uint age
            ) external view returns (PythStructs.Price memory price);
        
            /// @notice Update price feeds with given update messages.
            /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
            /// `getUpdateFee` with the length of the `updateData` array.
            /// Prices will be updated if they are more recent than the current stored prices.
            /// The call will succeed even if the update is not the most recent.
            /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
            /// @param updateData Array of price update data.
            function updatePriceFeeds(bytes[] calldata updateData) external payable;
        
            /// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
            /// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
            /// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
            ///
            /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
            /// `getUpdateFee` with the length of the `updateData` array.
            ///
            /// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
            /// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
            /// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
            /// Otherwise, it calls updatePriceFeeds method to update the prices.
            ///
            /// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
            /// @param updateData Array of price update data.
            /// @param priceIds Array of price ids.
            /// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
            function updatePriceFeedsIfNecessary(
                bytes[] calldata updateData,
                bytes32[] calldata priceIds,
                uint64[] calldata publishTimes
            ) external payable;
        
            /// @notice Returns the required fee to update an array of price updates.
            /// @param updateData Array of price update data.
            /// @return feeAmount The required fee in Wei.
            function getUpdateFee(
                bytes[] calldata updateData
            ) external view returns (uint feeAmount);
        
            /// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
            /// within `minPublishTime` and `maxPublishTime`.
            ///
            /// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
            /// otherwise, please consider using `updatePriceFeeds`. This method may store the price updates on-chain, if they
            /// are more recent than the current stored prices.
            ///
            /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
            /// `getUpdateFee` with the length of the `updateData` array.
            ///
            ///
            /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
            /// no update for any of the given `priceIds` within the given time range.
            /// @param updateData Array of price update data.
            /// @param priceIds Array of price ids.
            /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
            /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
            /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
            function parsePriceFeedUpdates(
                bytes[] calldata updateData,
                bytes32[] calldata priceIds,
                uint64 minPublishTime,
                uint64 maxPublishTime
            ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
        
            /// @notice Similar to `parsePriceFeedUpdates` but ensures the updates returned are
            /// the first updates published in minPublishTime. That is, if there are multiple updates for a given timestamp,
            /// this method will return the first update. This method may store the price updates on-chain, if they
            /// are more recent than the current stored prices.
            ///
            ///
            /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
            /// no update for any of the given `priceIds` within the given time range and uniqueness condition.
            /// @param updateData Array of price update data.
            /// @param priceIds Array of price ids.
            /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
            /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
            /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
            function parsePriceFeedUpdatesUnique(
                bytes[] calldata updateData,
                bytes32[] calldata priceIds,
                uint64 minPublishTime,
                uint64 maxPublishTime
            ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
        }
        
        
        // File contracts/main-contracts/KingdomlyFeeContract.sol
        
        // Original license: SPDX_License_Identifier: UNLICENSED
        pragma solidity ^0.8.24;
        
        
        error InsufficientUpdateFee(uint256 requiredFee);
        error ContractNotVerified(address contractAddress);
        
        contract KingdomlyFeeContract is Ownable {
            uint256 private cachedOneDollarInWei;
            uint256 private maxPriceAgeInSeconds;
        
            IPyth pyth;
            bytes32 ethUsdPriceId;
        
            constructor(address _pyth, bytes32 _ethUsdPriceId) Ownable(msg.sender) {
                pyth = IPyth(_pyth);
                ethUsdPriceId = _ethUsdPriceId;
                maxPriceAgeInSeconds = 60 * 60 * 24;
            }
        
            function getOneDollarInWei() public view returns (uint256) {
                try pyth.getPriceNoOlderThan(ethUsdPriceId, maxPriceAgeInSeconds) returns (PythStructs.Price memory price) {
                    uint256 ethPrice18Decimals =
                        (uint256(uint64(price.price)) * (10 ** 18)) / (10 ** uint8(uint32(-1 * price.expo)));
                    uint256 oneDollarInWei = ((10 ** 18) * (10 ** 18)) / ethPrice18Decimals;
        
                    return oneDollarInWei;
                } catch {
                    return cachedOneDollarInWei;
                }
            }
        
            function updateOracleAndGetOneDollarInWei(bytes[] calldata pythPriceUpdate) public payable returns (uint256) {
                uint256 updateFee = pyth.getUpdateFee(pythPriceUpdate);
        
                if (msg.value != updateFee) {
                    revert InsufficientUpdateFee(updateFee);
                }
        
                pyth.updatePriceFeeds{value: msg.value}(pythPriceUpdate);
        
                cachedOneDollarInWei = getOneDollarInWei();
        
                return cachedOneDollarInWei;
            }
        
            function updateMaxPriceAgeInSeconds(uint256 _maxPriceAgeInSeconds) public onlyOwner {
                maxPriceAgeInSeconds = _maxPriceAgeInSeconds;
            }
        }
        
        
        // File contracts/utils/IDelegateRegistry.sol
        
        // Original license: SPDX_License_Identifier: CC0-1.0
        pragma solidity >=0.8.13;
        
        /**
         * @title IDelegateRegistry
         * @custom:version 2.0
         * @custom:author foobar (0xfoobar)
         * @notice A standalone immutable registry storing delegated permissions from one address to another
         */
        interface IDelegateRegistry {
            /// @notice Delegation type, NONE is used when a delegation does not exist or is revoked
            enum DelegationType {
                NONE,
                ALL,
                CONTRACT,
                ERC721,
                ERC20,
                ERC1155
            }
        
            /// @notice Struct for returning delegations
            struct Delegation {
                DelegationType type_;
                address to;
                address from;
                bytes32 rights;
                address contract_;
                uint256 tokenId;
                uint256 amount;
            }
        
            /// @notice Emitted when an address delegates or revokes rights for their entire wallet
            event DelegateAll(address indexed from, address indexed to, bytes32 rights, bool enable);
        
            /// @notice Emitted when an address delegates or revokes rights for a contract address
            event DelegateContract(
                address indexed from, address indexed to, address indexed contract_, bytes32 rights, bool enable
            );
        
            /// @notice Emitted when an address delegates or revokes rights for an ERC721 tokenId
            event DelegateERC721(
                address indexed from,
                address indexed to,
                address indexed contract_,
                uint256 tokenId,
                bytes32 rights,
                bool enable
            );
        
            /// @notice Emitted when an address delegates or revokes rights for an amount of ERC20 tokens
            event DelegateERC20(
                address indexed from, address indexed to, address indexed contract_, bytes32 rights, uint256 amount
            );
        
            /// @notice Emitted when an address delegates or revokes rights for an amount of an ERC1155 tokenId
            event DelegateERC1155(
                address indexed from,
                address indexed to,
                address indexed contract_,
                uint256 tokenId,
                bytes32 rights,
                uint256 amount
            );
        
            /// @notice Thrown if multicall calldata is malformed
            error MulticallFailed();
        
            /**
             * -----------  WRITE -----------
             */
        
            /**
             * @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
             * @param data The encoded function data for each of the calls to make to this contract
             * @return results The results from each of the calls passed in via data
             */
            function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
        
            /**
             * @notice Allow the delegate to act on behalf of `msg.sender` for all contracts
             * @param to The address to act as delegate
             * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
             * @param enable Whether to enable or disable this delegation, true delegates and false revokes
             * @return delegationHash The unique identifier of the delegation
             */
            function delegateAll(address to, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);
        
            /**
             * @notice Allow the delegate to act on behalf of `msg.sender` for a specific contract
             * @param to The address to act as delegate
             * @param contract_ The contract whose rights are being delegated
             * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
             * @param enable Whether to enable or disable this delegation, true delegates and false revokes
             * @return delegationHash The unique identifier of the delegation
             */
            function delegateContract(address to, address contract_, bytes32 rights, bool enable)
                external
                payable
                returns (bytes32 delegationHash);
        
            /**
             * @notice Allow the delegate to act on behalf of `msg.sender` for a specific ERC721 token
             * @param to The address to act as delegate
             * @param contract_ The contract whose rights are being delegated
             * @param tokenId The token id to delegate
             * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
             * @param enable Whether to enable or disable this delegation, true delegates and false revokes
             * @return delegationHash The unique identifier of the delegation
             */
            function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable)
                external
                payable
                returns (bytes32 delegationHash);
        
            /**
             * @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC20 tokens
             * @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
             * @param to The address to act as delegate
             * @param contract_ The address for the fungible token contract
             * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
             * @param amount The amount to delegate, > 0 delegates and 0 revokes
             * @return delegationHash The unique identifier of the delegation
             */
            function delegateERC20(address to, address contract_, bytes32 rights, uint256 amount)
                external
                payable
                returns (bytes32 delegationHash);
        
            /**
             * @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC1155 tokens
             * @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
             * @param to The address to act as delegate
             * @param contract_ The address of the contract that holds the token
             * @param tokenId The token id to delegate
             * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
             * @param amount The amount of that token id to delegate, > 0 delegates and 0 revokes
             * @return delegationHash The unique identifier of the delegation
             */
            function delegateERC1155(address to, address contract_, uint256 tokenId, bytes32 rights, uint256 amount)
                external
                payable
                returns (bytes32 delegationHash);
        
            /**
             * ----------- CHECKS -----------
             */
        
            /**
             * @notice Check if `to` is a delegate of `from` for the entire wallet
             * @param to The potential delegate address
             * @param from The potential address who delegated rights
             * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
             * @return valid Whether delegate is granted to act on the from's behalf
             */
            function checkDelegateForAll(address to, address from, bytes32 rights) external view returns (bool);
        
            /**
             * @notice Check if `to` is a delegate of `from` for the specified `contract_` or the entire wallet
             * @param to The delegated address to check
             * @param contract_ The specific contract address being checked
             * @param from The cold wallet who issued the delegation
             * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
             * @return valid Whether delegate is granted to act on from's behalf for entire wallet or that specific contract
             */
            function checkDelegateForContract(address to, address from, address contract_, bytes32 rights)
                external
                view
                returns (bool);
        
            /**
             * @notice Check if `to` is a delegate of `from` for the specific `contract` and `tokenId`, the entire `contract_`, or the entire wallet
             * @param to The delegated address to check
             * @param contract_ The specific contract address being checked
             * @param tokenId The token id for the token to delegating
             * @param from The wallet that issued the delegation
             * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
             * @return valid Whether delegate is granted to act on from's behalf for entire wallet, that contract, or that specific tokenId
             */
            function checkDelegateForERC721(address to, address from, address contract_, uint256 tokenId, bytes32 rights)
                external
                view
                returns (bool);
        
            /**
             * @notice Returns the amount of ERC20 tokens the delegate is granted rights to act on the behalf of
             * @param to The delegated address to check
             * @param contract_ The address of the token contract
             * @param from The cold wallet who issued the delegation
             * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
             * @return balance The delegated balance, which will be 0 if the delegation does not exist
             */
            function checkDelegateForERC20(address to, address from, address contract_, bytes32 rights)
                external
                view
                returns (uint256);
        
            /**
             * @notice Returns the amount of a ERC1155 tokens the delegate is granted rights to act on the behalf of
             * @param to The delegated address to check
             * @param contract_ The address of the token contract
             * @param tokenId The token id to check the delegated amount of
             * @param from The cold wallet who issued the delegation
             * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
             * @return balance The delegated balance, which will be 0 if the delegation does not exist
             */
            function checkDelegateForERC1155(address to, address from, address contract_, uint256 tokenId, bytes32 rights)
                external
                view
                returns (uint256);
        
            /**
             * ----------- ENUMERATIONS -----------
             */
        
            /**
             * @notice Returns all enabled delegations a given delegate has received
             * @param to The address to retrieve delegations for
             * @return delegations Array of Delegation structs
             */
            function getIncomingDelegations(address to) external view returns (Delegation[] memory delegations);
        
            /**
             * @notice Returns all enabled delegations an address has given out
             * @param from The address to retrieve delegations for
             * @return delegations Array of Delegation structs
             */
            function getOutgoingDelegations(address from) external view returns (Delegation[] memory delegations);
        
            /**
             * @notice Returns all hashes associated with enabled delegations an address has received
             * @param to The address to retrieve incoming delegation hashes for
             * @return delegationHashes Array of delegation hashes
             */
            function getIncomingDelegationHashes(address to) external view returns (bytes32[] memory delegationHashes);
        
            /**
             * @notice Returns all hashes associated with enabled delegations an address has given out
             * @param from The address to retrieve outgoing delegation hashes for
             * @return delegationHashes Array of delegation hashes
             */
            function getOutgoingDelegationHashes(address from) external view returns (bytes32[] memory delegationHashes);
        
            /**
             * @notice Returns the delegations for a given array of delegation hashes
             * @param delegationHashes is an array of hashes that correspond to delegations
             * @return delegations Array of Delegation structs, return empty structs for nonexistent or revoked delegations
             */
            function getDelegationsFromHashes(bytes32[] calldata delegationHashes)
                external
                view
                returns (Delegation[] memory delegations);
        
            /**
             * ----------- STORAGE ACCESS -----------
             */
        
            /**
             * @notice Allows external contracts to read arbitrary storage slots
             */
            function readSlot(bytes32 location) external view returns (bytes32);
        
            /**
             * @notice Allows external contracts to read an arbitrary array of storage slots
             */
            function readSlots(bytes32[] calldata locations) external view returns (bytes32[] memory);
        }
        
        
        // File erc721a/contracts/IERC721A.sol@v4.3.0
        
        // Original license: SPDX_License_Identifier: MIT
        // ERC721A Contracts v4.3.0
        // 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();
        
            /**
             * `_sequentialUpTo()` must be greater than `_startTokenId()`.
             */
            error SequentialUpToTooSmall();
        
            /**
             * The `tokenId` of a sequential mint exceeds `_sequentialUpTo()`.
             */
            error SequentialMintExceedsLimit();
        
            /**
             * Spot minting requires a `tokenId` greater than `_sequentialUpTo()`.
             */
            error SpotMintTokenIdTooSmall();
        
            /**
             * Cannot mint over a token that already exists.
             */
            error TokenAlreadyExists();
        
            /**
             * The feature is not compatible with spot mints.
             */
            error NotCompatibleWithSpotMints();
        
            // =============================================================
            //                            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 erc721a/contracts/ERC721A.sol@v4.3.0
        
        // Original license: SPDX_License_Identifier: MIT
        // ERC721A Contracts v4.3.0
        // Creator: Chiru Labs
        
        pragma solidity ^0.8.4;
        
        /**
         * @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()`.
         *
         * The `_sequentialUpTo()` function can be overriden to enable spot mints
         * (i.e. non-consecutive mints) for `tokenId`s greater than `_sequentialUpTo()`.
         *
         * 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;
        
            // The amount of tokens minted above `_sequentialUpTo()`.
            // We call these spot mints (i.e. non-sequential mints).
            uint256 private _spotMinted;
        
            // =============================================================
            //                          CONSTRUCTOR
            // =============================================================
        
            constructor(string memory name_, string memory symbol_) {
                _name = name_;
                _symbol = symbol_;
                _currentIndex = _startTokenId();
        
                if (_sequentialUpTo() < _startTokenId()) _revert(SequentialUpToTooSmall.selector);
            }
        
            // =============================================================
            //                   TOKEN COUNTING OPERATIONS
            // =============================================================
        
            /**
             * @dev Returns the starting token ID for sequential mints.
             *
             * Override this function to change the starting token ID for sequential mints.
             *
             * Note: The value returned must never change after any tokens have been minted.
             */
            function _startTokenId() internal view virtual returns (uint256) {
                return 0;
            }
        
            /**
             * @dev Returns the maximum token ID (inclusive) for sequential mints.
             *
             * Override this function to return a value less than 2**256 - 1,
             * but greater than `_startTokenId()`, to enable spot (non-sequential) mints.
             *
             * Note: The value returned must never change after any tokens have been minted.
             */
            function _sequentialUpTo() internal view virtual returns (uint256) {
                return type(uint256).max;
            }
        
            /**
             * @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 result) {
                // Counter underflow is impossible as `_burnCounter` cannot be incremented
                // more than `_currentIndex + _spotMinted - _startTokenId()` times.
                unchecked {
                    // With spot minting, the intermediate `result` can be temporarily negative,
                    // and the computation must be unchecked.
                    result = _currentIndex - _burnCounter - _startTokenId();
                    if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
                }
            }
        
            /**
             * @dev Returns the total amount of tokens minted in the contract.
             */
            function _totalMinted() internal view virtual returns (uint256 result) {
                // Counter underflow is impossible as `_currentIndex` does not decrement,
                // and it is initialized to `_startTokenId()`.
                unchecked {
                    result = _currentIndex - _startTokenId();
                    if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
                }
            }
        
            /**
             * @dev Returns the total number of tokens burned.
             */
            function _totalBurned() internal view virtual returns (uint256) {
                return _burnCounter;
            }
        
            /**
             * @dev Returns the total number of tokens that are spot-minted.
             */
            function _totalSpotMinted() internal view virtual returns (uint256) {
                return _spotMinted;
            }
        
            // =============================================================
            //                    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.selector);
                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.selector);
        
                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 Returns whether the ownership slot at `index` is initialized.
             * An uninitialized slot does not necessarily mean that the slot has no owner.
             */
            function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) {
                return _packedOwnerships[index] != 0;
            }
        
            /**
             * @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);
                }
            }
        
            /**
             * @dev Returns the packed ownership data of `tokenId`.
             */
            function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
                if (_startTokenId() <= tokenId) {
                    packed = _packedOwnerships[tokenId];
        
                    if (tokenId > _sequentialUpTo()) {
                        if (_packedOwnershipExists(packed)) return packed;
                        _revert(OwnerQueryForNonexistentToken.selector);
                    }
        
                    // If the data at the starting slot does not exist, start the scan.
                    if (packed == 0) {
                        if (tokenId >= _currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
                        // 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, `tokenId` will not underflow.
                        //
                        // We can directly compare the packed value.
                        // If the address is zero, packed will be zero.
                        for (;;) {
                            unchecked {
                                packed = _packedOwnerships[--tokenId];
                            }
                            if (packed == 0) continue;
                            if (packed & _BITMASK_BURNED == 0) return packed;
                            // Otherwise, the token is burned, and we must revert.
                            // This handles the case of batch burned tokens, where only the burned bit
                            // of the starting slot is set, and remaining slots are left uninitialized.
                            _revert(OwnerQueryForNonexistentToken.selector);
                        }
                    }
                    // Otherwise, the data exists and we can skip the scan.
                    // This is possible because we have already achieved the target condition.
                    // This saves 2143 gas on transfers of initialized tokens.
                    // If the token is not burned, return `packed`. Otherwise, revert.
                    if (packed & _BITMASK_BURNED == 0) return packed;
                }
                _revert(OwnerQueryForNonexistentToken.selector);
            }
        
            /**
             * @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. See {ERC721A-_approve}.
             *
             * Requirements:
             *
             * - The caller must own the token or be an approved operator.
             */
            function approve(address to, uint256 tokenId) public payable virtual override {
                _approve(to, tokenId, true);
            }
        
            /**
             * @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.selector);
        
                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 result) {
                if (_startTokenId() <= tokenId) {
                    if (tokenId > _sequentialUpTo()) return _packedOwnershipExists(_packedOwnerships[tokenId]);
        
                    if (tokenId < _currentIndex) {
                        uint256 packed;
                        while ((packed = _packedOwnerships[tokenId]) == 0) --tokenId;
                        result = packed & _BITMASK_BURNED == 0;
                    }
                }
            }
        
            /**
             * @dev Returns whether `packed` represents a token that exists.
             */
            function _packedOwnershipExists(uint256 packed) private pure returns (bool result) {
                assembly {
                    // The following is equivalent to `owner != address(0) && burned == false`.
                    // Symbolically tested.
                    result := gt(and(packed, _BITMASK_ADDRESS), and(packed, _BITMASK_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);
        
                // Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
                from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
        
                if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);
        
                (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.selector);
        
                _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;
                            }
                        }
                    }
                }
        
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
                assembly {
                    // 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.
                        from, // `from`.
                        toMasked, // `to`.
                        tokenId // `tokenId`.
                    )
                }
                if (toMasked == 0) _revert(TransferToZeroAddress.selector);
        
                _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.selector);
                    }
            }
        
            /**
             * @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.selector);
                    }
                    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.selector);
        
                _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:
                    // - `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)
                    );
        
                    // Updates:
                    // - `balance += quantity`.
                    // - `numberMinted += quantity`.
                    //
                    // We can directly add to the `balance` and `numberMinted`.
                    _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
        
                    // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                    uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
        
                    if (toMasked == 0) _revert(MintToZeroAddress.selector);
        
                    uint256 end = startTokenId + quantity;
                    uint256 tokenId = startTokenId;
        
                    if (end - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
        
                    do {
                        assembly {
                            // 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`.
                                tokenId // `tokenId`.
                            )
                        }
                        // The `!=` check ensures that large values of `quantity`
                        // that overflows uint256 will make the loop run out of gas.
                    } while (++tokenId != end);
        
                    _currentIndex = end;
                }
                _afterTokenTransfers(address(0), to, startTokenId, quantity);
            }
        
            /**
             * @dev Mints `quantity` tokens and transfers them to `to`.
             *
             * This function is intended for efficient minting only during contract creation.
             *
             * It emits only one {ConsecutiveTransfer} as defined in
             * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
             * instead of a sequence of {Transfer} event(s).
             *
             * Calling this function outside of contract creation WILL make your contract
             * non-compliant with the ERC721 standard.
             * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
             * {ConsecutiveTransfer} event is only permissible during contract creation.
             *
             * Requirements:
             *
             * - `to` cannot be the zero address.
             * - `quantity` must be greater than 0.
             *
             * Emits a {ConsecutiveTransfer} event.
             */
            function _mintERC2309(address to, uint256 quantity) internal virtual {
                uint256 startTokenId = _currentIndex;
                if (to == address(0)) _revert(MintToZeroAddress.selector);
                if (quantity == 0) _revert(MintZeroQuantity.selector);
                if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);
        
                _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)
                    );
        
                    if (startTokenId + quantity - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
        
                    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.selector);
                            }
                        } while (index < end);
                        // This prevents reentrancy to `_safeMint`.
                        // It does not prevent reentrancy to `_safeMintSpot`.
                        if (_currentIndex != end) revert();
                    }
                }
            }
        
            /**
             * @dev Equivalent to `_safeMint(to, quantity, '')`.
             */
            function _safeMint(address to, uint256 quantity) internal virtual {
                _safeMint(to, quantity, '');
            }
        
            /**
             * @dev Mints a single token at `tokenId`.
             *
             * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
             *
             * Requirements:
             *
             * - `to` cannot be the zero address.
             * - `tokenId` must be greater than `_sequentialUpTo()`.
             * - `tokenId` must not exist.
             *
             * Emits a {Transfer} event for each mint.
             */
            function _mintSpot(address to, uint256 tokenId) internal virtual {
                if (tokenId <= _sequentialUpTo()) _revert(SpotMintTokenIdTooSmall.selector);
                uint256 prevOwnershipPacked = _packedOwnerships[tokenId];
                if (_packedOwnershipExists(prevOwnershipPacked)) _revert(TokenAlreadyExists.selector);
        
                _beforeTokenTransfers(address(0), to, tokenId, 1);
        
                // Overflows are incredibly unrealistic.
                // The `numberMinted` for `to` is incremented by 1, and has a max limit of 2**64 - 1.
                // `_spotMinted` is incremented by 1, and has a max limit of 2**256 - 1.
                unchecked {
                    // Updates:
                    // - `address` to the owner.
                    // - `startTimestamp` to the timestamp of minting.
                    // - `burned` to `false`.
                    // - `nextInitialized` to `true` (as `quantity == 1`).
                    _packedOwnerships[tokenId] = _packOwnershipData(
                        to,
                        _nextInitializedFlag(1) | _nextExtraData(address(0), to, prevOwnershipPacked)
                    );
        
                    // Updates:
                    // - `balance += 1`.
                    // - `numberMinted += 1`.
                    //
                    // We can directly add to the `balance` and `numberMinted`.
                    _packedAddressData[to] += (1 << _BITPOS_NUMBER_MINTED) | 1;
        
                    // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                    uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
        
                    if (toMasked == 0) _revert(MintToZeroAddress.selector);
        
                    assembly {
                        // 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`.
                            tokenId // `tokenId`.
                        )
                    }
        
                    ++_spotMinted;
                }
        
                _afterTokenTransfers(address(0), to, tokenId, 1);
            }
        
            /**
             * @dev Safely mints a single token at `tokenId`.
             *
             * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
             *
             * Requirements:
             *
             * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}.
             * - `tokenId` must be greater than `_sequentialUpTo()`.
             * - `tokenId` must not exist.
             *
             * See {_mintSpot}.
             *
             * Emits a {Transfer} event.
             */
            function _safeMintSpot(
                address to,
                uint256 tokenId,
                bytes memory _data
            ) internal virtual {
                _mintSpot(to, tokenId);
        
                unchecked {
                    if (to.code.length != 0) {
                        uint256 currentSpotMinted = _spotMinted;
                        if (!_checkContractOnERC721Received(address(0), to, tokenId, _data)) {
                            _revert(TransferToNonERC721ReceiverImplementer.selector);
                        }
                        // This prevents reentrancy to `_safeMintSpot`.
                        // It does not prevent reentrancy to `_safeMint`.
                        if (_spotMinted != currentSpotMinted) revert();
                    }
                }
            }
        
            /**
             * @dev Equivalent to `_safeMintSpot(to, tokenId, '')`.
             */
            function _safeMintSpot(address to, uint256 tokenId) internal virtual {
                _safeMintSpot(to, tokenId, '');
            }
        
            // =============================================================
            //                       APPROVAL OPERATIONS
            // =============================================================
        
            /**
             * @dev Equivalent to `_approve(to, tokenId, false)`.
             */
            function _approve(address to, uint256 tokenId) internal virtual {
                _approve(to, tokenId, false);
            }
        
            /**
             * @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:
             *
             * - `tokenId` must exist.
             *
             * Emits an {Approval} event.
             */
            function _approve(
                address to,
                uint256 tokenId,
                bool approvalCheck
            ) internal virtual {
                address owner = ownerOf(tokenId);
        
                if (approvalCheck && _msgSenderERC721A() != owner)
                    if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                        _revert(ApprovalCallerNotOwnerNorApproved.selector);
                    }
        
                _tokenApprovals[tokenId].value = to;
                emit Approval(owner, to, tokenId);
            }
        
            // =============================================================
            //                        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.selector);
                }
        
                _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 + _spotMinted` 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.selector);
                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)
                }
            }
        
            /**
             * @dev For more efficient reverts.
             */
            function _revert(bytes4 errorSelector) internal pure {
                assembly {
                    mstore(0x00, errorSelector)
                    revert(0x00, 0x04)
                }
            }
        }
        
        
        // File contracts/main-contracts/MegaRabbit.sol
        
        // Original license: SPDX_License_Identifier: UNLICENSED
        
        pragma solidity ^0.8.24;
        
        
        
        
        error MintInactive();
        error Unauthorized(address caller);
        error InvalidOperation(string reason);
        error ExceedsMaxSupply(uint256 requested, uint256 available);
        error InsufficientEther(uint256 required, uint256 provided);
        error ExceedsMaxPerWallet(uint256 requested, uint256 allowed);
        error ExceedsMintQuota(uint256 requested, uint256 allowed);
        
        error ExceedsMaxMintGroupSupply(uint256 requested, uint256 available); // Remove when allowlist is off
        error MintGroupInactive(uint256 mintId); // Remove when allowlist is off
        error NotInPresale(address caller, uint256 mintId); // Remove when allowlist is off
        error MintGroupDoesNotExist(uint256 mintId); // Remove when allowlist is off
        error ArrayLengthMismatch(); // Remove when allowlist is off
        
        error InvalidKingdomlyFeeContract();
        
        contract MegaRabbit is ERC721A, ERC2981, Ownable {
            event BatchMetadataUpdate(
                uint256 indexed fromTokenId,
                uint256 indexed toTokenId
            );
            event TokensMinted(
                address indexed recipient,
                uint256 amount,
                uint256 mintId
            );
        
            event TokensDelegateMinted(
                address indexed vault,
                address indexed hotWallet,
                uint256 amount,
                uint256 mintId
            );
        
            event SalePriceChanged(uint256 indexed mintId, uint256 newPrice);
        
            event MaxMintPerWalletChanged(
                uint256 newMaxMintPerWallet,
                uint256 mintGroupId
            );
        
            event PreSaleMintStatusChanged(bool status, uint256 mintGroupId);
        
            event PreSaleMintScheduledStartTimestampChanged(
                uint256 timestamp,
                uint256 mintGroupId
            );
        
            event KingdomlyFeeContractChanged(address feeContractAddress);
        
            struct BaseVariables {
                string name;
                string symbol;
                address ownerPayoutAddress;
                string initialBaseURI;
                uint256 maxSupply;
            }
        
            //Base variables
            mapping(address => uint256) private pendingBalances;
            uint256 public maxSupply;
            uint256 public immutable threeDollarsInCents;
            bool public contractMintLive = false;
            uint256 public scheduledMintLiveTimestamp = 0;
            string public baseURI;
            address public feeAddress;
            address public ownerPayoutAddress;
        
            address public kingdomlyAdmin;
            KingdomlyFeeContract public kingdomlyFeeContract;
        
            //Map pairings.
            mapping(uint256 => uint256) public maxMintPerWallet;
            mapping(uint256 => uint256) public mintPrice;
            mapping(uint256 => uint256) public maxSupplyPerMintGroup;
            mapping(uint256 => uint256) public mintGroupMints;
            mapping(address => mapping(uint256 => uint256)) private addressMints; // Added address mints for mint group cap
            mapping(uint256 => mapping(address => uint256)) public mintQuotas; // Changed presale checker to mintQuotas for individual addresses
            mapping(uint256 => bool) public contractPresaleActive;
            mapping(uint256 => uint256) public presaleScheduledStartTimestamp;
        
            uint256[] public activeMintGroups; //Array to get all active mint groups. Remove when allowlist is off
        
            constructor(
                //Base variables
                BaseVariables memory _baseVariables,
                //Allowlist variables
                uint256[] memory _maxMintPerWallet, // Turn into uint256 if allowlist is off
                uint256[] memory _maxSupplyPerMintGroup, // Remove if allowlist is off
                uint256[] memory _mintPrice, // Turn into uint256 if allowlist is off
                //Royalties variables
                uint96 _royaltyPercentage, // Remove if royalties is off
                address _kingdomlyAdmin,
                KingdomlyFeeContract _kingdomlyFeeContract
            ) ERC721A(_baseVariables.name, _baseVariables.symbol) Ownable(msg.sender) {
                //Error handler to check if map pairs each other. Remove if allowlist is off
                if (
                    _maxMintPerWallet.length != _maxSupplyPerMintGroup.length &&
                    _maxMintPerWallet.length != _mintPrice.length
                ) {
                    revert ArrayLengthMismatch();
                }
        
                //Remove if allowlist is off
                uint256 totalMaxSupplyPerMintGroup = 0;
                for (uint256 i = 0; i < _maxSupplyPerMintGroup.length; i++) {
                    totalMaxSupplyPerMintGroup += _maxSupplyPerMintGroup[i];
                    maxSupplyPerMintGroup[i] = _maxSupplyPerMintGroup[i];
                    maxMintPerWallet[i] = _maxMintPerWallet[i];
                    mintPrice[i] = _mintPrice[i];
                    mintGroupMints[i] = 0;
                    activeMintGroups.push(i);
                }
        
                //Checker if max supply per mint group exceeds total max supply. Remove if allowlist is off
                if (totalMaxSupplyPerMintGroup > _baseVariables.maxSupply) {
                    revert InvalidOperation({
                        reason: "Max supply per mint group exceeds total max supply"
                    });
                }
        
                //Base variables
                maxSupply = _baseVariables.maxSupply;
                baseURI = _baseVariables.initialBaseURI;
                ownerPayoutAddress = _baseVariables.ownerPayoutAddress;
                feeAddress = 0x10317Fa93da2a2e6d7B8e29D2BC2e6B95f2ECc84;
        
                // Setting up royalties and affiliate percentage
                _setDefaultRoyalty(
                    _baseVariables.ownerPayoutAddress,
                    _royaltyPercentage
                );
        
                // FEE Variables
                kingdomlyAdmin = _kingdomlyAdmin;
        
                kingdomlyFeeContract = _kingdomlyFeeContract;
                threeDollarsInCents = 300; // $3 * 100 = 300
            }
        
            // ###################### Modifiers ######################
        
            /**
             * @dev Ensures the caller is the Kingdomly Admin.
             */
            modifier isKingdomlyAdmin() {
                if (msg.sender != kingdomlyAdmin) {
                    revert Unauthorized(msg.sender);
                }
        
                _;
            }
        
            //===================================START Allowlist Functions===================================//
            // Initializer for new mint groups for all maps
            function initializeNewMintGroup(uint256 mintId) internal {
                mintPrice[mintId] = 0;
                maxMintPerWallet[mintId] = 0;
                maxSupplyPerMintGroup[mintId] = 0;
                mintGroupMints[mintId] = 0;
                activeMintGroups.push(mintId);
            }
        
            function isMintGroupActive(uint256 mintId) private view returns (bool) {
                for (uint256 i = 0; i < activeMintGroups.length; i++) {
                    if (activeMintGroups[i] == mintId) {
                        return true;
                    }
                }
                return false;
            }
        
            function mintLive() public view returns (bool) {
                if (!contractMintLive) {
                    if (
                        scheduledMintLiveTimestamp == 0 ||
                        block.timestamp <= scheduledMintLiveTimestamp
                    ) {
                        return false;
                    }
                }
        
                return true;
            }
        
            // Changes the max mint per mint group. Only the contract owner can call this function. Remove this function if allowlist is off
            function setNewMaxPerMintGroup(
                uint256 mintId,
                uint256 newMax
            ) public onlyOwner {
                //Checks if mintId already exists inside activeMintGroups. This allows the contract to adjust the mappings for new mint groups
                if (!isMintGroupActive(mintId)) {
                    initializeNewMintGroup(mintId);
                }
        
                // Checker if new max exceeds total supply
                uint256 totalMaxMintPerMG = 0;
                for (uint256 i = 0; i < activeMintGroups.length; i++) {
                    if (activeMintGroups[i] == mintId) {
                        totalMaxMintPerMG += newMax; // Use the new max for the specified mintId
                    } else {
                        totalMaxMintPerMG += maxSupplyPerMintGroup[activeMintGroups[i]];
                    }
                }
        
                if (totalMaxMintPerMG > maxSupply) {
                    revert InvalidOperation({
                        reason: "New supply per mint group exceeds total supply."
                    });
                }
        
                maxSupplyPerMintGroup[mintId] = newMax;
            }
        
            // Changed add to presale to set mint quota for individual addresses.
            function setMintQuota(
                address[] memory addressToAdd,
                uint256 mintId,
                uint256[] memory _mintQuotas
            ) external onlyOwner {
                //Checks if mintId already exists inside activeMintGroups. This allows the contract to adjust the mappings for new mint groups
                if (!isMintGroupActive(mintId)) {
                    initializeNewMintGroup(mintId);
                }
        
                for (uint256 i = 0; i < addressToAdd.length; i++) {
                    mintQuotas[mintId][addressToAdd[i]] = _mintQuotas[i];
                }
            }
        
            // Control the presale status
            function stopOrStartpresaleMint(
                bool presaleStatus,
                uint256 mintId
            ) public onlyOwner {
                //Checks if mintId already exists inside activeMintGroups.
                if (!isMintGroupActive(mintId)) {
                    revert MintGroupDoesNotExist({mintId: mintId});
                }
                contractPresaleActive[mintId] = presaleStatus;
        
                if (presaleStatus == false) {
                    presaleScheduledStartTimestamp[mintId] = 0;
                }
        
                emit PreSaleMintStatusChanged(presaleStatus, mintId);
            }
        
            function schedulePresaleMintStart(
                uint256 startTimestamp,
                uint256 mintId
            ) public onlyOwner {
                if (!isMintGroupActive(mintId)) {
                    revert MintGroupDoesNotExist({mintId: mintId});
                }
                presaleScheduledStartTimestamp[mintId] = startTimestamp;
        
                emit PreSaleMintScheduledStartTimestampChanged(startTimestamp, mintId);
            }
        
            //Checker whether presale is already active both on timestmap and the mapping
            function presaleActive(uint256 mintId) public view returns (bool) {
                if (!contractPresaleActive[mintId]) {
                    if (
                        presaleScheduledStartTimestamp[mintId] == 0 ||
                        block.timestamp <= presaleScheduledStartTimestamp[mintId]
                    ) {
                        return false;
                    }
                }
        
                return true;
            }
        
            //===================================END Allowlist Functions===================================//
            // Sets the maximum number of tokens that can be minted in a batch. Only the contract owner can call this function.
            function setMaxMintPerWallet(
                uint256 newMaxMintPerWallet,
                uint256 mintGroupId
            ) public onlyOwner {
                maxMintPerWallet[mintGroupId] = newMaxMintPerWallet;
        
                emit MaxMintPerWalletChanged(newMaxMintPerWallet, mintGroupId);
            }
        
            // Changes the price to mint a token. Only the contract owner can call this function.
            function changeSalePrice(
                uint256 newmintPrice,
                uint256 mintId
            ) public onlyOwner {
                //Checks if mintId already exists inside activeMintGroups. This allows the contract to adjust the mappings for new mint groups
                if (!isMintGroupActive(mintId)) {
                    initializeNewMintGroup(mintId);
                }
                mintPrice[mintId] = newmintPrice;
                emit SalePriceChanged(mintId, newmintPrice);
            }
        
            //===================================START Airdrop Functions===================================//
            // Modified airdrop function to charge the owner threeDollarsEth per mint
            function airdropNFTs(
                address[] memory recipients,
                uint256[] memory amounts
            ) external payable onlyOwner returns (uint256 totalCharge) {
                if (recipients.length != amounts.length) {
                    revert InvalidOperation({
                        reason: "Mismatch between recipients and amounts"
                    });
                }
        
                uint256 totalNFTToMint = 0;
                for (uint256 i = 0; i < amounts.length; i++) {
                    totalNFTToMint += amounts[i];
                }
        
                totalCharge = quoteAirdropFees(totalNFTToMint);
        
                if (msg.value < totalCharge) {
                    revert InvalidOperation({
                        reason: "Not enough Ether sent for the airdrop charge"
                    });
                }
        
                pendingBalances[feeAddress] += totalCharge; // Fee goes to the fee address
        
                for (uint256 j = 0; j < recipients.length; j++) {
                    uint256 amount = amounts[j];
                    if (totalSupply() + amount > maxSupply) {
                        revert InvalidOperation({reason: "Airdrop exceeds max supply"});
                    }
        
                    _safeMint(recipients[j], amount); // Mint NFTs to recipients
                }
        
                _refundExcessEther(totalCharge);
            }
        
            //===================================END Airdrop Functions===================================//
        
            //===================================START Mint Functions===================================//
        
            function canMintCheck(
                uint256 amount,
                uint256 mintId,
                address minterAddress
            ) public view returns (bool, string memory) {
                if (
                    amount + addressMints[minterAddress][mintId] >
                    maxMintPerWallet[mintId]
                ) {
                    return (false, "ExceedsMaxPerWallet");
                }
        
                if (amount == 0) {
                    return (false, "InvalidOperation");
                }
        
                // Pre-conditions checks
                if (!mintLive()) {
                    return (false, "MintInactive");
                }
        
                if (!contractPresaleActive[mintId]) {
                    if (
                        presaleScheduledStartTimestamp[mintId] == 0 ||
                        block.timestamp <= presaleScheduledStartTimestamp[mintId]
                    ) {
                        return (false, "MintGroupInactive");
                    }
                }
        
                if (mintId != 0) {
                    if (mintQuotas[mintId][minterAddress] == 0) {
                        return (false, "NotInPresale");
                    }
        
                    if (amount > mintQuotas[mintId][minterAddress]) {
                        return (false, "ExceedsMintQuota");
                    }
                }
        
                if (mintGroupMints[mintId] + amount > maxSupplyPerMintGroup[mintId]) {
                    return (false, "ExceedsMaxMintGroupSupply");
                }
        
                if (totalSupply() + amount > maxSupply) {
                    return (false, "ExceedsMaxSupply");
                }
        
                return (true, "");
            }
        
            // Cleaner and more efficient batchMint function
            function batchMint(
                uint256 amount,
                uint256 mintId // Remove if allowlist is off
            ) external payable returns (uint256 totalCostWithFee) {
                // Checker for connected wallet
                if (
                    amount + addressMints[msg.sender][mintId] > maxMintPerWallet[mintId]
                ) {
                    revert ExceedsMaxPerWallet({
                        requested: amount,
                        allowed: maxMintPerWallet[mintId] -
                            addressMints[msg.sender][mintId]
                    });
                }
        
                addressMints[msg.sender][mintId] += amount;
        
                // NOTE: Checks and Effects should always be before (avoid reentrancy!)
                totalCostWithFee = _batchMint(msg.sender, amount, mintId);
                emit TokensMinted(msg.sender, amount, mintId);
        
                _refundExcessEther(totalCostWithFee);
            }
        
            //==================START Delegate Functions==================//
            address constant DELEGATE_REGISTRY =
                0x00000000000000447e69651d841bD8D104Bed493;
        
            function canDelegateMintCheck(
                uint256 amount,
                uint256 mintId,
                address vault,
                address minterAddress
            ) public view returns (bool, string memory) {
                if (
                    !IDelegateRegistry(DELEGATE_REGISTRY).checkDelegateForContract(
                        minterAddress,
                        vault,
                        address(this),
                        ""
                    )
                ) {
                    return (false, "Unauthorized");
                }
        
                if (amount + addressMints[vault][mintId] > maxMintPerWallet[mintId]) {
                    return (false, "ExceedsMaxPerWallet");
                }
        
                if (amount == 0) {
                    return (false, "InvalidOperation");
                }
        
                // Pre-conditions checks
                if (!mintLive()) {
                    return (false, "MintInactive");
                }
        
                if (!contractPresaleActive[mintId]) {
                    if (
                        presaleScheduledStartTimestamp[mintId] == 0 ||
                        block.timestamp <= presaleScheduledStartTimestamp[mintId]
                    ) {
                        return (false, "MintGroupInactive");
                    }
                }
        
                if (mintId != 0) {
                    if (mintQuotas[mintId][vault] == 0) {
                        return (false, "NotInPresale");
                    }
        
                    if (amount > mintQuotas[mintId][vault]) {
                        return (false, "ExceedsMintQuota");
                    }
                }
        
                if (mintGroupMints[mintId] + amount > maxSupplyPerMintGroup[mintId]) {
                    return (false, "ExceedsMaxMintGroupSupply");
                }
        
                if (totalSupply() + amount > maxSupply) {
                    return (false, "ExceedsMaxSupply");
                }
        
                return (true, "");
            }
        
            function delegatedMint(
                uint256 amount,
                uint256 mintId,
                address vault
            ) external payable returns (uint256 totalCostWithFee) {
                if (
                    !IDelegateRegistry(DELEGATE_REGISTRY).checkDelegateForContract(
                        msg.sender,
                        vault,
                        address(this),
                        ""
                    )
                ) {
                    revert Unauthorized(vault);
                }
        
                // Checker for vault wallet
                if (amount + addressMints[vault][mintId] > maxMintPerWallet[mintId]) {
                    revert ExceedsMaxPerWallet({
                        requested: amount,
                        allowed: maxMintPerWallet[mintId] - addressMints[vault][mintId]
                    });
                }
        
                addressMints[vault][mintId] += amount;
        
                // NOTE: Checks and Effects should always be before (avoid reentrancy!)
                totalCostWithFee = _batchMint(vault, amount, mintId);
                emit TokensDelegateMinted(vault, msg.sender, amount, mintId);
        
                _refundExcessEther(totalCostWithFee);
            }
        
            //==================START Internal Mint Functions==================//
            function _batchMint(
                address delegatedCaller,
                uint256 amount,
                uint256 mintId
            ) internal returns (uint256) {
                if (amount == 0) {
                    revert InvalidOperation({reason: "Amount must be greater than 0"});
                }
        
                // Pre-conditions checks
                if (!mintLive()) {
                    revert MintInactive();
                }
        
                if (!contractPresaleActive[mintId]) {
                    if (
                        presaleScheduledStartTimestamp[mintId] == 0 ||
                        block.timestamp <= presaleScheduledStartTimestamp[mintId]
                    ) {
                        revert MintGroupInactive({mintId: mintId});
                    }
                }
        
                if (mintId != 0) {
                    if (mintQuotas[mintId][delegatedCaller] == 0) {
                        revert NotInPresale({caller: delegatedCaller, mintId: mintId});
                    }
        
                    if (amount > mintQuotas[mintId][delegatedCaller]) {
                        revert ExceedsMintQuota({
                            requested: amount,
                            allowed: mintQuotas[mintId][delegatedCaller]
                        });
                    }
                    mintQuotas[mintId][delegatedCaller] -= amount;
                }
        
                if (mintGroupMints[mintId] + amount > maxSupplyPerMintGroup[mintId]) {
                    revert ExceedsMaxMintGroupSupply({
                        requested: amount,
                        available: maxSupplyPerMintGroup[mintId] -
                            mintGroupMints[mintId]
                    });
                }
        
                if (totalSupply() + amount > maxSupply) {
                    revert ExceedsMaxSupply({
                        requested: amount,
                        available: maxSupply - totalSupply()
                    });
                }
        
                // Calculate fees, check if we have enough msg.value
                (
                    uint256 totalCostWithFee,
                    uint256 mintingCost,
                    uint256 minterFee,
                    uint256 creatorFee
                ) = quoteBatchMint(mintId, amount);
        
                if (msg.value < totalCostWithFee) {
                    revert InsufficientEther({
                        required: totalCostWithFee,
                        provided: msg.value
                    });
                }
        
                uint256 totalFee = minterFee + creatorFee;
        
                // Update balances
                pendingBalances[feeAddress] += totalFee;
                pendingBalances[ownerPayoutAddress] += mintingCost - creatorFee;
        
                // Finalize minting
                mintGroupMints[mintId] += amount;
                _safeMint(msg.sender, amount);
        
                return totalCostWithFee;
            }
        
            // @notice Quote the total cost of minting a batch of tokens
            // @dev This is the same price for both the owner and the delegate
            // @param mintId The mint group ID
            // @param amount The number of tokens to mint
            // @return totalCostWithFee The total cost of minting the batch, including the fee
            // @return feeAmount The fee amount only for minting the batch
            function quoteBatchMint(
                uint256 mintId,
                uint256 amount
            )
                public
                view
                returns (
                    uint256 totalCostWithFee,
                    uint256 mintingCost,
                    uint256 minterFee,
                    uint256 creatorFee
                )
            {
                mintingCost = mintPrice[mintId] * amount;
                minterFee = threeDollarsEth() * amount; // $3 fee
                creatorFee = (mintingCost * 3) / 100; // 3% fee from creators
                totalCostWithFee = mintingCost + minterFee;
            }
        
            // @notice Quote the total cost of airdropping a batch of tokens
            // @param amount The number of tokens to mint
            // @return totalAirdropCostWithFee The total cost of minting the batch ($0.33 per nft)
            function quoteAirdropFees(
                uint256 amount
            ) public view returns (uint256 totalAirdropCostWithFee) {
                totalAirdropCostWithFee = (threeDollarsEth() * amount * 11) / 100; // UPDATE: Changed airdrop fees to be $0.33 instead.
            }
        
            //==================END Delegate Functions==================//
        
            //===================================END Mint Functions===================================//
            //===================================START Base Functions===================================//
        
            // Changes the minting status. Only the contract owner can call this function.
            function changeMintStatus(bool status) public onlyOwner {
                if (contractMintLive == status) {
                    revert InvalidOperation({
                        reason: "Mint status is already the one you entered"
                    });
                }
        
                if (status == false) {
                    scheduledMintLiveTimestamp = 0;
                }
        
                contractMintLive = status;
            }
        
            function setMintLiveTimestamp(uint256 timestamp) public onlyOwner {
                scheduledMintLiveTimestamp = timestamp;
            }
        
            // Sets the base URI for the token metadata. Only the contract owner can call this function.
            function setBaseURI(string memory newBaseURI) public onlyOwner {
                baseURI = newBaseURI;
                emit BatchMetadataUpdate(1, type(uint256).max); // Signal that all token metadata has been updated
            }
        
            function _withdrawFor(address user) internal returns (uint256 payout) {
                payout = pendingBalances[user];
                pendingBalances[user] = 0;
                (bool success, ) = payable(user).call{value: payout}("");
                if (!success) {
                    revert InvalidOperation({reason: "Withdraw Transfer Failed"});
                }
            }
        
            // Allows the contract owner to withdraw the funds that have been paid into the contract.
            function withdrawMintFunds() public {
                _withdrawFor(ownerPayoutAddress);
                _withdrawFor(feeAddress);
            }
        
            // Allows the fee address to withdraw their portion of the funds.
            function withdrawFeeFunds() public {
                _withdrawFor(feeAddress);
            }
        
            // Internal function to refund excess Ether sent in a transaction
            function _refundExcessEther(uint256 totalCharge) internal {
                uint256 excess = msg.value - totalCharge;
                if (excess > 0) {
                    (bool success, ) = payable(msg.sender).call{value: excess}("");
                    if (!success) {
                        pendingBalances[msg.sender] += excess;
                    }
                }
            }
        
            // Returns the base URI for the token metadata.
            function _baseURI() internal view override returns (string memory) {
                return baseURI;
            }
        
            // Checks the balance pending withdrawal for the sender.
            function checkPendingBalance() public view returns (uint256) {
                return pendingBalances[msg.sender];
            }
        
            function checkPendingBalanceFor(
                address user
            ) public view returns (uint256) {
                return pendingBalances[user];
            }
        
            // Overrides the start token ID function from the ERC721A contract.
            function _startTokenId() internal view virtual override returns (uint256) {
                return 1;
            }
        
            function supportsInterface(
                bytes4 interfaceId
            ) public view override(ERC721A, ERC2981) returns (bool) {
                return
                    ERC721A.supportsInterface(interfaceId) ||
                    ERC2981.supportsInterface(interfaceId);
            }
            //===================================END Base Functions===================================//
        
            // ###################### Kingdomly Admin Functions ######################
        
            function setNewKingdomlyFeeContract(
                KingdomlyFeeContract _kingdomlyFeeContract
            ) external isKingdomlyAdmin {
                if (address(_kingdomlyFeeContract) == address(0)) {
                    revert InvalidKingdomlyFeeContract();
                }
        
                kingdomlyFeeContract = _kingdomlyFeeContract;
        
                emit KingdomlyFeeContractChanged(address(_kingdomlyFeeContract));
            }
        
            function getKingdomlyFeeContract()
                external
                view
                returns (KingdomlyFeeContract)
            {
                return kingdomlyFeeContract;
            }
        
            // ###################### Fee Functions ######################
        
            function getOneDollarInWei() internal view returns (uint256) {
                return kingdomlyFeeContract.getOneDollarInWei();
            }
        
            function threeDollarsEth() public view returns (uint256) {
                return (getOneDollarInWei() * threeDollarsInCents) / 100;
            }
        }

        File 2 of 2: ERC1967Proxy
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        
        /**
         * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
         * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
         * be specified by overriding the virtual {_implementation} function.
         *
         * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
         * different contract through the {_delegate} function.
         *
         * The success and return data of the delegated call will be returned back to the caller of the proxy.
         */
        abstract contract Proxy {
            /**
             * @dev Delegates the current call to `implementation`.
             *
             * This function does not return to its internall call site, it will return directly to the external caller.
             */
            function _delegate(address implementation) internal virtual {
                // solhint-disable-next-line no-inline-assembly
                assembly {
                // Copy msg.data. We take full control of memory in this inline assembly
                // block because it will not return to Solidity code. We overwrite the
                // Solidity scratch pad at memory position 0.
                    calldatacopy(0, 0, calldatasize())
        
                // Call the implementation.
                // out and outsize are 0 because we don't know the size yet.
                    let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
        
                // Copy the returned data.
                    returndatacopy(0, 0, returndatasize())
        
                    switch result
                    // delegatecall returns 0 on error.
                    case 0 { revert(0, returndatasize()) }
                    default { return(0, returndatasize()) }
                }
            }
        
            /**
             * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
             * and {_fallback} should delegate.
             */
            function _implementation() internal view virtual returns (address);
        
            /**
             * @dev Delegates the current call to the address returned by `_implementation()`.
             *
             * This function does not return to its internall call site, it will return directly to the external caller.
             */
            function _fallback() internal virtual {
                _beforeFallback();
                _delegate(_implementation());
            }
        
            /**
             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
             * function in the contract matches the call data.
             */
            fallback () external payable virtual {
                _fallback();
            }
        
            /**
             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
             * is empty.
             */
            receive () external payable virtual {
                _fallback();
            }
        
            /**
             * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
             * call, or as part of the Solidity `fallback` or `receive` functions.
             *
             * If overriden should call `super._beforeFallback()`.
             */
            function _beforeFallback() internal virtual {
            }
        }
        
        
        /**
         * @dev This abstract contract provides getters and event emitting update functions for
         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
         *
         * _Available since v4.1._
         *
         */
        abstract contract ERC1967Upgrade {
            // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
            bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
        
            /**
             * @dev Storage slot with the address of the current implementation.
             * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
             * validated in the constructor.
             */
            bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        
            /**
             * @dev Emitted when the implementation is upgraded.
             */
            event Upgraded(address indexed implementation);
        
            /**
             * @dev Returns the current implementation address.
             */
            function _getImplementation() internal view returns (address) {
                return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
            }
        
            /**
             * @dev Stores a new address in the EIP1967 implementation slot.
             */
            function _setImplementation(address newImplementation) private {
                require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
            }
        
            /**
             * @dev Perform implementation upgrade
             *
             * Emits an {Upgraded} event.
             */
            function _upgradeTo(address newImplementation) internal {
                _setImplementation(newImplementation);
                emit Upgraded(newImplementation);
            }
        
            /**
             * @dev Perform implementation upgrade with additional setup call.
             *
             * Emits an {Upgraded} event.
             */
            function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                _setImplementation(newImplementation);
                emit Upgraded(newImplementation);
                if (data.length > 0 || forceCall) {
                    Address.functionDelegateCall(newImplementation, data);
                }
            }
        
            /**
             * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
             *
             * Emits an {Upgraded} event.
             */
            function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                address oldImplementation = _getImplementation();
        
                // Initial upgrade and setup call
                _setImplementation(newImplementation);
                if (data.length > 0 || forceCall) {
                    Address.functionDelegateCall(newImplementation, data);
                }
        
                // Perform rollback test if not already in progress
                StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                if (!rollbackTesting.value) {
                    // Trigger rollback using upgradeTo from the new implementation
                    rollbackTesting.value = true;
                    Address.functionDelegateCall(
                        newImplementation,
                        abi.encodeWithSignature(
                            "upgradeTo(address)",
                            oldImplementation
                        )
                    );
                    rollbackTesting.value = false;
                    // Check rollback was effective
                    require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                    // Finally reset to the new implementation and log the upgrade
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                }
            }
        
            /**
             * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
             * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
             *
             * Emits a {BeaconUpgraded} event.
             */
            function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                _setBeacon(newBeacon);
                emit BeaconUpgraded(newBeacon);
                if (data.length > 0 || forceCall) {
                    Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                }
            }
        
            /**
             * @dev Storage slot with the admin of the contract.
             * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
             * validated in the constructor.
             */
            bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        
            /**
             * @dev Emitted when the admin account has changed.
             */
            event AdminChanged(address previousAdmin, address newAdmin);
        
            /**
             * @dev Returns the current admin.
             */
            function _getAdmin() internal view returns (address) {
                return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
            }
        
            /**
             * @dev Stores a new address in the EIP1967 admin slot.
             */
            function _setAdmin(address newAdmin) private {
                require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
            }
        
            /**
             * @dev Changes the admin of the proxy.
             *
             * Emits an {AdminChanged} event.
             */
            function _changeAdmin(address newAdmin) internal {
                emit AdminChanged(_getAdmin(), newAdmin);
                _setAdmin(newAdmin);
            }
        
            /**
             * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
             * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
             */
            bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
        
            /**
             * @dev Emitted when the beacon is upgraded.
             */
            event BeaconUpgraded(address indexed beacon);
        
            /**
             * @dev Returns the current beacon.
             */
            function _getBeacon() internal view returns (address) {
                return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
            }
        
            /**
             * @dev Stores a new beacon in the EIP1967 beacon slot.
             */
            function _setBeacon(address newBeacon) private {
                require(
                    Address.isContract(newBeacon),
                    "ERC1967: new beacon is not a contract"
                );
                require(
                    Address.isContract(IBeacon(newBeacon).implementation()),
                    "ERC1967: beacon implementation is not a contract"
                );
                StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
            }
        }
        
        /**
         * @dev This is the interface that {BeaconProxy} expects of its beacon.
         */
        interface IBeacon {
            /**
             * @dev Must return an address that can be used as a delegate call target.
             *
             * {BeaconProxy} will check that this address is a contract.
             */
            function implementation() external view returns (address);
        }
        
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
        
                uint256 size;
                // solhint-disable-next-line no-inline-assembly
                assembly { size := extcodesize(account) }
                return size > 0;
            }
        
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCall(target, data, "Address: low-level call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                require(isContract(target), "Address: call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.staticcall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionDelegateCall(target, data, "Address: low-level delegate call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                require(isContract(target), "Address: delegate call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
        
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        /**
         * @dev Library for reading and writing primitive types to specific storage slots.
         *
         * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
         * This library helps with reading and writing to such slots without the need for inline assembly.
         *
         * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
         *
         * Example usage to set ERC1967 implementation slot:
         * ```
         * contract ERC1967 {
         *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
         *
         *     function _getImplementation() internal view returns (address) {
         *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
         *     }
         *
         *     function _setImplementation(address newImplementation) internal {
         *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
         *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
         *     }
         * }
         * ```
         *
         * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
         */
        library StorageSlot {
            struct AddressSlot {
                address value;
            }
        
            struct BooleanSlot {
                bool value;
            }
        
            struct Bytes32Slot {
                bytes32 value;
            }
        
            struct Uint256Slot {
                uint256 value;
            }
        
            /**
             * @dev Returns an `AddressSlot` with member `value` located at `slot`.
             */
            function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                assembly {
                    r.slot := slot
                }
            }
        
            /**
             * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
             */
            function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                assembly {
                    r.slot := slot
                }
            }
        
            /**
             * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
             */
            function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                assembly {
                    r.slot := slot
                }
            }
        
            /**
             * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
             */
            function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                assembly {
                    r.slot := slot
                }
            }
        }
        
        /*
         * @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) {
                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                return msg.data;
            }
        }
        
        /**
         * @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 () {
                address msgSender = _msgSender();
                _owner = msgSender;
                emit OwnershipTransferred(address(0), msgSender);
            }
        
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view virtual returns (address) {
                return _owner;
            }
        
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                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 {
                emit OwnershipTransferred(_owner, address(0));
                _owner = 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");
                emit OwnershipTransferred(_owner, newOwner);
                _owner = newOwner;
            }
        }
        
        /**
         * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
         * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
         */
        contract ProxyAdmin is Ownable {
        
            /**
             * @dev Returns the current implementation of `proxy`.
             *
             * Requirements:
             *
             * - This contract must be the admin of `proxy`.
             */
            function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                // We need to manually run the static call since the getter cannot be flagged as view
                // bytes4(keccak256("implementation()")) == 0x5c60da1b
                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                require(success);
                return abi.decode(returndata, (address));
            }
        
            /**
             * @dev Returns the current admin of `proxy`.
             *
             * Requirements:
             *
             * - This contract must be the admin of `proxy`.
             */
            function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                // We need to manually run the static call since the getter cannot be flagged as view
                // bytes4(keccak256("admin()")) == 0xf851a440
                (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                require(success);
                return abi.decode(returndata, (address));
            }
        
            /**
             * @dev Changes the admin of `proxy` to `newAdmin`.
             *
             * Requirements:
             *
             * - This contract must be the current admin of `proxy`.
             */
            function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                proxy.changeAdmin(newAdmin);
            }
        
            /**
             * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
             *
             * Requirements:
             *
             * - This contract must be the admin of `proxy`.
             */
            function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                proxy.upgradeTo(implementation);
            }
        
            /**
             * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
             * {TransparentUpgradeableProxy-upgradeToAndCall}.
             *
             * Requirements:
             *
             * - This contract must be the admin of `proxy`.
             */
            function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                proxy.upgradeToAndCall{value: msg.value}(implementation, data);
            }
        }
        
        
        /**
         * @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
         * publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
         * continuation of the upgradability.
         *
         * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
         *
         * _Available since v4.1._
         */
        abstract contract UUPSUpgradeable is ERC1967Upgrade {
            function upgradeTo(address newImplementation) external virtual {
                _authorizeUpgrade(newImplementation);
                _upgradeToAndCallSecure(newImplementation, bytes(""), false);
            }
        
            function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
                _authorizeUpgrade(newImplementation);
                _upgradeToAndCallSecure(newImplementation, data, true);
            }
        
            function _authorizeUpgrade(address newImplementation) internal virtual;
        }
        
        
        abstract contract Proxiable is UUPSUpgradeable {
            function _authorizeUpgrade(address newImplementation) internal override {
                _beforeUpgrade(newImplementation);
            }
        
            function _beforeUpgrade(address newImplementation) internal virtual;
        }
        
        contract ChildOfProxiable is Proxiable {
            function _beforeUpgrade(address newImplementation) internal virtual override {}
        }
        
        
        /**
         * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
         * implementation address that can be changed. This address is stored in storage in the location specified by
         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
         * implementation behind the proxy.
         */
        contract ERC1967Proxy is Proxy, ERC1967Upgrade {
            /**
             * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
             *
             * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
             * function call, and allows initializating the storage of the proxy like a Solidity constructor.
             */
            constructor(address _logic, bytes memory _data) payable {
                assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                _upgradeToAndCall(_logic, _data, false);
            }
        
            /**
             * @dev Returns the current implementation address.
             */
            function _implementation() internal view virtual override returns (address impl) {
                return ERC1967Upgrade._getImplementation();
            }
        }
        
        /**
         * @dev This contract implements a proxy that is upgradeable by an admin.
         *
         * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
         * clashing], which can potentially be used in an attack, this contract uses the
         * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
         * things that go hand in hand:
         *
         * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
         * that call matches one of the admin functions exposed by the proxy itself.
         * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
         * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
         * "admin cannot fallback to proxy target".
         *
         * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
         * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
         * to sudden errors when trying to call a function from the proxy implementation.
         *
         * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
         * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
         */
        contract TransparentUpgradeableProxy is ERC1967Proxy {
            /**
             * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
             * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
             */
            constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                _changeAdmin(admin_);
            }
        
            /**
             * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
             */
            modifier ifAdmin() {
                if (msg.sender == _getAdmin()) {
                    _;
                } else {
                    _fallback();
                }
            }
        
            /**
             * @dev Returns the current admin.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
             *
             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
             * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
             */
            function admin() external ifAdmin returns (address admin_) {
                admin_ = _getAdmin();
            }
        
            /**
             * @dev Returns the current implementation.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
             *
             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
             * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
             */
            function implementation() external ifAdmin returns (address implementation_) {
                implementation_ = _implementation();
            }
        
            /**
             * @dev Changes the admin of the proxy.
             *
             * Emits an {AdminChanged} event.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
             */
            function changeAdmin(address newAdmin) external virtual ifAdmin {
                _changeAdmin(newAdmin);
            }
        
            /**
             * @dev Upgrade the implementation of the proxy.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
             */
            function upgradeTo(address newImplementation) external ifAdmin {
                _upgradeToAndCall(newImplementation, bytes(""), false);
            }
        
            /**
             * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
             * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
             * proxied contract.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
             */
            function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                _upgradeToAndCall(newImplementation, data, true);
            }
        
            /**
             * @dev Returns the current admin.
             */
            function _admin() internal view virtual returns (address) {
                return _getAdmin();
            }
        
            /**
             * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
             */
            function _beforeFallback() internal virtual override {
                require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                super._beforeFallback();
            }
        }
        
        
        // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
        contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
            constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
        }