ETH Price: $2,403.99 (-4.67%)

Transaction Decoder

Block:
15770087 at Oct-17-2022 08:03:35 PM +UTC
Transaction Fee:
0.004267952233638548 ETH $10.26
Gas Used:
122,414 Gas / 34.864902982 Gwei

Emitted Events:

380 SoundEditionV1.Transfer( from=0x00000000...000000000, to=[Sender] 0x372d3a8b2ba0f5255112894770aa07852841018c, tokenId=5 )
381 RangeEditionMinter.Minted( edition=SoundEditionV1, mintId=48, buyer=[Sender] 0x372d3a8b2ba0f5255112894770aa07852841018c, fromTokenId=5, quantity=1, requiredEtherValue=100000000000000000, platformFee=5000000000000000, affiliateFee=0, affiliate=0x00000000...000000000, affiliated=False )

Account State Difference:

  Address   Before After State Difference Code
0x372d3A8b...52841018C
2.007744163821134563 Eth
Nonce: 760
1.903476211587496015 Eth
Nonce: 761
0.104267952233638548
0x65c25FAD...14B708596 2.171 Eth2.176 Eth0.005
0xD6426d2f...9876DFa25 0.285 Eth0.38 Eth0.095
(bloXroute: Max Profit Builder)
2.784335341769587634 Eth2.784518962769587634 Eth0.000183621

Execution Trace

