Sonic Blaze Testnet
    /

    Contract Diff Checker

    Contract Name:
    LBFactory

    Contract Source Code:

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.20;
    
    import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
    import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
    
    import {PairParameterHelper} from "./libraries/PairParameterHelper.sol";
    import {Encoded} from "./libraries/math/Encoded.sol";
    import {ImmutableClone} from "./libraries/ImmutableClone.sol";
    import {PriceHelper} from "./libraries/PriceHelper.sol";
    import {SafeCast} from "./libraries/math/SafeCast.sol";
    import {Hooks} from "./libraries/Hooks.sol";
    
    import {ILBFactory} from "./interfaces/ILBFactory.sol";
    import {ILBPair} from "./interfaces/ILBPair.sol";
    import {ILBHooks} from "./interfaces/ILBHooks.sol";
    
    /**
     * @title Liquidity Book Factory
     * @author Trader Joe
     * @notice Contract used to deploy and register new LBPairs.
     * Enables setting fee parameters, flashloan fees and LBPair implementation.
     * Unless the `isOpen` is `true`, only the owner of the factory can create pairs.
     */
    contract LBFactory is Ownable2Step, AccessControl, ILBFactory {
        using SafeCast for uint256;
        using Encoded for bytes32;
        using PairParameterHelper for bytes32;
        using EnumerableSet for EnumerableSet.AddressSet;
        using EnumerableSet for EnumerableSet.UintSet;
        using EnumerableMap for EnumerableMap.UintToUintMap;
    
        bytes32 public constant LB_HOOKS_MANAGER_ROLE = keccak256("LB_HOOKS_MANAGER_ROLE");
    
        uint256 private constant _OFFSET_IS_PRESET_OPEN = 255;
    
        uint256 private constant _MIN_BIN_STEP = 1; // 0.001%
    
        uint256 private constant _MAX_FLASHLOAN_FEE = 0.1e18; // 10%
    
        address private _feeRecipient;
        uint256 private _flashLoanFee;
    
        address private _lbPairImplementation;
    
        ILBPair[] private _allLBPairs;
    
        /**
         * @dev Mapping from a (tokenA, tokenB, binStep) to a LBPair. The tokens are ordered to save gas, but they can be
         * in the reverse order in the actual pair.
         * Always query one of the 2 tokens of the pair to assert the order of the 2 tokens
         */
        mapping(IERC20 => mapping(IERC20 => mapping(uint256 => LBPairInformation))) private _lbPairsInfo;
    
        EnumerableMap.UintToUintMap private _presets;
        EnumerableSet.AddressSet private _quoteAssetWhitelist;
    
        /**
         * @dev Mapping from a (tokenA, tokenB) to a set of available bin steps, this is used to keep track of the
         * bin steps that are already used for a pair.
         * The tokens are ordered to save gas, but they can be in the reverse order in the actual pair.
         * Always query one of the 2 tokens of the pair to assert the order of the 2 tokens
         */
        mapping(IERC20 => mapping(IERC20 => EnumerableSet.UintSet)) private _availableLBPairBinSteps;
    
        /**
         * @notice Constructor
         * @param feeRecipient The address of the fee recipient
         * @param flashLoanFee The value of the fee for flash loan
         */
        constructor(address feeRecipient, address initialOwner, uint256 flashLoanFee) Ownable(initialOwner) {
            if (flashLoanFee > _MAX_FLASHLOAN_FEE) revert LBFactory__FlashLoanFeeAboveMax(flashLoanFee, _MAX_FLASHLOAN_FEE);
    
            _setFeeRecipient(feeRecipient);
    
            _flashLoanFee = flashLoanFee;
            emit FlashLoanFeeSet(0, flashLoanFee);
        }
    
        /**
         * @notice Get the minimum bin step a pair can have
         * @return minBinStep
         */
        function getMinBinStep() external pure override returns (uint256 minBinStep) {
            return _MIN_BIN_STEP;
        }
    
        /**
         * @notice Get the protocol fee recipient
         * @return feeRecipient
         */
        function getFeeRecipient() external view override returns (address feeRecipient) {
            return _feeRecipient;
        }
    
        /**
         * @notice Get the maximum fee percentage for flashLoans
         * @return maxFee
         */
        function getMaxFlashLoanFee() external pure override returns (uint256 maxFee) {
            return _MAX_FLASHLOAN_FEE;
        }
    
        /**
         * @notice Get the fee for flash loans, in 1e18
         * @return flashLoanFee The fee for flash loans, in 1e18
         */
        function getFlashLoanFee() external view override returns (uint256 flashLoanFee) {
            return _flashLoanFee;
        }
    
        /**
         * @notice Get the address of the LBPair implementation
         * @return lbPairImplementation The address of the LBPair implementation
         */
        function getLBPairImplementation() external view override returns (address lbPairImplementation) {
            return _lbPairImplementation;
        }
    
        /**
         * @notice View function to return the number of LBPairs created
         * @return lbPairNumber
         */
        function getNumberOfLBPairs() external view override returns (uint256 lbPairNumber) {
            return _allLBPairs.length;
        }
    
        /**
         * @notice View function to return the LBPair created at index `index`
         * @param index The index
         * @return lbPair The address of the LBPair at index `index`
         */
        function getLBPairAtIndex(uint256 index) external view override returns (ILBPair lbPair) {
            return _allLBPairs[index];
        }
    
        /**
         * @notice View function to return the number of quote assets whitelisted
         * @return numberOfQuoteAssets The number of quote assets
         */
        function getNumberOfQuoteAssets() external view override returns (uint256 numberOfQuoteAssets) {
            return _quoteAssetWhitelist.length();
        }
    
        /**
         * @notice View function to return the quote asset whitelisted at index `index`
         * @param index The index
         * @return asset The address of the quoteAsset at index `index`
         */
        function getQuoteAssetAtIndex(uint256 index) external view override returns (IERC20 asset) {
            return IERC20(_quoteAssetWhitelist.at(index));
        }
    
        /**
         * @notice View function to return whether a token is a quotedAsset (true) or not (false)
         * @param token The address of the asset
         * @return isQuote Whether the token is a quote asset or not
         */
        function isQuoteAsset(IERC20 token) external view override returns (bool isQuote) {
            return _quoteAssetWhitelist.contains(address(token));
        }
    
        /**
         * @notice Returns the LBPairInformation if it exists,
         * if not, then the address 0 is returned. The order doesn't matter
         * @param tokenA The address of the first token of the pair
         * @param tokenB The address of the second token of the pair
         * @param binStep The bin step of the LBPair
         * @return lbPairInformation The LBPairInformation
         */
        function getLBPairInformation(IERC20 tokenA, IERC20 tokenB, uint256 binStep)
            external
            view
            override
            returns (LBPairInformation memory lbPairInformation)
        {
            return _getLBPairInformation(tokenA, tokenB, binStep);
        }
    
        /**
         * @notice View function to return the different parameters of the preset
         * Will revert if the preset doesn't exist
         * @param binStep The bin step of the preset
         * @return baseFactor The base factor
         * @return filterPeriod The filter period of the preset
         * @return decayPeriod The decay period of the preset
         * @return reductionFactor The reduction factor of the preset
         * @return variableFeeControl The variable fee control of the preset
         * @return protocolShare The protocol share of the preset
         * @return maxVolatilityAccumulator The max volatility accumulator of the preset
         * @return isOpen Whether the preset is open or not
         */
        function getPreset(uint256 binStep)
            external
            view
            override
            returns (
                uint256 baseFactor,
                uint256 filterPeriod,
                uint256 decayPeriod,
                uint256 reductionFactor,
                uint256 variableFeeControl,
                uint256 protocolShare,
                uint256 maxVolatilityAccumulator,
                bool isOpen
            )
        {
            if (!_presets.contains(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);
    
            bytes32 preset = bytes32(_presets.get(binStep));
    
            baseFactor = preset.getBaseFactor();
            filterPeriod = preset.getFilterPeriod();
            decayPeriod = preset.getDecayPeriod();
            reductionFactor = preset.getReductionFactor();
            variableFeeControl = preset.getVariableFeeControl();
            protocolShare = preset.getProtocolShare();
            maxVolatilityAccumulator = preset.getMaxVolatilityAccumulator();
    
            isOpen = preset.decodeBool(_OFFSET_IS_PRESET_OPEN);
        }
    
        /**
         * @notice View function to return the list of available binStep with a preset
         * @return binStepWithPreset The list of binStep
         */
        function getAllBinSteps() external view override returns (uint256[] memory binStepWithPreset) {
            return _presets.keys();
        }
    
        /**
         * @notice View function to return the list of open binSteps
         * @return openBinStep The list of open binSteps
         */
        function getOpenBinSteps() external view override returns (uint256[] memory openBinStep) {
            uint256 length = _presets.length();
    
            if (length > 0) {
                openBinStep = new uint256[](length);
    
                uint256 index;
    
                for (uint256 i; i < length; ++i) {
                    (uint256 binStep, uint256 preset) = _presets.at(i);
    
                    if (_isPresetOpen(bytes32(preset))) {
                        openBinStep[index] = binStep;
                        index++;
                    }
                }
    
                if (index < length) {
                    assembly {
                        mstore(openBinStep, index)
                    }
                }
            }
        }
    
        /**
         * @notice View function to return all the LBPair of a pair of tokens
         * @param tokenX The first token of the pair
         * @param tokenY The second token of the pair
         * @return lbPairsAvailable The list of available LBPairs
         */
        function getAllLBPairs(IERC20 tokenX, IERC20 tokenY)
            external
            view
            override
            returns (LBPairInformation[] memory lbPairsAvailable)
        {
            unchecked {
                (IERC20 tokenA, IERC20 tokenB) = _sortTokens(tokenX, tokenY);
    
                EnumerableSet.UintSet storage addressSet = _availableLBPairBinSteps[tokenA][tokenB];
    
                uint256 length = addressSet.length();
    
                if (length > 0) {
                    lbPairsAvailable = new LBPairInformation[](length);
    
                    mapping(uint256 => LBPairInformation) storage lbPairsInfo = _lbPairsInfo[tokenA][tokenB];
    
                    for (uint256 i = 0; i < length; ++i) {
                        uint16 binStep = addressSet.at(i).safe16();
    
                        lbPairsAvailable[i] = LBPairInformation({
                            binStep: binStep,
                            LBPair: lbPairsInfo[binStep].LBPair,
                            createdByOwner: lbPairsInfo[binStep].createdByOwner,
                            ignoredForRouting: lbPairsInfo[binStep].ignoredForRouting
                        });
                    }
                }
            }
        }
    
        /**
         * @notice Set the LBPair implementation address
         * @dev Needs to be called by the owner
         * @param newLBPairImplementation The address of the implementation
         */
        function setLBPairImplementation(address newLBPairImplementation) external override onlyOwner {
            if (ILBPair(newLBPairImplementation).getFactory() != this) {
                revert LBFactory__LBPairSafetyCheckFailed(newLBPairImplementation);
            }
    
            address oldLBPairImplementation = _lbPairImplementation;
            if (oldLBPairImplementation == newLBPairImplementation) {
                revert LBFactory__SameImplementation(newLBPairImplementation);
            }
    
            _lbPairImplementation = newLBPairImplementation;
    
            emit LBPairImplementationSet(oldLBPairImplementation, newLBPairImplementation);
        }
    
        /**
         * @notice Create a liquidity bin LBPair for tokenX and tokenY
         * @param tokenX The address of the first token
         * @param tokenY The address of the second token
         * @param activeId The active id of the pair
         * @param binStep The bin step in basis point, used to calculate log(1 + binStep / 10_000)
         * @return pair The address of the newly created LBPair
         */
        function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
            external
            override
            returns (ILBPair pair)
        {
            if (!_presets.contains(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);
    
            bytes32 preset = bytes32(_presets.get(binStep));
            bool isOwner = msg.sender == owner();
    
            if (!_isPresetOpen(preset) && !isOwner) {
                revert LBFactory__PresetIsLockedForUsers(msg.sender, binStep);
            }
    
            if (!_quoteAssetWhitelist.contains(address(tokenY))) revert LBFactory__QuoteAssetNotWhitelisted(tokenY);
    
            if (tokenX == tokenY) revert LBFactory__IdenticalAddresses(tokenX);
    
            // safety check, making sure that the price can be calculated
            PriceHelper.getPriceFromId(activeId, binStep);
    
            // We sort token for storage efficiency, only one input needs to be stored because they are sorted
            (IERC20 tokenA, IERC20 tokenB) = _sortTokens(tokenX, tokenY);
            // single check is sufficient
            if (address(tokenA) == address(0)) revert LBFactory__AddressZero();
            if (address(_lbPairsInfo[tokenA][tokenB][binStep].LBPair) != address(0)) {
                revert LBFactory__LBPairAlreadyExists(tokenX, tokenY, binStep);
            }
    
            {
                address implementation = _lbPairImplementation;
    
                if (implementation == address(0)) revert LBFactory__ImplementationNotSet();
    
                pair = ILBPair(
                    ImmutableClone.cloneDeterministic(
                        implementation,
                        abi.encodePacked(tokenX, tokenY, binStep),
                        keccak256(abi.encode(tokenA, tokenB, binStep))
                    )
                );
            }
    
            _lbPairsInfo[tokenA][tokenB][binStep] =
                LBPairInformation({binStep: binStep, LBPair: pair, createdByOwner: isOwner, ignoredForRouting: false});
    
            _allLBPairs.push(pair);
            _availableLBPairBinSteps[tokenA][tokenB].add(binStep);
    
            emit LBPairCreated(tokenX, tokenY, binStep, pair, _allLBPairs.length - 1);
    
            pair.initialize(
                preset.getBaseFactor(),
                preset.getFilterPeriod(),
                preset.getDecayPeriod(),
                preset.getReductionFactor(),
                preset.getVariableFeeControl(),
                preset.getProtocolShare(),
                preset.getMaxVolatilityAccumulator(),
                activeId
            );
        }
    
        /**
         * @notice Function to set whether the pair is ignored or not for routing, it will make the pair unusable by the router
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The pair doesn't exist
         * - The ignored state is already in the same state
         * @param tokenX The address of the first token of the pair
         * @param tokenY The address of the second token of the pair
         * @param binStep The bin step in basis point of the pair
         * @param ignored Whether to ignore (true) or not (false) the pair for routing
         */
        function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external override onlyOwner {
            (IERC20 tokenA, IERC20 tokenB) = _sortTokens(tokenX, tokenY);
    
            LBPairInformation memory pairInformation = _lbPairsInfo[tokenA][tokenB][binStep];
            if (address(pairInformation.LBPair) == address(0)) {
                revert LBFactory__LBPairDoesNotExist(tokenX, tokenY, binStep);
            }
    
            if (pairInformation.ignoredForRouting == ignored) revert LBFactory__LBPairIgnoredIsAlreadyInTheSameState();
    
            _lbPairsInfo[tokenA][tokenB][binStep].ignoredForRouting = ignored;
    
            emit LBPairIgnoredStateChanged(pairInformation.LBPair, ignored);
        }
    
        /**
         * @notice Sets the preset parameters of a bin step
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The binStep is lower than the minimum bin step
         * @param binStep The bin step in basis point, used to calculate the price
         * @param baseFactor The base factor, used to calculate the base fee, baseFee = baseFactor * binStep
         * @param filterPeriod The period where the accumulator value is untouched, prevent spam
         * @param decayPeriod The period where the accumulator value is decayed, by the reduction factor
         * @param reductionFactor The reduction factor, used to calculate the reduction of the accumulator
         * @param variableFeeControl The variable fee control, used to control the variable fee, can be 0 to disable it
         * @param protocolShare The share of the fees received by the protocol
         * @param maxVolatilityAccumulator The max value of the volatility accumulator
         */
        function setPreset(
            uint16 binStep,
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator,
            bool isOpen
        ) external override onlyOwner {
            if (binStep < _MIN_BIN_STEP) revert LBFactory__BinStepTooLow(binStep);
    
            bytes32 preset = bytes32(0).setStaticFeeParameters(
                baseFactor,
                filterPeriod,
                decayPeriod,
                reductionFactor,
                variableFeeControl,
                protocolShare,
                maxVolatilityAccumulator
            );
    
            if (isOpen) {
                preset = preset.setBool(true, _OFFSET_IS_PRESET_OPEN);
            }
    
            _presets.set(binStep, uint256(preset));
    
            emit PresetSet(
                binStep,
                baseFactor,
                filterPeriod,
                decayPeriod,
                reductionFactor,
                variableFeeControl,
                protocolShare,
                maxVolatilityAccumulator
            );
    
            emit PresetOpenStateChanged(binStep, isOpen);
        }
    
        /**
         * @notice Sets if the preset is open or not to be used by users
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The binStep doesn't have a preset
         * - The preset is already in the same state
         * @param binStep The bin step in basis point, used to calculate the price
         * @param isOpen Whether the preset is open or not
         */
        function setPresetOpenState(uint16 binStep, bool isOpen) external override onlyOwner {
            if (!_presets.contains(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);
    
            bytes32 preset = bytes32(_presets.get(binStep));
    
            if (preset.decodeBool(_OFFSET_IS_PRESET_OPEN) == isOpen) {
                revert LBFactory__PresetOpenStateIsAlreadyInTheSameState();
            }
    
            _presets.set(binStep, uint256(preset.setBool(isOpen, _OFFSET_IS_PRESET_OPEN)));
    
            emit PresetOpenStateChanged(binStep, isOpen);
        }
    
        /**
         * @notice Remove the preset linked to a binStep
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The binStep doesn't have a preset
         * @param binStep The bin step to remove
         */
        function removePreset(uint16 binStep) external override onlyOwner {
            if (!_presets.remove(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);
    
            emit PresetRemoved(binStep);
        }
    
        /**
         * @notice Function to set the fee parameter of a LBPair
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The pair doesn't exist
         * @param tokenX The address of the first token
         * @param tokenY The address of the second token
         * @param binStep The bin step in basis point, used to calculate the price
         * @param baseFactor The base factor, used to calculate the base fee, baseFee = baseFactor * binStep
         * @param filterPeriod The period where the accumulator value is untouched, prevent spam
         * @param decayPeriod The period where the accumulator value is decayed, by the reduction factor
         * @param reductionFactor The reduction factor, used to calculate the reduction of the accumulator
         * @param variableFeeControl The variable fee control, used to control the variable fee, can be 0 to disable it
         * @param protocolShare The share of the fees received by the protocol
         * @param maxVolatilityAccumulator The max value of volatility accumulator
         */
        function setFeesParametersOnPair(
            IERC20 tokenX,
            IERC20 tokenY,
            uint16 binStep,
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator
        ) external override onlyOwner {
            ILBPair lbPair = _getLBPairInformation(tokenX, tokenY, binStep).LBPair;
    
            if (address(lbPair) == address(0)) revert LBFactory__LBPairNotCreated(tokenX, tokenY, binStep);
    
            lbPair.setStaticFeeParameters(
                baseFactor,
                filterPeriod,
                decayPeriod,
                reductionFactor,
                variableFeeControl,
                protocolShare,
                maxVolatilityAccumulator
            );
        }
    
        /**
         * @notice Function to set the hooks parameters of a pair
         * @dev Needs to be called by an address with the LB_HOOKS_MANAGER_ROLE
         * Reverts if:
         * - The pair doesn't exist
         * - The hooks is `address(0)` or the hooks flags are all false
         * @param tokenX The address of the first token
         * @param tokenY The address of the second token
         * @param binStep The bin step in basis point, used to calculate the price
         * @param hooksParameters The hooks parameters
         * @param onHooksSetData The data to pass to the onHooksSet function
         */
        function setLBHooksParametersOnPair(
            IERC20 tokenX,
            IERC20 tokenY,
            uint16 binStep,
            bytes32 hooksParameters,
            bytes calldata onHooksSetData
        ) external override onlyRole(LB_HOOKS_MANAGER_ROLE) {
            if (Hooks.getHooks(hooksParameters) == address(0) || Hooks.getFlags(hooksParameters) == 0) {
                revert LBFactory__InvalidHooksParameters();
            }
    
            _setLBHooksParametersOnPair(tokenX, tokenY, binStep, hooksParameters, onHooksSetData);
        }
    
        /**
         * @notice Function to remove the hooks contract from the pair
         * @dev Needs to be called by an address with the LB_HOOKS_MANAGER_ROLE
         * Reverts if:
         * - The pair doesn't exist
         * @param tokenX The address of the first token
         * @param tokenY The address of the second token
         * @param binStep The bin step in basis point, used to calculate the price
         */
        function removeLBHooksOnPair(IERC20 tokenX, IERC20 tokenY, uint16 binStep)
            external
            override
            onlyRole(LB_HOOKS_MANAGER_ROLE)
        {
            _setLBHooksParametersOnPair(tokenX, tokenY, binStep, 0, new bytes(0));
        }
    
        /**
         * @notice Function to set the recipient of the fees. This address needs to be able to receive ERC20s
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The feeRecipient is `address(0)`
         * - The feeRecipient is the same as the current one
         * @param feeRecipient The address of the recipient
         */
        function setFeeRecipient(address feeRecipient) external override onlyOwner {
            _setFeeRecipient(feeRecipient);
        }
    
        /**
         * @notice Function to set the flash loan fee
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The flashLoanFee is the same as the current one
         * - The flashLoanFee is above the maximum flash loan fee
         * @param flashLoanFee The value of the fee for flash loan
         */
        function setFlashLoanFee(uint256 flashLoanFee) external override onlyOwner {
            uint256 oldFlashLoanFee = _flashLoanFee;
    
            if (oldFlashLoanFee == flashLoanFee) revert LBFactory__SameFlashLoanFee(flashLoanFee);
            if (flashLoanFee > _MAX_FLASHLOAN_FEE) revert LBFactory__FlashLoanFeeAboveMax(flashLoanFee, _MAX_FLASHLOAN_FEE);
    
            _flashLoanFee = flashLoanFee;
            emit FlashLoanFeeSet(oldFlashLoanFee, flashLoanFee);
        }
    
        /**
         * @notice Function to add an asset to the whitelist of quote assets
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The quoteAsset is already whitelisted
         * @param quoteAsset The quote asset (e.g: NATIVE, USDC...)
         */
        function addQuoteAsset(IERC20 quoteAsset) external override onlyOwner {
            if (!_quoteAssetWhitelist.add(address(quoteAsset))) {
                revert LBFactory__QuoteAssetAlreadyWhitelisted(quoteAsset);
            }
    
            emit QuoteAssetAdded(quoteAsset);
        }
    
        /**
         * @notice Function to remove an asset from the whitelist of quote assets
         * @dev Needs to be called by the owner
         * Reverts if:
         * - The quoteAsset was not whitelisted
         * @param quoteAsset The quote asset (e.g: NATIVE, USDC...)
         */
        function removeQuoteAsset(IERC20 quoteAsset) external override onlyOwner {
            if (!_quoteAssetWhitelist.remove(address(quoteAsset))) revert LBFactory__QuoteAssetNotWhitelisted(quoteAsset);
    
            emit QuoteAssetRemoved(quoteAsset);
        }
    
        function _isPresetOpen(bytes32 preset) internal pure returns (bool) {
            return preset.decodeBool(_OFFSET_IS_PRESET_OPEN);
        }
    
        /**
         * @notice Internal function to set the recipient of the fee
         * @param feeRecipient The address of the recipient
         */
        function _setFeeRecipient(address feeRecipient) internal {
            if (feeRecipient == address(0)) revert LBFactory__AddressZero();
    
            address oldFeeRecipient = _feeRecipient;
            if (oldFeeRecipient == feeRecipient) revert LBFactory__SameFeeRecipient(_feeRecipient);
    
            _feeRecipient = feeRecipient;
            emit FeeRecipientSet(oldFeeRecipient, feeRecipient);
        }
    
        /**
         * @notice Function to force the decay of the volatility accumulator of a pair
         * @dev Needs to be called by the owner
         * @param pair The pair to force the decay
         */
        function forceDecay(ILBPair pair) external override onlyOwner {
            pair.forceDecay();
        }
    
        /**
         * @notice Returns the LBPairInformation if it exists,
         * if not, then the address 0 is returned. The order doesn't matter
         * @param tokenA The address of the first token of the pair
         * @param tokenB The address of the second token of the pair
         * @param binStep The bin step of the LBPair
         * @return The LBPairInformation
         */
        function _getLBPairInformation(IERC20 tokenA, IERC20 tokenB, uint256 binStep)
            private
            view
            returns (LBPairInformation memory)
        {
            (tokenA, tokenB) = _sortTokens(tokenA, tokenB);
            return _lbPairsInfo[tokenA][tokenB][binStep];
        }
    
        /**
         * @notice Private view function to sort 2 tokens in ascending order
         * @param tokenA The first token
         * @param tokenB The second token
         * @return The sorted first token
         * @return The sorted second token
         */
        function _sortTokens(IERC20 tokenA, IERC20 tokenB) private pure returns (IERC20, IERC20) {
            if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
            return (tokenA, tokenB);
        }
    
        /**
         * @notice Internal function to set a hooks contract to the pair
         * @param tokenX The address of the first token
         * @param tokenY The address of the second token
         * @param binStep The bin step in basis point, used to calculate the price
         * @param hooksParameters The hooks parameters
         * @param onHooksSetData The data to pass to the onHooksSet function
         */
        function _setLBHooksParametersOnPair(
            IERC20 tokenX,
            IERC20 tokenY,
            uint16 binStep,
            bytes32 hooksParameters,
            bytes memory onHooksSetData
        ) internal {
            ILBPair lbPair = _getLBPairInformation(tokenX, tokenY, binStep).LBPair;
    
            if (address(lbPair) == address(0)) revert LBFactory__LBPairNotCreated(tokenX, tokenY, binStep);
            if (lbPair.getLBHooksParameters() == hooksParameters) revert LBFactory__SameHooksParameters(hooksParameters);
    
            lbPair.setHooksParameters(hooksParameters, onHooksSetData);
        }
    
        /**
         * @notice Returns whether the caller has the role or not, only the owner has the DEFAULT_ADMIN_ROLE
         * @param role The role to check
         * @param account The address to check
         * @return Whether the account has the role or not
         */
        function hasRole(bytes32 role, address account) public view override returns (bool) {
            if (role == DEFAULT_ADMIN_ROLE) return account == owner();
            return super.hasRole(role, account);
        }
    
        /**
         * @notice Grants a role to an address, the DEFAULT_ADMIN_ROLE can not be granted
         * @param role The role to grant
         * @param account The address to grant the role to
         * @return Whether the role has been granted or not
         */
        function _grantRole(bytes32 role, address account) internal override returns (bool) {
            if (role == DEFAULT_ADMIN_ROLE) revert LBFactory__CannotGrantDefaultAdminRole();
            return super._grantRole(role, account);
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
    
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position is the index of the value in the `values` array plus 1.
            // Position 0 is used to mean a value is not in the set.
            mapping(bytes32 value => uint256) _positions;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._positions[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We cache the value's position to prevent multiple reads from the same storage slot
            uint256 position = set._positions[value];
    
            if (position != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
    
                uint256 valueIndex = position - 1;
                uint256 lastIndex = set._values.length - 1;
    
                if (valueIndex != lastIndex) {
                    bytes32 lastValue = set._values[lastIndex];
    
                    // Move the lastValue to the index where the value to delete is
                    set._values[valueIndex] = lastValue;
                    // Update the tracked position of the lastValue (that was just moved)
                    set._positions[lastValue] = position;
                }
    
                // Delete the slot where the moved value was stored
                set._values.pop();
    
                // Delete the tracked position for the deleted slot
                delete set._positions[value];
    
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._positions[value] != 0;
        }
    
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
    
        // Bytes32Set
    
        struct Bytes32Set {
            Set _inner;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
    
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // AddressSet
    
        struct AddressSet {
            Set _inner;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // UintSet
    
        struct UintSet {
            Set _inner;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
    
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableMap.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
    
    pragma solidity ^0.8.20;
    
    import {EnumerableSet} from "./EnumerableSet.sol";
    
    /**
     * @dev Library for managing an enumerable variant of Solidity's
     * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
     * type.
     *
     * Maps have the following properties:
     *
     * - Entries are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Entries are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableMap for EnumerableMap.UintToAddressMap;
     *
     *     // Declare a set state variable
     *     EnumerableMap.UintToAddressMap private myMap;
     * }
     * ```
     *
     * The following map types are supported:
     *
     * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
     * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
     * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
     * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
     * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
     * - `uint256 -> bytes32` (`UintToBytes32Map`) since v5.1.0
     * - `address -> address` (`AddressToAddressMap`) since v5.1.0
     * - `address -> bytes32` (`AddressToBytes32Map`) since v5.1.0
     * - `bytes32 -> address` (`Bytes32ToAddressMap`) since v5.1.0
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableMap.
     * ====
     */
    library EnumerableMap {
        using EnumerableSet for EnumerableSet.Bytes32Set;
    
        // To implement this library for multiple types with as little code repetition as possible, we write it in
        // terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
        // and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
        // This means that we can only create new EnumerableMaps for types that fit in bytes32.
    
        /**
         * @dev Query for a nonexistent map key.
         */
        error EnumerableMapNonexistentKey(bytes32 key);
    
        struct Bytes32ToBytes32Map {
            // Storage of keys
            EnumerableSet.Bytes32Set _keys;
            mapping(bytes32 key => bytes32) _values;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
            map._values[key] = value;
            return map._keys.add(key);
        }
    
        /**
         * @dev Removes a key-value pair from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
            delete map._values[key];
            return map._keys.remove(key);
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
            return map._keys.contains(key);
        }
    
        /**
         * @dev Returns the number of key-value pairs in the map. O(1).
         */
        function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
            return map._keys.length();
        }
    
        /**
         * @dev Returns the key-value pair stored at position `index` in the map. O(1).
         *
         * Note that there are no guarantees on the ordering of entries inside the
         * array, and it may change when more entries are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
            bytes32 key = map._keys.at(index);
            return (key, map._values[key]);
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
            bytes32 value = map._values[key];
            if (value == bytes32(0)) {
                return (contains(map, key), bytes32(0));
            } else {
                return (true, value);
            }
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
            bytes32 value = map._values[key];
            if (value == 0 && !contains(map, key)) {
                revert EnumerableMapNonexistentKey(key);
            }
            return value;
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
            return map._keys.values();
        }
    
        // UintToUintMap
    
        struct UintToUintMap {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
            return set(map._inner, bytes32(key), bytes32(value));
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
            return remove(map._inner, bytes32(key));
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
            return contains(map._inner, bytes32(key));
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(UintToUintMap storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (uint256(key), uint256(value));
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
            (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
            return (success, uint256(value));
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
            return uint256(get(map._inner, bytes32(key)));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
            bytes32[] memory store = keys(map._inner);
            uint256[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // UintToAddressMap
    
        struct UintToAddressMap {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
            return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
            return remove(map._inner, bytes32(key));
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
            return contains(map._inner, bytes32(key));
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(UintToAddressMap storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (uint256(key), address(uint160(uint256(value))));
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
            (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
            return (success, address(uint160(uint256(value))));
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
            return address(uint160(uint256(get(map._inner, bytes32(key)))));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
            bytes32[] memory store = keys(map._inner);
            uint256[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // UintToBytes32Map
    
        struct UintToBytes32Map {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(UintToBytes32Map storage map, uint256 key, bytes32 value) internal returns (bool) {
            return set(map._inner, bytes32(key), value);
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(UintToBytes32Map storage map, uint256 key) internal returns (bool) {
            return remove(map._inner, bytes32(key));
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(UintToBytes32Map storage map, uint256 key) internal view returns (bool) {
            return contains(map._inner, bytes32(key));
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(UintToBytes32Map storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintToBytes32Map storage map, uint256 index) internal view returns (uint256, bytes32) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (uint256(key), value);
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(UintToBytes32Map storage map, uint256 key) internal view returns (bool, bytes32) {
            (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
            return (success, value);
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(UintToBytes32Map storage map, uint256 key) internal view returns (bytes32) {
            return get(map._inner, bytes32(key));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(UintToBytes32Map storage map) internal view returns (uint256[] memory) {
            bytes32[] memory store = keys(map._inner);
            uint256[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // AddressToUintMap
    
        struct AddressToUintMap {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
            return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(AddressToUintMap storage map, address key) internal returns (bool) {
            return remove(map._inner, bytes32(uint256(uint160(key))));
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
            return contains(map._inner, bytes32(uint256(uint160(key))));
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(AddressToUintMap storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (address(uint160(uint256(key))), uint256(value));
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
            (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
            return (success, uint256(value));
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
            return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
            bytes32[] memory store = keys(map._inner);
            address[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // AddressToAddressMap
    
        struct AddressToAddressMap {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) {
            return set(map._inner, bytes32(uint256(uint160(key))), bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(AddressToAddressMap storage map, address key) internal returns (bool) {
            return remove(map._inner, bytes32(uint256(uint160(key))));
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(AddressToAddressMap storage map, address key) internal view returns (bool) {
            return contains(map._inner, bytes32(uint256(uint160(key))));
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(AddressToAddressMap storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressToAddressMap storage map, uint256 index) internal view returns (address, address) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (address(uint160(uint256(key))), address(uint160(uint256(value))));
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool, address) {
            (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
            return (success, address(uint160(uint256(value))));
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(AddressToAddressMap storage map, address key) internal view returns (address) {
            return address(uint160(uint256(get(map._inner, bytes32(uint256(uint160(key)))))));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(AddressToAddressMap storage map) internal view returns (address[] memory) {
            bytes32[] memory store = keys(map._inner);
            address[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // AddressToBytes32Map
    
        struct AddressToBytes32Map {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(AddressToBytes32Map storage map, address key, bytes32 value) internal returns (bool) {
            return set(map._inner, bytes32(uint256(uint160(key))), value);
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(AddressToBytes32Map storage map, address key) internal returns (bool) {
            return remove(map._inner, bytes32(uint256(uint160(key))));
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(AddressToBytes32Map storage map, address key) internal view returns (bool) {
            return contains(map._inner, bytes32(uint256(uint160(key))));
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(AddressToBytes32Map storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressToBytes32Map storage map, uint256 index) internal view returns (address, bytes32) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (address(uint160(uint256(key))), value);
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(AddressToBytes32Map storage map, address key) internal view returns (bool, bytes32) {
            (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
            return (success, value);
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) {
            return get(map._inner, bytes32(uint256(uint160(key))));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(AddressToBytes32Map storage map) internal view returns (address[] memory) {
            bytes32[] memory store = keys(map._inner);
            address[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // Bytes32ToUintMap
    
        struct Bytes32ToUintMap {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
            return set(map._inner, key, bytes32(value));
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
            return remove(map._inner, key);
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
            return contains(map._inner, key);
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (key, uint256(value));
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
            (bool success, bytes32 value) = tryGet(map._inner, key);
            return (success, uint256(value));
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
            return uint256(get(map._inner, key));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
            bytes32[] memory store = keys(map._inner);
            bytes32[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // Bytes32ToAddressMap
    
        struct Bytes32ToAddressMap {
            Bytes32ToBytes32Map _inner;
        }
    
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(Bytes32ToAddressMap storage map, bytes32 key, address value) internal returns (bool) {
            return set(map._inner, key, bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Removes a value from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(Bytes32ToAddressMap storage map, bytes32 key) internal returns (bool) {
            return remove(map._inner, key);
        }
    
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool) {
            return contains(map._inner, key);
        }
    
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(Bytes32ToAddressMap storage map) internal view returns (uint256) {
            return length(map._inner);
        }
    
        /**
         * @dev Returns the element stored at position `index` in the map. O(1).
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32ToAddressMap storage map, uint256 index) internal view returns (bytes32, address) {
            (bytes32 key, bytes32 value) = at(map._inner, index);
            return (key, address(uint160(uint256(value))));
        }
    
        /**
         * @dev Tries to returns the value associated with `key`. O(1).
         * Does not revert if `key` is not in the map.
         */
        function tryGet(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool, address) {
            (bool success, bytes32 value) = tryGet(map._inner, key);
            return (success, address(uint160(uint256(value))));
        }
    
        /**
         * @dev Returns the value associated with `key`. O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (address) {
            return address(uint160(uint256(get(map._inner, key))));
        }
    
        /**
         * @dev Return the an array containing all the keys
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function keys(Bytes32ToAddressMap storage map) internal view returns (bytes32[] memory) {
            bytes32[] memory store = keys(map._inner);
            bytes32[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
    
    pragma solidity ^0.8.20;
    
    import {IAccessControl} from "./IAccessControl.sol";
    import {Context} from "../utils/Context.sol";
    import {ERC165} from "../utils/introspection/ERC165.sol";
    
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```solidity
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```solidity
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
     * to enforce additional security measures for this role.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address account => bool) hasRole;
            bytes32 adminRole;
        }
    
        mapping(bytes32 role => RoleData) private _roles;
    
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
    
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with an {AccessControlUnauthorizedAccount} error including the required role.
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role);
            _;
        }
    
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
    
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual returns (bool) {
            return _roles[role].hasRole[account];
        }
    
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
         * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
    
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
         * is missing `role`.
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert AccessControlUnauthorizedAccount(account, role);
            }
        }
    
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
            return _roles[role].adminRole;
        }
    
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleGranted} event.
         */
        function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
    
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleRevoked} event.
         */
        function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
    
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `callerConfirmation`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address callerConfirmation) public virtual {
            if (callerConfirmation != _msgSender()) {
                revert AccessControlBadConfirmation();
            }
    
            _revokeRole(role, callerConfirmation);
        }
    
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
    
        /**
         * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
            if (!hasRole(role, account)) {
                _roles[role].hasRole[account] = true;
                emit RoleGranted(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
            if (hasRole(role, account)) {
                _roles[role].hasRole[account] = false;
                emit RoleRevoked(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface of the ERC-20 standard as defined in the ERC.
     */
    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 value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
    
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
    
        /**
         * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
    
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` 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 value) external returns (bool);
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    
    pragma solidity ^0.8.20;
    
    import {Ownable} from "./Ownable.sol";
    
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * This extension of the {Ownable} contract includes a two-step mechanism to transfer
     * ownership, where the new owner must call {acceptOwnership} in order to replace the
     * old one. This can help prevent common mistakes, such as transfers of ownership to
     * incorrect accounts, or to contracts that are unable to interact with the
     * permission system.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2Step is Ownable {
        address private _pendingOwner;
    
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            return _pendingOwner;
        }
    
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            _pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            delete _pendingOwner;
            super._transferOwnership(newOwner);
        }
    
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    import {Constants} from "./Constants.sol";
    import {SafeCast} from "./math/SafeCast.sol";
    import {Encoded} from "./math/Encoded.sol";
    
    /**
     * @title Liquidity Book Pair Parameter Helper Library
     * @author Trader Joe
     * @dev This library contains functions to get and set parameters of a pair
     * The parameters are stored in a single bytes32 variable in the following format:
     * [0 - 16[: base factor (16 bits)
     * [16 - 28[: filter period (12 bits)
     * [28 - 40[: decay period (12 bits)
     * [40 - 54[: reduction factor (14 bits)
     * [54 - 78[: variable fee control (24 bits)
     * [78 - 92[: protocol share (14 bits)
     * [92 - 112[: max volatility accumulator (20 bits)
     * [112 - 132[: volatility accumulator (20 bits)
     * [132 - 152[: volatility reference (20 bits)
     * [152 - 176[: index reference (24 bits)
     * [176 - 216[: time of last update (40 bits)
     * [216 - 232[: oracle index (16 bits)
     * [232 - 256[: active index (24 bits)
     */
    library PairParameterHelper {
        using SafeCast for uint256;
        using Encoded for bytes32;
    
        error PairParametersHelper__InvalidParameter();
    
        uint256 internal constant OFFSET_BASE_FACTOR = 0;
        uint256 internal constant OFFSET_FILTER_PERIOD = 16;
        uint256 internal constant OFFSET_DECAY_PERIOD = 28;
        uint256 internal constant OFFSET_REDUCTION_FACTOR = 40;
        uint256 internal constant OFFSET_VAR_FEE_CONTROL = 54;
        uint256 internal constant OFFSET_PROTOCOL_SHARE = 78;
        uint256 internal constant OFFSET_MAX_VOL_ACC = 92;
        uint256 internal constant OFFSET_VOL_ACC = 112;
        uint256 internal constant OFFSET_VOL_REF = 132;
        uint256 internal constant OFFSET_ID_REF = 152;
        uint256 internal constant OFFSET_TIME_LAST_UPDATE = 176;
        uint256 internal constant OFFSET_ORACLE_ID = 216;
        uint256 internal constant OFFSET_ACTIVE_ID = 232;
    
        uint256 internal constant MASK_STATIC_PARAMETER = 0xffffffffffffffffffffffffffff;
    
        /**
         * @dev Get the base factor from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 16[: base factor (16 bits)
         * [16 - 256[: other parameters
         * @return baseFactor The base factor
         */
        function getBaseFactor(bytes32 params) internal pure returns (uint16 baseFactor) {
            baseFactor = params.decodeUint16(OFFSET_BASE_FACTOR);
        }
    
        /**
         * @dev Get the filter period from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 16[: other parameters
         * [16 - 28[: filter period (12 bits)
         * [28 - 256[: other parameters
         * @return filterPeriod The filter period
         */
        function getFilterPeriod(bytes32 params) internal pure returns (uint16 filterPeriod) {
            filterPeriod = params.decodeUint12(OFFSET_FILTER_PERIOD);
        }
    
        /**
         * @dev Get the decay period from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 28[: other parameters
         * [28 - 40[: decay period (12 bits)
         * [40 - 256[: other parameters
         * @return decayPeriod The decay period
         */
        function getDecayPeriod(bytes32 params) internal pure returns (uint16 decayPeriod) {
            decayPeriod = params.decodeUint12(OFFSET_DECAY_PERIOD);
        }
    
        /**
         * @dev Get the reduction factor from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 40[: other parameters
         * [40 - 54[: reduction factor (14 bits)
         * [54 - 256[: other parameters
         * @return reductionFactor The reduction factor
         */
        function getReductionFactor(bytes32 params) internal pure returns (uint16 reductionFactor) {
            reductionFactor = params.decodeUint14(OFFSET_REDUCTION_FACTOR);
        }
    
        /**
         * @dev Get the variable fee control from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 54[: other parameters
         * [54 - 78[: variable fee control (24 bits)
         * [78 - 256[: other parameters
         * @return variableFeeControl The variable fee control
         */
        function getVariableFeeControl(bytes32 params) internal pure returns (uint24 variableFeeControl) {
            variableFeeControl = params.decodeUint24(OFFSET_VAR_FEE_CONTROL);
        }
    
        /**
         * @dev Get the protocol share from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 78[: other parameters
         * [78 - 92[: protocol share (14 bits)
         * [92 - 256[: other parameters
         * @return protocolShare The protocol share
         */
        function getProtocolShare(bytes32 params) internal pure returns (uint16 protocolShare) {
            protocolShare = params.decodeUint14(OFFSET_PROTOCOL_SHARE);
        }
    
        /**
         * @dev Get the max volatility accumulator from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 92[: other parameters
         * [92 - 112[: max volatility accumulator (20 bits)
         * [112 - 256[: other parameters
         * @return maxVolatilityAccumulator The max volatility accumulator
         */
        function getMaxVolatilityAccumulator(bytes32 params) internal pure returns (uint24 maxVolatilityAccumulator) {
            maxVolatilityAccumulator = params.decodeUint20(OFFSET_MAX_VOL_ACC);
        }
    
        /**
         * @dev Get the volatility accumulator from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 112[: other parameters
         * [112 - 132[: volatility accumulator (20 bits)
         * [132 - 256[: other parameters
         * @return volatilityAccumulator The volatility accumulator
         */
        function getVolatilityAccumulator(bytes32 params) internal pure returns (uint24 volatilityAccumulator) {
            volatilityAccumulator = params.decodeUint20(OFFSET_VOL_ACC);
        }
    
        /**
         * @dev Get the volatility reference from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 132[: other parameters
         * [132 - 152[: volatility reference (20 bits)
         * [152 - 256[: other parameters
         * @return volatilityReference The volatility reference
         */
        function getVolatilityReference(bytes32 params) internal pure returns (uint24 volatilityReference) {
            volatilityReference = params.decodeUint20(OFFSET_VOL_REF);
        }
    
        /**
         * @dev Get the index reference from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 152[: other parameters
         * [152 - 176[: index reference (24 bits)
         * [176 - 256[: other parameters
         * @return idReference The index reference
         */
        function getIdReference(bytes32 params) internal pure returns (uint24 idReference) {
            idReference = params.decodeUint24(OFFSET_ID_REF);
        }
    
        /**
         * @dev Get the time of last update from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 176[: other parameters
         * [176 - 216[: time of last update (40 bits)
         * [216 - 256[: other parameters
         * @return timeOflastUpdate The time of last update
         */
        function getTimeOfLastUpdate(bytes32 params) internal pure returns (uint40 timeOflastUpdate) {
            timeOflastUpdate = params.decodeUint40(OFFSET_TIME_LAST_UPDATE);
        }
    
        /**
         * @dev Get the oracle id from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 216[: other parameters
         * [216 - 232[: oracle id (16 bits)
         * [232 - 256[: other parameters
         * @return oracleId The oracle id
         */
        function getOracleId(bytes32 params) internal pure returns (uint16 oracleId) {
            oracleId = params.decodeUint16(OFFSET_ORACLE_ID);
        }
    
        /**
         * @dev Get the active index from the encoded pair parameters
         * @param params The encoded pair parameters, as follows:
         * [0 - 232[: other parameters
         * [232 - 256[: active index (24 bits)
         * @return activeId The active index
         */
        function getActiveId(bytes32 params) internal pure returns (uint24 activeId) {
            activeId = params.decodeUint24(OFFSET_ACTIVE_ID);
        }
    
        /**
         * @dev Get the delta between the current active index and the cached active index
         * @param params The encoded pair parameters, as follows:
         * [0 - 232[: other parameters
         * [232 - 256[: active index (24 bits)
         * @param activeId The current active index
         * @return The delta
         */
        function getDeltaId(bytes32 params, uint24 activeId) internal pure returns (uint24) {
            uint24 id = getActiveId(params);
            unchecked {
                return activeId > id ? activeId - id : id - activeId;
            }
        }
    
        /**
         * @dev Calculates the base fee, with 18 decimals
         * @param params The encoded pair parameters
         * @param binStep The bin step (in basis points)
         * @return baseFee The base fee
         */
        function getBaseFee(bytes32 params, uint16 binStep) internal pure returns (uint256) {
            unchecked {
                // Base factor is in basis points, binStep is in basis points, so we multiply by 1e10
                return uint256(getBaseFactor(params)) * binStep * 1e10;
            }
        }
    
        /**
         * @dev Calculates the variable fee
         * @param params The encoded pair parameters
         * @param binStep The bin step (in basis points)
         * @return variableFee The variable fee
         */
        function getVariableFee(bytes32 params, uint16 binStep) internal pure returns (uint256 variableFee) {
            uint256 variableFeeControl = getVariableFeeControl(params);
    
            if (variableFeeControl != 0) {
                unchecked {
                    // The volatility accumulator is in basis points, binStep is in basis points,
                    // and the variable fee control is in basis points, so the result is in 100e18th
                    uint256 prod = uint256(getVolatilityAccumulator(params)) * binStep;
                    variableFee = (prod * prod * variableFeeControl + 99) / 100;
                }
            }
        }
    
        /**
         * @dev Calculates the total fee, which is the sum of the base fee and the variable fee
         * @param params The encoded pair parameters
         * @param binStep The bin step (in basis points)
         * @return totalFee The total fee
         */
        function getTotalFee(bytes32 params, uint16 binStep) internal pure returns (uint128) {
            unchecked {
                return (getBaseFee(params, binStep) + getVariableFee(params, binStep)).safe128();
            }
        }
    
        /**
         * @dev Set the oracle id in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param oracleId The oracle id
         * @return The updated encoded pair parameters
         */
        function setOracleId(bytes32 params, uint16 oracleId) internal pure returns (bytes32) {
            return params.set(oracleId, Encoded.MASK_UINT16, OFFSET_ORACLE_ID);
        }
    
        /**
         * @dev Set the volatility reference in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param volRef The volatility reference
         * @return The updated encoded pair parameters
         */
        function setVolatilityReference(bytes32 params, uint24 volRef) internal pure returns (bytes32) {
            if (volRef > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter();
    
            return params.set(volRef, Encoded.MASK_UINT20, OFFSET_VOL_REF);
        }
    
        /**
         * @dev Set the volatility accumulator in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param volAcc The volatility accumulator
         * @return The updated encoded pair parameters
         */
        function setVolatilityAccumulator(bytes32 params, uint24 volAcc) internal pure returns (bytes32) {
            if (volAcc > Encoded.MASK_UINT20) revert PairParametersHelper__InvalidParameter();
    
            return params.set(volAcc, Encoded.MASK_UINT20, OFFSET_VOL_ACC);
        }
    
        /**
         * @dev Set the active id in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param activeId The active id
         * @return newParams The updated encoded pair parameters
         */
        function setActiveId(bytes32 params, uint24 activeId) internal pure returns (bytes32 newParams) {
            return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ACTIVE_ID);
        }
    
        /**
         * @dev Sets the static fee parameters in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param baseFactor The base factor
         * @param filterPeriod The filter period
         * @param decayPeriod The decay period
         * @param reductionFactor The reduction factor
         * @param variableFeeControl The variable fee control
         * @param protocolShare The protocol share
         * @param maxVolatilityAccumulator The max volatility accumulator
         * @return newParams The updated encoded pair parameters
         */
        function setStaticFeeParameters(
            bytes32 params,
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator
        ) internal pure returns (bytes32 newParams) {
            if (
                filterPeriod > decayPeriod || decayPeriod > Encoded.MASK_UINT12
                    || reductionFactor > Constants.BASIS_POINT_MAX || protocolShare > Constants.MAX_PROTOCOL_SHARE
                    || maxVolatilityAccumulator > Encoded.MASK_UINT20
            ) revert PairParametersHelper__InvalidParameter();
    
            newParams = newParams.set(baseFactor, Encoded.MASK_UINT16, OFFSET_BASE_FACTOR);
            newParams = newParams.set(filterPeriod, Encoded.MASK_UINT12, OFFSET_FILTER_PERIOD);
            newParams = newParams.set(decayPeriod, Encoded.MASK_UINT12, OFFSET_DECAY_PERIOD);
            newParams = newParams.set(reductionFactor, Encoded.MASK_UINT14, OFFSET_REDUCTION_FACTOR);
            newParams = newParams.set(variableFeeControl, Encoded.MASK_UINT24, OFFSET_VAR_FEE_CONTROL);
            newParams = newParams.set(protocolShare, Encoded.MASK_UINT14, OFFSET_PROTOCOL_SHARE);
            newParams = newParams.set(maxVolatilityAccumulator, Encoded.MASK_UINT20, OFFSET_MAX_VOL_ACC);
    
            return params.set(uint256(newParams), MASK_STATIC_PARAMETER, 0);
        }
    
        /**
         * @dev Updates the index reference in the encoded pair parameters
         * @param params The encoded pair parameters
         * @return newParams The updated encoded pair parameters
         */
        function updateIdReference(bytes32 params) internal pure returns (bytes32 newParams) {
            uint24 activeId = getActiveId(params);
            return params.set(activeId, Encoded.MASK_UINT24, OFFSET_ID_REF);
        }
    
        /**
         * @dev Updates the time of last update in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param timestamp The timestamp
         * @return newParams The updated encoded pair parameters
         */
        function updateTimeOfLastUpdate(bytes32 params, uint256 timestamp) internal pure returns (bytes32 newParams) {
            uint40 currentTime = timestamp.safe40();
            return params.set(currentTime, Encoded.MASK_UINT40, OFFSET_TIME_LAST_UPDATE);
        }
    
        /**
         * @dev Updates the volatility reference in the encoded pair parameters
         * @param params The encoded pair parameters
         * @return The updated encoded pair parameters
         */
        function updateVolatilityReference(bytes32 params) internal pure returns (bytes32) {
            uint256 volAcc = getVolatilityAccumulator(params);
            uint256 reductionFactor = getReductionFactor(params);
    
            uint24 volRef;
            unchecked {
                volRef = uint24(volAcc * reductionFactor / Constants.BASIS_POINT_MAX);
            }
    
            return setVolatilityReference(params, volRef);
        }
    
        /**
         * @dev Updates the volatility accumulator in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param activeId The active id
         * @return The updated encoded pair parameters
         */
        function updateVolatilityAccumulator(bytes32 params, uint24 activeId) internal pure returns (bytes32) {
            uint256 idReference = getIdReference(params);
    
            uint256 deltaId;
            uint256 volAcc;
    
            unchecked {
                deltaId = activeId > idReference ? activeId - idReference : idReference - activeId;
                volAcc = (uint256(getVolatilityReference(params)) + deltaId * Constants.BASIS_POINT_MAX);
            }
    
            uint256 maxVolAcc = getMaxVolatilityAccumulator(params);
    
            volAcc = volAcc > maxVolAcc ? maxVolAcc : volAcc;
    
            return setVolatilityAccumulator(params, uint24(volAcc));
        }
    
        /**
         * @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param timestamp The timestamp
         * @return The updated encoded pair parameters
         */
        function updateReferences(bytes32 params, uint256 timestamp) internal pure returns (bytes32) {
            uint256 dt = timestamp - getTimeOfLastUpdate(params);
    
            if (dt >= getFilterPeriod(params)) {
                params = updateIdReference(params);
                params = dt < getDecayPeriod(params) ? updateVolatilityReference(params) : setVolatilityReference(params, 0);
            }
    
            return updateTimeOfLastUpdate(params, timestamp);
        }
    
        /**
         * @dev Updates the volatility reference and the volatility accumulator in the encoded pair parameters
         * @param params The encoded pair parameters
         * @param activeId The active id
         * @param timestamp The timestamp
         * @return The updated encoded pair parameters
         */
        function updateVolatilityParameters(bytes32 params, uint24 activeId, uint256 timestamp)
            internal
            pure
            returns (bytes32)
        {
            params = updateReferences(params, timestamp);
            return updateVolatilityAccumulator(params, activeId);
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    /**
     * @title Liquidity Book Encoded Library
     * @author Trader Joe
     * @notice Helper contract used for decoding bytes32 sample
     */
    library Encoded {
        uint256 internal constant MASK_UINT1 = 0x1;
        uint256 internal constant MASK_UINT8 = 0xff;
        uint256 internal constant MASK_UINT12 = 0xfff;
        uint256 internal constant MASK_UINT14 = 0x3fff;
        uint256 internal constant MASK_UINT16 = 0xffff;
        uint256 internal constant MASK_UINT20 = 0xfffff;
        uint256 internal constant MASK_UINT24 = 0xffffff;
        uint256 internal constant MASK_UINT40 = 0xffffffffff;
        uint256 internal constant MASK_UINT64 = 0xffffffffffffffff;
        uint256 internal constant MASK_UINT128 = 0xffffffffffffffffffffffffffffffff;
    
        /**
         * @notice Internal function to set a value in an encoded bytes32 using a mask and offset
         * @dev This function can overflow
         * @param encoded The previous encoded value
         * @param value The value to encode
         * @param mask The mask
         * @param offset The offset
         * @return newEncoded The new encoded value
         */
        function set(bytes32 encoded, uint256 value, uint256 mask, uint256 offset)
            internal
            pure
            returns (bytes32 newEncoded)
        {
            assembly {
                newEncoded := and(encoded, not(shl(offset, mask)))
                newEncoded := or(newEncoded, shl(offset, and(value, mask)))
            }
        }
    
        /**
         * @notice Internal function to set a bool in an encoded bytes32 using an offset
         * @dev This function can overflow
         * @param encoded The previous encoded value
         * @param boolean The bool to encode
         * @param offset The offset
         * @return newEncoded The new encoded value
         */
        function setBool(bytes32 encoded, bool boolean, uint256 offset) internal pure returns (bytes32 newEncoded) {
            return set(encoded, boolean ? 1 : 0, MASK_UINT1, offset);
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample using a mask and offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param mask The mask
         * @param offset The offset
         * @return value The decoded value
         */
        function decode(bytes32 encoded, uint256 mask, uint256 offset) internal pure returns (uint256 value) {
            assembly {
                value := and(shr(offset, encoded), mask)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a bool using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return boolean The decoded value as a bool
         */
        function decodeBool(bytes32 encoded, uint256 offset) internal pure returns (bool boolean) {
            assembly {
                boolean := and(shr(offset, encoded), MASK_UINT1)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint8 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value
         */
        function decodeUint8(bytes32 encoded, uint256 offset) internal pure returns (uint8 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT8)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint12 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value as a uint16, since uint12 is not supported
         */
        function decodeUint12(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT12)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint14 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value as a uint16, since uint14 is not supported
         */
        function decodeUint14(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT14)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint16 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value
         */
        function decodeUint16(bytes32 encoded, uint256 offset) internal pure returns (uint16 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT16)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint20 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value as a uint24, since uint20 is not supported
         */
        function decodeUint20(bytes32 encoded, uint256 offset) internal pure returns (uint24 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT20)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint24 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value
         */
        function decodeUint24(bytes32 encoded, uint256 offset) internal pure returns (uint24 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT24)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint40 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value
         */
        function decodeUint40(bytes32 encoded, uint256 offset) internal pure returns (uint40 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT40)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint64 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value
         */
        function decodeUint64(bytes32 encoded, uint256 offset) internal pure returns (uint64 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT64)
            }
        }
    
        /**
         * @notice Internal function to decode a bytes32 sample into a uint128 using an offset
         * @dev This function can overflow
         * @param encoded The encoded value
         * @param offset The offset
         * @return value The decoded value
         */
        function decodeUint128(bytes32 encoded, uint256 offset) internal pure returns (uint128 value) {
            assembly {
                value := and(shr(offset, encoded), MASK_UINT128)
            }
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    /**
     * @title Liquidity Book Immutable Clone Library
     * @notice Minimal immutable proxy library.
     * @author Trader Joe
     * @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol)
     * @author Minimal proxy by 0age (https://github.com/0age)
     * @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie
     * (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
     * @dev Minimal proxy:
     * Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime,
     * it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern,
     * which saves 4 gas over the erc-1167 pattern during runtime, and has the smallest bytecode.
     * @dev Clones with immutable args (CWIA):
     * The implementation of CWIA here doesn't implements a `receive()` as it is not needed for LB.
     */
    library ImmutableClone {
        error DeploymentFailed();
        error PackedDataTooBig();
    
        /**
         * @dev Deploys a deterministic clone of `implementation` using immutable arguments encoded in `data`, with `salt`
         * @param implementation The address of the implementation
         * @param data The encoded immutable arguments
         * @param salt The salt
         */
        function cloneDeterministic(address implementation, bytes memory data, bytes32 salt)
            internal
            returns (address instance)
        {
            assembly {
                // Compute the boundaries of the data and cache the memory slots around it.
                let mBefore2 := mload(sub(data, 0x40))
                let mBefore1 := mload(sub(data, 0x20))
                let dataLength := mload(data)
                let dataEnd := add(add(data, 0x20), dataLength)
                let mAfter1 := mload(dataEnd)
    
                // +2 bytes for telling how much data there is appended to the call.
                let extraLength := add(dataLength, 2)
                // The `creationSize` is `extraLength + 63`
                // The `runSize` is `creationSize - 10`.
    
                // if `extraLength` is greater than `0xffca` revert as the `creationSize` would be greater than `0xffff`.
                if gt(extraLength, 0xffca) {
                    // Store the function selector of `PackedDataTooBig()`.
                    mstore(0x00, 0xc8c78139)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
    
                /**
                 * ---------------------------------------------------------------------------------------------------+
                 * CREATION (10 bytes)                                                                                |
                 * ---------------------------------------------------------------------------------------------------|
                 * Opcode     | Mnemonic          | Stack     | Memory                                                |
                 * ---------------------------------------------------------------------------------------------------|
                 * 61 runSize | PUSH2 runSize     | r         |                                                       |
                 * 3d         | RETURNDATASIZE    | 0 r       |                                                       |
                 * 81         | DUP2              | r 0 r     |                                                       |
                 * 60 offset  | PUSH1 offset      | o r 0 r   |                                                       |
                 * 3d         | RETURNDATASIZE    | 0 o r 0 r |                                                       |
                 * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code                            |
                 * f3         | RETURN            |           | [0..runSize): runtime code                            |
                 * ---------------------------------------------------------------------------------------------------|
                 * RUNTIME (98 bytes + extraLength)                                                                   |
                 * ---------------------------------------------------------------------------------------------------|
                 * Opcode   | Mnemonic       | Stack                    | Memory                                      |
                 * ---------------------------------------------------------------------------------------------------|
                 *                                                                                                    |
                 * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
                 * 36       | CALLDATASIZE   | cds                      |                                             |
                 * 3d       | RETURNDATASIZE | 0 cds                    |                                             |
                 * 3d       | RETURNDATASIZE | 0 0 cds                  |                                             |
                 * 37       | CALLDATACOPY   |                          | [0..cds): calldata                          |
                 *                                                                                                    |
                 * ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
                 * 3d       | RETURNDATASIZE | 0                        | [0..cds): calldata                          |
                 * 3d       | RETURNDATASIZE | 0 0                      | [0..cds): calldata                          |
                 * 3d       | RETURNDATASIZE | 0 0 0                    | [0..cds): calldata                          |
                 * 3d       | RETURNDATASIZE | 0 0 0 0                  | [0..cds): calldata                          |
                 * 61 extra | PUSH2 extra    | e 0 0 0 0                | [0..cds): calldata                          |
                 *                                                                                                    |
                 * ::: copy extra data to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
                 * 80       | DUP1           | e e 0 0 0 0              | [0..cds): calldata                          |
                 * 60 0x35  | PUSH1 0x35     | 0x35 e e 0 0 0 0         | [0..cds): calldata                          |
                 * 36       | CALLDATASIZE   | cds 0x35 e e 0 0 0 0     | [0..cds): calldata                          |
                 * 39       | CODECOPY       | e 0 0 0 0                | [0..cds): calldata, [cds..cds+e): extraData |
                 *                                                                                                    |
                 * ::: delegate call to the implementation contract ::::::::::::::::::::::::::::::::::::::::::::::::: |
                 * 36       | CALLDATASIZE   | cds e 0 0 0 0            | [0..cds): calldata, [cds..cds+e): extraData |
                 * 01       | ADD            | cds+e 0 0 0 0            | [0..cds): calldata, [cds..cds+e): extraData |
                 * 3d       | RETURNDATASIZE | 0 cds+e 0 0 0 0          | [0..cds): calldata, [cds..cds+e): extraData |
                 * 73 addr  | PUSH20 addr    | addr 0 cds+e 0 0 0 0     | [0..cds): calldata, [cds..cds+e): extraData |
                 * 5a       | GAS            | gas addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
                 * f4       | DELEGATECALL   | success 0 0              | [0..cds): calldata, [cds..cds+e): extraData |
                 *                                                                                                    |
                 * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
                 * 3d       | RETURNDATASIZE | rds success 0 0          | [0..cds): calldata, [cds..cds+e): extraData |
                 * 3d       | RETURNDATASIZE | rds rds success 0 0      | [0..cds): calldata, [cds..cds+e): extraData |
                 * 93       | SWAP4          | 0 rds success 0 rds      | [0..cds): calldata, [cds..cds+e): extraData |
                 * 80       | DUP1           | 0 0 rds success 0 rds    | [0..cds): calldata, [cds..cds+e): extraData |
                 * 3e       | RETURNDATACOPY | success 0 rds            | [0..rds): returndata                        |
                 *                                                                                                    |
                 * 60 0x33  | PUSH1 0x33     | 0x33 success 0 rds       | [0..rds): returndata                        |
                 * 57       | JUMPI          | 0 rds                    | [0..rds): returndata                        |
                 *                                                                                                    |
                 * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
                 * fd       | REVERT         |                          | [0..rds): returndata                        |
                 *                                                                                                    |
                 * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
                 * 5b       | JUMPDEST       | 0 rds                    | [0..rds): returndata                        |
                 * f3       | RETURN         |                          | [0..rds): returndata                        |
                 * ---------------------------------------------------------------------------------------------------+
                 */
                // Write the bytecode before the data.
                mstore(data, 0x5af43d3d93803e603357fd5bf3)
                // Write the address of the implementation.
                mstore(sub(data, 0x0d), implementation)
                mstore(
                    sub(data, 0x21),
                    or(
                        shl(0xd8, add(extraLength, 0x35)),
                        or(shl(0x48, extraLength), 0x6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d73)
                    )
                )
                mstore(dataEnd, shl(0xf0, extraLength))
    
                // Create the instance.
                instance := create2(0, sub(data, 0x1f), add(extraLength, 0x3f), salt)
    
                // If `instance` is zero, revert.
                if iszero(instance) {
                    // Store the function selector of `DeploymentFailed()`.
                    mstore(0x00, 0x30116425)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
    
                // Restore the overwritten memory surrounding `data`.
                mstore(dataEnd, mAfter1)
                mstore(data, dataLength)
                mstore(sub(data, 0x20), mBefore1)
                mstore(sub(data, 0x40), mBefore2)
            }
        }
    
        /**
         * @dev Returns the initialization code hash of the clone of `implementation`
         * using immutable arguments encoded in `data`.
         * Used for mining vanity addresses with create2crunch.
         * @param implementation The address of the implementation contract.
         * @param data The encoded immutable arguments.
         * @return hash The initialization code hash.
         */
        function initCodeHash(address implementation, bytes memory data) internal pure returns (bytes32 hash) {
            assembly {
                // Compute the boundaries of the data and cache the memory slots around it.
                let mBefore2 := mload(sub(data, 0x40))
                let mBefore1 := mload(sub(data, 0x20))
                let dataLength := mload(data)
                let dataEnd := add(add(data, 0x20), dataLength)
                let mAfter1 := mload(dataEnd)
    
                // +2 bytes for telling how much data there is appended to the call.
                let extraLength := add(dataLength, 2)
                // The `creationSize` is `extraLength + 63`
                // The `runSize` is `creationSize - 10`.
    
                // if `extraLength` is greater than `0xffca` revert as the `creationSize` would be greater than `0xffff`.
                if gt(extraLength, 0xffca) {
                    // Store the function selector of `PackedDataTooBig()`.
                    mstore(0x00, 0xc8c78139)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
    
                // Write the bytecode before the data.
                mstore(data, 0x5af43d3d93803e603357fd5bf3)
                // Write the address of the implementation.
                mstore(sub(data, 0x0d), implementation)
                mstore(
                    sub(data, 0x21),
                    or(
                        shl(0xd8, add(extraLength, 0x35)),
                        or(shl(0x48, extraLength), 0x6100003d81600a3d39f3363d3d373d3d3d3d610000806035363936013d73)
                    )
                )
                mstore(dataEnd, shl(0xf0, extraLength))
    
                // Create the instance.
                hash := keccak256(sub(data, 0x1f), add(extraLength, 0x3f))
    
                // Restore the overwritten memory surrounding `data`.
                mstore(dataEnd, mAfter1)
                mstore(data, dataLength)
                mstore(sub(data, 0x20), mBefore1)
                mstore(sub(data, 0x40), mBefore2)
            }
        }
    
        /**
         * @dev Returns the address of the deterministic clone of
         * `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`.
         * @param implementation The address of the implementation.
         * @param data The immutable arguments of the implementation.
         * @param salt The salt used to compute the address.
         * @param deployer The address of the deployer.
         * @return predicted The predicted address.
         */
        function predictDeterministicAddress(address implementation, bytes memory data, bytes32 salt, address deployer)
            internal
            pure
            returns (address predicted)
        {
            bytes32 hash = initCodeHash(implementation, data);
            predicted = predictDeterministicAddress(hash, salt, deployer);
        }
    
        /**
         * @dev Returns the address when a contract with initialization code hash,
         * `hash`, is deployed with `salt`, by `deployer`.
         * @param hash The initialization code hash.
         * @param salt The salt used to compute the address.
         * @param deployer The address of the deployer.
         * @return predicted The predicted address.
         */
        function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer)
            internal
            pure
            returns (address predicted)
        {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the boundaries of the data and cache the memory slots around it.
                let mBefore := mload(0x35)
    
                // Compute and store the bytecode hash.
                mstore8(0x00, 0xff) // Write the prefix.
                mstore(0x35, hash)
                mstore(0x01, shl(96, deployer))
                mstore(0x15, salt)
                predicted := keccak256(0x00, 0x55)
    
                // Restore the part of the free memory pointer that has been overwritten.
                mstore(0x35, mBefore)
            }
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    import {Uint128x128Math} from "./math/Uint128x128Math.sol";
    import {Uint256x256Math} from "./math/Uint256x256Math.sol";
    import {SafeCast} from "./math/SafeCast.sol";
    import {Constants} from "./Constants.sol";
    
    /**
     * @title Liquidity Book Price Helper Library
     * @author Trader Joe
     * @notice This library contains functions to calculate prices
     */
    library PriceHelper {
        using Uint128x128Math for uint256;
        using Uint256x256Math for uint256;
        using SafeCast for uint256;
    
        int256 private constant REAL_ID_SHIFT = 1 << 23;
    
        /**
         * @dev Calculates the price from the id and the bin step
         * @param id The id
         * @param binStep The bin step
         * @return price The price as a 128.128-binary fixed-point number
         */
        function getPriceFromId(uint24 id, uint16 binStep) internal pure returns (uint256 price) {
            uint256 base = getBase(binStep);
            int256 exponent = getExponent(id);
    
            price = base.pow(exponent);
        }
    
        /**
         * @dev Calculates the id from the price and the bin step
         * @param price The price as a 128.128-binary fixed-point number
         * @param binStep The bin step
         * @return id The id
         */
        function getIdFromPrice(uint256 price, uint16 binStep) internal pure returns (uint24 id) {
            uint256 base = getBase(binStep);
            int256 realId = price.log2() / base.log2();
    
            unchecked {
                id = uint256(REAL_ID_SHIFT + realId).safe24();
            }
        }
    
        /**
         * @dev Calculates the base from the bin step, which is `1 + binStep / BASIS_POINT_MAX`
         * @param binStep The bin step
         * @return base The base
         */
        function getBase(uint16 binStep) internal pure returns (uint256) {
            unchecked {
                return Constants.SCALE + (uint256(binStep) << Constants.SCALE_OFFSET) / Constants.BASIS_POINT_MAX;
            }
        }
    
        /**
         * @dev Calculates the exponent from the id, which is `id - REAL_ID_SHIFT`
         * @param id The id
         * @return exponent The exponent
         */
        function getExponent(uint24 id) internal pure returns (int256) {
            unchecked {
                return int256(uint256(id)) - REAL_ID_SHIFT;
            }
        }
    
        /**
         * @dev Converts a price with 18 decimals to a 128.128-binary fixed-point number
         * @param price The price with 18 decimals
         * @return price128x128 The 128.128-binary fixed-point number
         */
        function convertDecimalPriceTo128x128(uint256 price) internal pure returns (uint256) {
            return price.shiftDivRoundDown(Constants.SCALE_OFFSET, Constants.PRECISION);
        }
    
        /**
         * @dev Converts a 128.128-binary fixed-point number to a price with 18 decimals
         * @param price128x128 The 128.128-binary fixed-point number
         * @return price The price with 18 decimals
         */
        function convert128x128PriceToDecimal(uint256 price128x128) internal pure returns (uint256) {
            return price128x128.mulShiftRoundDown(Constants.PRECISION, Constants.SCALE_OFFSET);
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    /**
     * @title Liquidity Book Safe Cast Library
     * @author Trader Joe
     * @notice This library contains functions to safely cast uint256 to different uint types.
     */
    library SafeCast {
        error SafeCast__Exceeds248Bits();
        error SafeCast__Exceeds240Bits();
        error SafeCast__Exceeds232Bits();
        error SafeCast__Exceeds224Bits();
        error SafeCast__Exceeds216Bits();
        error SafeCast__Exceeds208Bits();
        error SafeCast__Exceeds200Bits();
        error SafeCast__Exceeds192Bits();
        error SafeCast__Exceeds184Bits();
        error SafeCast__Exceeds176Bits();
        error SafeCast__Exceeds168Bits();
        error SafeCast__Exceeds160Bits();
        error SafeCast__Exceeds152Bits();
        error SafeCast__Exceeds144Bits();
        error SafeCast__Exceeds136Bits();
        error SafeCast__Exceeds128Bits();
        error SafeCast__Exceeds120Bits();
        error SafeCast__Exceeds112Bits();
        error SafeCast__Exceeds104Bits();
        error SafeCast__Exceeds96Bits();
        error SafeCast__Exceeds88Bits();
        error SafeCast__Exceeds80Bits();
        error SafeCast__Exceeds72Bits();
        error SafeCast__Exceeds64Bits();
        error SafeCast__Exceeds56Bits();
        error SafeCast__Exceeds48Bits();
        error SafeCast__Exceeds40Bits();
        error SafeCast__Exceeds32Bits();
        error SafeCast__Exceeds24Bits();
        error SafeCast__Exceeds16Bits();
        error SafeCast__Exceeds8Bits();
    
        /**
         * @dev Returns x on uint248 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint248
         */
        function safe248(uint256 x) internal pure returns (uint248 y) {
            if ((y = uint248(x)) != x) revert SafeCast__Exceeds248Bits();
        }
    
        /**
         * @dev Returns x on uint240 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint240
         */
        function safe240(uint256 x) internal pure returns (uint240 y) {
            if ((y = uint240(x)) != x) revert SafeCast__Exceeds240Bits();
        }
    
        /**
         * @dev Returns x on uint232 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint232
         */
        function safe232(uint256 x) internal pure returns (uint232 y) {
            if ((y = uint232(x)) != x) revert SafeCast__Exceeds232Bits();
        }
    
        /**
         * @dev Returns x on uint224 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint224
         */
        function safe224(uint256 x) internal pure returns (uint224 y) {
            if ((y = uint224(x)) != x) revert SafeCast__Exceeds224Bits();
        }
    
        /**
         * @dev Returns x on uint216 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint216
         */
        function safe216(uint256 x) internal pure returns (uint216 y) {
            if ((y = uint216(x)) != x) revert SafeCast__Exceeds216Bits();
        }
    
        /**
         * @dev Returns x on uint208 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint208
         */
        function safe208(uint256 x) internal pure returns (uint208 y) {
            if ((y = uint208(x)) != x) revert SafeCast__Exceeds208Bits();
        }
    
        /**
         * @dev Returns x on uint200 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint200
         */
        function safe200(uint256 x) internal pure returns (uint200 y) {
            if ((y = uint200(x)) != x) revert SafeCast__Exceeds200Bits();
        }
    
        /**
         * @dev Returns x on uint192 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint192
         */
        function safe192(uint256 x) internal pure returns (uint192 y) {
            if ((y = uint192(x)) != x) revert SafeCast__Exceeds192Bits();
        }
    
        /**
         * @dev Returns x on uint184 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint184
         */
        function safe184(uint256 x) internal pure returns (uint184 y) {
            if ((y = uint184(x)) != x) revert SafeCast__Exceeds184Bits();
        }
    
        /**
         * @dev Returns x on uint176 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint176
         */
        function safe176(uint256 x) internal pure returns (uint176 y) {
            if ((y = uint176(x)) != x) revert SafeCast__Exceeds176Bits();
        }
    
        /**
         * @dev Returns x on uint168 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint168
         */
        function safe168(uint256 x) internal pure returns (uint168 y) {
            if ((y = uint168(x)) != x) revert SafeCast__Exceeds168Bits();
        }
    
        /**
         * @dev Returns x on uint160 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint160
         */
        function safe160(uint256 x) internal pure returns (uint160 y) {
            if ((y = uint160(x)) != x) revert SafeCast__Exceeds160Bits();
        }
    
        /**
         * @dev Returns x on uint152 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint152
         */
        function safe152(uint256 x) internal pure returns (uint152 y) {
            if ((y = uint152(x)) != x) revert SafeCast__Exceeds152Bits();
        }
    
        /**
         * @dev Returns x on uint144 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint144
         */
        function safe144(uint256 x) internal pure returns (uint144 y) {
            if ((y = uint144(x)) != x) revert SafeCast__Exceeds144Bits();
        }
    
        /**
         * @dev Returns x on uint136 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint136
         */
        function safe136(uint256 x) internal pure returns (uint136 y) {
            if ((y = uint136(x)) != x) revert SafeCast__Exceeds136Bits();
        }
    
        /**
         * @dev Returns x on uint128 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint128
         */
        function safe128(uint256 x) internal pure returns (uint128 y) {
            if ((y = uint128(x)) != x) revert SafeCast__Exceeds128Bits();
        }
    
        /**
         * @dev Returns x on uint120 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint120
         */
        function safe120(uint256 x) internal pure returns (uint120 y) {
            if ((y = uint120(x)) != x) revert SafeCast__Exceeds120Bits();
        }
    
        /**
         * @dev Returns x on uint112 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint112
         */
        function safe112(uint256 x) internal pure returns (uint112 y) {
            if ((y = uint112(x)) != x) revert SafeCast__Exceeds112Bits();
        }
    
        /**
         * @dev Returns x on uint104 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint104
         */
        function safe104(uint256 x) internal pure returns (uint104 y) {
            if ((y = uint104(x)) != x) revert SafeCast__Exceeds104Bits();
        }
    
        /**
         * @dev Returns x on uint96 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint96
         */
        function safe96(uint256 x) internal pure returns (uint96 y) {
            if ((y = uint96(x)) != x) revert SafeCast__Exceeds96Bits();
        }
    
        /**
         * @dev Returns x on uint88 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint88
         */
        function safe88(uint256 x) internal pure returns (uint88 y) {
            if ((y = uint88(x)) != x) revert SafeCast__Exceeds88Bits();
        }
    
        /**
         * @dev Returns x on uint80 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint80
         */
        function safe80(uint256 x) internal pure returns (uint80 y) {
            if ((y = uint80(x)) != x) revert SafeCast__Exceeds80Bits();
        }
    
        /**
         * @dev Returns x on uint72 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint72
         */
        function safe72(uint256 x) internal pure returns (uint72 y) {
            if ((y = uint72(x)) != x) revert SafeCast__Exceeds72Bits();
        }
    
        /**
         * @dev Returns x on uint64 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint64
         */
        function safe64(uint256 x) internal pure returns (uint64 y) {
            if ((y = uint64(x)) != x) revert SafeCast__Exceeds64Bits();
        }
    
        /**
         * @dev Returns x on uint56 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint56
         */
        function safe56(uint256 x) internal pure returns (uint56 y) {
            if ((y = uint56(x)) != x) revert SafeCast__Exceeds56Bits();
        }
    
        /**
         * @dev Returns x on uint48 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint48
         */
        function safe48(uint256 x) internal pure returns (uint48 y) {
            if ((y = uint48(x)) != x) revert SafeCast__Exceeds48Bits();
        }
    
        /**
         * @dev Returns x on uint40 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint40
         */
        function safe40(uint256 x) internal pure returns (uint40 y) {
            if ((y = uint40(x)) != x) revert SafeCast__Exceeds40Bits();
        }
    
        /**
         * @dev Returns x on uint32 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint32
         */
        function safe32(uint256 x) internal pure returns (uint32 y) {
            if ((y = uint32(x)) != x) revert SafeCast__Exceeds32Bits();
        }
    
        /**
         * @dev Returns x on uint24 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint24
         */
        function safe24(uint256 x) internal pure returns (uint24 y) {
            if ((y = uint24(x)) != x) revert SafeCast__Exceeds24Bits();
        }
    
        /**
         * @dev Returns x on uint16 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint16
         */
        function safe16(uint256 x) internal pure returns (uint16 y) {
            if ((y = uint16(x)) != x) revert SafeCast__Exceeds16Bits();
        }
    
        /**
         * @dev Returns x on uint8 and check that it does not overflow
         * @param x The value as an uint256
         * @return y The value as an uint8
         */
        function safe8(uint256 x) internal pure returns (uint8 y) {
            if ((y = uint8(x)) != x) revert SafeCast__Exceeds8Bits();
        }
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {ILBHooks} from "../interfaces/ILBHooks.sol";
    
    /**
     * @title Hooks library
     * @notice This library contains functions that should be used to interact with hooks
     */
    library Hooks {
        error Hooks__CallFailed();
    
        bytes32 internal constant BEFORE_SWAP_FLAG = bytes32(uint256(1 << 160));
        bytes32 internal constant AFTER_SWAP_FLAG = bytes32(uint256(1 << 161));
        bytes32 internal constant BEFORE_FLASH_LOAN_FLAG = bytes32(uint256(1 << 162));
        bytes32 internal constant AFTER_FLASH_LOAN_FLAG = bytes32(uint256(1 << 163));
        bytes32 internal constant BEFORE_MINT_FLAG = bytes32(uint256(1 << 164));
        bytes32 internal constant AFTER_MINT_FLAG = bytes32(uint256(1 << 165));
        bytes32 internal constant BEFORE_BURN_FLAG = bytes32(uint256(1 << 166));
        bytes32 internal constant AFTER_BURN_FLAG = bytes32(uint256(1 << 167));
        bytes32 internal constant BEFORE_TRANSFER_FLAG = bytes32(uint256(1 << 168));
        bytes32 internal constant AFTER_TRANSFER_FLAG = bytes32(uint256(1 << 169));
    
        struct Parameters {
            address hooks;
            bool beforeSwap;
            bool afterSwap;
            bool beforeFlashLoan;
            bool afterFlashLoan;
            bool beforeMint;
            bool afterMint;
            bool beforeBurn;
            bool afterBurn;
            bool beforeBatchTransferFrom;
            bool afterBatchTransferFrom;
        }
    
        /**
         * @dev Helper function to encode the hooks parameters to a single bytes32 value
         * @param parameters The hooks parameters
         * @return hooksParameters The encoded hooks parameters
         */
        function encode(Parameters memory parameters) internal pure returns (bytes32 hooksParameters) {
            hooksParameters = bytes32(uint256(uint160(address(parameters.hooks))));
    
            if (parameters.beforeSwap) hooksParameters |= BEFORE_SWAP_FLAG;
            if (parameters.afterSwap) hooksParameters |= AFTER_SWAP_FLAG;
            if (parameters.beforeFlashLoan) hooksParameters |= BEFORE_FLASH_LOAN_FLAG;
            if (parameters.afterFlashLoan) hooksParameters |= AFTER_FLASH_LOAN_FLAG;
            if (parameters.beforeMint) hooksParameters |= BEFORE_MINT_FLAG;
            if (parameters.afterMint) hooksParameters |= AFTER_MINT_FLAG;
            if (parameters.beforeBurn) hooksParameters |= BEFORE_BURN_FLAG;
            if (parameters.afterBurn) hooksParameters |= AFTER_BURN_FLAG;
            if (parameters.beforeBatchTransferFrom) hooksParameters |= BEFORE_TRANSFER_FLAG;
            if (parameters.afterBatchTransferFrom) hooksParameters |= AFTER_TRANSFER_FLAG;
        }
    
        /**
         * @dev Helper function to decode the hooks parameters from a single bytes32 value
         * @param hooksParameters The encoded hooks parameters
         * @return parameters The hooks parameters
         */
        function decode(bytes32 hooksParameters) internal pure returns (Parameters memory parameters) {
            parameters.hooks = getHooks(hooksParameters);
    
            parameters.beforeSwap = (hooksParameters & BEFORE_SWAP_FLAG) != 0;
            parameters.afterSwap = (hooksParameters & AFTER_SWAP_FLAG) != 0;
            parameters.beforeFlashLoan = (hooksParameters & BEFORE_FLASH_LOAN_FLAG) != 0;
            parameters.afterFlashLoan = (hooksParameters & AFTER_FLASH_LOAN_FLAG) != 0;
            parameters.beforeMint = (hooksParameters & BEFORE_MINT_FLAG) != 0;
            parameters.afterMint = (hooksParameters & AFTER_MINT_FLAG) != 0;
            parameters.beforeBurn = (hooksParameters & BEFORE_BURN_FLAG) != 0;
            parameters.afterBurn = (hooksParameters & AFTER_BURN_FLAG) != 0;
            parameters.beforeBatchTransferFrom = (hooksParameters & BEFORE_TRANSFER_FLAG) != 0;
            parameters.afterBatchTransferFrom = (hooksParameters & AFTER_TRANSFER_FLAG) != 0;
        }
    
        /**
         * @dev Helper function to get the hooks address from the encoded hooks parameters
         * @param hooksParameters The encoded hooks parameters
         * @return hooks The hooks address
         */
        function getHooks(bytes32 hooksParameters) internal pure returns (address hooks) {
            hooks = address(uint160(uint256(hooksParameters)));
        }
    
        /**
         * @dev Helper function to set the hooks address in the encoded hooks parameters
         * @param hooksParameters The encoded hooks parameters
         * @param newHooks The new hooks address
         * @return hooksParameters The updated hooks parameters
         */
        function setHooks(bytes32 hooksParameters, address newHooks) internal pure returns (bytes32) {
            return bytes32(bytes12(hooksParameters)) | bytes32(uint256(uint160(newHooks)));
        }
    
        /**
         * @dev Helper function to get the flags from the encoded hooks parameters
         * @param hooksParameters The encoded hooks parameters
         * @return flags The flags
         */
        function getFlags(bytes32 hooksParameters) internal pure returns (bytes12 flags) {
            flags = bytes12(hooksParameters);
        }
    
        /**
         * @dev Helper function call the onHooksSet function on the hooks contract, only if the
         * hooksParameters is not 0
         * @param hooksParameters The encoded hooks parameters
         * @param onHooksSetData The data to pass to the onHooksSet function
         */
        function onHooksSet(bytes32 hooksParameters, bytes calldata onHooksSetData) internal {
            if (hooksParameters != 0) {
                _safeCall(
                    hooksParameters, abi.encodeWithSelector(ILBHooks.onHooksSet.selector, hooksParameters, onHooksSetData)
                );
            }
        }
    
        /**
         * @dev Helper function to call the beforeSwap function on the hooks contract, only if the
         * BEFORE_SWAP_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param to The recipient
         * @param swapForY Whether the swap is for Y
         * @param amountsIn The amounts in
         */
        function beforeSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsIn)
            internal
        {
            if ((hooksParameters & BEFORE_SWAP_FLAG) != 0) {
                _safeCall(
                    hooksParameters, abi.encodeWithSelector(ILBHooks.beforeSwap.selector, sender, to, swapForY, amountsIn)
                );
            }
        }
    
        /**
         * @dev Helper function to call the afterSwap function on the hooks contract, only if the
         * AFTER_SWAP_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param to The recipient
         * @param swapForY Whether the swap is for Y
         * @param amountsOut The amounts out
         */
        function afterSwap(bytes32 hooksParameters, address sender, address to, bool swapForY, bytes32 amountsOut)
            internal
        {
            if ((hooksParameters & AFTER_SWAP_FLAG) != 0) {
                _safeCall(
                    hooksParameters, abi.encodeWithSelector(ILBHooks.afterSwap.selector, sender, to, swapForY, amountsOut)
                );
            }
        }
    
        /**
         * @dev Helper function to call the beforeFlashLoan function on the hooks contract, only if the
         * BEFORE_FLASH_LOAN_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param to The recipient
         * @param amounts The amounts
         */
        function beforeFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 amounts) internal {
            if ((hooksParameters & BEFORE_FLASH_LOAN_FLAG) != 0) {
                _safeCall(hooksParameters, abi.encodeWithSelector(ILBHooks.beforeFlashLoan.selector, sender, to, amounts));
            }
        }
    
        /**
         * @dev Helper function to call the afterFlashLoan function on the hooks contract, only if the
         * AFTER_FLASH_LOAN_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param to The recipient
         * @param fees The fees
         * @param feesReceived The fees received
         */
        function afterFlashLoan(bytes32 hooksParameters, address sender, address to, bytes32 fees, bytes32 feesReceived)
            internal
        {
            if ((hooksParameters & AFTER_FLASH_LOAN_FLAG) != 0) {
                _safeCall(
                    hooksParameters,
                    abi.encodeWithSelector(ILBHooks.afterFlashLoan.selector, sender, to, fees, feesReceived)
                );
            }
        }
    
        /**
         * @dev Helper function to call the beforeMint function on the hooks contract, only if the
         * BEFORE_MINT_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param to The recipient
         * @param liquidityConfigs The liquidity configs
         * @param amountsReceived The amounts received
         */
        function beforeMint(
            bytes32 hooksParameters,
            address sender,
            address to,
            bytes32[] calldata liquidityConfigs,
            bytes32 amountsReceived
        ) internal {
            if ((hooksParameters & BEFORE_MINT_FLAG) != 0) {
                _safeCall(
                    hooksParameters,
                    abi.encodeWithSelector(ILBHooks.beforeMint.selector, sender, to, liquidityConfigs, amountsReceived)
                );
            }
        }
    
        /**
         * @dev Helper function to call the afterMint function on the hooks contract, only if the
         * AFTER_MINT_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param to The recipient
         * @param liquidityConfigs The liquidity configs
         * @param amountsIn The amounts in
         */
        function afterMint(
            bytes32 hooksParameters,
            address sender,
            address to,
            bytes32[] calldata liquidityConfigs,
            bytes32 amountsIn
        ) internal {
            if ((hooksParameters & AFTER_MINT_FLAG) != 0) {
                _safeCall(
                    hooksParameters,
                    abi.encodeWithSelector(ILBHooks.afterMint.selector, sender, to, liquidityConfigs, amountsIn)
                );
            }
        }
    
        /**
         * @dev Helper function to call the beforeBurn function on the hooks contract, only if the
         * BEFORE_BURN_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param from The sender
         * @param to The recipient
         * @param ids The ids
         * @param amountsToBurn The amounts to burn
         */
        function beforeBurn(
            bytes32 hooksParameters,
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amountsToBurn
        ) internal {
            if ((hooksParameters & BEFORE_BURN_FLAG) != 0) {
                _safeCall(
                    hooksParameters,
                    abi.encodeWithSelector(ILBHooks.beforeBurn.selector, sender, from, to, ids, amountsToBurn)
                );
            }
        }
    
        /**
         * @dev Helper function to call the afterBurn function on the hooks contract, only if the
         * AFTER_BURN_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param from The sender
         * @param to The recipient
         * @param ids The ids
         * @param amountsToBurn The amounts to burn
         */
        function afterBurn(
            bytes32 hooksParameters,
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amountsToBurn
        ) internal {
            if ((hooksParameters & AFTER_BURN_FLAG) != 0) {
                _safeCall(
                    hooksParameters,
                    abi.encodeWithSelector(ILBHooks.afterBurn.selector, sender, from, to, ids, amountsToBurn)
                );
            }
        }
    
        /**
         * @dev Helper function to call the beforeTransferFrom function on the hooks contract, only if the
         * BEFORE_TRANSFER_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param from The sender
         * @param to The recipient
         * @param ids The list of ids
         * @param amounts The list of amounts
         */
        function beforeBatchTransferFrom(
            bytes32 hooksParameters,
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts
        ) internal {
            if ((hooksParameters & BEFORE_TRANSFER_FLAG) != 0) {
                _safeCall(
                    hooksParameters,
                    abi.encodeWithSelector(ILBHooks.beforeBatchTransferFrom.selector, sender, from, to, ids, amounts)
                );
            }
        }
    
        /**
         * @dev Helper function to call the afterTransferFrom function on the hooks contract, only if the
         * AFTER_TRANSFER_FLAG is set in the hooksParameters
         * @param hooksParameters The encoded hooks parameters
         * @param sender The sender
         * @param from The sender
         * @param to The recipient
         * @param ids The list of ids
         * @param amounts The list of amounts
         */
        function afterBatchTransferFrom(
            bytes32 hooksParameters,
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts
        ) internal {
            if ((hooksParameters & AFTER_TRANSFER_FLAG) != 0) {
                _safeCall(
                    hooksParameters,
                    abi.encodeWithSelector(ILBHooks.afterBatchTransferFrom.selector, sender, from, to, ids, amounts)
                );
            }
        }
    
        /**
         * @dev Helper function to call the hooks contract and verify the call was successful
         * by matching the expected selector with the returned data
         * @param hooksParameters The encoded hooks parameters
         * @param data The data to pass to the hooks contract
         */
        function _safeCall(bytes32 hooksParameters, bytes memory data) private {
            bool success;
    
            address hooks = getHooks(hooksParameters);
    
            assembly {
                let expectedSelector := shr(224, mload(add(data, 0x20)))
    
                success := call(gas(), hooks, 0, add(data, 0x20), mload(data), 0, 0x20)
    
                if and(iszero(success), iszero(iszero(returndatasize()))) {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }
    
                success := and(success, and(gt(returndatasize(), 0x1f), eq(shr(224, mload(0)), expectedSelector)))
            }
    
            if (!success) revert Hooks__CallFailed();
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    
    import {ILBHooks} from "./ILBHooks.sol";
    import {ILBPair} from "./ILBPair.sol";
    
    /**
     * @title Liquidity Book Factory Interface
     * @author Trader Joe
     * @notice Required interface of LBFactory contract
     */
    interface ILBFactory {
        error LBFactory__IdenticalAddresses(IERC20 token);
        error LBFactory__QuoteAssetNotWhitelisted(IERC20 quoteAsset);
        error LBFactory__QuoteAssetAlreadyWhitelisted(IERC20 quoteAsset);
        error LBFactory__AddressZero();
        error LBFactory__LBPairAlreadyExists(IERC20 tokenX, IERC20 tokenY, uint256 _binStep);
        error LBFactory__LBPairDoesNotExist(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
        error LBFactory__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
        error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees);
        error LBFactory__BinStepTooLow(uint256 binStep);
        error LBFactory__PresetIsLockedForUsers(address user, uint256 binStep);
        error LBFactory__LBPairIgnoredIsAlreadyInTheSameState();
        error LBFactory__BinStepHasNoPreset(uint256 binStep);
        error LBFactory__PresetOpenStateIsAlreadyInTheSameState();
        error LBFactory__SameFeeRecipient(address feeRecipient);
        error LBFactory__SameFlashLoanFee(uint256 flashLoanFee);
        error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation);
        error LBFactory__SameImplementation(address LBPairImplementation);
        error LBFactory__ImplementationNotSet();
        error LBFactory__SameHooksImplementation(address hooksImplementation);
        error LBFactory__SameHooksParameters(bytes32 hooksParameters);
        error LBFactory__InvalidHooksParameters();
        error LBFactory__CannotGrantDefaultAdminRole();
    
        /**
         * @dev Structure to store the LBPair information, such as:
         * binStep: The bin step of the LBPair
         * LBPair: The address of the LBPair
         * createdByOwner: Whether the pair was created by the owner of the factory
         * ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding
         */
        struct LBPairInformation {
            uint16 binStep;
            ILBPair LBPair;
            bool createdByOwner;
            bool ignoredForRouting;
        }
    
        event LBPairCreated(
            IERC20 indexed tokenX, IERC20 indexed tokenY, uint256 indexed binStep, ILBPair LBPair, uint256 pid
        );
    
        event FeeRecipientSet(address oldRecipient, address newRecipient);
    
        event FlashLoanFeeSet(uint256 oldFlashLoanFee, uint256 newFlashLoanFee);
    
        event LBPairImplementationSet(address oldLBPairImplementation, address LBPairImplementation);
    
        event LBPairIgnoredStateChanged(ILBPair indexed LBPair, bool ignored);
    
        event PresetSet(
            uint256 indexed binStep,
            uint256 baseFactor,
            uint256 filterPeriod,
            uint256 decayPeriod,
            uint256 reductionFactor,
            uint256 variableFeeControl,
            uint256 protocolShare,
            uint256 maxVolatilityAccumulator
        );
    
        event PresetOpenStateChanged(uint256 indexed binStep, bool indexed isOpen);
    
        event PresetRemoved(uint256 indexed binStep);
    
        event QuoteAssetAdded(IERC20 indexed quoteAsset);
    
        event QuoteAssetRemoved(IERC20 indexed quoteAsset);
    
        function getMinBinStep() external pure returns (uint256);
    
        function getFeeRecipient() external view returns (address);
    
        function getMaxFlashLoanFee() external pure returns (uint256);
    
        function getFlashLoanFee() external view returns (uint256);
    
        function getLBPairImplementation() external view returns (address);
    
        function getNumberOfLBPairs() external view returns (uint256);
    
        function getLBPairAtIndex(uint256 id) external returns (ILBPair);
    
        function getNumberOfQuoteAssets() external view returns (uint256);
    
        function getQuoteAssetAtIndex(uint256 index) external view returns (IERC20);
    
        function isQuoteAsset(IERC20 token) external view returns (bool);
    
        function getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep)
            external
            view
            returns (LBPairInformation memory);
    
        function getPreset(uint256 binStep)
            external
            view
            returns (
                uint256 baseFactor,
                uint256 filterPeriod,
                uint256 decayPeriod,
                uint256 reductionFactor,
                uint256 variableFeeControl,
                uint256 protocolShare,
                uint256 maxAccumulator,
                bool isOpen
            );
    
        function getAllBinSteps() external view returns (uint256[] memory presetsBinStep);
    
        function getOpenBinSteps() external view returns (uint256[] memory openBinStep);
    
        function getAllLBPairs(IERC20 tokenX, IERC20 tokenY)
            external
            view
            returns (LBPairInformation[] memory LBPairsBinStep);
    
        function setLBPairImplementation(address lbPairImplementation) external;
    
        function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
            external
            returns (ILBPair pair);
    
        function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external;
    
        function setPreset(
            uint16 binStep,
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator,
            bool isOpen
        ) external;
    
        function setPresetOpenState(uint16 binStep, bool isOpen) external;
    
        function removePreset(uint16 binStep) external;
    
        function setFeesParametersOnPair(
            IERC20 tokenX,
            IERC20 tokenY,
            uint16 binStep,
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator
        ) external;
    
        function setLBHooksParametersOnPair(
            IERC20 tokenX,
            IERC20 tokenY,
            uint16 binStep,
            bytes32 hooksParameters,
            bytes memory onHooksSetData
        ) external;
    
        function removeLBHooksOnPair(IERC20 tokenX, IERC20 tokenY, uint16 binStep) external;
    
        function setFeeRecipient(address feeRecipient) external;
    
        function setFlashLoanFee(uint256 flashLoanFee) external;
    
        function addQuoteAsset(IERC20 quoteAsset) external;
    
        function removeQuoteAsset(IERC20 quoteAsset) external;
    
        function forceDecay(ILBPair lbPair) external;
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    
    import {Hooks} from "../libraries/Hooks.sol";
    import {ILBFactory} from "./ILBFactory.sol";
    import {ILBFlashLoanCallback} from "./ILBFlashLoanCallback.sol";
    import {ILBToken} from "./ILBToken.sol";
    
    interface ILBPair is ILBToken {
        error LBPair__ZeroBorrowAmount();
        error LBPair__AddressZero();
        error LBPair__EmptyMarketConfigs();
        error LBPair__FlashLoanCallbackFailed();
        error LBPair__FlashLoanInsufficientAmount();
        error LBPair__InsufficientAmountIn();
        error LBPair__InsufficientAmountOut();
        error LBPair__InvalidInput();
        error LBPair__InvalidStaticFeeParameters();
        error LBPair__OnlyFactory();
        error LBPair__OnlyProtocolFeeRecipient();
        error LBPair__OutOfLiquidity();
        error LBPair__TokenNotSupported();
        error LBPair__ZeroAmount(uint24 id);
        error LBPair__ZeroAmountsOut(uint24 id);
        error LBPair__ZeroShares(uint24 id);
        error LBPair__MaxTotalFeeExceeded();
        error LBPair__InvalidHooks();
    
        struct MintArrays {
            uint256[] ids;
            bytes32[] amounts;
            uint256[] liquidityMinted;
        }
    
        event DepositedToBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);
    
        event WithdrawnFromBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);
    
        event CompositionFees(address indexed sender, uint24 id, bytes32 totalFees, bytes32 protocolFees);
    
        event CollectedProtocolFees(address indexed feeRecipient, bytes32 protocolFees);
    
        event Swap(
            address indexed sender,
            address indexed to,
            uint24 id,
            bytes32 amountsIn,
            bytes32 amountsOut,
            uint24 volatilityAccumulator,
            bytes32 totalFees,
            bytes32 protocolFees
        );
    
        event StaticFeeParametersSet(
            address indexed sender,
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator
        );
    
        event HooksParametersSet(address indexed sender, bytes32 hooksParameters);
    
        event FlashLoan(
            address indexed sender,
            ILBFlashLoanCallback indexed receiver,
            uint24 activeId,
            bytes32 amounts,
            bytes32 totalFees,
            bytes32 protocolFees
        );
    
        event OracleLengthIncreased(address indexed sender, uint16 oracleLength);
    
        event ForcedDecay(address indexed sender, uint24 idReference, uint24 volatilityReference);
    
        function initialize(
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator,
            uint24 activeId
        ) external;
    
        function implementation() external view returns (address);
    
        function getFactory() external view returns (ILBFactory factory);
    
        function getTokenX() external view returns (IERC20 tokenX);
    
        function getTokenY() external view returns (IERC20 tokenY);
    
        function getBinStep() external view returns (uint16 binStep);
    
        function getReserves() external view returns (uint128 reserveX, uint128 reserveY);
    
        function getActiveId() external view returns (uint24 activeId);
    
        function getBin(uint24 id) external view returns (uint128 binReserveX, uint128 binReserveY);
    
        function getNextNonEmptyBin(bool swapForY, uint24 id) external view returns (uint24 nextId);
    
        function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY);
    
        function getStaticFeeParameters()
            external
            view
            returns (
                uint16 baseFactor,
                uint16 filterPeriod,
                uint16 decayPeriod,
                uint16 reductionFactor,
                uint24 variableFeeControl,
                uint16 protocolShare,
                uint24 maxVolatilityAccumulator
            );
    
        function getLBHooksParameters() external view returns (bytes32 hooksParameters);
    
        function getVariableFeeParameters()
            external
            view
            returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate);
    
        function getOracleParameters()
            external
            view
            returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp);
    
        function getOracleSampleAt(uint40 lookupTimestamp)
            external
            view
            returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed);
    
        function getPriceFromId(uint24 id) external view returns (uint256 price);
    
        function getIdFromPrice(uint256 price) external view returns (uint24 id);
    
        function getSwapIn(uint128 amountOut, bool swapForY)
            external
            view
            returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee);
    
        function getSwapOut(uint128 amountIn, bool swapForY)
            external
            view
            returns (uint128 amountInLeft, uint128 amountOut, uint128 fee);
    
        function swap(bool swapForY, address to) external returns (bytes32 amountsOut);
    
        function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external;
    
        function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo)
            external
            returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted);
    
        function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn)
            external
            returns (bytes32[] memory amounts);
    
        function collectProtocolFees() external returns (bytes32 collectedProtocolFees);
    
        function increaseOracleLength(uint16 newLength) external;
    
        function setStaticFeeParameters(
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,
            uint16 protocolShare,
            uint24 maxVolatilityAccumulator
        ) external;
    
        function setHooksParameters(bytes32 hooksParameters, bytes calldata onHooksSetData) external;
    
        function forceDecay() external;
    }

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    
    import {ILBPair} from "./ILBPair.sol";
    
    import {Hooks} from "../libraries/Hooks.sol";
    
    interface ILBHooks {
        function getLBPair() external view returns (ILBPair);
    
        function isLinked() external view returns (bool);
    
        function onHooksSet(bytes32 hooksParameters, bytes calldata onHooksSetData) external returns (bytes4);
    
        function beforeSwap(address sender, address to, bool swapForY, bytes32 amountsIn) external returns (bytes4);
    
        function afterSwap(address sender, address to, bool swapForY, bytes32 amountsOut) external returns (bytes4);
    
        function beforeFlashLoan(address sender, address to, bytes32 amounts) external returns (bytes4);
    
        function afterFlashLoan(address sender, address to, bytes32 fees, bytes32 feesReceived) external returns (bytes4);
    
        function beforeMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsReceived)
            external
            returns (bytes4);
    
        function afterMint(address sender, address to, bytes32[] calldata liquidityConfigs, bytes32 amountsIn)
            external
            returns (bytes4);
    
        function beforeBurn(
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amountsToBurn
        ) external returns (bytes4);
    
        function afterBurn(
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amountsToBurn
        ) external returns (bytes4);
    
        function beforeBatchTransferFrom(
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts
        ) external returns (bytes4);
    
        function afterBatchTransferFrom(
            address sender,
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts
        ) external returns (bytes4);
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev External interface of AccessControl declared to support ERC-165 detection.
     */
    interface IAccessControl {
        /**
         * @dev The `account` is missing a role.
         */
        error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
    
        /**
         * @dev The caller of a function is not the expected one.
         *
         * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
         */
        error AccessControlBadConfirmation();
    
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
    
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
    
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
    
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
    
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
    
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
    
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
    
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `callerConfirmation`.
         */
        function renounceRole(bytes32 role, address callerConfirmation) external;
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC165} from "./IERC165.sol";
    
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }

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

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    /**
     * @title Liquidity Book Constants Library
     * @author Trader Joe
     * @notice Set of constants for Liquidity Book contracts
     */
    library Constants {
        uint8 internal constant SCALE_OFFSET = 128;
        uint256 internal constant SCALE = 1 << SCALE_OFFSET;
    
        uint256 internal constant PRECISION = 1e18;
        uint256 internal constant SQUARED_PRECISION = PRECISION * PRECISION;
    
        uint256 internal constant MAX_FEE = 0.1e18; // 10%
        uint256 internal constant MAX_PROTOCOL_SHARE = 2_500; // 25% of the fee
    
        uint256 internal constant BASIS_POINT_MAX = 10_000;
    
        // (2^256 - 1) / (2 * log(2**128) / log(1.0001))
        uint256 internal constant MAX_LIQUIDITY_PER_BIN =
            65251743116719673010965625540244653191619923014385985379600384103134737;
    
        /// @dev The expected return after a successful flash loan
        bytes32 internal constant CALLBACK_SUCCESS = keccak256("LBPair.onFlashLoan");
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    import {Constants} from "../Constants.sol";
    import {BitMath} from "./BitMath.sol";
    
    /**
     * @title Liquidity Book Uint128x128 Math Library
     * @author Trader Joe
     * @notice Helper contract used for power and log calculations
     */
    library Uint128x128Math {
        using BitMath for uint256;
    
        error Uint128x128Math__LogUnderflow();
        error Uint128x128Math__PowUnderflow(uint256 x, int256 y);
    
        uint256 constant LOG_SCALE_OFFSET = 127;
        uint256 constant LOG_SCALE = 1 << LOG_SCALE_OFFSET;
        uint256 constant LOG_SCALE_SQUARED = LOG_SCALE * LOG_SCALE;
    
        /**
         * @notice Calculates the binary logarithm of x.
         * @dev Based on the iterative approximation algorithm.
         * https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
         * Requirements:
         * - x must be greater than zero.
         * Caveats:
         * - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation
         * Also because x is converted to an unsigned 129.127-binary fixed-point number during the operation to optimize the multiplication
         * @param x The unsigned 128.128-binary fixed-point number for which to calculate the binary logarithm.
         * @return result The binary logarithm as a signed 128.128-binary fixed-point number.
         */
        function log2(uint256 x) internal pure returns (int256 result) {
            // Convert x to a unsigned 129.127-binary fixed-point number to optimize the multiplication.
            // If we use an offset of 128 bits, y would need 129 bits and y**2 would would overflow and we would have to
            // use mulDiv, by reducing x to 129.127-binary fixed-point number we assert that y will use 128 bits, and we
            // can use the regular multiplication
    
            if (x == 1) return -128;
            if (x == 0) revert Uint128x128Math__LogUnderflow();
    
            x >>= 1;
    
            unchecked {
                // This works because log2(x) = -log2(1/x).
                int256 sign;
                if (x >= LOG_SCALE) {
                    sign = 1;
                } else {
                    sign = -1;
                    // Do the fixed-point inversion inline to save gas
                    x = LOG_SCALE_SQUARED / x;
                }
    
                // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
                uint256 n = (x >> LOG_SCALE_OFFSET).mostSignificantBit();
    
                // The integer part of the logarithm as a signed 129.127-binary fixed-point number. The operation can't overflow
                // because n is maximum 255, LOG_SCALE_OFFSET is 127 bits and sign is either 1 or -1.
                result = int256(n) << LOG_SCALE_OFFSET;
    
                // This is y = x * 2^(-n).
                uint256 y = x >> n;
    
                // If y = 1, the fractional part is zero.
                if (y != LOG_SCALE) {
                    // Calculate the fractional part via the iterative approximation.
                    // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
                    for (int256 delta = int256(1 << (LOG_SCALE_OFFSET - 1)); delta > 0; delta >>= 1) {
                        y = (y * y) >> LOG_SCALE_OFFSET;
    
                        // Is y^2 > 2 and so in the range [2,4)?
                        if (y >= 1 << (LOG_SCALE_OFFSET + 1)) {
                            // Add the 2^(-m) factor to the logarithm.
                            result += delta;
    
                            // Corresponds to z/2 on Wikipedia.
                            y >>= 1;
                        }
                    }
                }
                // Convert x back to unsigned 128.128-binary fixed-point number
                result = (result * sign) << 1;
            }
        }
    
        /**
         * @notice Returns the value of x^y. It calculates `1 / x^abs(y)` if x is bigger than 2^128.
         * At the end of the operations, we invert the result if needed.
         * @param x The unsigned 128.128-binary fixed-point number for which to calculate the power
         * @param y A relative number without any decimals, needs to be between ]-2^21; 2^21[
         */
        function pow(uint256 x, int256 y) internal pure returns (uint256 result) {
            bool invert;
            uint256 absY;
    
            if (y == 0) return Constants.SCALE;
    
            assembly {
                absY := y
                if slt(absY, 0) {
                    absY := sub(0, absY)
                    invert := iszero(invert)
                }
            }
    
            if (absY < 0x100000) {
                result = Constants.SCALE;
                assembly {
                    let squared := x
                    if gt(x, 0xffffffffffffffffffffffffffffffff) {
                        squared := div(not(0), squared)
                        invert := iszero(invert)
                    }
    
                    if and(absY, 0x1) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x2) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x4) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x8) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x10) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x20) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x40) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x80) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x100) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x200) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x400) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x800) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x1000) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x2000) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x4000) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x8000) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x10000) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x20000) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x40000) { result := shr(128, mul(result, squared)) }
                    squared := shr(128, mul(squared, squared))
                    if and(absY, 0x80000) { result := shr(128, mul(result, squared)) }
                }
            }
    
            // revert if y is too big or if x^y underflowed
            if (result == 0) revert Uint128x128Math__PowUnderflow(x, y);
    
            return invert ? type(uint256).max / result : result;
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    import {BitMath} from "./BitMath.sol";
    
    /**
     * @title Liquidity Book Uint256x256 Math Library
     * @author Trader Joe
     * @notice Helper contract used for full precision calculations
     */
    library Uint256x256Math {
        error Uint256x256Math__MulShiftOverflow();
        error Uint256x256Math__MulDivOverflow();
    
        /**
         * @notice Calculates floor(x*y/denominator) with full precision
         * The result will be rounded down
         * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
         * Requirements:
         * - The denominator cannot be zero
         * - The result must fit within uint256
         * Caveats:
         * - This function does not work with fixed-point numbers
         * @param x The multiplicand as an uint256
         * @param y The multiplier as an uint256
         * @param denominator The divisor as an uint256
         * @return result The result as an uint256
         */
        function mulDivRoundDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            (uint256 prod0, uint256 prod1) = _getMulProds(x, y);
    
            return _getEndOfDivRoundDown(x, y, denominator, prod0, prod1);
        }
    
        /**
         * @notice Calculates ceil(x*y/denominator) with full precision
         * The result will be rounded up
         * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
         * Requirements:
         * - The denominator cannot be zero
         * - The result must fit within uint256
         * Caveats:
         * - This function does not work with fixed-point numbers
         * @param x The multiplicand as an uint256
         * @param y The multiplier as an uint256
         * @param denominator The divisor as an uint256
         * @return result The result as an uint256
         */
        function mulDivRoundUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            result = mulDivRoundDown(x, y, denominator);
            if (mulmod(x, y, denominator) != 0) result += 1;
        }
    
        /**
         * @notice Calculates floor(x * y / 2**offset) with full precision
         * The result will be rounded down
         * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
         * Requirements:
         * - The offset needs to be strictly lower than 256
         * - The result must fit within uint256
         * Caveats:
         * - This function does not work with fixed-point numbers
         * @param x The multiplicand as an uint256
         * @param y The multiplier as an uint256
         * @param offset The offset as an uint256, can't be greater than 256
         * @return result The result as an uint256
         */
        function mulShiftRoundDown(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) {
            (uint256 prod0, uint256 prod1) = _getMulProds(x, y);
    
            if (prod0 != 0) result = prod0 >> offset;
            if (prod1 != 0) {
                // Make sure the result is less than 2^256.
                if (prod1 >= 1 << offset) revert Uint256x256Math__MulShiftOverflow();
    
                unchecked {
                    result += prod1 << (256 - offset);
                }
            }
        }
    
        /**
         * @notice Calculates floor(x * y / 2**offset) with full precision
         * The result will be rounded down
         * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
         * Requirements:
         * - The offset needs to be strictly lower than 256
         * - The result must fit within uint256
         * Caveats:
         * - This function does not work with fixed-point numbers
         * @param x The multiplicand as an uint256
         * @param y The multiplier as an uint256
         * @param offset The offset as an uint256, can't be greater than 256
         * @return result The result as an uint256
         */
        function mulShiftRoundUp(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) {
            result = mulShiftRoundDown(x, y, offset);
            if (mulmod(x, y, 1 << offset) != 0) result += 1;
        }
    
        /**
         * @notice Calculates floor(x << offset / y) with full precision
         * The result will be rounded down
         * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
         * Requirements:
         * - The offset needs to be strictly lower than 256
         * - The result must fit within uint256
         * Caveats:
         * - This function does not work with fixed-point numbers
         * @param x The multiplicand as an uint256
         * @param offset The number of bit to shift x as an uint256
         * @param denominator The divisor as an uint256
         * @return result The result as an uint256
         */
        function shiftDivRoundDown(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) {
            uint256 prod0;
            uint256 prod1;
    
            prod0 = x << offset; // Least significant 256 bits of the product
            unchecked {
                prod1 = x >> (256 - offset); // Most significant 256 bits of the product
            }
    
            return _getEndOfDivRoundDown(x, 1 << offset, denominator, prod0, prod1);
        }
    
        /**
         * @notice Calculates ceil(x << offset / y) with full precision
         * The result will be rounded up
         * @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
         * Requirements:
         * - The offset needs to be strictly lower than 256
         * - The result must fit within uint256
         * Caveats:
         * - This function does not work with fixed-point numbers
         * @param x The multiplicand as an uint256
         * @param offset The number of bit to shift x as an uint256
         * @param denominator The divisor as an uint256
         * @return result The result as an uint256
         */
        function shiftDivRoundUp(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) {
            result = shiftDivRoundDown(x, offset, denominator);
            if (mulmod(x, 1 << offset, denominator) != 0) result += 1;
        }
    
        /**
         * @notice Helper function to return the result of `x * y` as 2 uint256
         * @param x The multiplicand as an uint256
         * @param y The multiplier as an uint256
         * @return prod0 The least significant 256 bits of the product
         * @return prod1 The most significant 256 bits of the product
         */
        function _getMulProds(uint256 x, uint256 y) private pure returns (uint256 prod0, uint256 prod1) {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }
        }
    
        /**
         * @notice Helper function to return the result of `x * y / denominator` with full precision
         * @param x The multiplicand as an uint256
         * @param y The multiplier as an uint256
         * @param denominator The divisor as an uint256
         * @param prod0 The least significant 256 bits of the product
         * @param prod1 The most significant 256 bits of the product
         * @return result The result as an uint256
         */
        function _getEndOfDivRoundDown(uint256 x, uint256 y, uint256 denominator, uint256 prod0, uint256 prod1)
            private
            pure
            returns (uint256 result)
        {
            // Handle non-overflow cases, 256 by 256 division
            if (prod1 == 0) {
                unchecked {
                    result = prod0 / denominator;
                }
            } else {
                // Make sure the result is less than 2^256. Also prevents denominator == 0
                if (prod1 >= denominator) revert Uint256x256Math__MulDivOverflow();
    
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
    
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
    
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1
                // See https://cs.stackexchange.com/q/138556/92363
                unchecked {
                    // Does not overflow because the denominator cannot be zero at this stage in the function
                    uint256 lpotdod = denominator & (~denominator + 1);
                    assembly {
                        // Divide denominator by lpotdod.
                        denominator := div(denominator, lpotdod)
    
                        // Divide [prod1 prod0] by lpotdod.
                        prod0 := div(prod0, lpotdod)
    
                        // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one
                        lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
                    }
    
                    // Shift in bits from prod1 into prod0
                    prod0 |= prod1 * lpotdod;
    
                    // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                    // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                    // four bits. That is, denominator * inv = 1 mod 2^4
                    uint256 inverse = (3 * denominator) ^ 2;
    
                    // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                    // in modular arithmetic, doubling the correct bits in each step
                    inverse *= 2 - denominator * inverse; // inverse mod 2^8
                    inverse *= 2 - denominator * inverse; // inverse mod 2^16
                    inverse *= 2 - denominator * inverse; // inverse mod 2^32
                    inverse *= 2 - denominator * inverse; // inverse mod 2^64
                    inverse *= 2 - denominator * inverse; // inverse mod 2^128
                    inverse *= 2 - denominator * inverse; // inverse mod 2^256
    
                    // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                    // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                    // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                    // is no longer required.
                    result = prod0 * inverse;
                }
            }
        }
    
        /**
         * @notice Calculates the square root of x
         * @dev Credit to OpenZeppelin's Math library under MIT license
         */
        function sqrt(uint256 x) internal pure returns (uint256 sqrtX) {
            if (x == 0) return 0;
    
            uint256 msb = BitMath.mostSignificantBit(x);
    
            assembly {
                sqrtX := shl(shr(1, msb), 1)
    
                sqrtX := shr(1, add(sqrtX, div(x, sqrtX)))
                sqrtX := shr(1, add(sqrtX, div(x, sqrtX)))
                sqrtX := shr(1, add(sqrtX, div(x, sqrtX)))
                sqrtX := shr(1, add(sqrtX, div(x, sqrtX)))
                sqrtX := shr(1, add(sqrtX, div(x, sqrtX)))
                sqrtX := shr(1, add(sqrtX, div(x, sqrtX)))
                sqrtX := shr(1, add(sqrtX, div(x, sqrtX)))
    
                x := div(x, sqrtX)
            }
    
            return sqrtX < x ? sqrtX : x;
        }
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    
    /// @title Liquidity Book Flashloan Callback Interface
    /// @author Trader Joe
    /// @notice Required interface to interact with LB flash loans
    interface ILBFlashLoanCallback {
        function LBFlashLoanCallback(
            address sender,
            IERC20 tokenX,
            IERC20 tokenY,
            bytes32 amounts,
            bytes32 totalFees,
            bytes calldata data
        ) external returns (bytes32);
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    /**
     * @title Liquidity Book Token Interface
     * @author Trader Joe
     * @notice Interface to interact with the LBToken.
     */
    interface ILBToken {
        error LBToken__AddressThisOrZero();
        error LBToken__InvalidLength();
        error LBToken__SelfApproval(address owner);
        error LBToken__SpenderNotApproved(address from, address spender);
        error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount);
        error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount);
    
        event TransferBatch(
            address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts
        );
    
        event ApprovalForAll(address indexed account, address indexed sender, bool approved);
    
        function name() external view returns (string memory);
    
        function symbol() external view returns (string memory);
    
        function totalSupply(uint256 id) external view returns (uint256);
    
        function balanceOf(address account, uint256 id) external view returns (uint256);
    
        function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
            external
            view
            returns (uint256[] memory);
    
        function isApprovedForAll(address owner, address spender) external view returns (bool);
    
        function approveForAll(address spender, bool approved) external;
    
        function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) external;
    }

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface of the ERC-165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[ERC].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.10;
    
    /**
     * @title Liquidity Book Bit Math Library
     * @author Trader Joe
     * @notice Helper contract used for bit calculations
     */
    library BitMath {
        /**
         * @dev Returns the index of the closest bit on the right of x that is non null
         * @param x The value as a uint256
         * @param bit The index of the bit to start searching at
         * @return id The index of the closest non null bit on the right of x.
         * If there is no closest bit, it returns max(uint256)
         */
        function closestBitRight(uint256 x, uint8 bit) internal pure returns (uint256 id) {
            unchecked {
                uint256 shift = 255 - bit;
                x <<= shift;
    
                // can't overflow as it's non-zero and we shifted it by `_shift`
                return (x == 0) ? type(uint256).max : mostSignificantBit(x) - shift;
            }
        }
    
        /**
         * @dev Returns the index of the closest bit on the left of x that is non null
         * @param x The value as a uint256
         * @param bit The index of the bit to start searching at
         * @return id The index of the closest non null bit on the left of x.
         * If there is no closest bit, it returns max(uint256)
         */
        function closestBitLeft(uint256 x, uint8 bit) internal pure returns (uint256 id) {
            unchecked {
                x >>= bit;
    
                return (x == 0) ? type(uint256).max : leastSignificantBit(x) + bit;
            }
        }
    
        /**
         * @dev Returns the index of the most significant bit of x
         * This function returns 0 if x is 0
         * @param x The value as a uint256
         * @return msb The index of the most significant bit of x
         */
        function mostSignificantBit(uint256 x) internal pure returns (uint8 msb) {
            assembly {
                if gt(x, 0xffffffffffffffffffffffffffffffff) {
                    x := shr(128, x)
                    msb := 128
                }
                if gt(x, 0xffffffffffffffff) {
                    x := shr(64, x)
                    msb := add(msb, 64)
                }
                if gt(x, 0xffffffff) {
                    x := shr(32, x)
                    msb := add(msb, 32)
                }
                if gt(x, 0xffff) {
                    x := shr(16, x)
                    msb := add(msb, 16)
                }
                if gt(x, 0xff) {
                    x := shr(8, x)
                    msb := add(msb, 8)
                }
                if gt(x, 0xf) {
                    x := shr(4, x)
                    msb := add(msb, 4)
                }
                if gt(x, 0x3) {
                    x := shr(2, x)
                    msb := add(msb, 2)
                }
                if gt(x, 0x1) { msb := add(msb, 1) }
            }
        }
    
        /**
         * @dev Returns the index of the least significant bit of x
         * This function returns 255 if x is 0
         * @param x The value as a uint256
         * @return lsb The index of the least significant bit of x
         */
        function leastSignificantBit(uint256 x) internal pure returns (uint8 lsb) {
            assembly {
                let sx := shl(128, x)
                if iszero(iszero(sx)) {
                    lsb := 128
                    x := sx
                }
                sx := shl(64, x)
                if iszero(iszero(sx)) {
                    x := sx
                    lsb := add(lsb, 64)
                }
                sx := shl(32, x)
                if iszero(iszero(sx)) {
                    x := sx
                    lsb := add(lsb, 32)
                }
                sx := shl(16, x)
                if iszero(iszero(sx)) {
                    x := sx
                    lsb := add(lsb, 16)
                }
                sx := shl(8, x)
                if iszero(iszero(sx)) {
                    x := sx
                    lsb := add(lsb, 8)
                }
                sx := shl(4, x)
                if iszero(iszero(sx)) {
                    x := sx
                    lsb := add(lsb, 4)
                }
                sx := shl(2, x)
                if iszero(iszero(sx)) {
                    x := sx
                    lsb := add(lsb, 2)
                }
                if iszero(iszero(shl(1, x))) { lsb := add(lsb, 1) }
    
                lsb := sub(255, lsb)
            }
        }
    }

    Please enter a contract address above to load the contract details and source code.

    Context size (optional):