contract PassiveStrategy {
    using SafeMath for uint256;

    ExistVault public vault;
    IUniswapV3Pool public pool;
    bool setup = false;
    int24 public tickSpacing;

    int24 public baseThreshold;
    int24 public limitThreshold;
    uint256 public period;
    int24 public minTickMove;
    int24 public maxTwapDeviation;
    uint32 public twapDuration;
    address public keeper;
    address public governance;

    uint256 public lastTimestamp;
    int24 public lastTick;

    /**
     */
    constructor(
    ) {
        keeper = msg.sender;
        governance = msg.sender;
    }
    function setupContract(
        //launch vault first set to address of new contract
        address _vault,
        uint256 _period,
        uint32 _twapDuration
    ) public onlyGovernance returns(bool success){
        IUniswapV3Pool _pool = ExistVault(_vault).pool();
        int24 _tickSpacing = _pool.tickSpacing();

        vault = ExistVault(_vault);
        pool = _pool;
        tickSpacing = _tickSpacing;

        period = _period;
        twapDuration = _twapDuration;
        (, lastTick, , , , , ) = _pool.slot0();
        success = true;
    }

    /**
     * @notice Calculates new ranges for orders and calls `vault.rebalance()`
     * so that vault can update its positions. Can only be called by keeper.
     */
    function rebalance() external {
        require(msg.sender == keeper, "can only be called by keeper");
        require(block.timestamp >= lastTimestamp.add(period), "not enough time has passed");

        (, int24 tick, , , , , ) = pool.slot0();
        int24 tickMove = tick > lastTick ? tick - lastTick : lastTick - tick;
        require(tickMove >= minTickMove, "price has not moved enough");

        int24 twap = getTwap();
        int24 twapDeviation = tick > twap ? tick - twap : twap - tick;
        require(twapDeviation <= maxTwapDeviation, "price deviates from twap too much");

        int24 _baseThreshold = baseThreshold;
        int24 _limitThreshold = limitThreshold;
        int24 maxThreshold =
            _baseThreshold > _limitThreshold ? _baseThreshold : _limitThreshold;
        require(tick >= TickMath.MIN_TICK + maxThreshold + tickSpacing, "price too low");
        require(tick <= TickMath.MAX_TICK - maxThreshold - tickSpacing, "price too high");

        int24 tickFloor = _floor(tick);
        int24 tickCeil = tickFloor + tickSpacing;

        vault.rebalance(
            0,
            0,
            tickFloor - _baseThreshold,
            tickCeil + _baseThreshold,
            tickFloor - _limitThreshold,
            tickFloor,
            tickCeil,
            tickCeil + _limitThreshold
        );

        lastTimestamp = block.timestamp;
        lastTick = tick;
    }

    /// @dev Fetches time-weighted average price in ticks from Uniswap pool.
    function getTwap() public view returns (int24) {
        uint32 _twapDuration = twapDuration;
        uint32[] memory secondsAgo = new uint32[](2);
        secondsAgo[0] = _twapDuration;
        secondsAgo[1] = 0;

        (int56[] memory tickCumulatives, ) = pool.observe(secondsAgo);
        return int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int32(_twapDuration)));
    }

    /// @dev Rounds tick down towards negative infinity so that it's a multiple
    /// of `tickSpacing`.
    function _floor(int24 tick) internal view returns (int24) {
        int24 compressed = tick / tickSpacing;
        if (tick < 0 && tick % tickSpacing != 0) compressed--;
        return compressed * tickSpacing;
    }

    function _checkThreshold(int24 threshold, int24 _tickSpacing) internal pure {
        require(threshold > 0, "threshold must be > 0");
        require(threshold <= TickMath.MAX_TICK, "threshold too high");
        require(threshold % _tickSpacing == 0, "threshold must be multiple of tickSpacing");
    }

    function setKeeper(address _keeper) external onlyGovernance {
        keeper = _keeper;
    }

    function setBaseThreshold(int24 _baseThreshold) external onlyGovernance returns(int24 baseThreshold_){
        baseThreshold_ = _baseThreshold;
        baseThreshold = _baseThreshold;
        _checkThreshold(baseThreshold, tickSpacing);
    }

    function setLimitThreshold(int24 _limitThreshold) external onlyGovernance returns(int24 limitThreshold_){
        limitThreshold_ = _limitThreshold;
        limitThreshold = _limitThreshold;
        _checkThreshold(limitThreshold, tickSpacing);
    }

    function setPeriod(uint256 _period) external onlyGovernance {
        //set to 360
        period = _period;
    }

    function setMinTickMove(int24 _minTickMove) external onlyGovernance returns(int24 minTickMove_){
        require(minTickMove >= 0, "minTickMove must be >= 0");
        minTickMove = _minTickMove;
        minTickMove_ = _minTickMove;
    }

    function setMaxTwapDeviation(int24 _deviation) external onlyGovernance returns(int24 maxTwapDeviation_){
        require(maxTwapDeviation >= 0, "maxTwapDeviation must be >= 0");
        maxTwapDeviation = _deviation;
        maxTwapDeviation_ = _deviation;
    }

    function setTwapDuration(uint32 _twapDuration) external onlyGovernance {
        //set it to 360
        require(_twapDuration > 0, "twapDuration must be > 0");
        twapDuration = _twapDuration;
    }

    /// @dev Uses same governance as underlying vault.
    modifier onlyGovernance {
        require(msg.sender == governance, "governance");
        _;
    }
}
/**
 * @title   Passive Strategy
 * @notice  Rebalancing strategy for Exist Vault that maintains the two
 *          following range orders:
 *
 *          1. Base order is placed between X - B and X + B + TS.
 *          2. Limit order is placed between X - L and X, or between X + TS
 *             and X + L + TS, depending on which token it holds more of.
 *
 *          where:
 *
 *              X = current tick rounded down to multiple of tick spacing
 *              TS = tick spacing
 *              B = base threshold
 *              L = limit threshold
 *
 *          Note that after these two orders, the vault should have deposited
 *          all its tokens and should only have a few wei left.
 *
 *          Because the limit order tries to sell whichever token the vault
 *          holds more of, the vault's holdings will have a tendency to get
 *          closer to a 1:1 balance. This enables it to continue providing
 *          liquidity without running out of inventory of either token, and
 *          achieves this without the need to swap directly on Uniswap and pay
 *          fees.
 */
