false
false
0
The new Blockscout UI is now open source! Learn how to deploy it here

Contract Address Details

0x116Dba5DcE9CcDA828218b7eB46406810632014C

Contract Name
HenjinMultiPositionLiqu..Manager
Creator
0x077675–fb67cc at 0x075b50–80eb30
Balance
0 ETH
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
486329
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
HenjinMultiPositionLiquidityManager




Optimization enabled
true
Compiler version
v0.8.12+commit.f00d7308




Optimization runs
10
EVM Version
london




Verified at
2024-06-07T13:15:23.296242Z

contracts/vault-types/HenjinLiquidityManagers/HenjinMultiPositionLiquidityManager.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.12;

import "./HenjinBaseLiquidityManager.sol";

contract HenjinMultiPositionLiquidityManager is HenjinBaseLiquidityManager {
    using SafeERC20 for IERC20;

    // Types

    /// @dev The vault's position data. At any given moment this represents
    ///      all active positions inside the pool.
    ///      Each lowerTick is the lower bound of the position at that index.
    ///      Each upperTick is the upper bound of the position at that index.
    ///      Each relativeWeight is the relative weight of the position at that index,
    ///      relative to the other positions.
    ///      So for example if LiquidityPositions is
    ///        {
    ///            lowerTicks: [0, 20, 40],
    ///            upperTicks: [10, 30, 50],
    ///            relativeWeights: [1, 2, 3]
    ///        }
    ///        then that means the vault has 3 positions:
    ///            1. 0-10 with relative weight 1
    ///            2. 20-30 with relative weight 2
    ///            3. 40-50 with relative weight 3
    struct LiquidityPositions {
        int24 lowerTick;
        int24 upperTick;
        uint16 relativeWeight;
    }

    struct TendLiquidityPositions {
        int24[] lowerTick;
        int24[] upperTick;
        uint16[] relativeWeight;
    }
    // Storage
    LiquidityPositions[] internal positions;

    // External Functions

    /// @dev Get current positions held by the vault
    /// This function is used for mainly read calls so looping shouldn't be a problem
    function getPositions()
        external
        view
        returns (int24[] memory, int24[] memory, uint16[] memory)
    {
        uint256 length = positions.length;
        TendLiquidityPositions memory oldPositions;

        // Initialize the dynamic arrays with the correct length
        oldPositions.lowerTick = new int24[](length);
        oldPositions.upperTick = new int24[](length);
        oldPositions.relativeWeight = new uint16[](length);

        for (uint256 i; i != length; ++i) {
            oldPositions.lowerTick[i] = positions[i].lowerTick;
            oldPositions.upperTick[i] = positions[i].upperTick;
            oldPositions.relativeWeight[i] = positions[i].relativeWeight;
        }

        return (
            oldPositions.lowerTick,
            oldPositions.upperTick,
            oldPositions.relativeWeight
        );
    }

    /// @dev Internal function to pull funds from pool.
    ///      Update positions if necessary, then deposit funds into pool.
    ///      Reverts if the vault does not own any liquidity or tokens.
    ///      newPositions requirements:
    ///        Each lowerTick must be lower than its corresponding upperTick
    ///        Each lowerTick must be greater than or equal to the tick min (-887272)
    ///        Each upperTick must be less than or equal to the tick max (887272)
    ///        All lowerTicks and upperTicks must be divisible by the pool tickSpacing--
    ///      A 0.05% fee pool has tick spacing of 10, 0.3% has tick spacing 60.
    ///      And 1% has tick spacing 200.
    /// @param totalWeight The share of liquidity we want deposited, multiplied by 10,000.
    ///           A value of 10,000 means we want to deposit all tokens into Algebra.
    ///           A value of 0 means we just want all the liquidity out.
    ///           Note that values above 10,000 are not explicitly prohibited
    ///           but will generally cause the tend to fail.
    /// @param newPositions The info of each new position to be set.
    ///           newPositions.lowerTick[] is an array, in order, of the lower ticks of each position
    ///           newPositions.upperTick[] is an array, in order, of the upper ticks of each position
    ///           newPositions.relativeWeight[] is an array, in order, of the relative weights of each position
    ///           So if for example newPositions is called with lowerTick = [-120, 0],
    ///           upperTick = [-60, 60], and relativeWeight = [1, 5],
    ///           then the positions would be -120 to -60 with a weight of 1,
    ///           and 0 to 60 with a weight of 5.
    ///           The weight differences roughly correspond to what
    ///           proportion of the liquidity is added to each position.
    /// @param timeSensitiveData Encoded info of the swapAmount and sqrtPriceLimitX96.
    ///           It must be encoded as bytes so that it the data is placed after
    ///           the newPositions, which is also a dynamic data type.
    ///        timeSensitiveData.swapAmount: the amount to be swapped from one token to another this tend. zeroForOne if positive, oneForZero if negative.
    ///        timeSensitiveData.sqrtPriceLimitX96: the slippage limit of the swap. Protections elsewhere prevent extreme slippage if the keeper calling this
    ///        function is malicious, but this is the first line of defense against MEV attacks.
    struct TendCache {
        int256 swapAmount;
        uint160 sqrtPriceLimitX96;
        uint160 sqrtPriceX96;
        int24 currentTick;
        uint256 newPosLength;
        uint256 balance0;
        uint256 balance1;
        bool zeroForOne;
    }

    function tend(
        uint256 totalWeight,
        TendLiquidityPositions memory newPositions,
        bytes calldata timeSensitiveData
    ) external onlyRole(MANAGER_ROLE) whenNotPaused {
        TendCache memory tendCache;
        require(totalWeight <= TOTAL_WEIGHT_MAX);
        (tendCache.swapAmount, tendCache.sqrtPriceLimitX96) = abi.decode(
            timeSensitiveData,
            (int256, uint160)
        );

        // Get current pool state
        (tendCache.sqrtPriceX96, tendCache.currentTick, , , , ) = pool
            .globalState();

        // currentTick must be close enough to TWAP tick to avoid MEV exploit
        // This is essentially a way to prevent a flashloan attack
        // even if sqrtPriceLimit is set incorrectly.
        _checkVolatility(tendCache.currentTick);

        // Withdraw liquidity from Algebra pool by passing in 1 and 1
        // (indicating we're withdrawing 100% of liquidity)
        _burnAndCollect(1, 1);

        // Update positions if desired. If newPositions is empty,
        // we'll just continue with the old positions instead.
        tendCache.newPosLength = newPositions.lowerTick.length;
        if (tendCache.newPosLength > 0) {
            delete positions;
            for (uint256 i = 0; i < tendCache.newPosLength; i++) {
                LiquidityPositions memory newPosition = LiquidityPositions({
                    lowerTick: newPositions.lowerTick[i],
                    upperTick: newPositions.upperTick[i],
                    relativeWeight: newPositions.relativeWeight[i]
                });

                positions.push(newPosition);
            }
        }

        // Perform a swap if desired.
        if (tendCache.swapAmount != 0) {
            tendCache.zeroForOne = tendCache.swapAmount > 0;
            require(tendCache.swapAmount != type(int256).min);
            swapCallBackProtection = true;
            pool.swap(
                address(this),
                tendCache.zeroForOne,
                tendCache.zeroForOne
                    ? tendCache.swapAmount
                    : -tendCache.swapAmount,
                tendCache.sqrtPriceLimitX96,
                ""
            );

            // Update sqrtPriceX96; it will have moved due to the swap
            (tendCache.sqrtPriceX96, , , , , ) = pool.globalState();
        }

        tendCache.balance0 = _getBalance0();
        tendCache.balance1 = _getBalance1();

        emit Snapshot(
            tendCache.sqrtPriceX96,
            tendCache.balance0,
            tendCache.balance1,
            totalSupply()
        );

        // Create new positions in Algebra
        if (totalWeight > 0) {
            _setBins(
                tendCache.sqrtPriceX96,
                // balance0 adjusted by totalWeight
                FullMath.mulDiv(tendCache.balance0, totalWeight, DIVISOR),
                // balance1 adjusted by totalWeight
                FullMath.mulDiv(tendCache.balance1, totalWeight, DIVISOR),
                tendCache.swapAmount
            );
        }
    }

    // Public Functions

    /// @dev burns each vault position which contains liquidity, updating fees owed to that position.
    ///      Call this before calling getTotalAmounts if total amounts must include fees.
    ///      There's a function in the periphery to do so through a static call.
    function poke() public override {
        LiquidityPositions[] memory _positions = positions;
        uint256 positionCount = _positions.length;
        for (uint256 i; i != positionCount; ++i) {
            // Get position liquidity so that we can ignore this position if it has 0 liquidity.
            (uint256 positionLiquidity, , , , ) = _position(
                _positions[i].lowerTick,
                _positions[i].upperTick
            );

            // If position has liquidity, update fees owed.
            if (positionLiquidity > 0) {
                pool.burn(
                    _positions[i].lowerTick,
                    _positions[i].upperTick,
                    0,
                    ""
                );
            }
        }
    }

    /// @dev Calculates the vault's total holdings of token0 and token1.
    ///      in other words, how much of each token the vault would hold if it withdrew
    ///      all its liquidity from Algebra.
    ///      This function DOES NOT include fees earned since the last burn.
    ///      To include fees, first poke() and then call getTotalAmounts.
    ///      There's a function inside the periphery to do so.
    function getTotalAmounts()
        public
        view
        override
        returns (uint256 total0, uint256 total1)
    {
        // Start with tokens currently held inside the vault
        total0 = _getBalance0();
        total1 = _getBalance1();

        LiquidityPositions[] memory _positions = positions;
        (uint160 sqrtPriceX96, , , , , ) = pool.globalState();

        uint256 totalFees = IFeeManager(feeManager).vaultTotalFees(
            address(this)
        );
        uint256 feeSubtract = FEE_DIVISOR - totalFees;
        uint256 positionCount = _positions.length;
        for (uint256 i; i != positionCount; ++i) {
            (uint256 liquidity, , , uint256 fees0, uint256 fees1) = _position(
                _positions[i].lowerTick,
                _positions[i].upperTick
            );

            (uint256 amt0, uint256 amt1) = LiquidityAmounts
                .getAmountsForLiquidity(
                    sqrtPriceX96,
                    TickMath.getSqrtRatioAtTick(_positions[i].lowerTick),
                    TickMath.getSqrtRatioAtTick(_positions[i].upperTick),
                    uint128(liquidity)
                );

            // Directly adding to totals, removing intermediate total fee variables
            total0 += amt0 + FullMath.mulDiv(fees0, feeSubtract, FEE_DIVISOR);
            total1 += amt1 + FullMath.mulDiv(fees1, feeSubtract, FEE_DIVISOR);
        }
    }

    // Internal Functions

    /// @dev Given desired positions, desired relative weights, and a current token amount,
    ///      This function deposits as much liquidity as possible into each position
    ///      while respecting relative weights.
    /// @param sqrtPriceX96 The current sqrtPriceX96 of the pool
    /// @param t0ToDeposit The vault's current balance of token0 ready to be deposited
    ///                 (excluding steer and strategist fees)
    /// @param t1ToDeposit The vault's current balance of token1 ready to be deposited
    ///                 (excluding steer and strategist fees)
    /// @param swapAmount The amount to be swapped from one token to another this tend.
    ///                   zeroForOne if positive, oneForZero if negative.
    ///                   Here it is mainly used to determine which direction the swap was,
    ///                   so that we can check whether the swap was too large.
    function _setBins(
        uint160 sqrtPriceX96,
        uint256 t0ToDeposit,
        uint256 t1ToDeposit,
        int256 swapAmount
    ) internal {
        //@todo check the updated code
        LiquidityPositions[] memory _positions = positions;

        // Get relative amounts of t0 and t1 for each position.
        ///    Temporary array built to hold the weights of each token in each liquidity position.
        ///      t0Weights[0] = Token 0 weight in the first liquidity position, multiplied by PRECISION.
        ///      t1 weight in that position can be calculated using PRECISION * total bin weight - t0 weight.
        uint256[] memory positionT0Requested;
        uint256[] memory positionT1Requested;
        uint256 totalT0Requested;
        uint256 totalT1Requested;
        uint256 positionCount = _positions.length;
        positionT0Requested = new uint256[](positionCount);
        positionT1Requested = new uint256[](positionCount);

        // For each bin, figure out how much of the bin will be in token0,
        // and how much will be in token1.
        // Set weights accordingly--if a bin's weight is 10, and nine tenths of its value
        // will be in token0, then its token0 weight will be 9 and its token1 weight will be 1.
        for (uint256 i; i != positionCount; ++i) {
            // For each position, find amount0Wanted and amount1Wanted
            // given a liquidity of PRECISION * relativeWeight.
            if (i >= 1) {
                require(
                    _positions[i - 1].lowerTick < _positions[i].lowerTick &&
                        _positions[i - 1].upperTick < _positions[i].upperTick
                );
            }
            (uint256 amount0Wanted, uint256 amount1Wanted) = LiquidityAmounts
                .getAmountsForLiquidity(
                    sqrtPriceX96,
                    TickMath.getSqrtRatioAtTick(_positions[i].lowerTick),
                    TickMath.getSqrtRatioAtTick(_positions[i].upperTick),
                    uint128(PRECISION * _positions[i].relativeWeight)
                    // No safecast here--an overflow will lead to an incorrect number,
                    // which will either (usually) revert, or cause a harmless liquidity miscalculation.
                );
            // Record amt0Delta and amt1Delta for this position
            positionT0Requested[i] = amount0Wanted;
            positionT1Requested[i] = amount1Wanted;

            // Add amt0Delta and amt1Delta to totalT0Requested and totalT1Requested
            totalT0Requested += amount0Wanted;
            totalT1Requested += amount1Wanted;
        }

        // Now add liquidity to those bins based on their weights vs total token weights.
        // If relativeWeights have a bad input (such as a weight of 0, or a very high weight,
        // in one of the positions) the below code will revert in some cases, proceed in others.
        // The result will not be correct but all that a bad input can do
        // is cause a revert or cause less than 100% of liquidity to be deployed.
        for (uint256 i; i != positionCount; ++i) {
            // Liquidity to deposit for this position is calculated using _liquidityForAmounts
            // and the calculated tokens to deposit for the position.
            // Set token amounts for this position
            uint256 positionT0Amount = totalT0Requested > 0
                ? FullMath.mulDiv(
                    positionT0Requested[i],
                    t0ToDeposit,
                    totalT0Requested
                )
                : 0;
            uint256 positionT1Amount = totalT1Requested > 0
                ? FullMath.mulDiv(
                    positionT1Requested[i],
                    t1ToDeposit,
                    totalT1Requested
                )
                : 0;

            uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts(
                sqrtPriceX96,
                TickMath.getSqrtRatioAtTick(_positions[i].lowerTick),
                TickMath.getSqrtRatioAtTick(_positions[i].upperTick),
                positionT0Amount,
                positionT1Amount
            );

            // Create the position inside the pool.
            if (liquidity > 0) {
                mintCallBackProtection = true;
                pool.mint(
                    address(this),
                    address(this),
                    _positions[i].lowerTick,
                    _positions[i].upperTick,
                    liquidity,
                    ""
                );
            }
        }

        // Check post-mint balances.
        // We need to check that less than 5% of the TO token (i.e. the token we swapped into) remains post-mint.
        // Having the right liquidity ratio is extremely valuable,
        // but the main thing we're protecting against here is dynamic data that swaps more than it should.

        // As an example, assume a malicious keeper has flashloaned the Algebra pool
        // into a very bad exchange rate before handling the swap.
        // If exchange rate is extremely high token1PerToken0, exploiter will want to swap from token1 to token0
        // (going the other way just helps the liquidity manager)
        // But the positions will be entirely in token1.
        // The check below ensures that no more than 5% of the total contract token0 remains undeposited,
        // a reliable indicator that the correct amount of token1 was swapped.

        // This combined with the TWAP check makes flashloan exploits extremely difficult for a single keeper.

        // If swapAmount > 0, that means zeroForOne. Otherwise, oneForZero.
        // No overflow checks here because some kind of overflow exploit is
        // both implausible and would just cause a revert.
        if (swapAmount > 0) {
            // Require that at least 95% of t1 has been deposited
            // (ensuring that swap amount wasn't too great)
            require(_getBalance1() < (t1ToDeposit * FIVE) / DIVISOR100, "S");
        } else if (swapAmount < 0) {
            // Require that at least 95% of t0 has been deposited
            // (ensuring that swap amount wasn't too great)
            require(_getBalance0() < (t0ToDeposit * FIVE) / DIVISOR100, "S");
        }
    }

    /// @dev Withdraws liquidity from all positions, allocating fees correctly in the process.
    /// @param shares LP shares being withdrawn
    /// @param totalShares total # of LP tokens in the vault
    /// @return t0 Token0 earned from burned liquidity + fees.
    ///            Only includes burned + fees corresponding to LP shares being withdrawn (100% if tend)
    /// @return t1 Token1 earned from burned liquidity + fees
    function _burnAndCollect(
        uint256 shares,
        uint256 totalShares
    ) internal override returns (uint256 t0, uint256 t1) {
        // First, fetch current positions, Only tend() and withdraw() call this function,
        // and neither uses these positions elsewhere (tend uses updated ones).
        LiquidityPositions[] memory _positions = positions;

        // For each position, burn() and then withdraw correct amount of liquidity.
        uint256 fees0;
        uint256 fees1;
        uint256 positionCount = _positions.length;
        for (uint256 i; i != positionCount; ++i) {
            int24 lowerTick = _positions[i].lowerTick;
            int24 upperTick = _positions[i].upperTick;

            uint128 liquidityToBurn;

            // Get position liquidity. If we don't want all of it,
            // here is where we specify how much we want
            // (liquidity * fraction of pool which is being withdrawn)
            // Slightly tortured logic here due to stack management
            {
                (uint256 totalPositionLiquidity, , , , ) = _position(
                    lowerTick,
                    upperTick
                );

                liquidityToBurn = uint128(
                    FullMath.mulDiv(
                        totalPositionLiquidity,
                        shares,
                        totalShares
                    )
                );
            }

            // amountsOwed are always pulled with liquidity in this contract,
            // so if position liquidity is 0, no need to withdraw anything.
            if (liquidityToBurn > 0) {
                // Amount burned in each position.
                // Corresponds to amount of liquidity withdrawn (i.e. doesn't include fees).
                (uint256 posBurned0, uint256 posBurned1) = pool.burn(
                    lowerTick,
                    upperTick,
                    liquidityToBurn,
                    ""
                );

                // Collect all owed tokens including earned fees
                (uint256 collect0, uint256 collect1) = pool.collect(
                    address(this),
                    lowerTick,
                    upperTick,
                    type(uint128).max,
                    type(uint128).max
                );

                /*
                 * Add liquidity burned to t0 and t1--this is already proportional to amt being withdrawn
                 * No need to check for overflow--values come from Algebra, and a total burn greater than 2^256 - 1 would represent burning more than a token's total supply.
                 * Technically possible given a malicious token, but a malicious token can already steal vault holdings due to the nature of Algebra
                    (just have the vault deposit all tokens, then mint an arbitrary amount of the malicious token and swap for the real token)
                */
                t0 += posBurned0;
                t1 += posBurned1;

                // Fees earned by liquidity inside Algebra = collected - burned.
                // First we allocate some to steer and some to strategist.
                // The remainder is the fees earned by LPs.
                // So after that we add remainder * shares / totalShares,
                // and that gives us t0 and t1 allocated to whatever's being withdrawn.

                // Since burned will never be greater than collected, no need to check for underflow here.
                // Since collect values were originally uint128's, no need to check for overflow either. It would take ~2^128 max additions to overflow.
                fees0 += collect0 - posBurned0;
                fees1 += collect1 - posBurned1;
            }
        }
        // Emit fee info
        emit FeesEarned(fees0, fees1);
        IFeeManager.Fee[] memory fees = IFeeManager(feeManager).getFees(
            address(this)
        );
        uint256 totalFees = IFeeManager(feeManager).vaultTotalFees(
            address(this)
        );
        uint256 totalCut0;
        uint256 totalCut1;
        if (fees0 > 0)
            totalCut0 = FullMath.mulDiv(fees0, totalFees, FEE_DIVISOR);
        if (fees1 > 0)
            totalCut1 = FullMath.mulDiv(fees1, totalFees, FEE_DIVISOR);
        totalFees0 += totalCut0; //Add to total Fees
        totalFees1 += totalCut1; //Add to total Fees
        // Subtract fees going to strategist/steer from fees going to vault
        fees0 -= totalCut0;
        // Subtract fees going to strategist/steer from fees going to vault
        fees1 -= totalCut1;
        for (uint256 j; j != fees.length; ++j) {
            // Increase fees
            accruedFees0[fees[j].feeIdentifier] =
                accruedFees0[fees[j].feeIdentifier] +
                FullMath.mulDiv(totalCut0, fees[j].feeValue, FEE_DIVISOR);

            // Increase fees
            accruedFees1[fees[j].feeIdentifier] =
                accruedFees1[fees[j].feeIdentifier] +
                FullMath.mulDiv(totalCut1, fees[j].feeValue, FEE_DIVISOR);
        }
        // Add fees earned by burned position to burned amount
        t0 += FullMath.mulDiv(fees0, shares, totalShares);
        t1 += FullMath.mulDiv(fees1, shares, totalShares);
    }
}
        

