Contract Diff Checker

Contract Name:
BeefyPositionMulticall

Contract Source Code:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

interface IStrategy {
    function lastPositionAdjustment() external view returns (uint256);
    function currentTick() external view returns (int24);
    function positionMain() external view returns (int24 lowerTick, int24 upperTick);
    function pool() external view returns (address);
    function twap() external view returns (int56);
    function positionWidth() external view returns (int24);
    function isCalm() external view returns (bool);
    function moveTicks() external;
    function maxTickDeviation() external view returns (int56);
}

interface IStrategyVelodrome {
    function currentTick() external view returns (int24);
    function positionMain() external view returns (int24 baseLower, int24 lowerTick, int24 upperTick);
    function pool() external view returns (address);
    function nftManager() external view returns (address);
}

interface IPool {
    function tickSpacing() external view returns (int24);
    function observe(uint32[] calldata secondsAgos) external view returns (int56[] memory tickCumulatives, int160[] memory secondsPerLiquidityCumulativeX128s);
}

interface IAlgebraPool {
    function getTimepoints(uint32[] calldata secondsAgos)
    external
    view
    returns (
      int56[] memory tickCumulatives,
      uint160[] memory secondsPerLiquidityCumulatives,
      uint112[] memory volatilityCumulatives,
      uint256[] memory volumePerAvgLiquiditys
    );
}

// Beefy Position Mulicall
// Contract that returns needed data for our position adjuster
contract BeefyPositionMulticall {
    // Gelato address to that is authorized to move the ticks
    address public gelato = address(0x035066D430e5ca34e7BdB0756FCe82186CAfe1F0);

    error NotAuthorized();

    /**
     * @notice Check if the position of the strategy is in range
     * @param _strategies an array of strategies to check
     * @return inRange an array of booleans indicating if the position is in tickspacing of range
     */
    function positionNeedsTickAdjustment(address[] memory _strategies) external view returns (bool[] memory inRange) {
        inRange = new bool[](_strategies.length);
        for (uint256 i; i < _strategies.length;) {
            IStrategy _strategy = IStrategy(_strategies[i]);

            address pool = _strategy.pool();
            int56 twapTick = _strategy.twap();
            int24 width = _strategy.positionWidth();
            int56 deviation = _strategy.maxTickDeviation();
            int24 tickSpacing = IPool(pool).tickSpacing();

            try IStrategyVelodrome(address(_strategy)).nftManager() returns (address) {
                IStrategyVelodrome _veloStrategy = IStrategyVelodrome(_strategies[i]);
                (,int24 lowerTick, int24 upperTick) = _veloStrategy.positionMain();

                // Check if the current tick is in range or in need of adjustment
                inRange[i] = _inRange(int24(twapTick), lowerTick, upperTick, width, tickSpacing, deviation);
            } catch {
                (int24 lowerTick, int24 upperTick) = _strategy.positionMain();

                // Check if the current tick is in range or in need of adjustment  
                inRange[i] = _inRange(int24(twapTick), lowerTick, upperTick, width, tickSpacing, deviation);
            }

            unchecked { ++i; }
        }
        return inRange;
    }

    /// @notice Check if the current tick is in range or in need of adjustment
    function _inRange(int24 twapTick, int24 lowerTick, int24 upperTick, int24 width, int24 tickSpacing, int56 deviation) internal pure returns (bool) {
        int24 bound = (width * tickSpacing) / 2;
        if (deviation <= 2) {
            // If we end up +5% out range, do nothing return true.
            int24 extremeBound = 500;
            if (twapTick <= (lowerTick - extremeBound) || twapTick >= (upperTick + extremeBound)) return true;
            return twapTick >= (lowerTick + bound) && twapTick <= (upperTick - bound);
        } else {
            return twapTick >= (lowerTick + bound) && twapTick <= (upperTick - bound);
        }
    }

    /**
     * @notice Check the last time the positions were adjusted
     * @param _strategies an array of strategies to check
     * @return lastAdjustments an array of uint256 indicating the last time the position was adjusted
     */
    function lastPositionAdjustments(address[] memory _strategies) external view returns (uint256[] memory) {
        uint256[] memory lastAdjustments = new uint256[](_strategies.length);
        for (uint256 i; i < _strategies.length;) {
            IStrategy _strategy = IStrategy(_strategies[i]);
            lastAdjustments[i] = _strategy.lastPositionAdjustment();
            unchecked { ++i; }
        }
        return lastAdjustments;
    }

    /// encode some data
    function encodeData(address one, address two, address three, uint num) external pure returns (bytes memory) {
        if (num == 1) {
            return abi.encode(one);
        } else if (num == 2) {
            return abi.encode(one, two);
        } else {
            return abi.encode(one, two, three);
        }
    }

    /// decode some data
    function decodeData(bytes memory _data, uint num) internal pure returns (address[] memory) {
        if (num == 1) {
            address[] memory strats = new address[](num);
            address decodedAddress = abi.decode(_data, (address));
            strats[0] = decodedAddress;
            return strats;
        } else if (num == 2) {
            address[] memory strats = new address[](num);
            (address one, address two) = abi.decode(_data, (address, address));
            strats[0] = one;
            strats[1] = two;
            return strats;
        } else {
            (address one, address two, address three) = abi.decode(_data, (address, address, address));
            address[] memory strats = new address[](num);
            strats[0] = one;
            strats[1] = two;
            strats[2] = three;
            return strats;
        }
    }

    /// @notice Move the ticks of the strategies that are calm, called only by gelato
    function moveTicks(bytes memory _data, uint num) external {
        if (msg.sender != gelato) {
            revert NotAuthorized();
        }
        address[] memory strats = decodeData(_data, num);
        
        for (uint i; i < strats.length;) {
            bool calm = IStrategy(strats[i]).isCalm(); 
            if (calm) {
                try IStrategy(strats[i]).moveTicks() {
                    unchecked { ++i; }
                } catch {
                    unchecked { ++i; }
                }
            }
        }
    }

    /// Check if the strategy is calm
    function isCalm(address[] memory _strategies) external view returns (bool[] memory) {

        bool[] memory calm = new bool[](_strategies.length);

        for (uint256 i; i < _strategies.length;) {

            calm[i] = IStrategy(_strategies[i]).isCalm();

            unchecked { ++i; }

        }

        return calm;

    }
}

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

Context size (optional):