function rebalance() external {
    require(msg.sender == keeper, "can only be called by keeper");
    require(block.timestamp >= lastTimestamp.add(period), "not enough time has passed");

    (, int24 tick, , , , , ) = pool.slot0();
    int24 tickMove = tick > lastTick ? tick - lastTick : lastTick - tick;
    require(tickMove >= minTickMove, "price has not moved enough");

    int24 twap = getTwap();
    int24 twapDeviation = tick > twap ? tick - twap : twap - tick;
    require(twapDeviation <= maxTwapDeviation, "price deviates from twap too much");

    int24 _baseThreshold = baseThreshold;
    int24 _limitThreshold = limitThreshold;
    int24 maxThreshold =
        _baseThreshold > _limitThreshold ? _baseThreshold : _limitThreshold;
    require(tick >= TickMath.MIN_TICK + maxThreshold + tickSpacing, "price too low");
    require(tick <= TickMath.MAX_TICK - maxThreshold - tickSpacing, "price too high");

    int24 tickFloor = _floor(tick);
    int24 tickCeil = tickFloor + tickSpacing;

    vault.rebalance(
        0,
        0,
        tickFloor - _baseThreshold,
        tickCeil + _baseThreshold,
        tickFloor - _limitThreshold,
        tickFloor,
        tickCeil,
        tickCeil + _limitThreshold
    );

    lastTimestamp = block.timestamp;
    lastTick = tick;
}

/**
 * @notice Calculates new ranges for orders and calls `vault.rebalance()`
 * so that vault can update its positions. Can only be called by keeper.
 */