ETH 0.1 RangeEditionMinter.mint( edition=0xD6426d2f3317f9D1211d29E42bE55419876DFa25, mintId=48, quantity=1, affiliate=0x0000000000000000000000000000000000000000 )
  • SoundEditionV1.numberMinted( owner=0x372d3A8b2ba0f5255112894770aa07852841018C ) => ( 0 )
    • SoundEditionV1.numberMinted( owner=0x372d3A8b2ba0f5255112894770aa07852841018C ) => ( 0 )
    • SoundFeeRegistry.platformFee( requiredEtherValue=100000000000000000 ) => ( fee=5000000000000000 )
    • ETH 0.095 SoundEditionV1.mint( to=0x372d3A8b2ba0f5255112894770aa07852841018C, quantity=1 ) => ( fromTokenId=5 )
      • ETH 0.095 SoundEditionV1.mint( to=0x372d3A8b2ba0f5255112894770aa07852841018C, quantity=1 ) => ( fromTokenId=5 )
        File 1 of 4: RangeEditionMinter
        // SPDX-License-Identifier: GPL-3.0-or-later
        pragma solidity ^0.8.16;
        import { IERC165 } from "openzeppelin/utils/introspection/IERC165.sol";
        import { ISoundFeeRegistry } from "@core/interfaces/ISoundFeeRegistry.sol";
        import { IRangeEditionMinter, EditionMintData, MintInfo } from "./interfaces/IRangeEditionMinter.sol";
        import { BaseMinter } from "./BaseMinter.sol";
        import { IMinterModule } from "@core/interfaces/IMinterModule.sol";
        import { ISoundEditionV1 } from "@core/interfaces/ISoundEditionV1.sol";
        /*
         * @title RangeEditionMinter
         * @notice Module for range edition mints of Sound editions.
         * @author Sound.xyz
         */
        contract RangeEditionMinter is IRangeEditionMinter, BaseMinter {
            // =============================================================
            //                            STORAGE
            // =============================================================
            /**
             * @dev Edition mint data
             * edition => mintId => EditionMintData
             */
            mapping(address => mapping(uint128 => EditionMintData)) internal _editionMintData;
            // =============================================================
            //                          CONSTRUCTOR
            // =============================================================
            constructor(ISoundFeeRegistry feeRegistry_) BaseMinter(feeRegistry_) {}
            // =============================================================
            //               PUBLIC / EXTERNAL WRITE FUNCTIONS
            // =============================================================
            /**
             * @inheritdoc IRangeEditionMinter
             */
            function createEditionMint(
                address edition,
                uint96 price,
                uint32 startTime,
                uint32 cutoffTime,
                uint32 endTime,
                uint16 affiliateFeeBPS,
                uint32 maxMintableLower,
                uint32 maxMintableUpper,
                uint32 maxMintablePerAccount
            ) public onlyValidCombinedTimeRange(startTime, cutoffTime, endTime) returns (uint128 mintId) {
                if (maxMintableLower > maxMintableUpper) revert InvalidMaxMintableRange();
                if (maxMintablePerAccount == 0) revert MaxMintablePerAccountIsZero();
                mintId = _createEditionMint(edition, startTime, endTime, affiliateFeeBPS);
                EditionMintData storage data = _editionMintData[edition][mintId];
                data.price = price;
                data.cutoffTime = cutoffTime;
                data.maxMintableLower = maxMintableLower;
                data.maxMintableUpper = maxMintableUpper;
                data.maxMintablePerAccount = maxMintablePerAccount;
                // prettier-ignore
                emit RangeEditionMintCreated(
                    edition,
                    mintId,
                    price,
                    startTime,
                    cutoffTime,
                    endTime,
                    affiliateFeeBPS,
                    maxMintableLower,
                    maxMintableUpper,
                    maxMintablePerAccount
                );
            }
            /**
             * @inheritdoc IRangeEditionMinter
             */
            function mint(
                address edition,
                uint128 mintId,
                uint32 quantity,
                address affiliate
            ) public payable {
                EditionMintData storage data = _editionMintData[edition][mintId];
                uint32 _maxMintable = _getMaxMintable(data);
                // Increase `totalMinted` by `quantity`.
                // Require that the increased value does not exceed `maxMintable`.
                data.totalMinted = _incrementTotalMinted(data.totalMinted, quantity, _maxMintable);
                unchecked {
                    // Check the additional `requestedQuantity` does not exceed the maximum mintable per account.
                    uint256 numberMinted = ISoundEditionV1(edition).numberMinted(msg.sender);
                    // Won't overflow. The total number of tokens minted in `edition` won't exceed `type(uint32).max`,
                    // and `quantity` has 32 bits.
                    if (numberMinted + quantity > data.maxMintablePerAccount) revert ExceedsMaxPerAccount();
                }
                _mint(edition, mintId, quantity, affiliate);
            }
            /**
             * @inheritdoc IRangeEditionMinter
             */
            function setTimeRange(
                address edition,
                uint128 mintId,
                uint32 startTime,
                uint32 cutoffTime,
                uint32 endTime
            ) public onlyEditionOwnerOrAdmin(edition) onlyValidCombinedTimeRange(startTime, cutoffTime, endTime) {
                // Set cutoffTime first, as its stored value gets validated later in the execution.
                EditionMintData storage data = _editionMintData[edition][mintId];
                data.cutoffTime = cutoffTime;
                // This calls the overriden `setTimeRange`, which will check that
                // `startTime < cutoffTime < endTime`.
                RangeEditionMinter.setTimeRange(edition, mintId, startTime, endTime);
                emit CutoffTimeSet(edition, mintId, cutoffTime);
            }
            /**
             * @inheritdoc BaseMinter
             */
            function setTimeRange(
                address edition,
                uint128 mintId,
                uint32 startTime,
                uint32 endTime
            ) public override(BaseMinter, IMinterModule) onlyEditionOwnerOrAdmin(edition) {
                EditionMintData storage data = _editionMintData[edition][mintId];
                if (!(startTime < data.cutoffTime && data.cutoffTime < endTime)) revert InvalidTimeRange();
                BaseMinter.setTimeRange(edition, mintId, startTime, endTime);
            }
            /**
             * @inheritdoc IRangeEditionMinter
             */
            function setMaxMintableRange(
                address edition,
                uint128 mintId,
                uint32 maxMintableLower,
                uint32 maxMintableUpper
            ) public onlyEditionOwnerOrAdmin(edition) {
                if (maxMintableLower > maxMintableUpper) revert InvalidMaxMintableRange();
                EditionMintData storage data = _editionMintData[edition][mintId];
                data.maxMintableLower = maxMintableLower;
                data.maxMintableUpper = maxMintableUpper;
                emit MaxMintableRangeSet(edition, mintId, maxMintableLower, maxMintableUpper);
            }
            /**
             * @inheritdoc IRangeEditionMinter
             */
            function setPrice(
                address edition,
                uint128 mintId,
                uint96 price
            ) public onlyEditionOwnerOrAdmin(edition) {
                _editionMintData[edition][mintId].price = price;
                emit PriceSet(edition, mintId, price);
            }
            /**
             * @inheritdoc IRangeEditionMinter
             */
            function setMaxMintablePerAccount(
                address edition,
                uint128 mintId,
                uint32 maxMintablePerAccount
            ) public onlyEditionOwnerOrAdmin(edition) {
                if (maxMintablePerAccount == 0) revert MaxMintablePerAccountIsZero();
                _editionMintData[edition][mintId].maxMintablePerAccount = maxMintablePerAccount;
                emit MaxMintablePerAccountSet(edition, mintId, maxMintablePerAccount);
            }
            // =============================================================
            //               PUBLIC / EXTERNAL VIEW FUNCTIONS
            // =============================================================
            /**
             * @inheritdoc IMinterModule
             */
            function totalPrice(
                address edition,
                uint128 mintId,
                address, /* minter */
                uint32 quantity
            ) public view virtual override(BaseMinter, IMinterModule) returns (uint128) {
                unchecked {
                    // Will not overflow, as `price` is 96 bits, and `quantity` is 32 bits. 96 + 32 = 128.
                    return uint128(uint256(_editionMintData[edition][mintId].price) * uint256(quantity));
                }
            }
            /**
             * @inheritdoc IRangeEditionMinter
             */
            function mintInfo(address edition, uint128 mintId) external view returns (MintInfo memory) {
                BaseData memory baseData = _baseData[edition][mintId];
                EditionMintData storage mintData = _editionMintData[edition][mintId];
                MintInfo memory combinedMintData = MintInfo(
                    baseData.startTime,
                    baseData.endTime,
                    baseData.affiliateFeeBPS,
                    baseData.mintPaused,
                    mintData.price,
                    mintData.maxMintableUpper,
                    mintData.maxMintableLower,
                    mintData.maxMintablePerAccount,
                    mintData.totalMinted,
                    mintData.cutoffTime
                );
                return combinedMintData;
            }
            /**
             * @inheritdoc IERC165
             */
            function supportsInterface(bytes4 interfaceId) public view override(IERC165, BaseMinter) returns (bool) {
                return BaseMinter.supportsInterface(interfaceId) || interfaceId == type(IRangeEditionMinter).interfaceId;
            }
            /**
             * @inheritdoc IMinterModule
             */
            function moduleInterfaceId() public pure returns (bytes4) {
                return type(IRangeEditionMinter).interfaceId;
            }
            // =============================================================
            //                  INTERNAL / PRIVATE HELPERS
            // =============================================================
            /**
             * @dev Restricts the `startTime` to be less than `cutoffTime`,
             *      and `cutoffTime` to be less than `endTime`.
             * @param startTime   The start unix timestamp of the mint.
             * @param cutoffTime  The cutoff unix timestamp of the mint.
             * @param endTime     The end unix timestamp of the mint.
             */
            modifier onlyValidCombinedTimeRange(
                uint32 startTime,
                uint32 cutoffTime,
                uint32 endTime
            ) {
                if (!(startTime < cutoffTime && cutoffTime < endTime)) revert InvalidTimeRange();
                _;
            }
            /**
             * @dev Gets the current maximum mintable quantity.
             * @param data The edition mint data.
             * @return The computed value.
             */
            function _getMaxMintable(EditionMintData storage data) internal view returns (uint32) {
                uint32 _maxMintable;
                if (block.timestamp < data.cutoffTime) {
                    _maxMintable = data.maxMintableUpper;
                } else {
                    _maxMintable = data.maxMintableLower;
                }
                return _maxMintable;
            }
        }
        // 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 IERC165 {
            /**
             * @dev Returns true if this contract implements the interface defined by
             * `interfaceId`. See the corresponding
             * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
             * to learn more about how these ids are created.
             *
             * This function call must use less than 30 000 gas.
             */
            function supportsInterface(bytes4 interfaceId) external view returns (bool);
        }
        // SPDX-License-Identifier: GPL-3.0-or-later
        pragma solidity ^0.8.16;
        /**
         * @title ISoundFeeRegistry
         * @author Sound.xyz
         */
        interface ISoundFeeRegistry {
            // =============================================================
            //                            EVENTS
            // =============================================================
            /**
             * @dev Emitted when the `soundFeeAddress` is changed.
             */
            event SoundFeeAddressSet(address soundFeeAddress);
            /**
             * @dev Emitted when the `platformFeeBPS` is changed.
             */
            event PlatformFeeSet(uint16 platformFeeBPS);
            // =============================================================
            //                             ERRORS
            // =============================================================
            /**
             * @dev The new `soundFeeAddress` must not be address(0).
             */
            error InvalidSoundFeeAddress();
            /**
             * @dev The platform fee numerator must not exceed `_MAX_BPS`.
             */
            error InvalidPlatformFeeBPS();
            // =============================================================
            //               PUBLIC / EXTERNAL WRITE FUNCTIONS
            // =============================================================
            /**
             * @dev Sets the `soundFeeAddress`.
             *
             * Calling conditions:
             * - The caller must be the owner of the contract.
             *
             * @param soundFeeAddress_ The sound fee address.
             */
            function setSoundFeeAddress(address soundFeeAddress_) external;
            /**
             * @dev Sets the `platformFeePBS`.
             *
             * Calling conditions:
             * - The caller must be the owner of the contract.
             *
             * @param platformFeeBPS_ Platform fee amount in bps (basis points).
             */
            function setPlatformFeeBPS(uint16 platformFeeBPS_) external;
            // =============================================================
            //               PUBLIC / EXTERNAL VIEW FUNCTIONS
            // =============================================================
            /**
             * @dev The sound protocol's address that receives platform fees.
             * @return The configured value.
             */
            function soundFeeAddress() external view returns (address);
            /**
             * @dev The numerator of the platform fee.
             * @return The configured value.
             */
            function platformFeeBPS() external view returns (uint16);
            /**
             * @dev The platform fee for `requiredEtherValue`.
             * @param requiredEtherValue The required Ether value for payment.
             * @return fee The computed value.
             */
            function platformFee(uint128 requiredEtherValue) external view returns (uint128 fee);
        }
        // SPDX-License-Identifier: GPL-3.0-or-later
        pragma solidity ^0.8.16;
        import { IMinterModule } from "@core/interfaces/IMinterModule.sol";
        /**
         * @dev Data unique to a range edition mint.
         */
        struct EditionMintData {
            // The price at which each token will be sold, in ETH.
            uint96 price;
            // The timestamp (in seconds since unix epoch) after which the
            // max amount of tokens mintable will drop from
            // `maxMintableUpper` to `maxMintableLower`.
            uint32 cutoffTime;
            // The total number of tokens minted. Includes permissioned mints.
            uint32 totalMinted;
            // The lower limit of the maximum number of tokens that can be minted.
            uint32 maxMintableLower;
            // The upper limit of the maximum number of tokens that can be minted.
            uint32 maxMintableUpper;
            // The maximum number of tokens that a wallet can mint.
            uint32 maxMintablePerAccount;
        }
        /**
         * @dev All the information about a range edition mint (combines EditionMintData with BaseData).
         */
        struct MintInfo {
            uint32 startTime;
            uint32 endTime;
            uint16 affiliateFeeBPS;
            bool mintPaused;
            uint96 price;
            uint32 maxMintableUpper;
            uint32 maxMintableLower;
            uint32 maxMintablePerAccount;
            uint32 totalMinted;
            uint32 cutoffTime;
        }
        /**
         * @title IRangeEditionMinter
         * @dev Interface for the `RangeEditionMinter` module.
         * @author Sound.xyz
         */
        interface IRangeEditionMinter is IMinterModule {
            // =============================================================
            //                            EVENTS
            // =============================================================
            /**
             * @dev Emitted when a range edition is created.
             * @param edition          Address of the song edition contract we are minting for.
             * @param mintId           The mint ID.
             * @param price            Sale price in ETH for minting a single token in `edition`.
             * @param startTime        Start timestamp of sale (in seconds since unix epoch).
             * @param cutoffTime       The timestamp (in seconds since unix epoch) after which the
             *                         max amount of tokens mintable will drop from
             *                         `maxMintableUpper` to `maxMintableLower`.
             * @param endTime          End timestamp of sale (in seconds since unix epoch).
             * @param affiliateFeeBPS  The affiliate fee in basis points.
             * @param maxMintableLower The lower limit of the maximum number of tokens that can be minted.
             * @param maxMintableUpper The upper limit of the maximum number of tokens that can be minted.
             */
            event RangeEditionMintCreated(
                address indexed edition,
                uint128 indexed mintId,
                uint96 price,
                uint32 startTime,
                uint32 cutoffTime,
                uint32 endTime,
                uint16 affiliateFeeBPS,
                uint32 maxMintableLower,
                uint32 maxMintableUpper,
                uint32 maxMintablePerAccount
            );
            event CutoffTimeSet(address indexed edition, uint128 indexed mintId, uint32 cutoffTime);
            /**
             * @dev Emitted when the max mintable range is updated.
             * @param edition          Address of the song edition contract we are minting for.
             * @param mintId           The mint ID.
             * @param maxMintableLower The lower limit of the maximum number of tokens that can be minted.
             * @param maxMintableUpper The upper limit of the maximum number of tokens that can be minted.
             */
            event MaxMintableRangeSet(
                address indexed edition,
                uint128 indexed mintId,
                uint32 maxMintableLower,
                uint32 maxMintableUpper
            );
            /**
             * @dev Emitted when the `price` is changed for (`edition`, `mintId`).
             * @param edition Address of the song edition contract we are minting for.
             * @param mintId  The mint ID.
             * @param price   Sale price in ETH for minting a single token in `edition`.
             */
            event PriceSet(address indexed edition, uint128 indexed mintId, uint96 price);
            /**
             * @dev Emitted when the `maxMintablePerAccount` is changed for (`edition`, `mintId`).
             * @param edition               Address of the song edition contract we are minting for.
             * @param mintId                The mint ID.
             * @param maxMintablePerAccount The maximum number of tokens that can be minted per account.
             */
            event MaxMintablePerAccountSet(address indexed edition, uint128 indexed mintId, uint32 maxMintablePerAccount);
            // =============================================================
            //                            ERRORS
            // =============================================================
            /**
             * @dev The `maxMintableLower` must not be greater than `maxMintableUpper`.
             */
            error InvalidMaxMintableRange();
            /**
             * @dev The number of tokens minted has exceeded the number allowed for each account.
             */
            error ExceedsMaxPerAccount();
            /**
             * @dev The max mintable per account cannot be zero.
             */
            error MaxMintablePerAccountIsZero();
            // =============================================================
            //               PUBLIC / EXTERNAL WRITE FUNCTIONS
            // =============================================================
            /*
             * @dev Initializes a range mint instance
             * @param edition                Address of the song edition contract we are minting for.
             * @param price                  Sale price in ETH for minting a single token in `edition`.
             * @param startTime              Start timestamp of sale (in seconds since unix epoch).
             * @param cutoffTime             The timestamp (in seconds since unix epoch) after which the
             *                               max amount of tokens mintable will drop from
             *                               `maxMintableUpper` to `maxMintableLower`.
             * @param endTime                End timestamp of sale (in seconds since unix epoch).
             * @param affiliateFeeBPS        The affiliate fee in basis points.
             * @param maxMintableLower       The lower limit of the maximum number of tokens that can be minted.
             * @param maxMintableUpper       The upper limit of the maximum number of tokens that can be minted.
             * @param maxMintablePerAccount_ The maximum number of tokens that can be minted by an account.
             * @return mintId The ID for the new mint instance.
             */
            function createEditionMint(
                address edition,
                uint96 price,
                uint32 startTime,
                uint32 cutoffTime,
                uint32 endTime,
                uint16 affiliateFeeBPS,
                uint32 maxMintableLower,
                uint32 maxMintableUpper,
                uint32 maxMintablePerAccount_
            ) external returns (uint128 mintId);
            /*
             * @dev Sets the time range.
             * @param edition     Address of the song edition contract we are minting for.
             * @param startTime   Start timestamp of sale (in seconds since unix epoch).
             * @param cutoffTime  The timestamp (in seconds since unix epoch) after which the
             *                    max amount of tokens mintable will drop from
             *                    `maxMintableUpper` to `maxMintableLower`.
             * @param endTime     End timestamp of sale (in seconds since unix epoch).
             */
            function setTimeRange(
                address edition,
                uint128 mintId,
                uint32 startTime,
                uint32 cutoffTime,
                uint32 endTime
            ) external;
            /*
             * @dev Sets the max mintable range.
             * @param edition          Address of the song edition contract we are minting for.
             * @param maxMintableLower The lower limit of the maximum number of tokens that can be minted.
             * @param maxMintableUpper The upper limit of the maximum number of tokens that can be minted.
             */
            function setMaxMintableRange(
                address edition,
                uint128 mintId,
                uint32 maxMintableLower,
                uint32 maxMintableUpper
            ) external;
            /*
             * @dev Mints tokens for a given edition.
             * @param edition  Address of the song edition contract we are minting for.
             * @param quantity Token quantity to mint in song `edition`.
             */
            function mint(
                address edition,
                uint128 mintId,
                uint32 quantity,
                address affiliate
            ) external payable;
            /*
             * @dev Sets the `price` for (`edition`, `mintId`).
             * @param edition Address of the song edition contract we are minting for.
             * @param mintId  The mint ID.
             * @param price   Sale price in ETH for minting a single token in `edition`.
             */
            function setPrice(
                address edition,
                uint128 mintId,
                uint96 price
            ) external;
            /*
             * @dev Sets the `maxMintablePerAccount` for (`edition`, `mintId`).
             * @param edition               Address of the song edition contract we are minting for.
             * @param mintId                The mint ID.
             * @param maxMintablePerAccount The maximum number of tokens that can be minted by an account.
             */
            function setMaxMintablePerAccount(
                address edition,
                uint128 mintId,
                uint32 maxMintablePerAccount
            ) external;
            // =============================================================
            //               PUBLIC / EXTERNAL VIEW FUNCTIONS
            // =============================================================
            /**
             * @dev Returns {IRangeEditionMinter.MintInfo} instance containing the full minter parameter set.
             * @param edition The edition to get the mint instance for.
             * @param mintId  The ID of the mint instance.
             * @return mintInfo Information about this mint.
             */
            function mintInfo(address edition, uint128 mintId) external view returns (MintInfo memory);
        }
        // SPDX-License-Identifier: GPL-3.0-or-later
        pragma solidity ^0.8.16;
        import { OwnableRoles } from "solady/auth/OwnableRoles.sol";
        import { ISoundEditionV1 } from "@core/interfaces/ISoundEditionV1.sol";
        import { IMinterModule } from "@core/interfaces/IMinterModule.sol";
        import { ISoundFeeRegistry } from "@core/interfaces/ISoundFeeRegistry.sol";
        import { IERC165 } from "openzeppelin/utils/introspection/IERC165.sol";
        import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
        import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol";
        /**
         * @title Minter Base
         * @dev The `BaseMinter` class maintains a central storage record of edition mint instances.
         */
        abstract contract BaseMinter is IMinterModule {
            // =============================================================
            //                           CONSTANTS
            // =============================================================
            /**
             * @dev This is the denominator, in basis points (BPS), for:
             * - platform fees
             * - affiliate fees
             */
            uint16 private constant _MAX_BPS = 10_000;
            // =============================================================
            //                            STORAGE
            // =============================================================
            /**
             * @dev The next mint ID. Shared amongst all editions connected.
             */
            uint128 private _nextMintId;
            /**
             * @dev How much platform fees have been accrued.
             */
            uint128 private _platformFeesAccrued;
            /**
             * @dev Maps an edition and the mint ID to a mint instance.
             */
            mapping(address => mapping(uint256 => BaseData)) internal _baseData;
            /**
             * @dev Maps an address to how much affiliate fees have they accrued.
             */
            mapping(address => uint128) private _affiliateFeesAccrued;
            /**
             * @dev The fee registry. Used for handling platform fees.
             */
            ISoundFeeRegistry public immutable feeRegistry;
            // =============================================================
            //                          CONSTRUCTOR
            // =============================================================
            constructor(ISoundFeeRegistry feeRegistry_) {
                if (address(feeRegistry_) == address(0)) revert FeeRegistryIsZeroAddress();
                feeRegistry = feeRegistry_;
            }
            // =============================================================
            //               PUBLIC / EXTERNAL WRITE FUNCTIONS
            // =============================================================
            /**
             * @inheritdoc IMinterModule
             */
            function setEditionMintPaused(
                address edition,
                uint128 mintId,
                bool paused
            ) public virtual onlyEditionOwnerOrAdmin(edition) {
                _baseData[edition][mintId].mintPaused = paused;
                emit MintPausedSet(edition, mintId, paused);
            }
            /**
             * @inheritdoc IMinterModule
             */
            function setTimeRange(
                address edition,
                uint128 mintId,
                uint32 startTime,
                uint32 endTime
            ) public virtual onlyEditionOwnerOrAdmin(edition) onlyValidTimeRange(startTime, endTime) {
                _baseData[edition][mintId].startTime = startTime;
                _baseData[edition][mintId].endTime = endTime;
                emit TimeRangeSet(edition, mintId, startTime, endTime);
            }
            /**
             * @inheritdoc IMinterModule
             */
            function setAffiliateFee(
                address edition,
                uint128 mintId,
                uint16 feeBPS
            ) public virtual override onlyEditionOwnerOrAdmin(edition) onlyValidAffiliateFeeBPS(feeBPS) {
                _baseData[edition][mintId].affiliateFeeBPS = feeBPS;
                emit AffiliateFeeSet(edition, mintId, feeBPS);
            }
            /**
             * @inheritdoc IMinterModule
             */
            function withdrawForAffiliate(address affiliate) public override {
                uint256 accrued = _affiliateFeesAccrued[affiliate];
                if (accrued != 0) {
                    _affiliateFeesAccrued[affiliate] = 0;
                    SafeTransferLib.safeTransferETH(affiliate, accrued);
                }
            }
            /**
             * @inheritdoc IMinterModule
             */
            function withdrawForPlatform() public override {
                uint256 accrued = _platformFeesAccrued;
                if (accrued != 0) {
                    _platformFeesAccrued = 0;
                    SafeTransferLib.safeTransferETH(feeRegistry.soundFeeAddress(), accrued);
                }
            }
            // =============================================================
            //               PUBLIC / EXTERNAL VIEW FUNCTIONS
            // =============================================================
            /**
             * @dev Getter for the max basis points.
             */
            function MAX_BPS() external pure returns (uint16) {
                return _MAX_BPS;
            }
            /**
             * @inheritdoc IMinterModule
             */
            function affiliateFeesAccrued(address affiliate) external view returns (uint128) {
                return _affiliateFeesAccrued[affiliate];
            }
            /**
             * @inheritdoc IMinterModule
             */
            function platformFeesAccrued() external view returns (uint128) {
                return _platformFeesAccrued;
            }
            /**
             * @inheritdoc IMinterModule
             */
            function isAffiliated(
                address, /* edition */
                uint128, /* mintId */
                address affiliate
            ) public view virtual override returns (bool) {
                return affiliate != address(0);
            }
            /**
             * @inheritdoc IMinterModule
             */
            function nextMintId() public view returns (uint128) {
                return _nextMintId;
            }
            /**
             * @inheritdoc IERC165
             */
            function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                return interfaceId == type(IMinterModule).interfaceId || interfaceId == this.supportsInterface.selector;
            }
            /**
             * @inheritdoc IMinterModule
             */
            function totalPrice(
                address edition,
                uint128 mintId,
                address minter,
                uint32 quantity
            ) public view virtual override returns (uint128);
            // =============================================================
            //                  INTERNAL / PRIVATE HELPERS
            // =============================================================
            /**
             * @dev Restricts the function to be only callable by the owner or admin of `edition`.
             * @param edition The edition address.
             */
            modifier onlyEditionOwnerOrAdmin(address edition) virtual {
                if (
                    msg.sender != OwnableRoles(edition).owner() &&
                    !OwnableRoles(edition).hasAnyRole(msg.sender, ISoundEditionV1(edition).ADMIN_ROLE())
                ) revert Unauthorized();
                _;
            }
            /**
             * @dev Restricts the start time to be less than the end time.
             * @param startTime The start time of the mint.
             * @param endTime The end time of the mint.
             */
            modifier onlyValidTimeRange(uint32 startTime, uint32 endTime) virtual {
                if (startTime >= endTime) revert InvalidTimeRange();
                _;
            }
            /**
             * @dev Restricts the affiliate fee numerator to not exceed the `MAX_BPS`.
             */
            modifier onlyValidAffiliateFeeBPS(uint16 affiliateFeeBPS) virtual {
                if (affiliateFeeBPS > _MAX_BPS) revert InvalidAffiliateFeeBPS();
                _;
            }
            /**
             * @dev Creates an edition mint instance.
             * @param edition The edition address.
             * @param startTime The start time of the mint.
             * @param endTime The end time of the mint.
             * @param affiliateFeeBPS The affiliate fee in basis points.
             * @return mintId The ID for the mint instance.
             * Calling conditions:
             * - Must be owner or admin of the edition.
             */
            function _createEditionMint(
                address edition,
                uint32 startTime,
                uint32 endTime,
                uint16 affiliateFeeBPS
            )
                internal
                onlyEditionOwnerOrAdmin(edition)
                onlyValidTimeRange(startTime, endTime)
                onlyValidAffiliateFeeBPS(affiliateFeeBPS)
                returns (uint128 mintId)
            {
                mintId = _nextMintId;
                BaseData storage data = _baseData[edition][mintId];
                data.startTime = startTime;
                data.endTime = endTime;
                data.affiliateFeeBPS = affiliateFeeBPS;
                _nextMintId = mintId + 1;
                emit MintConfigCreated(edition, msg.sender, mintId, startTime, endTime, affiliateFeeBPS);
            }
            /**
             * @dev Mints `quantity` of `edition` to `to` with a required payment of `requiredEtherValue`.
             * Note: this function should be called at the end of a function due to it refunding any
             * excess ether paid, to adhere to the checks-effects-interactions pattern.
             * Otherwise, a reentrancy guard must be used.
             * @param edition The edition address.
             * @param mintId The ID for the mint instance.
             * @param quantity The quantity of tokens to mint.
             * @param affiliate The affiliate (referral) address.
             */
            function _mint(
                address edition,
                uint128 mintId,
                uint32 quantity,
                address affiliate
            ) internal {
                BaseData storage baseData = _baseData[edition][mintId];
                /* --------------------- GENERAL CHECKS --------------------- */
                {
                    uint32 startTime = baseData.startTime;
                    uint32 endTime = baseData.endTime;
                    if (block.timestamp < startTime) revert MintNotOpen(block.timestamp, startTime, endTime);
                    if (block.timestamp > endTime) revert MintNotOpen(block.timestamp, startTime, endTime);
                    if (baseData.mintPaused) revert MintPaused();
                }
                /* ----------- AFFILIATE AND PLATFORM FEES LOGIC ------------ */
                uint128 requiredEtherValue = totalPrice(edition, mintId, msg.sender, quantity);
                // Reverts if the payment is not exact.
                if (msg.value < requiredEtherValue) revert Underpaid(msg.value, requiredEtherValue);
                (uint128 remainingPayment, uint128 platformFee) = _deductPlatformFee(requiredEtherValue);
                // Check if the mint is an affiliated mint.
                bool affiliated = isAffiliated(edition, mintId, affiliate);
                uint128 affiliateFee;
                unchecked {
                    if (affiliated) {
                        // Compute the affiliate fee.
                        // Won't overflow, as `remainingPayment` is 128 bits, and `affiliateFeeBPS` is 16 bits.
                        affiliateFee = uint128(
                            (uint256(remainingPayment) * uint256(baseData.affiliateFeeBPS)) / uint256(_MAX_BPS)
                        );
                        // Deduct the affiliate fee from the remaining payment.
                        // Won't underflow as `affiliateFee <= remainingPayment`.
                        remainingPayment -= affiliateFee;
                        // Increment the affiliate fees accrued.
                        // Overflow is incredibly unrealistic.
                        _affiliateFeesAccrued[affiliate] += affiliateFee;
                    }
                }
                /* ------------------------- MINT --------------------------- */
                // Emit the event.
                emit Minted(
                    edition,
                    mintId,
                    msg.sender,
                    // Need to put this call here to avoid stack-too-deep error (it returns fromTokenId)
                    uint32(ISoundEditionV1(edition).mint{ value: remainingPayment }(msg.sender, quantity)),
                    quantity,
                    requiredEtherValue,
                    platformFee,
                    affiliateFee,
                    affiliate,
                    affiliated
                );
                /* ------------------------- REFUND ------------------------- */
                unchecked {
                    // Note: We do this at the end to avoid creating a reentrancy vector.
                    // Refund the user any ETH they spent over the current total price of the NFTs.
                    if (msg.value > requiredEtherValue) {
                        SafeTransferLib.safeTransferETH(msg.sender, msg.value - requiredEtherValue);
                    }
                }
            }
            /**
             * @dev Deducts the platform fee from `requiredEtherValue`.
             * @param requiredEtherValue The amount of Ether required.
             * @return remainingPayment  The remaining payment Ether amount.
             * @return platformFee       The platform fee.
             */
            function _deductPlatformFee(uint128 requiredEtherValue)
                internal
                returns (uint128 remainingPayment, uint128 platformFee)
            {
                unchecked {
                    // Compute the platform fee.
                    platformFee = feeRegistry.platformFee(requiredEtherValue);
                    // Increment the platform fees accrued.
                    // Overflow is incredibly unrealistic.
                    _platformFeesAccrued += platformFee;
                    // Deduct the platform fee.
                    // Won't underflow as `platformFee <= requiredEtherValue`;
                    remainingPayment = requiredEtherValue - platformFee;
                }
            }
            /**
             * @dev Increments `totalMinted` with `quantity`, reverting if `totalMinted + quantity > maxMintable`.
             * @param totalMinted The current total number of minted tokens.
             * @param maxMintable The maximum number of mintable tokens.
             * @return `totalMinted` + `quantity`.
             */
            function _incrementTotalMinted(
                uint32 totalMinted,
                uint32 quantity,
                uint32 maxMintable
            ) internal pure returns (uint32) {
                unchecked {
                    // Won't overflow as both are 32 bits.
                    uint256 sum = uint256(totalMinted) + uint256(quantity);
                    if (sum > maxMintable) {
                        // Note that the `maxMintable` may vary and drop over time
                        // and cause `totalMinted` to be greater than `maxMintable`.
                        // The `zeroFloorSub` is equivalent to `max(0, x - y)`.
                        uint32 available = uint32(FixedPointMathLib.zeroFloorSub(maxMintable, totalMinted));
                        revert ExceedsAvailableSupply(available);
                    }
                    return uint32(sum);
                }
            }
        }
        // SPDX-License-Identifier: GPL-3.0-or-later
        pragma solidity ^0.8.16;
        import { IERC165 } from "openzeppelin/utils/introspection/IERC165.sol";
        import { ISoundFeeRegistry } from "./ISoundFeeRegistry.sol";
        /**
         * @title IMinterModule
         * @notice The interface for Sound protocol minter modules.
         */
        interface IMinterModule is IERC165 {
            // =============================================================
            //                            STRUCTS
            // =============================================================
            struct BaseData {
                // The start unix timestamp of the mint.
                uint32 startTime;
                // The end unix timestamp of the mint.
                uint32 endTime;
                // The affiliate fee in basis points.
                uint16 affiliateFeeBPS;
                // Whether the mint is paused.
                bool mintPaused;
            }
            // =============================================================
            //                            EVENTS
            // =============================================================
            /**
             * @dev Emitted when the mint instance for an `edition` is created.
             * @param edition The edition address.
             * @param mintId The mint ID, a global incrementing identifier used within the minter
             * @param startTime The start time of the mint.
             * @param endTime The end time of the mint.
             * @param affiliateFeeBPS The affiliate fee in basis points.
             */
            event MintConfigCreated(
                address indexed edition,
                address indexed creator,
                uint128 mintId,
                uint32 startTime,
                uint32 endTime,
                uint16 affiliateFeeBPS
            );
            /**
             * @dev Emitted when the `paused` status of `edition` is updated.
             * @param edition The edition address.
             * @param mintId  The mint ID, to distinguish between multiple mints for the same edition.
             * @param paused  The new paused status.
             */
            event MintPausedSet(address indexed edition, uint128 mintId, bool paused);
            /**
             * @dev Emitted when the `paused` status of `edition` is updated.
             * @param edition   The edition address.
             * @param mintId    The mint ID, to distinguish between multiple mints for the same edition.
             * @param startTime The start time of the mint.
             * @param endTime   The end time of the mint.
             */
            event TimeRangeSet(address indexed edition, uint128 indexed mintId, uint32 startTime, uint32 endTime);
            /**
             * @notice Emitted when the `affiliateFeeBPS` is updated.
             * @param edition The edition address.
             * @param mintId  The mint ID, to distinguish between multiple mints for the same edition.
             * @param bps     The affiliate fee basis points.
             */
            event AffiliateFeeSet(address indexed edition, uint128 indexed mintId, uint16 bps);
            /**
             * @notice Emitted when a mint happens.
             * @param edition            The edition address.
             * @param mintId             The mint ID, to distinguish between multiple mints for
             *                           the same edition.
             * @param buyer              The buyer address.
             * @param fromTokenId        The first token ID of the batch.
             * @param quantity           The size of the batch.
             * @param requiredEtherValue Total amount of Ether required for payment.
             * @param platformFee        The cut paid to the platform.
             * @param affiliateFee       The cut paid to the affiliate.
             * @param affiliate          The affiliate's address.
             * @param affiliated         Whether the affiliate is affiliated.
             */
            event Minted(
                address indexed edition,
                uint128 indexed mintId,
                address indexed buyer,
                uint32 fromTokenId,
                uint32 quantity,
                uint128 requiredEtherValue,
                uint128 platformFee,
                uint128 affiliateFee,
                address affiliate,
                bool affiliated
            );
            // =============================================================
            //                            ERRORS
            // =============================================================
            /**
             * @dev The Ether value paid is below the value required.
             * @param paid The amount sent to the contract.
             * @param required The amount required to mint.
             */
            error Underpaid(uint256 paid, uint256 required);
            /**
             * @dev The number minted has exceeded the max mintable amount.
             * @param available The number of tokens remaining available for mint.
             */
            error ExceedsAvailableSupply(uint32 available);
            /**
             * @dev The mint is not opened.
             * @param blockTimestamp The current block timestamp.
             * @param startTime The start time of the mint.
             * @param endTime The end time of the mint.
             */
            error MintNotOpen(uint256 blockTimestamp, uint32 startTime, uint32 endTime);
            /**
             * @dev The mint is paused.
             */
            error MintPaused();
            /**
             * @dev The `startTime` is not less than the `endTime`.
             */
            error InvalidTimeRange();
            /**
             * @dev Unauthorized caller
             */
            error Unauthorized();
            /**
             * @dev The affiliate fee numerator must not exceed `MAX_BPS`.
             */
            error InvalidAffiliateFeeBPS();
            /**
             * @dev Fee registry cannot be the zero address.
             */
            error FeeRegistryIsZeroAddress();
            // =============================================================
            //               PUBLIC / EXTERNAL WRITE FUNCTIONS
            // =============================================================
            /**
             * @dev Sets the paused status for (`edition`, `mintId`).
             *
             * Calling conditions:
             * - The caller must be the edition's owner or admin.
             */
            function setEditionMintPaused(
                address edition,
                uint128 mintId,
                bool paused
            ) external;
            /**
             * @dev Sets the time range for an edition mint.
             *
             * Calling conditions:
             * - The caller must be the edition's owner or admin.
             *
             * @param edition The edition address.
             * @param mintId The mint ID, a global incrementing identifier used within the minter
             * @param startTime The start time of the mint.
             * @param endTime The end time of the mint.
             */
            function setTimeRange(
                address edition,
                uint128 mintId,
                uint32 startTime,
                uint32 endTime
            ) external;
            /**
             * @dev Sets the affiliate fee for (`edition`, `mintId`).
             *
             * Calling conditions:
             * - The caller must be the edition's owner or admin.
             */
            function setAffiliateFee(
                address edition,
                uint128 mintId,
                uint16 affiliateFeeBPS
            ) external;
            /**
             * @dev Withdraws all the accrued fees for `affiliate`.
             */
            function withdrawForAffiliate(address affiliate) external;
            /**
             * @dev Withdraws all the accrued fees for the platform.
             */
            function withdrawForPlatform() external;
            // =============================================================
            //               PUBLIC / EXTERNAL VIEW FUNCTIONS
            // =============================================================
            /**
             * @dev The total fees accrued for `affiliate`.
             * @param affiliate The affiliate's address.
             * @return The latest value.
             */
            function affiliateFeesAccrued(address affiliate) external view returns (uint128);
            /**
             * @dev The total fees accrued for the platform.
             * @return The latest value.
             */
            function platformFeesAccrued() external view returns (uint128);
            /**
             * @dev Whether `affiliate` is affiliated for (`edition`, `mintId`).
             * @param edition   The edition's address.
             * @param mintId    The mint ID.
             * @param affiliate The affiliate's address.
             * @return The computed value.
             */
            function isAffiliated(
                address edition,
                uint128 mintId,
                address affiliate
            ) external view returns (bool);
            /**
             * @dev The total price for `quantity` tokens for (`edition`, `mintId`).
             * @param edition   The edition's address.
             * @param mintId    The mint ID.
             * @param mintId    The minter's address.
             * @param quantity  The number of tokens to mint.
             * @return The computed value.
             */
            function totalPrice(
                address edition,
                uint128 mintId,
                address minter,
                uint32 quantity
            ) external view returns (uint128);
            /**
             * @dev The next mint ID.
             *      A mint ID is assigned sequentially starting from (0, 1, 2, ...),
             *      and is shared amongst all editions connected to the minter contract.
             * @return The latest value.
             */
            function nextMintId() external view returns (uint128);
            /**
             * @dev The interface ID of the minter.
             * @return The constant value.
             */
            function moduleInterfaceId() external view returns (bytes4);
            /**
             * @dev The fee registry. Used for handling platform fees.
             * @return The immutable value.
             */
            function feeRegistry() external view returns (ISoundFeeRegistry);
        }
        // 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: 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: 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
        // 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
        // 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
        // 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: 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);
        }
        

        File 2 of 4: 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 3 of 4: 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 4 of 4: SoundFeeRegistry
        // SPDX-License-Identifier: GPL-3.0-or-later
        pragma solidity ^0.8.16;
        import { OwnableRoles } from "solady/auth/OwnableRoles.sol";
        import { ISoundFeeRegistry } from "@core/interfaces/ISoundFeeRegistry.sol";
        /**
         * @title SoundFeeRegistry
         * @author Sound.xyz
         */
        contract SoundFeeRegistry is ISoundFeeRegistry, OwnableRoles {
            // =============================================================
            //                           CONSTANTS
            // =============================================================
            /**
             * @dev This is the denominator, in basis points (BPS), for platform fees.
             */
            uint16 private constant _MAX_BPS = 10_000;
            // =============================================================
            //                            STORAGE
            // =============================================================
            /**
             * @dev The Sound protocol's address that receives platform fees.
             */
            address public override soundFeeAddress;
            /**
             * @dev The numerator of the platform fee.
             */
            uint16 public override platformFeeBPS;
            // =============================================================
            //                          CONSTRUCTOR
            // =============================================================
            constructor(address soundFeeAddress_, uint16 platformFeeBPS_)
                onlyValidSoundFeeAddress(soundFeeAddress_)
                onlyValidPlatformFeeBPS(platformFeeBPS_)
            {
                soundFeeAddress = soundFeeAddress_;
                platformFeeBPS = platformFeeBPS_;
                _initializeOwner(msg.sender);
            }
            // =============================================================
            //               PUBLIC / EXTERNAL WRITE FUNCTIONS
            // =============================================================
            /**
             * @inheritdoc ISoundFeeRegistry
             */
            function setSoundFeeAddress(address soundFeeAddress_)
                external
                onlyOwner
                onlyValidSoundFeeAddress(soundFeeAddress_)
            {
                soundFeeAddress = soundFeeAddress_;
                emit SoundFeeAddressSet(soundFeeAddress_);
            }
            /**
             * @inheritdoc ISoundFeeRegistry
             */
            function setPlatformFeeBPS(uint16 platformFeeBPS_) external onlyOwner onlyValidPlatformFeeBPS(platformFeeBPS_) {
                platformFeeBPS = platformFeeBPS_;
                emit PlatformFeeSet(platformFeeBPS_);
            }
            /**
             * @inheritdoc ISoundFeeRegistry
             */
            function platformFee(uint128 requiredEtherValue) external view returns (uint128 fee) {
                // Won't overflow, as `requiredEtherValue` is 128 bits, and `platformFeeBPS` is 16 bits.
                unchecked {
                    fee = uint128((uint256(requiredEtherValue) * uint256(platformFeeBPS)) / uint256(_MAX_BPS));
                }
            }
            // =============================================================
            //                  INTERNAL / PRIVATE HELPERS
            // =============================================================
            /**
             * @dev Restricts the sound fee address to be address(0).
             * @param soundFeeAddress_ The sound fee address.
             */
            modifier onlyValidSoundFeeAddress(address soundFeeAddress_) {
                if (soundFeeAddress_ == address(0)) revert InvalidSoundFeeAddress();
                _;
            }
            /**
             * @dev Restricts the platform fee numerator to not exceed the `_MAX_BPS`.
             * @param platformFeeBPS_ Platform fee amount in bps (basis points).
             */
            modifier onlyValidPlatformFeeBPS(uint16 platformFeeBPS_) {
                if (platformFeeBPS_ > _MAX_BPS) revert InvalidPlatformFeeBPS();
                _;
            }
        }
        // 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;
        /**
         * @title ISoundFeeRegistry
         * @author Sound.xyz
         */
        interface ISoundFeeRegistry {
            // =============================================================
            //                            EVENTS
            // =============================================================
            /**
             * @dev Emitted when the `soundFeeAddress` is changed.
             */
            event SoundFeeAddressSet(address soundFeeAddress);
            /**
             * @dev Emitted when the `platformFeeBPS` is changed.
             */
            event PlatformFeeSet(uint16 platformFeeBPS);
            // =============================================================
            //                             ERRORS
            // =============================================================
            /**
             * @dev The new `soundFeeAddress` must not be address(0).
             */
            error InvalidSoundFeeAddress();
            /**
             * @dev The platform fee numerator must not exceed `_MAX_BPS`.
             */
            error InvalidPlatformFeeBPS();
            // =============================================================
            //               PUBLIC / EXTERNAL WRITE FUNCTIONS
            // =============================================================
            /**
             * @dev Sets the `soundFeeAddress`.
             *
             * Calling conditions:
             * - The caller must be the owner of the contract.
             *
             * @param soundFeeAddress_ The sound fee address.
             */
            function setSoundFeeAddress(address soundFeeAddress_) external;
            /**
             * @dev Sets the `platformFeePBS`.
             *
             * Calling conditions:
             * - The caller must be the owner of the contract.
             *
             * @param platformFeeBPS_ Platform fee amount in bps (basis points).
             */
            function setPlatformFeeBPS(uint16 platformFeeBPS_) external;
            // =============================================================
            //               PUBLIC / EXTERNAL VIEW FUNCTIONS
            // =============================================================
            /**
             * @dev The sound protocol's address that receives platform fees.
             * @return The configured value.
             */
            function soundFeeAddress() external view returns (address);
            /**
             * @dev The numerator of the platform fee.
             * @return The configured value.
             */
            function platformFeeBPS() external view returns (uint16);
            /**
             * @dev The platform fee for `requiredEtherValue`.
             * @param requiredEtherValue The required Ether value for payment.
             * @return fee The computed value.
             */
            function platformFee(uint128 requiredEtherValue) external view returns (uint128 fee);
        }