@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolState.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that can change
/// @dev Important security note: when using this data by external contracts, it is necessary to take into account the possibility
/// of manipulation (including read-only reentrancy).
/// This interface is based on the UniswapV3 interface, credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolState {
  /// @notice Safely get most important state values of Algebra Integral AMM
  /// @dev Several values exposed as a single method to save gas when accessed externally.
  /// **Important security note: this method checks reentrancy lock and should be preferred in most cases**.
  /// @return sqrtPrice The current price of the pool as a sqrt(dToken1/dToken0) Q64.96 value
  /// @return tick The current global tick of the pool. May not always be equal to SqrtTickMath.getTickAtSqrtRatio(price) if the price is on a tick boundary
  /// @return lastFee The current (last known) pool fee value in hundredths of a bip, i.e. 1e-6 (so '100' is '0.01%'). May be obsolete if using dynamic fee plugin
  /// @return pluginConfig The current plugin config as bitmap. Each bit is responsible for enabling/disabling the hooks, the last bit turns on/off dynamic fees logic
  /// @return activeLiquidity  The currently in-range liquidity available to the pool
  /// @return nextTick The next initialized tick after current global tick
  /// @return previousTick The previous initialized tick before (or at) current global tick
  function safelyGetStateOfAMM()
    external
    view
    returns (uint160 sqrtPrice, int24 tick, uint16 lastFee, uint8 pluginConfig, uint128 activeLiquidity, int24 nextTick, int24 previousTick);

  /// @notice Allows to easily get current reentrancy lock status
  /// @dev can be used to prevent read-only reentrancy.
  /// This method just returns `globalState.unlocked` value
  /// @return unlocked Reentrancy lock flag, true if the pool currently is unlocked, otherwise - false
  function isUnlocked() external view returns (bool unlocked);

  // ! IMPORTANT security note: the pool state can be manipulated.
  // ! The following methods do not check reentrancy lock themselves.

  /// @notice The globalState structure in the pool stores many values but requires only one slot
  /// and is exposed as a single method to save gas when accessed externally.
  /// @dev **important security note: caller should check `unlocked` flag to prevent read-only reentrancy**
  /// @return price The current price of the pool as a sqrt(dToken1/dToken0) Q64.96 value
  /// @return tick The current tick of the pool, i.e. according to the last tick transition that was run
  /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(price) if the price is on a tick boundary
  /// @return lastFee The current (last known) pool fee value in hundredths of a bip, i.e. 1e-6 (so '100' is '0.01%'). May be obsolete if using dynamic fee plugin
  /// @return pluginConfig The current plugin config as bitmap. Each bit is responsible for enabling/disabling the hooks, the last bit turns on/off dynamic fees logic
  /// @return communityFee The community fee represented as a percent of all collected fee in thousandths, i.e. 1e-3 (so 100 is 10%)
  /// @return unlocked Reentrancy lock flag, true if the pool currently is unlocked, otherwise - false
  function globalState() external view returns (uint160 price, int24 tick, uint16 lastFee, uint8 pluginConfig, uint16 communityFee, bool unlocked);

  /// @notice Look up information about a specific tick in the pool
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @param tick The tick to look up
  /// @return liquidityTotal The total amount of position liquidity that uses the pool either as tick lower or tick upper
  /// @return liquidityDelta How much liquidity changes when the pool price crosses the tick
  /// @return prevTick The previous tick in tick list
  /// @return nextTick The next tick in tick list
  /// @return outerFeeGrowth0Token The fee growth on the other side of the tick from the current tick in token0
  /// @return outerFeeGrowth1Token The fee growth on the other side of the tick from the current tick in token1
  /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
  /// a specific position.
  function ticks(
    int24 tick
  )
    external
    view
    returns (
      uint256 liquidityTotal,
      int128 liquidityDelta,
      int24 prevTick,
      int24 nextTick,
      uint256 outerFeeGrowth0Token,
      uint256 outerFeeGrowth1Token
    );

  /// @notice The timestamp of the last sending of tokens to community vault
  /// @return The timestamp truncated to 32 bits
  function communityFeeLastTimestamp() external view returns (uint32);

  /// @notice The amounts of token0 and token1 that will be sent to the vault
  /// @dev Will be sent COMMUNITY_FEE_TRANSFER_FREQUENCY after communityFeeLastTimestamp
  /// @return communityFeePending0 The amount of token0 that will be sent to the vault
  /// @return communityFeePending1 The amount of token1 that will be sent to the vault
  function getCommunityFeePending() external view returns (uint128 communityFeePending0, uint128 communityFeePending1);

  /// @notice Returns the address of currently used plugin
  /// @dev The plugin is subject to change
  /// @return pluginAddress The address of currently used plugin
  function plugin() external view returns (address pluginAddress);

  /// @notice Returns 256 packed tick initialized boolean values. See TickTree for more information
  /// @param wordPosition Index of 256-bits word with ticks
  /// @return The 256-bits word with packed ticks info
  function tickTable(int16 wordPosition) external view returns (uint256);

  /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  /// @return The fee growth accumulator for token0
  function totalFeeGrowth0Token() external view returns (uint256);

  /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  /// @return The fee growth accumulator for token1
  function totalFeeGrowth1Token() external view returns (uint256);

  /// @notice The current pool fee value
  /// @dev In case dynamic fee is enabled in the pool, this method will call the plugin to get the current fee.
  /// If the plugin implements complex fee logic, this method may return an incorrect value or revert.
  /// In this case, see the plugin implementation and related documentation.
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return currentFee The current pool fee value in hundredths of a bip, i.e. 1e-6
  function fee() external view returns (uint16 currentFee);

  /// @notice The tracked token0 and token1 reserves of pool
  /// @dev If at any time the real balance is larger, the excess will be transferred to liquidity providers as additional fee.
  /// If the balance exceeds uint128, the excess will be sent to the communityVault.
  /// @return reserve0 The last known reserve of token0
  /// @return reserve1 The last known reserve of token1
  function getReserves() external view returns (uint128 reserve0, uint128 reserve1);

  /// @notice Returns the information about a position by the position's key
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @param key The position's key is a packed concatenation of the owner address, bottomTick and topTick indexes
  /// @return liquidity The amount of liquidity in the position
  /// @return innerFeeGrowth0Token Fee growth of token0 inside the tick range as of the last mint/burn/poke
  /// @return innerFeeGrowth1Token Fee growth of token1 inside the tick range as of the last mint/burn/poke
  /// @return fees0 The computed amount of token0 owed to the position as of the last mint/burn/poke
  /// @return fees1 The computed amount of token1 owed to the position as of the last mint/burn/poke
  function positions(
    bytes32 key
  ) external view returns (uint256 liquidity, uint256 innerFeeGrowth0Token, uint256 innerFeeGrowth1Token, uint128 fees0, uint128 fees1);

  /// @notice The currently in range liquidity available to the pool
  /// @dev This value has no relationship to the total liquidity across all ticks.
  /// Returned value cannot exceed type(uint128).max
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The current in range liquidity
  function liquidity() external view returns (uint128);

  /// @notice The current tick spacing
  /// @dev Ticks can only be initialized by new mints at multiples of this value
  /// e.g.: a tickSpacing of 60 means ticks can be initialized every 60th tick, i.e., ..., -120, -60, 0, 60, 120, ...
  /// However, tickspacing can be changed after the ticks have been initialized.
  /// This value is an int24 to avoid casting even though it is always positive.
  /// @return The current tick spacing
  function tickSpacing() external view returns (int24);

  /// @notice The previous initialized tick before (or at) current global tick
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The previous initialized tick
  function prevTickGlobal() external view returns (int24);

  /// @notice The next initialized tick after current global tick
  /// @dev **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The next initialized tick
  function nextTickGlobal() external view returns (int24);

  /// @notice The root of tick search tree
  /// @dev Each bit corresponds to one node in the second layer of tick tree: '1' if node has at least one active bit.
  /// **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The root of tick search tree as bitmap
  function tickTreeRoot() external view returns (uint32);

  /// @notice The second layer of tick search tree
  /// @dev Each bit in node corresponds to one node in the leafs layer (`tickTable`) of tick tree: '1' if leaf has at least one active bit.
  /// **important security note: caller should check reentrancy lock to prevent read-only reentrancy**
  /// @return The node of tick search tree second layer
  function tickTreeSecondLayer(int16) external view returns (uint256);
}
          

contracts/interfaces/IFeeManager.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;
pragma abicoder v2;

interface IFeeManager {
    struct Fee {
        string feeIdentifier;
        uint256 feeValue;
    }

    function setFeeAndWithdrawalPermission(
        address vault,
        string[] memory feeIdentifier,
        uint256[] memory feeValue,
        address[] memory withdrawer
    ) external;

    function setDefaultFeeAndWithdrawalPermission(
        address vault,
        uint256 totalVaultFees,
        string[] memory feeIdentifier,
        uint256[] memory feeValue,
        address[] memory withdrawer
    ) external;

    function withdrawFee(address vault, string memory feeIdentifier) external;

    function getFees(address vault) external view returns (Fee[] memory);

    function vaultTotalFees(address vault) external view returns (uint256);

    function setMigratedVaultFeeAndWithdrawalPermission() external;

    function withdrawalPermissions(
        address vault,
        string memory feeIdentifier
    ) external view returns (address);
}
          

@cryptoalgebra/integral-core/contracts/interfaces/plugin/IAlgebraPluginFactory.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title An interface for a contract that is capable of deploying Algebra plugins
/// @dev Such a factory is needed if the plugin should be automatically created and connected to each new pool
interface IAlgebraPluginFactory {
  /// @notice Deploys new plugin contract for pool
  /// @param pool The address of the pool for which the new plugin will be created
  /// @return New plugin address
  function createPlugin(address pool) external returns (address);
}
          

@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
          

@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4 <0.9.0;

