{
    "Function": "slitherConstructorConstantVariables",
    "File": "contracts/GeVault.sol",
    "Parent Contracts": [
        "contracts/openzeppelin-solidity/contracts/security/ReentrancyGuard.sol",
        "contracts/openzeppelin-solidity/contracts/access/Ownable.sol",
        "contracts/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol",
        "contracts/openzeppelin-solidity/contracts/token/ERC20/extensions/IERC20Metadata.sol",
        "contracts/openzeppelin-solidity/contracts/token/ERC20/IERC20.sol",
        "contracts/openzeppelin-solidity/contracts/utils/Context.sol"
    ],
    "High-Level Calls": [],
    "Internal Calls": [],
    "Library Calls": [],
    "Low-Level Calls": [],
    "Code": "contract GeVault is ERC20, Ownable, ReentrancyGuard {\n  using SafeERC20 for ERC20;\n  \n  event Deposit(address indexed sender, address indexed token, uint amount, uint liquidity);\n  event Withdraw(address indexed sender, address indexed token, uint amount, uint liquidity);\n  event PushTick(address indexed ticker);\n  event ShiftTick(address indexed ticker);\n  event ModifyTick(address indexed ticker, uint index);\n  event Rebalance(uint tickIndex);\n  event SetEnabled(bool isEnabled);\n  event SetTreasury(address treasury);\n  event SetFee(uint baseFeeX4);\n  event SetTvlCap(uint tvlCap);\n\n  RangeManager rangeManager; \n  /// @notice Ticks properly ordered in ascending price order\n  TokenisableRange[] public ticks;\n  \n  /// @notice Tracks the beginning of active ticks: the next 4 ticks are the active\n  uint public tickIndex; \n  /// @notice Pair tokens\n  ERC20 public token0;\n  ERC20 public token1;\n  bool public isEnabled = true;\n  /// @notice Pool base fee \n  uint public baseFeeX4 = 20;\n  /// @notice Max vault TVL with 8 decimals\n  uint public tvlCap = 1e12;\n  \n  /// CONSTANTS \n  /// immutable keyword removed for coverage testing bug in brownie\n  address public treasury;\n  IUniswapV3Pool public uniswapPool;\n  ILendingPool public lendingPool;\n  IPriceOracle public oracle;\n  uint public constant nearbyRanges = 2;\n  IWETH public WETH;\n  bool public baseTokenIsToken0;\n  \n\n  constructor(\n    address _treasury, \n    address roeRouter, \n    address _uniswapPool, \n    uint poolId, \n    string memory name, \n    string memory symbol,\n    address weth,\n    bool _baseTokenIsToken0\n  ) \n    ERC20(name, symbol)\n  {\n    require(_treasury != address(0x0), \"GEV: Invalid Treasury\");\n    require(_uniswapPool != address(0x0), \"GEV: Invalid Pool\");\n    require(weth != address(0x0), \"GEV: Invalid WETH\");\n\n    (address lpap, address _token0, address _token1,, ) = RoeRouter(roeRouter).pools(poolId);\n    token0 = ERC20(_token0);\n    token1 = ERC20(_token1);\n    \n    lendingPool = ILendingPool(ILendingPoolAddressesProvider(lpap).getLendingPool());\n    oracle = IPriceOracle(ILendingPoolAddressesProvider(lpap).getPriceOracle());\n    treasury = _treasury;\n    uniswapPool = IUniswapV3Pool(_uniswapPool);\n    WETH = IWETH(weth);\n    baseTokenIsToken0 = _baseTokenIsToken0;\n  }\n  \n  \n  //////// ADMIN\n  \n  \n  /// @notice Set pool status\n  /// @param _isEnabled Pool status\n  function setEnabled(bool _isEnabled) public onlyOwner { \n    isEnabled = _isEnabled; \n    emit SetEnabled(_isEnabled);\n  }\n  \n  /// @notice Set treasury address\n  /// @param newTreasury New address\n  function setTreasury(address newTreasury) public onlyOwner { \n    treasury = newTreasury; \n    emit SetTreasury(newTreasury);\n  }\n\n\n  /// @notice Add a new ticker to the list\n  /// @param tr Tick address\n  function pushTick(address tr) public onlyOwner {\n    TokenisableRange t = TokenisableRange(tr);\n    (ERC20 t0,) = t.TOKEN0();\n    (ERC20 t1,) = t.TOKEN1();\n    require(t0 == token0 && t1 == token1, \"GEV: Invalid TR\");\n    if (ticks.length == 0) ticks.push(t);\n    else {\n      // Check that tick is properly ordered\n      if (baseTokenIsToken0) \n        require( t.lowerTick() > ticks[ticks.length-1].upperTick(), \"GEV: Push Tick Overlap\");\n      else \n        require( t.upperTick() < ticks[ticks.length-1].lowerTick(), \"GEV: Push Tick Overlap\");\n      \n      ticks.push(TokenisableRange(tr));\n    }\n    emit PushTick(tr);\n  }  \n\n\n  /// @notice Add a new ticker to the list\n  /// @param tr Tick address\n  function shiftTick(address tr) public onlyOwner {\n    TokenisableRange t = TokenisableRange(tr);\n    (ERC20 t0,) = t.TOKEN0();\n    (ERC20 t1,) = t.TOKEN1();\n    require(t0 == token0 && t1 == token1, \"GEV: Invalid TR\");\n    if (ticks.length == 0) ticks.push(t);\n    else {\n      // Check that tick is properly ordered\n      if (!baseTokenIsToken0) \n        require( t.lowerTick() > ticks[0].upperTick(), \"GEV: Shift Tick Overlap\");\n      else \n        require( t.upperTick() < ticks[0].lowerTick(), \"GEV: Shift Tick Overlap\");\n      \n      // extend array by pushing last elt\n      ticks.push(ticks[ticks.length-1]);\n      // shift each element\n      if (ticks.length > 2){\n        for (uint k = 0; k < ticks.length - 2; k++) \n          ticks[ticks.length - 2 - k] = ticks[ticks.length - 3 - k];\n        }\n      // add new tick in first place\n      ticks[0] = t;\n    }\n    emit ShiftTick(tr);\n  }\n\n\n  /// @notice Modify ticker\n  /// @param tr New tick address\n  /// @param index Tick to modify\n  function modifyTick(address tr, uint index) public onlyOwner {\n    (ERC20 t0,) = TokenisableRange(tr).TOKEN0();\n    (ERC20 t1,) = TokenisableRange(tr).TOKEN1();\n    require(t0 == token0 && t1 == token1, \"GEV: Invalid TR\");\n    ticks[index] = TokenisableRange(tr);\n    emit ModifyTick(tr, index);\n  }\n  \n  /// @notice Ticks length getter\n  /// @return len Ticks length\n  function getTickLength() public view returns(uint len){\n    len = ticks.length;\n  }\n  \n  /// @notice Set the base fee\n  /// @param newBaseFeeX4 New base fee in E4\n  function setBaseFee(uint newBaseFeeX4) public onlyOwner {\n  require(newBaseFeeX4 < 1e4, \"GEV: Invalid Base Fee\");\n    baseFeeX4 = newBaseFeeX4;\n    emit SetFee(newBaseFeeX4);\n  }\n  \n  /// @notice Set the TVL cap\n  /// @param newTvlCap New TVL cap\n  function setTvlCap(uint newTvlCap) public onlyOwner {\n    tvlCap = newTvlCap;\n    emit SetTvlCap(newTvlCap);\n  }\n  \n  \n  //////// PUBLIC FUNCTIONS\n  \n    \n  /// @notice Rebalance tickers\n  /// @dev Provide the list of tickers from \n  function rebalance() public {\n    require(poolMatchesOracle(), \"GEV: Oracle Error\");\n    removeFromAllTicks();\n    if (isEnabled) deployAssets();\n  }\n  \n\n  /// @notice Withdraw assets from the ticker\n  /// @param liquidity Amount of GEV tokens to redeem; if 0, redeem all\n  /// @param token Address of the token redeemed for\n  /// @return amount Total token returned\n  /// @dev For simplicity+efficieny, withdrawal is like a rebalancing, but a subset of the tokens are sent back to the user before redeploying\n  function withdraw(uint liquidity, address token) public nonReentrant returns (uint amount) {\n    require(poolMatchesOracle(), \"GEV: Oracle Error\");\n    if (liquidity == 0) liquidity = balanceOf(msg.sender);\n    require(liquidity <= balanceOf(msg.sender), \"GEV: Insufficient Balance\");\n    require(liquidity > 0, \"GEV: Withdraw Zero\");\n    \n    uint vaultValueX8 = getTVL();\n    uint valueX8 = vaultValueX8 * liquidity / totalSupply();\n    amount = valueX8 * 10**ERC20(token).decimals() / oracle.getAssetPrice(token);\n    uint fee = amount * getAdjustedBaseFee(token == address(token1)) / 1e4;\n    \n    _burn(msg.sender, liquidity);\n    removeFromAllTicks();\n    ERC20(token).safeTransfer(treasury, fee);\n    uint bal = amount - fee;\n\n    if (token == address(WETH)){\n      WETH.withdraw(bal);\n      payable(msg.sender).transfer(bal);\n    }\n    else {\n      ERC20(token).safeTransfer(msg.sender, bal);\n    }\n    \n    // if pool enabled, deploy assets in ticks, otherwise just let assets sit here until totally withdrawn\n    if (isEnabled) deployAssets();\n    emit Withdraw(msg.sender, token, amount, liquidity);\n  }\n\n\n  /// @notice deposit tokens in the pool, convert to WETH if necessary\n  /// @param token Token address\n  /// @param amount Amount of token deposited\n  function deposit(address token, uint amount) public payable nonReentrant returns (uint liquidity) \n  {\n    require(isEnabled, \"GEV: Pool Disabled\");\n    require(poolMatchesOracle(), \"GEV: Oracle Error\");\n    require(token == address(token0) || token == address(token1), \"GEV: Invalid Token\");\n    require(amount > 0 || msg.value > 0, \"GEV: Deposit Zero\");\n    \n    // Wrap if necessary and deposit here\n    if (msg.value > 0){\n      require(token == address(WETH), \"GEV: Invalid Weth\");\n      // wraps ETH by sending to the wrapper that sends back WETH\n      WETH.deposit{value: msg.value}();\n      amount = msg.value;\n    }\n    else { \n      ERC20(token).safeTransferFrom(msg.sender, address(this), amount);\n    }\n    \n    // Send deposit fee to treasury\n    uint fee = amount * getAdjustedBaseFee(token == address(token0)) / 1e4;\n    ERC20(token).safeTransfer(treasury, fee);\n    uint valueX8 = oracle.getAssetPrice(token) * (amount - fee) / 10**ERC20(token).decimals();\n    require(tvlCap > valueX8 + getTVL(), \"GEV: Max Cap Reached\");\n\n    uint vaultValueX8 = getTVL();\n    uint tSupply = totalSupply();\n    // initial liquidity at 1e18 token ~ $1\n    if (tSupply == 0 || vaultValueX8 == 0)\n      liquidity = valueX8 * 1e10;\n    else {\n      liquidity = tSupply * valueX8 / vaultValueX8;\n    }\n    \n    rebalance();\n    require(liquidity > 0, \"GEV: No Liquidity Added\");\n    _mint(msg.sender, liquidity);    \n    emit Deposit(msg.sender, token, amount, liquidity);\n  }\n  \n  \n  /// @notice Get value of 1e18 GEV tokens\n  /// @return priceX8 price of 1e18 tokens with 8 decimals\n  function latestAnswer() external view returns (uint256 priceX8) {\n    uint supply = totalSupply();\n    if (supply == 0) return 0;\n    uint vaultValue = getTVL();\n    priceX8 = vaultValue * 1e18 / supply;\n  }\n  \n  \n  /// @notice Get vault underlying assets\n  function getReserves() public view returns (uint amount0, uint amount1){\n    for (uint k = 0; k < ticks.length; k++){\n      TokenisableRange t = ticks[k];\n      address aTick = lendingPool.getReserveData(address(t)).aTokenAddress;\n      uint bal = ERC20(aTick).balanceOf(address(this));\n      (uint amt0, uint amt1) = t.getTokenAmounts(bal);\n      amount0 += amt0;\n      amount1 += amt1;\n    }\n  }\n\n\n  //////// INTERNAL FUNCTIONS\n  \n  /// @notice Remove assets from all the underlying ticks\n  function removeFromAllTicks() internal {\n    for (uint k = 0; k < ticks.length; k++){\n      removeFromTick(k);\n    }    \n  }\n  \n  \n  /// @notice Remove from tick\n  function removeFromTick(uint index) internal {\n    TokenisableRange tr = ticks[index];\n    address aTokenAddress = lendingPool.getReserveData(address(tr)).aTokenAddress;\n    uint aBal = ERC20(aTokenAddress).balanceOf(address(this));\n    uint sBal = tr.balanceOf(aTokenAddress);\n\n    // if there are less tokens available than the balance (because of outstanding debt), withdraw what's available\n    if (aBal > sBal) aBal = sBal;\n    if (aBal > 0){\n      lendingPool.withdraw(address(tr), aBal, address(this));\n      tr.withdraw(aBal, 0, 0);\n    }\n  }\n  \n  \n  /// @notice \n  function deployAssets() internal { \n    uint newTickIndex = getActiveTickIndex();\n    uint availToken0 = token0.balanceOf(address(this));\n    uint availToken1 = token1.balanceOf(address(this));\n    \n    // Check which is the main token\n    (uint amount0ft, uint amount1ft) = ticks[newTickIndex].getTokenAmountsExcludingFees(1e18);\n    uint tick0Index = newTickIndex;\n    uint tick1Index = newTickIndex + 2;\n    if (amount1ft > 0){\n      tick0Index = newTickIndex + 2;\n      tick1Index = newTickIndex;\n    }\n    \n    // Deposit into the ticks + into the LP\n    if (availToken0 > 0){\n      depositAndStash(ticks[tick0Index], availToken0 / 2, 0);\n      depositAndStash(ticks[tick0Index+1], availToken0 / 2, 0);\n    }\n    if (availToken1 > 0){\n      depositAndStash(ticks[tick1Index], 0, availToken1 / 2);\n      depositAndStash(ticks[tick1Index+1], 0, availToken1 / 2);\n    }\n    \n    if (newTickIndex != tickIndex) tickIndex = newTickIndex;\n    emit Rebalance(tickIndex);\n  }\n  \n  \n  /// @notice Checks that the pool price isn't manipulated\n  function poolMatchesOracle() public view returns (bool matches){\n    (uint160 sqrtPriceX96,,,,,,) = uniswapPool.slot0();\n    \n    uint decimals0 = token0.decimals();\n    uint decimals1 = token1.decimals();\n    uint priceX8 = 10**decimals0;\n    // Overflow if dont scale down the sqrtPrice before div 2*192\n    priceX8 = priceX8 * uint(sqrtPriceX96 / 2 ** 12) ** 2 * 1e8 / 2**168;\n    priceX8 = priceX8 / 10**decimals1;\n    uint oraclePrice = 1e8 * oracle.getAssetPrice(address(token0)) / oracle.getAssetPrice(address(token1));\n    if (oraclePrice < priceX8 * 101 / 100 && oraclePrice > priceX8 * 99 / 100) matches = true;\n  }\n\n\n  /// @notice Helper that checks current allowance and approves if necessary\n  /// @param token Target token\n  /// @param spender Spender\n  /// @param amount Amount below which we need to approve the token spending\n  function checkSetApprove(address token, address spender, uint amount) private {\n    if ( ERC20(token).allowance(address(this), spender) < amount ) ERC20(token).safeIncreaseAllowance(spender, type(uint256).max);\n  }\n  \n  \n  /// @notice Calculate the vault total ticks value\n  /// @return valueX8 Total value of the vault with 8 decimals\n  function getTVL() public view returns (uint valueX8){\n    for(uint k=0; k<ticks.length; k++){\n      TokenisableRange t = ticks[k];\n      uint bal = getTickBalance(k);\n      valueX8 += bal * t.latestAnswer() / 1e18;\n    }\n  }\n  \n  \n  /// @notice Deposit assets in a ticker, and the ticker in lending pool\n  /// @param t Tik address\n  /// @return liquidity The amount of ticker liquidity added\n  function depositAndStash(TokenisableRange t, uint amount0, uint amount1) internal returns (uint liquidity){\n    checkSetApprove(address(token0), address(t), amount0);\n    checkSetApprove(address(token1), address(t), amount1);\n    liquidity = t.deposit(amount0, amount1);\n    \n    uint bal = t.balanceOf(address(this));\n    if (bal > 0){\n      checkSetApprove(address(t), address(lendingPool), bal);\n      lendingPool.deposit(address(t), bal, address(this), 0);\n    }\n  }\n  \n  \n  /// @notice Get balance of tick deposited in GE\n  /// @param index Tick index\n  /// @return liquidity Amount of Ticker\n  function getTickBalance(uint index) public view returns (uint liquidity) {\n    TokenisableRange t = ticks[index];\n    address aTokenAddress = lendingPool.getReserveData(address(t)).aTokenAddress;\n    liquidity = ERC20(aTokenAddress).balanceOf(address(this));\n  }\n  \n  \n  /// @notice Return first valid tick\n  function getActiveTickIndex() public view returns (uint activeTickIndex) {\n    if (ticks.length >= 5){\n      // looking for index at which the underlying asset differs from the next tick\n      for (activeTickIndex = 0; activeTickIndex < ticks.length - 3; activeTickIndex++){\n        (uint amt0, uint amt1) = ticks[activeTickIndex+1].getTokenAmountsExcludingFees(1e18);\n        (uint amt0n, uint amt1n) = ticks[activeTickIndex+2].getTokenAmountsExcludingFees(1e18);\n        if ( (amt0 == 0 && amt0n > 0) || (amt1 == 0 && amt1n > 0) )\n          break;\n      }\n    }\n  }\n\n\n  /// @notice Get deposit fee\n  /// @param increaseToken0 Whether (token0 added || token1 removed) or not\n  /// @dev Simple linear model: from baseFeeX4 / 2 to baseFeeX4 * 2\n  /// @dev Call before withdrawing from ticks or reserves will both be 0\n  function getAdjustedBaseFee(bool increaseToken0) public view returns (uint adjustedBaseFeeX4) {\n    (uint res0, uint res1) = getReserves();\n    uint value0 = res0 * oracle.getAssetPrice(address(token0)) / 10**token0.decimals();\n    uint value1 = res1 * oracle.getAssetPrice(address(token1)) / 10**token1.decimals();\n\n    if (increaseToken0)\n      adjustedBaseFeeX4 = baseFeeX4 * value0 / (value1 + 1);\n    else\n      adjustedBaseFeeX4 = baseFeeX4 * value1 / (value0 + 1);\n\n    // Adjust from -50% to +50%\n    if (adjustedBaseFeeX4 < baseFeeX4 / 2) adjustedBaseFeeX4 = baseFeeX4 / 2;\n    if (adjustedBaseFeeX4 > baseFeeX4 * 3 / 2) adjustedBaseFeeX4 = baseFeeX4 * 3 / 2;\n  }\n\n\n  /// @notice fallback: deposit unless it's WETH being unwrapped\n  receive() external payable {\n    if(msg.sender != address(WETH)) deposit(address(WETH), msg.value);\n  }\n  \n}"
}