ETH Price: $2,443.94 (+4.05%)

Transaction Decoder

Block:
16077492 at Nov-29-2022 06:39:35 PM +UTC
Transaction Fee:
0.000415276741901754 ETH $1.01
Gas Used:
26,834 Gas / 15.475767381 Gwei

Emitted Events:

146 SoundEditionV1.ApprovalForAll( owner=[Sender] 0xac793d2c26c1da7305216d8852f72ba282d4100d, operator=0x1E004978...d54003c71, approved=False )

Account State Difference:

  Address   Before After State Difference Code
0x33aF8e1C...56027c9E6
0xAC793D2C...282D4100d
0.030272592456725476 Eth
Nonce: 11
0.029857315714823722 Eth
Nonce: 12
0.000415276741901754
(bloXroute: Max Profit Builder)
1.085061292878648983 Eth1.085101543878648983 Eth0.000040251

Execution Trace

SoundEditionV1.setApprovalForAll( operator=0x1E0049783F008A0085193E00003D00cd54003c71, approved=False )
  • SoundEditionV1.setApprovalForAll( operator=0x1E0049783F008A0085193E00003D00cd54003c71, approved=False )
    File 1 of 2: SoundEditionV1
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    /*
                     â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’
                   ▒███████████████████████████████████████████████████████████
                   ▒███████████████████████████████████████████████████████████
     ▒▓▓▓▓▓▓▓▓▓▓▓▓▓████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓               ▒▒▒▒▒▒▒▒▒▒▒▒▒██████████████████████████████
     █████████████████████████████▓                            ▒█████████████████████████████
     █████████████████████████████▓                             ▒████████████████████████████
     █████████████████████████████████████████████████████████▓
     ███████████████████████████████████████████████████████████
     ███████████████████████████████████████████████████████████▒
                                  ███████████████████████████████████████████████████████████▒
                                  ▓██████████████████████████████████████████████████████████▒
                                   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████████████████████▒
     █████████████████████████████                             ▒█████████████████████████████▒
     ██████████████████████████████                            ▒█████████████████████████████▒
     ██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒              ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ▒▒▒▒▒▒▒▒▒▒▒▒▒▒███████████████████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                   ▓██████████████████████████████████████████████████████████▒
                   ▓██████████████████████████████████████████████████████████
    */
    import { IERC721AUpgradeable } from "chiru-labs/ERC721A-Upgradeable/IERC721AUpgradeable.sol";
    import { ERC721AUpgradeable, ERC721AStorage } from "chiru-labs/ERC721A-Upgradeable/ERC721AUpgradeable.sol";
    import { ERC721AQueryableUpgradeable } from "chiru-labs/ERC721A-Upgradeable/extensions/ERC721AQueryableUpgradeable.sol";
    import { ERC721ABurnableUpgradeable } from "chiru-labs/ERC721A-Upgradeable/extensions/ERC721ABurnableUpgradeable.sol";
    import { IERC20 } from "openzeppelin/token/ERC20/IERC20.sol";
    import { IERC2981Upgradeable } from "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol";
    import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
    import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol";
    import { OwnableRoles } from "solady/auth/OwnableRoles.sol";
    import { ISoundEditionV1, EditionInfo } from "./interfaces/ISoundEditionV1.sol";
    import { IMetadataModule } from "./interfaces/IMetadataModule.sol";
    import { ArweaveURILib } from "./utils/ArweaveURILib.sol";
    /**
     * @title SoundEditionV1
     * @notice The Sound Edition contract - a creator-owned, modifiable implementation of ERC721A.
     */
    contract SoundEditionV1 is ISoundEditionV1, ERC721AQueryableUpgradeable, ERC721ABurnableUpgradeable, OwnableRoles {
        using ArweaveURILib for ArweaveURILib.URI;
        // =============================================================
        //                           CONSTANTS
        // =============================================================
        /**
         * @dev A role every minter module must have in order to mint new tokens.
         */
        uint256 public constant MINTER_ROLE = _ROLE_1;
        /**
         * @dev A role the owner can grant for performing admin actions.
         */
        uint256 public constant ADMIN_ROLE = _ROLE_0;
        /**
         * @dev The maximum limit for the mint or airdrop `quantity`.
         *      Prevents the first-time transfer costs for tokens near the end of large mint batches
         *      via ERC721A from becoming too expensive due to the need to scan many storage slots.
         *      See: https://chiru-labs.github.io/ERC721A/#/tips?id=batch-size
         */
        uint256 public constant ADDRESS_BATCH_MINT_LIMIT = 255;
        /**
         * @dev Basis points denominator used in fee calculations.
         */
        uint16 internal constant _MAX_BPS = 10_000;
        /**
         * @dev The interface ID for EIP-2981 (royaltyInfo)
         */
        bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
        /**
         * @dev The boolean flag on whether the metadata is frozen.
         */
        uint8 public constant METADATA_IS_FROZEN_FLAG = 1 << 0;
        /**
         * @dev The boolean flag on whether the `mintRandomness` is enabled.
         */
        uint8 public constant MINT_RANDOMNESS_ENABLED_FLAG = 1 << 1;
        // =============================================================
        //                            STORAGE
        // =============================================================
        /**
         * @dev The value for `name` and `symbol` if their combined
         *      length is (32 - 2) bytes. We need 2 bytes for their lengths.
         */
        bytes32 private _shortNameAndSymbol;
        /**
         * @dev The metadata's base URI.
         */
        ArweaveURILib.URI private _baseURIStorage;
        /**
         * @dev The contract base URI.
         */
        ArweaveURILib.URI private _contractURIStorage;
        /**
         * @dev The destination for ETH withdrawals.
         */
        address public fundingRecipient;
        /**
         * @dev The upper bound of the max mintable quantity for the edition.
         */
        uint32 public editionMaxMintableUpper;
        /**
         * @dev The lower bound for the maximum tokens that can be minted for this edition.
         */
        uint32 public editionMaxMintableLower;
        /**
         * @dev The timestamp after which `editionMaxMintable` drops from
         *      `editionMaxMintableUpper` to `max(_totalMinted(), editionMaxMintableLower)`.
         */
        uint32 public editionCutoffTime;
        /**
         * @dev Metadata module used for `tokenURI` and `contractURI` if it is set.
         */
        address public metadataModule;
        /**
         * @dev The randomness based on latest block hash, which is stored upon each mint
         *      unless `randomnessLockedAfterMinted` or `randomnessLockedTimestamp` have been surpassed.
         *      Used for game mechanics like the Sound Golden Egg.
         */
        bytes9 private _mintRandomness;
        /**
         * @dev The royalty fee in basis points.
         */
        uint16 public royaltyBPS;
        /**
         * @dev Packed boolean flags.
         */
        uint8 private _flags;
        // =============================================================
        //               PUBLIC / EXTERNAL WRITE FUNCTIONS
        // =============================================================
        /**
         * @inheritdoc ISoundEditionV1
         */
        function initialize(
            string memory name_,
            string memory symbol_,
            address metadataModule_,
            string memory baseURI_,
            string memory contractURI_,
            address fundingRecipient_,
            uint16 royaltyBPS_,
            uint32 editionMaxMintableLower_,
            uint32 editionMaxMintableUpper_,
            uint32 editionCutoffTime_,
            uint8 flags_
        ) external onlyValidRoyaltyBPS(royaltyBPS_) {
            // Prevent double initialization.
            // We can "cheat" here and avoid the initializer modifer to save a SSTORE,
            // since the `_nextTokenId()` is defined to always return 1.
            if (_nextTokenId() != 0) revert Unauthorized();
            if (fundingRecipient_ == address(0)) revert InvalidFundingRecipient();
            if (editionMaxMintableLower_ > editionMaxMintableUpper_) revert InvalidEditionMaxMintableRange();
            _initializeNameAndSymbol(name_, symbol_);
            ERC721AStorage.layout()._currentIndex = _startTokenId();
            _initializeOwner(msg.sender);
            _baseURIStorage.initialize(baseURI_);
            _contractURIStorage.initialize(contractURI_);
            fundingRecipient = fundingRecipient_;
            editionMaxMintableUpper = editionMaxMintableUpper_;
            editionMaxMintableLower = editionMaxMintableLower_;
            editionCutoffTime = editionCutoffTime_;
            _flags = flags_;
            metadataModule = metadataModule_;
            royaltyBPS = royaltyBPS_;
            emit SoundEditionInitialized(
                address(this),
                name_,
                symbol_,
                metadataModule_,
                baseURI_,
                contractURI_,
                fundingRecipient_,
                royaltyBPS_,
                editionMaxMintableLower_,
                editionMaxMintableUpper_,
                editionCutoffTime_,
                flags_
            );
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mint(address to, uint256 quantity)
            external
            payable
            onlyRolesOrOwner(ADMIN_ROLE | MINTER_ROLE)
            requireWithinAddressBatchMintLimit(quantity)
            requireMintable(quantity)
            updatesMintRandomness
            returns (uint256 fromTokenId)
        {
            fromTokenId = _nextTokenId();
            // Mint the tokens. Will revert if `quantity` is zero.
            _mint(to, quantity);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function airdrop(address[] calldata to, uint256 quantity)
            external
            onlyRolesOrOwner(ADMIN_ROLE)
            requireWithinAddressBatchMintLimit(quantity)
            requireMintable(to.length * quantity)
            updatesMintRandomness
            returns (uint256 fromTokenId)
        {
            if (to.length == 0) revert NoAddressesToAirdrop();
            fromTokenId = _nextTokenId();
            // Won't overflow, as `to.length` is bounded by the block max gas limit.
            unchecked {
                uint256 toLength = to.length;
                // Mint the tokens. Will revert if `quantity` is zero.
                for (uint256 i; i != toLength; ++i) {
                    _mint(to[i], quantity);
                }
            }
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function withdrawETH() external {
            SafeTransferLib.safeTransferETH(fundingRecipient, address(this).balance);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function withdrawERC20(address[] calldata tokens) external {
            unchecked {
                uint256 n = tokens.length;
                for (uint256 i; i != n; ++i) {
                    SafeTransferLib.safeTransfer(tokens[i], fundingRecipient, IERC20(tokens[i]).balanceOf(address(this)));
                }
            }
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setMetadataModule(address metadataModule_) external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            metadataModule = metadataModule_;
            emit MetadataModuleSet(metadataModule_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setBaseURI(string memory baseURI_) external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            _baseURIStorage.update(baseURI_);
            emit BaseURISet(baseURI_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setContractURI(string memory contractURI_) external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            _contractURIStorage.update(contractURI_);
            emit ContractURISet(contractURI_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function freezeMetadata() external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            _flags |= METADATA_IS_FROZEN_FLAG;
            emit MetadataFrozen(metadataModule, baseURI(), contractURI());
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setFundingRecipient(address fundingRecipient_) external onlyRolesOrOwner(ADMIN_ROLE) {
            if (fundingRecipient_ == address(0)) revert InvalidFundingRecipient();
            fundingRecipient = fundingRecipient_;
            emit FundingRecipientSet(fundingRecipient_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setRoyalty(uint16 royaltyBPS_) external onlyRolesOrOwner(ADMIN_ROLE) onlyValidRoyaltyBPS(royaltyBPS_) {
            royaltyBPS = royaltyBPS_;
            emit RoyaltySet(royaltyBPS_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setEditionMaxMintableRange(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_)
            external
            onlyRolesOrOwner(ADMIN_ROLE)
        {
            if (mintConcluded()) revert MintHasConcluded();
            uint32 currentTotalMinted = uint32(_totalMinted());
            if (currentTotalMinted != 0) {
                editionMaxMintableLower_ = uint32(FixedPointMathLib.max(editionMaxMintableLower_, currentTotalMinted));
                editionMaxMintableUpper_ = uint32(FixedPointMathLib.max(editionMaxMintableUpper_, currentTotalMinted));
                // If the upper bound is larger than the current stored value, revert.
                if (editionMaxMintableUpper_ > editionMaxMintableUpper) revert InvalidEditionMaxMintableRange();
            }
            // If the lower bound is larger than the upper bound, revert.
            if (editionMaxMintableLower_ > editionMaxMintableUpper_) revert InvalidEditionMaxMintableRange();
            editionMaxMintableLower = editionMaxMintableLower_;
            editionMaxMintableUpper = editionMaxMintableUpper_;
            emit EditionMaxMintableRangeSet(editionMaxMintableLower, editionMaxMintableUpper);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setEditionCutoffTime(uint32 editionCutoffTime_) external onlyRolesOrOwner(ADMIN_ROLE) {
            if (mintConcluded()) revert MintHasConcluded();
            editionCutoffTime = editionCutoffTime_;
            emit EditionCutoffTimeSet(editionCutoffTime_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setMintRandomnessEnabled(bool mintRandomnessEnabled_) external onlyRolesOrOwner(ADMIN_ROLE) {
            if (_totalMinted() != 0) revert MintsAlreadyExist();
            if (mintRandomnessEnabled() != mintRandomnessEnabled_) {
                _flags ^= MINT_RANDOMNESS_ENABLED_FLAG;
            }
            emit MintRandomnessEnabledSet(mintRandomnessEnabled_);
        }
        // =============================================================
        //               PUBLIC / EXTERNAL VIEW FUNCTIONS
        // =============================================================
        /**
         * @inheritdoc ISoundEditionV1
         */
        function editionInfo() external view returns (EditionInfo memory info) {
            info.baseURI = baseURI();
            info.contractURI = contractURI();
            info.name = name();
            info.symbol = symbol();
            info.fundingRecipient = fundingRecipient;
            info.editionMaxMintable = editionMaxMintable();
            info.editionMaxMintableUpper = editionMaxMintableUpper;
            info.editionMaxMintableLower = editionMaxMintableLower;
            info.editionCutoffTime = editionCutoffTime;
            info.metadataModule = metadataModule;
            info.mintRandomness = mintRandomness();
            info.royaltyBPS = royaltyBPS;
            info.mintRandomnessEnabled = mintRandomnessEnabled();
            info.mintConcluded = mintConcluded();
            info.isMetadataFrozen = isMetadataFrozen();
            info.nextTokenId = nextTokenId();
            info.totalMinted = totalMinted();
            info.totalBurned = totalBurned();
            info.totalSupply = totalSupply();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mintRandomness() public view returns (uint256) {
            if (mintConcluded() && mintRandomnessEnabled()) {
                return uint256(keccak256(abi.encode(_mintRandomness, address(this))));
            }
            return 0;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function editionMaxMintable() public view returns (uint32) {
            if (block.timestamp < editionCutoffTime) {
                return editionMaxMintableUpper;
            } else {
                return uint32(FixedPointMathLib.max(editionMaxMintableLower, _totalMinted()));
            }
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function isMetadataFrozen() public view returns (bool) {
            return _flags & METADATA_IS_FROZEN_FLAG != 0;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mintRandomnessEnabled() public view returns (bool) {
            return _flags & MINT_RANDOMNESS_ENABLED_FLAG != 0;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mintConcluded() public view returns (bool) {
            return _totalMinted() == editionMaxMintable();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function nextTokenId() public view returns (uint256) {
            return _nextTokenId();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function numberMinted(address owner) external view returns (uint256) {
            return _numberMinted(owner);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function numberBurned(address owner) external view returns (uint256) {
            return _numberBurned(owner);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function totalMinted() public view returns (uint256) {
            return _totalMinted();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function totalBurned() public view returns (uint256) {
            return _totalBurned();
        }
        /**
         * @inheritdoc IERC721AUpgradeable
         */
        function tokenURI(uint256 tokenId)
            public
            view
            override(ERC721AUpgradeable, IERC721AUpgradeable)
            returns (string memory)
        {
            if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
            if (metadataModule != address(0)) {
                return IMetadataModule(metadataModule).tokenURI(tokenId);
            }
            string memory baseURI_ = baseURI();
            return bytes(baseURI_).length != 0 ? string.concat(baseURI_, _toString(tokenId)) : "";
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function supportsInterface(bytes4 interfaceId)
            public
            view
            override(ISoundEditionV1, ERC721AUpgradeable, IERC721AUpgradeable)
            returns (bool)
        {
            return
                interfaceId == type(ISoundEditionV1).interfaceId ||
                ERC721AUpgradeable.supportsInterface(interfaceId) ||
                interfaceId == _INTERFACE_ID_ERC2981 ||
                interfaceId == this.supportsInterface.selector;
        }
        /**
         * @inheritdoc IERC2981Upgradeable
         */
        function royaltyInfo(
            uint256, // tokenId
            uint256 salePrice
        ) external view override(IERC2981Upgradeable) returns (address fundingRecipient_, uint256 royaltyAmount) {
            fundingRecipient_ = fundingRecipient;
            royaltyAmount = (salePrice * royaltyBPS) / _MAX_BPS;
        }
        /**
         * @inheritdoc IERC721AUpgradeable
         */
        function name() public view override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) {
            (string memory name_, ) = _loadNameAndSymbol();
            return name_;
        }
        /**
         * @inheritdoc IERC721AUpgradeable
         */
        function symbol() public view override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) {
            (, string memory symbol_) = _loadNameAndSymbol();
            return symbol_;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function baseURI() public view returns (string memory) {
            return _baseURIStorage.load();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function contractURI() public view returns (string memory) {
            return _contractURIStorage.load();
        }
        // =============================================================
        //                  INTERNAL / PRIVATE HELPERS
        // =============================================================
        /**
         * @inheritdoc ERC721AUpgradeable
         */
        function _startTokenId() internal pure override returns (uint256) {
            return 1;
        }
        /**
         * @dev Ensures the royalty basis points is a valid value.
         * @param bps The royalty BPS.
         */
        modifier onlyValidRoyaltyBPS(uint16 bps) {
            if (bps > _MAX_BPS) revert InvalidRoyaltyBPS();
            _;
        }
        /**
         * @dev Reverts if the metadata is frozen.
         */
        modifier onlyMetadataNotFrozen() {
            // Inlined to save gas.
            if (_flags & METADATA_IS_FROZEN_FLAG != 0) revert MetadataIsFrozen();
            _;
        }
        /**
         * @dev Ensures that `totalQuantity` can be minted.
         * @param totalQuantity The total number of tokens to mint.
         */
        modifier requireMintable(uint256 totalQuantity) {
            unchecked {
                uint256 currentTotalMinted = _totalMinted();
                uint256 currentEditionMaxMintable = editionMaxMintable();
                // Check if there are enough tokens to mint.
                // We use version v4.2+ of ERC721A, which `_mint` will revert with out-of-gas
                // error via a loop if `totalQuantity` is large enough to cause an overflow in uint256.
                if (currentTotalMinted + totalQuantity > currentEditionMaxMintable) {
                    // Won't underflow.
                    //
                    // `currentTotalMinted`, which is `_totalMinted()`,
                    // will return either `editionMaxMintableUpper`
                    // or `max(editionMaxMintableLower, _totalMinted())`.
                    //
                    // We have the following invariants:
                    // - `editionMaxMintableUpper >= _totalMinted()`
                    // - `max(editionMaxMintableLower, _totalMinted()) >= _totalMinted()`
                    uint256 available = currentEditionMaxMintable - currentTotalMinted;
                    revert ExceedsEditionAvailableSupply(uint32(available));
                }
            }
            _;
        }
        /**
         * @dev Ensures that the `quantity` does not exceed `ADDRESS_BATCH_MINT_LIMIT`.
         * @param quantity The number of tokens minted per address.
         */
        modifier requireWithinAddressBatchMintLimit(uint256 quantity) {
            if (quantity > ADDRESS_BATCH_MINT_LIMIT) revert ExceedsAddressBatchMintLimit();
            _;
        }
        /**
         * @dev Updates the mint randomness.
         */
        modifier updatesMintRandomness() {
            if (mintRandomnessEnabled() && !mintConcluded()) {
                bytes32 randomness = _mintRandomness;
                assembly {
                    // Pick any of the last 256 blocks psuedorandomly for the blockhash.
                    // Store the blockhash, the current `randomness` and the `coinbase()`
                    // into the scratch space.
                    mstore(0x00, blockhash(sub(number(), add(1, byte(0, randomness)))))
                    // `randomness` is left-aligned.
                    // `coinbase()` is right-aligned.
                    // `difficulty()` is right-aligned.
                    // After the merge, if [EIP-4399](https://eips.ethereum.org/EIPS/eip-4399)
                    // is implemented, the randomness will be determined by the beacon chain.
                    mstore(0x20, xor(randomness, xor(coinbase(), difficulty())))
                    // Compute the new `randomness` by hashing the scratch space.
                    randomness := keccak256(0x00, 0x40)
                }
                _mintRandomness = bytes9(randomness);
            }
            _;
        }
        /**
         * @dev Helper function for initializing the name and symbol,
         *      packing them into a single word if possible.
         * @param name_   Name of the collection.
         * @param symbol_ Symbol of the collection.
         */
        function _initializeNameAndSymbol(string memory name_, string memory symbol_) internal {
            // Overflow impossible since max block gas limit bounds the length of the strings.
            unchecked {
                uint256 nameLength = bytes(name_).length;
                uint256 symbolLength = bytes(symbol_).length;
                uint256 totalLength = nameLength + symbolLength;
                // If we cannot pack both strings into a single 32-byte word, store separately.
                // We need 2 bytes to store their lengths.
                if (totalLength > 32 - 2) {
                    ERC721AStorage.layout()._name = name_;
                    ERC721AStorage.layout()._symbol = symbol_;
                    return;
                }
                // Otherwise, pack them and store them into a single word.
                _shortNameAndSymbol = bytes32(abi.encodePacked(uint8(nameLength), name_, uint8(symbolLength), symbol_));
            }
        }
        /**
         * @dev Helper function for retrieving the name and symbol,
         *      unpacking them from a single word in storage if previously packed.
         * @return name_   Name of the collection.
         * @return symbol_ Symbol of the collection.
         */
        function _loadNameAndSymbol() internal view returns (string memory name_, string memory symbol_) {
            // Overflow impossible since max block gas limit bounds the length of the strings.
            unchecked {
                bytes32 packed = _shortNameAndSymbol;
                // If the strings have been previously packed.
                if (packed != bytes32(0)) {
                    // Allocate the bytes.
                    bytes memory nameBytes = new bytes(uint8(packed[0]));
                    bytes memory symbolBytes = new bytes(uint8(packed[1 + nameBytes.length]));
                    // Copy the bytes.
                    for (uint256 i; i < nameBytes.length; ++i) {
                        nameBytes[i] = bytes1(packed[1 + i]);
                    }
                    for (uint256 i; i < symbolBytes.length; ++i) {
                        symbolBytes[i] = bytes1(packed[2 + nameBytes.length + i]);
                    }
                    // Cast the bytes.
                    name_ = string(nameBytes);
                    symbol_ = string(symbolBytes);
                } else {
                    // Otherwise, load them from their separate variables.
                    name_ = ERC721AStorage.layout()._name;
                    symbol_ = ERC721AStorage.layout()._symbol;
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    /**
     * @dev Interface of ERC721A.
     */
    interface IERC721AUpgradeable {
        /**
         * The caller must own the token or be an approved operator.
         */
        error ApprovalCallerNotOwnerNorApproved();
        /**
         * The token does not exist.
         */
        error ApprovalQueryForNonexistentToken();
        /**
         * Cannot query the balance for the zero address.
         */
        error BalanceQueryForZeroAddress();
        /**
         * Cannot mint to the zero address.
         */
        error MintToZeroAddress();
        /**
         * The quantity of tokens minted must be more than zero.
         */
        error MintZeroQuantity();
        /**
         * The token does not exist.
         */
        error OwnerQueryForNonexistentToken();
        /**
         * The caller must own the token or be an approved operator.
         */
        error TransferCallerNotOwnerNorApproved();
        /**
         * The token must be owned by `from`.
         */
        error TransferFromIncorrectOwner();
        /**
         * Cannot safely transfer to a contract that does not implement the
         * ERC721Receiver interface.
         */
        error TransferToNonERC721ReceiverImplementer();
        /**
         * Cannot transfer to the zero address.
         */
        error TransferToZeroAddress();
        /**
         * The token does not exist.
         */
        error URIQueryForNonexistentToken();
        /**
         * The `quantity` minted with ERC2309 exceeds the safety limit.
         */
        error MintERC2309QuantityExceedsLimit();
        /**
         * The `extraData` cannot be set on an unintialized ownership slot.
         */
        error OwnershipNotInitializedForExtraData();
        // =============================================================
        //                            STRUCTS
        // =============================================================
        struct TokenOwnership {
            // The address of the owner.
            address addr;
            // Stores the start time of ownership with minimal overhead for tokenomics.
            uint64 startTimestamp;
            // Whether the token has been burned.
            bool burned;
            // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
            uint24 extraData;
        }
        // =============================================================
        //                         TOKEN COUNTERS
        // =============================================================
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() external view returns (uint256);
        // =============================================================
        //                            IERC165
        // =============================================================
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
        // =============================================================
        //                            IERC721
        // =============================================================
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables
         * (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`,
         * checking first that contract recipients are aware of the ERC721 protocol
         * to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move
         * this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @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;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the
         * zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom}
         * for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
        // =============================================================
        //                           IERC2309
        // =============================================================
        /**
         * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
         * (inclusive) is transferred from `from` to `to`, as defined in the
         * [ERC2309](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);
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721AUpgradeable.sol';
    import {ERC721AStorage} from './ERC721AStorage.sol';
    import './ERC721A__Initializable.sol';
    /**
     * @dev Interface of ERC721 token receiver.
     */
    interface ERC721A__IERC721ReceiverUpgradeable {
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    /**
     * @title ERC721A
     *
     * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
     * Non-Fungible Token Standard, including the Metadata extension.
     * Optimized for lower gas during batch mints.
     *
     * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
     * starting from `_startTokenId()`.
     *
     * Assumptions:
     *
     * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
     * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
     */
    contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable {
        using ERC721AStorage for ERC721AStorage.Layout;
        // =============================================================
        //                           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;
        // =============================================================
        //                          CONSTRUCTOR
        // =============================================================
        function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
            __ERC721A_init_unchained(name_, symbol_);
        }
        function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
            ERC721AStorage.layout()._name = name_;
            ERC721AStorage.layout()._symbol = symbol_;
            ERC721AStorage.layout()._currentIndex = _startTokenId();
        }
        // =============================================================
        //                   TOKEN COUNTING OPERATIONS
        // =============================================================
        /**
         * @dev Returns the starting token ID.
         * To change the starting token ID, please override this function.
         */
        function _startTokenId() internal view virtual returns (uint256) {
            return 0;
        }
        /**
         * @dev Returns the next token ID to be minted.
         */
        function _nextTokenId() internal view virtual returns (uint256) {
            return ERC721AStorage.layout()._currentIndex;
        }
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            // Counter underflow is impossible as _burnCounter cannot be incremented
            // more than `_currentIndex - _startTokenId()` times.
            unchecked {
                return ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId();
            }
        }
        /**
         * @dev Returns the total amount of tokens minted in the contract.
         */
        function _totalMinted() internal view virtual returns (uint256) {
            // Counter underflow is impossible as `_currentIndex` does not decrement,
            // and it is initialized to `_startTokenId()`.
            unchecked {
                return ERC721AStorage.layout()._currentIndex - _startTokenId();
            }
        }
        /**
         * @dev Returns the total number of tokens burned.
         */
        function _totalBurned() internal view virtual returns (uint256) {
            return ERC721AStorage.layout()._burnCounter;
        }
        // =============================================================
        //                    ADDRESS DATA OPERATIONS
        // =============================================================
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            if (owner == address(0)) revert BalanceQueryForZeroAddress();
            return ERC721AStorage.layout()._packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
        }
        /**
         * Returns the number of tokens minted by `owner`.
         */
        function _numberMinted(address owner) internal view returns (uint256) {
            return
                (ERC721AStorage.layout()._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
                (ERC721AStorage.layout()._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(ERC721AStorage.layout()._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 = ERC721AStorage.layout()._packedAddressData[owner];
            uint256 auxCasted;
            // Cast `aux` with assembly to avoid redundant masking.
            assembly {
                auxCasted := aux
            }
            packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
            ERC721AStorage.layout()._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 ERC721AStorage.layout()._name;
        }
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() public view virtual override returns (string memory) {
            return ERC721AStorage.layout()._symbol;
        }
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
            string memory baseURI = _baseURI();
            return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
        }
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, it can be overridden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return '';
        }
        // =============================================================
        //                     OWNERSHIPS OPERATIONS
        // =============================================================
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            return address(uint160(_packedOwnershipOf(tokenId)));
        }
        /**
         * @dev Gas spent here starts off proportional to the maximum mint batch size.
         * It gradually moves to O(1) as tokens get transferred around over time.
         */
        function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(_packedOwnershipOf(tokenId));
        }
        /**
         * @dev Returns the unpacked `TokenOwnership` struct at `index`.
         */
        function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(ERC721AStorage.layout()._packedOwnerships[index]);
        }
        /**
         * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
         */
        function _initializeOwnershipAt(uint256 index) internal virtual {
            if (ERC721AStorage.layout()._packedOwnerships[index] == 0) {
                ERC721AStorage.layout()._packedOwnerships[index] = _packedOwnershipOf(index);
            }
        }
        /**
         * Returns the packed ownership data of `tokenId`.
         */
        function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
            uint256 curr = tokenId;
            unchecked {
                if (_startTokenId() <= curr)
                    if (curr < ERC721AStorage.layout()._currentIndex) {
                        uint256 packed = ERC721AStorage.layout()._packedOwnerships[curr];
                        // If not burned.
                        if (packed & _BITMASK_BURNED == 0) {
                            // Invariant:
                            // There will always be an initialized ownership slot
                            // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                            // before an unintialized ownership slot
                            // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                            // Hence, `curr` will not underflow.
                            //
                            // We can directly compare the packed value.
                            // If the address is zero, packed will be zero.
                            while (packed == 0) {
                                packed = ERC721AStorage.layout()._packedOwnerships[--curr];
                            }
                            return packed;
                        }
                    }
            }
            revert OwnerQueryForNonexistentToken();
        }
        /**
         * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
         */
        function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
            ownership.addr = address(uint160(packed));
            ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
            ownership.burned = packed & _BITMASK_BURNED != 0;
            ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
        }
        /**
         * @dev Packs ownership data into a single uint256.
         */
        function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
            }
        }
        /**
         * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
         */
        function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
            // For branchless setting of the `nextInitialized` flag.
            assembly {
                // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
            }
        }
        // =============================================================
        //                      APPROVAL OPERATIONS
        // =============================================================
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the
         * zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ownerOf(tokenId);
            if (_msgSenderERC721A() != owner)
                if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                    revert ApprovalCallerNotOwnerNorApproved();
                }
            ERC721AStorage.layout()._tokenApprovals[tokenId].value = to;
            emit Approval(owner, to, tokenId);
        }
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
            return ERC721AStorage.layout()._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 {
            ERC721AStorage.layout()._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 ERC721AStorage.layout()._operatorApprovals[owner][operator];
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted. See {_mint}.
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return
                _startTokenId() <= tokenId &&
                tokenId < ERC721AStorage.layout()._currentIndex && // If within bounds,
                ERC721AStorage.layout()._packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
        }
        /**
         * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
         */
        function _isSenderApprovedOrOwner(
            address approvedAddress,
            address owner,
            address msgSender
        ) private pure returns (bool result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                msgSender := and(msgSender, _BITMASK_ADDRESS)
                // `msgSender == owner || msgSender == approvedAddress`.
                result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
            }
        }
        /**
         * @dev Returns the storage slot and value for the approved address of `tokenId`.
         */
        function _getApprovedSlotAndAddress(uint256 tokenId)
            private
            view
            returns (uint256 approvedAddressSlot, address approvedAddress)
        {
            ERC721AStorage.TokenApprovalRef storage tokenApproval = ERC721AStorage.layout()._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 virtual override {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
            if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
            if (to == address(0)) revert TransferToZeroAddress();
            _beforeTokenTransfers(from, to, tokenId, 1);
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // We can directly increment and decrement the balances.
                --ERC721AStorage.layout()._packedAddressData[from]; // Updates: `balance -= 1`.
                ++ERC721AStorage.layout()._packedAddressData[to]; // Updates: `balance += 1`.
                // Updates:
                // - `address` to the next owner.
                // - `startTimestamp` to the timestamp of transfering.
                // - `burned` to `false`.
                // - `nextInitialized` to `true`.
                ERC721AStorage.layout()._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 (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
            emit Transfer(from, to, tokenId);
            _afterTokenTransfers(from, to, tokenId, 1);
        }
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public 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 virtual override {
            transferFrom(from, to, tokenId);
            if (to.code.length != 0)
                if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                    revert TransferToNonERC721ReceiverImplementer();
                }
        }
        /**
         * @dev Hook that is called before a set of serially-ordered token IDs
         * are about to be transferred. This includes minting.
         * And also called before burning one token.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _beforeTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
        /**
         * @dev Hook that is called after a set of serially-ordered token IDs
         * have been transferred. This includes minting.
         * And also called after one token has been burned.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
         * transferred to `to`.
         * - When `from` is zero, `tokenId` has been minted for `to`.
         * - When `to` is zero, `tokenId` has been burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _afterTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
        /**
         * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
         *
         * `from` - Previous owner of the given token ID.
         * `to` - Target address that will receive the token.
         * `tokenId` - Token ID to be transferred.
         * `_data` - Optional data to send along with the call.
         *
         * Returns whether the call correctly returned the expected magic value.
         */
        function _checkContractOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            try
                ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data)
            returns (bytes4 retval) {
                return retval == ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert TransferToNonERC721ReceiverImplementer();
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
        // =============================================================
        //                        MINT OPERATIONS
        // =============================================================
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _mint(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
            if (quantity == 0) revert MintZeroQuantity();
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
            // Overflows are incredibly unrealistic.
            // `balance` and `numberMinted` have a maximum limit of 2**64.
            // `tokenId` has a maximum limit of 2**256.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                ERC721AStorage.layout()._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`.
                ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
                uint256 toMasked;
                uint256 end = startTokenId + quantity;
                // Use assembly to loop and emit the `Transfer` event for gas savings.
                // The duplicated `log4` removes an extra check and reduces stack juggling.
                // The assembly, together with the surrounding Solidity code, have been
                // delicately arranged to nudge the compiler into producing optimized opcodes.
                assembly {
                    // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                    toMasked := and(to, _BITMASK_ADDRESS)
                    // Emit the `Transfer` event.
                    log4(
                        0, // Start of data (0, since no data).
                        0, // End of data (0, since no data).
                        _TRANSFER_EVENT_SIGNATURE, // Signature.
                        0, // `address(0)`.
                        toMasked, // `to`.
                        startTokenId // `tokenId`.
                    )
                    for {
                        let tokenId := add(startTokenId, 1)
                    } iszero(eq(tokenId, end)) {
                        tokenId := add(tokenId, 1)
                    } {
                        // Emit the `Transfer` event. Similar to above.
                        log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                    }
                }
                if (toMasked == 0) revert MintToZeroAddress();
                ERC721AStorage.layout()._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 = ERC721AStorage.layout()._currentIndex;
            if (to == address(0)) revert MintToZeroAddress();
            if (quantity == 0) revert MintZeroQuantity();
            if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
            // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                ERC721AStorage.layout()._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`.
                ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
                emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
                ERC721AStorage.layout()._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 = ERC721AStorage.layout()._currentIndex;
                    uint256 index = end - quantity;
                    do {
                        if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                            revert TransferToNonERC721ReceiverImplementer();
                        }
                    } while (index < end);
                    // Reentrancy protection.
                    if (ERC721AStorage.layout()._currentIndex != end) revert();
                }
            }
        }
        /**
         * @dev Equivalent to `_safeMint(to, quantity, '')`.
         */
        function _safeMint(address to, uint256 quantity) internal virtual {
            _safeMint(to, quantity, '');
        }
        // =============================================================
        //                        BURN OPERATIONS
        // =============================================================
        /**
         * @dev Equivalent to `_burn(tokenId, false)`.
         */
        function _burn(uint256 tokenId) internal virtual {
            _burn(tokenId, false);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
            address from = address(uint160(prevOwnershipPacked));
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
            if (approvalCheck) {
                // The nested ifs save around 20+ gas over a compound boolean condition.
                if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                    if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
            }
            _beforeTokenTransfers(from, address(0), tokenId, 1);
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // Updates:
                // - `balance -= 1`.
                // - `numberBurned += 1`.
                //
                // We can directly decrement the balance, and increment the number burned.
                // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                ERC721AStorage.layout()._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`.
                ERC721AStorage.layout()._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 (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
            emit Transfer(from, address(0), tokenId);
            _afterTokenTransfers(from, address(0), tokenId, 1);
            // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
            unchecked {
                ERC721AStorage.layout()._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 = ERC721AStorage.layout()._packedOwnerships[index];
            if (packed == 0) revert OwnershipNotInitializedForExtraData();
            uint256 extraDataCasted;
            // Cast `extraData` with assembly to avoid redundant masking.
            assembly {
                extraDataCasted := extraData
            }
            packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
            ERC721AStorage.layout()._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)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721AQueryableUpgradeable.sol';
    import '../ERC721AUpgradeable.sol';
    import '../ERC721A__Initializable.sol';
    /**
     * @title ERC721AQueryable.
     *
     * @dev ERC721A subclass with convenience query functions.
     */
    abstract contract ERC721AQueryableUpgradeable is
        ERC721A__Initializable,
        ERC721AUpgradeable,
        IERC721AQueryableUpgradeable
    {
        function __ERC721AQueryable_init() internal onlyInitializingERC721A {
            __ERC721AQueryable_init_unchained();
        }
        function __ERC721AQueryable_init_unchained() internal onlyInitializingERC721A {}
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) public view virtual override returns (TokenOwnership memory) {
            TokenOwnership memory ownership;
            if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) {
                return ownership;
            }
            ownership = _ownershipAt(tokenId);
            if (ownership.burned) {
                return ownership;
            }
            return _ownershipOf(tokenId);
        }
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] calldata tokenIds)
            external
            view
            virtual
            override
            returns (TokenOwnership[] memory)
        {
            unchecked {
                uint256 tokenIdsLength = tokenIds.length;
                TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
                for (uint256 i; i != tokenIdsLength; ++i) {
                    ownerships[i] = explicitOwnershipOf(tokenIds[i]);
                }
                return ownerships;
            }
        }
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view virtual override returns (uint256[] memory) {
            unchecked {
                if (start >= stop) revert InvalidQueryRange();
                uint256 tokenIdsIdx;
                uint256 stopLimit = _nextTokenId();
                // Set `start = max(start, _startTokenId())`.
                if (start < _startTokenId()) {
                    start = _startTokenId();
                }
                // Set `stop = min(stop, stopLimit)`.
                if (stop > stopLimit) {
                    stop = stopLimit;
                }
                uint256 tokenIdsMaxLength = balanceOf(owner);
                // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
                // to cater for cases where `balanceOf(owner)` is too big.
                if (start < stop) {
                    uint256 rangeLength = stop - start;
                    if (rangeLength < tokenIdsMaxLength) {
                        tokenIdsMaxLength = rangeLength;
                    }
                } else {
                    tokenIdsMaxLength = 0;
                }
                uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
                if (tokenIdsMaxLength == 0) {
                    return tokenIds;
                }
                // We need to call `explicitOwnershipOf(start)`,
                // because the slot at `start` may not be initialized.
                TokenOwnership memory ownership = explicitOwnershipOf(start);
                address currOwnershipAddr;
                // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`.
                // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range.
                if (!ownership.burned) {
                    currOwnershipAddr = ownership.addr;
                }
                for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
                    ownership = _ownershipAt(i);
                    if (ownership.burned) {
                        continue;
                    }
                    if (ownership.addr != address(0)) {
                        currOwnershipAddr = ownership.addr;
                    }
                    if (currOwnershipAddr == owner) {
                        tokenIds[tokenIdsIdx++] = i;
                    }
                }
                // Downsize the array to fit.
                assembly {
                    mstore(tokenIds, tokenIdsIdx)
                }
                return tokenIds;
            }
        }
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) {
            unchecked {
                uint256 tokenIdsIdx;
                address currOwnershipAddr;
                uint256 tokenIdsLength = balanceOf(owner);
                uint256[] memory tokenIds = new uint256[](tokenIdsLength);
                TokenOwnership memory ownership;
                for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
                    ownership = _ownershipAt(i);
                    if (ownership.burned) {
                        continue;
                    }
                    if (ownership.addr != address(0)) {
                        currOwnershipAddr = ownership.addr;
                    }
                    if (currOwnershipAddr == owner) {
                        tokenIds[tokenIdsIdx++] = i;
                    }
                }
                return tokenIds;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721ABurnableUpgradeable.sol';
    import '../ERC721AUpgradeable.sol';
    import '../ERC721A__Initializable.sol';
    /**
     * @title ERC721ABurnable.
     *
     * @dev ERC721A token that can be irreversibly burned (destroyed).
     */
    abstract contract ERC721ABurnableUpgradeable is
        ERC721A__Initializable,
        ERC721AUpgradeable,
        IERC721ABurnableUpgradeable
    {
        function __ERC721ABurnable_init() internal onlyInitializingERC721A {
            __ERC721ABurnable_init_unchained();
        }
        function __ERC721ABurnable_init_unchained() internal onlyInitializingERC721A {}
        /**
         * @dev Burns `tokenId`. See {ERC721A-_burn}.
         *
         * Requirements:
         *
         * - The caller must own `tokenId` or be an approved operator.
         */
        function burn(uint256 tokenId) public virtual override {
            _burn(tokenId, true);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
    pragma solidity ^0.8.0;
    import "../utils/introspection/IERC165Upgradeable.sol";
    /**
     * @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.
     *
     * _Available since v4.5._
     */
    interface IERC2981Upgradeable is IERC165Upgradeable {
        /**
         * @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.
         */
        function royaltyInfo(uint256 tokenId, uint256 salePrice)
            external
            view
            returns (address receiver, uint256 royaltyAmount);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
    /// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
    library SafeTransferLib {
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        error ETHTransferFailed();
        error TransferFromFailed();
        error TransferFailed();
        error ApproveFailed();
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       ETH OPERATIONS                       */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        function safeTransferETH(address to, uint256 amount) internal {
            assembly {
                // Transfer the ETH and check if it succeeded or not.
                if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                    // Store the function selector of `ETHTransferFailed()`.
                    mstore(0x00, 0xb12d13eb)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                      ERC20 OPERATIONS                      */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        function safeTransferFrom(
            address token,
            address from,
            address to,
            uint256 amount
        ) internal {
            assembly {
                // We'll write our calldata to this slot below, but restore it later.
                let memPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(0x00, 0x23b872dd)
                mstore(0x20, from) // Append the "from" argument.
                mstore(0x40, to) // Append the "to" argument.
                mstore(0x60, amount) // Append the "amount" argument.
                if iszero(
                    and(
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        // We use 0x64 because that's the total length of our calldata (0x04 + 0x20 * 3)
                        // Counterintuitively, this call() must be positioned after the or() in the
                        // surrounding and() because and() evaluates its arguments from right to left.
                        call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFromFailed()`.
                    mstore(0x00, 0x7939f424)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot to zero.
                mstore(0x40, memPointer) // Restore the memPointer.
            }
        }
        function safeTransfer(
            address token,
            address to,
            uint256 amount
        ) internal {
            assembly {
                // We'll write our calldata to this slot below, but restore it later.
                let memPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(0x00, 0xa9059cbb)
                mstore(0x20, to) // Append the "to" argument.
                mstore(0x40, amount) // Append the "amount" argument.
                if iszero(
                    and(
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        // We use 0x44 because that's the total length of our calldata (0x04 + 0x20 * 2)
                        // Counterintuitively, this call() must be positioned after the or() in the
                        // surrounding and() because and() evaluates its arguments from right to left.
                        call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFailed()`.
                    mstore(0x00, 0x90b8ec18)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x40, memPointer) // Restore the memPointer.
            }
        }
        function safeApprove(
            address token,
            address to,
            uint256 amount
        ) internal {
            assembly {
                // We'll write our calldata to this slot below, but restore it later.
                let memPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(0x00, 0x095ea7b3)
                mstore(0x20, to) // Append the "to" argument.
                mstore(0x40, amount) // Append the "amount" argument.
                if iszero(
                    and(
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        // We use 0x44 because that's the total length of our calldata (0x04 + 0x20 * 2)
                        // Counterintuitively, this call() must be positioned after the or() in the
                        // surrounding and() because and() evaluates its arguments from right to left.
                        call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `ApproveFailed()`.
                    mstore(0x00, 0x3e3f8f73)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x40, memPointer) // Restore the memPointer.
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Arithmetic library with operations for fixed-point numbers.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
    library FixedPointMathLib {
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The operation failed, as the output exceeds the maximum value of uint256.
        error ExpOverflow();
        /// @dev The operation failed, as the output exceeds the maximum value of uint256.
        error FactorialOverflow();
        /// @dev The operation failed, due to an multiplication overflow.
        error MulWadFailed();
        /// @dev The operation failed, either due to a
        /// multiplication overflow, or a division by a zero.
        error DivWadFailed();
        /// @dev The multiply-divide operation failed, either due to a
        /// multiplication overflow, or a division by a zero.
        error MulDivFailed();
        /// @dev The division failed, as the denominator is zero.
        error DivFailed();
        /// @dev The output is undefined, as the input is less-than-or-equal to zero.
        error LnWadUndefined();
        /// @dev The output is undefined, as the input is zero.
        error Log2Undefined();
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The scalar of ETH and most ERC20s.
        uint256 internal constant WAD = 1e18;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*              SIMPLIFIED FIXED POINT OPERATIONS             */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Equivalent to `(x * y) / WAD` rounded down.
        function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
                if mul(y, gt(x, div(not(0), y))) {
                    // Store the function selector of `MulWadFailed()`.
                    mstore(0x00, 0xbac65e5b)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := div(mul(x, y), WAD)
            }
        }
        /// @dev Equivalent to `(x * y) / WAD` rounded up.
        function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
                if mul(y, gt(x, div(not(0), y))) {
                    // Store the function selector of `MulWadFailed()`.
                    mstore(0x00, 0xbac65e5b)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
            }
        }
        /// @dev Equivalent to `(x * WAD) / y` rounded down.
        function divWadDown(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
                if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                    // Store the function selector of `DivWadFailed()`.
                    mstore(0x00, 0x7c5f487d)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := div(mul(x, WAD), y)
            }
        }
        /// @dev Equivalent to `(x * WAD) / y` rounded up.
        function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
                if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                    // Store the function selector of `DivWadFailed()`.
                    mstore(0x00, 0x7c5f487d)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
            }
        }
        /// @dev Equivalent to `x` to the power of `y`.
        /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
        function powWad(int256 x, int256 y) internal pure returns (int256) {
            // Using `ln(x)` means `x` must be greater than 0.
            return expWad((lnWad(x) * y) / int256(WAD));
        }
        /// @dev Returns `exp(x)`, denominated in `WAD`.
        function expWad(int256 x) internal pure returns (int256 r) {
            unchecked {
                // When the result is < 0.5 we return zero. This happens when
                // x <= floor(log(0.5e18) * 1e18) ~ -42e18
                if (x <= -42139678854452767551) return 0;
                // When the result is > (2**255 - 1) / 1e18 we can not represent it as an
                // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
                if (x >= 135305999368893231589) revert ExpOverflow();
                // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
                // for more intermediate precision and a binary basis. This base conversion
                // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
                x = (x << 78) / 5**18;
                // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
                // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
                // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
                int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;
                x = x - k * 54916777467707473351141471128;
                // k is in the range [-61, 195].
                // Evaluate using a (6, 7)-term rational approximation.
                // p is made monic, we'll multiply by a scale factor later.
                int256 y = x + 1346386616545796478920950773328;
                y = ((y * x) >> 96) + 57155421227552351082224309758442;
                int256 p = y + x - 94201549194550492254356042504812;
                p = ((p * y) >> 96) + 28719021644029726153956944680412240;
                p = p * x + (4385272521454847904659076985693276 << 96);
                // We leave p in 2**192 basis so we don't need to scale it back up for the division.
                int256 q = x - 2855989394907223263936484059900;
                q = ((q * x) >> 96) + 50020603652535783019961831881945;
                q = ((q * x) >> 96) - 533845033583426703283633433725380;
                q = ((q * x) >> 96) + 3604857256930695427073651918091429;
                q = ((q * x) >> 96) - 14423608567350463180887372962807573;
                q = ((q * x) >> 96) + 26449188498355588339934803723976023;
                assembly {
                    // Div in assembly because solidity adds a zero check despite the unchecked.
                    // The q polynomial won't have zeros in the domain as all its roots are complex.
                    // No scaling is necessary because p is already 2**96 too large.
                    r := sdiv(p, q)
                }
                // r should be in the range (0.09, 0.25) * 2**96.
                // We now need to multiply r by:
                // * the scale factor s = ~6.031367120.
                // * the 2**k factor from the range reduction.
                // * the 1e18 / 2**96 factor for base conversion.
                // We do this all at once, with an intermediate result in 2**213
                // basis, so the final right shift is always by a positive amount.
                r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));
            }
        }
        /// @dev Returns `ln(x)`, denominated in `WAD`.
        function lnWad(int256 x) internal pure returns (int256 r) {
            unchecked {
                if (x <= 0) revert LnWadUndefined();
                // We want to convert x from 10**18 fixed point to 2**96 fixed point.
                // We do this by multiplying by 2**96 / 10**18. But since
                // ln(x * C) = ln(x) + ln(C), we can simply do nothing here
                // and add ln(2**96 / 10**18) at the end.
                // Compute k = log2(x) - 96.
                int256 k;
                assembly {
                    let v := x
                    k := shl(7, lt(0xffffffffffffffffffffffffffffffff, v))
                    k := or(k, shl(6, lt(0xffffffffffffffff, shr(k, v))))
                    k := or(k, shl(5, lt(0xffffffff, shr(k, v))))
                    // For the remaining 32 bits, use a De Bruijn lookup.
                    // See: https://graphics.stanford.edu/~seander/bithacks.html
                    v := shr(k, v)
                    v := or(v, shr(1, v))
                    v := or(v, shr(2, v))
                    v := or(v, shr(4, v))
                    v := or(v, shr(8, v))
                    v := or(v, shr(16, v))
                    // prettier-ignore
                    k := sub(or(k, byte(shr(251, mul(v, shl(224, 0x07c4acdd))),
                        0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)), 96)
                }
                // Reduce range of x to (1, 2) * 2**96
                // ln(2^k * x) = k * ln(2) + ln(x)
                x <<= uint256(159 - k);
                x = int256(uint256(x) >> 159);
                // Evaluate using a (8, 8)-term rational approximation.
                // p is made monic, we will multiply by a scale factor later.
                int256 p = x + 3273285459638523848632254066296;
                p = ((p * x) >> 96) + 24828157081833163892658089445524;
                p = ((p * x) >> 96) + 43456485725739037958740375743393;
                p = ((p * x) >> 96) - 11111509109440967052023855526967;
                p = ((p * x) >> 96) - 45023709667254063763336534515857;
                p = ((p * x) >> 96) - 14706773417378608786704636184526;
                p = p * x - (795164235651350426258249787498 << 96);
                // We leave p in 2**192 basis so we don't need to scale it back up for the division.
                // q is monic by convention.
                int256 q = x + 5573035233440673466300451813936;
                q = ((q * x) >> 96) + 71694874799317883764090561454958;
                q = ((q * x) >> 96) + 283447036172924575727196451306956;
                q = ((q * x) >> 96) + 401686690394027663651624208769553;
                q = ((q * x) >> 96) + 204048457590392012362485061816622;
                q = ((q * x) >> 96) + 31853899698501571402653359427138;
                q = ((q * x) >> 96) + 909429971244387300277376558375;
                assembly {
                    // Div in assembly because solidity adds a zero check despite the unchecked.
                    // The q polynomial is known not to have zeros in the domain.
                    // No scaling required because p is already 2**96 too large.
                    r := sdiv(p, q)
                }
                // r is in the range (0, 0.125) * 2**96
                // Finalization, we need to:
                // * multiply by the scale factor s = 5.549…
                // * add ln(2**96 / 10**18)
                // * add k * ln(2)
                // * multiply by 10**18 / 2**96 = 5**18 >> 78
                // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
                r *= 1677202110996718588342820967067443963516166;
                // add ln(2) * k * 5e18 * 2**192
                r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
                // add ln(2**96 / 10**18) * 5e18 * 2**192
                r += 600920179829731861736702779321621459595472258049074101567377883020018308;
                // base conversion: mul 2**18 / 2**192
                r >>= 174;
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                  GENERAL NUMBER UTILITIES                  */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Returns `floor(x * y / denominator)`.
        /// Reverts if `x * y` overflows, or `denominator` is zero.
        function mulDivDown(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                if iszero(mul(denominator, iszero(mul(y, gt(x, div(not(0), y)))))) {
                    // Store the function selector of `MulDivFailed()`.
                    mstore(0x00, 0xad251c27)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := div(mul(x, y), denominator)
            }
        }
        /// @dev Returns `ceil(x * y / denominator)`.
        /// Reverts if `x * y` overflows, or `denominator` is zero.
        function mulDivUp(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                if iszero(mul(denominator, iszero(mul(y, gt(x, div(not(0), y)))))) {
                    // Store the function selector of `MulDivFailed()`.
                    mstore(0x00, 0xad251c27)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(mul(x, y), denominator))), div(mul(x, y), denominator))
            }
        }
        /// @dev Returns `ceil(x / denominator)`.
        /// Reverts if `denominator` is zero.
        function divUp(uint256 x, uint256 denominator) internal pure returns (uint256 z) {
            assembly {
                if iszero(denominator) {
                    // Store the function selector of `DivFailed()`.
                    mstore(0x00, 0x65244e4e)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(x, denominator))), div(x, denominator))
            }
        }
        /// @dev Returns `max(0, x - y)`.
        function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := mul(gt(x, y), sub(x, y))
            }
        }
        /// @dev Returns the square root of `x`.
        function sqrt(uint256 x) internal pure returns (uint256 z) {
            assembly {
                // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
                z := 181 // The "correct" value is 1, but this saves a multiplication later.
                // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                // Let `y = x / 2**r`.
                // We check `y >= 2**(k + 8)` but shift right by `k` bits
                // each branch to ensure that if `x >= 256`, then `y >= 256`.
                let r := shl(7, gt(x, 0xffffffffffffffffffffffffffffffffff))
                r := or(r, shl(6, gt(shr(r, x), 0xffffffffffffffffff)))
                r := or(r, shl(5, gt(shr(r, x), 0xffffffffff)))
                r := or(r, shl(4, gt(shr(r, x), 0xffffff)))
                z := shl(shr(1, r), z)
                // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
                // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
                // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
                // That's not possible if `x < 256` but we can just verify those cases exhaustively.
                // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
                // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
                // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
                // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
                // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
                // with largest error when `s = 1` and when `s = 256` or `1/256`.
                // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
                // Then we can estimate `sqrt(y)` using
                // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
                // There is no overflow risk here since `y < 2**136` after the first branch above.
                z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
                // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                // If `x+1` is a perfect square, the Babylonian method cycles between
                // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
                // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                z := sub(z, lt(div(x, z), z))
            }
        }
        /// @dev Returns the factorial of `x`.
        function factorial(uint256 x) public pure returns (uint256 result) {
            unchecked {
                if (x < 11) {
                    // prettier-ignore
                    result = (0x375f0016260009d80004ec0002d00001e0000180000180000200000400001 >> (x * 22)) & 0x3fffff;
                } else if (x < 32) {
                    result = 3628800;
                    do {
                        result = result * x;
                        x = x - 1;
                    } while (x != 10);
                } else if (x < 58) {
                    // Just cheat lol.
                    result = 8222838654177922817725562880000000;
                    do {
                        result = result * x;
                        x = x - 1;
                    } while (x != 31);
                } else {
                    revert FactorialOverflow();
                }
            }
        }
        /// @dev Returns the log2 of `x`.
        /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
        function log2(uint256 x) internal pure returns (uint256 r) {
            assembly {
                if iszero(x) {
                    // Store the function selector of `Log2Undefined()`.
                    mstore(0x00, 0x5be3aa5c)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
                r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
                r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                // For the remaining 32 bits, use a De Bruijn lookup.
                // See: https://graphics.stanford.edu/~seander/bithacks.html
                x := shr(r, x)
                x := or(x, shr(1, x))
                x := or(x, shr(2, x))
                x := or(x, shr(4, x))
                x := or(x, shr(8, x))
                x := or(x, shr(16, x))
                // prettier-ignore
                r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))),
                    0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f))
            }
        }
        /// @dev Returns the averege of `x` and `y`.
        function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := add(and(x, y), shr(1, xor(x, y)))
            }
        }
        /// @dev Returns the absolute value of `x`.
        function abs(int256 x) internal pure returns (uint256 z) {
            assembly {
                let mask := mul(shr(255, x), not(0))
                z := xor(mask, add(mask, x))
            }
        }
        /// @dev Returns the absolute distance between `x` and `y`.
        function dist(int256 x, int256 y) internal pure returns (uint256 z) {
            assembly {
                let a := sub(y, x)
                z := xor(a, mul(xor(a, sub(x, y)), sgt(x, y)))
            }
        }
        /// @dev Returns the minimum of `x` and `y`.
        function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := xor(x, mul(xor(x, y), lt(y, x)))
            }
        }
        /// @dev Returns the maximum of `x` and `y`.
        function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := xor(x, mul(xor(x, y), gt(y, x)))
            }
        }
        /// @dev Returns gcd of `x` and `y`.
        function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // prettier-ignore
                for { z := x } y {} {
                    let t := y
                    y := mod(z, y)
                    z := t
                }
            }
        }
        /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
        function clamp(
            uint256 x,
            uint256 minValue,
            uint256 maxValue
        ) internal pure returns (uint256 z) {
            return min(max(x, minValue), maxValue);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Simple single owner and multiroles authorization mixin.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol)
    /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
    /// for compatibility, the nomenclature for the 2-step ownership handover and roles
    /// may be unique to this codebase.
    abstract contract OwnableRoles {
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The caller is not authorized to call the function.
        error Unauthorized();
        /// @dev The `newOwner` cannot be the zero address.
        error NewOwnerIsZeroAddress();
        /// @dev The `pendingOwner` does not have a valid handover request.
        error NoHandoverRequest();
        /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
        uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;
        /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`.
        uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae;
        /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`.
        uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                           EVENTS                           */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
        /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
        /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
        /// despite it not being as lightweight as a single argument event.
        event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
        /// @dev An ownership handover to `pendingOwner` has been requested.
        event OwnershipHandoverRequested(address indexed pendingOwner);
        /// @dev The ownership handover to `pendingOwner` has been cancelled.
        event OwnershipHandoverCanceled(address indexed pendingOwner);
        /// @dev The `user`'s roles is updated to `roles`.
        /// Each bit of `roles` represents whether the role is set.
        event RolesUpdated(address indexed user, uint256 indexed roles);
        /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
        uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
            0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
        /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
        uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
            0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
        /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
        uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
            0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
        /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
        uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
            0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                          STORAGE                           */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
        /// It is intentionally choosen to be a high value
        /// to avoid collision with lower slots.
        /// The choice of manual storage layout is to enable compatibility
        /// with both regular and upgradeable contracts.
        ///
        /// The role slot of `user` is given by:
        /// ```
        ///     mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
        ///     let roleSlot := keccak256(0x00, 0x20)
        /// ```
        /// This automatically ignores the upper bits of the `user` in case
        /// they are not clean, as well as keep the `keccak256` under 32-bytes.
        uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
        /// The ownership handover slot of `newOwner` is given by:
        /// ```
        ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
        ///     let handoverSlot := keccak256(0x00, 0x20)
        /// ```
        /// It stores the expiry timestamp of the two-step ownership handover.
        uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                     INTERNAL FUNCTIONS                     */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Initializes the owner directly without authorization guard.
        /// This function must be called upon initialization,
        /// regardless of whether the contract is upgradeable or not.
        /// This is to enable generalization to both regular and upgradeable contracts,
        /// and to save gas in case the initial owner is not the caller.
        /// For performance reasons, this function will not check if there
        /// is an existing owner.
        function _initializeOwner(address newOwner) internal virtual {
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
        /// @dev Sets the owner directly without authorization guard.
        function _setOwner(address newOwner) internal virtual {
            assembly {
                let ownerSlot := not(_OWNER_SLOT_NOT)
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
        /// @dev Grants the roles directly without authorization guard.
        /// Each bit of `roles` represents the role to turn on.
        function _grantRoles(address user, uint256 roles) internal virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                let roleSlot := keccak256(0x00, 0x20)
                // Load the current value and `or` it with `roles`.
                let newRoles := or(sload(roleSlot), roles)
                // Store the new value.
                sstore(roleSlot, newRoles)
                // Emit the {RolesUpdated} event.
                log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles)
            }
        }
        /// @dev Removes the roles directly without authorization guard.
        /// Each bit of `roles` represents the role to turn off.
        function _removeRoles(address user, uint256 roles) internal virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                let roleSlot := keccak256(0x00, 0x20)
                // Load the current value.
                let currentRoles := sload(roleSlot)
                // Use `and` to compute the intersection of `currentRoles` and `roles`,
                // `xor` it with `currentRoles` to flip the bits in the intersection.
                let newRoles := xor(currentRoles, and(currentRoles, roles))
                // Then, store the new value.
                sstore(roleSlot, newRoles)
                // Emit the {RolesUpdated} event.
                log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles)
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                  PUBLIC UPDATE FUNCTIONS                   */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Allows the owner to transfer the ownership to `newOwner`.
        function transferOwnership(address newOwner) public virtual onlyOwner {
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Reverts if the `newOwner` is the zero address.
                if iszero(newOwner) {
                    mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), newOwner)
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), newOwner)
            }
        }
        /// @dev Allows the owner to renounce their ownership.
        function renounceOwnership() public virtual onlyOwner {
            assembly {
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), 0)
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), 0)
            }
        }
        /// @dev Request a two-step ownership handover to the caller.
        /// The request will be automatically expire in 48 hours (172800 seconds) by default.
        function requestOwnershipHandover() public virtual {
            unchecked {
                uint256 expires = block.timestamp + ownershipHandoverValidFor();
                assembly {
                    // Compute and set the handover slot to 1.
                    mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED))
                    sstore(keccak256(0x00, 0x20), expires)
                    // Emit the {OwnershipHandoverRequested} event.
                    log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                }
            }
        }
        /// @dev Cancels the two-step ownership handover to the caller, if any.
        function cancelOwnershipHandover() public virtual {
            assembly {
                // Compute and set the handover slot to 0.
                mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED))
                sstore(keccak256(0x00, 0x20), 0)
                // Emit the {OwnershipHandoverCanceled} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
            }
        }
        /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
        /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
        function completeOwnershipHandover(address pendingOwner) public virtual onlyOwner {
            assembly {
                // Clean the upper 96 bits.
                pendingOwner := shr(96, shl(96, pendingOwner))
                // Compute and set the handover slot to 0.
                mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED))
                let handoverSlot := keccak256(0x00, 0x20)
                // If the handover does not exist, or has expired.
                if gt(timestamp(), sload(handoverSlot)) {
                    mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
                // Set the handover slot to 0.
                sstore(handoverSlot, 0)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), pendingOwner)
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), pendingOwner)
            }
        }
        /// @dev Allows the owner to grant `user` `roles`.
        /// If the `user` already has a role, then it will be an no-op for the role.
        function grantRoles(address user, uint256 roles) public virtual onlyOwner {
            _grantRoles(user, roles);
        }
        /// @dev Allows the owner to remove `user` `roles`.
        /// If the `user` does not have a role, then it will be an no-op for the role.
        function revokeRoles(address user, uint256 roles) public virtual onlyOwner {
            _removeRoles(user, roles);
        }
        /// @dev Allow the caller to remove their own roles.
        /// If the caller does not have a role, then it will be an no-op for the role.
        function renounceRoles(uint256 roles) public virtual {
            _removeRoles(msg.sender, roles);
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                   PUBLIC READ FUNCTIONS                    */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Returns the owner of the contract.
        function owner() public view virtual returns (address result) {
            assembly {
                result := sload(not(_OWNER_SLOT_NOT))
            }
        }
        /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
        function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) {
            assembly {
                // Compute the handover slot.
                mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED))
                // Load the handover slot.
                result := sload(keccak256(0x00, 0x20))
            }
        }
        /// @dev Returns how long a two-step ownership handover is valid for in seconds.
        function ownershipHandoverValidFor() public view virtual returns (uint64) {
            return 48 * 3600;
        }
        /// @dev Returns whether `user` has any of `roles`.
        function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                // Load the stored value, and set the result to whether the
                // `and` intersection of the value and `roles` is not zero.
                result := iszero(iszero(and(sload(keccak256(0x00, 0x20)), roles)))
            }
        }
        /// @dev Returns whether `user` has all of `roles`.
        function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                // Whether the stored value is contains all the set bits in `roles`.
                result := eq(and(sload(keccak256(0x00, 0x20)), roles), roles)
            }
        }
        /// @dev Returns the roles of `user`.
        function rolesOf(address user) public view virtual returns (uint256 roles) {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                // Load the stored value.
                roles := sload(keccak256(0x00, 0x20))
            }
        }
        /// @dev Convenience function to return a `roles` bitmap from the `ordinals`.
        /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
        /// Not recommended to be called on-chain.
        function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) {
            assembly {
                // Skip the length slot.
                let o := add(ordinals, 0x20)
                // `shl` 5 is equivalent to multiplying by 0x20.
                let end := add(o, shl(5, mload(ordinals)))
                // prettier-ignore
                for {} iszero(eq(o, end)) { o := add(o, 0x20) } {
                    roles := or(roles, shl(and(mload(o), 0xff), 1))
                }
            }
        }
        /// @dev Convenience function to return a `roles` bitmap from the `ordinals`.
        /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
        /// Not recommended to be called on-chain.
        function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) {
            assembly {
                // Grab the pointer to the free memory.
                let ptr := add(mload(0x40), 0x20)
                // The absence of lookup tables, De Bruijn, etc., here is intentional for
                // smaller bytecode, as this function is not meant to be called on-chain.
                // prettier-ignore
                for { let i := 0 } 1 { i := add(i, 1) } {
                    mstore(ptr, i)
                    // `shr` 5 is equivalent to multiplying by 0x20.
                    // Push back into the ordinals array if the bit is set.
                    ptr := add(ptr, shl(5, and(roles, 1)))
                    roles := shr(1, roles)
                    // prettier-ignore
                    if iszero(roles) { break }
                }
                // Set `ordinals` to the start of the free memory.
                ordinals := mload(0x40)
                // Allocate the memory.
                mstore(0x40, ptr)
                // Store the length of `ordinals`.
                mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                         MODIFIERS                          */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Marks a function as only callable by the owner.
        modifier onlyOwner() virtual {
            assembly {
                // If the caller is not the stored owner, revert.
                if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
            _;
        }
        /// @dev Marks a function as only callable by an account with `roles`.
        modifier onlyRoles(uint256 roles) virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
            _;
        }
        /// @dev Marks a function as only callable by the owner or by an account
        /// with `roles`. Checks for ownership first, then lazily checks for roles.
        modifier onlyOwnerOrRoles(uint256 roles) virtual {
            assembly {
                // If the caller is not the stored owner.
                if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                    // Compute the role slot.
                    mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
                    // Load the stored value, and if the `and` intersection
                    // of the value and `roles` is zero, revert.
                    if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                        mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                        revert(0x1c, 0x04)
                    }
                }
            }
            _;
        }
        /// @dev Marks a function as only callable by an account with `roles`
        /// or the owner. Checks for roles first, then lazily checks for ownership.
        modifier onlyRolesOrOwner(uint256 roles) virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                    // If the caller is not the stored owner.
                    if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                        mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                        revert(0x1c, 0x04)
                    }
                }
            }
            _;
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       ROLE CONSTANTS                       */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        // IYKYK
        uint256 internal constant _ROLE_0 = 1 << 0;
        uint256 internal constant _ROLE_1 = 1 << 1;
        uint256 internal constant _ROLE_2 = 1 << 2;
        uint256 internal constant _ROLE_3 = 1 << 3;
        uint256 internal constant _ROLE_4 = 1 << 4;
        uint256 internal constant _ROLE_5 = 1 << 5;
        uint256 internal constant _ROLE_6 = 1 << 6;
        uint256 internal constant _ROLE_7 = 1 << 7;
        uint256 internal constant _ROLE_8 = 1 << 8;
        uint256 internal constant _ROLE_9 = 1 << 9;
        uint256 internal constant _ROLE_10 = 1 << 10;
        uint256 internal constant _ROLE_11 = 1 << 11;
        uint256 internal constant _ROLE_12 = 1 << 12;
        uint256 internal constant _ROLE_13 = 1 << 13;
        uint256 internal constant _ROLE_14 = 1 << 14;
        uint256 internal constant _ROLE_15 = 1 << 15;
        uint256 internal constant _ROLE_16 = 1 << 16;
        uint256 internal constant _ROLE_17 = 1 << 17;
        uint256 internal constant _ROLE_18 = 1 << 18;
        uint256 internal constant _ROLE_19 = 1 << 19;
        uint256 internal constant _ROLE_20 = 1 << 20;
        uint256 internal constant _ROLE_21 = 1 << 21;
        uint256 internal constant _ROLE_22 = 1 << 22;
        uint256 internal constant _ROLE_23 = 1 << 23;
        uint256 internal constant _ROLE_24 = 1 << 24;
        uint256 internal constant _ROLE_25 = 1 << 25;
        uint256 internal constant _ROLE_26 = 1 << 26;
        uint256 internal constant _ROLE_27 = 1 << 27;
        uint256 internal constant _ROLE_28 = 1 << 28;
        uint256 internal constant _ROLE_29 = 1 << 29;
        uint256 internal constant _ROLE_30 = 1 << 30;
        uint256 internal constant _ROLE_31 = 1 << 31;
        uint256 internal constant _ROLE_32 = 1 << 32;
        uint256 internal constant _ROLE_33 = 1 << 33;
        uint256 internal constant _ROLE_34 = 1 << 34;
        uint256 internal constant _ROLE_35 = 1 << 35;
        uint256 internal constant _ROLE_36 = 1 << 36;
        uint256 internal constant _ROLE_37 = 1 << 37;
        uint256 internal constant _ROLE_38 = 1 << 38;
        uint256 internal constant _ROLE_39 = 1 << 39;
        uint256 internal constant _ROLE_40 = 1 << 40;
        uint256 internal constant _ROLE_41 = 1 << 41;
        uint256 internal constant _ROLE_42 = 1 << 42;
        uint256 internal constant _ROLE_43 = 1 << 43;
        uint256 internal constant _ROLE_44 = 1 << 44;
        uint256 internal constant _ROLE_45 = 1 << 45;
        uint256 internal constant _ROLE_46 = 1 << 46;
        uint256 internal constant _ROLE_47 = 1 << 47;
        uint256 internal constant _ROLE_48 = 1 << 48;
        uint256 internal constant _ROLE_49 = 1 << 49;
        uint256 internal constant _ROLE_50 = 1 << 50;
        uint256 internal constant _ROLE_51 = 1 << 51;
        uint256 internal constant _ROLE_52 = 1 << 52;
        uint256 internal constant _ROLE_53 = 1 << 53;
        uint256 internal constant _ROLE_54 = 1 << 54;
        uint256 internal constant _ROLE_55 = 1 << 55;
        uint256 internal constant _ROLE_56 = 1 << 56;
        uint256 internal constant _ROLE_57 = 1 << 57;
        uint256 internal constant _ROLE_58 = 1 << 58;
        uint256 internal constant _ROLE_59 = 1 << 59;
        uint256 internal constant _ROLE_60 = 1 << 60;
        uint256 internal constant _ROLE_61 = 1 << 61;
        uint256 internal constant _ROLE_62 = 1 << 62;
        uint256 internal constant _ROLE_63 = 1 << 63;
        uint256 internal constant _ROLE_64 = 1 << 64;
        uint256 internal constant _ROLE_65 = 1 << 65;
        uint256 internal constant _ROLE_66 = 1 << 66;
        uint256 internal constant _ROLE_67 = 1 << 67;
        uint256 internal constant _ROLE_68 = 1 << 68;
        uint256 internal constant _ROLE_69 = 1 << 69;
        uint256 internal constant _ROLE_70 = 1 << 70;
        uint256 internal constant _ROLE_71 = 1 << 71;
        uint256 internal constant _ROLE_72 = 1 << 72;
        uint256 internal constant _ROLE_73 = 1 << 73;
        uint256 internal constant _ROLE_74 = 1 << 74;
        uint256 internal constant _ROLE_75 = 1 << 75;
        uint256 internal constant _ROLE_76 = 1 << 76;
        uint256 internal constant _ROLE_77 = 1 << 77;
        uint256 internal constant _ROLE_78 = 1 << 78;
        uint256 internal constant _ROLE_79 = 1 << 79;
        uint256 internal constant _ROLE_80 = 1 << 80;
        uint256 internal constant _ROLE_81 = 1 << 81;
        uint256 internal constant _ROLE_82 = 1 << 82;
        uint256 internal constant _ROLE_83 = 1 << 83;
        uint256 internal constant _ROLE_84 = 1 << 84;
        uint256 internal constant _ROLE_85 = 1 << 85;
        uint256 internal constant _ROLE_86 = 1 << 86;
        uint256 internal constant _ROLE_87 = 1 << 87;
        uint256 internal constant _ROLE_88 = 1 << 88;
        uint256 internal constant _ROLE_89 = 1 << 89;
        uint256 internal constant _ROLE_90 = 1 << 90;
        uint256 internal constant _ROLE_91 = 1 << 91;
        uint256 internal constant _ROLE_92 = 1 << 92;
        uint256 internal constant _ROLE_93 = 1 << 93;
        uint256 internal constant _ROLE_94 = 1 << 94;
        uint256 internal constant _ROLE_95 = 1 << 95;
        uint256 internal constant _ROLE_96 = 1 << 96;
        uint256 internal constant _ROLE_97 = 1 << 97;
        uint256 internal constant _ROLE_98 = 1 << 98;
        uint256 internal constant _ROLE_99 = 1 << 99;
        uint256 internal constant _ROLE_100 = 1 << 100;
        uint256 internal constant _ROLE_101 = 1 << 101;
        uint256 internal constant _ROLE_102 = 1 << 102;
        uint256 internal constant _ROLE_103 = 1 << 103;
        uint256 internal constant _ROLE_104 = 1 << 104;
        uint256 internal constant _ROLE_105 = 1 << 105;
        uint256 internal constant _ROLE_106 = 1 << 106;
        uint256 internal constant _ROLE_107 = 1 << 107;
        uint256 internal constant _ROLE_108 = 1 << 108;
        uint256 internal constant _ROLE_109 = 1 << 109;
        uint256 internal constant _ROLE_110 = 1 << 110;
        uint256 internal constant _ROLE_111 = 1 << 111;
        uint256 internal constant _ROLE_112 = 1 << 112;
        uint256 internal constant _ROLE_113 = 1 << 113;
        uint256 internal constant _ROLE_114 = 1 << 114;
        uint256 internal constant _ROLE_115 = 1 << 115;
        uint256 internal constant _ROLE_116 = 1 << 116;
        uint256 internal constant _ROLE_117 = 1 << 117;
        uint256 internal constant _ROLE_118 = 1 << 118;
        uint256 internal constant _ROLE_119 = 1 << 119;
        uint256 internal constant _ROLE_120 = 1 << 120;
        uint256 internal constant _ROLE_121 = 1 << 121;
        uint256 internal constant _ROLE_122 = 1 << 122;
        uint256 internal constant _ROLE_123 = 1 << 123;
        uint256 internal constant _ROLE_124 = 1 << 124;
        uint256 internal constant _ROLE_125 = 1 << 125;
        uint256 internal constant _ROLE_126 = 1 << 126;
        uint256 internal constant _ROLE_127 = 1 << 127;
        uint256 internal constant _ROLE_128 = 1 << 128;
        uint256 internal constant _ROLE_129 = 1 << 129;
        uint256 internal constant _ROLE_130 = 1 << 130;
        uint256 internal constant _ROLE_131 = 1 << 131;
        uint256 internal constant _ROLE_132 = 1 << 132;
        uint256 internal constant _ROLE_133 = 1 << 133;
        uint256 internal constant _ROLE_134 = 1 << 134;
        uint256 internal constant _ROLE_135 = 1 << 135;
        uint256 internal constant _ROLE_136 = 1 << 136;
        uint256 internal constant _ROLE_137 = 1 << 137;
        uint256 internal constant _ROLE_138 = 1 << 138;
        uint256 internal constant _ROLE_139 = 1 << 139;
        uint256 internal constant _ROLE_140 = 1 << 140;
        uint256 internal constant _ROLE_141 = 1 << 141;
        uint256 internal constant _ROLE_142 = 1 << 142;
        uint256 internal constant _ROLE_143 = 1 << 143;
        uint256 internal constant _ROLE_144 = 1 << 144;
        uint256 internal constant _ROLE_145 = 1 << 145;
        uint256 internal constant _ROLE_146 = 1 << 146;
        uint256 internal constant _ROLE_147 = 1 << 147;
        uint256 internal constant _ROLE_148 = 1 << 148;
        uint256 internal constant _ROLE_149 = 1 << 149;
        uint256 internal constant _ROLE_150 = 1 << 150;
        uint256 internal constant _ROLE_151 = 1 << 151;
        uint256 internal constant _ROLE_152 = 1 << 152;
        uint256 internal constant _ROLE_153 = 1 << 153;
        uint256 internal constant _ROLE_154 = 1 << 154;
        uint256 internal constant _ROLE_155 = 1 << 155;
        uint256 internal constant _ROLE_156 = 1 << 156;
        uint256 internal constant _ROLE_157 = 1 << 157;
        uint256 internal constant _ROLE_158 = 1 << 158;
        uint256 internal constant _ROLE_159 = 1 << 159;
        uint256 internal constant _ROLE_160 = 1 << 160;
        uint256 internal constant _ROLE_161 = 1 << 161;
        uint256 internal constant _ROLE_162 = 1 << 162;
        uint256 internal constant _ROLE_163 = 1 << 163;
        uint256 internal constant _ROLE_164 = 1 << 164;
        uint256 internal constant _ROLE_165 = 1 << 165;
        uint256 internal constant _ROLE_166 = 1 << 166;
        uint256 internal constant _ROLE_167 = 1 << 167;
        uint256 internal constant _ROLE_168 = 1 << 168;
        uint256 internal constant _ROLE_169 = 1 << 169;
        uint256 internal constant _ROLE_170 = 1 << 170;
        uint256 internal constant _ROLE_171 = 1 << 171;
        uint256 internal constant _ROLE_172 = 1 << 172;
        uint256 internal constant _ROLE_173 = 1 << 173;
        uint256 internal constant _ROLE_174 = 1 << 174;
        uint256 internal constant _ROLE_175 = 1 << 175;
        uint256 internal constant _ROLE_176 = 1 << 176;
        uint256 internal constant _ROLE_177 = 1 << 177;
        uint256 internal constant _ROLE_178 = 1 << 178;
        uint256 internal constant _ROLE_179 = 1 << 179;
        uint256 internal constant _ROLE_180 = 1 << 180;
        uint256 internal constant _ROLE_181 = 1 << 181;
        uint256 internal constant _ROLE_182 = 1 << 182;
        uint256 internal constant _ROLE_183 = 1 << 183;
        uint256 internal constant _ROLE_184 = 1 << 184;
        uint256 internal constant _ROLE_185 = 1 << 185;
        uint256 internal constant _ROLE_186 = 1 << 186;
        uint256 internal constant _ROLE_187 = 1 << 187;
        uint256 internal constant _ROLE_188 = 1 << 188;
        uint256 internal constant _ROLE_189 = 1 << 189;
        uint256 internal constant _ROLE_190 = 1 << 190;
        uint256 internal constant _ROLE_191 = 1 << 191;
        uint256 internal constant _ROLE_192 = 1 << 192;
        uint256 internal constant _ROLE_193 = 1 << 193;
        uint256 internal constant _ROLE_194 = 1 << 194;
        uint256 internal constant _ROLE_195 = 1 << 195;
        uint256 internal constant _ROLE_196 = 1 << 196;
        uint256 internal constant _ROLE_197 = 1 << 197;
        uint256 internal constant _ROLE_198 = 1 << 198;
        uint256 internal constant _ROLE_199 = 1 << 199;
        uint256 internal constant _ROLE_200 = 1 << 200;
        uint256 internal constant _ROLE_201 = 1 << 201;
        uint256 internal constant _ROLE_202 = 1 << 202;
        uint256 internal constant _ROLE_203 = 1 << 203;
        uint256 internal constant _ROLE_204 = 1 << 204;
        uint256 internal constant _ROLE_205 = 1 << 205;
        uint256 internal constant _ROLE_206 = 1 << 206;
        uint256 internal constant _ROLE_207 = 1 << 207;
        uint256 internal constant _ROLE_208 = 1 << 208;
        uint256 internal constant _ROLE_209 = 1 << 209;
        uint256 internal constant _ROLE_210 = 1 << 210;
        uint256 internal constant _ROLE_211 = 1 << 211;
        uint256 internal constant _ROLE_212 = 1 << 212;
        uint256 internal constant _ROLE_213 = 1 << 213;
        uint256 internal constant _ROLE_214 = 1 << 214;
        uint256 internal constant _ROLE_215 = 1 << 215;
        uint256 internal constant _ROLE_216 = 1 << 216;
        uint256 internal constant _ROLE_217 = 1 << 217;
        uint256 internal constant _ROLE_218 = 1 << 218;
        uint256 internal constant _ROLE_219 = 1 << 219;
        uint256 internal constant _ROLE_220 = 1 << 220;
        uint256 internal constant _ROLE_221 = 1 << 221;
        uint256 internal constant _ROLE_222 = 1 << 222;
        uint256 internal constant _ROLE_223 = 1 << 223;
        uint256 internal constant _ROLE_224 = 1 << 224;
        uint256 internal constant _ROLE_225 = 1 << 225;
        uint256 internal constant _ROLE_226 = 1 << 226;
        uint256 internal constant _ROLE_227 = 1 << 227;
        uint256 internal constant _ROLE_228 = 1 << 228;
        uint256 internal constant _ROLE_229 = 1 << 229;
        uint256 internal constant _ROLE_230 = 1 << 230;
        uint256 internal constant _ROLE_231 = 1 << 231;
        uint256 internal constant _ROLE_232 = 1 << 232;
        uint256 internal constant _ROLE_233 = 1 << 233;
        uint256 internal constant _ROLE_234 = 1 << 234;
        uint256 internal constant _ROLE_235 = 1 << 235;
        uint256 internal constant _ROLE_236 = 1 << 236;
        uint256 internal constant _ROLE_237 = 1 << 237;
        uint256 internal constant _ROLE_238 = 1 << 238;
        uint256 internal constant _ROLE_239 = 1 << 239;
        uint256 internal constant _ROLE_240 = 1 << 240;
        uint256 internal constant _ROLE_241 = 1 << 241;
        uint256 internal constant _ROLE_242 = 1 << 242;
        uint256 internal constant _ROLE_243 = 1 << 243;
        uint256 internal constant _ROLE_244 = 1 << 244;
        uint256 internal constant _ROLE_245 = 1 << 245;
        uint256 internal constant _ROLE_246 = 1 << 246;
        uint256 internal constant _ROLE_247 = 1 << 247;
        uint256 internal constant _ROLE_248 = 1 << 248;
        uint256 internal constant _ROLE_249 = 1 << 249;
        uint256 internal constant _ROLE_250 = 1 << 250;
        uint256 internal constant _ROLE_251 = 1 << 251;
        uint256 internal constant _ROLE_252 = 1 << 252;
        uint256 internal constant _ROLE_253 = 1 << 253;
        uint256 internal constant _ROLE_254 = 1 << 254;
        uint256 internal constant _ROLE_255 = 1 << 255;
    }
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    import { IERC721AUpgradeable } from "chiru-labs/ERC721A-Upgradeable/IERC721AUpgradeable.sol";
    import { IERC2981Upgradeable } from "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol";
    import { IERC165Upgradeable } from "openzeppelin-upgradeable/utils/introspection/IERC165Upgradeable.sol";
    import { IMetadataModule } from "./IMetadataModule.sol";
    /**
     * @dev The information pertaining to this edition.
     */
    struct EditionInfo {
        // Base URI for the tokenId.
        string baseURI;
        // Contract URI for OpenSea storefront.
        string contractURI;
        // Name of the collection.
        string name;
        // Symbol of the collection.
        string symbol;
        // Address that receives primary and secondary royalties.
        address fundingRecipient;
        // The current max mintable amount;
        uint32 editionMaxMintable;
        // The lower limit of the maximum number of tokens that can be minted.
        uint32 editionMaxMintableUpper;
        // The upper limit of the maximum number of tokens that can be minted.
        uint32 editionMaxMintableLower;
        // The timestamp (in seconds since unix epoch) after which the
        // max amount of tokens mintable will drop from
        // `maxMintableUpper` to `maxMintableLower`.
        uint32 editionCutoffTime;
        // Address of metadata module, address(0x00) if not used.
        address metadataModule;
        // The current mint randomness value.
        uint256 mintRandomness;
        // The royalty BPS (basis points).
        uint16 royaltyBPS;
        // Whether the mint randomness is enabled.
        bool mintRandomnessEnabled;
        // Whether the mint has concluded.
        bool mintConcluded;
        // Whether the metadata has been frozen.
        bool isMetadataFrozen;
        // Next token ID to be minted.
        uint256 nextTokenId;
        // Total number of tokens burned.
        uint256 totalBurned;
        // Total number of tokens minted.
        uint256 totalMinted;
        // Total number of tokens currently in existence.
        uint256 totalSupply;
    }
    /**
     * @title ISoundEditionV1
     * @notice The interface for Sound edition contracts.
     */
    interface ISoundEditionV1 is IERC721AUpgradeable, IERC2981Upgradeable {
        // =============================================================
        //                            EVENTS
        // =============================================================
        /**
         * @dev Emitted when the metadata module is set.
         * @param metadataModule the address of the metadata module.
         */
        event MetadataModuleSet(address metadataModule);
        /**
         * @dev Emitted when the `baseURI` is set.
         * @param baseURI the base URI of the edition.
         */
        event BaseURISet(string baseURI);
        /**
         * @dev Emitted when the `contractURI` is set.
         * @param contractURI The contract URI of the edition.
         */
        event ContractURISet(string contractURI);
        /**
         * @dev Emitted when the metadata is frozen (e.g.: `baseURI` can no longer be changed).
         * @param metadataModule The address of the metadata module.
         * @param baseURI        The base URI of the edition.
         * @param contractURI    The contract URI of the edition.
         */
        event MetadataFrozen(address metadataModule, string baseURI, string contractURI);
        /**
         * @dev Emitted when the `fundingRecipient` is set.
         * @param fundingRecipient The address of the funding recipient.
         */
        event FundingRecipientSet(address fundingRecipient);
        /**
         * @dev Emitted when the `royaltyBPS` is set.
         * @param bps The new royalty, measured in basis points.
         */
        event RoyaltySet(uint16 bps);
        /**
         * @dev Emitted when the edition's maximum mintable token quantity range is set.
         * @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
         * @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
         */
        event EditionMaxMintableRangeSet(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_);
        /**
         * @dev Emitted when the edition's cutoff time set.
         * @param editionCutoffTime_ The timestamp.
         */
        event EditionCutoffTimeSet(uint32 editionCutoffTime_);
        /**
         * @dev Emitted when the `mintRandomnessEnabled` is set.
         * @param mintRandomnessEnabled_ The boolean value.
         */
        event MintRandomnessEnabledSet(bool mintRandomnessEnabled_);
        /**
         * @dev Emitted upon initialization.
         * @param edition_                 The address of the edition.
         * @param name_                    Name of the collection.
         * @param symbol_                  Symbol of the collection.
         * @param metadataModule_          Address of metadata module, address(0x00) if not used.
         * @param baseURI_                 Base URI.
         * @param contractURI_             Contract URI for OpenSea storefront.
         * @param fundingRecipient_        Address that receives primary and secondary royalties.
         * @param royaltyBPS_              Royalty amount in bps (basis points).
         * @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
         * @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
         * @param editionCutoffTime_       The timestamp after which `editionMaxMintable` drops from
         *                                 `editionMaxMintableUpper` to
         *                                 `max(_totalMinted(), editionMaxMintableLower)`.
         * @param flags_                   The bitwise OR result of the initialization flags.
         *                                 See: {METADATA_IS_FROZEN_FLAG}
         *                                 See: {MINT_RANDOMNESS_ENABLED_FLAG}
         */
        event SoundEditionInitialized(
            address indexed edition_,
            string name_,
            string symbol_,
            address metadataModule_,
            string baseURI_,
            string contractURI_,
            address fundingRecipient_,
            uint16 royaltyBPS_,
            uint32 editionMaxMintableLower_,
            uint32 editionMaxMintableUpper_,
            uint32 editionCutoffTime_,
            uint8 flags_
        );
        // =============================================================
        //                            ERRORS
        // =============================================================
        /**
         * @dev The edition's metadata is frozen (e.g.: `baseURI` can no longer be changed).
         */
        error MetadataIsFrozen();
        /**
         * @dev The given `royaltyBPS` is invalid.
         */
        error InvalidRoyaltyBPS();
        /**
         * @dev The given `randomnessLockedAfterMinted` value is invalid.
         */
        error InvalidRandomnessLock();
        /**
         * @dev The requested quantity exceeds the edition's remaining mintable token quantity.
         * @param available The number of tokens remaining available for mint.
         */
        error ExceedsEditionAvailableSupply(uint32 available);
        /**
         * @dev The given amount is invalid.
         */
        error InvalidAmount();
        /**
         * @dev The given `fundingRecipient` address is invalid.
         */
        error InvalidFundingRecipient();
        /**
         * @dev The `editionMaxMintableLower` must not be greater than `editionMaxMintableUpper`.
         */
        error InvalidEditionMaxMintableRange();
        /**
         * @dev The `editionMaxMintable` has already been reached.
         */
        error MaximumHasAlreadyBeenReached();
        /**
         * @dev The mint `quantity` cannot exceed `ADDRESS_BATCH_MINT_LIMIT` tokens.
         */
        error ExceedsAddressBatchMintLimit();
        /**
         * @dev The mint randomness has already been revealed.
         */
        error MintRandomnessAlreadyRevealed();
        /**
         * @dev No addresses to airdrop.
         */
        error NoAddressesToAirdrop();
        /**
         * @dev The mint has already concluded.
         */
        error MintHasConcluded();
        /**
         * @dev Cannot perform the operation after a token has been minted.
         */
        error MintsAlreadyExist();
        // =============================================================
        //               PUBLIC / EXTERNAL WRITE FUNCTIONS
        // =============================================================
        /**
         * @dev Initializes the contract.
         * @param name_                    Name of the collection.
         * @param symbol_                  Symbol of the collection.
         * @param metadataModule_          Address of metadata module, address(0x00) if not used.
         * @param baseURI_                 Base URI.
         * @param contractURI_             Contract URI for OpenSea storefront.
         * @param fundingRecipient_        Address that receives primary and secondary royalties.
         * @param royaltyBPS_              Royalty amount in bps (basis points).
         * @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
         * @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
         * @param editionCutoffTime_       The timestamp after which `editionMaxMintable` drops from
         *                                 `editionMaxMintableUpper` to
         *                                 `max(_totalMinted(), editionMaxMintableLower)`.
         * @param flags_                   The bitwise OR result of the initialization flags.
         *                                 See: {METADATA_IS_FROZEN_FLAG}
         *                                 See: {MINT_RANDOMNESS_ENABLED_FLAG}
         */
        function initialize(
            string memory name_,
            string memory symbol_,
            address metadataModule_,
            string memory baseURI_,
            string memory contractURI_,
            address fundingRecipient_,
            uint16 royaltyBPS_,
            uint32 editionMaxMintableLower_,
            uint32 editionMaxMintableUpper_,
            uint32 editionCutoffTime_,
            uint8 flags_
        ) external;
        /**
         * @dev Mints `quantity` tokens to addrress `to`
         *      Each token will be assigned a token ID that is consecutively increasing.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have either the
         *   `ADMIN_ROLE`, `MINTER_ROLE`, which can be granted via {grantRole}.
         *   Multiple minters, such as different minter contracts,
         *   can be authorized simultaneously.
         *
         * @param to       Address to mint to.
         * @param quantity Number of tokens to mint.
         * @return fromTokenId The first token ID minted.
         */
        function mint(address to, uint256 quantity) external payable returns (uint256 fromTokenId);
        /**
         * @dev Mints `quantity` tokens to each of the addresses in `to`.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the
         *   `ADMIN_ROLE`, which can be granted via {grantRole}.
         *
         * @param to           Address to mint to.
         * @param quantity     Number of tokens to mint.
         * @return fromTokenId The first token ID minted.
         */
        function airdrop(address[] calldata to, uint256 quantity) external returns (uint256 fromTokenId);
        /**
         * @dev Withdraws collected ETH royalties to the fundingRecipient.
         */
        function withdrawETH() external;
        /**
         * @dev Withdraws collected ERC20 royalties to the fundingRecipient.
         * @param tokens array of ERC20 tokens to withdraw
         */
        function withdrawERC20(address[] calldata tokens) external;
        /**
         * @dev Sets metadata module.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param metadataModule Address of metadata module.
         */
        function setMetadataModule(address metadataModule) external;
        /**
         * @dev Sets global base URI.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param baseURI The base URI to be set.
         */
        function setBaseURI(string memory baseURI) external;
        /**
         * @dev Sets contract URI.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param contractURI The contract URI to be set.
         */
        function setContractURI(string memory contractURI) external;
        /**
         * @dev Freezes metadata by preventing any more changes to base URI.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         */
        function freezeMetadata() external;
        /**
         * @dev Sets funding recipient address.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param fundingRecipient Address to be set as the new funding recipient.
         */
        function setFundingRecipient(address fundingRecipient) external;
        /**
         * @dev Sets royalty amount in bps (basis points).
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param bps The new royalty basis points to be set.
         */
        function setRoyalty(uint16 bps) external;
        /**
         * @dev Sets the edition max mintable range.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
         * @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
         */
        function setEditionMaxMintableRange(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_) external;
        /**
         * @dev Sets the timestamp after which, the `editionMaxMintable` drops
         *      from `editionMaxMintableUpper` to `editionMaxMintableLower.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param editionCutoffTime_ The timestamp.
         */
        function setEditionCutoffTime(uint32 editionCutoffTime_) external;
        /**
         * @dev Sets whether the `mintRandomness` is enabled.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param mintRandomnessEnabled_ The boolean value.
         */
        function setMintRandomnessEnabled(bool mintRandomnessEnabled_) external;
        // =============================================================
        //               PUBLIC / EXTERNAL VIEW FUNCTIONS
        // =============================================================
        /**
         * @dev Returns the edition info.
         * @return editionInfo The latest value.
         */
        function editionInfo() external view returns (EditionInfo memory editionInfo);
        /**
         * @dev Returns the minter role flag.
         * @return The constant value.
         */
        function MINTER_ROLE() external view returns (uint256);
        /**
         * @dev Returns the admin role flag.
         * @return The constant value.
         */
        function ADMIN_ROLE() external view returns (uint256);
        /**
         * @dev Returns the maximum limit for the mint or airdrop `quantity`.
         *      Prevents the first-time transfer costs for tokens near the end of large mint batches
         *      via ERC721A from becoming too expensive due to the need to scan many storage slots.
         *      See: https://chiru-labs.github.io/ERC721A/#/tips?id=batch-size
         * @return The constant value.
         */
        function ADDRESS_BATCH_MINT_LIMIT() external pure returns (uint256);
        /**
         * @dev Returns the bit flag to freeze the metadata on initialization.
         * @return The constant value.
         */
        function METADATA_IS_FROZEN_FLAG() external pure returns (uint8);
        /**
         * @dev Returns the bit flag to enable the mint randomness feature on initialization.
         * @return The constant value.
         */
        function MINT_RANDOMNESS_ENABLED_FLAG() external pure returns (uint8);
        /**
         * @dev Returns the base token URI for the collection.
         * @return The configured value.
         */
        function baseURI() external view returns (string memory);
        /**
         * @dev Returns the contract URI to be used by Opensea.
         *      See: https://docs.opensea.io/docs/contract-level-metadata
         * @return The configured value.
         */
        function contractURI() external view returns (string memory);
        /**
         * @dev Returns the address of the funding recipient.
         * @return The configured value.
         */
        function fundingRecipient() external view returns (address);
        /**
         * @dev Returns the maximum amount of tokens mintable for this edition.
         * @return The configured value.
         */
        function editionMaxMintable() external view returns (uint32);
        /**
         * @dev Returns the upper bound for the maximum tokens that can be minted for this edition.
         * @return The configured value.
         */
        function editionMaxMintableUpper() external view returns (uint32);
        /**
         * @dev Returns the lower bound for the maximum tokens that can be minted for this edition.
         * @return The configured value.
         */
        function editionMaxMintableLower() external view returns (uint32);
        /**
         * @dev Returns the timestamp after which `editionMaxMintable` drops from
         *      `editionMaxMintableUpper` to `editionMaxMintableLower`.
         * @return The configured value.
         */
        function editionCutoffTime() external view returns (uint32);
        /**
         * @dev Returns the address of the metadata module.
         * @return The configured value.
         */
        function metadataModule() external view returns (address);
        /**
         * @dev Returns the randomness based on latest block hash, which is stored upon each mint.
         *      unless {mintConcluded} is true.
         *      Used for game mechanics like the Sound Golden Egg.
         *      Returns 0 before revealed.
         *      WARNING: This value should NOT be used for any reward of significant monetary
         *      value, due to it being computed via a purely on-chain psuedorandom mechanism.
         * @return The latest value.
         */
        function mintRandomness() external view returns (uint256);
        /**
         * @dev Returns whether the `mintRandomness` has been enabled.
         * @return The configured value.
         */
        function mintRandomnessEnabled() external view returns (bool);
        /**
         * @dev Returns whether the mint has been concluded.
         * @return The latest value.
         */
        function mintConcluded() external view returns (bool);
        /**
         * @dev Returns the royalty basis points.
         * @return The configured value.
         */
        function royaltyBPS() external view returns (uint16);
        /**
         * @dev Returns whether the metadata module is frozen.
         * @return The configured value.
         */
        function isMetadataFrozen() external view returns (bool);
        /**
         * @dev Returns the next token ID to be minted.
         * @return The latest value.
         */
        function nextTokenId() external view returns (uint256);
        /**
         * @dev Returns the number of tokens minted by `owner`.
         * @param owner Address to query for number minted.
         * @return The latest value.
         */
        function numberMinted(address owner) external view returns (uint256);
        /**
         * @dev Returns the number of tokens burned by `owner`.
         * @param owner Address to query for number burned.
         * @return The latest value.
         */
        function numberBurned(address owner) external view returns (uint256);
        /**
         * @dev Returns the total amount of tokens minted.
         * @return The latest value.
         */
        function totalMinted() external view returns (uint256);
        /**
         * @dev Returns the total amount of tokens burned.
         * @return The latest value.
         */
        function totalBurned() external view returns (uint256);
        /**
         * @dev Informs other contracts which interfaces this contract supports.
         *      Required by https://eips.ethereum.org/EIPS/eip-165
         * @param interfaceId The interface id to check.
         * @return Whether the `interfaceId` is supported.
         */
        function supportsInterface(bytes4 interfaceId)
            external
            view
            override(IERC721AUpgradeable, IERC165Upgradeable)
            returns (bool);
    }
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    /**
     * @title IMetadataModule
     * @notice The interface for custom metadata modules.
     */
    interface IMetadataModule {
        /**
         * @dev When implemented, SoundEdition's `tokenURI` redirects execution to this `tokenURI`.
         * @param tokenId The token ID to retrieve the token URI for.
         * @return The token URI string.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    /*
                     â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’
                   ▒███████████████████████████████████████████████████████████
                   ▒███████████████████████████████████████████████████████████
     ▒▓▓▓▓▓▓▓▓▓▓▓▓▓████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓               ▒▒▒▒▒▒▒▒▒▒▒▒▒██████████████████████████████
     █████████████████████████████▓                            ▒█████████████████████████████
     █████████████████████████████▓                             ▒████████████████████████████
     █████████████████████████████████████████████████████████▓
     ███████████████████████████████████████████████████████████
     ███████████████████████████████████████████████████████████▒
                                  ███████████████████████████████████████████████████████████▒
                                  ▓██████████████████████████████████████████████████████████▒
                                   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████████████████████▒
     █████████████████████████████                             ▒█████████████████████████████▒
     ██████████████████████████████                            ▒█████████████████████████████▒
     ██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒              ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ▒▒▒▒▒▒▒▒▒▒▒▒▒▒███████████████████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                   ▓██████████████████████████████████████████████████████████▒
                   ▓██████████████████████████████████████████████████████████
    */
    import { Base64 } from "solady/utils/Base64.sol";
    library ArweaveURILib {
        // =============================================================
        //                            STRUCTS
        // =============================================================
        struct URI {
            bytes32 arweave;
            string regular;
        }
        // =============================================================
        //                  INTERNAL / PRIVATE HELPERS
        // =============================================================
        /**
         * @dev Helper function for storing a URI that may be an Arweave URI.
         *      Efficiently stores Arweave CIDs by converting them into a single bytes32 word.
         *      The Arweave CID is a base64 encoded sha-256 output (32 bytes when decoded).
         *      See: https://docs.arweave.org/developers/server/http-api
         * @param uri      The URI storage reference.
         * @param value    The string representation of the URI.
         * @param isUpdate Whether this is called in an update.
         */
        function store(
            URI storage uri,
            string memory value,
            bool isUpdate
        ) internal {
            uint256 valueLength;
            bool isArweave;
            assembly {
                // Example: "ar://Hjtz2YLeVyXQkGxKTNcIYfWkKnHioDvfICulzQIAt3E"
                valueLength := mload(value)
                // If the URI is length 48 or 49 (due to a trailing slash).
                if or(eq(valueLength, 48), eq(valueLength, 49)) {
                    // If starts with "ar://".
                    if eq(and(mload(add(value, 5)), 0xffffffffff), 0x61723a2f2f) {
                        isArweave := 1
                        value := add(value, 5)
                        // Sets the length of the `value` to 43,
                        // such that it only contains the CID.
                        mstore(value, 43)
                    }
                }
            }
            if (isArweave) {
                bytes memory decodedCIDBytes = Base64.decode(value);
                bytes32 arweaveCID;
                assembly {
                    arweaveCID := mload(add(decodedCIDBytes, 0x20))
                    // Restore the "ar://".
                    mstore(value, 0x61723a2f2f)
                    // Restore the original position of the `value` pointer.
                    value := sub(value, 5)
                    // Restore the original length.
                    mstore(value, valueLength)
                }
                uri.arweave = arweaveCID;
                if (isUpdate) delete uri.regular;
            } else {
                uri.regular = value;
                if (isUpdate) delete uri.arweave;
            }
        }
        /**
         * @dev Equivalent to `store(uri, value, false)`.
         * @param uri      The URI storage reference.
         * @param value    The string representation of the URI.
         */
        function initialize(URI storage uri, string memory value) internal {
            store(uri, value, false);
        }
        /**
         * @dev Equivalent to `store(uri, value, true)`.
         * @param uri      The URI storage reference.
         * @param value    The string representation of the URI.
         */
        function update(URI storage uri, string memory value) internal {
            store(uri, value, true);
        }
        /**
         * @dev Helper function for retrieving a URI stored with {_setURI}.
         * @param uri The URI storage reference.
         */
        function load(URI storage uri) internal view returns (string memory) {
            bytes32 arweaveCID = uri.arweave;
            if (arweaveCID == bytes32(0)) {
                return uri.regular;
            }
            bytes memory decoded;
            assembly {
                // Copy `arweaveCID`.
                // First, grab the free memory pointer.
                decoded := mload(0x40)
                // Allocate 2 slots.
                // 1 slot for the length, 1 slot for the bytes.
                mstore(0x40, add(decoded, 0x40))
                mstore(decoded, 0x20) // Set the length (32 bytes).
                mstore(add(decoded, 0x20), arweaveCID) // Set the bytes.
            }
            return string.concat("ar://", Base64.encode(decoded, true, true), "/");
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    library ERC721AStorage {
        // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
        struct TokenApprovalRef {
            address value;
        }
        struct Layout {
            // =============================================================
            //                            STORAGE
            // =============================================================
            // The next token ID to be minted.
            uint256 _currentIndex;
            // The number of tokens burned.
            uint256 _burnCounter;
            // Token name
            string _name;
            // Token symbol
            string _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) _packedOwnerships;
            // Mapping owner address to address data.
            //
            // Bits Layout:
            // - [0..63]    `balance`
            // - [64..127]  `numberMinted`
            // - [128..191] `numberBurned`
            // - [192..255] `aux`
            mapping(address => uint256) _packedAddressData;
            // Mapping from token ID to approved address.
            mapping(uint256 => ERC721AStorage.TokenApprovalRef) _tokenApprovals;
            // Mapping from owner to operator approvals
            mapping(address => mapping(address => bool)) _operatorApprovals;
        }
        bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.ERC721A');
        function layout() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev This is a base contract to aid in writing upgradeable diamond facet contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     */
    import {ERC721A__InitializableStorage} from './ERC721A__InitializableStorage.sol';
    abstract contract ERC721A__Initializable {
        using ERC721A__InitializableStorage for ERC721A__InitializableStorage.Layout;
        /**
         * @dev Modifier to protect an initializer function from being invoked twice.
         */
        modifier initializerERC721A() {
            // If the contract is initializing we ignore whether _initialized is set in order to support multiple
            // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
            // contract may have been reentered.
            require(
                ERC721A__InitializableStorage.layout()._initializing
                    ? _isConstructor()
                    : !ERC721A__InitializableStorage.layout()._initialized,
                'ERC721A__Initializable: contract is already initialized'
            );
            bool isTopLevelCall = !ERC721A__InitializableStorage.layout()._initializing;
            if (isTopLevelCall) {
                ERC721A__InitializableStorage.layout()._initializing = true;
                ERC721A__InitializableStorage.layout()._initialized = true;
            }
            _;
            if (isTopLevelCall) {
                ERC721A__InitializableStorage.layout()._initializing = false;
            }
        }
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} modifier, directly or indirectly.
         */
        modifier onlyInitializingERC721A() {
            require(
                ERC721A__InitializableStorage.layout()._initializing,
                'ERC721A__Initializable: contract is not initializing'
            );
            _;
        }
        /// @dev Returns true if and only if the function is running in the constructor
        function _isConstructor() private view returns (bool) {
            // extcodesize checks the size of the code stored in an address, and
            // address returns the current address. Since the code is still not
            // deployed when running a constructor, any checks on its code size will
            // yield zero, making it an effective way to detect if a contract is
            // under construction or not.
            address self = address(this);
            uint256 cs;
            assembly {
                cs := extcodesize(self)
            }
            return cs == 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import '../IERC721AUpgradeable.sol';
    /**
     * @dev Interface of ERC721AQueryable.
     */
    interface IERC721AQueryableUpgradeable is IERC721AUpgradeable {
        /**
         * Invalid query range (`start` >= `stop`).
         */
        error InvalidQueryRange();
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view returns (uint256[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view returns (uint256[] memory);
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import '../IERC721AUpgradeable.sol';
    /**
     * @dev Interface of ERC721ABurnable.
     */
    interface IERC721ABurnableUpgradeable is IERC721AUpgradeable {
        /**
         * @dev Burns `tokenId`. See {ERC721A-_burn}.
         *
         * Requirements:
         *
         * - The caller must own `tokenId` or be an approved operator.
         */
        function burn(uint256 tokenId) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165Upgradeable {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Library to encode strings in Base64.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
    /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>.
    library Base64 {
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// See: https://datatracker.ietf.org/doc/html/rfc4648
        /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
        /// @param noPadding Whether to strip away the padding.
        function encode(
            bytes memory data,
            bool fileSafe,
            bool noPadding
        ) internal pure returns (string memory result) {
            assembly {
                let dataLength := mload(data)
                if dataLength {
                    // Multiply by 4/3 rounded up.
                    // The `shl(2, ...)` is equivalent to multiplying by 4.
                    let encodedLength := shl(2, div(add(dataLength, 2), 3))
                    // Set `result` to point to the start of the free memory.
                    result := mload(0x40)
                    // Store the table into the scratch space.
                    // Offsetted by -1 byte so that the `mload` will load the character.
                    // We will rewrite the free memory pointer at `0x40` later with
                    // the allocated size.
                    // The magic constant 0x0230 will translate "-_" + "+/".
                    mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                    mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230)))
                    // Skip the first slot, which stores the length.
                    let ptr := add(result, 0x20)
                    let end := add(ptr, encodedLength)
                    // Run over the input, 3 bytes at a time.
                    // prettier-ignore
                    for {} 1 {} {
                        data := add(data, 3) // Advance 3 bytes.
                        let input := mload(data)
                        // Write 4 bytes. Optimized for fewer stack operations.
                        mstore8(    ptr    , mload(and(shr(18, input), 0x3F)))
                        mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
                        mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
                        mstore8(add(ptr, 3), mload(and(        input , 0x3F)))
                        
                        ptr := add(ptr, 4) // Advance 4 bytes.
                        // prettier-ignore
                        if iszero(lt(ptr, end)) { break }
                    }
                    let r := mod(dataLength, 3)
                    switch noPadding
                    case 0 {
                        // Offset `ptr` and pad with '='. We can simply write over the end.
                        mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.
                        mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
                        // Write the length of the string.
                        mstore(result, encodedLength)
                    }
                    default {
                        // Write the length of the string.
                        mstore(result, sub(encodedLength, add(iszero(iszero(r)), eq(r, 1))))
                    }
                    // Allocate the memory for the string.
                    // Add 31 and mask with `not(31)` to round the
                    // free memory pointer up the next multiple of 32.
                    mstore(0x40, and(add(end, 31), not(31)))
                }
            }
        }
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// Equivalent to `encode(data, false, false)`.
        function encode(bytes memory data) internal pure returns (string memory result) {
            result = encode(data, false, false);
        }
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// Equivalent to `encode(data, fileSafe, false)`.
        function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) {
            result = encode(data, fileSafe, false);
        }
        /// @dev Encodes base64 encoded `data`.
        ///
        /// Supports:
        /// - RFC 4648 (both standard and file-safe mode).
        /// - RFC 3501 (63: ',').
        ///
        /// Does not support:
        /// - Line breaks.
        ///
        /// Note: For performance reasons,
        /// this function will NOT revert on invalid `data` inputs.
        /// Outputs for invalid inputs will simply be undefined behaviour.
        /// It is the user's responsibility to ensure that the `data`
        /// is a valid base64 encoded string.
        function decode(string memory data) internal pure returns (bytes memory result) {
            assembly {
                let dataLength := mload(data)
                if dataLength {
                    let end := add(data, dataLength)
                    let decodedLength := mul(shr(2, dataLength), 3)
                    switch and(dataLength, 3)
                    case 0 {
                        // If padded.
                        decodedLength := sub(
                            decodedLength,
                            add(eq(and(mload(end), 0xFF), 0x3d), eq(and(mload(end), 0xFFFF), 0x3d3d))
                        )
                    }
                    default {
                        // If non-padded.
                        decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                    }
                    result := mload(0x40)
                    // Write the length of the string.
                    mstore(result, decodedLength)
                    // Skip the first slot, which stores the length.
                    let ptr := add(result, 0x20)
                    // Load the table into the scratch space.
                    // Constants are optimized for smaller bytecode with zero gas overhead.
                    // `m` also doubles as the mask of the upper 6 bits.
                    let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                    mstore(0x5b, m)
                    mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                    mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)
                    // prettier-ignore
                    for {} 1 {} {
                        // Read 4 bytes.
                        data := add(data, 4)
                        let input := mload(data)
                        // Write 3 bytes.
                        mstore(ptr, or(
                            and(m, mload(byte(28, input))),
                            shr(6, or(
                                and(m, mload(byte(29, input))),
                                shr(6, or(
                                    and(m, mload(byte(30, input))),
                                    shr(6, mload(byte(31, input)))
                                ))
                            ))
                        ))
                        ptr := add(ptr, 3)
                        
                        // prettier-ignore
                        if iszero(lt(data, end)) { break }
                    }
                    // Allocate the memory for the string.
                    // Add 32 + 31 and mask with `not(31)` to round the
                    // free memory pointer up the next multiple of 32.
                    mstore(0x40, and(add(add(result, decodedLength), 63), not(31)))
                    // Restore the zero slot.
                    mstore(0x60, 0)
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev This is a base storage for the  initialization function for upgradeable diamond facet contracts
     **/
    library ERC721A__InitializableStorage {
        struct Layout {
            /*
             * Indicates that the contract has been initialized.
             */
            bool _initialized;
            /*
             * Indicates that the contract is in the process of being initialized.
             */
            bool _initializing;
        }
        bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.initializable.facet');
        function layout() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }
    

    File 2 of 2: SoundEditionV1
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    /*
                     â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’
                   ▒███████████████████████████████████████████████████████████
                   ▒███████████████████████████████████████████████████████████
     ▒▓▓▓▓▓▓▓▓▓▓▓▓▓████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓               ▒▒▒▒▒▒▒▒▒▒▒▒▒██████████████████████████████
     █████████████████████████████▓                            ▒█████████████████████████████
     █████████████████████████████▓                             ▒████████████████████████████
     █████████████████████████████████████████████████████████▓
     ███████████████████████████████████████████████████████████
     ███████████████████████████████████████████████████████████▒
                                  ███████████████████████████████████████████████████████████▒
                                  ▓██████████████████████████████████████████████████████████▒
                                   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████████████████████▒
     █████████████████████████████                             ▒█████████████████████████████▒
     ██████████████████████████████                            ▒█████████████████████████████▒
     ██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒              ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ▒▒▒▒▒▒▒▒▒▒▒▒▒▒███████████████████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                   ▓██████████████████████████████████████████████████████████▒
                   ▓██████████████████████████████████████████████████████████
    */
    import { IERC721AUpgradeable } from "chiru-labs/ERC721A-Upgradeable/IERC721AUpgradeable.sol";
    import { ERC721AUpgradeable, ERC721AStorage } from "chiru-labs/ERC721A-Upgradeable/ERC721AUpgradeable.sol";
    import { ERC721AQueryableUpgradeable } from "chiru-labs/ERC721A-Upgradeable/extensions/ERC721AQueryableUpgradeable.sol";
    import { ERC721ABurnableUpgradeable } from "chiru-labs/ERC721A-Upgradeable/extensions/ERC721ABurnableUpgradeable.sol";
    import { IERC20 } from "openzeppelin/token/ERC20/IERC20.sol";
    import { IERC2981Upgradeable } from "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol";
    import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
    import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol";
    import { OwnableRoles } from "solady/auth/OwnableRoles.sol";
    import { ISoundEditionV1, EditionInfo } from "./interfaces/ISoundEditionV1.sol";
    import { IMetadataModule } from "./interfaces/IMetadataModule.sol";
    import { ArweaveURILib } from "./utils/ArweaveURILib.sol";
    /**
     * @title SoundEditionV1
     * @notice The Sound Edition contract - a creator-owned, modifiable implementation of ERC721A.
     */
    contract SoundEditionV1 is ISoundEditionV1, ERC721AQueryableUpgradeable, ERC721ABurnableUpgradeable, OwnableRoles {
        using ArweaveURILib for ArweaveURILib.URI;
        // =============================================================
        //                           CONSTANTS
        // =============================================================
        /**
         * @dev A role every minter module must have in order to mint new tokens.
         */
        uint256 public constant MINTER_ROLE = _ROLE_1;
        /**
         * @dev A role the owner can grant for performing admin actions.
         */
        uint256 public constant ADMIN_ROLE = _ROLE_0;
        /**
         * @dev The maximum limit for the mint or airdrop `quantity`.
         *      Prevents the first-time transfer costs for tokens near the end of large mint batches
         *      via ERC721A from becoming too expensive due to the need to scan many storage slots.
         *      See: https://chiru-labs.github.io/ERC721A/#/tips?id=batch-size
         */
        uint256 public constant ADDRESS_BATCH_MINT_LIMIT = 255;
        /**
         * @dev Basis points denominator used in fee calculations.
         */
        uint16 internal constant _MAX_BPS = 10_000;
        /**
         * @dev The interface ID for EIP-2981 (royaltyInfo)
         */
        bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
        /**
         * @dev The boolean flag on whether the metadata is frozen.
         */
        uint8 public constant METADATA_IS_FROZEN_FLAG = 1 << 0;
        /**
         * @dev The boolean flag on whether the `mintRandomness` is enabled.
         */
        uint8 public constant MINT_RANDOMNESS_ENABLED_FLAG = 1 << 1;
        // =============================================================
        //                            STORAGE
        // =============================================================
        /**
         * @dev The value for `name` and `symbol` if their combined
         *      length is (32 - 2) bytes. We need 2 bytes for their lengths.
         */
        bytes32 private _shortNameAndSymbol;
        /**
         * @dev The metadata's base URI.
         */
        ArweaveURILib.URI private _baseURIStorage;
        /**
         * @dev The contract base URI.
         */
        ArweaveURILib.URI private _contractURIStorage;
        /**
         * @dev The destination for ETH withdrawals.
         */
        address public fundingRecipient;
        /**
         * @dev The upper bound of the max mintable quantity for the edition.
         */
        uint32 public editionMaxMintableUpper;
        /**
         * @dev The lower bound for the maximum tokens that can be minted for this edition.
         */
        uint32 public editionMaxMintableLower;
        /**
         * @dev The timestamp after which `editionMaxMintable` drops from
         *      `editionMaxMintableUpper` to `max(_totalMinted(), editionMaxMintableLower)`.
         */
        uint32 public editionCutoffTime;
        /**
         * @dev Metadata module used for `tokenURI` and `contractURI` if it is set.
         */
        address public metadataModule;
        /**
         * @dev The randomness based on latest block hash, which is stored upon each mint
         *      unless `randomnessLockedAfterMinted` or `randomnessLockedTimestamp` have been surpassed.
         *      Used for game mechanics like the Sound Golden Egg.
         */
        bytes9 private _mintRandomness;
        /**
         * @dev The royalty fee in basis points.
         */
        uint16 public royaltyBPS;
        /**
         * @dev Packed boolean flags.
         */
        uint8 private _flags;
        // =============================================================
        //               PUBLIC / EXTERNAL WRITE FUNCTIONS
        // =============================================================
        /**
         * @inheritdoc ISoundEditionV1
         */
        function initialize(
            string memory name_,
            string memory symbol_,
            address metadataModule_,
            string memory baseURI_,
            string memory contractURI_,
            address fundingRecipient_,
            uint16 royaltyBPS_,
            uint32 editionMaxMintableLower_,
            uint32 editionMaxMintableUpper_,
            uint32 editionCutoffTime_,
            uint8 flags_
        ) external onlyValidRoyaltyBPS(royaltyBPS_) {
            // Prevent double initialization.
            // We can "cheat" here and avoid the initializer modifer to save a SSTORE,
            // since the `_nextTokenId()` is defined to always return 1.
            if (_nextTokenId() != 0) revert Unauthorized();
            if (fundingRecipient_ == address(0)) revert InvalidFundingRecipient();
            if (editionMaxMintableLower_ > editionMaxMintableUpper_) revert InvalidEditionMaxMintableRange();
            _initializeNameAndSymbol(name_, symbol_);
            ERC721AStorage.layout()._currentIndex = _startTokenId();
            _initializeOwner(msg.sender);
            _baseURIStorage.initialize(baseURI_);
            _contractURIStorage.initialize(contractURI_);
            fundingRecipient = fundingRecipient_;
            editionMaxMintableUpper = editionMaxMintableUpper_;
            editionMaxMintableLower = editionMaxMintableLower_;
            editionCutoffTime = editionCutoffTime_;
            _flags = flags_;
            metadataModule = metadataModule_;
            royaltyBPS = royaltyBPS_;
            emit SoundEditionInitialized(
                address(this),
                name_,
                symbol_,
                metadataModule_,
                baseURI_,
                contractURI_,
                fundingRecipient_,
                royaltyBPS_,
                editionMaxMintableLower_,
                editionMaxMintableUpper_,
                editionCutoffTime_,
                flags_
            );
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mint(address to, uint256 quantity)
            external
            payable
            onlyRolesOrOwner(ADMIN_ROLE | MINTER_ROLE)
            requireWithinAddressBatchMintLimit(quantity)
            requireMintable(quantity)
            updatesMintRandomness
            returns (uint256 fromTokenId)
        {
            fromTokenId = _nextTokenId();
            // Mint the tokens. Will revert if `quantity` is zero.
            _mint(to, quantity);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function airdrop(address[] calldata to, uint256 quantity)
            external
            onlyRolesOrOwner(ADMIN_ROLE)
            requireWithinAddressBatchMintLimit(quantity)
            requireMintable(to.length * quantity)
            updatesMintRandomness
            returns (uint256 fromTokenId)
        {
            if (to.length == 0) revert NoAddressesToAirdrop();
            fromTokenId = _nextTokenId();
            // Won't overflow, as `to.length` is bounded by the block max gas limit.
            unchecked {
                uint256 toLength = to.length;
                // Mint the tokens. Will revert if `quantity` is zero.
                for (uint256 i; i != toLength; ++i) {
                    _mint(to[i], quantity);
                }
            }
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function withdrawETH() external {
            SafeTransferLib.safeTransferETH(fundingRecipient, address(this).balance);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function withdrawERC20(address[] calldata tokens) external {
            unchecked {
                uint256 n = tokens.length;
                for (uint256 i; i != n; ++i) {
                    SafeTransferLib.safeTransfer(tokens[i], fundingRecipient, IERC20(tokens[i]).balanceOf(address(this)));
                }
            }
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setMetadataModule(address metadataModule_) external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            metadataModule = metadataModule_;
            emit MetadataModuleSet(metadataModule_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setBaseURI(string memory baseURI_) external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            _baseURIStorage.update(baseURI_);
            emit BaseURISet(baseURI_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setContractURI(string memory contractURI_) external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            _contractURIStorage.update(contractURI_);
            emit ContractURISet(contractURI_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function freezeMetadata() external onlyRolesOrOwner(ADMIN_ROLE) onlyMetadataNotFrozen {
            _flags |= METADATA_IS_FROZEN_FLAG;
            emit MetadataFrozen(metadataModule, baseURI(), contractURI());
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setFundingRecipient(address fundingRecipient_) external onlyRolesOrOwner(ADMIN_ROLE) {
            if (fundingRecipient_ == address(0)) revert InvalidFundingRecipient();
            fundingRecipient = fundingRecipient_;
            emit FundingRecipientSet(fundingRecipient_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setRoyalty(uint16 royaltyBPS_) external onlyRolesOrOwner(ADMIN_ROLE) onlyValidRoyaltyBPS(royaltyBPS_) {
            royaltyBPS = royaltyBPS_;
            emit RoyaltySet(royaltyBPS_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setEditionMaxMintableRange(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_)
            external
            onlyRolesOrOwner(ADMIN_ROLE)
        {
            if (mintConcluded()) revert MintHasConcluded();
            uint32 currentTotalMinted = uint32(_totalMinted());
            if (currentTotalMinted != 0) {
                editionMaxMintableLower_ = uint32(FixedPointMathLib.max(editionMaxMintableLower_, currentTotalMinted));
                editionMaxMintableUpper_ = uint32(FixedPointMathLib.max(editionMaxMintableUpper_, currentTotalMinted));
                // If the upper bound is larger than the current stored value, revert.
                if (editionMaxMintableUpper_ > editionMaxMintableUpper) revert InvalidEditionMaxMintableRange();
            }
            // If the lower bound is larger than the upper bound, revert.
            if (editionMaxMintableLower_ > editionMaxMintableUpper_) revert InvalidEditionMaxMintableRange();
            editionMaxMintableLower = editionMaxMintableLower_;
            editionMaxMintableUpper = editionMaxMintableUpper_;
            emit EditionMaxMintableRangeSet(editionMaxMintableLower, editionMaxMintableUpper);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setEditionCutoffTime(uint32 editionCutoffTime_) external onlyRolesOrOwner(ADMIN_ROLE) {
            if (mintConcluded()) revert MintHasConcluded();
            editionCutoffTime = editionCutoffTime_;
            emit EditionCutoffTimeSet(editionCutoffTime_);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function setMintRandomnessEnabled(bool mintRandomnessEnabled_) external onlyRolesOrOwner(ADMIN_ROLE) {
            if (_totalMinted() != 0) revert MintsAlreadyExist();
            if (mintRandomnessEnabled() != mintRandomnessEnabled_) {
                _flags ^= MINT_RANDOMNESS_ENABLED_FLAG;
            }
            emit MintRandomnessEnabledSet(mintRandomnessEnabled_);
        }
        // =============================================================
        //               PUBLIC / EXTERNAL VIEW FUNCTIONS
        // =============================================================
        /**
         * @inheritdoc ISoundEditionV1
         */
        function editionInfo() external view returns (EditionInfo memory info) {
            info.baseURI = baseURI();
            info.contractURI = contractURI();
            info.name = name();
            info.symbol = symbol();
            info.fundingRecipient = fundingRecipient;
            info.editionMaxMintable = editionMaxMintable();
            info.editionMaxMintableUpper = editionMaxMintableUpper;
            info.editionMaxMintableLower = editionMaxMintableLower;
            info.editionCutoffTime = editionCutoffTime;
            info.metadataModule = metadataModule;
            info.mintRandomness = mintRandomness();
            info.royaltyBPS = royaltyBPS;
            info.mintRandomnessEnabled = mintRandomnessEnabled();
            info.mintConcluded = mintConcluded();
            info.isMetadataFrozen = isMetadataFrozen();
            info.nextTokenId = nextTokenId();
            info.totalMinted = totalMinted();
            info.totalBurned = totalBurned();
            info.totalSupply = totalSupply();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mintRandomness() public view returns (uint256) {
            if (mintConcluded() && mintRandomnessEnabled()) {
                return uint256(keccak256(abi.encode(_mintRandomness, address(this))));
            }
            return 0;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function editionMaxMintable() public view returns (uint32) {
            if (block.timestamp < editionCutoffTime) {
                return editionMaxMintableUpper;
            } else {
                return uint32(FixedPointMathLib.max(editionMaxMintableLower, _totalMinted()));
            }
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function isMetadataFrozen() public view returns (bool) {
            return _flags & METADATA_IS_FROZEN_FLAG != 0;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mintRandomnessEnabled() public view returns (bool) {
            return _flags & MINT_RANDOMNESS_ENABLED_FLAG != 0;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function mintConcluded() public view returns (bool) {
            return _totalMinted() == editionMaxMintable();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function nextTokenId() public view returns (uint256) {
            return _nextTokenId();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function numberMinted(address owner) external view returns (uint256) {
            return _numberMinted(owner);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function numberBurned(address owner) external view returns (uint256) {
            return _numberBurned(owner);
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function totalMinted() public view returns (uint256) {
            return _totalMinted();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function totalBurned() public view returns (uint256) {
            return _totalBurned();
        }
        /**
         * @inheritdoc IERC721AUpgradeable
         */
        function tokenURI(uint256 tokenId)
            public
            view
            override(ERC721AUpgradeable, IERC721AUpgradeable)
            returns (string memory)
        {
            if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
            if (metadataModule != address(0)) {
                return IMetadataModule(metadataModule).tokenURI(tokenId);
            }
            string memory baseURI_ = baseURI();
            return bytes(baseURI_).length != 0 ? string.concat(baseURI_, _toString(tokenId)) : "";
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function supportsInterface(bytes4 interfaceId)
            public
            view
            override(ISoundEditionV1, ERC721AUpgradeable, IERC721AUpgradeable)
            returns (bool)
        {
            return
                interfaceId == type(ISoundEditionV1).interfaceId ||
                ERC721AUpgradeable.supportsInterface(interfaceId) ||
                interfaceId == _INTERFACE_ID_ERC2981 ||
                interfaceId == this.supportsInterface.selector;
        }
        /**
         * @inheritdoc IERC2981Upgradeable
         */
        function royaltyInfo(
            uint256, // tokenId
            uint256 salePrice
        ) external view override(IERC2981Upgradeable) returns (address fundingRecipient_, uint256 royaltyAmount) {
            fundingRecipient_ = fundingRecipient;
            royaltyAmount = (salePrice * royaltyBPS) / _MAX_BPS;
        }
        /**
         * @inheritdoc IERC721AUpgradeable
         */
        function name() public view override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) {
            (string memory name_, ) = _loadNameAndSymbol();
            return name_;
        }
        /**
         * @inheritdoc IERC721AUpgradeable
         */
        function symbol() public view override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) {
            (, string memory symbol_) = _loadNameAndSymbol();
            return symbol_;
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function baseURI() public view returns (string memory) {
            return _baseURIStorage.load();
        }
        /**
         * @inheritdoc ISoundEditionV1
         */
        function contractURI() public view returns (string memory) {
            return _contractURIStorage.load();
        }
        // =============================================================
        //                  INTERNAL / PRIVATE HELPERS
        // =============================================================
        /**
         * @inheritdoc ERC721AUpgradeable
         */
        function _startTokenId() internal pure override returns (uint256) {
            return 1;
        }
        /**
         * @dev Ensures the royalty basis points is a valid value.
         * @param bps The royalty BPS.
         */
        modifier onlyValidRoyaltyBPS(uint16 bps) {
            if (bps > _MAX_BPS) revert InvalidRoyaltyBPS();
            _;
        }
        /**
         * @dev Reverts if the metadata is frozen.
         */
        modifier onlyMetadataNotFrozen() {
            // Inlined to save gas.
            if (_flags & METADATA_IS_FROZEN_FLAG != 0) revert MetadataIsFrozen();
            _;
        }
        /**
         * @dev Ensures that `totalQuantity` can be minted.
         * @param totalQuantity The total number of tokens to mint.
         */
        modifier requireMintable(uint256 totalQuantity) {
            unchecked {
                uint256 currentTotalMinted = _totalMinted();
                uint256 currentEditionMaxMintable = editionMaxMintable();
                // Check if there are enough tokens to mint.
                // We use version v4.2+ of ERC721A, which `_mint` will revert with out-of-gas
                // error via a loop if `totalQuantity` is large enough to cause an overflow in uint256.
                if (currentTotalMinted + totalQuantity > currentEditionMaxMintable) {
                    // Won't underflow.
                    //
                    // `currentTotalMinted`, which is `_totalMinted()`,
                    // will return either `editionMaxMintableUpper`
                    // or `max(editionMaxMintableLower, _totalMinted())`.
                    //
                    // We have the following invariants:
                    // - `editionMaxMintableUpper >= _totalMinted()`
                    // - `max(editionMaxMintableLower, _totalMinted()) >= _totalMinted()`
                    uint256 available = currentEditionMaxMintable - currentTotalMinted;
                    revert ExceedsEditionAvailableSupply(uint32(available));
                }
            }
            _;
        }
        /**
         * @dev Ensures that the `quantity` does not exceed `ADDRESS_BATCH_MINT_LIMIT`.
         * @param quantity The number of tokens minted per address.
         */
        modifier requireWithinAddressBatchMintLimit(uint256 quantity) {
            if (quantity > ADDRESS_BATCH_MINT_LIMIT) revert ExceedsAddressBatchMintLimit();
            _;
        }
        /**
         * @dev Updates the mint randomness.
         */
        modifier updatesMintRandomness() {
            if (mintRandomnessEnabled() && !mintConcluded()) {
                bytes32 randomness = _mintRandomness;
                assembly {
                    // Pick any of the last 256 blocks psuedorandomly for the blockhash.
                    // Store the blockhash, the current `randomness` and the `coinbase()`
                    // into the scratch space.
                    mstore(0x00, blockhash(sub(number(), add(1, byte(0, randomness)))))
                    // `randomness` is left-aligned.
                    // `coinbase()` is right-aligned.
                    // `difficulty()` is right-aligned.
                    // After the merge, if [EIP-4399](https://eips.ethereum.org/EIPS/eip-4399)
                    // is implemented, the randomness will be determined by the beacon chain.
                    mstore(0x20, xor(randomness, xor(coinbase(), difficulty())))
                    // Compute the new `randomness` by hashing the scratch space.
                    randomness := keccak256(0x00, 0x40)
                }
                _mintRandomness = bytes9(randomness);
            }
            _;
        }
        /**
         * @dev Helper function for initializing the name and symbol,
         *      packing them into a single word if possible.
         * @param name_   Name of the collection.
         * @param symbol_ Symbol of the collection.
         */
        function _initializeNameAndSymbol(string memory name_, string memory symbol_) internal {
            // Overflow impossible since max block gas limit bounds the length of the strings.
            unchecked {
                uint256 nameLength = bytes(name_).length;
                uint256 symbolLength = bytes(symbol_).length;
                uint256 totalLength = nameLength + symbolLength;
                // If we cannot pack both strings into a single 32-byte word, store separately.
                // We need 2 bytes to store their lengths.
                if (totalLength > 32 - 2) {
                    ERC721AStorage.layout()._name = name_;
                    ERC721AStorage.layout()._symbol = symbol_;
                    return;
                }
                // Otherwise, pack them and store them into a single word.
                _shortNameAndSymbol = bytes32(abi.encodePacked(uint8(nameLength), name_, uint8(symbolLength), symbol_));
            }
        }
        /**
         * @dev Helper function for retrieving the name and symbol,
         *      unpacking them from a single word in storage if previously packed.
         * @return name_   Name of the collection.
         * @return symbol_ Symbol of the collection.
         */
        function _loadNameAndSymbol() internal view returns (string memory name_, string memory symbol_) {
            // Overflow impossible since max block gas limit bounds the length of the strings.
            unchecked {
                bytes32 packed = _shortNameAndSymbol;
                // If the strings have been previously packed.
                if (packed != bytes32(0)) {
                    // Allocate the bytes.
                    bytes memory nameBytes = new bytes(uint8(packed[0]));
                    bytes memory symbolBytes = new bytes(uint8(packed[1 + nameBytes.length]));
                    // Copy the bytes.
                    for (uint256 i; i < nameBytes.length; ++i) {
                        nameBytes[i] = bytes1(packed[1 + i]);
                    }
                    for (uint256 i; i < symbolBytes.length; ++i) {
                        symbolBytes[i] = bytes1(packed[2 + nameBytes.length + i]);
                    }
                    // Cast the bytes.
                    name_ = string(nameBytes);
                    symbol_ = string(symbolBytes);
                } else {
                    // Otherwise, load them from their separate variables.
                    name_ = ERC721AStorage.layout()._name;
                    symbol_ = ERC721AStorage.layout()._symbol;
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    /**
     * @dev Interface of ERC721A.
     */
    interface IERC721AUpgradeable {
        /**
         * The caller must own the token or be an approved operator.
         */
        error ApprovalCallerNotOwnerNorApproved();
        /**
         * The token does not exist.
         */
        error ApprovalQueryForNonexistentToken();
        /**
         * Cannot query the balance for the zero address.
         */
        error BalanceQueryForZeroAddress();
        /**
         * Cannot mint to the zero address.
         */
        error MintToZeroAddress();
        /**
         * The quantity of tokens minted must be more than zero.
         */
        error MintZeroQuantity();
        /**
         * The token does not exist.
         */
        error OwnerQueryForNonexistentToken();
        /**
         * The caller must own the token or be an approved operator.
         */
        error TransferCallerNotOwnerNorApproved();
        /**
         * The token must be owned by `from`.
         */
        error TransferFromIncorrectOwner();
        /**
         * Cannot safely transfer to a contract that does not implement the
         * ERC721Receiver interface.
         */
        error TransferToNonERC721ReceiverImplementer();
        /**
         * Cannot transfer to the zero address.
         */
        error TransferToZeroAddress();
        /**
         * The token does not exist.
         */
        error URIQueryForNonexistentToken();
        /**
         * The `quantity` minted with ERC2309 exceeds the safety limit.
         */
        error MintERC2309QuantityExceedsLimit();
        /**
         * The `extraData` cannot be set on an unintialized ownership slot.
         */
        error OwnershipNotInitializedForExtraData();
        // =============================================================
        //                            STRUCTS
        // =============================================================
        struct TokenOwnership {
            // The address of the owner.
            address addr;
            // Stores the start time of ownership with minimal overhead for tokenomics.
            uint64 startTimestamp;
            // Whether the token has been burned.
            bool burned;
            // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
            uint24 extraData;
        }
        // =============================================================
        //                         TOKEN COUNTERS
        // =============================================================
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() external view returns (uint256);
        // =============================================================
        //                            IERC165
        // =============================================================
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
        // =============================================================
        //                            IERC721
        // =============================================================
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables
         * (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`,
         * checking first that contract recipients are aware of the ERC721 protocol
         * to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move
         * this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @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;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the
         * zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom}
         * for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
        // =============================================================
        //                           IERC2309
        // =============================================================
        /**
         * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
         * (inclusive) is transferred from `from` to `to`, as defined in the
         * [ERC2309](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);
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721AUpgradeable.sol';
    import {ERC721AStorage} from './ERC721AStorage.sol';
    import './ERC721A__Initializable.sol';
    /**
     * @dev Interface of ERC721 token receiver.
     */
    interface ERC721A__IERC721ReceiverUpgradeable {
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    /**
     * @title ERC721A
     *
     * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
     * Non-Fungible Token Standard, including the Metadata extension.
     * Optimized for lower gas during batch mints.
     *
     * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
     * starting from `_startTokenId()`.
     *
     * Assumptions:
     *
     * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
     * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
     */
    contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable {
        using ERC721AStorage for ERC721AStorage.Layout;
        // =============================================================
        //                           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;
        // =============================================================
        //                          CONSTRUCTOR
        // =============================================================
        function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
            __ERC721A_init_unchained(name_, symbol_);
        }
        function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
            ERC721AStorage.layout()._name = name_;
            ERC721AStorage.layout()._symbol = symbol_;
            ERC721AStorage.layout()._currentIndex = _startTokenId();
        }
        // =============================================================
        //                   TOKEN COUNTING OPERATIONS
        // =============================================================
        /**
         * @dev Returns the starting token ID.
         * To change the starting token ID, please override this function.
         */
        function _startTokenId() internal view virtual returns (uint256) {
            return 0;
        }
        /**
         * @dev Returns the next token ID to be minted.
         */
        function _nextTokenId() internal view virtual returns (uint256) {
            return ERC721AStorage.layout()._currentIndex;
        }
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            // Counter underflow is impossible as _burnCounter cannot be incremented
            // more than `_currentIndex - _startTokenId()` times.
            unchecked {
                return ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId();
            }
        }
        /**
         * @dev Returns the total amount of tokens minted in the contract.
         */
        function _totalMinted() internal view virtual returns (uint256) {
            // Counter underflow is impossible as `_currentIndex` does not decrement,
            // and it is initialized to `_startTokenId()`.
            unchecked {
                return ERC721AStorage.layout()._currentIndex - _startTokenId();
            }
        }
        /**
         * @dev Returns the total number of tokens burned.
         */
        function _totalBurned() internal view virtual returns (uint256) {
            return ERC721AStorage.layout()._burnCounter;
        }
        // =============================================================
        //                    ADDRESS DATA OPERATIONS
        // =============================================================
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            if (owner == address(0)) revert BalanceQueryForZeroAddress();
            return ERC721AStorage.layout()._packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
        }
        /**
         * Returns the number of tokens minted by `owner`.
         */
        function _numberMinted(address owner) internal view returns (uint256) {
            return
                (ERC721AStorage.layout()._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
                (ERC721AStorage.layout()._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(ERC721AStorage.layout()._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 = ERC721AStorage.layout()._packedAddressData[owner];
            uint256 auxCasted;
            // Cast `aux` with assembly to avoid redundant masking.
            assembly {
                auxCasted := aux
            }
            packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
            ERC721AStorage.layout()._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 ERC721AStorage.layout()._name;
        }
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() public view virtual override returns (string memory) {
            return ERC721AStorage.layout()._symbol;
        }
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
            string memory baseURI = _baseURI();
            return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
        }
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, it can be overridden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return '';
        }
        // =============================================================
        //                     OWNERSHIPS OPERATIONS
        // =============================================================
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            return address(uint160(_packedOwnershipOf(tokenId)));
        }
        /**
         * @dev Gas spent here starts off proportional to the maximum mint batch size.
         * It gradually moves to O(1) as tokens get transferred around over time.
         */
        function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(_packedOwnershipOf(tokenId));
        }
        /**
         * @dev Returns the unpacked `TokenOwnership` struct at `index`.
         */
        function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(ERC721AStorage.layout()._packedOwnerships[index]);
        }
        /**
         * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
         */
        function _initializeOwnershipAt(uint256 index) internal virtual {
            if (ERC721AStorage.layout()._packedOwnerships[index] == 0) {
                ERC721AStorage.layout()._packedOwnerships[index] = _packedOwnershipOf(index);
            }
        }
        /**
         * Returns the packed ownership data of `tokenId`.
         */
        function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
            uint256 curr = tokenId;
            unchecked {
                if (_startTokenId() <= curr)
                    if (curr < ERC721AStorage.layout()._currentIndex) {
                        uint256 packed = ERC721AStorage.layout()._packedOwnerships[curr];
                        // If not burned.
                        if (packed & _BITMASK_BURNED == 0) {
                            // Invariant:
                            // There will always be an initialized ownership slot
                            // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                            // before an unintialized ownership slot
                            // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                            // Hence, `curr` will not underflow.
                            //
                            // We can directly compare the packed value.
                            // If the address is zero, packed will be zero.
                            while (packed == 0) {
                                packed = ERC721AStorage.layout()._packedOwnerships[--curr];
                            }
                            return packed;
                        }
                    }
            }
            revert OwnerQueryForNonexistentToken();
        }
        /**
         * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
         */
        function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
            ownership.addr = address(uint160(packed));
            ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
            ownership.burned = packed & _BITMASK_BURNED != 0;
            ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
        }
        /**
         * @dev Packs ownership data into a single uint256.
         */
        function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
            }
        }
        /**
         * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
         */
        function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
            // For branchless setting of the `nextInitialized` flag.
            assembly {
                // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
            }
        }
        // =============================================================
        //                      APPROVAL OPERATIONS
        // =============================================================
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the
         * zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ownerOf(tokenId);
            if (_msgSenderERC721A() != owner)
                if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                    revert ApprovalCallerNotOwnerNorApproved();
                }
            ERC721AStorage.layout()._tokenApprovals[tokenId].value = to;
            emit Approval(owner, to, tokenId);
        }
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
            return ERC721AStorage.layout()._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 {
            ERC721AStorage.layout()._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 ERC721AStorage.layout()._operatorApprovals[owner][operator];
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted. See {_mint}.
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return
                _startTokenId() <= tokenId &&
                tokenId < ERC721AStorage.layout()._currentIndex && // If within bounds,
                ERC721AStorage.layout()._packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
        }
        /**
         * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
         */
        function _isSenderApprovedOrOwner(
            address approvedAddress,
            address owner,
            address msgSender
        ) private pure returns (bool result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                msgSender := and(msgSender, _BITMASK_ADDRESS)
                // `msgSender == owner || msgSender == approvedAddress`.
                result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
            }
        }
        /**
         * @dev Returns the storage slot and value for the approved address of `tokenId`.
         */
        function _getApprovedSlotAndAddress(uint256 tokenId)
            private
            view
            returns (uint256 approvedAddressSlot, address approvedAddress)
        {
            ERC721AStorage.TokenApprovalRef storage tokenApproval = ERC721AStorage.layout()._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 virtual override {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
            if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
            if (to == address(0)) revert TransferToZeroAddress();
            _beforeTokenTransfers(from, to, tokenId, 1);
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // We can directly increment and decrement the balances.
                --ERC721AStorage.layout()._packedAddressData[from]; // Updates: `balance -= 1`.
                ++ERC721AStorage.layout()._packedAddressData[to]; // Updates: `balance += 1`.
                // Updates:
                // - `address` to the next owner.
                // - `startTimestamp` to the timestamp of transfering.
                // - `burned` to `false`.
                // - `nextInitialized` to `true`.
                ERC721AStorage.layout()._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 (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
            emit Transfer(from, to, tokenId);
            _afterTokenTransfers(from, to, tokenId, 1);
        }
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public 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 virtual override {
            transferFrom(from, to, tokenId);
            if (to.code.length != 0)
                if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                    revert TransferToNonERC721ReceiverImplementer();
                }
        }
        /**
         * @dev Hook that is called before a set of serially-ordered token IDs
         * are about to be transferred. This includes minting.
         * And also called before burning one token.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _beforeTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
        /**
         * @dev Hook that is called after a set of serially-ordered token IDs
         * have been transferred. This includes minting.
         * And also called after one token has been burned.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
         * transferred to `to`.
         * - When `from` is zero, `tokenId` has been minted for `to`.
         * - When `to` is zero, `tokenId` has been burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _afterTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
        /**
         * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
         *
         * `from` - Previous owner of the given token ID.
         * `to` - Target address that will receive the token.
         * `tokenId` - Token ID to be transferred.
         * `_data` - Optional data to send along with the call.
         *
         * Returns whether the call correctly returned the expected magic value.
         */
        function _checkContractOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            try
                ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data)
            returns (bytes4 retval) {
                return retval == ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert TransferToNonERC721ReceiverImplementer();
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
        // =============================================================
        //                        MINT OPERATIONS
        // =============================================================
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _mint(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
            if (quantity == 0) revert MintZeroQuantity();
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
            // Overflows are incredibly unrealistic.
            // `balance` and `numberMinted` have a maximum limit of 2**64.
            // `tokenId` has a maximum limit of 2**256.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                ERC721AStorage.layout()._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`.
                ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
                uint256 toMasked;
                uint256 end = startTokenId + quantity;
                // Use assembly to loop and emit the `Transfer` event for gas savings.
                // The duplicated `log4` removes an extra check and reduces stack juggling.
                // The assembly, together with the surrounding Solidity code, have been
                // delicately arranged to nudge the compiler into producing optimized opcodes.
                assembly {
                    // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                    toMasked := and(to, _BITMASK_ADDRESS)
                    // Emit the `Transfer` event.
                    log4(
                        0, // Start of data (0, since no data).
                        0, // End of data (0, since no data).
                        _TRANSFER_EVENT_SIGNATURE, // Signature.
                        0, // `address(0)`.
                        toMasked, // `to`.
                        startTokenId // `tokenId`.
                    )
                    for {
                        let tokenId := add(startTokenId, 1)
                    } iszero(eq(tokenId, end)) {
                        tokenId := add(tokenId, 1)
                    } {
                        // Emit the `Transfer` event. Similar to above.
                        log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                    }
                }
                if (toMasked == 0) revert MintToZeroAddress();
                ERC721AStorage.layout()._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 = ERC721AStorage.layout()._currentIndex;
            if (to == address(0)) revert MintToZeroAddress();
            if (quantity == 0) revert MintZeroQuantity();
            if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
            // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                ERC721AStorage.layout()._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`.
                ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
                emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
                ERC721AStorage.layout()._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 = ERC721AStorage.layout()._currentIndex;
                    uint256 index = end - quantity;
                    do {
                        if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                            revert TransferToNonERC721ReceiverImplementer();
                        }
                    } while (index < end);
                    // Reentrancy protection.
                    if (ERC721AStorage.layout()._currentIndex != end) revert();
                }
            }
        }
        /**
         * @dev Equivalent to `_safeMint(to, quantity, '')`.
         */
        function _safeMint(address to, uint256 quantity) internal virtual {
            _safeMint(to, quantity, '');
        }
        // =============================================================
        //                        BURN OPERATIONS
        // =============================================================
        /**
         * @dev Equivalent to `_burn(tokenId, false)`.
         */
        function _burn(uint256 tokenId) internal virtual {
            _burn(tokenId, false);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
            address from = address(uint160(prevOwnershipPacked));
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
            if (approvalCheck) {
                // The nested ifs save around 20+ gas over a compound boolean condition.
                if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                    if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
            }
            _beforeTokenTransfers(from, address(0), tokenId, 1);
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // Updates:
                // - `balance -= 1`.
                // - `numberBurned += 1`.
                //
                // We can directly decrement the balance, and increment the number burned.
                // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                ERC721AStorage.layout()._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`.
                ERC721AStorage.layout()._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 (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
            emit Transfer(from, address(0), tokenId);
            _afterTokenTransfers(from, address(0), tokenId, 1);
            // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
            unchecked {
                ERC721AStorage.layout()._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 = ERC721AStorage.layout()._packedOwnerships[index];
            if (packed == 0) revert OwnershipNotInitializedForExtraData();
            uint256 extraDataCasted;
            // Cast `extraData` with assembly to avoid redundant masking.
            assembly {
                extraDataCasted := extraData
            }
            packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
            ERC721AStorage.layout()._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)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721AQueryableUpgradeable.sol';
    import '../ERC721AUpgradeable.sol';
    import '../ERC721A__Initializable.sol';
    /**
     * @title ERC721AQueryable.
     *
     * @dev ERC721A subclass with convenience query functions.
     */
    abstract contract ERC721AQueryableUpgradeable is
        ERC721A__Initializable,
        ERC721AUpgradeable,
        IERC721AQueryableUpgradeable
    {
        function __ERC721AQueryable_init() internal onlyInitializingERC721A {
            __ERC721AQueryable_init_unchained();
        }
        function __ERC721AQueryable_init_unchained() internal onlyInitializingERC721A {}
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) public view virtual override returns (TokenOwnership memory) {
            TokenOwnership memory ownership;
            if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) {
                return ownership;
            }
            ownership = _ownershipAt(tokenId);
            if (ownership.burned) {
                return ownership;
            }
            return _ownershipOf(tokenId);
        }
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] calldata tokenIds)
            external
            view
            virtual
            override
            returns (TokenOwnership[] memory)
        {
            unchecked {
                uint256 tokenIdsLength = tokenIds.length;
                TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
                for (uint256 i; i != tokenIdsLength; ++i) {
                    ownerships[i] = explicitOwnershipOf(tokenIds[i]);
                }
                return ownerships;
            }
        }
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view virtual override returns (uint256[] memory) {
            unchecked {
                if (start >= stop) revert InvalidQueryRange();
                uint256 tokenIdsIdx;
                uint256 stopLimit = _nextTokenId();
                // Set `start = max(start, _startTokenId())`.
                if (start < _startTokenId()) {
                    start = _startTokenId();
                }
                // Set `stop = min(stop, stopLimit)`.
                if (stop > stopLimit) {
                    stop = stopLimit;
                }
                uint256 tokenIdsMaxLength = balanceOf(owner);
                // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
                // to cater for cases where `balanceOf(owner)` is too big.
                if (start < stop) {
                    uint256 rangeLength = stop - start;
                    if (rangeLength < tokenIdsMaxLength) {
                        tokenIdsMaxLength = rangeLength;
                    }
                } else {
                    tokenIdsMaxLength = 0;
                }
                uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
                if (tokenIdsMaxLength == 0) {
                    return tokenIds;
                }
                // We need to call `explicitOwnershipOf(start)`,
                // because the slot at `start` may not be initialized.
                TokenOwnership memory ownership = explicitOwnershipOf(start);
                address currOwnershipAddr;
                // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`.
                // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range.
                if (!ownership.burned) {
                    currOwnershipAddr = ownership.addr;
                }
                for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
                    ownership = _ownershipAt(i);
                    if (ownership.burned) {
                        continue;
                    }
                    if (ownership.addr != address(0)) {
                        currOwnershipAddr = ownership.addr;
                    }
                    if (currOwnershipAddr == owner) {
                        tokenIds[tokenIdsIdx++] = i;
                    }
                }
                // Downsize the array to fit.
                assembly {
                    mstore(tokenIds, tokenIdsIdx)
                }
                return tokenIds;
            }
        }
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) {
            unchecked {
                uint256 tokenIdsIdx;
                address currOwnershipAddr;
                uint256 tokenIdsLength = balanceOf(owner);
                uint256[] memory tokenIds = new uint256[](tokenIdsLength);
                TokenOwnership memory ownership;
                for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
                    ownership = _ownershipAt(i);
                    if (ownership.burned) {
                        continue;
                    }
                    if (ownership.addr != address(0)) {
                        currOwnershipAddr = ownership.addr;
                    }
                    if (currOwnershipAddr == owner) {
                        tokenIds[tokenIdsIdx++] = i;
                    }
                }
                return tokenIds;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import './IERC721ABurnableUpgradeable.sol';
    import '../ERC721AUpgradeable.sol';
    import '../ERC721A__Initializable.sol';
    /**
     * @title ERC721ABurnable.
     *
     * @dev ERC721A token that can be irreversibly burned (destroyed).
     */
    abstract contract ERC721ABurnableUpgradeable is
        ERC721A__Initializable,
        ERC721AUpgradeable,
        IERC721ABurnableUpgradeable
    {
        function __ERC721ABurnable_init() internal onlyInitializingERC721A {
            __ERC721ABurnable_init_unchained();
        }
        function __ERC721ABurnable_init_unchained() internal onlyInitializingERC721A {}
        /**
         * @dev Burns `tokenId`. See {ERC721A-_burn}.
         *
         * Requirements:
         *
         * - The caller must own `tokenId` or be an approved operator.
         */
        function burn(uint256 tokenId) public virtual override {
            _burn(tokenId, true);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
    pragma solidity ^0.8.0;
    import "../utils/introspection/IERC165Upgradeable.sol";
    /**
     * @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.
     *
     * _Available since v4.5._
     */
    interface IERC2981Upgradeable is IERC165Upgradeable {
        /**
         * @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.
         */
        function royaltyInfo(uint256 tokenId, uint256 salePrice)
            external
            view
            returns (address receiver, uint256 royaltyAmount);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
    /// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
    library SafeTransferLib {
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        error ETHTransferFailed();
        error TransferFromFailed();
        error TransferFailed();
        error ApproveFailed();
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       ETH OPERATIONS                       */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        function safeTransferETH(address to, uint256 amount) internal {
            assembly {
                // Transfer the ETH and check if it succeeded or not.
                if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
                    // Store the function selector of `ETHTransferFailed()`.
                    mstore(0x00, 0xb12d13eb)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                      ERC20 OPERATIONS                      */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        function safeTransferFrom(
            address token,
            address from,
            address to,
            uint256 amount
        ) internal {
            assembly {
                // We'll write our calldata to this slot below, but restore it later.
                let memPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(0x00, 0x23b872dd)
                mstore(0x20, from) // Append the "from" argument.
                mstore(0x40, to) // Append the "to" argument.
                mstore(0x60, amount) // Append the "amount" argument.
                if iszero(
                    and(
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        // We use 0x64 because that's the total length of our calldata (0x04 + 0x20 * 3)
                        // Counterintuitively, this call() must be positioned after the or() in the
                        // surrounding and() because and() evaluates its arguments from right to left.
                        call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFromFailed()`.
                    mstore(0x00, 0x7939f424)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot to zero.
                mstore(0x40, memPointer) // Restore the memPointer.
            }
        }
        function safeTransfer(
            address token,
            address to,
            uint256 amount
        ) internal {
            assembly {
                // We'll write our calldata to this slot below, but restore it later.
                let memPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(0x00, 0xa9059cbb)
                mstore(0x20, to) // Append the "to" argument.
                mstore(0x40, amount) // Append the "amount" argument.
                if iszero(
                    and(
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        // We use 0x44 because that's the total length of our calldata (0x04 + 0x20 * 2)
                        // Counterintuitively, this call() must be positioned after the or() in the
                        // surrounding and() because and() evaluates its arguments from right to left.
                        call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `TransferFailed()`.
                    mstore(0x00, 0x90b8ec18)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x40, memPointer) // Restore the memPointer.
            }
        }
        function safeApprove(
            address token,
            address to,
            uint256 amount
        ) internal {
            assembly {
                // We'll write our calldata to this slot below, but restore it later.
                let memPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(0x00, 0x095ea7b3)
                mstore(0x20, to) // Append the "to" argument.
                mstore(0x40, amount) // Append the "amount" argument.
                if iszero(
                    and(
                        // Set success to whether the call reverted, if not we check it either
                        // returned exactly 1 (can't just be non-zero data), or had no return data.
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        // We use 0x44 because that's the total length of our calldata (0x04 + 0x20 * 2)
                        // Counterintuitively, this call() must be positioned after the or() in the
                        // surrounding and() because and() evaluates its arguments from right to left.
                        call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `ApproveFailed()`.
                    mstore(0x00, 0x3e3f8f73)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                mstore(0x40, memPointer) // Restore the memPointer.
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Arithmetic library with operations for fixed-point numbers.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
    library FixedPointMathLib {
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The operation failed, as the output exceeds the maximum value of uint256.
        error ExpOverflow();
        /// @dev The operation failed, as the output exceeds the maximum value of uint256.
        error FactorialOverflow();
        /// @dev The operation failed, due to an multiplication overflow.
        error MulWadFailed();
        /// @dev The operation failed, either due to a
        /// multiplication overflow, or a division by a zero.
        error DivWadFailed();
        /// @dev The multiply-divide operation failed, either due to a
        /// multiplication overflow, or a division by a zero.
        error MulDivFailed();
        /// @dev The division failed, as the denominator is zero.
        error DivFailed();
        /// @dev The output is undefined, as the input is less-than-or-equal to zero.
        error LnWadUndefined();
        /// @dev The output is undefined, as the input is zero.
        error Log2Undefined();
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The scalar of ETH and most ERC20s.
        uint256 internal constant WAD = 1e18;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*              SIMPLIFIED FIXED POINT OPERATIONS             */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Equivalent to `(x * y) / WAD` rounded down.
        function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
                if mul(y, gt(x, div(not(0), y))) {
                    // Store the function selector of `MulWadFailed()`.
                    mstore(0x00, 0xbac65e5b)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := div(mul(x, y), WAD)
            }
        }
        /// @dev Equivalent to `(x * y) / WAD` rounded up.
        function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
                if mul(y, gt(x, div(not(0), y))) {
                    // Store the function selector of `MulWadFailed()`.
                    mstore(0x00, 0xbac65e5b)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
            }
        }
        /// @dev Equivalent to `(x * WAD) / y` rounded down.
        function divWadDown(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
                if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                    // Store the function selector of `DivWadFailed()`.
                    mstore(0x00, 0x7c5f487d)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := div(mul(x, WAD), y)
            }
        }
        /// @dev Equivalent to `(x * WAD) / y` rounded up.
        function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
                if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                    // Store the function selector of `DivWadFailed()`.
                    mstore(0x00, 0x7c5f487d)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
            }
        }
        /// @dev Equivalent to `x` to the power of `y`.
        /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
        function powWad(int256 x, int256 y) internal pure returns (int256) {
            // Using `ln(x)` means `x` must be greater than 0.
            return expWad((lnWad(x) * y) / int256(WAD));
        }
        /// @dev Returns `exp(x)`, denominated in `WAD`.
        function expWad(int256 x) internal pure returns (int256 r) {
            unchecked {
                // When the result is < 0.5 we return zero. This happens when
                // x <= floor(log(0.5e18) * 1e18) ~ -42e18
                if (x <= -42139678854452767551) return 0;
                // When the result is > (2**255 - 1) / 1e18 we can not represent it as an
                // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
                if (x >= 135305999368893231589) revert ExpOverflow();
                // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
                // for more intermediate precision and a binary basis. This base conversion
                // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
                x = (x << 78) / 5**18;
                // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
                // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
                // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
                int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;
                x = x - k * 54916777467707473351141471128;
                // k is in the range [-61, 195].
                // Evaluate using a (6, 7)-term rational approximation.
                // p is made monic, we'll multiply by a scale factor later.
                int256 y = x + 1346386616545796478920950773328;
                y = ((y * x) >> 96) + 57155421227552351082224309758442;
                int256 p = y + x - 94201549194550492254356042504812;
                p = ((p * y) >> 96) + 28719021644029726153956944680412240;
                p = p * x + (4385272521454847904659076985693276 << 96);
                // We leave p in 2**192 basis so we don't need to scale it back up for the division.
                int256 q = x - 2855989394907223263936484059900;
                q = ((q * x) >> 96) + 50020603652535783019961831881945;
                q = ((q * x) >> 96) - 533845033583426703283633433725380;
                q = ((q * x) >> 96) + 3604857256930695427073651918091429;
                q = ((q * x) >> 96) - 14423608567350463180887372962807573;
                q = ((q * x) >> 96) + 26449188498355588339934803723976023;
                assembly {
                    // Div in assembly because solidity adds a zero check despite the unchecked.
                    // The q polynomial won't have zeros in the domain as all its roots are complex.
                    // No scaling is necessary because p is already 2**96 too large.
                    r := sdiv(p, q)
                }
                // r should be in the range (0.09, 0.25) * 2**96.
                // We now need to multiply r by:
                // * the scale factor s = ~6.031367120.
                // * the 2**k factor from the range reduction.
                // * the 1e18 / 2**96 factor for base conversion.
                // We do this all at once, with an intermediate result in 2**213
                // basis, so the final right shift is always by a positive amount.
                r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));
            }
        }
        /// @dev Returns `ln(x)`, denominated in `WAD`.
        function lnWad(int256 x) internal pure returns (int256 r) {
            unchecked {
                if (x <= 0) revert LnWadUndefined();
                // We want to convert x from 10**18 fixed point to 2**96 fixed point.
                // We do this by multiplying by 2**96 / 10**18. But since
                // ln(x * C) = ln(x) + ln(C), we can simply do nothing here
                // and add ln(2**96 / 10**18) at the end.
                // Compute k = log2(x) - 96.
                int256 k;
                assembly {
                    let v := x
                    k := shl(7, lt(0xffffffffffffffffffffffffffffffff, v))
                    k := or(k, shl(6, lt(0xffffffffffffffff, shr(k, v))))
                    k := or(k, shl(5, lt(0xffffffff, shr(k, v))))
                    // For the remaining 32 bits, use a De Bruijn lookup.
                    // See: https://graphics.stanford.edu/~seander/bithacks.html
                    v := shr(k, v)
                    v := or(v, shr(1, v))
                    v := or(v, shr(2, v))
                    v := or(v, shr(4, v))
                    v := or(v, shr(8, v))
                    v := or(v, shr(16, v))
                    // prettier-ignore
                    k := sub(or(k, byte(shr(251, mul(v, shl(224, 0x07c4acdd))),
                        0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)), 96)
                }
                // Reduce range of x to (1, 2) * 2**96
                // ln(2^k * x) = k * ln(2) + ln(x)
                x <<= uint256(159 - k);
                x = int256(uint256(x) >> 159);
                // Evaluate using a (8, 8)-term rational approximation.
                // p is made monic, we will multiply by a scale factor later.
                int256 p = x + 3273285459638523848632254066296;
                p = ((p * x) >> 96) + 24828157081833163892658089445524;
                p = ((p * x) >> 96) + 43456485725739037958740375743393;
                p = ((p * x) >> 96) - 11111509109440967052023855526967;
                p = ((p * x) >> 96) - 45023709667254063763336534515857;
                p = ((p * x) >> 96) - 14706773417378608786704636184526;
                p = p * x - (795164235651350426258249787498 << 96);
                // We leave p in 2**192 basis so we don't need to scale it back up for the division.
                // q is monic by convention.
                int256 q = x + 5573035233440673466300451813936;
                q = ((q * x) >> 96) + 71694874799317883764090561454958;
                q = ((q * x) >> 96) + 283447036172924575727196451306956;
                q = ((q * x) >> 96) + 401686690394027663651624208769553;
                q = ((q * x) >> 96) + 204048457590392012362485061816622;
                q = ((q * x) >> 96) + 31853899698501571402653359427138;
                q = ((q * x) >> 96) + 909429971244387300277376558375;
                assembly {
                    // Div in assembly because solidity adds a zero check despite the unchecked.
                    // The q polynomial is known not to have zeros in the domain.
                    // No scaling required because p is already 2**96 too large.
                    r := sdiv(p, q)
                }
                // r is in the range (0, 0.125) * 2**96
                // Finalization, we need to:
                // * multiply by the scale factor s = 5.549…
                // * add ln(2**96 / 10**18)
                // * add k * ln(2)
                // * multiply by 10**18 / 2**96 = 5**18 >> 78
                // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
                r *= 1677202110996718588342820967067443963516166;
                // add ln(2) * k * 5e18 * 2**192
                r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
                // add ln(2**96 / 10**18) * 5e18 * 2**192
                r += 600920179829731861736702779321621459595472258049074101567377883020018308;
                // base conversion: mul 2**18 / 2**192
                r >>= 174;
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                  GENERAL NUMBER UTILITIES                  */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Returns `floor(x * y / denominator)`.
        /// Reverts if `x * y` overflows, or `denominator` is zero.
        function mulDivDown(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                if iszero(mul(denominator, iszero(mul(y, gt(x, div(not(0), y)))))) {
                    // Store the function selector of `MulDivFailed()`.
                    mstore(0x00, 0xad251c27)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := div(mul(x, y), denominator)
            }
        }
        /// @dev Returns `ceil(x * y / denominator)`.
        /// Reverts if `x * y` overflows, or `denominator` is zero.
        function mulDivUp(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 z) {
            assembly {
                // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                if iszero(mul(denominator, iszero(mul(y, gt(x, div(not(0), y)))))) {
                    // Store the function selector of `MulDivFailed()`.
                    mstore(0x00, 0xad251c27)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(mul(x, y), denominator))), div(mul(x, y), denominator))
            }
        }
        /// @dev Returns `ceil(x / denominator)`.
        /// Reverts if `denominator` is zero.
        function divUp(uint256 x, uint256 denominator) internal pure returns (uint256 z) {
            assembly {
                if iszero(denominator) {
                    // Store the function selector of `DivFailed()`.
                    mstore(0x00, 0x65244e4e)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                z := add(iszero(iszero(mod(x, denominator))), div(x, denominator))
            }
        }
        /// @dev Returns `max(0, x - y)`.
        function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := mul(gt(x, y), sub(x, y))
            }
        }
        /// @dev Returns the square root of `x`.
        function sqrt(uint256 x) internal pure returns (uint256 z) {
            assembly {
                // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
                z := 181 // The "correct" value is 1, but this saves a multiplication later.
                // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                // Let `y = x / 2**r`.
                // We check `y >= 2**(k + 8)` but shift right by `k` bits
                // each branch to ensure that if `x >= 256`, then `y >= 256`.
                let r := shl(7, gt(x, 0xffffffffffffffffffffffffffffffffff))
                r := or(r, shl(6, gt(shr(r, x), 0xffffffffffffffffff)))
                r := or(r, shl(5, gt(shr(r, x), 0xffffffffff)))
                r := or(r, shl(4, gt(shr(r, x), 0xffffff)))
                z := shl(shr(1, r), z)
                // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
                // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
                // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
                // That's not possible if `x < 256` but we can just verify those cases exhaustively.
                // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
                // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
                // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
                // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
                // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
                // with largest error when `s = 1` and when `s = 256` or `1/256`.
                // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
                // Then we can estimate `sqrt(y)` using
                // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
                // There is no overflow risk here since `y < 2**136` after the first branch above.
                z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
                // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                // If `x+1` is a perfect square, the Babylonian method cycles between
                // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
                // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                z := sub(z, lt(div(x, z), z))
            }
        }
        /// @dev Returns the factorial of `x`.
        function factorial(uint256 x) public pure returns (uint256 result) {
            unchecked {
                if (x < 11) {
                    // prettier-ignore
                    result = (0x375f0016260009d80004ec0002d00001e0000180000180000200000400001 >> (x * 22)) & 0x3fffff;
                } else if (x < 32) {
                    result = 3628800;
                    do {
                        result = result * x;
                        x = x - 1;
                    } while (x != 10);
                } else if (x < 58) {
                    // Just cheat lol.
                    result = 8222838654177922817725562880000000;
                    do {
                        result = result * x;
                        x = x - 1;
                    } while (x != 31);
                } else {
                    revert FactorialOverflow();
                }
            }
        }
        /// @dev Returns the log2 of `x`.
        /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
        function log2(uint256 x) internal pure returns (uint256 r) {
            assembly {
                if iszero(x) {
                    // Store the function selector of `Log2Undefined()`.
                    mstore(0x00, 0x5be3aa5c)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
                r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
                r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
                r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                // For the remaining 32 bits, use a De Bruijn lookup.
                // See: https://graphics.stanford.edu/~seander/bithacks.html
                x := shr(r, x)
                x := or(x, shr(1, x))
                x := or(x, shr(2, x))
                x := or(x, shr(4, x))
                x := or(x, shr(8, x))
                x := or(x, shr(16, x))
                // prettier-ignore
                r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))),
                    0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f))
            }
        }
        /// @dev Returns the averege of `x` and `y`.
        function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := add(and(x, y), shr(1, xor(x, y)))
            }
        }
        /// @dev Returns the absolute value of `x`.
        function abs(int256 x) internal pure returns (uint256 z) {
            assembly {
                let mask := mul(shr(255, x), not(0))
                z := xor(mask, add(mask, x))
            }
        }
        /// @dev Returns the absolute distance between `x` and `y`.
        function dist(int256 x, int256 y) internal pure returns (uint256 z) {
            assembly {
                let a := sub(y, x)
                z := xor(a, mul(xor(a, sub(x, y)), sgt(x, y)))
            }
        }
        /// @dev Returns the minimum of `x` and `y`.
        function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := xor(x, mul(xor(x, y), lt(y, x)))
            }
        }
        /// @dev Returns the maximum of `x` and `y`.
        function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                z := xor(x, mul(xor(x, y), gt(y, x)))
            }
        }
        /// @dev Returns gcd of `x` and `y`.
        function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
            assembly {
                // prettier-ignore
                for { z := x } y {} {
                    let t := y
                    y := mod(z, y)
                    z := t
                }
            }
        }
        /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
        function clamp(
            uint256 x,
            uint256 minValue,
            uint256 maxValue
        ) internal pure returns (uint256 z) {
            return min(max(x, minValue), maxValue);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Simple single owner and multiroles authorization mixin.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol)
    /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
    /// for compatibility, the nomenclature for the 2-step ownership handover and roles
    /// may be unique to this codebase.
    abstract contract OwnableRoles {
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The caller is not authorized to call the function.
        error Unauthorized();
        /// @dev The `newOwner` cannot be the zero address.
        error NewOwnerIsZeroAddress();
        /// @dev The `pendingOwner` does not have a valid handover request.
        error NoHandoverRequest();
        /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
        uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;
        /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`.
        uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae;
        /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`.
        uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                           EVENTS                           */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
        /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
        /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
        /// despite it not being as lightweight as a single argument event.
        event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
        /// @dev An ownership handover to `pendingOwner` has been requested.
        event OwnershipHandoverRequested(address indexed pendingOwner);
        /// @dev The ownership handover to `pendingOwner` has been cancelled.
        event OwnershipHandoverCanceled(address indexed pendingOwner);
        /// @dev The `user`'s roles is updated to `roles`.
        /// Each bit of `roles` represents whether the role is set.
        event RolesUpdated(address indexed user, uint256 indexed roles);
        /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
        uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
            0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
        /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
        uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
            0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
        /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
        uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
            0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
        /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
        uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
            0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                          STORAGE                           */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
        /// It is intentionally choosen to be a high value
        /// to avoid collision with lower slots.
        /// The choice of manual storage layout is to enable compatibility
        /// with both regular and upgradeable contracts.
        ///
        /// The role slot of `user` is given by:
        /// ```
        ///     mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
        ///     let roleSlot := keccak256(0x00, 0x20)
        /// ```
        /// This automatically ignores the upper bits of the `user` in case
        /// they are not clean, as well as keep the `keccak256` under 32-bytes.
        uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
        /// The ownership handover slot of `newOwner` is given by:
        /// ```
        ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
        ///     let handoverSlot := keccak256(0x00, 0x20)
        /// ```
        /// It stores the expiry timestamp of the two-step ownership handover.
        uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                     INTERNAL FUNCTIONS                     */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Initializes the owner directly without authorization guard.
        /// This function must be called upon initialization,
        /// regardless of whether the contract is upgradeable or not.
        /// This is to enable generalization to both regular and upgradeable contracts,
        /// and to save gas in case the initial owner is not the caller.
        /// For performance reasons, this function will not check if there
        /// is an existing owner.
        function _initializeOwner(address newOwner) internal virtual {
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
        /// @dev Sets the owner directly without authorization guard.
        function _setOwner(address newOwner) internal virtual {
            assembly {
                let ownerSlot := not(_OWNER_SLOT_NOT)
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
        /// @dev Grants the roles directly without authorization guard.
        /// Each bit of `roles` represents the role to turn on.
        function _grantRoles(address user, uint256 roles) internal virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                let roleSlot := keccak256(0x00, 0x20)
                // Load the current value and `or` it with `roles`.
                let newRoles := or(sload(roleSlot), roles)
                // Store the new value.
                sstore(roleSlot, newRoles)
                // Emit the {RolesUpdated} event.
                log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles)
            }
        }
        /// @dev Removes the roles directly without authorization guard.
        /// Each bit of `roles` represents the role to turn off.
        function _removeRoles(address user, uint256 roles) internal virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                let roleSlot := keccak256(0x00, 0x20)
                // Load the current value.
                let currentRoles := sload(roleSlot)
                // Use `and` to compute the intersection of `currentRoles` and `roles`,
                // `xor` it with `currentRoles` to flip the bits in the intersection.
                let newRoles := xor(currentRoles, and(currentRoles, roles))
                // Then, store the new value.
                sstore(roleSlot, newRoles)
                // Emit the {RolesUpdated} event.
                log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles)
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                  PUBLIC UPDATE FUNCTIONS                   */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Allows the owner to transfer the ownership to `newOwner`.
        function transferOwnership(address newOwner) public virtual onlyOwner {
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Reverts if the `newOwner` is the zero address.
                if iszero(newOwner) {
                    mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), newOwner)
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), newOwner)
            }
        }
        /// @dev Allows the owner to renounce their ownership.
        function renounceOwnership() public virtual onlyOwner {
            assembly {
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), 0)
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), 0)
            }
        }
        /// @dev Request a two-step ownership handover to the caller.
        /// The request will be automatically expire in 48 hours (172800 seconds) by default.
        function requestOwnershipHandover() public virtual {
            unchecked {
                uint256 expires = block.timestamp + ownershipHandoverValidFor();
                assembly {
                    // Compute and set the handover slot to 1.
                    mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED))
                    sstore(keccak256(0x00, 0x20), expires)
                    // Emit the {OwnershipHandoverRequested} event.
                    log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                }
            }
        }
        /// @dev Cancels the two-step ownership handover to the caller, if any.
        function cancelOwnershipHandover() public virtual {
            assembly {
                // Compute and set the handover slot to 0.
                mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED))
                sstore(keccak256(0x00, 0x20), 0)
                // Emit the {OwnershipHandoverCanceled} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
            }
        }
        /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
        /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
        function completeOwnershipHandover(address pendingOwner) public virtual onlyOwner {
            assembly {
                // Clean the upper 96 bits.
                pendingOwner := shr(96, shl(96, pendingOwner))
                // Compute and set the handover slot to 0.
                mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED))
                let handoverSlot := keccak256(0x00, 0x20)
                // If the handover does not exist, or has expired.
                if gt(timestamp(), sload(handoverSlot)) {
                    mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
                // Set the handover slot to 0.
                sstore(handoverSlot, 0)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), pendingOwner)
                // Store the new value.
                sstore(not(_OWNER_SLOT_NOT), pendingOwner)
            }
        }
        /// @dev Allows the owner to grant `user` `roles`.
        /// If the `user` already has a role, then it will be an no-op for the role.
        function grantRoles(address user, uint256 roles) public virtual onlyOwner {
            _grantRoles(user, roles);
        }
        /// @dev Allows the owner to remove `user` `roles`.
        /// If the `user` does not have a role, then it will be an no-op for the role.
        function revokeRoles(address user, uint256 roles) public virtual onlyOwner {
            _removeRoles(user, roles);
        }
        /// @dev Allow the caller to remove their own roles.
        /// If the caller does not have a role, then it will be an no-op for the role.
        function renounceRoles(uint256 roles) public virtual {
            _removeRoles(msg.sender, roles);
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                   PUBLIC READ FUNCTIONS                    */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Returns the owner of the contract.
        function owner() public view virtual returns (address result) {
            assembly {
                result := sload(not(_OWNER_SLOT_NOT))
            }
        }
        /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
        function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) {
            assembly {
                // Compute the handover slot.
                mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED))
                // Load the handover slot.
                result := sload(keccak256(0x00, 0x20))
            }
        }
        /// @dev Returns how long a two-step ownership handover is valid for in seconds.
        function ownershipHandoverValidFor() public view virtual returns (uint64) {
            return 48 * 3600;
        }
        /// @dev Returns whether `user` has any of `roles`.
        function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                // Load the stored value, and set the result to whether the
                // `and` intersection of the value and `roles` is not zero.
                result := iszero(iszero(and(sload(keccak256(0x00, 0x20)), roles)))
            }
        }
        /// @dev Returns whether `user` has all of `roles`.
        function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                // Whether the stored value is contains all the set bits in `roles`.
                result := eq(and(sload(keccak256(0x00, 0x20)), roles), roles)
            }
        }
        /// @dev Returns the roles of `user`.
        function rolesOf(address user) public view virtual returns (uint256 roles) {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
                // Load the stored value.
                roles := sload(keccak256(0x00, 0x20))
            }
        }
        /// @dev Convenience function to return a `roles` bitmap from the `ordinals`.
        /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
        /// Not recommended to be called on-chain.
        function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) {
            assembly {
                // Skip the length slot.
                let o := add(ordinals, 0x20)
                // `shl` 5 is equivalent to multiplying by 0x20.
                let end := add(o, shl(5, mload(ordinals)))
                // prettier-ignore
                for {} iszero(eq(o, end)) { o := add(o, 0x20) } {
                    roles := or(roles, shl(and(mload(o), 0xff), 1))
                }
            }
        }
        /// @dev Convenience function to return a `roles` bitmap from the `ordinals`.
        /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
        /// Not recommended to be called on-chain.
        function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) {
            assembly {
                // Grab the pointer to the free memory.
                let ptr := add(mload(0x40), 0x20)
                // The absence of lookup tables, De Bruijn, etc., here is intentional for
                // smaller bytecode, as this function is not meant to be called on-chain.
                // prettier-ignore
                for { let i := 0 } 1 { i := add(i, 1) } {
                    mstore(ptr, i)
                    // `shr` 5 is equivalent to multiplying by 0x20.
                    // Push back into the ordinals array if the bit is set.
                    ptr := add(ptr, shl(5, and(roles, 1)))
                    roles := shr(1, roles)
                    // prettier-ignore
                    if iszero(roles) { break }
                }
                // Set `ordinals` to the start of the free memory.
                ordinals := mload(0x40)
                // Allocate the memory.
                mstore(0x40, ptr)
                // Store the length of `ordinals`.
                mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
            }
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                         MODIFIERS                          */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        /// @dev Marks a function as only callable by the owner.
        modifier onlyOwner() virtual {
            assembly {
                // If the caller is not the stored owner, revert.
                if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
            _;
        }
        /// @dev Marks a function as only callable by an account with `roles`.
        modifier onlyRoles(uint256 roles) virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
            _;
        }
        /// @dev Marks a function as only callable by the owner or by an account
        /// with `roles`. Checks for ownership first, then lazily checks for roles.
        modifier onlyOwnerOrRoles(uint256 roles) virtual {
            assembly {
                // If the caller is not the stored owner.
                if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                    // Compute the role slot.
                    mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
                    // Load the stored value, and if the `and` intersection
                    // of the value and `roles` is zero, revert.
                    if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                        mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                        revert(0x1c, 0x04)
                    }
                }
            }
            _;
        }
        /// @dev Marks a function as only callable by an account with `roles`
        /// or the owner. Checks for roles first, then lazily checks for ownership.
        modifier onlyRolesOrOwner(uint256 roles) virtual {
            assembly {
                // Compute the role slot.
                mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                    // If the caller is not the stored owner.
                    if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                        mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                        revert(0x1c, 0x04)
                    }
                }
            }
            _;
        }
        /*´:°â€¢.°+.*•´.*:Ëš.°*.˚•´.°:°â€¢.°â€¢.*•´.*:Ëš.°*.˚•´.°:°â€¢.°+.*•´.*:*/
        /*                       ROLE CONSTANTS                       */
        /*.•°:°.´+Ëš.*°.Ëš:*.´â€¢*.+°.•°:´*.´â€¢*.•°.•°:°.´:•˚°.*°.Ëš:*.´+°.•*/
        // IYKYK
        uint256 internal constant _ROLE_0 = 1 << 0;
        uint256 internal constant _ROLE_1 = 1 << 1;
        uint256 internal constant _ROLE_2 = 1 << 2;
        uint256 internal constant _ROLE_3 = 1 << 3;
        uint256 internal constant _ROLE_4 = 1 << 4;
        uint256 internal constant _ROLE_5 = 1 << 5;
        uint256 internal constant _ROLE_6 = 1 << 6;
        uint256 internal constant _ROLE_7 = 1 << 7;
        uint256 internal constant _ROLE_8 = 1 << 8;
        uint256 internal constant _ROLE_9 = 1 << 9;
        uint256 internal constant _ROLE_10 = 1 << 10;
        uint256 internal constant _ROLE_11 = 1 << 11;
        uint256 internal constant _ROLE_12 = 1 << 12;
        uint256 internal constant _ROLE_13 = 1 << 13;
        uint256 internal constant _ROLE_14 = 1 << 14;
        uint256 internal constant _ROLE_15 = 1 << 15;
        uint256 internal constant _ROLE_16 = 1 << 16;
        uint256 internal constant _ROLE_17 = 1 << 17;
        uint256 internal constant _ROLE_18 = 1 << 18;
        uint256 internal constant _ROLE_19 = 1 << 19;
        uint256 internal constant _ROLE_20 = 1 << 20;
        uint256 internal constant _ROLE_21 = 1 << 21;
        uint256 internal constant _ROLE_22 = 1 << 22;
        uint256 internal constant _ROLE_23 = 1 << 23;
        uint256 internal constant _ROLE_24 = 1 << 24;
        uint256 internal constant _ROLE_25 = 1 << 25;
        uint256 internal constant _ROLE_26 = 1 << 26;
        uint256 internal constant _ROLE_27 = 1 << 27;
        uint256 internal constant _ROLE_28 = 1 << 28;
        uint256 internal constant _ROLE_29 = 1 << 29;
        uint256 internal constant _ROLE_30 = 1 << 30;
        uint256 internal constant _ROLE_31 = 1 << 31;
        uint256 internal constant _ROLE_32 = 1 << 32;
        uint256 internal constant _ROLE_33 = 1 << 33;
        uint256 internal constant _ROLE_34 = 1 << 34;
        uint256 internal constant _ROLE_35 = 1 << 35;
        uint256 internal constant _ROLE_36 = 1 << 36;
        uint256 internal constant _ROLE_37 = 1 << 37;
        uint256 internal constant _ROLE_38 = 1 << 38;
        uint256 internal constant _ROLE_39 = 1 << 39;
        uint256 internal constant _ROLE_40 = 1 << 40;
        uint256 internal constant _ROLE_41 = 1 << 41;
        uint256 internal constant _ROLE_42 = 1 << 42;
        uint256 internal constant _ROLE_43 = 1 << 43;
        uint256 internal constant _ROLE_44 = 1 << 44;
        uint256 internal constant _ROLE_45 = 1 << 45;
        uint256 internal constant _ROLE_46 = 1 << 46;
        uint256 internal constant _ROLE_47 = 1 << 47;
        uint256 internal constant _ROLE_48 = 1 << 48;
        uint256 internal constant _ROLE_49 = 1 << 49;
        uint256 internal constant _ROLE_50 = 1 << 50;
        uint256 internal constant _ROLE_51 = 1 << 51;
        uint256 internal constant _ROLE_52 = 1 << 52;
        uint256 internal constant _ROLE_53 = 1 << 53;
        uint256 internal constant _ROLE_54 = 1 << 54;
        uint256 internal constant _ROLE_55 = 1 << 55;
        uint256 internal constant _ROLE_56 = 1 << 56;
        uint256 internal constant _ROLE_57 = 1 << 57;
        uint256 internal constant _ROLE_58 = 1 << 58;
        uint256 internal constant _ROLE_59 = 1 << 59;
        uint256 internal constant _ROLE_60 = 1 << 60;
        uint256 internal constant _ROLE_61 = 1 << 61;
        uint256 internal constant _ROLE_62 = 1 << 62;
        uint256 internal constant _ROLE_63 = 1 << 63;
        uint256 internal constant _ROLE_64 = 1 << 64;
        uint256 internal constant _ROLE_65 = 1 << 65;
        uint256 internal constant _ROLE_66 = 1 << 66;
        uint256 internal constant _ROLE_67 = 1 << 67;
        uint256 internal constant _ROLE_68 = 1 << 68;
        uint256 internal constant _ROLE_69 = 1 << 69;
        uint256 internal constant _ROLE_70 = 1 << 70;
        uint256 internal constant _ROLE_71 = 1 << 71;
        uint256 internal constant _ROLE_72 = 1 << 72;
        uint256 internal constant _ROLE_73 = 1 << 73;
        uint256 internal constant _ROLE_74 = 1 << 74;
        uint256 internal constant _ROLE_75 = 1 << 75;
        uint256 internal constant _ROLE_76 = 1 << 76;
        uint256 internal constant _ROLE_77 = 1 << 77;
        uint256 internal constant _ROLE_78 = 1 << 78;
        uint256 internal constant _ROLE_79 = 1 << 79;
        uint256 internal constant _ROLE_80 = 1 << 80;
        uint256 internal constant _ROLE_81 = 1 << 81;
        uint256 internal constant _ROLE_82 = 1 << 82;
        uint256 internal constant _ROLE_83 = 1 << 83;
        uint256 internal constant _ROLE_84 = 1 << 84;
        uint256 internal constant _ROLE_85 = 1 << 85;
        uint256 internal constant _ROLE_86 = 1 << 86;
        uint256 internal constant _ROLE_87 = 1 << 87;
        uint256 internal constant _ROLE_88 = 1 << 88;
        uint256 internal constant _ROLE_89 = 1 << 89;
        uint256 internal constant _ROLE_90 = 1 << 90;
        uint256 internal constant _ROLE_91 = 1 << 91;
        uint256 internal constant _ROLE_92 = 1 << 92;
        uint256 internal constant _ROLE_93 = 1 << 93;
        uint256 internal constant _ROLE_94 = 1 << 94;
        uint256 internal constant _ROLE_95 = 1 << 95;
        uint256 internal constant _ROLE_96 = 1 << 96;
        uint256 internal constant _ROLE_97 = 1 << 97;
        uint256 internal constant _ROLE_98 = 1 << 98;
        uint256 internal constant _ROLE_99 = 1 << 99;
        uint256 internal constant _ROLE_100 = 1 << 100;
        uint256 internal constant _ROLE_101 = 1 << 101;
        uint256 internal constant _ROLE_102 = 1 << 102;
        uint256 internal constant _ROLE_103 = 1 << 103;
        uint256 internal constant _ROLE_104 = 1 << 104;
        uint256 internal constant _ROLE_105 = 1 << 105;
        uint256 internal constant _ROLE_106 = 1 << 106;
        uint256 internal constant _ROLE_107 = 1 << 107;
        uint256 internal constant _ROLE_108 = 1 << 108;
        uint256 internal constant _ROLE_109 = 1 << 109;
        uint256 internal constant _ROLE_110 = 1 << 110;
        uint256 internal constant _ROLE_111 = 1 << 111;
        uint256 internal constant _ROLE_112 = 1 << 112;
        uint256 internal constant _ROLE_113 = 1 << 113;
        uint256 internal constant _ROLE_114 = 1 << 114;
        uint256 internal constant _ROLE_115 = 1 << 115;
        uint256 internal constant _ROLE_116 = 1 << 116;
        uint256 internal constant _ROLE_117 = 1 << 117;
        uint256 internal constant _ROLE_118 = 1 << 118;
        uint256 internal constant _ROLE_119 = 1 << 119;
        uint256 internal constant _ROLE_120 = 1 << 120;
        uint256 internal constant _ROLE_121 = 1 << 121;
        uint256 internal constant _ROLE_122 = 1 << 122;
        uint256 internal constant _ROLE_123 = 1 << 123;
        uint256 internal constant _ROLE_124 = 1 << 124;
        uint256 internal constant _ROLE_125 = 1 << 125;
        uint256 internal constant _ROLE_126 = 1 << 126;
        uint256 internal constant _ROLE_127 = 1 << 127;
        uint256 internal constant _ROLE_128 = 1 << 128;
        uint256 internal constant _ROLE_129 = 1 << 129;
        uint256 internal constant _ROLE_130 = 1 << 130;
        uint256 internal constant _ROLE_131 = 1 << 131;
        uint256 internal constant _ROLE_132 = 1 << 132;
        uint256 internal constant _ROLE_133 = 1 << 133;
        uint256 internal constant _ROLE_134 = 1 << 134;
        uint256 internal constant _ROLE_135 = 1 << 135;
        uint256 internal constant _ROLE_136 = 1 << 136;
        uint256 internal constant _ROLE_137 = 1 << 137;
        uint256 internal constant _ROLE_138 = 1 << 138;
        uint256 internal constant _ROLE_139 = 1 << 139;
        uint256 internal constant _ROLE_140 = 1 << 140;
        uint256 internal constant _ROLE_141 = 1 << 141;
        uint256 internal constant _ROLE_142 = 1 << 142;
        uint256 internal constant _ROLE_143 = 1 << 143;
        uint256 internal constant _ROLE_144 = 1 << 144;
        uint256 internal constant _ROLE_145 = 1 << 145;
        uint256 internal constant _ROLE_146 = 1 << 146;
        uint256 internal constant _ROLE_147 = 1 << 147;
        uint256 internal constant _ROLE_148 = 1 << 148;
        uint256 internal constant _ROLE_149 = 1 << 149;
        uint256 internal constant _ROLE_150 = 1 << 150;
        uint256 internal constant _ROLE_151 = 1 << 151;
        uint256 internal constant _ROLE_152 = 1 << 152;
        uint256 internal constant _ROLE_153 = 1 << 153;
        uint256 internal constant _ROLE_154 = 1 << 154;
        uint256 internal constant _ROLE_155 = 1 << 155;
        uint256 internal constant _ROLE_156 = 1 << 156;
        uint256 internal constant _ROLE_157 = 1 << 157;
        uint256 internal constant _ROLE_158 = 1 << 158;
        uint256 internal constant _ROLE_159 = 1 << 159;
        uint256 internal constant _ROLE_160 = 1 << 160;
        uint256 internal constant _ROLE_161 = 1 << 161;
        uint256 internal constant _ROLE_162 = 1 << 162;
        uint256 internal constant _ROLE_163 = 1 << 163;
        uint256 internal constant _ROLE_164 = 1 << 164;
        uint256 internal constant _ROLE_165 = 1 << 165;
        uint256 internal constant _ROLE_166 = 1 << 166;
        uint256 internal constant _ROLE_167 = 1 << 167;
        uint256 internal constant _ROLE_168 = 1 << 168;
        uint256 internal constant _ROLE_169 = 1 << 169;
        uint256 internal constant _ROLE_170 = 1 << 170;
        uint256 internal constant _ROLE_171 = 1 << 171;
        uint256 internal constant _ROLE_172 = 1 << 172;
        uint256 internal constant _ROLE_173 = 1 << 173;
        uint256 internal constant _ROLE_174 = 1 << 174;
        uint256 internal constant _ROLE_175 = 1 << 175;
        uint256 internal constant _ROLE_176 = 1 << 176;
        uint256 internal constant _ROLE_177 = 1 << 177;
        uint256 internal constant _ROLE_178 = 1 << 178;
        uint256 internal constant _ROLE_179 = 1 << 179;
        uint256 internal constant _ROLE_180 = 1 << 180;
        uint256 internal constant _ROLE_181 = 1 << 181;
        uint256 internal constant _ROLE_182 = 1 << 182;
        uint256 internal constant _ROLE_183 = 1 << 183;
        uint256 internal constant _ROLE_184 = 1 << 184;
        uint256 internal constant _ROLE_185 = 1 << 185;
        uint256 internal constant _ROLE_186 = 1 << 186;
        uint256 internal constant _ROLE_187 = 1 << 187;
        uint256 internal constant _ROLE_188 = 1 << 188;
        uint256 internal constant _ROLE_189 = 1 << 189;
        uint256 internal constant _ROLE_190 = 1 << 190;
        uint256 internal constant _ROLE_191 = 1 << 191;
        uint256 internal constant _ROLE_192 = 1 << 192;
        uint256 internal constant _ROLE_193 = 1 << 193;
        uint256 internal constant _ROLE_194 = 1 << 194;
        uint256 internal constant _ROLE_195 = 1 << 195;
        uint256 internal constant _ROLE_196 = 1 << 196;
        uint256 internal constant _ROLE_197 = 1 << 197;
        uint256 internal constant _ROLE_198 = 1 << 198;
        uint256 internal constant _ROLE_199 = 1 << 199;
        uint256 internal constant _ROLE_200 = 1 << 200;
        uint256 internal constant _ROLE_201 = 1 << 201;
        uint256 internal constant _ROLE_202 = 1 << 202;
        uint256 internal constant _ROLE_203 = 1 << 203;
        uint256 internal constant _ROLE_204 = 1 << 204;
        uint256 internal constant _ROLE_205 = 1 << 205;
        uint256 internal constant _ROLE_206 = 1 << 206;
        uint256 internal constant _ROLE_207 = 1 << 207;
        uint256 internal constant _ROLE_208 = 1 << 208;
        uint256 internal constant _ROLE_209 = 1 << 209;
        uint256 internal constant _ROLE_210 = 1 << 210;
        uint256 internal constant _ROLE_211 = 1 << 211;
        uint256 internal constant _ROLE_212 = 1 << 212;
        uint256 internal constant _ROLE_213 = 1 << 213;
        uint256 internal constant _ROLE_214 = 1 << 214;
        uint256 internal constant _ROLE_215 = 1 << 215;
        uint256 internal constant _ROLE_216 = 1 << 216;
        uint256 internal constant _ROLE_217 = 1 << 217;
        uint256 internal constant _ROLE_218 = 1 << 218;
        uint256 internal constant _ROLE_219 = 1 << 219;
        uint256 internal constant _ROLE_220 = 1 << 220;
        uint256 internal constant _ROLE_221 = 1 << 221;
        uint256 internal constant _ROLE_222 = 1 << 222;
        uint256 internal constant _ROLE_223 = 1 << 223;
        uint256 internal constant _ROLE_224 = 1 << 224;
        uint256 internal constant _ROLE_225 = 1 << 225;
        uint256 internal constant _ROLE_226 = 1 << 226;
        uint256 internal constant _ROLE_227 = 1 << 227;
        uint256 internal constant _ROLE_228 = 1 << 228;
        uint256 internal constant _ROLE_229 = 1 << 229;
        uint256 internal constant _ROLE_230 = 1 << 230;
        uint256 internal constant _ROLE_231 = 1 << 231;
        uint256 internal constant _ROLE_232 = 1 << 232;
        uint256 internal constant _ROLE_233 = 1 << 233;
        uint256 internal constant _ROLE_234 = 1 << 234;
        uint256 internal constant _ROLE_235 = 1 << 235;
        uint256 internal constant _ROLE_236 = 1 << 236;
        uint256 internal constant _ROLE_237 = 1 << 237;
        uint256 internal constant _ROLE_238 = 1 << 238;
        uint256 internal constant _ROLE_239 = 1 << 239;
        uint256 internal constant _ROLE_240 = 1 << 240;
        uint256 internal constant _ROLE_241 = 1 << 241;
        uint256 internal constant _ROLE_242 = 1 << 242;
        uint256 internal constant _ROLE_243 = 1 << 243;
        uint256 internal constant _ROLE_244 = 1 << 244;
        uint256 internal constant _ROLE_245 = 1 << 245;
        uint256 internal constant _ROLE_246 = 1 << 246;
        uint256 internal constant _ROLE_247 = 1 << 247;
        uint256 internal constant _ROLE_248 = 1 << 248;
        uint256 internal constant _ROLE_249 = 1 << 249;
        uint256 internal constant _ROLE_250 = 1 << 250;
        uint256 internal constant _ROLE_251 = 1 << 251;
        uint256 internal constant _ROLE_252 = 1 << 252;
        uint256 internal constant _ROLE_253 = 1 << 253;
        uint256 internal constant _ROLE_254 = 1 << 254;
        uint256 internal constant _ROLE_255 = 1 << 255;
    }
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    import { IERC721AUpgradeable } from "chiru-labs/ERC721A-Upgradeable/IERC721AUpgradeable.sol";
    import { IERC2981Upgradeable } from "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol";
    import { IERC165Upgradeable } from "openzeppelin-upgradeable/utils/introspection/IERC165Upgradeable.sol";
    import { IMetadataModule } from "./IMetadataModule.sol";
    /**
     * @dev The information pertaining to this edition.
     */
    struct EditionInfo {
        // Base URI for the tokenId.
        string baseURI;
        // Contract URI for OpenSea storefront.
        string contractURI;
        // Name of the collection.
        string name;
        // Symbol of the collection.
        string symbol;
        // Address that receives primary and secondary royalties.
        address fundingRecipient;
        // The current max mintable amount;
        uint32 editionMaxMintable;
        // The lower limit of the maximum number of tokens that can be minted.
        uint32 editionMaxMintableUpper;
        // The upper limit of the maximum number of tokens that can be minted.
        uint32 editionMaxMintableLower;
        // The timestamp (in seconds since unix epoch) after which the
        // max amount of tokens mintable will drop from
        // `maxMintableUpper` to `maxMintableLower`.
        uint32 editionCutoffTime;
        // Address of metadata module, address(0x00) if not used.
        address metadataModule;
        // The current mint randomness value.
        uint256 mintRandomness;
        // The royalty BPS (basis points).
        uint16 royaltyBPS;
        // Whether the mint randomness is enabled.
        bool mintRandomnessEnabled;
        // Whether the mint has concluded.
        bool mintConcluded;
        // Whether the metadata has been frozen.
        bool isMetadataFrozen;
        // Next token ID to be minted.
        uint256 nextTokenId;
        // Total number of tokens burned.
        uint256 totalBurned;
        // Total number of tokens minted.
        uint256 totalMinted;
        // Total number of tokens currently in existence.
        uint256 totalSupply;
    }
    /**
     * @title ISoundEditionV1
     * @notice The interface for Sound edition contracts.
     */
    interface ISoundEditionV1 is IERC721AUpgradeable, IERC2981Upgradeable {
        // =============================================================
        //                            EVENTS
        // =============================================================
        /**
         * @dev Emitted when the metadata module is set.
         * @param metadataModule the address of the metadata module.
         */
        event MetadataModuleSet(address metadataModule);
        /**
         * @dev Emitted when the `baseURI` is set.
         * @param baseURI the base URI of the edition.
         */
        event BaseURISet(string baseURI);
        /**
         * @dev Emitted when the `contractURI` is set.
         * @param contractURI The contract URI of the edition.
         */
        event ContractURISet(string contractURI);
        /**
         * @dev Emitted when the metadata is frozen (e.g.: `baseURI` can no longer be changed).
         * @param metadataModule The address of the metadata module.
         * @param baseURI        The base URI of the edition.
         * @param contractURI    The contract URI of the edition.
         */
        event MetadataFrozen(address metadataModule, string baseURI, string contractURI);
        /**
         * @dev Emitted when the `fundingRecipient` is set.
         * @param fundingRecipient The address of the funding recipient.
         */
        event FundingRecipientSet(address fundingRecipient);
        /**
         * @dev Emitted when the `royaltyBPS` is set.
         * @param bps The new royalty, measured in basis points.
         */
        event RoyaltySet(uint16 bps);
        /**
         * @dev Emitted when the edition's maximum mintable token quantity range is set.
         * @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
         * @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
         */
        event EditionMaxMintableRangeSet(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_);
        /**
         * @dev Emitted when the edition's cutoff time set.
         * @param editionCutoffTime_ The timestamp.
         */
        event EditionCutoffTimeSet(uint32 editionCutoffTime_);
        /**
         * @dev Emitted when the `mintRandomnessEnabled` is set.
         * @param mintRandomnessEnabled_ The boolean value.
         */
        event MintRandomnessEnabledSet(bool mintRandomnessEnabled_);
        /**
         * @dev Emitted upon initialization.
         * @param edition_                 The address of the edition.
         * @param name_                    Name of the collection.
         * @param symbol_                  Symbol of the collection.
         * @param metadataModule_          Address of metadata module, address(0x00) if not used.
         * @param baseURI_                 Base URI.
         * @param contractURI_             Contract URI for OpenSea storefront.
         * @param fundingRecipient_        Address that receives primary and secondary royalties.
         * @param royaltyBPS_              Royalty amount in bps (basis points).
         * @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
         * @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
         * @param editionCutoffTime_       The timestamp after which `editionMaxMintable` drops from
         *                                 `editionMaxMintableUpper` to
         *                                 `max(_totalMinted(), editionMaxMintableLower)`.
         * @param flags_                   The bitwise OR result of the initialization flags.
         *                                 See: {METADATA_IS_FROZEN_FLAG}
         *                                 See: {MINT_RANDOMNESS_ENABLED_FLAG}
         */
        event SoundEditionInitialized(
            address indexed edition_,
            string name_,
            string symbol_,
            address metadataModule_,
            string baseURI_,
            string contractURI_,
            address fundingRecipient_,
            uint16 royaltyBPS_,
            uint32 editionMaxMintableLower_,
            uint32 editionMaxMintableUpper_,
            uint32 editionCutoffTime_,
            uint8 flags_
        );
        // =============================================================
        //                            ERRORS
        // =============================================================
        /**
         * @dev The edition's metadata is frozen (e.g.: `baseURI` can no longer be changed).
         */
        error MetadataIsFrozen();
        /**
         * @dev The given `royaltyBPS` is invalid.
         */
        error InvalidRoyaltyBPS();
        /**
         * @dev The given `randomnessLockedAfterMinted` value is invalid.
         */
        error InvalidRandomnessLock();
        /**
         * @dev The requested quantity exceeds the edition's remaining mintable token quantity.
         * @param available The number of tokens remaining available for mint.
         */
        error ExceedsEditionAvailableSupply(uint32 available);
        /**
         * @dev The given amount is invalid.
         */
        error InvalidAmount();
        /**
         * @dev The given `fundingRecipient` address is invalid.
         */
        error InvalidFundingRecipient();
        /**
         * @dev The `editionMaxMintableLower` must not be greater than `editionMaxMintableUpper`.
         */
        error InvalidEditionMaxMintableRange();
        /**
         * @dev The `editionMaxMintable` has already been reached.
         */
        error MaximumHasAlreadyBeenReached();
        /**
         * @dev The mint `quantity` cannot exceed `ADDRESS_BATCH_MINT_LIMIT` tokens.
         */
        error ExceedsAddressBatchMintLimit();
        /**
         * @dev The mint randomness has already been revealed.
         */
        error MintRandomnessAlreadyRevealed();
        /**
         * @dev No addresses to airdrop.
         */
        error NoAddressesToAirdrop();
        /**
         * @dev The mint has already concluded.
         */
        error MintHasConcluded();
        /**
         * @dev Cannot perform the operation after a token has been minted.
         */
        error MintsAlreadyExist();
        // =============================================================
        //               PUBLIC / EXTERNAL WRITE FUNCTIONS
        // =============================================================
        /**
         * @dev Initializes the contract.
         * @param name_                    Name of the collection.
         * @param symbol_                  Symbol of the collection.
         * @param metadataModule_          Address of metadata module, address(0x00) if not used.
         * @param baseURI_                 Base URI.
         * @param contractURI_             Contract URI for OpenSea storefront.
         * @param fundingRecipient_        Address that receives primary and secondary royalties.
         * @param royaltyBPS_              Royalty amount in bps (basis points).
         * @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
         * @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
         * @param editionCutoffTime_       The timestamp after which `editionMaxMintable` drops from
         *                                 `editionMaxMintableUpper` to
         *                                 `max(_totalMinted(), editionMaxMintableLower)`.
         * @param flags_                   The bitwise OR result of the initialization flags.
         *                                 See: {METADATA_IS_FROZEN_FLAG}
         *                                 See: {MINT_RANDOMNESS_ENABLED_FLAG}
         */
        function initialize(
            string memory name_,
            string memory symbol_,
            address metadataModule_,
            string memory baseURI_,
            string memory contractURI_,
            address fundingRecipient_,
            uint16 royaltyBPS_,
            uint32 editionMaxMintableLower_,
            uint32 editionMaxMintableUpper_,
            uint32 editionCutoffTime_,
            uint8 flags_
        ) external;
        /**
         * @dev Mints `quantity` tokens to addrress `to`
         *      Each token will be assigned a token ID that is consecutively increasing.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have either the
         *   `ADMIN_ROLE`, `MINTER_ROLE`, which can be granted via {grantRole}.
         *   Multiple minters, such as different minter contracts,
         *   can be authorized simultaneously.
         *
         * @param to       Address to mint to.
         * @param quantity Number of tokens to mint.
         * @return fromTokenId The first token ID minted.
         */
        function mint(address to, uint256 quantity) external payable returns (uint256 fromTokenId);
        /**
         * @dev Mints `quantity` tokens to each of the addresses in `to`.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the
         *   `ADMIN_ROLE`, which can be granted via {grantRole}.
         *
         * @param to           Address to mint to.
         * @param quantity     Number of tokens to mint.
         * @return fromTokenId The first token ID minted.
         */
        function airdrop(address[] calldata to, uint256 quantity) external returns (uint256 fromTokenId);
        /**
         * @dev Withdraws collected ETH royalties to the fundingRecipient.
         */
        function withdrawETH() external;
        /**
         * @dev Withdraws collected ERC20 royalties to the fundingRecipient.
         * @param tokens array of ERC20 tokens to withdraw
         */
        function withdrawERC20(address[] calldata tokens) external;
        /**
         * @dev Sets metadata module.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param metadataModule Address of metadata module.
         */
        function setMetadataModule(address metadataModule) external;
        /**
         * @dev Sets global base URI.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param baseURI The base URI to be set.
         */
        function setBaseURI(string memory baseURI) external;
        /**
         * @dev Sets contract URI.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param contractURI The contract URI to be set.
         */
        function setContractURI(string memory contractURI) external;
        /**
         * @dev Freezes metadata by preventing any more changes to base URI.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         */
        function freezeMetadata() external;
        /**
         * @dev Sets funding recipient address.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param fundingRecipient Address to be set as the new funding recipient.
         */
        function setFundingRecipient(address fundingRecipient) external;
        /**
         * @dev Sets royalty amount in bps (basis points).
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param bps The new royalty basis points to be set.
         */
        function setRoyalty(uint16 bps) external;
        /**
         * @dev Sets the edition max mintable range.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
         * @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
         */
        function setEditionMaxMintableRange(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_) external;
        /**
         * @dev Sets the timestamp after which, the `editionMaxMintable` drops
         *      from `editionMaxMintableUpper` to `editionMaxMintableLower.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param editionCutoffTime_ The timestamp.
         */
        function setEditionCutoffTime(uint32 editionCutoffTime_) external;
        /**
         * @dev Sets whether the `mintRandomness` is enabled.
         *
         * Calling conditions:
         * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
         *
         * @param mintRandomnessEnabled_ The boolean value.
         */
        function setMintRandomnessEnabled(bool mintRandomnessEnabled_) external;
        // =============================================================
        //               PUBLIC / EXTERNAL VIEW FUNCTIONS
        // =============================================================
        /**
         * @dev Returns the edition info.
         * @return editionInfo The latest value.
         */
        function editionInfo() external view returns (EditionInfo memory editionInfo);
        /**
         * @dev Returns the minter role flag.
         * @return The constant value.
         */
        function MINTER_ROLE() external view returns (uint256);
        /**
         * @dev Returns the admin role flag.
         * @return The constant value.
         */
        function ADMIN_ROLE() external view returns (uint256);
        /**
         * @dev Returns the maximum limit for the mint or airdrop `quantity`.
         *      Prevents the first-time transfer costs for tokens near the end of large mint batches
         *      via ERC721A from becoming too expensive due to the need to scan many storage slots.
         *      See: https://chiru-labs.github.io/ERC721A/#/tips?id=batch-size
         * @return The constant value.
         */
        function ADDRESS_BATCH_MINT_LIMIT() external pure returns (uint256);
        /**
         * @dev Returns the bit flag to freeze the metadata on initialization.
         * @return The constant value.
         */
        function METADATA_IS_FROZEN_FLAG() external pure returns (uint8);
        /**
         * @dev Returns the bit flag to enable the mint randomness feature on initialization.
         * @return The constant value.
         */
        function MINT_RANDOMNESS_ENABLED_FLAG() external pure returns (uint8);
        /**
         * @dev Returns the base token URI for the collection.
         * @return The configured value.
         */
        function baseURI() external view returns (string memory);
        /**
         * @dev Returns the contract URI to be used by Opensea.
         *      See: https://docs.opensea.io/docs/contract-level-metadata
         * @return The configured value.
         */
        function contractURI() external view returns (string memory);
        /**
         * @dev Returns the address of the funding recipient.
         * @return The configured value.
         */
        function fundingRecipient() external view returns (address);
        /**
         * @dev Returns the maximum amount of tokens mintable for this edition.
         * @return The configured value.
         */
        function editionMaxMintable() external view returns (uint32);
        /**
         * @dev Returns the upper bound for the maximum tokens that can be minted for this edition.
         * @return The configured value.
         */
        function editionMaxMintableUpper() external view returns (uint32);
        /**
         * @dev Returns the lower bound for the maximum tokens that can be minted for this edition.
         * @return The configured value.
         */
        function editionMaxMintableLower() external view returns (uint32);
        /**
         * @dev Returns the timestamp after which `editionMaxMintable` drops from
         *      `editionMaxMintableUpper` to `editionMaxMintableLower`.
         * @return The configured value.
         */
        function editionCutoffTime() external view returns (uint32);
        /**
         * @dev Returns the address of the metadata module.
         * @return The configured value.
         */
        function metadataModule() external view returns (address);
        /**
         * @dev Returns the randomness based on latest block hash, which is stored upon each mint.
         *      unless {mintConcluded} is true.
         *      Used for game mechanics like the Sound Golden Egg.
         *      Returns 0 before revealed.
         *      WARNING: This value should NOT be used for any reward of significant monetary
         *      value, due to it being computed via a purely on-chain psuedorandom mechanism.
         * @return The latest value.
         */
        function mintRandomness() external view returns (uint256);
        /**
         * @dev Returns whether the `mintRandomness` has been enabled.
         * @return The configured value.
         */
        function mintRandomnessEnabled() external view returns (bool);
        /**
         * @dev Returns whether the mint has been concluded.
         * @return The latest value.
         */
        function mintConcluded() external view returns (bool);
        /**
         * @dev Returns the royalty basis points.
         * @return The configured value.
         */
        function royaltyBPS() external view returns (uint16);
        /**
         * @dev Returns whether the metadata module is frozen.
         * @return The configured value.
         */
        function isMetadataFrozen() external view returns (bool);
        /**
         * @dev Returns the next token ID to be minted.
         * @return The latest value.
         */
        function nextTokenId() external view returns (uint256);
        /**
         * @dev Returns the number of tokens minted by `owner`.
         * @param owner Address to query for number minted.
         * @return The latest value.
         */
        function numberMinted(address owner) external view returns (uint256);
        /**
         * @dev Returns the number of tokens burned by `owner`.
         * @param owner Address to query for number burned.
         * @return The latest value.
         */
        function numberBurned(address owner) external view returns (uint256);
        /**
         * @dev Returns the total amount of tokens minted.
         * @return The latest value.
         */
        function totalMinted() external view returns (uint256);
        /**
         * @dev Returns the total amount of tokens burned.
         * @return The latest value.
         */
        function totalBurned() external view returns (uint256);
        /**
         * @dev Informs other contracts which interfaces this contract supports.
         *      Required by https://eips.ethereum.org/EIPS/eip-165
         * @param interfaceId The interface id to check.
         * @return Whether the `interfaceId` is supported.
         */
        function supportsInterface(bytes4 interfaceId)
            external
            view
            override(IERC721AUpgradeable, IERC165Upgradeable)
            returns (bool);
    }
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    /**
     * @title IMetadataModule
     * @notice The interface for custom metadata modules.
     */
    interface IMetadataModule {
        /**
         * @dev When implemented, SoundEdition's `tokenURI` redirects execution to this `tokenURI`.
         * @param tokenId The token ID to retrieve the token URI for.
         * @return The token URI string.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    // SPDX-License-Identifier: GPL-3.0-or-later
    pragma solidity ^0.8.16;
    /*
                     â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’â–’
                   ▒███████████████████████████████████████████████████████████
                   ▒███████████████████████████████████████████████████████████
     ▒▓▓▓▓▓▓▓▓▓▓▓▓▓████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓              ████████████████████████████████████████████
     █████████████████████████████▓               ▒▒▒▒▒▒▒▒▒▒▒▒▒██████████████████████████████
     █████████████████████████████▓                            ▒█████████████████████████████
     █████████████████████████████▓                             ▒████████████████████████████
     █████████████████████████████████████████████████████████▓
     ███████████████████████████████████████████████████████████
     ███████████████████████████████████████████████████████████▒
                                  ███████████████████████████████████████████████████████████▒
                                  ▓██████████████████████████████████████████████████████████▒
                                   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████████████████████▒
     █████████████████████████████                             ▒█████████████████████████████▒
     ██████████████████████████████                            ▒█████████████████████████████▒
     ██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒              ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ████████████████████████████████████████████▒             ▒█████████████████████████████▒
     ▒▒▒▒▒▒▒▒▒▒▒▒▒▒███████████████████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                   ▓██████████████████████████████████████████████████████████▒
                   ▓██████████████████████████████████████████████████████████
    */
    import { Base64 } from "solady/utils/Base64.sol";
    library ArweaveURILib {
        // =============================================================
        //                            STRUCTS
        // =============================================================
        struct URI {
            bytes32 arweave;
            string regular;
        }
        // =============================================================
        //                  INTERNAL / PRIVATE HELPERS
        // =============================================================
        /**
         * @dev Helper function for storing a URI that may be an Arweave URI.
         *      Efficiently stores Arweave CIDs by converting them into a single bytes32 word.
         *      The Arweave CID is a base64 encoded sha-256 output (32 bytes when decoded).
         *      See: https://docs.arweave.org/developers/server/http-api
         * @param uri      The URI storage reference.
         * @param value    The string representation of the URI.
         * @param isUpdate Whether this is called in an update.
         */
        function store(
            URI storage uri,
            string memory value,
            bool isUpdate
        ) internal {
            uint256 valueLength;
            bool isArweave;
            assembly {
                // Example: "ar://Hjtz2YLeVyXQkGxKTNcIYfWkKnHioDvfICulzQIAt3E"
                valueLength := mload(value)
                // If the URI is length 48 or 49 (due to a trailing slash).
                if or(eq(valueLength, 48), eq(valueLength, 49)) {
                    // If starts with "ar://".
                    if eq(and(mload(add(value, 5)), 0xffffffffff), 0x61723a2f2f) {
                        isArweave := 1
                        value := add(value, 5)
                        // Sets the length of the `value` to 43,
                        // such that it only contains the CID.
                        mstore(value, 43)
                    }
                }
            }
            if (isArweave) {
                bytes memory decodedCIDBytes = Base64.decode(value);
                bytes32 arweaveCID;
                assembly {
                    arweaveCID := mload(add(decodedCIDBytes, 0x20))
                    // Restore the "ar://".
                    mstore(value, 0x61723a2f2f)
                    // Restore the original position of the `value` pointer.
                    value := sub(value, 5)
                    // Restore the original length.
                    mstore(value, valueLength)
                }
                uri.arweave = arweaveCID;
                if (isUpdate) delete uri.regular;
            } else {
                uri.regular = value;
                if (isUpdate) delete uri.arweave;
            }
        }
        /**
         * @dev Equivalent to `store(uri, value, false)`.
         * @param uri      The URI storage reference.
         * @param value    The string representation of the URI.
         */
        function initialize(URI storage uri, string memory value) internal {
            store(uri, value, false);
        }
        /**
         * @dev Equivalent to `store(uri, value, true)`.
         * @param uri      The URI storage reference.
         * @param value    The string representation of the URI.
         */
        function update(URI storage uri, string memory value) internal {
            store(uri, value, true);
        }
        /**
         * @dev Helper function for retrieving a URI stored with {_setURI}.
         * @param uri The URI storage reference.
         */
        function load(URI storage uri) internal view returns (string memory) {
            bytes32 arweaveCID = uri.arweave;
            if (arweaveCID == bytes32(0)) {
                return uri.regular;
            }
            bytes memory decoded;
            assembly {
                // Copy `arweaveCID`.
                // First, grab the free memory pointer.
                decoded := mload(0x40)
                // Allocate 2 slots.
                // 1 slot for the length, 1 slot for the bytes.
                mstore(0x40, add(decoded, 0x40))
                mstore(decoded, 0x20) // Set the length (32 bytes).
                mstore(add(decoded, 0x20), arweaveCID) // Set the bytes.
            }
            return string.concat("ar://", Base64.encode(decoded, true, true), "/");
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    library ERC721AStorage {
        // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
        struct TokenApprovalRef {
            address value;
        }
        struct Layout {
            // =============================================================
            //                            STORAGE
            // =============================================================
            // The next token ID to be minted.
            uint256 _currentIndex;
            // The number of tokens burned.
            uint256 _burnCounter;
            // Token name
            string _name;
            // Token symbol
            string _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) _packedOwnerships;
            // Mapping owner address to address data.
            //
            // Bits Layout:
            // - [0..63]    `balance`
            // - [64..127]  `numberMinted`
            // - [128..191] `numberBurned`
            // - [192..255] `aux`
            mapping(address => uint256) _packedAddressData;
            // Mapping from token ID to approved address.
            mapping(uint256 => ERC721AStorage.TokenApprovalRef) _tokenApprovals;
            // Mapping from owner to operator approvals
            mapping(address => mapping(address => bool)) _operatorApprovals;
        }
        bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.ERC721A');
        function layout() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev This is a base contract to aid in writing upgradeable diamond facet contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     */
    import {ERC721A__InitializableStorage} from './ERC721A__InitializableStorage.sol';
    abstract contract ERC721A__Initializable {
        using ERC721A__InitializableStorage for ERC721A__InitializableStorage.Layout;
        /**
         * @dev Modifier to protect an initializer function from being invoked twice.
         */
        modifier initializerERC721A() {
            // If the contract is initializing we ignore whether _initialized is set in order to support multiple
            // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
            // contract may have been reentered.
            require(
                ERC721A__InitializableStorage.layout()._initializing
                    ? _isConstructor()
                    : !ERC721A__InitializableStorage.layout()._initialized,
                'ERC721A__Initializable: contract is already initialized'
            );
            bool isTopLevelCall = !ERC721A__InitializableStorage.layout()._initializing;
            if (isTopLevelCall) {
                ERC721A__InitializableStorage.layout()._initializing = true;
                ERC721A__InitializableStorage.layout()._initialized = true;
            }
            _;
            if (isTopLevelCall) {
                ERC721A__InitializableStorage.layout()._initializing = false;
            }
        }
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} modifier, directly or indirectly.
         */
        modifier onlyInitializingERC721A() {
            require(
                ERC721A__InitializableStorage.layout()._initializing,
                'ERC721A__Initializable: contract is not initializing'
            );
            _;
        }
        /// @dev Returns true if and only if the function is running in the constructor
        function _isConstructor() private view returns (bool) {
            // extcodesize checks the size of the code stored in an address, and
            // address returns the current address. Since the code is still not
            // deployed when running a constructor, any checks on its code size will
            // yield zero, making it an effective way to detect if a contract is
            // under construction or not.
            address self = address(this);
            uint256 cs;
            assembly {
                cs := extcodesize(self)
            }
            return cs == 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import '../IERC721AUpgradeable.sol';
    /**
     * @dev Interface of ERC721AQueryable.
     */
    interface IERC721AQueryableUpgradeable is IERC721AUpgradeable {
        /**
         * Invalid query range (`start` >= `stop`).
         */
        error InvalidQueryRange();
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view returns (uint256[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view returns (uint256[] memory);
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.2
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import '../IERC721AUpgradeable.sol';
    /**
     * @dev Interface of ERC721ABurnable.
     */
    interface IERC721ABurnableUpgradeable is IERC721AUpgradeable {
        /**
         * @dev Burns `tokenId`. See {ERC721A-_burn}.
         *
         * Requirements:
         *
         * - The caller must own `tokenId` or be an approved operator.
         */
        function burn(uint256 tokenId) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165Upgradeable {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Library to encode strings in Base64.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
    /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>.
    library Base64 {
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// See: https://datatracker.ietf.org/doc/html/rfc4648
        /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
        /// @param noPadding Whether to strip away the padding.
        function encode(
            bytes memory data,
            bool fileSafe,
            bool noPadding
        ) internal pure returns (string memory result) {
            assembly {
                let dataLength := mload(data)
                if dataLength {
                    // Multiply by 4/3 rounded up.
                    // The `shl(2, ...)` is equivalent to multiplying by 4.
                    let encodedLength := shl(2, div(add(dataLength, 2), 3))
                    // Set `result` to point to the start of the free memory.
                    result := mload(0x40)
                    // Store the table into the scratch space.
                    // Offsetted by -1 byte so that the `mload` will load the character.
                    // We will rewrite the free memory pointer at `0x40` later with
                    // the allocated size.
                    // The magic constant 0x0230 will translate "-_" + "+/".
                    mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                    mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230)))
                    // Skip the first slot, which stores the length.
                    let ptr := add(result, 0x20)
                    let end := add(ptr, encodedLength)
                    // Run over the input, 3 bytes at a time.
                    // prettier-ignore
                    for {} 1 {} {
                        data := add(data, 3) // Advance 3 bytes.
                        let input := mload(data)
                        // Write 4 bytes. Optimized for fewer stack operations.
                        mstore8(    ptr    , mload(and(shr(18, input), 0x3F)))
                        mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
                        mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
                        mstore8(add(ptr, 3), mload(and(        input , 0x3F)))
                        
                        ptr := add(ptr, 4) // Advance 4 bytes.
                        // prettier-ignore
                        if iszero(lt(ptr, end)) { break }
                    }
                    let r := mod(dataLength, 3)
                    switch noPadding
                    case 0 {
                        // Offset `ptr` and pad with '='. We can simply write over the end.
                        mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.
                        mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
                        // Write the length of the string.
                        mstore(result, encodedLength)
                    }
                    default {
                        // Write the length of the string.
                        mstore(result, sub(encodedLength, add(iszero(iszero(r)), eq(r, 1))))
                    }
                    // Allocate the memory for the string.
                    // Add 31 and mask with `not(31)` to round the
                    // free memory pointer up the next multiple of 32.
                    mstore(0x40, and(add(end, 31), not(31)))
                }
            }
        }
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// Equivalent to `encode(data, false, false)`.
        function encode(bytes memory data) internal pure returns (string memory result) {
            result = encode(data, false, false);
        }
        /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
        /// Equivalent to `encode(data, fileSafe, false)`.
        function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) {
            result = encode(data, fileSafe, false);
        }
        /// @dev Encodes base64 encoded `data`.
        ///
        /// Supports:
        /// - RFC 4648 (both standard and file-safe mode).
        /// - RFC 3501 (63: ',').
        ///
        /// Does not support:
        /// - Line breaks.
        ///
        /// Note: For performance reasons,
        /// this function will NOT revert on invalid `data` inputs.
        /// Outputs for invalid inputs will simply be undefined behaviour.
        /// It is the user's responsibility to ensure that the `data`
        /// is a valid base64 encoded string.
        function decode(string memory data) internal pure returns (bytes memory result) {
            assembly {
                let dataLength := mload(data)
                if dataLength {
                    let end := add(data, dataLength)
                    let decodedLength := mul(shr(2, dataLength), 3)
                    switch and(dataLength, 3)
                    case 0 {
                        // If padded.
                        decodedLength := sub(
                            decodedLength,
                            add(eq(and(mload(end), 0xFF), 0x3d), eq(and(mload(end), 0xFFFF), 0x3d3d))
                        )
                    }
                    default {
                        // If non-padded.
                        decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                    }
                    result := mload(0x40)
                    // Write the length of the string.
                    mstore(result, decodedLength)
                    // Skip the first slot, which stores the length.
                    let ptr := add(result, 0x20)
                    // Load the table into the scratch space.
                    // Constants are optimized for smaller bytecode with zero gas overhead.
                    // `m` also doubles as the mask of the upper 6 bits.
                    let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                    mstore(0x5b, m)
                    mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                    mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)
                    // prettier-ignore
                    for {} 1 {} {
                        // Read 4 bytes.
                        data := add(data, 4)
                        let input := mload(data)
                        // Write 3 bytes.
                        mstore(ptr, or(
                            and(m, mload(byte(28, input))),
                            shr(6, or(
                                and(m, mload(byte(29, input))),
                                shr(6, or(
                                    and(m, mload(byte(30, input))),
                                    shr(6, mload(byte(31, input)))
                                ))
                            ))
                        ))
                        ptr := add(ptr, 3)
                        
                        // prettier-ignore
                        if iszero(lt(data, end)) { break }
                    }
                    // Allocate the memory for the string.
                    // Add 32 + 31 and mask with `not(31)` to round the
                    // free memory pointer up the next multiple of 32.
                    mstore(0x40, and(add(add(result, decodedLength), 63), not(31)))
                    // Restore the zero slot.
                    mstore(0x60, 0)
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /**
     * @dev This is a base storage for the  initialization function for upgradeable diamond facet contracts
     **/
    library ERC721A__InitializableStorage {
        struct Layout {
            /*
             * Indicates that the contract has been initialized.
             */
            bool _initialized;
            /*
             * Indicates that the contract is in the process of being initialized.
             */
            bool _initializing;
        }
        bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.initializable.facet');
        function layout() internal pure returns (Layout storage l) {
            bytes32 slot = STORAGE_SLOT;
            assembly {
                l.slot := slot
            }
        }
    }