import '../interfaces/pool/IAlgebraPoolErrors.sol';

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries
library TickMath {
  /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
  int24 internal constant MIN_TICK = -887272;
  /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
  int24 internal constant MAX_TICK = -MIN_TICK;

  /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
  uint160 internal constant MIN_SQRT_RATIO = 4295128739;
  /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
  uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

  /// @notice Calculates sqrt(1.0001^tick) * 2^96
  /// @dev Throws if |tick| > max tick
  /// @param tick The input tick for the above formula
  /// @return price A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
  /// at the given tick
  function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 price) {
    unchecked {
      // get abs value
      int24 absTickMask = tick >> (24 - 1);
      uint256 absTick = uint24((tick + absTickMask) ^ absTickMask);
      if (absTick > uint24(MAX_TICK)) revert IAlgebraPoolErrors.tickOutOfRange();

      uint256 ratio = 0x100000000000000000000000000000000;
      if (absTick & 0x1 != 0) ratio = 0xfffcb933bd6fad37aa2d162d1a594001;
      if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
      if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
      if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
      if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
      if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
      if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
      if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
      if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
      if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
      if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
      if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
      if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
      if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
      if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
      if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
      if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
      if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
      if (absTick >= 0x40000) {
        if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
      }

      if (tick > 0) {
        assembly {
          ratio := div(not(0), ratio)
        }
      }

      // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
      // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
      // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
      price = uint160((ratio + 0xFFFFFFFF) >> 32);
    }
  }

  /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
  /// @dev Throws in case price < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
  /// ever return.
  /// @param price The sqrt ratio for which to compute the tick as a Q64.96
  /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
  function getTickAtSqrtRatio(uint160 price) internal pure returns (int24 tick) {
    unchecked {
      // second inequality must be >= because the price can never reach the price at the max tick
      if (price < MIN_SQRT_RATIO || price >= MAX_SQRT_RATIO) revert IAlgebraPoolErrors.priceOutOfRange();
      uint256 ratio = uint256(price) << 32;

      uint256 r = ratio;
      uint256 msb;

      assembly {
        let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(5, gt(r, 0xFFFFFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(4, gt(r, 0xFFFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(3, gt(r, 0xFF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(2, gt(r, 0xF))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := shl(1, gt(r, 0x3))
        msb := or(msb, f)
        r := shr(f, r)
      }
      assembly {
        let f := gt(r, 0x1)
        msb := or(msb, f)
      }

      if (msb >= 128) r = ratio >> (msb - 127);
      else r = ratio << (127 - msb);

      int256 log_2 = (int256(msb) - 128) << 64;

      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(63, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(62, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(61, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(60, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(59, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(58, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(57, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(56, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(55, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(54, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(53, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(52, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(51, f))
        r := shr(f, r)
      }
      assembly {
        r := shr(127, mul(r, r))
        let f := shr(128, r)
        log_2 := or(log_2, shl(50, f))
      }

      int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

      int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
      int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

      tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= price ? tickHi : tickLow;
    }
  }
}
          

contracts/interfaces/IBaseDeposit.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.7.6;

/// @dev Interface for supporting depositing 2 assets via periphery
interface IBaseDeposit {
    ///
    /// @dev Deposits tokens in proportion to the vault's current holdings.
    /// @dev These tokens sit in the vault and are not used for liquidity on
    /// Uniswap until the next rebalance.
    /// @param amount0Desired Max amount of token0 to deposit
    /// @param amount1Desired Max amount of token1 to deposit
    /// @param amount0Min Revert if resulting `amount0` is less than this
    /// @param amount1Min Revert if resulting `amount1` is less than this
    /// @param to Recipient of shares
    /// @return shares Number of shares minted
    /// @return amount0 Amount of token0 deposited
    /// @return amount1 Amount of token1 deposited
    ///
    function deposit(
        uint256 amount0Desired,
        uint256 amount1Desired,
        uint256 amount0Min,
        uint256 amount1Min,
        address to
    )
        external
        returns (
            uint256 shares,
            uint256 amount0,
            uint256 amount1
        );
}
          

@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @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.
     *
     * _Available since v3.1._
     */
    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 `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}
          

@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolErrors.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;

/// @title Errors emitted by a pool
/// @notice Contains custom errors emitted by the pool
/// @dev Custom errors are separated from the common pool interface for compatibility with older versions of Solidity
interface IAlgebraPoolErrors {
  // ####  pool errors  ####

  /// @notice Emitted by the reentrancy guard
  error locked();

  /// @notice Emitted if arithmetic error occurred
  error arithmeticError();

  /// @notice Emitted if an attempt is made to initialize the pool twice
  error alreadyInitialized();

  /// @notice Emitted if an attempt is made to mint or swap in uninitialized pool
  error notInitialized();

  /// @notice Emitted if 0 is passed as amountRequired to swap function
  error zeroAmountRequired();

  /// @notice Emitted if invalid amount is passed as amountRequired to swap function
  error invalidAmountRequired();

  /// @notice Emitted if the pool received fewer tokens than it should have
  error insufficientInputAmount();

  /// @notice Emitted if there was an attempt to mint zero liquidity
  error zeroLiquidityDesired();
  /// @notice Emitted if actual amount of liquidity is zero (due to insufficient amount of tokens received)
  error zeroLiquidityActual();

  /// @notice Emitted if the pool received fewer tokens{0,1} after flash than it should have
  error flashInsufficientPaid0();
  error flashInsufficientPaid1();

  /// @notice Emitted if limitSqrtPrice param is incorrect
  error invalidLimitSqrtPrice();

  /// @notice Tick must be divisible by tickspacing
  error tickIsNotSpaced();

  /// @notice Emitted if a method is called that is accessible only to the factory owner or dedicated role
  error notAllowed();
  /// @notice Emitted if a method is called that is accessible only to the farming
  error onlyFarming();

  error invalidNewTickSpacing();
  error invalidNewCommunityFee();

  error dynamicFeeActive();
  error dynamicFeeDisabled();

  error pluginIsNotConnected();

  error invalidHookResponse(bytes4 selector);

  // ####  LiquidityMath errors  ####
  /// @notice Emitted if liquidity underflows
  error liquiditySub();
  /// @notice Emitted if liquidity overflows
  error liquidityAdd();

  // ####  TickManagement errors  ####
  error topTickLowerOrEqBottomTick();
  error bottomTickLowerThanMIN();
  error topTickAboveMAX();
  error liquidityOverflow();
  error tickIsNotInitialized();
  error tickInvalidLinks();

  // ####  SafeTransfer errors  ####
  error transferFailed();

  // ####  TickMath errors  ####
  error tickOutOfRange();
  error priceOutOfRange();
}
          

@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}
          

@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}
          

@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
          

@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @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);
}
          

@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Context_init_unchained();
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
    uint256[49] private __gap;
}
          

@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolEvents.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Events emitted by a pool
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolEvents {
  /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
  /// @dev Mint/Burn/Swaps cannot be emitted by the pool before Initialize
  /// @param price The initial sqrt price of the pool, as a Q64.96
  /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
  event Initialize(uint160 price, int24 tick);

  /// @notice Emitted when liquidity is minted for a given position
  /// @param sender The address that minted the liquidity
  /// @param owner The owner of the position and recipient of any minted liquidity
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param liquidityAmount The amount of liquidity minted to the position range
  /// @param amount0 How much token0 was required for the minted liquidity
  /// @param amount1 How much token1 was required for the minted liquidity
  event Mint(
    address sender,
    address indexed owner,
    int24 indexed bottomTick,
    int24 indexed topTick,
    uint128 liquidityAmount,
    uint256 amount0,
    uint256 amount1
  );

  /// @notice Emitted when fees are collected by the owner of a position
  /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
  /// @param owner The owner of the position for which fees are collected
  /// @param recipient The address that received fees
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param amount0 The amount of token0 fees collected
  /// @param amount1 The amount of token1 fees collected
  event Collect(address indexed owner, address recipient, int24 indexed bottomTick, int24 indexed topTick, uint128 amount0, uint128 amount1);

  /// @notice Emitted when a position's liquidity is removed
  /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
  /// @param owner The owner of the position for which liquidity is removed
  /// @param bottomTick The lower tick of the position
  /// @param topTick The upper tick of the position
  /// @param liquidityAmount The amount of liquidity to remove
  /// @param amount0 The amount of token0 withdrawn
  /// @param amount1 The amount of token1 withdrawn
  event Burn(address indexed owner, int24 indexed bottomTick, int24 indexed topTick, uint128 liquidityAmount, uint256 amount0, uint256 amount1);

  /// @notice Emitted by the pool for any swaps between token0 and token1
  /// @param sender The address that initiated the swap call, and that received the callback
  /// @param recipient The address that received the output of the swap
  /// @param amount0 The delta of the token0 balance of the pool
  /// @param amount1 The delta of the token1 balance of the pool
  /// @param price The sqrt(price) of the pool after the swap, as a Q64.96
  /// @param liquidity The liquidity of the pool after the swap
  /// @param tick The log base 1.0001 of price of the pool after the swap
  event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 price, uint128 liquidity, int24 tick);

  /// @notice Emitted by the pool for any flashes of token0/token1
  /// @param sender The address that initiated the swap call, and that received the callback
  /// @param recipient The address that received the tokens from flash
  /// @param amount0 The amount of token0 that was flashed
  /// @param amount1 The amount of token1 that was flashed
  /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
  /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
  event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1);

  /// @notice Emitted when the community fee is changed by the pool
  /// @param communityFeeNew The updated value of the community fee in thousandths (1e-3)
  event CommunityFee(uint16 communityFeeNew);

  /// @notice Emitted when the tick spacing changes
  /// @param newTickSpacing The updated value of the new tick spacing
  event TickSpacing(int24 newTickSpacing);

  /// @notice Emitted when the plugin address changes
  /// @param newPluginAddress New plugin address
  event Plugin(address newPluginAddress);

  /// @notice Emitted when the plugin config changes
  /// @param newPluginConfig New plugin config
  event PluginConfig(uint8 newPluginConfig);

  /// @notice Emitted when the fee changes inside the pool
  /// @param fee The current fee in hundredths of a bip, i.e. 1e-6
  event Fee(uint16 fee);
}
          

@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __Context_init_unchained();
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
    uint256[45] private __gap;
}
          

@cryptoalgebra/integral-core/contracts/libraries/Constants.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0 <0.9.0;

/// @title Contains common constants for Algebra contracts
/// @dev Constants moved to the library, not the base contract, to further emphasize their constant nature
library Constants {
  uint8 internal constant RESOLUTION = 96;
  uint256 internal constant Q96 = 1 << 96;
  uint256 internal constant Q128 = 1 << 128;

  uint24 internal constant FEE_DENOMINATOR = 1e6;
  uint16 internal constant FLASH_FEE = 0.01e4; // fee for flash loan in hundredths of a bip (0.01%)
  uint16 internal constant INIT_DEFAULT_FEE = 0.05e4; // init default fee value in hundredths of a bip (0.05%)
  uint16 internal constant MAX_DEFAULT_FEE = 5e4; // max default fee value in hundredths of a bip (5%)

  int24 internal constant INIT_DEFAULT_TICK_SPACING = 60;
  int24 internal constant MAX_TICK_SPACING = 500;
  int24 internal constant MIN_TICK_SPACING = 1;

  // the frequency with which the accumulated community fees are sent to the vault
  uint32 internal constant COMMUNITY_FEE_TRANSFER_FREQUENCY = 8 hours;

  // max(uint128) / (MAX_TICK - MIN_TICK)
  uint128 internal constant MAX_LIQUIDITY_PER_TICK = 191757638537527648490752896198553;

  uint16 internal constant MAX_COMMUNITY_FEE = 1e3; // 100%
  uint256 internal constant COMMUNITY_FEE_DENOMINATOR = 1e3;
  // role that can change settings in pools
  bytes32 internal constant POOLS_ADMINISTRATOR_ROLE = keccak256('POOLS_ADMINISTRATOR');
}
          

@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolPermissionedActions.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by permissioned addresses
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolPermissionedActions {
  /// @notice Set the community's % share of the fees. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @param newCommunityFee The new community fee percent in thousandths (1e-3)
  function setCommunityFee(uint16 newCommunityFee) external;

  /// @notice Set the new tick spacing values. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @param newTickSpacing The new tick spacing value
  function setTickSpacing(int24 newTickSpacing) external;

  /// @notice Set the new plugin address. Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  /// @param newPluginAddress The new plugin address
  function setPlugin(address newPluginAddress) external;

  /// @notice Set new plugin config
  /// @param newConfig In the new configuration of the plugin,
  /// each bit of which is responsible for a particular hook.
  /// Only factory owner or POOLS_ADMINISTRATOR_ROLE role
  function setPluginConfig(uint8 newConfig) external;

  /// @notice Set new pool fee. Can be called by owner if dynamic fee is disabled.
  /// Called by the plugin if dynamic fee is enabled
  /// @param newFee The new fee value
  function setFee(uint16 newFee) external;
}
          

@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
    uint256[49] private __gap;
}
          

@cryptoalgebra/integral-periphery/contracts/libraries/LiquidityAmounts.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@cryptoalgebra/integral-core/contracts/libraries/FullMath.sol';
import '@cryptoalgebra/integral-core/contracts/libraries/Constants.sol';

/// @title Liquidity amount functions
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-periphery
library LiquidityAmounts {
    /// @notice Downcasts uint256 to uint128
    /// @param x The uint258 to be downcasted
    /// @return y The passed value, downcasted to uint128
    function toUint128(uint256 x) private pure returns (uint128 y) {
        require((y = uint128(x)) == x);
    }

    /// @notice Computes the amount of liquidity received for a given amount of token0 and price range
    /// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param amount0 The amount0 being sent in
    /// @return liquidity The amount of returned liquidity
    function getLiquidityForAmount0(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount0
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, Constants.Q96);
        unchecked {
            return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96));
        }
    }

    /// @notice Computes the amount of liquidity received for a given amount of token1 and price range
    /// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param amount1 The amount1 being sent in
    /// @return liquidity The amount of returned liquidity
    function getLiquidityForAmount1(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount1
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        unchecked {
            return toUint128(FullMath.mulDiv(amount1, Constants.Q96, sqrtRatioBX96 - sqrtRatioAX96));
        }
    }

    /// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current
    /// pool prices and the prices at the tick boundaries
    /// @param sqrtRatioX96 A sqrt price representing the current pool prices
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param amount0 The amount of token0 being sent in
    /// @param amount1 The amount of token1 being sent in
    /// @return liquidity The maximum amount of liquidity received
    function getLiquidityForAmounts(
        uint160 sqrtRatioX96,
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount0,
        uint256 amount1
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

        if (sqrtRatioX96 <= sqrtRatioAX96) {
            liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0);
        } else if (sqrtRatioX96 < sqrtRatioBX96) {
            uint128 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0);
            uint128 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1);

            liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
        } else {
            liquidity = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1);
        }
    }

    /// @notice Computes the amount of token0 for a given amount of liquidity and a price range
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param liquidity The liquidity being valued
    /// @return amount0 The amount of token0
    function getAmount0ForLiquidity(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint256 amount0) {
        unchecked {
            if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

            return
                FullMath.mulDiv(
                    uint256(liquidity) << Constants.RESOLUTION,
                    sqrtRatioBX96 - sqrtRatioAX96,
                    sqrtRatioBX96
                ) / sqrtRatioAX96;
        }
    }

    /// @notice Computes the amount of token1 for a given amount of liquidity and a price range
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param liquidity The liquidity being valued
    /// @return amount1 The amount of token1
    function getAmount1ForLiquidity(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint256 amount1) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        unchecked {
            return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, Constants.Q96);
        }
    }

    /// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
    /// pool prices and the prices at the tick boundaries
    /// @param sqrtRatioX96 A sqrt price representing the current pool prices
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param liquidity The liquidity being valued
    /// @return amount0 The amount of token0
    /// @return amount1 The amount of token1
    function getAmountsForLiquidity(
        uint160 sqrtRatioX96,
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint256 amount0, uint256 amount1) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

        if (sqrtRatioX96 <= sqrtRatioAX96) {
            amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
        } else if (sqrtRatioX96 < sqrtRatioBX96) {
            amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
            amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
        } else {
            amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
        }
    }
}
          

@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
          

contracts/vault-types/HenjinLiquidityManagers/HenjinBaseLiquidityManager.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.12;

// ERC support
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

// Math
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; // max(), min(), and average
// Algebra
import { IAlgebraFactory } from "@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol";
import { IAlgebraPool } from "@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol";
import { TickMath } from "@cryptoalgebra/integral-core/contracts/libraries/TickMath.sol";
import { FullMath } from "@cryptoalgebra/integral-core/contracts/libraries/FullMath.sol";
import { IAlgebraMintCallback } from "@cryptoalgebra/integral-core/contracts/interfaces/callback/IAlgebraMintCallback.sol";
import { IAlgebraSwapCallback } from "@cryptoalgebra/integral-core/contracts/interfaces/callback/IAlgebraSwapCallback.sol";
import { LiquidityAmounts } from "@cryptoalgebra/integral-periphery/contracts/libraries/LiquidityAmounts.sol";
import { PositionKey } from "@cryptoalgebra/integral-periphery/contracts/libraries/PositionKey.sol";
import { IVolatilityOracle } from "@cryptoalgebra/integral-base-plugin/contracts/interfaces/plugins/IVolatilityOracle.sol";

// Util support
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import { StringsUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";

// Sundry
import { IBareVaultRegistry8 } from "contracts/interfaces/IBareVaultRegistry8.sol";
import { IBaseDeposit } from "contracts/interfaces/IBaseDeposit.sol";
import { IFeeManager } from "../../interfaces/IFeeManager.sol";

abstract contract HenjinBaseLiquidityManager is
    Initializable,
    ERC20Upgradeable,
    PausableUpgradeable,
    AccessControlUpgradeable,
    IAlgebraMintCallback,
    IAlgebraSwapCallback
{
    using SafeERC20 for IERC20;
    // Storage

    uint256 internal constant PRECISION = 1e18;

    /// @dev Can collect Steer fees
    bytes32 internal constant STEER_ROLE = keccak256("STEER_ROLE");
    /// @dev Can call tend function
    bytes32 internal constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

    /// @notice Fee info
    /// @dev Fee rates, each multiplied by 10,000
    ///      (a TOTAL_FEE of 100 means a 1% cut of total Algebra fees)
    /// @dev Total fraction of fees not going towards LPs, multiplied by 10,000
    uint256 public constant TOTAL_FEE = 15_00;
    /// @dev Total fraction of fees going towards Steer (as opposed to going towards strategist)
    uint256 public constant STEER_FRACTION_OF_FEE = 66_67;

    uint256 public constant STRATEGIST_FRACTION_OF_FEE = 33_33;

    uint256 internal constant FEE_DIVISOR = 100_00;
    uint256 internal constant DIVISOR = 100_00;
    /// @dev maximum value that can be passed in total weight parameter of tend
    uint256 internal constant TOTAL_WEIGHT_MAX = 100_00;

    uint256 internal constant DIVISOR100 = 1_00;

    uint256 internal constant FIVE = 5;

    /// @dev maxTickChange value should be greater than MAX_TICK_CHANGE_MIN
    int24 internal constant MAX_TICK_CHANGE_MIN = 9;
    /// @dev maxTickChange value should be less than MAX_TICK_CHANGE_MAX
    int24 internal constant MAX_TICK_CHANGE_MAX = 2_001;
    /// @dev twapInterval value should be greater than TWAP_INTERVAL_MIN
    uint32 internal constant TWAP_INTERVAL_MIN = 3_0;
    /// @dev twapInterval value should be less than TWAP_INTERVAL_MAX
    uint32 internal constant TWAP_INTERVAL_MAX = 6_00;
    /// @dev first deposit should mint MIN_SHARES or greater number of shares
    uint256 internal constant MIN_SHARES = 100_00_00;

    /// @dev Address of Algebra Factory
    IAlgebraFactory internal constant FACTORY =
        IAlgebraFactory(0x42B08e7a9211482d3643a126a7dF1895448d3509);

    /// @dev Address of vault registry
    ///      Address strategist can collect strategist fees, but is not stored here.
    address internal vaultRegistry;

    /// @notice Addresses of Token0 and Token1
    IERC20 public token0;
    IERC20 public token1;

    /// @notice Address of Algebra pool
    IAlgebraPool public pool;

    /// @dev For depositing
    /// Roughly corresponds to a 5% diff between current price and twap price
    int24 public maxTickChange;

    /// @dev Number of seconds to get the time-weighted average over
    uint32 public twapInterval;

    //For mint Callback Protection
    bool internal mintCallBackProtection;

    //For swap Callback Protection
    bool internal swapCallBackProtection;
    mapping(string => uint256) public accruedFees0;
    mapping(string => uint256) public accruedFees1;

    address internal feeManager;
    uint256 public totalFees0;
    uint256 public totalFees1;

    // Events

    /// @dev Pool/vault info as of the end of a tend
    /// @param sqrtPriceX96 Current pool price
    /// @param totalAmount0 The total user-owned token0, including funds held in the vault
    ///                     and funds deposited into the pool
    /// @param totalSupply The total number of shares in this vault.
    event Snapshot(
        uint160 sqrtPriceX96,
        uint256 totalAmount0,
        uint256 totalAmount1,
        uint256 totalSupply
    );

    /// @dev Deposit info
    /// @param sender The address which provided the tokens
    /// @param to The receiver of vault shares from this deposit, generally the same as sender
    /// @param shares The number of shares that have been minted by this deposit
    /// @param amount0 The amount of t0 that was used to mint the shares
    /// @param amount1 The amount of t1 that was used to mint the shares
    event Deposit(
        address indexed sender,
        address indexed to,
        uint256 shares,
        uint256 amount0,
        uint256 amount1
    );

    /// @dev Withdraw info
    /// @param sender msg.sender and the owner of the shares being burned
    /// @param to The receiver of the tokens earned via the burn
    /// @param shares The number of shares being burned
    /// @param amount0 The amount of t0 earned via the burn
    /// @param amount1 The amount of t1 earned via the burn
    event Withdraw(
        address indexed sender,
        address indexed to,
        uint256 shares,
        uint256 amount0,
        uint256 amount1
    );

    /// @dev Info on protocol fees earned. Triggers whenever a withdraw or a tend happens.
    /// @param amount0Earned Total t0 fees earned, including fees going to steer and strategist.
    /// @param amount1Earned Total t1 fees earned, including fees going to steer and strategist.
    event FeesEarned(uint256 amount0Earned, uint256 amount1Earned);

    // Constructor

    constructor() initializer {}

    // External Functions

    /// @notice Withdraws tokens in proportion to the vault's holdings.
    /// @param shares Shares burned by sender
    /// @param amount0Min Revert if resulting `amount0` is smaller than this
    /// @param amount1Min Revert if resulting `amount1` is smaller than this
    /// @param to Recipient of tokens
    /// @return amount0 Amount of token0 sent to recipient
    /// @return amount1 Amount of token1 sent to recipient
    function withdraw(
        uint256 shares,
        uint256 amount0Min,
        uint256 amount1Min,
        address to
    ) external virtual returns (uint256 amount0, uint256 amount1) {
        require(to != address(0));
        // Shares to withdraw must be greater than zero
        require(shares > 0);

        uint256 _totalSupply = totalSupply();

        // Burn LPTs
        _burn(msg.sender, shares);

        // Calculate token amounts proportional to unused balances
        // LP value = (Value deposited in Algebra + value held in vault undeposited) / total LP tokens.
        // Here we calculate value held in vault undeposited.
        // No div(0) safemath here because totalSupply != 0;
        // since shares != 0 and totalSupply >= shares
        amount0 = FullMath.mulDiv(_getBalance0(), shares, _totalSupply);
        amount1 = FullMath.mulDiv(_getBalance1(), shares, _totalSupply);

        // Withdraw proportion of liquidity from Algebra pool
        (uint256 t0FromPool, uint256 t1FromPool) = _burnAndCollect(
            shares,
            _totalSupply
        );

        // In addition to their share of vault-held tokens,
        // withdraw their share of Algebra-held tokens.
        amount0 += t0FromPool;
        amount1 += t1FromPool;

        require(amount0 >= amount0Min);
        require(amount1 >= amount1Min);

        // Push tokens to recipient
        _transferTokens(to, amount0, amount1);

        emit Withdraw(msg.sender, to, shares, amount0, amount1);
    }

    /// @dev Callback for Algebra pool.
    function algebraMintCallback(
        uint256 amount0,
        uint256 amount1,
        bytes calldata /* data */
    ) external override {
        require(msg.sender == address(pool));
        require(mintCallBackProtection);
        mintCallBackProtection = false;
        _transferTokens(msg.sender, amount0, amount1);
    }

    /// @dev Callback for Algebra pool.
    function algebraSwapCallback(
        int256 amount0Wanted,
        int256 amount1Wanted,
        bytes calldata /* data */
    ) external override {
        require(msg.sender == address(pool));
        require(swapCallBackProtection);
        swapCallBackProtection = false;
        uint256 amount0;
        uint256 amount1;
        if (amount0Wanted > 0) {
            amount0 = uint256(amount0Wanted);
        }
        if (amount1Wanted > 0) {
            amount1 = uint256(amount1Wanted);
        }
        _transferTokens(msg.sender, amount0, amount1);
    }

    /// @notice Removes liquidity in case of emergency.
    function emergencyBurn(
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity
    )
        external
        onlyRole(STEER_ROLE)
        returns (uint256 amount0, uint256 amount1)
    {
        (amount0, amount1) = pool.burn(tickLower, tickUpper, liquidity, "");
        pool.collect(
            address(this),
            tickLower,
            tickUpper,
            type(uint128).max,
            type(uint128).max
        );
    }

    /// @notice Used by integral-periphery to verify vault is depositable.
    function supportsInterface(
        bytes4 interfaceId
    ) public view override returns (bool) {
        return
            IBareVaultRegistry8(vaultRegistry).doISupportInterface(
                interfaceId
            );
    }

    /// @dev Pause and unpause
    function pause() external onlyRole(STEER_ROLE) {
        _pause();
    }

    function unpause() external onlyRole(STEER_ROLE) {
        _unpause();
    }

    // Public Functions

    /// @dev Initializes vault
    /// @param _vaultManager is the address which will manage the vault being created, pass orchestrator address if the vault is meant to be managed by the orchestrator
    /// @param _steer The steer multisig address, responsible for some governance functions.
    /// @param _params All other parameters this vault will use
    function initialize(
        address _vaultManager,
        address, //orchestrator not needed here as, if this vault is to be managed by orchestrator, _vaultManager parameter should be the orchestrator address
        address _steer,
        bytes memory _params
    ) public virtual initializer {
        // _token0 is address of token0
        // _token1 is address of token1
        // _maxTickChange is max difference between TWAP tick and current tick,
        //      useful for protection against frontrunning. Cannot be negative.
        // _twapInterval is the number of seconds to get the time-weighted average over
        (
            address _token0,
            address _token1,
            int24 _maxTickChange,
            uint32 _twapInterval
        ) = abi.decode(_params, (address, address, int24, uint32));

        // Validate input parameters
        // Below values should be chosen according to the chain this contract is deployed on and
        // the volume of the pool it manages
        require(
            _maxTickChange > MAX_TICK_CHANGE_MIN &&
                _maxTickChange < MAX_TICK_CHANGE_MAX
        );
        require(
            _twapInterval > TWAP_INTERVAL_MIN &&
                _twapInterval < TWAP_INTERVAL_MAX
        );
        require(_token0 < _token1);

        // Context inits
        vaultRegistry = msg.sender;
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);

        // Get relevant pool from Algebra factory
        address _pool = FACTORY.poolByPair(_token0, _token1);

        // Revert if pool doesn't exist yet
        require(_pool != address(0));

        // Set pool
        pool = IAlgebraPool(_pool);
        address _plugin = IAlgebraPool(_pool).plugin();
        require(_plugin != address(0));
        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = _twapInterval;
        IVolatilityOracle(_plugin).getTimepoints(secondsAgos); //Just to check if this pool has a oracle plugin if not reverts from here
        feeManager = IBareVaultRegistry8(vaultRegistry).feeManager();

        // Init the LP token ERC20 using totalVaultCount from the vaultRegistry
        // For e.g. if current vault count is 20 than LP name is STEER_ALGEBRA_VAULT_20
        string memory vaultCount = StringsUpgradeable.toString(
            IBareVaultRegistry8(msg.sender).totalVaultCount() + 1
        );
        __ERC20_init(
            string(abi.encodePacked("STEER_ALGEBRA_VAULT_", vaultCount)),
            string(abi.encodePacked("STEERAV", vaultCount))
        );

        // Init everything else
        __Pausable_init();
        __AccessControl_init();

        _setupRole(STEER_ROLE, _steer);
        _setupRole(MANAGER_ROLE, _vaultManager);

        // Set security params
        maxTickChange = _maxTickChange;
        twapInterval = _twapInterval;
    }

    /// @notice Deposits tokens in proportion to the vault's current holdings.
    /// @dev These tokens sit in the vault and are not used for liquidity on
    ///      Algebra until the next rebalance.
    ///      function requirements:
    ///        Either amount0Desired or amount1Desired must be > 0 or it will revert with 'CROSS'
    /// @param amount0Desired Max amount of token0 to deposit
    /// @param amount1Desired Max amount of token1 to deposit
    /// @param amount0Min Revert if resulting amount0Used is less than this
    /// @param amount1Min Revert if resulting amount1Used is less than this
    /// @param to Recipient of shares
    /// @return shares Number of shares minted
    /// @return amount0Used Amount of token0 deposited
    /// @return amount1Used Amount of token1 deposited
    function deposit(
        uint256 amount0Desired,
        uint256 amount1Desired,
        uint256 amount0Min,
        uint256 amount1Min,
        address to
    )
        public
        virtual
        whenNotPaused
        returns (uint256 shares, uint256 amount0Used, uint256 amount1Used)
    {
        require(to != address(0));
        IBareVaultRegistry8.VaultState state = IBareVaultRegistry8(
            vaultRegistry
        ).getVaultDetails(address(this)).state;
        require(
            state == IBareVaultRegistry8.VaultState.PendingThreshold ||
                state == IBareVaultRegistry8.VaultState.Active
        );
        // Update pool fees earned
        poke();

        // Calculate amounts proportional to vault's holdings
        uint256 _totalSupply = totalSupply();
        (uint256 total0, uint256 total1) = getTotalAmounts();

        // If total supply > 0, vault can't be empty.
        assert(_totalSupply == 0 || total0 > 0 || total1 > 0);

        if (_totalSupply == 0) {
            // For first deposit, just use the amounts desired
            amount0Used = amount0Desired;
            amount1Used = amount1Desired;
            shares = Math.max(amount0Used, amount1Used);
            require(shares >= MIN_SHARES);
        } else if (total0 == 0) {
            shares = FullMath.mulDiv(amount1Desired, _totalSupply, total1);
            amount1Used = FullMath.mulDivRoundingUp(
                shares,
                total1,
                _totalSupply
            );
        } else if (total1 == 0) {
            shares = FullMath.mulDiv(amount0Desired, _totalSupply, total0);
            amount0Used = FullMath.mulDivRoundingUp(
                shares,
                total0,
                _totalSupply
            );
        } else {
            uint256 cross = Math.min(
                amount0Desired * total1,
                amount1Desired * total0
            );

            // If cross is zero, this means that the inputted ratio is totally wrong
            // and must be adjusted to better match the vault's held ratio.
            // This pretty much only happens if all of the vault's holdings are in one token,
            // and the user wants to exclusively deposit the other token.
            require(cross > 0, "C");

            // Round up amounts
            // cross - 1 can be unchecked since above we require cross != 0
            // total1 and total0 are also both > 0
            amount0Used = ((cross - 1) / total1) + 1;
            amount1Used = ((cross - 1) / total0) + 1;

            shares = FullMath.mulDiv(cross, _totalSupply, total0) / total1;
        }

        // Make sure deposit meets slippage requirements.
        // If amount0Used < amount0Min or amount1Used < amount1Min,
        // there has been too much slippage.
        require(shares > 0);
        require(amount0Used >= amount0Min, "0");
        require(amount1Used >= amount1Min, "1");

        // Pull in tokens from sender
        if (amount0Used > 0) {
            token0.safeTransferFrom(msg.sender, address(this), amount0Used);
        }
        if (amount1Used > 0) {
            token1.safeTransferFrom(msg.sender, address(this), amount1Used);
        }

        // Mint shares to recipient
        _mint(to, shares);
        emit Deposit(msg.sender, to, shares, amount0Used, amount1Used);
    }

    function poke() public virtual;

    function getTotalAmounts() public view virtual returns (uint256, uint256);

    // Internal Functions

    /// @dev Wrapper around `IAlgebraPool.positions()`.
    /// @param tickLower Lower bound of position whose info is requested
    /// @param tickUpper Upper bound of position
    /// @return liquidity The amount of liquidity owned by this position
    /// @return feeGrowthInside0LastX128 Fee growth per unit of liquidity
    ///           as of the last update to liquidity or fees owed
    /// @return feeGrowthInside1LastX128 Fee growth per unit of liquidity
    ///           as of the last update to liquidity or fees owed
    /// @return tokensOwed0 The fees owed to the position owner in token0
    /// @return tokensOwed1 The fees owed to the position owner in token1
    function _position(
        int24 tickLower,
        int24 tickUpper
    ) internal view returns (uint256, uint256, uint256, uint128, uint128) {
        return
            pool.positions(
                PositionKey.compute(address(this), tickLower, tickUpper)
            );
    }

    function _transferTokens(
        address to,
        uint256 amount0,
        uint256 amount1
    ) internal {
        if (amount0 > 0) {
            token0.safeTransfer(to, amount0);
        }
        if (amount1 > 0) {
            token1.safeTransfer(to, amount1);
        }
    }

    /// @dev Withdraws liquidity from all positions, allocating fees correctly in the process.
    /// @param shares LP shares being withdrawn
    /// @param totalShares Total # of LP tokens in the vault
    /// @return t0 Token0 earned from burned liquidity + fees.
    ///            Only includes burned + fees corresponding to LP shares being withdrawn (100% if tend)
    /// @return t1 Token1 earned from burned liquidity + fees
    function _burnAndCollect(
        uint256 shares,
        uint256 totalShares
    ) internal virtual returns (uint256 t0, uint256 t1);

    /// @dev revert if volatility is above acceptable levels
    ///      (mainly used to prevent flashloan attacks)
    /// @param currentTick Current pool tick
    function _checkVolatility(int24 currentTick) internal {
        // SLOADS for efficiency
        uint32 _twapInterval = twapInterval;

        int24 _maxTickChange = maxTickChange;

        // Get TWAP tick
        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = _twapInterval;

        // tickCumulatives is basically where the tick was as of twapInterval seconds ago
        try
            IVolatilityOracle(IAlgebraPool(pool).plugin()).getTimepoints(
                secondsAgos
            )
        returns (int56[] memory tickCumulatives, uint88[] memory) {
            int56 tickCumulativesDelta = tickCumulatives[1] -
                tickCumulatives[0];

            int24 twapTick = int24(
                tickCumulativesDelta / int32(_twapInterval)
            );

            // Always round to negative infinity
            if (
                tickCumulativesDelta < 0 &&
                (tickCumulativesDelta % int56(uint56(_twapInterval)) != 0)
            ) {
                twapTick--;
            }

            // Make sure currentTick is not more than maxTickChange ticks away from twapTick
            // No SafeMath here--even if a compromised governance contract set _maxTickChange to a very high value,
            // it would only wrap around and cause this check to fail.
            require(
                currentTick <= twapTick + _maxTickChange &&
                    currentTick >= twapTick - _maxTickChange,
                "V"
            );
        } catch {
            _pause();
            // Pause the vault so pause new deposits,collectFees and tend until there is a oracle plugin/function
            // To collect fees the vault should be unpaused and it should not be a problem even by chance a tend is called
            // it should again reach here and pause the vault again also it should be unpaused after a proper oracle
            // is setup to resume working
            return;
        }
    }

    /// @notice Balance of token0 in vault not used in any position.
    function _getBalance0() internal view returns (uint256) {
        return (token0.balanceOf(address(this)) - totalFees0);
    }

    /// @notice Balance of token1 in vault not used in any position.
    function _getBalance1() internal view returns (uint256) {
        return (token1.balanceOf(address(this)) - totalFees1);
    }

    function collectFees(
        string memory feeIdentifier,
        uint256 amount0,
        uint256 amount1
    ) external whenNotPaused {
        require(msg.sender == feeManager);
        address to = IFeeManager(feeManager).withdrawalPermissions(
            address(this),
            feeIdentifier
        );
        uint256 currentFee0 = accruedFees0[feeIdentifier];
        uint256 currentFee1 = accruedFees1[feeIdentifier];

        if (amount0 > 0) {
            accruedFees0[feeIdentifier] = currentFee0 - amount0;
            totalFees0 -= amount0;
            token0.safeTransfer(to, amount0);
        }
        if (amount1 > 0) {
            accruedFees1[feeIdentifier] = currentFee1 - amount1;
            totalFees1 -= amount1;
            token1.safeTransfer(to, amount1);
        }
    }

    function feeDetails()
        external
        view
        returns (uint256, address[] memory, string[] memory, uint256[] memory)
    {
        address[] memory feeWithdrawerAddresses = new address[](2);
        feeWithdrawerAddresses[0] = IBareVaultRegistry8(vaultRegistry).owner(); //steer withdrawer
        feeWithdrawerAddresses[1] = IBareVaultRegistry8(vaultRegistry)
            .getStrategyCreatorForVault(address(this)); //strategist withdrawer
        string[] memory feeIdentifiers = new string[](2);
        feeIdentifiers[0] = "STEER_FEES";
        feeIdentifiers[1] = "STRATEGIST_FEES";
        uint256[] memory feeValues = new uint256[](2);
        feeValues[0] = STEER_FRACTION_OF_FEE;
        feeValues[1] = STRATEGIST_FRACTION_OF_FEE;
        return (TOTAL_FEE, feeWithdrawerAddresses, feeIdentifiers, feeValues);
    }

    // Gap

    /// @dev Useful if we upgrade to a contract which needs more storage slots
    /// This contract consumes 9 slots and following openzeppelin standards(50 - slots consumed by this contract i.e.50 - 9 = 41) the gap array size is set to 41
    uint256[41] private gap;
}
          

@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
    uint256[50] private __gap;
}
          

@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
          

@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolImmutables.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that never changes
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolImmutables {
  /// @notice The Algebra factory contract, which must adhere to the IAlgebraFactory interface
  /// @return The contract address
  function factory() external view returns (address);

  /// @notice The first of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token0() external view returns (address);

  /// @notice The second of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token1() external view returns (address);

  /// @notice The contract to which community fees are transferred
  /// @return The communityVault address
  function communityVault() external view returns (address);

  /// @notice The maximum amount of position liquidity that can use any tick in the range
  /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
  /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
  /// @return The max amount of liquidity per tick
  function maxLiquidityPerTick() external view returns (uint128);
}
          

@cryptoalgebra/integral-core/contracts/interfaces/callback/IAlgebraMintCallback.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IAlgebraPoolActions#mint
/// @notice Any contract that calls IAlgebraPoolActions#mint must implement this interface
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraMintCallback {
  /// @notice Called to `msg.sender` after minting liquidity to a position from IAlgebraPool#mint.
  /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity.
  /// The caller of this method _must_ be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
  /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity
  /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity
  /// @param data Any data passed through by the caller via the IAlgebraPoolActions#mint call
  function algebraMintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata data) external;
}
          

@openzeppelin/contracts/utils/math/Math.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 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.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // 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;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}
          

@cryptoalgebra/integral-core/contracts/interfaces/callback/IAlgebraSwapCallback.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IAlgebraPoolActions#swap
/// @notice Any contract that calls IAlgebraPoolActions#swap must implement this interface
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraSwapCallback {
  /// @notice Called to `msg.sender` after executing a swap via IAlgebraPool#swap.
  /// @dev In the implementation you must pay the pool tokens owed for the swap.
  /// The caller of this method _must_ be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
  /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
  /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
  /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
  /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
  /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
  /// @param data Any data passed through by the caller via the IAlgebraPoolActions#swap call
  function algebraSwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}
          

@openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
          

@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraPool.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;

import './pool/IAlgebraPoolImmutables.sol';
import './pool/IAlgebraPoolState.sol';
import './pool/IAlgebraPoolActions.sol';
import './pool/IAlgebraPoolPermissionedActions.sol';
import './pool/IAlgebraPoolEvents.sol';
import './pool/IAlgebraPoolErrors.sol';

/// @title The interface for a Algebra Pool
/// @dev The pool interface is broken up into many smaller pieces.
/// This interface includes custom error definitions and cannot be used in older versions of Solidity.
/// For older versions of Solidity use #IAlgebraPoolLegacy
/// Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPool is
  IAlgebraPoolImmutables,
  IAlgebraPoolState,
  IAlgebraPoolActions,
  IAlgebraPoolPermissionedActions,
  IAlgebraPoolEvents,
  IAlgebraPoolErrors
{
  // used only for combining interfaces
}
          

@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.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:
 *
 * ```
 * 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}:
 *
 * ```
 * 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.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal onlyInitializing {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(uint160(account), 20),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @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 override 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.
     */
    function grantRole(bytes32 role, address account) public virtual override 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.
     */
    function revokeRole(bytes32 role, address account) public virtual override 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 `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @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 Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
    uint256[49] private __gap;
}
          

contracts/interfaces/IBareVaultRegistry8.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/**
 * Used only as a 0.7.6 registry for the UniLiquidityManager.
 */
interface IBareVaultRegistry8 {
    /**
     * PendingApproval: strategy is submitted but has not yet been approved by the owner
     * PendingThreshold: strategy is approved but has not yet reached the threshold of TVL required
     * Paused: strategy was active but something went wrong, so now it's paused
     * Active: strategy is active and can be used
     * Retired: strategy is retired and can no longer be used
     */
    enum VaultState {
        PendingApproval,
        PendingThreshold,
        Paused,
        Active,
        Retired
    }

    /**
     * @dev all necessary data for vault. Name and symbol are stored in vault's ERC20. Owner is stored with tokenId in StrategyRegistry.
     * tokenId: NFT identifier number
     * vaultAddress: address of vault this describes
     * state: state of the vault.
     */
    struct VaultData {
        VaultState state;
        uint256 tokenId; //NFT ownership of this vault and all others that use vault's exec bundle
        uint256 vaultID; //unique identifier for this vault and strategy token id
        string payloadIpfs;
        address vaultAddress;
        string beaconName;
    }

    /// @notice Retrieves the creator of a given vault
    /// @param _vault The address of the vault
    /// @return The address of the creator
    function getStrategyCreatorForVault(
        address _vault
    ) external view returns (address);

    //Total vault count
    function totalVaultCount() external view returns (uint256);

    function whitelistRegistry() external view returns (address);

    function doISupportInterface(
        bytes4 interfaceId
    ) external view returns (bool);

    function feeManager() external view returns (address);

    /// @dev Retrieves the details of a given vault by address
    /// @param _address The address of the vault
    /// @return The details of the vault
    function getVaultDetails(
        address _address
    ) external view returns (VaultData memory);

    function owner() external view returns (address);

    function vaultHelper() external view returns (address);
}
          

@cryptoalgebra/integral-base-plugin/contracts/interfaces/plugins/IVolatilityOracle.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Algebra volatility oracle
/// @dev This contract stores timepoints and calculates statistical averages
interface IVolatilityOracle {
  /// @notice Returns data belonging to a certain timepoint
  /// @param index The index of timepoint in the array
  /// @dev There is more convenient function to fetch a timepoint: getTimepoints(). Which requires not an index but seconds
  /// @return initialized Whether the timepoint has been initialized and the values are safe to use
  /// @return blockTimestamp The timestamp of the timepoint
  /// @return tickCumulative The tick multiplied by seconds elapsed for the life of the pool as of the timepoint timestamp
  /// @return volatilityCumulative Cumulative standard deviation for the life of the pool as of the timepoint timestamp
  /// @return tick The tick at blockTimestamp
  /// @return averageTick Time-weighted average tick
  /// @return windowStartIndex Index of closest timepoint >= WINDOW seconds ago
  function timepoints(
    uint256 index
  )
    external
    view
    returns (
      bool initialized,
      uint32 blockTimestamp,
      int56 tickCumulative,
      uint88 volatilityCumulative,
      int24 tick,
      int24 averageTick,
      uint16 windowStartIndex
    );

  /// @notice Returns the index of the last timepoint that was written.
  /// @return index of the last timepoint written
  function timepointIndex() external view returns (uint16);

  /// @notice Returns the timestamp of the last timepoint that was written.
  /// @return timestamp of the last timepoint
  function lastTimepointTimestamp() external view returns (uint32);

  /// @notice Returns information about whether oracle is initialized
  /// @return true if oracle is initialized, otherwise false
  function isInitialized() external view returns (bool);

  /// @dev Reverts if a timepoint at or before the desired timepoint timestamp does not exist.
  /// 0 may be passed as `secondsAgo' to return the current cumulative values.
  /// If called with a timestamp falling between two timepoints, returns the counterfactual accumulator values
  /// at exactly the timestamp between the two timepoints.
  /// @dev `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared because they may differ due to interpolation errors
  /// @param secondsAgo The amount of time to look back, in seconds, at which point to return a timepoint
  /// @return tickCumulative The cumulative tick since the pool was first initialized, as of `secondsAgo`
  /// @return volatilityCumulative The cumulative volatility value since the pool was first initialized, as of `secondsAgo`
  function getSingleTimepoint(uint32 secondsAgo) external view returns (int56 tickCumulative, uint88 volatilityCumulative);

  /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
  /// @dev Reverts if `secondsAgos` > oldest timepoint
  /// @dev `volatilityCumulative` values for timestamps after the last timepoint _should not_ be compared because they may differ due to interpolation errors
  /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return a timepoint
  /// @return tickCumulatives The cumulative tick since the pool was first initialized, as of each `secondsAgo`
  /// @return volatilityCumulatives The cumulative volatility values since the pool was first initialized, as of each `secondsAgo`
  function getTimepoints(uint32[] memory secondsAgos) external view returns (int56[] memory tickCumulatives, uint88[] memory volatilityCumulatives);

  /// @notice Fills uninitialized timepoints with nonzero value
  /// @dev Can be used to reduce the gas cost of future swaps
  /// @param startIndex The start index, must be not initialized
  /// @param amount of slots to fill, startIndex + amount must be <= type(uint16).max
  function prepayTimepointsStorageSlots(uint16 startIndex, uint16 amount) external;
}
          

@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import './plugin/IAlgebraPluginFactory.sol';

/// @title The interface for the Algebra Factory
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraFactory {
  /// @notice Emitted when a process of ownership renounce is started
  /// @param timestamp The timestamp of event
  /// @param finishTimestamp The timestamp when ownership renounce will be possible to finish
  event RenounceOwnershipStart(uint256 timestamp, uint256 finishTimestamp);

  /// @notice Emitted when a process of ownership renounce cancelled
  /// @param timestamp The timestamp of event
  event RenounceOwnershipStop(uint256 timestamp);

  /// @notice Emitted when a process of ownership renounce finished
  /// @param timestamp The timestamp of ownership renouncement
  event RenounceOwnershipFinish(uint256 timestamp);

  /// @notice Emitted when a pool is created
  /// @param token0 The first token of the pool by address sort order
  /// @param token1 The second token of the pool by address sort order
  /// @param pool The address of the created pool
  event Pool(address indexed token0, address indexed token1, address pool);

  /// @notice Emitted when the default community fee is changed
  /// @param newDefaultCommunityFee The new default community fee value
  event DefaultCommunityFee(uint16 newDefaultCommunityFee);

  /// @notice Emitted when the default tickspacing is changed
  /// @param newDefaultTickspacing The new default tickspacing value
  event DefaultTickspacing(int24 newDefaultTickspacing);

  /// @notice Emitted when the default fee is changed
  /// @param newDefaultFee The new default fee value
  event DefaultFee(uint16 newDefaultFee);

  /// @notice Emitted when the defaultPluginFactory address is changed
  /// @param defaultPluginFactoryAddress The new defaultPluginFactory address
  event DefaultPluginFactory(address defaultPluginFactoryAddress);

  /// @notice role that can change communityFee and tickspacing in pools
  /// @return The hash corresponding to this role
  function POOLS_ADMINISTRATOR_ROLE() external view returns (bytes32);

  /// @notice Returns `true` if `account` has been granted `role` or `account` is owner.
  /// @param role The hash corresponding to the role
  /// @param account The address for which the role is checked
  /// @return bool Whether the address has this role or the owner role or not
  function hasRoleOrOwner(bytes32 role, address account) external view returns (bool);

  /// @notice Returns the current owner of the factory
  /// @dev Can be changed by the current owner via transferOwnership(address newOwner)
  /// @return The address of the factory owner
  function owner() external view returns (address);

  /// @notice Returns the current poolDeployerAddress
  /// @return The address of the poolDeployer
  function poolDeployer() external view returns (address);

  /// @notice Returns the current communityVaultAddress
  /// @return The address to which community fees are transferred
  function communityVault() external view returns (address);

  /// @notice Returns the default community fee
  /// @return Fee which will be set at the creation of the pool
  function defaultCommunityFee() external view returns (uint16);

  /// @notice Returns the default fee
  /// @return Fee which will be set at the creation of the pool
  function defaultFee() external view returns (uint16);

  /// @notice Returns the default tickspacing
  /// @return Tickspacing which will be set at the creation of the pool
  function defaultTickspacing() external view returns (int24);

  /// @notice Return the current pluginFactory address
  /// @return Algebra plugin factory
  function defaultPluginFactory() external view returns (IAlgebraPluginFactory);

  /// @notice Returns the default communityFee and tickspacing
  /// @return communityFee which will be set at the creation of the pool
  /// @return tickSpacing which will be set at the creation of the pool
  /// @return fee which will be set at the creation of the pool
  function defaultConfigurationForPool() external view returns (uint16 communityFee, int24 tickSpacing, uint16 fee);

  /// @notice Deterministically computes the pool address given the token0 and token1
  /// @dev The method does not check if such a pool has been created
  /// @param token0 first token
  /// @param token1 second token
  /// @return pool The contract address of the Algebra pool
  function computePoolAddress(address token0, address token1) external view returns (address pool);

  /// @notice Returns the pool address for a given pair of tokens, or address 0 if it does not exist
  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
  /// @param tokenA The contract address of either token0 or token1
  /// @param tokenB The contract address of the other token
  /// @return pool The pool address
  function poolByPair(address tokenA, address tokenB) external view returns (address pool);

  /// @notice returns keccak256 of AlgebraPool init bytecode.
  /// @dev the hash value changes with any change in the pool bytecode
  /// @return Keccak256 hash of AlgebraPool contract init bytecode
  function POOL_INIT_CODE_HASH() external view returns (bytes32);

  /// @return timestamp The timestamp of the beginning of the renounceOwnership process
  function renounceOwnershipStartTimestamp() external view returns (uint256 timestamp);

  /// @notice Creates a pool for the given two tokens
  /// @param tokenA One of the two tokens in the desired pool
  /// @param tokenB The other of the two tokens in the desired pool
  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0.
  /// The call will revert if the pool already exists or the token arguments are invalid.
  /// @return pool The address of the newly created pool
  function createPool(address tokenA, address tokenB) external returns (address pool);

  /// @dev updates default community fee for new pools
  /// @param newDefaultCommunityFee The new community fee, _must_ be <= MAX_COMMUNITY_FEE
  function setDefaultCommunityFee(uint16 newDefaultCommunityFee) external;

  /// @dev updates default fee for new pools
  /// @param newDefaultFee The new  fee, _must_ be <= MAX_DEFAULT_FEE
  function setDefaultFee(uint16 newDefaultFee) external;

  /// @dev updates default tickspacing for new pools
  /// @param newDefaultTickspacing The new tickspacing, _must_ be <= MAX_TICK_SPACING and >= MIN_TICK_SPACING
  function setDefaultTickspacing(int24 newDefaultTickspacing) external;

  /// @dev updates pluginFactory address
  /// @param newDefaultPluginFactory address of new plugin factory
  function setDefaultPluginFactory(address newDefaultPluginFactory) external;

  /// @notice Starts process of renounceOwnership. After that, a certain period
  /// of time must pass before the ownership renounce can be completed.
  function startRenounceOwnership() external;

  /// @notice Stops process of renounceOwnership and removes timer.
  function stopRenounceOwnership() external;
}
          

@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 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);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
        __ERC165_init_unchained();
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }
    uint256[50] private __gap;
}
          

@cryptoalgebra/integral-periphery/contracts/libraries/PositionKey.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

library PositionKey {
    /// @dev Returns the key of the position in the core library
    function compute(
        address owner,
        int24 bottomTick,
        int24 topTick
    ) internal pure returns (bytes32 key) {
        assembly {
            key := or(shl(24, or(shl(24, owner), and(bottomTick, 0xFFFFFF))), and(topTick, 0xFFFFFF))
        }
    }
}
          

@cryptoalgebra/integral-core/contracts/interfaces/pool/IAlgebraPoolActions.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissionless pool actions
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraPoolActions {
  /// @notice Sets the initial price for the pool
  /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
  /// @dev Initialization should be done in one transaction with pool creation to avoid front-running
  /// @param initialPrice The initial sqrt price of the pool as a Q64.96
  function initialize(uint160 initialPrice) external;

  /// @notice Adds liquidity for the given recipient/bottomTick/topTick position
  /// @dev The caller of this method receives a callback in the form of IAlgebraMintCallback# AlgebraMintCallback
  /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
  /// on bottomTick, topTick, the amount of liquidity, and the current price.
  /// @param leftoversRecipient The address which will receive potential surplus of paid tokens
  /// @param recipient The address for which the liquidity will be created
  /// @param bottomTick The lower tick of the position in which to add liquidity
  /// @param topTick The upper tick of the position in which to add liquidity
  /// @param liquidityDesired The desired amount of liquidity to mint
  /// @param data Any data that should be passed through to the callback
  /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
  /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
  /// @return liquidityActual The actual minted amount of liquidity
  function mint(
    address leftoversRecipient,
    address recipient,
    int24 bottomTick,
    int24 topTick,
    uint128 liquidityDesired,
    bytes calldata data
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityActual);

  /// @notice Collects tokens owed to a position
  /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
  /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
  /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
  /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
  /// @param recipient The address which should receive the fees collected
  /// @param bottomTick The lower tick of the position for which to collect fees
  /// @param topTick The upper tick of the position for which to collect fees
  /// @param amount0Requested How much token0 should be withdrawn from the fees owed
  /// @param amount1Requested How much token1 should be withdrawn from the fees owed
  /// @return amount0 The amount of fees collected in token0
  /// @return amount1 The amount of fees collected in token1
  function collect(
    address recipient,
    int24 bottomTick,
    int24 topTick,
    uint128 amount0Requested,
    uint128 amount1Requested
  ) external returns (uint128 amount0, uint128 amount1);

  /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
  /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
  /// @dev Fees must be collected separately via a call to #collect
  /// @param bottomTick The lower tick of the position for which to burn liquidity
  /// @param topTick The upper tick of the position for which to burn liquidity
  /// @param amount How much liquidity to burn
  /// @param data Any data that should be passed through to the plugin
  /// @return amount0 The amount of token0 sent to the recipient
  /// @return amount1 The amount of token1 sent to the recipient
  function burn(int24 bottomTick, int24 topTick, uint128 amount, bytes calldata data) external returns (uint256 amount0, uint256 amount1);

  /// @notice Swap token0 for token1, or token1 for token0
  /// @dev The caller of this method receives a callback in the form of IAlgebraSwapCallback#AlgebraSwapCallback
  /// @param recipient The address to receive the output of the swap
  /// @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
  /// @param amountRequired The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
  /// @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
  /// @param data Any data to be passed through to the callback. If using the Router it should contain SwapRouter#SwapCallbackData
  /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
  /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
  function swap(
    address recipient,
    bool zeroToOne,
    int256 amountRequired,
    uint160 limitSqrtPrice,
    bytes calldata data
  ) external returns (int256 amount0, int256 amount1);

  /// @notice Swap token0 for token1, or token1 for token0 with prepayment
  /// @dev The caller of this method receives a callback in the form of IAlgebraSwapCallback#AlgebraSwapCallback
  /// caller must send tokens in callback before swap calculation
  /// the actually sent amount of tokens is used for further calculations
  /// @param leftoversRecipient The address which will receive potential surplus of paid tokens
  /// @param recipient The address to receive the output of the swap
  /// @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
  /// @param amountToSell The amount of the swap, only positive (exact input) amount allowed
  /// @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
  /// @param data Any data to be passed through to the callback. If using the Router it should contain SwapRouter#SwapCallbackData
  /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
  /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
  function swapWithPaymentInAdvance(
    address leftoversRecipient,
    address recipient,
    bool zeroToOne,
    int256 amountToSell,
    uint160 limitSqrtPrice,
    bytes calldata data
  ) external returns (int256 amount0, int256 amount1);

  /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
  /// @dev The caller of this method receives a callback in the form of IAlgebraFlashCallback#AlgebraFlashCallback
  /// @dev All excess tokens paid in the callback are distributed to currently in-range liquidity providers as an additional fee.
  /// If there are no in-range liquidity providers, the fee will be transferred to the first active provider in the future
  /// @param recipient The address which will receive the token0 and token1 amounts
  /// @param amount0 The amount of token0 to send
  /// @param amount1 The amount of token1 to send
  /// @param data Any data to be passed through to the callback
  function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;
}
          

@cryptoalgebra/integral-core/contracts/libraries/FullMath.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
  /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
  /// @param a The multiplicand
  /// @param b The multiplier
  /// @param denominator The divisor
  /// @return result The 256-bit result
  /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
  function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
    unchecked {
      // 512-bit multiply [prod1 prod0] = a * b
      // Compute the product mod 2**256 and mod 2**256 - 1
      // then 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
      uint256 prod0 = a * b; // Least significant 256 bits of the product
      uint256 prod1; // Most significant 256 bits of the product
      assembly {
        let mm := mulmod(a, b, not(0))
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
      }

      // Make sure the result is less than 2**256.
      // Also prevents denominator == 0
      require(denominator > prod1);

      // Handle non-overflow cases, 256 by 256 division
      if (prod1 == 0) {
        assembly {
          result := div(prod0, denominator)
        }
        return result;
      }

      ///////////////////////////////////////////////
      // 512 by 256 division.
      ///////////////////////////////////////////////

      // Make division exact by subtracting the remainder from [prod1 prod0]
      // Compute remainder using mulmod
      // Subtract 256 bit remainder from 512 bit number
      assembly {
        let remainder := mulmod(a, b, denominator)
        prod1 := sub(prod1, gt(remainder, prod0))
        prod0 := sub(prod0, remainder)
      }

      // Factor powers of two out of denominator
      // Compute largest power of two divisor of denominator.
      // Always >= 1.
      uint256 twos = (0 - denominator) & denominator;
      // Divide denominator by power of two
      assembly {
        denominator := div(denominator, twos)
      }

      // Divide [prod1 prod0] by the factors of two
      assembly {
        prod0 := div(prod0, twos)
      }
      // Shift in bits from prod1 into prod0. For this we need
      // to flip `twos` such that it is 2**256 / twos.
      // If twos is zero, then it becomes one
      assembly {
        twos := add(div(sub(0, twos), twos), 1)
      }
      prod0 |= prod1 * twos;

      // 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
      // correct for four bits. That is, denominator * inv = 1 mod 2**4
      uint256 inv = (3 * denominator) ^ 2;
      // Now use 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.
      inv *= 2 - denominator * inv; // inverse mod 2**8
      inv *= 2 - denominator * inv; // inverse mod 2**16
      inv *= 2 - denominator * inv; // inverse mod 2**32
      inv *= 2 - denominator * inv; // inverse mod 2**64
      inv *= 2 - denominator * inv; // inverse mod 2**128
      inv *= 2 - denominator * inv; // 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 * inv;
      return result;
    }
  }

  /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
  /// @param a The multiplicand
  /// @param b The multiplier
  /// @param denominator The divisor
  /// @return result The 256-bit result
  function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
    unchecked {
      if (a == 0 || ((result = a * b) / a == b)) {
        require(denominator > 0);
        assembly {
          result := add(div(result, denominator), gt(mod(result, denominator), 0))
        }
      } else {
        result = mulDiv(a, b, denominator);
        if (mulmod(a, b, denominator) > 0) {
          require(result < type(uint256).max);
          result++;
        }
      }
    }
  }

  /// @notice Returns ceil(x / y)
  /// @dev division by 0 has unspecified behavior, and must be checked externally
  /// @param x The dividend
  /// @param y The divisor
  /// @return z The quotient, ceil(x / y)
  function unsafeDivRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
    assembly {
      z := add(div(x, y), gt(mod(x, y), 0))
    }
  }
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
          

Compiler Settings

{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":10,"enabled":true},"metadata":{"useLiteralContent":true,"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london"}
              

Contract ABI

[{"type":"error","name":"tickOutOfRange","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Deposit","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeesEarned","inputs":[{"type":"uint256","name":"amount0Earned","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1Earned","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Paused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"previousAdminRole","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"newAdminRole","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Snapshot","inputs":[{"type":"uint160","name":"sqrtPriceX96","internalType":"uint160","indexed":false},{"type":"uint256","name":"totalAmount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalAmount1","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalSupply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Unpaused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"Withdraw","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DEFAULT_ADMIN_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"STEER_FRACTION_OF_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"STRATEGIST_FRACTION_OF_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TOTAL_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"accruedFees0","inputs":[{"type":"string","name":"","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"accruedFees1","inputs":[{"type":"string","name":"","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"algebraMintCallback","inputs":[{"type":"uint256","name":"amount0","internalType":"uint256"},{"type":"uint256","name":"amount1","internalType":"uint256"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"algebraSwapCallback","inputs":[{"type":"int256","name":"amount0Wanted","internalType":"int256"},{"type":"int256","name":"amount1Wanted","internalType":"int256"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"collectFees","inputs":[{"type":"string","name":"feeIdentifier","internalType":"string"},{"type":"uint256","name":"amount0","internalType":"uint256"},{"type":"uint256","name":"amount1","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"shares","internalType":"uint256"},{"type":"uint256","name":"amount0Used","internalType":"uint256"},{"type":"uint256","name":"amount1Used","internalType":"uint256"}],"name":"deposit","inputs":[{"type":"uint256","name":"amount0Desired","internalType":"uint256"},{"type":"uint256","name":"amount1Desired","internalType":"uint256"},{"type":"uint256","name":"amount0Min","internalType":"uint256"},{"type":"uint256","name":"amount1Min","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amount0","internalType":"uint256"},{"type":"uint256","name":"amount1","internalType":"uint256"}],"name":"emergencyBurn","inputs":[{"type":"int24","name":"tickLower","internalType":"int24"},{"type":"int24","name":"tickUpper","internalType":"int24"},{"type":"uint128","name":"liquidity","internalType":"uint128"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"address[]","name":"","internalType":"address[]"},{"type":"string[]","name":"","internalType":"string[]"},{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"feeDetails","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"int24[]","name":"","internalType":"int24[]"},{"type":"int24[]","name":"","internalType":"int24[]"},{"type":"uint16[]","name":"","internalType":"uint16[]"}],"name":"getPositions","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getRoleAdmin","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"total0","internalType":"uint256"},{"type":"uint256","name":"total1","internalType":"uint256"}],"name":"getTotalAmounts","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_vaultManager","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"address","name":"_steer","internalType":"address"},{"type":"bytes","name":"_params","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int24","name":"","internalType":"int24"}],"name":"maxTickChange","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pause","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"poke","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAlgebraPool"}],"name":"pool","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"interfaceId","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tend","inputs":[{"type":"uint256","name":"totalWeight","internalType":"uint256"},{"type":"tuple","name":"newPositions","internalType":"struct HenjinMultiPositionLiquidityManager.TendLiquidityPositions","components":[{"type":"int24[]","name":"lowerTick","internalType":"int24[]"},{"type":"int24[]","name":"upperTick","internalType":"int24[]"},{"type":"uint16[]","name":"relativeWeight","internalType":"uint16[]"}]},{"type":"bytes","name":"timeSensitiveData","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"token0","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"token1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalFees0","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalFees1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"twapInterval","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unpause","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amount0","internalType":"uint256"},{"type":"uint256","name":"amount1","internalType":"uint256"}],"name":"withdraw","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"},{"type":"uint256","name":"amount0Min","internalType":"uint256"},{"type":"uint256","name":"amount1Min","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"}]}]
              

Contract Creation Code

0x60806040523480156200001157600080fd5b50600054610100900460ff166200002f5760005460ff161562000039565b62000039620000de565b620000a15760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b600054610100900460ff16158015620000c4576000805461ffff19166101011790555b8015620000d7576000805461ff00191690555b5062000102565b6000620000f630620000fc60201b6200252d1760201c565b15905090565b3b151590565b615df580620001126000396000f3fe608060405234801561001057600080fd5b506004361061020f5760003560e01c806301ffc9a71461021457806306fdde031461023c578063095ea7b3146102515780630dfe1681146102645780631053f8711461028457806316f0115b146102ab57806318160ddd146102be57806318178358146102d05780631c1f1936146102da5780631f2c5443146102e357806323b872dd146102fb578063246581f71461030e578063248a9ca3146103215780632c8958f6146103345780632f2ff15d1461034757806330faab2a1461035a578063313ce5671461036357806336568abe14610372578063365d0ed71461038557806339509351146103a75780633bec034a146103ba5780633c1d5df0146103e55780633dd657c5146104115780633f4ba83a146104245780635c975abb1461042c57806363db7eae1461043757806370a0823114610440578063802758601461046957806380814ffb146104805780638456cb591461048a5780638ec4daf6146104925780638f747bb8146104a557806391d14854146104b857806395d89b41146104cb578063a217fddf146104d3578063a457c2d7146104db578063a9059cbb146104ee578063abbffcb914610501578063b7ab45b614610529578063c4a7761e14610555578063d21220a71461055d578063d331bef714610570578063d547741f14610583578063dd62ed3e14610596578063ef095f8d146105cf575b600080fd5b6102276102223660046149b4565b6105d9565b60405190151581526020015b60405180910390f35b610244610656565b6040516102339190614a36565b61022761025f366004614a5e565b6106e8565b60fc54610277906001600160a01b031681565b6040516102339190614a8a565b60fe5461029890600160a01b900460020b81565b60405160029190910b8152602001610233565b60fe54610277906001600160a01b031681565b6035545b604051908152602001610233565b6102d86106fe565b005b6102c2611a0b81565b6102eb6108ac565b6040516102339493929190614ad9565b610227610309366004614b97565b610b31565b6102d861031c366004614cef565b610be2565b6102c261032f366004614d77565b611136565b6102d8610342366004614dd8565b61114b565b6102d8610355366004614e2a565b6111b3565b6102c2610d0581565b60405160128152602001610233565b6102d8610380366004614e2a565b6111d0565b610398610393366004614e5a565b61124e565b60405161023393929190614ea7565b6102276103b5366004614a5e565b6115cd565b6102c26103c8366004614edd565b805160208183018101805160ff8252928201919093012091525481565b60fe546103fc90600160b81b900463ffffffff1681565b60405163ffffffff9091168152602001610233565b6102d861041f366004614dd8565b611609565b6102d8611654565b60655460ff16610227565b6102c26105dc81565b6102c261044e366004614f11565b6001600160a01b031660009081526033602052604090205490565b610471611678565b60405161023393929190614f61565b6102c26101025481565b6102d86118a5565b6102d86104a0366004614fce565b6118c6565b6102d86104b33660046150cc565b611a83565b6102276104c6366004614e2a565b611f4b565b610244611f76565b6102c2600081565b6102276104e9366004614a5e565b611f85565b6102276104fc366004614a5e565b61201e565b61051461050f36600461521c565b61202b565b60408051928352602083019190915201610233565b6102c2610537366004614edd565b80516020818301810180516101008252928201919093012091525481565b61051461214d565b60fd54610277906001600160a01b031681565b61051461057e366004615267565b61240b565b6102d8610591366004614e2a565b612510565b6102c26105a43660046152a8565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b6102c26101035481565b60fb5460405163ab8b2a7360e01b81526001600160e01b0319831660048201526000916001600160a01b03169063ab8b2a7390602401602060405180830381865afa15801561062c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065091906152e6565b92915050565b60606036805461066590615301565b80601f016020809104026020016040519081016040528092919081815260200182805461069190615301565b80156106de5780601f106106b3576101008083540402835291602001916106de565b820191906000526020600020905b8154815290600101906020018083116106c157829003601f168201915b5050505050905090565b60006106f5338484612533565b50600192915050565b600061012d805480602002602001604051908101604052809291908181526020016000905b828210156107755760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff1690820152825260019092019101610723565b5050825192935060009150505b8181146108a75760006107cf8483815181106107a0576107a061533c565b6020026020010151600001518584815181106107be576107be61533c565b602002602001015160200151612657565b50505050905060008111156108965760fe5484516001600160a01b0390911690633b3bc70e908690859081106108075761080761533c565b6020026020010151600001518685815181106108255761082561533c565b60200260200101516020015160006040518463ffffffff1660e01b815260040161085193929190615352565b60408051808303816000875af115801561086f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108939190615387565b50505b506108a0816153c1565b9050610782565b505050565b604080516002808252606082810190935260009291829182918591816020016020820280368337505060fb5460408051638da5cb5b60e01b815290519394506001600160a01b0390911692638da5cb5b925060048083019260209291908290030181865afa158015610922573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094691906153e7565b816000815181106109595761095961533c565b6001600160a01b03928316602091820292909201015260fb5460405163015a1d6760e31b8152911690630ad0eb3890610996903090600401614a8a565b602060405180830381865afa1580156109b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d791906153e7565b816001815181106109ea576109ea61533c565b6001600160a01b03929092166020928302919091019091015260408051600280825260608201909252600091816020015b6060815260200190600190039081610a1b5790505090506040518060400160405280600a81526020016953544545525f4645455360b01b81525081600081518110610a6857610a6861533c565b60200260200101819052506040518060400160405280600f81526020016e535452415445474953545f4645455360881b81525081600181518110610aae57610aae61533c565b602090810291909101015260408051600280825260608201909252600091816020016020820280368337019050509050611a0b81600081518110610af457610af461533c565b602002602001018181525050610d0581600181518110610b1657610b1661533c565b60209081029190910101526105dc9792965090945092509050565b6000610b3e848484612701565b6001600160a01b038416600090815260346020908152604080832033845290915290205482811015610bc85760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610bd58533858403612533565b60019150505b9392505050565b600054610100900460ff16610bfd5760005460ff1615610c01565b303b155b610c645760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bbf565b600054610100900460ff16158015610c86576000805461ffff19166101011790555b60008060008085806020019051810190610ca09190615404565b9350935093509350600960020b8260020b138015610cc357506107d1600283900b125b610ccc57600080fd5b601e63ffffffff8216118015610ce9575061025863ffffffff8216105b610cf257600080fd5b826001600160a01b0316846001600160a01b031610610d1057600080fd5b60fb8054336001600160a01b03199182161790915560fc805482166001600160a01b0387811691821790925560fd805490931691861691821790925560405163d9a641e160e01b8152600481019290925260248201526000907342b08e7a9211482d3643a126a7df1895448d35099063d9a641e190604401602060405180830381865afa158015610da5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc991906153e7565b90506001600160a01b038116610dde57600080fd5b60fe80546001600160a01b0319166001600160a01b0383169081179091556040805163ef01df4f60e01b815290516000929163ef01df4f9160048083019260209291908290030181865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e91906153e7565b90506001600160a01b038116610e7357600080fd5b6040805160028082526060820183526000926020830190803683370190505090508381600081518110610ea857610ea861533c565b63ffffffff90921660209283029190910190910152604051639d3a524160e01b81526001600160a01b03831690639d3a524190610ee9908490600401615461565b600060405180830381865afa158015610f06573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f2e919081019061551c565b505060fb60009054906101000a90046001600160a01b03166001600160a01b031663d0fb02036040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa791906153e7565b61010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506000611045336001600160a01b031663381193696040518163ffffffff1660e01b8152600401602060405180830381865afa158015611011573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103591906155e7565b611040906001615600565b6128bd565b905061108f8160405160200161105b9190615618565b6040516020818303038152906040528260405160200161107b9190615654565b6040516020818303038152906040526129c2565b6110976129fb565b61109f612a34565b6110b7600080516020615da08339815191528c612a73565b6110cf600080516020615d608339815191528e612a73565b505060fe805463ffffffff909416600160b81b0263ffffffff60b81b1962ffffff909616600160a01b029590951666ffffffffffffff60a01b19909416939093179390931790915550508215915061112f9050576000805461ff00191690555b5050505050565b600090815260c9602052604090206001015490565b60fe546001600160a01b0316331461116257600080fd5b60fe54600160e01b900460ff1661117857600080fd5b60fe805460ff60e01b1916905560008080861315611194578591505b60008513156111a05750835b6111ab338383612a7d565b505050505050565b6111bc82611136565b6111c68133612ab7565b6108a78383612b1b565b6001600160a01b03811633146112405760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610bbf565b61124a8282612ba1565b5050565b600080600061125f60655460ff1690565b1561127c5760405162461bcd60e51b8152600401610bbf90615683565b6001600160a01b03841661128f57600080fd5b60fb546040516367a44ca360e01b81526000916001600160a01b0316906367a44ca3906112c0903090600401614a8a565b600060405180830381865afa1580156112dd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113059190810190615701565b519050600181600481111561131c5761131c6157c7565b148061133957506003816004811115611337576113376157c7565b145b61134257600080fd5b61134a6106fe565b600061135560355490565b905060008061136261214d565b9150915082600014806113755750600082115b806113805750600081115b61138c5761138c6157dd565b826113b8578b95508a94506113a18686612c08565b9650620f42408710156113b357600080fd5b6114b4565b816113dc576113c88b8483612c1e565b96506113d5878285612cb4565b94506114b4565b80611400576113ec8c8484612c1e565b96506113f9878385612cb4565b95506114b4565b600061141e61140f838f6157f3565b611419858f6157f3565b612d34565b9050600081116114545760405162461bcd60e51b81526020600482015260016024820152604360f81b6044820152606401610bbf565b81611460600183615812565b61146a919061583f565b611475906001615600565b965082611483600183615812565b61148d919061583f565b611498906001615600565b9550816114a6828686612c1e565b6114b0919061583f565b9750505b600087116114c157600080fd5b898610156114f55760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610bbf565b888510156115295760405162461bcd60e51b81526020600482015260016024820152603160f81b6044820152606401610bbf565b85156115475760fc54611547906001600160a01b0316333089612d43565b84156115655760fd54611565906001600160a01b0316333088612d43565b61156f8888612dae565b876001600160a01b0316336001600160a01b03167f4e2ca0515ed1aef1395f66b5303bb5d6f1bf9d61a353fa53f73f8ac9973fa9f68989896040516115b693929190614ea7565b60405180910390a350505050955095509592505050565b3360008181526034602090815260408083206001600160a01b038716845290915281205490916106f5918590611604908690615600565b612533565b60fe546001600160a01b0316331461162057600080fd5b60fe54600160d81b900460ff1661163657600080fd5b60fe805460ff60d81b1916905561164e338585612a7d565b50505050565b600080516020615da083398151915261166d8133612ab7565b611675612e7b565b50565b61012d5460408051606081810183528082526020820181905291810182905290918291829190816001600160401b038111156116b6576116b6614bd8565b6040519080825280602002602001820160405280156116df578160200160208202803683370190505b508152816001600160401b038111156116fa576116fa614bd8565b604051908082528060200260200182016040528015611723578160200160208202803683370190505b506020820152816001600160401b0381111561174157611741614bd8565b60405190808252806020026020018201604052801561176a578160200160208202803683370190505b50604082015260005b82811461188b5761012d818154811061178e5761178e61533c565b6000918252602090912001548251805160029290920b91839081106117b5576117b561533c565b602002602001019060020b908160020b8152505061012d81815481106117dd576117dd61533c565b9060005260206000200160000160039054906101000a900460020b8260200151828151811061180e5761180e61533c565b602002602001019060020b908160020b8152505061012d81815481106118365761183661533c565b9060005260206000200160000160069054906101000a900461ffff16826040015182815181106118685761186861533c565b61ffff90921660209283029190910190910152611884816153c1565b9050611773565b508051602082015160409092015190969195509350915050565b600080516020615da08339815191526118be8133612ab7565b611675612f08565b60655460ff16156118e95760405162461bcd60e51b8152600401610bbf90615683565b610101546001600160a01b0316331461190157600080fd5b6101015460405163f3aefd5560e01b81526000916001600160a01b03169063f3aefd55906119359030908890600401615853565b602060405180830381865afa158015611952573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197691906153e7565b9050600060ff8560405161198a9190615877565b90815260200160405180910390205490506000610100866040516119ae9190615877565b9081526040519081900360200190205490508415611a21576119d08583615812565b60ff876040516119e09190615877565b908152602001604051809103902081905550846101026000828254611a059190615812565b909155505060fc54611a21906001600160a01b03168487612f60565b83156111ab57611a318482615812565b61010087604051611a429190615877565b908152602001604051809103902081905550836101036000828254611a679190615812565b909155505060fd546111ab906001600160a01b03168486612f60565b600080516020615d60833981519152611a9c8133612ab7565b60655460ff1615611abf5760405162461bcd60e51b8152600401610bbf90615683565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152612710861115611b0f57600080fd5b611b1b83850185614e2a565b6001600160a01b03908116602084015290825260fe54604080516339db007960e21b81529051919092169163e76c01e49160048083019260c09291908290030181865afa158015611b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b949190615893565b5050505060020b606083018190526001600160a01b039091166040830152611bbb90612f90565b611bc66001806131c8565b50508451516080820181905215611d0a57611be461012d60006148de565b60005b8160800151811015611d08576000604051806060016040528088600001518481518110611c1657611c1661533c565b602002602001015160020b815260200188602001518481518110611c3c57611c3c61533c565b602002602001015160020b815260200188604001518481518110611c6257611c6261533c565b60209081029190910181015161ffff90811690925261012d805460018101825560009190915283517f193a3ae4da5049eb74cee39e4cf5827f7ce7b1d1d1775ef1c6311eb60558e6d5909101805492850151604090950151909316600160301b0261ffff60301b1962ffffff95861663010000000265ffffffffffff19909416959092169490941791909117169190911790555080611d00816153c1565b915050611be7565b505b805115611e965780516000811360e0830152600160ff1b1415611d2c57600080fd5b60fe805460ff60e01b198116600160e01b1790915560e08201516001600160a01b039091169063128acb0890309080611d6f578451611d6a9061591b565b611d72565b84515b60208601516040516001600160e01b031960e087901b1681526001600160a01b0394851660048201529215156024840152604483019190915291909116606482015260a06084820152600060a482015260c40160408051808303816000875af1158015611de3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e079190615387565b505060fe60009054906101000a90046001600160a01b03166001600160a01b031663e76c01e46040518163ffffffff1660e01b815260040160c060405180830381865afa158015611e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e809190615893565b5050506001600160a01b03909216604084015250505b611e9e613789565b60a0820152611eab61380f565b60c08201819052604082015160a08301517f2fd4737bac0995700ade358ae308c92f20ee63884dfbe658bf55d2f99f60f07792611ee760355490565b604080516001600160a01b039095168552602085019390935291830152606082015260800160405180910390a185156111ab576111ab8160400151611f338360a0015189612710612c1e565b611f448460c001518a612710612c1e565b8451613845565b600091825260c9602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60606037805461066590615301565b3360009081526034602090815260408083206001600160a01b0386168452909152812054828110156120075760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610bbf565b6120143385858403612533565b5060019392505050565b60006106f5338484612701565b600080600080516020615da08339815191526120478133612ab7565b60fe54604051631d9de38760e11b81526001600160a01b0390911690633b3bc70e9061207b90899089908990600401615352565b60408051808303816000875af1158015612099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bd9190615387565b60fe546040516309e3d67b60e31b81529295509093506001600160a01b031690634f1eb3d8906121009030908a908a906001600160801b03908190600401615938565b60408051808303816000875af115801561211e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121429190615975565b505050935093915050565b600080612158613789565b915061216261380f565b9050600061012d805480602002602001604051908101604052809291908181526020016000905b828210156121db5760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff1690820152825260019092019101612189565b505050509050600060fe60009054906101000a90046001600160a01b03166001600160a01b031663e76c01e46040518163ffffffff1660e01b815260040160c060405180830381865afa158015612236573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225a9190615893565b505061010154604051634867e20360e11b81529495506000946001600160a01b0390911693506390cfc406925061229691503090600401614a8a565b602060405180830381865afa1580156122b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d791906155e7565b905060006122e782612710615812565b845190915060005b81811461240157600080600061232e8985815181106123105761231061533c565b6020026020010151600001518a86815181106107be576107be61533c565b6001600160801b031694506001600160801b03169450505092506000806123a18a6123758d89815181106123645761236461533c565b602002602001015160000151613d5d565b61239b8e8a8151811061238a5761238a61533c565b602002602001015160200151613d5d565b88614038565b915091506123b28489612710612c1e565b6123bc9083615600565b6123c6908e615600565b9c506123d58389612710612c1e565b6123df9082615600565b6123e9908d615600565b9b505050505050806123fa906153c1565b90506122ef565b5050505050509091565b6000806001600160a01b03831661242157600080fd5b6000861161242e57600080fd5b600061243960355490565b905061244533886140d4565b612457612450613789565b8883612c1e565b925061246461245061380f565b915060008061247389846131c8565b90925090506124828286615600565b945061248e8185615600565b93508785101561249d57600080fd5b868410156124aa57600080fd5b6124b5868686612a7d565b856001600160a01b0316336001600160a01b03167febff2602b3f468259e1e99f613fed6691f3a6526effe6ef3e768ba7ae7a36c4f8b88886040516124fc93929190614ea7565b60405180910390a350505094509492505050565b61251982611136565b6125238133612ab7565b6108a78383612ba1565b3b151590565b6001600160a01b0383166125955760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610bbf565b6001600160a01b0382166125f65760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610bbf565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60fe5460009081908190819081906001600160a01b031663514ea4bf61268f308a8a601892831b62ffffff9283161790921b91161790565b6040518263ffffffff1660e01b81526004016126ad91815260200190565b60a060405180830381865afa1580156126ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ee91906159a4565b939b929a50909850965090945092505050565b6001600160a01b0383166127655760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610bbf565b6001600160a01b0382166127c75760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610bbf565b6001600160a01b0383166000908152603360205260409020548181101561283f5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610bbf565b6001600160a01b03808516600090815260336020526040808220858503905591851681529081208054849290612876908490615600565b92505081905550826001600160a01b0316846001600160a01b0316600080516020615d80833981519152846040516128b091815260200190565b60405180910390a361164e565b6060816128e15750506040805180820190915260018152600360fc1b602082015290565b8160005b811561290b57806128f5816153c1565b91506129049050600a8361583f565b91506128e5565b6000816001600160401b0381111561292557612925614bd8565b6040519080825280601f01601f19166020018201604052801561294f576020820181803683370190505b5090505b84156129ba57612964600183615812565b9150612971600a866159ed565b61297c906030615600565b60f81b8183815181106129915761299161533c565b60200101906001600160f81b031916908160001a9053506129b3600a8661583f565b9450612953565b949350505050565b600054610100900460ff166129e95760405162461bcd60e51b8152600401610bbf90615a01565b6129f1614210565b61124a8282614237565b600054610100900460ff16612a225760405162461bcd60e51b8152600401610bbf90615a01565b612a2a614210565b612a32614285565b565b600054610100900460ff16612a5b5760405162461bcd60e51b8152600401610bbf90615a01565b612a63614210565b612a6b614210565b612a32614210565b61124a8282612b1b565b8115612a9a5760fc54612a9a906001600160a01b03168484612f60565b80156108a75760fd546108a7906001600160a01b03168483612f60565b612ac18282611f4b565b61124a57612ad9816001600160a01b031660146142b8565b612ae48360206142b8565b604051602001612af5929190615a4c565b60408051601f198184030181529082905262461bcd60e51b8252610bbf91600401614a36565b612b258282611f4b565b61124a57600082815260c9602090815260408083206001600160a01b03851684529091529020805460ff19166001179055612b5d3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b612bab8282611f4b565b1561124a57600082815260c9602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000818311612c175781610bdb565b5090919050565b60008383028160001985870982811083820303915050808411612c4057600080fd5b80612c5057508290049050610bdb565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b6000831580612cd557505082820282848281612cd257612cd2615829565b04145b15612cf65760008211612ce757600080fd5b81810490829006151501610bdb565b612d01848484612c1e565b905060008280612d1357612d13615829565b8486091115610bdb576000198110612d2a57600080fd5b6001019392505050565b6000818310612c175781610bdb565b6040516001600160a01b038085166024830152831660448201526064810182905261164e9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614453565b6001600160a01b038216612e045760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610bbf565b8060356000828254612e169190615600565b90915550506001600160a01b03821660009081526033602052604081208054839290612e43908490615600565b90915550506040518181526001600160a01b03831690600090600080516020615d808339815191529060200160405180910390a35050565b60655460ff16612ec45760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bbf565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b604051612efe9190614a8a565b60405180910390a1565b60655460ff1615612f2b5760405162461bcd60e51b8152600401610bbf90615683565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612ef13390565b6040516001600160a01b0383166024820152604481018290526108a790849063a9059cbb60e01b90606401612d77565b60fe5460408051600280825260608201835263ffffffff600160b81b85041693600160a01b9004810b9260009291906020830190803683370190505090508281600081518110612fe257612fe261533c565b63ffffffff90921660209283029190910182015260fe546040805163ef01df4f60e01b815290516001600160a01b039092169263ef01df4f926004808401938290030181865afa15801561303a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305e91906153e7565b6001600160a01b0316639d3a5241826040518263ffffffff1660e01b81526004016130899190615461565b600060405180830381865afa9250505080156130c757506040513d6000823e601f3d908101601f191682016040526130c4919081019061551c565b60015b6130d35761164e612f08565b6000826000815181106130e8576130e861533c565b6020026020010151836001815181106131035761310361533c565b60200260200101516131159190615abb565b90506000613127600388900b83615b0b565b905060008260060b12801561314d575061314763ffffffff881683615b49565b60060b15155b15613160578061315c81615b6b565b9150505b61316a8682615b8f565b60020b8860020b1315801561318e57506131848682615bd6565b60020b8860020b12155b6131be5760405162461bcd60e51b81526020600482015260016024820152602b60f91b6044820152606401610bbf565b5050505050505050565b600080600061012d805480602002602001604051908101604052809291908181526020016000905b828210156132425760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff16908201528252600190920191016131f0565b50505050905060008060008351905060005b81811461344157600085828151811061326f5761326f61533c565b602002602001015160000151905060008683815181106132915761329161533c565b60200260200101516020015190506000806132ac8484612657565b5050505090506132bd818e8e612c1e565b9150506001600160801b0381161561342d5760fe54604051631d9de38760e11b815260009182916001600160a01b0390911690633b3bc70e9061330890889088908890600401615352565b60408051808303816000875af1158015613326573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061334a9190615387565b60fe546040516309e3d67b60e31b815292945090925060009182916001600160a01b031690634f1eb3d8906133929030908b908b906001600160801b03908190600401615938565b60408051808303816000875af11580156133b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d49190615975565b6001600160801b0391821693501690506133ee848f615600565b9d506133fa838e615600565b9c506134068483615812565b613410908c615600565b9a5061341c8382615812565b613426908b615600565b9950505050505b5050508061343a906153c1565b9050613254565b5060408051848152602081018490527fc28ad1de9c0c32e5394ba60323e44d8d9536312236a47231772e448a3e49de42910160405180910390a161010154604051639af608c960e01b81526000916001600160a01b031690639af608c9906134ad903090600401614a8a565b600060405180830381865afa1580156134ca573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134f29190810190615c14565b61010154604051634867e20360e11b81529192506000916001600160a01b03909116906390cfc40690613529903090600401614a8a565b602060405180830381865afa158015613546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356a91906155e7565b90506000808615613585576135828784612710612c1e565b91505b851561359b576135988684612710612c1e565b90505b8161010260008282546135ae9190615600565b925050819055508061010360008282546135c89190615600565b909155506135d890508288615812565b96506135e48187615812565b955060005b8451811461374b5761361b838683815181106136075761360761533c565b602002602001015160200151612710612c1e565b60ff86838151811061362f5761362f61533c565b6020026020010151600001516040516136489190615877565b9081526020016040518091039020546136619190615600565b60ff8683815181106136755761367561533c565b60200260200101516000015160405161368e9190615877565b9081526020016040518091039020819055506136b6828683815181106136075761360761533c565b6101008683815181106136cb576136cb61533c565b6020026020010151600001516040516136e49190615877565b9081526020016040518091039020546136fd9190615600565b6101008683815181106137125761371261533c565b60200260200101516000015160405161372b9190615877565b90815260405190819003602001902055613744816153c1565b90506135e9565b50613757878d8d612c1e565b613761908b615600565b995061376e868d8d612c1e565b613778908a615600565b985050505050505050509250929050565b6101025460fc546040516370a0823160e01b8152600092916001600160a01b0316906370a08231906137bf903090600401614a8a565b602060405180830381865afa1580156137dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061380091906155e7565b61380a9190615812565b905090565b6101035460fd546040516370a0823160e01b8152600092916001600160a01b0316906370a08231906137bf903090600401614a8a565b600061012d805480602002602001604051908101604052809291908181526020016000905b828210156138bc5760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff169082015282526001909201910161386a565b505050509050606080600080600085519050806001600160401b038111156138e6576138e6614bd8565b60405190808252806020026020018201604052801561390f578160200160208202803683370190505b509450806001600160401b0381111561392a5761392a614bd8565b604051908082528060200260200182016040528015613953578160200160208202803683370190505b50935060005b818114613aee5760018110613a175786818151811061397a5761397a61533c565b60200260200101516000015160020b876001836139979190615812565b815181106139a7576139a761533c565b60200260200101516000015160020b128015613a0e57508681815181106139d0576139d061533c565b60200260200101516020015160020b876001836139ed9190615812565b815181106139fd576139fd61533c565b60200260200101516020015160020b125b613a1757600080fd5b600080613a828d613a338b86815181106123645761236461533c565b613a488c878151811061238a5761238a61533c565b8c8781518110613a5a57613a5a61533c565b60200260200101516040015161ffff16670de0b6b3a7640000613a7d91906157f3565b614038565b9150915081888481518110613a9957613a9961533c565b60200260200101818152505080878481518110613ab857613ab861533c565b6020908102919091010152613acd8287615600565b9550613ad98186615600565b9450505080613ae7906153c1565b9050613959565b5060005b818114613cc1576000808511613b09576000613b2d565b613b2d878381518110613b1e57613b1e61533c565b60200260200101518c87612c1e565b90506000808511613b3f576000613b54565b613b54878481518110613b1e57613b1e61533c565b90506000613b8d8e613b718c87815181106123645761236461533c565b613b868d888151811061238a5761238a61533c565b8686614528565b90506001600160801b03811615613cad5760fe805460ff60d81b198116600160d81b179091558a516001600160a01b039091169063aafe29c090309081908e9089908110613bdd57613bdd61533c565b6020026020010151600001518e8981518110613bfb57613bfb61533c565b60209081029190910181015101516040516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152600290810b60448401520b60648201526001600160801b038416608482015260c060a4820152600060c482015260e4016060604051808303816000875af1158015613c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ca99190615cff565b5050505b50505080613cba906153c1565b9050613af2565b506000871315613d0c576064613cd860058a6157f3565b613ce2919061583f565b613cea61380f565b10613d075760405162461bcd60e51b8152600401610bbf90615d2d565b613d51565b6000871215613d51576064613d2260058b6157f3565b613d2c919061583f565b613d34613789565b10613d515760405162461bcd60e51b8152600401610bbf90615d2d565b50505050505050505050565b6000600282900b60171d62ffffff818401821816620d89e8811115613d9557604051633c10250f60e01b815260040160405180910390fd5b600160801b6001821615613db657506ffffcb933bd6fad37aa2d162d1a5940015b6002821615613dd5576ffff97272373d413259a46990580e213a0260801c5b6004821615613df4576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615613e13576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615613e32576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615613e51576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615613e70576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615613e8f576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615613eaf576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615613ecf576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615613eef576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615613f0f576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615613f2f576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615613f4f576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615613f6f576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615613f8f576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615613fb0576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615613fd0576e5d6af8dedb81196699c329225ee6040260801c5b6204000082106140165762040000821615613ff9576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614016576b048a170391f7dc42444e8fa20260801c5b60008560020b131561402757600019045b63ffffffff0160201c949350505050565b600080836001600160a01b0316856001600160a01b03161115614059579293925b846001600160a01b0316866001600160a01b0316116140845761407d8585856145ec565b91506140cb565b836001600160a01b0316866001600160a01b031610156140bd576140a98685856145ec565b91506140b685878561465b565b90506140cb565b6140c885858561465b565b90505b94509492505050565b6001600160a01b0382166141345760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610bbf565b6001600160a01b038216600090815260336020526040902054818110156141a85760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610bbf565b6001600160a01b03831660009081526033602052604081208383039055603580548492906141d7908490615812565b90915550506040518281526000906001600160a01b03851690600080516020615d808339815191529060200160405180910390a3505050565b600054610100900460ff16612a325760405162461bcd60e51b8152600401610bbf90615a01565b600054610100900460ff1661425e5760405162461bcd60e51b8152600401610bbf90615a01565b81516142719060369060208501906148fc565b5080516108a79060379060208401906148fc565b600054610100900460ff166142ac5760405162461bcd60e51b8152600401610bbf90615a01565b6065805460ff19169055565b606060006142c78360026157f3565b6142d2906002615600565b6001600160401b038111156142e9576142e9614bd8565b6040519080825280601f01601f191660200182016040528015614313576020820181803683370190505b509050600360fc1b8160008151811061432e5761432e61533c565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061435d5761435d61533c565b60200101906001600160f81b031916908160001a90535060006143818460026157f3565b61438c906001615600565b90505b6001811115614404576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106143c0576143c061533c565b1a60f81b8282815181106143d6576143d661533c565b60200101906001600160f81b031916908160001a90535060049490941c936143fd81615d48565b905061438f565b508315610bdb5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610bbf565b60006144a8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661469e9092919063ffffffff16565b90508051600014806144c95750808060200190518101906144c991906152e6565b6108a75760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bbf565b6000836001600160a01b0316856001600160a01b03161115614548579293925b846001600160a01b0316866001600160a01b0316116145735761456c8585856146ad565b90506145e3565b836001600160a01b0316866001600160a01b031610156145d557600061459a8786866146ad565b905060006145a9878986614710565b9050806001600160801b0316826001600160801b0316106145ca57806145cc565b815b925050506145e3565b6145e0858584614710565b90505b95945050505050565b6000826001600160a01b0316846001600160a01b0316111561460c579192915b836001600160a01b0316614645606060ff16846001600160801b0316901b8686036001600160a01b0316866001600160a01b0316612c1e565b8161465257614652615829565b04949350505050565b6000826001600160a01b0316846001600160a01b0316111561467b579192915b6129ba826001600160801b03168585036001600160a01b0316600160601b612c1e565b60606129ba848460008561474d565b6000826001600160a01b0316846001600160a01b031611156146cd579192915b60006146f0856001600160a01b0316856001600160a01b0316600160601b612c1e565b90506145e361470b84838888036001600160a01b0316612c1e565b614828565b6000826001600160a01b0316846001600160a01b03161115614730579192915b6129ba61470b83600160601b8787036001600160a01b0316612c1e565b6060824710156147ae5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610bbf565b600080866001600160a01b031685876040516147ca9190615877565b60006040518083038185875af1925050503d8060008114614807576040519150601f19603f3d011682016040523d82523d6000602084013e61480c565b606091505b509150915061481d87838387614843565b979650505050505050565b806001600160801b038116811461483e57600080fd5b919050565b606083156148af5782516148a8576001600160a01b0385163b6148a85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bbf565b50816129ba565b6129ba83838151156148c45781518083602001fd5b8060405162461bcd60e51b8152600401610bbf9190614a36565b50805460008255906000526020600020908101906116759190614980565b82805461490890615301565b90600052602060002090601f01602090048101928261492a5760008555614970565b82601f1061494357805160ff1916838001178555614970565b82800160010185558215614970579182015b82811115614970578251825591602001919060010190614955565b5061497c92915061499f565b5090565b5b8082111561497c5780546001600160401b0319168155600101614981565b5b8082111561497c57600081556001016149a0565b6000602082840312156149c657600080fd5b81356001600160e01b031981168114610bdb57600080fd5b60005b838110156149f95781810151838201526020016149e1565b8381111561164e5750506000910152565b60008151808452614a228160208601602086016149de565b601f01601f19169290920160200192915050565b602081526000610bdb6020830184614a0a565b6001600160a01b038116811461167557600080fd5b60008060408385031215614a7157600080fd5b8235614a7c81614a49565b946020939093013593505050565b6001600160a01b0391909116815260200190565b600081518084526020808501945080840160005b83811015614ace57815187529582019590820190600101614ab2565b509495945050505050565b600060808201868352602060808185015281875180845260a086019150828901935060005b81811015614b235784516001600160a01b031683529383019391830191600101614afe565b5050848103604086015286518082528282019350600581901b8201830183890160005b83811015614b7457601f19858403018752614b62838351614a0a565b96860196925090850190600101614b46565b50508681036060880152614b888189614a9e565b9b9a5050505050505050505050565b600080600060608486031215614bac57600080fd5b8335614bb781614a49565b92506020840135614bc781614a49565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715614c1057614c10614bd8565b60405290565b60405160c081016001600160401b0381118282101715614c1057614c10614bd8565b604080519081016001600160401b0381118282101715614c1057614c10614bd8565b604051601f8201601f191681016001600160401b0381118282101715614c8257614c82614bd8565b604052919050565b60006001600160401b03821115614ca357614ca3614bd8565b50601f01601f191660200190565b6000614cc4614cbf84614c8a565b614c5a565b9050828152838383011115614cd857600080fd5b828260208301376000602084830101529392505050565b60008060008060808587031215614d0557600080fd5b8435614d1081614a49565b93506020850135614d2081614a49565b92506040850135614d3081614a49565b915060608501356001600160401b03811115614d4b57600080fd5b8501601f81018713614d5c57600080fd5b614d6b87823560208401614cb1565b91505092959194509250565b600060208284031215614d8957600080fd5b5035919050565b60008083601f840112614da257600080fd5b5081356001600160401b03811115614db957600080fd5b602083019150836020828501011115614dd157600080fd5b9250929050565b60008060008060608587031215614dee57600080fd5b843593506020850135925060408501356001600160401b03811115614e1257600080fd5b614e1e87828801614d90565b95989497509550505050565b60008060408385031215614e3d57600080fd5b823591506020830135614e4f81614a49565b809150509250929050565b600080600080600060a08688031215614e7257600080fd5b853594506020860135935060408601359250606086013591506080860135614e9981614a49565b809150509295509295909350565b9283526020830191909152604082015260600190565b600082601f830112614ece57600080fd5b610bdb83833560208501614cb1565b600060208284031215614eef57600080fd5b81356001600160401b03811115614f0557600080fd5b6129ba84828501614ebd565b600060208284031215614f2357600080fd5b8135610bdb81614a49565b600081518084526020808501945080840160005b83811015614ace57815160020b87529582019590820190600101614f42565b606081526000614f746060830186614f2e565b602083820381850152614f878287614f2e565b8481036040860152855180825282870193509082019060005b81811015614fc057845161ffff1683529383019391830191600101614fa0565b509098975050505050505050565b600080600060608486031215614fe357600080fd5b83356001600160401b03811115614ff957600080fd5b61500586828701614ebd565b9660208601359650604090950135949350505050565b60006001600160401b0382111561503457615034614bd8565b5060051b60200190565b8060020b811461167557600080fd5b600082601f83011261505e57600080fd5b8135602061506e614cbf8361501b565b82815260059290921b8401810191818101908684111561508d57600080fd5b8286015b848110156150b15780356150a48161503e565b8352918301918301615091565b509695505050505050565b61ffff8116811461167557600080fd5b600080600080606085870312156150e257600080fd5b843593506020808601356001600160401b038082111561510157600080fd5b908701906060828a03121561511557600080fd5b61511d614bee565b82358281111561512c57600080fd5b6151388b82860161504d565b825250838301358281111561514c57600080fd5b6151588b82860161504d565b858301525060408301358281111561516f57600080fd5b80840193505089601f84011261518457600080fd5b8235615192614cbf8261501b565b81815260059190911b8401850190858101908c8311156151b157600080fd5b948601945b828610156151d85785356151c9816150bc565b825294860194908601906151b6565b8060408501525050508096505060408801359250808311156151f957600080fd5b5050614e1e87828801614d90565b6001600160801b038116811461167557600080fd5b60008060006060848603121561523157600080fd5b833561523c8161503e565b9250602084013561524c8161503e565b9150604084013561525c81615207565b809150509250925092565b6000806000806080858703121561527d57600080fd5b843593506020850135925060408501359150606085013561529d81614a49565b939692955090935050565b600080604083850312156152bb57600080fd5b82356152c681614a49565b91506020830135614e4f81614a49565b8051801515811461483e57600080fd5b6000602082840312156152f857600080fd5b610bdb826152d6565b600181811c9082168061531557607f821691505b6020821081141561533657634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b600293840b81529190920b60208201526001600160801b03909116604082015260806060820181905260009082015260a00190565b6000806040838503121561539a57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b60006000198214156153d5576153d56153ab565b5060010190565b805161483e81614a49565b6000602082840312156153f957600080fd5b8151610bdb81614a49565b6000806000806080858703121561541a57600080fd5b845161542581614a49565b602086015190945061543681614a49565b60408601519093506154478161503e565b606086015190925063ffffffff8116811461529d57600080fd5b6020808252825182820181905260009190848201906040850190845b8181101561549f57835163ffffffff168352928401929184019160010161547d565b50909695505050505050565b600082601f8301126154bc57600080fd5b815160206154cc614cbf8361501b565b82815260059290921b840181019181810190868411156154eb57600080fd5b8286015b848110156150b15780516001600160581b038116811461550f5760008081fd5b83529183019183016154ef565b6000806040838503121561552f57600080fd5b82516001600160401b038082111561554657600080fd5b818501915085601f83011261555a57600080fd5b8151602061556a614cbf8361501b565b82815260059290921b8401810191818101908984111561558957600080fd5b948201945b838610156155b75785518060060b81146155a85760008081fd5b8252948201949082019061558e565b918801519196509093505050808211156155d057600080fd5b506155dd858286016154ab565b9150509250929050565b6000602082840312156155f957600080fd5b5051919050565b60008219821115615613576156136153ab565b500190565b7353544545525f414c47454252415f5641554c545f60601b8152600082516156478160148501602087016149de565b9190910160140192915050565b6629aa22a2a920ab60c91b8152600082516156768160078501602087016149de565b9190910160070192915050565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b80516005811061483e57600080fd5b600082601f8301126156cd57600080fd5b81516156db614cbf82614c8a565b8181528460208386010111156156f057600080fd5b6129ba8260208301602087016149de565b60006020828403121561571357600080fd5b81516001600160401b038082111561572a57600080fd5b9083019060c0828603121561573e57600080fd5b615746614c16565b61574f836156ad565b8152602083015160208201526040830151604082015260608301518281111561577757600080fd5b615783878286016156bc565b606083015250615795608084016153dc565b608082015260a0830151828111156157ac57600080fd5b6157b8878286016156bc565b60a08301525095945050505050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b600081600019048311821515161561580d5761580d6153ab565b500290565b600082821015615824576158246153ab565b500390565b634e487b7160e01b600052601260045260246000fd5b60008261584e5761584e615829565b500490565b6001600160a01b03831681526040602082018190526000906129ba90830184614a0a565b600082516158898184602087016149de565b9190910192915050565b60008060008060008060c087890312156158ac57600080fd5b86516158b781614a49565b60208801519096506158c88161503e565b60408801519095506158d9816150bc565b606088015190945060ff811681146158f057600080fd5b6080880151909350615901816150bc565b915061590f60a088016152d6565b90509295509295509295565b6000600160ff1b821415615931576159316153ab565b5060000390565b6001600160a01b03959095168552600293840b60208601529190920b60408401526001600160801b03918216606084015216608082015260a00190565b6000806040838503121561598857600080fd5b825161599381615207565b6020840151909250614e4f81615207565b600080600080600060a086880312156159bc57600080fd5b85519450602086015193506040860151925060608601516159dc81615207565b6080870151909250614e9981615207565b6000826159fc576159fc615829565b500690565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351615a7e8160178501602088016149de565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615aaf8160288401602088016149de565b01602801949350505050565b60008160060b8360060b6000811281667fffffffffffff1901831281151615615ae657615ae66153ab565b81667fffffffffffff018313811615615b0157615b016153ab565b5090039392505050565b60008160060b8360060b80615b2257615b22615829565b667fffffffffffff19821460001982141615615b4057615b406153ab565b90059392505050565b60008260060b80615b5c57615b5c615829565b808360060b0791505092915050565b60008160020b627fffff19811415615b8557615b856153ab565b6000190192915050565b60008160020b8360020b6000821282627fffff03821381151615615bb557615bb56153ab565b82627fffff19038212811615615bcd57615bcd6153ab565b50019392505050565b60008160020b8360020b6000811281627fffff1901831281151615615bfd57615bfd6153ab565b81627fffff018313811615615b0157615b016153ab565b60006020808385031215615c2757600080fd5b82516001600160401b0380821115615c3e57600080fd5b818501915085601f830112615c5257600080fd5b8151615c60614cbf8261501b565b81815260059190911b83018401908481019088831115615c7f57600080fd5b8585015b83811015615cf257805185811115615c9b5760008081fd5b86016040818c03601f1901811315615cb35760008081fd5b615cbb614c38565b8983015188811115615ccd5760008081fd5b615cdb8e8c838701016156bc565b825250910151888201528352918601918601615c83565b5098975050505050505050565b600080600060608486031215615d1457600080fd5b8351925060208401519150604084015161525c81615207565b6020808252600190820152605360f81b604082015260600190565b600081615d5757615d576153ab565b50600019019056fe241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efc62a261ff212893ed0a2ac422d821462bb04a4b5c153a746ad6a15108fb4f951a264697066735822122033d321221a9a3760d4a74c1384bc5f0d412f56d2c593f8ce7f77f458cc80f63564736f6c634300080c0033

Deployed ByteCode

0x608060405234801561001057600080fd5b506004361061020f5760003560e01c806301ffc9a71461021457806306fdde031461023c578063095ea7b3146102515780630dfe1681146102645780631053f8711461028457806316f0115b146102ab57806318160ddd146102be57806318178358146102d05780631c1f1936146102da5780631f2c5443146102e357806323b872dd146102fb578063246581f71461030e578063248a9ca3146103215780632c8958f6146103345780632f2ff15d1461034757806330faab2a1461035a578063313ce5671461036357806336568abe14610372578063365d0ed71461038557806339509351146103a75780633bec034a146103ba5780633c1d5df0146103e55780633dd657c5146104115780633f4ba83a146104245780635c975abb1461042c57806363db7eae1461043757806370a0823114610440578063802758601461046957806380814ffb146104805780638456cb591461048a5780638ec4daf6146104925780638f747bb8146104a557806391d14854146104b857806395d89b41146104cb578063a217fddf146104d3578063a457c2d7146104db578063a9059cbb146104ee578063abbffcb914610501578063b7ab45b614610529578063c4a7761e14610555578063d21220a71461055d578063d331bef714610570578063d547741f14610583578063dd62ed3e14610596578063ef095f8d146105cf575b600080fd5b6102276102223660046149b4565b6105d9565b60405190151581526020015b60405180910390f35b610244610656565b6040516102339190614a36565b61022761025f366004614a5e565b6106e8565b60fc54610277906001600160a01b031681565b6040516102339190614a8a565b60fe5461029890600160a01b900460020b81565b60405160029190910b8152602001610233565b60fe54610277906001600160a01b031681565b6035545b604051908152602001610233565b6102d86106fe565b005b6102c2611a0b81565b6102eb6108ac565b6040516102339493929190614ad9565b610227610309366004614b97565b610b31565b6102d861031c366004614cef565b610be2565b6102c261032f366004614d77565b611136565b6102d8610342366004614dd8565b61114b565b6102d8610355366004614e2a565b6111b3565b6102c2610d0581565b60405160128152602001610233565b6102d8610380366004614e2a565b6111d0565b610398610393366004614e5a565b61124e565b60405161023393929190614ea7565b6102276103b5366004614a5e565b6115cd565b6102c26103c8366004614edd565b805160208183018101805160ff8252928201919093012091525481565b60fe546103fc90600160b81b900463ffffffff1681565b60405163ffffffff9091168152602001610233565b6102d861041f366004614dd8565b611609565b6102d8611654565b60655460ff16610227565b6102c26105dc81565b6102c261044e366004614f11565b6001600160a01b031660009081526033602052604090205490565b610471611678565b60405161023393929190614f61565b6102c26101025481565b6102d86118a5565b6102d86104a0366004614fce565b6118c6565b6102d86104b33660046150cc565b611a83565b6102276104c6366004614e2a565b611f4b565b610244611f76565b6102c2600081565b6102276104e9366004614a5e565b611f85565b6102276104fc366004614a5e565b61201e565b61051461050f36600461521c565b61202b565b60408051928352602083019190915201610233565b6102c2610537366004614edd565b80516020818301810180516101008252928201919093012091525481565b61051461214d565b60fd54610277906001600160a01b031681565b61051461057e366004615267565b61240b565b6102d8610591366004614e2a565b612510565b6102c26105a43660046152a8565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b6102c26101035481565b60fb5460405163ab8b2a7360e01b81526001600160e01b0319831660048201526000916001600160a01b03169063ab8b2a7390602401602060405180830381865afa15801561062c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065091906152e6565b92915050565b60606036805461066590615301565b80601f016020809104026020016040519081016040528092919081815260200182805461069190615301565b80156106de5780601f106106b3576101008083540402835291602001916106de565b820191906000526020600020905b8154815290600101906020018083116106c157829003601f168201915b5050505050905090565b60006106f5338484612533565b50600192915050565b600061012d805480602002602001604051908101604052809291908181526020016000905b828210156107755760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff1690820152825260019092019101610723565b5050825192935060009150505b8181146108a75760006107cf8483815181106107a0576107a061533c565b6020026020010151600001518584815181106107be576107be61533c565b602002602001015160200151612657565b50505050905060008111156108965760fe5484516001600160a01b0390911690633b3bc70e908690859081106108075761080761533c565b6020026020010151600001518685815181106108255761082561533c565b60200260200101516020015160006040518463ffffffff1660e01b815260040161085193929190615352565b60408051808303816000875af115801561086f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108939190615387565b50505b506108a0816153c1565b9050610782565b505050565b604080516002808252606082810190935260009291829182918591816020016020820280368337505060fb5460408051638da5cb5b60e01b815290519394506001600160a01b0390911692638da5cb5b925060048083019260209291908290030181865afa158015610922573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094691906153e7565b816000815181106109595761095961533c565b6001600160a01b03928316602091820292909201015260fb5460405163015a1d6760e31b8152911690630ad0eb3890610996903090600401614a8a565b602060405180830381865afa1580156109b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d791906153e7565b816001815181106109ea576109ea61533c565b6001600160a01b03929092166020928302919091019091015260408051600280825260608201909252600091816020015b6060815260200190600190039081610a1b5790505090506040518060400160405280600a81526020016953544545525f4645455360b01b81525081600081518110610a6857610a6861533c565b60200260200101819052506040518060400160405280600f81526020016e535452415445474953545f4645455360881b81525081600181518110610aae57610aae61533c565b602090810291909101015260408051600280825260608201909252600091816020016020820280368337019050509050611a0b81600081518110610af457610af461533c565b602002602001018181525050610d0581600181518110610b1657610b1661533c565b60209081029190910101526105dc9792965090945092509050565b6000610b3e848484612701565b6001600160a01b038416600090815260346020908152604080832033845290915290205482811015610bc85760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b610bd58533858403612533565b60019150505b9392505050565b600054610100900460ff16610bfd5760005460ff1615610c01565b303b155b610c645760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bbf565b600054610100900460ff16158015610c86576000805461ffff19166101011790555b60008060008085806020019051810190610ca09190615404565b9350935093509350600960020b8260020b138015610cc357506107d1600283900b125b610ccc57600080fd5b601e63ffffffff8216118015610ce9575061025863ffffffff8216105b610cf257600080fd5b826001600160a01b0316846001600160a01b031610610d1057600080fd5b60fb8054336001600160a01b03199182161790915560fc805482166001600160a01b0387811691821790925560fd805490931691861691821790925560405163d9a641e160e01b8152600481019290925260248201526000907342b08e7a9211482d3643a126a7df1895448d35099063d9a641e190604401602060405180830381865afa158015610da5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc991906153e7565b90506001600160a01b038116610dde57600080fd5b60fe80546001600160a01b0319166001600160a01b0383169081179091556040805163ef01df4f60e01b815290516000929163ef01df4f9160048083019260209291908290030181865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e91906153e7565b90506001600160a01b038116610e7357600080fd5b6040805160028082526060820183526000926020830190803683370190505090508381600081518110610ea857610ea861533c565b63ffffffff90921660209283029190910190910152604051639d3a524160e01b81526001600160a01b03831690639d3a524190610ee9908490600401615461565b600060405180830381865afa158015610f06573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f2e919081019061551c565b505060fb60009054906101000a90046001600160a01b03166001600160a01b031663d0fb02036040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa791906153e7565b61010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506000611045336001600160a01b031663381193696040518163ffffffff1660e01b8152600401602060405180830381865afa158015611011573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103591906155e7565b611040906001615600565b6128bd565b905061108f8160405160200161105b9190615618565b6040516020818303038152906040528260405160200161107b9190615654565b6040516020818303038152906040526129c2565b6110976129fb565b61109f612a34565b6110b7600080516020615da08339815191528c612a73565b6110cf600080516020615d608339815191528e612a73565b505060fe805463ffffffff909416600160b81b0263ffffffff60b81b1962ffffff909616600160a01b029590951666ffffffffffffff60a01b19909416939093179390931790915550508215915061112f9050576000805461ff00191690555b5050505050565b600090815260c9602052604090206001015490565b60fe546001600160a01b0316331461116257600080fd5b60fe54600160e01b900460ff1661117857600080fd5b60fe805460ff60e01b1916905560008080861315611194578591505b60008513156111a05750835b6111ab338383612a7d565b505050505050565b6111bc82611136565b6111c68133612ab7565b6108a78383612b1b565b6001600160a01b03811633146112405760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610bbf565b61124a8282612ba1565b5050565b600080600061125f60655460ff1690565b1561127c5760405162461bcd60e51b8152600401610bbf90615683565b6001600160a01b03841661128f57600080fd5b60fb546040516367a44ca360e01b81526000916001600160a01b0316906367a44ca3906112c0903090600401614a8a565b600060405180830381865afa1580156112dd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113059190810190615701565b519050600181600481111561131c5761131c6157c7565b148061133957506003816004811115611337576113376157c7565b145b61134257600080fd5b61134a6106fe565b600061135560355490565b905060008061136261214d565b9150915082600014806113755750600082115b806113805750600081115b61138c5761138c6157dd565b826113b8578b95508a94506113a18686612c08565b9650620f42408710156113b357600080fd5b6114b4565b816113dc576113c88b8483612c1e565b96506113d5878285612cb4565b94506114b4565b80611400576113ec8c8484612c1e565b96506113f9878385612cb4565b95506114b4565b600061141e61140f838f6157f3565b611419858f6157f3565b612d34565b9050600081116114545760405162461bcd60e51b81526020600482015260016024820152604360f81b6044820152606401610bbf565b81611460600183615812565b61146a919061583f565b611475906001615600565b965082611483600183615812565b61148d919061583f565b611498906001615600565b9550816114a6828686612c1e565b6114b0919061583f565b9750505b600087116114c157600080fd5b898610156114f55760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610bbf565b888510156115295760405162461bcd60e51b81526020600482015260016024820152603160f81b6044820152606401610bbf565b85156115475760fc54611547906001600160a01b0316333089612d43565b84156115655760fd54611565906001600160a01b0316333088612d43565b61156f8888612dae565b876001600160a01b0316336001600160a01b03167f4e2ca0515ed1aef1395f66b5303bb5d6f1bf9d61a353fa53f73f8ac9973fa9f68989896040516115b693929190614ea7565b60405180910390a350505050955095509592505050565b3360008181526034602090815260408083206001600160a01b038716845290915281205490916106f5918590611604908690615600565b612533565b60fe546001600160a01b0316331461162057600080fd5b60fe54600160d81b900460ff1661163657600080fd5b60fe805460ff60d81b1916905561164e338585612a7d565b50505050565b600080516020615da083398151915261166d8133612ab7565b611675612e7b565b50565b61012d5460408051606081810183528082526020820181905291810182905290918291829190816001600160401b038111156116b6576116b6614bd8565b6040519080825280602002602001820160405280156116df578160200160208202803683370190505b508152816001600160401b038111156116fa576116fa614bd8565b604051908082528060200260200182016040528015611723578160200160208202803683370190505b506020820152816001600160401b0381111561174157611741614bd8565b60405190808252806020026020018201604052801561176a578160200160208202803683370190505b50604082015260005b82811461188b5761012d818154811061178e5761178e61533c565b6000918252602090912001548251805160029290920b91839081106117b5576117b561533c565b602002602001019060020b908160020b8152505061012d81815481106117dd576117dd61533c565b9060005260206000200160000160039054906101000a900460020b8260200151828151811061180e5761180e61533c565b602002602001019060020b908160020b8152505061012d81815481106118365761183661533c565b9060005260206000200160000160069054906101000a900461ffff16826040015182815181106118685761186861533c565b61ffff90921660209283029190910190910152611884816153c1565b9050611773565b508051602082015160409092015190969195509350915050565b600080516020615da08339815191526118be8133612ab7565b611675612f08565b60655460ff16156118e95760405162461bcd60e51b8152600401610bbf90615683565b610101546001600160a01b0316331461190157600080fd5b6101015460405163f3aefd5560e01b81526000916001600160a01b03169063f3aefd55906119359030908890600401615853565b602060405180830381865afa158015611952573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197691906153e7565b9050600060ff8560405161198a9190615877565b90815260200160405180910390205490506000610100866040516119ae9190615877565b9081526040519081900360200190205490508415611a21576119d08583615812565b60ff876040516119e09190615877565b908152602001604051809103902081905550846101026000828254611a059190615812565b909155505060fc54611a21906001600160a01b03168487612f60565b83156111ab57611a318482615812565b61010087604051611a429190615877565b908152602001604051809103902081905550836101036000828254611a679190615812565b909155505060fd546111ab906001600160a01b03168486612f60565b600080516020615d60833981519152611a9c8133612ab7565b60655460ff1615611abf5760405162461bcd60e51b8152600401610bbf90615683565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152612710861115611b0f57600080fd5b611b1b83850185614e2a565b6001600160a01b03908116602084015290825260fe54604080516339db007960e21b81529051919092169163e76c01e49160048083019260c09291908290030181865afa158015611b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b949190615893565b5050505060020b606083018190526001600160a01b039091166040830152611bbb90612f90565b611bc66001806131c8565b50508451516080820181905215611d0a57611be461012d60006148de565b60005b8160800151811015611d08576000604051806060016040528088600001518481518110611c1657611c1661533c565b602002602001015160020b815260200188602001518481518110611c3c57611c3c61533c565b602002602001015160020b815260200188604001518481518110611c6257611c6261533c565b60209081029190910181015161ffff90811690925261012d805460018101825560009190915283517f193a3ae4da5049eb74cee39e4cf5827f7ce7b1d1d1775ef1c6311eb60558e6d5909101805492850151604090950151909316600160301b0261ffff60301b1962ffffff95861663010000000265ffffffffffff19909416959092169490941791909117169190911790555080611d00816153c1565b915050611be7565b505b805115611e965780516000811360e0830152600160ff1b1415611d2c57600080fd5b60fe805460ff60e01b198116600160e01b1790915560e08201516001600160a01b039091169063128acb0890309080611d6f578451611d6a9061591b565b611d72565b84515b60208601516040516001600160e01b031960e087901b1681526001600160a01b0394851660048201529215156024840152604483019190915291909116606482015260a06084820152600060a482015260c40160408051808303816000875af1158015611de3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e079190615387565b505060fe60009054906101000a90046001600160a01b03166001600160a01b031663e76c01e46040518163ffffffff1660e01b815260040160c060405180830381865afa158015611e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e809190615893565b5050506001600160a01b03909216604084015250505b611e9e613789565b60a0820152611eab61380f565b60c08201819052604082015160a08301517f2fd4737bac0995700ade358ae308c92f20ee63884dfbe658bf55d2f99f60f07792611ee760355490565b604080516001600160a01b039095168552602085019390935291830152606082015260800160405180910390a185156111ab576111ab8160400151611f338360a0015189612710612c1e565b611f448460c001518a612710612c1e565b8451613845565b600091825260c9602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60606037805461066590615301565b3360009081526034602090815260408083206001600160a01b0386168452909152812054828110156120075760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610bbf565b6120143385858403612533565b5060019392505050565b60006106f5338484612701565b600080600080516020615da08339815191526120478133612ab7565b60fe54604051631d9de38760e11b81526001600160a01b0390911690633b3bc70e9061207b90899089908990600401615352565b60408051808303816000875af1158015612099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120bd9190615387565b60fe546040516309e3d67b60e31b81529295509093506001600160a01b031690634f1eb3d8906121009030908a908a906001600160801b03908190600401615938565b60408051808303816000875af115801561211e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121429190615975565b505050935093915050565b600080612158613789565b915061216261380f565b9050600061012d805480602002602001604051908101604052809291908181526020016000905b828210156121db5760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff1690820152825260019092019101612189565b505050509050600060fe60009054906101000a90046001600160a01b03166001600160a01b031663e76c01e46040518163ffffffff1660e01b815260040160c060405180830381865afa158015612236573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225a9190615893565b505061010154604051634867e20360e11b81529495506000946001600160a01b0390911693506390cfc406925061229691503090600401614a8a565b602060405180830381865afa1580156122b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d791906155e7565b905060006122e782612710615812565b845190915060005b81811461240157600080600061232e8985815181106123105761231061533c565b6020026020010151600001518a86815181106107be576107be61533c565b6001600160801b031694506001600160801b03169450505092506000806123a18a6123758d89815181106123645761236461533c565b602002602001015160000151613d5d565b61239b8e8a8151811061238a5761238a61533c565b602002602001015160200151613d5d565b88614038565b915091506123b28489612710612c1e565b6123bc9083615600565b6123c6908e615600565b9c506123d58389612710612c1e565b6123df9082615600565b6123e9908d615600565b9b505050505050806123fa906153c1565b90506122ef565b5050505050509091565b6000806001600160a01b03831661242157600080fd5b6000861161242e57600080fd5b600061243960355490565b905061244533886140d4565b612457612450613789565b8883612c1e565b925061246461245061380f565b915060008061247389846131c8565b90925090506124828286615600565b945061248e8185615600565b93508785101561249d57600080fd5b868410156124aa57600080fd5b6124b5868686612a7d565b856001600160a01b0316336001600160a01b03167febff2602b3f468259e1e99f613fed6691f3a6526effe6ef3e768ba7ae7a36c4f8b88886040516124fc93929190614ea7565b60405180910390a350505094509492505050565b61251982611136565b6125238133612ab7565b6108a78383612ba1565b3b151590565b6001600160a01b0383166125955760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610bbf565b6001600160a01b0382166125f65760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610bbf565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60fe5460009081908190819081906001600160a01b031663514ea4bf61268f308a8a601892831b62ffffff9283161790921b91161790565b6040518263ffffffff1660e01b81526004016126ad91815260200190565b60a060405180830381865afa1580156126ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ee91906159a4565b939b929a50909850965090945092505050565b6001600160a01b0383166127655760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610bbf565b6001600160a01b0382166127c75760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610bbf565b6001600160a01b0383166000908152603360205260409020548181101561283f5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610bbf565b6001600160a01b03808516600090815260336020526040808220858503905591851681529081208054849290612876908490615600565b92505081905550826001600160a01b0316846001600160a01b0316600080516020615d80833981519152846040516128b091815260200190565b60405180910390a361164e565b6060816128e15750506040805180820190915260018152600360fc1b602082015290565b8160005b811561290b57806128f5816153c1565b91506129049050600a8361583f565b91506128e5565b6000816001600160401b0381111561292557612925614bd8565b6040519080825280601f01601f19166020018201604052801561294f576020820181803683370190505b5090505b84156129ba57612964600183615812565b9150612971600a866159ed565b61297c906030615600565b60f81b8183815181106129915761299161533c565b60200101906001600160f81b031916908160001a9053506129b3600a8661583f565b9450612953565b949350505050565b600054610100900460ff166129e95760405162461bcd60e51b8152600401610bbf90615a01565b6129f1614210565b61124a8282614237565b600054610100900460ff16612a225760405162461bcd60e51b8152600401610bbf90615a01565b612a2a614210565b612a32614285565b565b600054610100900460ff16612a5b5760405162461bcd60e51b8152600401610bbf90615a01565b612a63614210565b612a6b614210565b612a32614210565b61124a8282612b1b565b8115612a9a5760fc54612a9a906001600160a01b03168484612f60565b80156108a75760fd546108a7906001600160a01b03168483612f60565b612ac18282611f4b565b61124a57612ad9816001600160a01b031660146142b8565b612ae48360206142b8565b604051602001612af5929190615a4c565b60408051601f198184030181529082905262461bcd60e51b8252610bbf91600401614a36565b612b258282611f4b565b61124a57600082815260c9602090815260408083206001600160a01b03851684529091529020805460ff19166001179055612b5d3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b612bab8282611f4b565b1561124a57600082815260c9602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000818311612c175781610bdb565b5090919050565b60008383028160001985870982811083820303915050808411612c4057600080fd5b80612c5057508290049050610bdb565b8385870960008581038616958690049560026003880281188089028203028089028203028089028203028089028203028089028203028089029091030291819003819004600101858411909403939093029190930391909104170290509392505050565b6000831580612cd557505082820282848281612cd257612cd2615829565b04145b15612cf65760008211612ce757600080fd5b81810490829006151501610bdb565b612d01848484612c1e565b905060008280612d1357612d13615829565b8486091115610bdb576000198110612d2a57600080fd5b6001019392505050565b6000818310612c175781610bdb565b6040516001600160a01b038085166024830152831660448201526064810182905261164e9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614453565b6001600160a01b038216612e045760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610bbf565b8060356000828254612e169190615600565b90915550506001600160a01b03821660009081526033602052604081208054839290612e43908490615600565b90915550506040518181526001600160a01b03831690600090600080516020615d808339815191529060200160405180910390a35050565b60655460ff16612ec45760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bbf565b6065805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b604051612efe9190614a8a565b60405180910390a1565b60655460ff1615612f2b5760405162461bcd60e51b8152600401610bbf90615683565b6065805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612ef13390565b6040516001600160a01b0383166024820152604481018290526108a790849063a9059cbb60e01b90606401612d77565b60fe5460408051600280825260608201835263ffffffff600160b81b85041693600160a01b9004810b9260009291906020830190803683370190505090508281600081518110612fe257612fe261533c565b63ffffffff90921660209283029190910182015260fe546040805163ef01df4f60e01b815290516001600160a01b039092169263ef01df4f926004808401938290030181865afa15801561303a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305e91906153e7565b6001600160a01b0316639d3a5241826040518263ffffffff1660e01b81526004016130899190615461565b600060405180830381865afa9250505080156130c757506040513d6000823e601f3d908101601f191682016040526130c4919081019061551c565b60015b6130d35761164e612f08565b6000826000815181106130e8576130e861533c565b6020026020010151836001815181106131035761310361533c565b60200260200101516131159190615abb565b90506000613127600388900b83615b0b565b905060008260060b12801561314d575061314763ffffffff881683615b49565b60060b15155b15613160578061315c81615b6b565b9150505b61316a8682615b8f565b60020b8860020b1315801561318e57506131848682615bd6565b60020b8860020b12155b6131be5760405162461bcd60e51b81526020600482015260016024820152602b60f91b6044820152606401610bbf565b5050505050505050565b600080600061012d805480602002602001604051908101604052809291908181526020016000905b828210156132425760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff16908201528252600190920191016131f0565b50505050905060008060008351905060005b81811461344157600085828151811061326f5761326f61533c565b602002602001015160000151905060008683815181106132915761329161533c565b60200260200101516020015190506000806132ac8484612657565b5050505090506132bd818e8e612c1e565b9150506001600160801b0381161561342d5760fe54604051631d9de38760e11b815260009182916001600160a01b0390911690633b3bc70e9061330890889088908890600401615352565b60408051808303816000875af1158015613326573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061334a9190615387565b60fe546040516309e3d67b60e31b815292945090925060009182916001600160a01b031690634f1eb3d8906133929030908b908b906001600160801b03908190600401615938565b60408051808303816000875af11580156133b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d49190615975565b6001600160801b0391821693501690506133ee848f615600565b9d506133fa838e615600565b9c506134068483615812565b613410908c615600565b9a5061341c8382615812565b613426908b615600565b9950505050505b5050508061343a906153c1565b9050613254565b5060408051848152602081018490527fc28ad1de9c0c32e5394ba60323e44d8d9536312236a47231772e448a3e49de42910160405180910390a161010154604051639af608c960e01b81526000916001600160a01b031690639af608c9906134ad903090600401614a8a565b600060405180830381865afa1580156134ca573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134f29190810190615c14565b61010154604051634867e20360e11b81529192506000916001600160a01b03909116906390cfc40690613529903090600401614a8a565b602060405180830381865afa158015613546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356a91906155e7565b90506000808615613585576135828784612710612c1e565b91505b851561359b576135988684612710612c1e565b90505b8161010260008282546135ae9190615600565b925050819055508061010360008282546135c89190615600565b909155506135d890508288615812565b96506135e48187615812565b955060005b8451811461374b5761361b838683815181106136075761360761533c565b602002602001015160200151612710612c1e565b60ff86838151811061362f5761362f61533c565b6020026020010151600001516040516136489190615877565b9081526020016040518091039020546136619190615600565b60ff8683815181106136755761367561533c565b60200260200101516000015160405161368e9190615877565b9081526020016040518091039020819055506136b6828683815181106136075761360761533c565b6101008683815181106136cb576136cb61533c565b6020026020010151600001516040516136e49190615877565b9081526020016040518091039020546136fd9190615600565b6101008683815181106137125761371261533c565b60200260200101516000015160405161372b9190615877565b90815260405190819003602001902055613744816153c1565b90506135e9565b50613757878d8d612c1e565b613761908b615600565b995061376e868d8d612c1e565b613778908a615600565b985050505050505050509250929050565b6101025460fc546040516370a0823160e01b8152600092916001600160a01b0316906370a08231906137bf903090600401614a8a565b602060405180830381865afa1580156137dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061380091906155e7565b61380a9190615812565b905090565b6101035460fd546040516370a0823160e01b8152600092916001600160a01b0316906370a08231906137bf903090600401614a8a565b600061012d805480602002602001604051908101604052809291908181526020016000905b828210156138bc5760008481526020908190206040805160608101825291850154600281810b845263010000008204900b83850152600160301b900461ffff169082015282526001909201910161386a565b505050509050606080600080600085519050806001600160401b038111156138e6576138e6614bd8565b60405190808252806020026020018201604052801561390f578160200160208202803683370190505b509450806001600160401b0381111561392a5761392a614bd8565b604051908082528060200260200182016040528015613953578160200160208202803683370190505b50935060005b818114613aee5760018110613a175786818151811061397a5761397a61533c565b60200260200101516000015160020b876001836139979190615812565b815181106139a7576139a761533c565b60200260200101516000015160020b128015613a0e57508681815181106139d0576139d061533c565b60200260200101516020015160020b876001836139ed9190615812565b815181106139fd576139fd61533c565b60200260200101516020015160020b125b613a1757600080fd5b600080613a828d613a338b86815181106123645761236461533c565b613a488c878151811061238a5761238a61533c565b8c8781518110613a5a57613a5a61533c565b60200260200101516040015161ffff16670de0b6b3a7640000613a7d91906157f3565b614038565b9150915081888481518110613a9957613a9961533c565b60200260200101818152505080878481518110613ab857613ab861533c565b6020908102919091010152613acd8287615600565b9550613ad98186615600565b9450505080613ae7906153c1565b9050613959565b5060005b818114613cc1576000808511613b09576000613b2d565b613b2d878381518110613b1e57613b1e61533c565b60200260200101518c87612c1e565b90506000808511613b3f576000613b54565b613b54878481518110613b1e57613b1e61533c565b90506000613b8d8e613b718c87815181106123645761236461533c565b613b868d888151811061238a5761238a61533c565b8686614528565b90506001600160801b03811615613cad5760fe805460ff60d81b198116600160d81b179091558a516001600160a01b039091169063aafe29c090309081908e9089908110613bdd57613bdd61533c565b6020026020010151600001518e8981518110613bfb57613bfb61533c565b60209081029190910181015101516040516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152600290810b60448401520b60648201526001600160801b038416608482015260c060a4820152600060c482015260e4016060604051808303816000875af1158015613c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ca99190615cff565b5050505b50505080613cba906153c1565b9050613af2565b506000871315613d0c576064613cd860058a6157f3565b613ce2919061583f565b613cea61380f565b10613d075760405162461bcd60e51b8152600401610bbf90615d2d565b613d51565b6000871215613d51576064613d2260058b6157f3565b613d2c919061583f565b613d34613789565b10613d515760405162461bcd60e51b8152600401610bbf90615d2d565b50505050505050505050565b6000600282900b60171d62ffffff818401821816620d89e8811115613d9557604051633c10250f60e01b815260040160405180910390fd5b600160801b6001821615613db657506ffffcb933bd6fad37aa2d162d1a5940015b6002821615613dd5576ffff97272373d413259a46990580e213a0260801c5b6004821615613df4576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b6008821615613e13576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b6010821615613e32576fffcb9843d60f6159c9db58835c9266440260801c5b6020821615613e51576fff973b41fa98c081472e6896dfb254c00260801c5b6040821615613e70576fff2ea16466c96a3843ec78b326b528610260801c5b6080821615613e8f576ffe5dee046a99a2a811c461f1969c30530260801c5b610100821615613eaf576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b610200821615613ecf576ff987a7253ac413176f2b074cf7815e540260801c5b610400821615613eef576ff3392b0822b70005940c7a398e4b70f30260801c5b610800821615613f0f576fe7159475a2c29b7443b29c7fa6e889d90260801c5b611000821615613f2f576fd097f3bdfd2022b8845ad8f792aa58250260801c5b612000821615613f4f576fa9f746462d870fdf8a65dc1f90e061e50260801c5b614000821615613f6f576f70d869a156d2a1b890bb3df62baf32f70260801c5b618000821615613f8f576f31be135f97d08fd981231505542fcfa60260801c5b62010000821615613fb0576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b62020000821615613fd0576e5d6af8dedb81196699c329225ee6040260801c5b6204000082106140165762040000821615613ff9576d2216e584f5fa1ea926041bedfe980260801c5b62080000821615614016576b048a170391f7dc42444e8fa20260801c5b60008560020b131561402757600019045b63ffffffff0160201c949350505050565b600080836001600160a01b0316856001600160a01b03161115614059579293925b846001600160a01b0316866001600160a01b0316116140845761407d8585856145ec565b91506140cb565b836001600160a01b0316866001600160a01b031610156140bd576140a98685856145ec565b91506140b685878561465b565b90506140cb565b6140c885858561465b565b90505b94509492505050565b6001600160a01b0382166141345760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610bbf565b6001600160a01b038216600090815260336020526040902054818110156141a85760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610bbf565b6001600160a01b03831660009081526033602052604081208383039055603580548492906141d7908490615812565b90915550506040518281526000906001600160a01b03851690600080516020615d808339815191529060200160405180910390a3505050565b600054610100900460ff16612a325760405162461bcd60e51b8152600401610bbf90615a01565b600054610100900460ff1661425e5760405162461bcd60e51b8152600401610bbf90615a01565b81516142719060369060208501906148fc565b5080516108a79060379060208401906148fc565b600054610100900460ff166142ac5760405162461bcd60e51b8152600401610bbf90615a01565b6065805460ff19169055565b606060006142c78360026157f3565b6142d2906002615600565b6001600160401b038111156142e9576142e9614bd8565b6040519080825280601f01601f191660200182016040528015614313576020820181803683370190505b509050600360fc1b8160008151811061432e5761432e61533c565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061435d5761435d61533c565b60200101906001600160f81b031916908160001a90535060006143818460026157f3565b61438c906001615600565b90505b6001811115614404576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106143c0576143c061533c565b1a60f81b8282815181106143d6576143d661533c565b60200101906001600160f81b031916908160001a90535060049490941c936143fd81615d48565b905061438f565b508315610bdb5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610bbf565b60006144a8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661469e9092919063ffffffff16565b90508051600014806144c95750808060200190518101906144c991906152e6565b6108a75760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bbf565b6000836001600160a01b0316856001600160a01b03161115614548579293925b846001600160a01b0316866001600160a01b0316116145735761456c8585856146ad565b90506145e3565b836001600160a01b0316866001600160a01b031610156145d557600061459a8786866146ad565b905060006145a9878986614710565b9050806001600160801b0316826001600160801b0316106145ca57806145cc565b815b925050506145e3565b6145e0858584614710565b90505b95945050505050565b6000826001600160a01b0316846001600160a01b0316111561460c579192915b836001600160a01b0316614645606060ff16846001600160801b0316901b8686036001600160a01b0316866001600160a01b0316612c1e565b8161465257614652615829565b04949350505050565b6000826001600160a01b0316846001600160a01b0316111561467b579192915b6129ba826001600160801b03168585036001600160a01b0316600160601b612c1e565b60606129ba848460008561474d565b6000826001600160a01b0316846001600160a01b031611156146cd579192915b60006146f0856001600160a01b0316856001600160a01b0316600160601b612c1e565b90506145e361470b84838888036001600160a01b0316612c1e565b614828565b6000826001600160a01b0316846001600160a01b03161115614730579192915b6129ba61470b83600160601b8787036001600160a01b0316612c1e565b6060824710156147ae5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610bbf565b600080866001600160a01b031685876040516147ca9190615877565b60006040518083038185875af1925050503d8060008114614807576040519150601f19603f3d011682016040523d82523d6000602084013e61480c565b606091505b509150915061481d87838387614843565b979650505050505050565b806001600160801b038116811461483e57600080fd5b919050565b606083156148af5782516148a8576001600160a01b0385163b6148a85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bbf565b50816129ba565b6129ba83838151156148c45781518083602001fd5b8060405162461bcd60e51b8152600401610bbf9190614a36565b50805460008255906000526020600020908101906116759190614980565b82805461490890615301565b90600052602060002090601f01602090048101928261492a5760008555614970565b82601f1061494357805160ff1916838001178555614970565b82800160010185558215614970579182015b82811115614970578251825591602001919060010190614955565b5061497c92915061499f565b5090565b5b8082111561497c5780546001600160401b0319168155600101614981565b5b8082111561497c57600081556001016149a0565b6000602082840312156149c657600080fd5b81356001600160e01b031981168114610bdb57600080fd5b60005b838110156149f95781810151838201526020016149e1565b8381111561164e5750506000910152565b60008151808452614a228160208601602086016149de565b601f01601f19169290920160200192915050565b602081526000610bdb6020830184614a0a565b6001600160a01b038116811461167557600080fd5b60008060408385031215614a7157600080fd5b8235614a7c81614a49565b946020939093013593505050565b6001600160a01b0391909116815260200190565b600081518084526020808501945080840160005b83811015614ace57815187529582019590820190600101614ab2565b509495945050505050565b600060808201868352602060808185015281875180845260a086019150828901935060005b81811015614b235784516001600160a01b031683529383019391830191600101614afe565b5050848103604086015286518082528282019350600581901b8201830183890160005b83811015614b7457601f19858403018752614b62838351614a0a565b96860196925090850190600101614b46565b50508681036060880152614b888189614a9e565b9b9a5050505050505050505050565b600080600060608486031215614bac57600080fd5b8335614bb781614a49565b92506020840135614bc781614a49565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715614c1057614c10614bd8565b60405290565b60405160c081016001600160401b0381118282101715614c1057614c10614bd8565b604080519081016001600160401b0381118282101715614c1057614c10614bd8565b604051601f8201601f191681016001600160401b0381118282101715614c8257614c82614bd8565b604052919050565b60006001600160401b03821115614ca357614ca3614bd8565b50601f01601f191660200190565b6000614cc4614cbf84614c8a565b614c5a565b9050828152838383011115614cd857600080fd5b828260208301376000602084830101529392505050565b60008060008060808587031215614d0557600080fd5b8435614d1081614a49565b93506020850135614d2081614a49565b92506040850135614d3081614a49565b915060608501356001600160401b03811115614d4b57600080fd5b8501601f81018713614d5c57600080fd5b614d6b87823560208401614cb1565b91505092959194509250565b600060208284031215614d8957600080fd5b5035919050565b60008083601f840112614da257600080fd5b5081356001600160401b03811115614db957600080fd5b602083019150836020828501011115614dd157600080fd5b9250929050565b60008060008060608587031215614dee57600080fd5b843593506020850135925060408501356001600160401b03811115614e1257600080fd5b614e1e87828801614d90565b95989497509550505050565b60008060408385031215614e3d57600080fd5b823591506020830135614e4f81614a49565b809150509250929050565b600080600080600060a08688031215614e7257600080fd5b853594506020860135935060408601359250606086013591506080860135614e9981614a49565b809150509295509295909350565b9283526020830191909152604082015260600190565b600082601f830112614ece57600080fd5b610bdb83833560208501614cb1565b600060208284031215614eef57600080fd5b81356001600160401b03811115614f0557600080fd5b6129ba84828501614ebd565b600060208284031215614f2357600080fd5b8135610bdb81614a49565b600081518084526020808501945080840160005b83811015614ace57815160020b87529582019590820190600101614f42565b606081526000614f746060830186614f2e565b602083820381850152614f878287614f2e565b8481036040860152855180825282870193509082019060005b81811015614fc057845161ffff1683529383019391830191600101614fa0565b509098975050505050505050565b600080600060608486031215614fe357600080fd5b83356001600160401b03811115614ff957600080fd5b61500586828701614ebd565b9660208601359650604090950135949350505050565b60006001600160401b0382111561503457615034614bd8565b5060051b60200190565b8060020b811461167557600080fd5b600082601f83011261505e57600080fd5b8135602061506e614cbf8361501b565b82815260059290921b8401810191818101908684111561508d57600080fd5b8286015b848110156150b15780356150a48161503e565b8352918301918301615091565b509695505050505050565b61ffff8116811461167557600080fd5b600080600080606085870312156150e257600080fd5b843593506020808601356001600160401b038082111561510157600080fd5b908701906060828a03121561511557600080fd5b61511d614bee565b82358281111561512c57600080fd5b6151388b82860161504d565b825250838301358281111561514c57600080fd5b6151588b82860161504d565b858301525060408301358281111561516f57600080fd5b80840193505089601f84011261518457600080fd5b8235615192614cbf8261501b565b81815260059190911b8401850190858101908c8311156151b157600080fd5b948601945b828610156151d85785356151c9816150bc565b825294860194908601906151b6565b8060408501525050508096505060408801359250808311156151f957600080fd5b5050614e1e87828801614d90565b6001600160801b038116811461167557600080fd5b60008060006060848603121561523157600080fd5b833561523c8161503e565b9250602084013561524c8161503e565b9150604084013561525c81615207565b809150509250925092565b6000806000806080858703121561527d57600080fd5b843593506020850135925060408501359150606085013561529d81614a49565b939692955090935050565b600080604083850312156152bb57600080fd5b82356152c681614a49565b91506020830135614e4f81614a49565b8051801515811461483e57600080fd5b6000602082840312156152f857600080fd5b610bdb826152d6565b600181811c9082168061531557607f821691505b6020821081141561533657634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b600293840b81529190920b60208201526001600160801b03909116604082015260806060820181905260009082015260a00190565b6000806040838503121561539a57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b60006000198214156153d5576153d56153ab565b5060010190565b805161483e81614a49565b6000602082840312156153f957600080fd5b8151610bdb81614a49565b6000806000806080858703121561541a57600080fd5b845161542581614a49565b602086015190945061543681614a49565b60408601519093506154478161503e565b606086015190925063ffffffff8116811461529d57600080fd5b6020808252825182820181905260009190848201906040850190845b8181101561549f57835163ffffffff168352928401929184019160010161547d565b50909695505050505050565b600082601f8301126154bc57600080fd5b815160206154cc614cbf8361501b565b82815260059290921b840181019181810190868411156154eb57600080fd5b8286015b848110156150b15780516001600160581b038116811461550f5760008081fd5b83529183019183016154ef565b6000806040838503121561552f57600080fd5b82516001600160401b038082111561554657600080fd5b818501915085601f83011261555a57600080fd5b8151602061556a614cbf8361501b565b82815260059290921b8401810191818101908984111561558957600080fd5b948201945b838610156155b75785518060060b81146155a85760008081fd5b8252948201949082019061558e565b918801519196509093505050808211156155d057600080fd5b506155dd858286016154ab565b9150509250929050565b6000602082840312156155f957600080fd5b5051919050565b60008219821115615613576156136153ab565b500190565b7353544545525f414c47454252415f5641554c545f60601b8152600082516156478160148501602087016149de565b9190910160140192915050565b6629aa22a2a920ab60c91b8152600082516156768160078501602087016149de565b9190910160070192915050565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b80516005811061483e57600080fd5b600082601f8301126156cd57600080fd5b81516156db614cbf82614c8a565b8181528460208386010111156156f057600080fd5b6129ba8260208301602087016149de565b60006020828403121561571357600080fd5b81516001600160401b038082111561572a57600080fd5b9083019060c0828603121561573e57600080fd5b615746614c16565b61574f836156ad565b8152602083015160208201526040830151604082015260608301518281111561577757600080fd5b615783878286016156bc565b606083015250615795608084016153dc565b608082015260a0830151828111156157ac57600080fd5b6157b8878286016156bc565b60a08301525095945050505050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b600081600019048311821515161561580d5761580d6153ab565b500290565b600082821015615824576158246153ab565b500390565b634e487b7160e01b600052601260045260246000fd5b60008261584e5761584e615829565b500490565b6001600160a01b03831681526040602082018190526000906129ba90830184614a0a565b600082516158898184602087016149de565b9190910192915050565b60008060008060008060c087890312156158ac57600080fd5b86516158b781614a49565b60208801519096506158c88161503e565b60408801519095506158d9816150bc565b606088015190945060ff811681146158f057600080fd5b6080880151909350615901816150bc565b915061590f60a088016152d6565b90509295509295509295565b6000600160ff1b821415615931576159316153ab565b5060000390565b6001600160a01b03959095168552600293840b60208601529190920b60408401526001600160801b03918216606084015216608082015260a00190565b6000806040838503121561598857600080fd5b825161599381615207565b6020840151909250614e4f81615207565b600080600080600060a086880312156159bc57600080fd5b85519450602086015193506040860151925060608601516159dc81615207565b6080870151909250614e9981615207565b6000826159fc576159fc615829565b500690565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351615a7e8160178501602088016149de565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351615aaf8160288401602088016149de565b01602801949350505050565b60008160060b8360060b6000811281667fffffffffffff1901831281151615615ae657615ae66153ab565b81667fffffffffffff018313811615615b0157615b016153ab565b5090039392505050565b60008160060b8360060b80615b2257615b22615829565b667fffffffffffff19821460001982141615615b4057615b406153ab565b90059392505050565b60008260060b80615b5c57615b5c615829565b808360060b0791505092915050565b60008160020b627fffff19811415615b8557615b856153ab565b6000190192915050565b60008160020b8360020b6000821282627fffff03821381151615615bb557615bb56153ab565b82627fffff19038212811615615bcd57615bcd6153ab565b50019392505050565b60008160020b8360020b6000811281627fffff1901831281151615615bfd57615bfd6153ab565b81627fffff018313811615615b0157615b016153ab565b60006020808385031215615c2757600080fd5b82516001600160401b0380821115615c3e57600080fd5b818501915085601f830112615c5257600080fd5b8151615c60614cbf8261501b565b81815260059190911b83018401908481019088831115615c7f57600080fd5b8585015b83811015615cf257805185811115615c9b5760008081fd5b86016040818c03601f1901811315615cb35760008081fd5b615cbb614c38565b8983015188811115615ccd5760008081fd5b615cdb8e8c838701016156bc565b825250910151888201528352918601918601615c83565b5098975050505050505050565b600080600060608486031215615d1457600080fd5b8351925060208401519150604084015161525c81615207565b6020808252600190820152605360f81b604082015260600190565b600081615d5757615d576153ab565b50600019019056fe241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efc62a261ff212893ed0a2ac422d821462bb04a4b5c153a746ad6a15108fb4f951a264697066735822122033d321221a9a3760d4a74c1384bc5f0d412f56d2c593f8ce7f77f458cc80f63564736f6c634300080c0033