{
    "Function": "slitherConstructorVariables",
    "File": "contracts/TokenisableRange.sol",
    "Parent Contracts": [
        "contracts/openzeppelin-solidity/contracts/security/ReentrancyGuard.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 TokenisableRange is ERC20(\"\", \"\"), ReentrancyGuard {\n  using SafeERC20 for ERC20;\n  /// EVENTS\n  event InitTR(address asset0, address asset1, uint128 startX10, uint128 endX10);\n  event Deposit(address sender, uint trAmount);\n  event Withdraw(address sender, uint trAmount);\n  event ClaimFees(uint fee0, uint fee1);\n  \n  /// VARIABLES\n\n  int24 public lowerTick;\n  int24 public upperTick;\n  uint24 public feeTier;\n  \n  uint256 public tokenId;\n  uint256 public fee0;\n  uint256 public fee1;\n  \n  struct ASSET {\n    ERC20 token;\n    uint8 decimals;\n  }\n  \n  ASSET public TOKEN0;\n  ASSET public TOKEN1;\n  IAaveOracle public ORACLE;\n  \n  string _name;\n  string _symbol;\n  \n  enum ProxyState { INIT_PROXY, INIT_LP, READY }\n  ProxyState public status;\n  address private creator;\n  \n  uint128 public liquidity;\n  // @notice deprecated, keep to avoid beacon storage slot overwriting errors\n  address public TREASURY_DEPRECATED = 0x22Cc3f665ba4C898226353B672c5123c58751692;\n  uint public treasuryFee_deprecated = 20;\n  \n  // These are constant across chains - https://docs.uniswap.org/protocol/reference/deployments\n  INonfungiblePositionManager constant public POS_MGR = INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); \n  IUniswapV3Factory constant public V3_FACTORY = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); \n  address constant public treasury = 0x22Cc3f665ba4C898226353B672c5123c58751692;\n  uint constant public treasuryFee = 20;\n\n  /// @notice Babylonian method for sqrt\n  /// @param x sqrt parameter\n  /// @return y Square root\n  function sqrt(uint x) internal pure returns (uint y) {\n      uint z = (x + 1) / 2;\n      y = x;\n      while (z < y) {\n          y = z;\n          z = (x / z + z) / 2;\n      }\n  }\n\n\n  /// @notice Store range parameters\n  /// @param _oracle Address of the IAaveOracle interface of the ROE lending pool\n  /// @param asset0 Quote token address\n  /// @param asset1 Base token address \n  /// @param startX10 Range lower price scaled by 1e10\n  /// @param endX10 Range high price scaled by 1e10\n  /// @param startName Name of the range lower bound \n  /// @param endName Name of the range higher bound\n  /// @param isTicker Range is single tick liquidity around upperTick/startX10/startName\n  function initProxy(IAaveOracle _oracle, ERC20 asset0, ERC20 asset1, uint128 startX10, uint128 endX10, string memory startName, string memory endName, bool isTicker) external {\n    require(address(_oracle) != address(0x0), \"Invalid oracle\");\n    require(status == ProxyState.INIT_PROXY, \"!InitProxy\");\n    creator = msg.sender;\n    status = ProxyState.INIT_LP;\n    ORACLE = _oracle;\n    \n    TOKEN0.token    = asset0;\n    TOKEN0.decimals = asset0.decimals();\n    TOKEN1.token     = asset1;\n    TOKEN1.decimals  = asset1.decimals();\n    string memory quoteSymbol = asset0.symbol();\n    string memory baseSymbol  = asset1.symbol();\n        \n    int24 _upperTick = TickMath.getTickAtSqrtRatio( uint160( 2**48 * sqrt( (2 ** 96 * (10 ** TOKEN1.decimals)) * 1e10 / (uint256(startX10) * 10 ** TOKEN0.decimals) ) ) );\n    int24 _lowerTick = TickMath.getTickAtSqrtRatio( uint160( 2**48 * sqrt( (2 ** 96 * (10 ** TOKEN1.decimals)) * 1e10 / (uint256(endX10  ) * 10 ** TOKEN0.decimals) ) ) );\n    \n    if (isTicker) { \n      feeTier   = 5;\n      int24 midleTick;\n      midleTick = (_upperTick + _lowerTick) / 2;\n      _upperTick = (midleTick + int24(feeTier)) - (midleTick + int24(feeTier)) % int24(feeTier * 2);\n      _lowerTick = _upperTick - int24(feeTier) - int24(feeTier);\n      _name     = string(abi.encodePacked(\"Ticker \", baseSymbol, \" \", quoteSymbol, \" \", startName, \"-\", endName));\n     _symbol    = string(abi.encodePacked(\"T-\",startName,\"_\",endName,\"-\",baseSymbol,\"-\",quoteSymbol));\n    } else {\n      feeTier   = 5;\n      _lowerTick = (_lowerTick + int24(feeTier)) - (_lowerTick + int24(feeTier)) % int24(feeTier * 2);\n      _upperTick = (_upperTick + int24(feeTier)) - (_upperTick + int24(feeTier)) % int24(feeTier * 2);\n      _name     = string(abi.encodePacked(\"Ranger \", baseSymbol, \" \", quoteSymbol, \" \", startName, \"-\", endName));\n      _symbol   = string(abi.encodePacked(\"R-\",startName,\"_\",endName,\"-\",baseSymbol,\"-\",quoteSymbol));\n    }\n    lowerTick = _lowerTick;\n    upperTick = _upperTick;\n    emit InitTR(address(asset0), address(asset1), startX10, endX10);\n  }\n  \n\n  /// @notice Get the name of this contract token\n  /// @dev Override name, symbol and decimals from ERC20 inheritance\n  function name()     public view virtual override returns (string memory) { return _name; }\n  /// @notice Get the symbol of this contract token\n  function symbol()   public view virtual override returns (string memory) { return _symbol; }\n\n\n  /// @notice Initialize a TokenizableRange by adding assets in the underlying Uniswap V3 position\n  /// @param n0 Amount of quote token added\n  /// @param n1 Amount of base token added\n  /// @notice The token amounts must be 95% correct or this will fail the Uniswap slippage check\n  function init(uint n0, uint n1) external {\n    require(status == ProxyState.INIT_LP, \"!InitLP\");\n    require(msg.sender == creator, \"Unallowed call\");\n    status = ProxyState.READY;\n    TOKEN0.token.safeTransferFrom(msg.sender, address(this), n0);\n    TOKEN1.token.safeTransferFrom(msg.sender, address(this), n1);\n    TOKEN0.token.safeIncreaseAllowance(address(POS_MGR), n0);\n    TOKEN1.token.safeIncreaseAllowance(address(POS_MGR), n1);\n    (tokenId, liquidity, , ) = POS_MGR.mint( \n      INonfungiblePositionManager.MintParams({\n         token0: address(TOKEN0.token),\n         token1: address(TOKEN1.token),\n         fee: feeTier * 100,\n         tickLower: lowerTick,\n         tickUpper: upperTick,\n         amount0Desired: n0,\n         amount1Desired: n1,\n         amount0Min: n0 * 95 / 100,\n         amount1Min: n1 * 95 / 100,\n         recipient: address(this),\n         deadline: block.timestamp\n      })\n    );\n    \n    // Transfer remaining assets back to user\n    TOKEN0.token.safeTransfer( msg.sender,  TOKEN0.token.balanceOf(address(this)));\n    TOKEN1.token.safeTransfer(msg.sender, TOKEN1.token.balanceOf(address(this)));\n    _mint(msg.sender, 1e18);\n    emit Deposit(msg.sender, 1e18);\n  }\n  \n  \n  /// @notice Claim the accumulated Uniswap V3 trading fees\n  function claimFee() public {\n    (uint256 newFee0, uint256 newFee1) = POS_MGR.collect( \n      INonfungiblePositionManager.CollectParams({\n        tokenId: tokenId,\n        recipient: address(this),\n        amount0Max: type(uint128).max,\n        amount1Max: type(uint128).max\n      })\n    );\n    // If there's no new fees generated, skip compounding logic;\n    if ((newFee0 == 0) && (newFee1 == 0)) return;  \n    uint tf0 = newFee0 * treasuryFee / 100;\n    uint tf1 = newFee1 * treasuryFee / 100;\n    if (tf0 > 0) TOKEN0.token.safeTransfer(treasury, tf0);\n    if (tf1 > 0) TOKEN1.token.safeTransfer(treasury, tf1);\n    \n    fee0 = fee0 + newFee0 - tf0;\n    fee1 = fee1 + newFee1 - tf1;\n    \n    // Calculate expected balance,  \n    (uint256 bal0, uint256 bal1) = returnExpectedBalanceWithoutFees(0, 0);\n    \n    // If accumulated more than 1% worth of fees, compound by adding fees to Uniswap position\n    if ((fee0 * 100 > bal0 ) && (fee1 * 100 > bal1)) { \n      TOKEN0.token.safeIncreaseAllowance(address(POS_MGR), fee0);\n      TOKEN1.token.safeIncreaseAllowance(address(POS_MGR), fee1);\n      (uint128 newLiquidity, uint256 added0, uint256 added1) = POS_MGR.increaseLiquidity(\n        INonfungiblePositionManager.IncreaseLiquidityParams({\n          tokenId: tokenId,\n          amount0Desired: fee0,\n          amount1Desired: fee1,\n          amount0Min: 0,\n          amount1Min: 0,\n          deadline: block.timestamp\n        })\n      );\n      // check slippage: validate against value since token amounts can move widely\n      uint token0Price = ORACLE.getAssetPrice(address(TOKEN0.token));\n      uint token1Price = ORACLE.getAssetPrice(address(TOKEN1.token));\n      uint addedValue = added0 * token0Price / 10**TOKEN0.decimals + added1 * token1Price / 10**TOKEN1.decimals;\n      uint totalValue =   bal0 * token0Price / 10**TOKEN0.decimals +   bal1 * token1Price / 10**TOKEN1.decimals;\n      uint liquidityValue = totalValue * newLiquidity / liquidity;\n      require(addedValue > liquidityValue * 95 / 100 && liquidityValue > addedValue * 95 / 100, \"TR: Claim Fee Slippage\");\n      fee0 -= added0;\n      fee1 -= added1;\n      liquidity = liquidity + newLiquidity;\n    }\n    emit ClaimFees(newFee0, newFee1);\n  }\n  \n  \n  /// @notice Deposit assets into the range\n  /// @param n0 Amount of quote asset\n  /// @param n1 Amount of base asset\n  /// @return lpAmt Amount of LP tokens created\n  function deposit(uint256 n0, uint256 n1) external nonReentrant returns (uint256 lpAmt) {\n    // Once all assets were withdrawn after initialisation, this is considered closed\n    // Prevents TR oracle values from being too manipulatable by emptying the range and redepositing \n    require(totalSupply() > 0, \"TR Closed\"); \n    \n    claimFee();\n    TOKEN0.token.transferFrom(msg.sender, address(this), n0);\n    TOKEN1.token.transferFrom(msg.sender, address(this), n1);\n    \n    uint newFee0; \n    uint newFee1;\n    // Calculate proportion of deposit that goes to pending fee pool, useful to deposit exact amount of liquidity and fully repay a position\n    // Cannot repay only one side, if fees are both 0, or if one side is missing, skip adding fees here\n      // if ( fee0+fee1 == 0 || (n0 == 0 && fee0 > 0) || (n1 == 0 && fee1 > 0) ) skip  \n      // DeMorgan: !( (n0 == 0 && fee0 > 0) || (n1 == 0 && fee1 > 0) ) = !(n0 == 0 && fee0 > 0) && !(n0 == 0 && fee1 > 0)\n    if ( fee0+fee1 > 0 && ( n0 > 0 || fee0 == 0) && ( n1 > 0 || fee1 == 0 ) ){\n      address pool = V3_FACTORY.getPool(address(TOKEN0.token), address(TOKEN1.token), feeTier * 100);\n      (uint160 sqrtPriceX96,,,,,,)  = IUniswapV3Pool(pool).slot0();\n      (uint256 token0Amount, uint256 token1Amount) = LiquidityAmounts.getAmountsForLiquidity( sqrtPriceX96, TickMath.getSqrtRatioAtTick(lowerTick), TickMath.getSqrtRatioAtTick(upperTick), liquidity);\n      if (token0Amount + fee0 > 0) newFee0 = n0 * fee0 / (token0Amount + fee0);\n      if (token1Amount + fee1 > 0) newFee1 = n1 * fee1 / (token1Amount + fee1);\n      fee0 += newFee0;\n      fee1 += newFee1; \n      n0   -= newFee0;\n      n1   -= newFee1;\n    }\n\n    TOKEN0.token.safeIncreaseAllowance(address(POS_MGR), n0);\n    TOKEN1.token.safeIncreaseAllowance(address(POS_MGR), n1);\n\n    // New liquidity is indeed the amount of liquidity added, not the total, despite being unclear in Uniswap doc\n    (uint128 newLiquidity, uint256 added0, uint256 added1) = POS_MGR.increaseLiquidity(\n      INonfungiblePositionManager.IncreaseLiquidityParams({\n        tokenId: tokenId,\n        amount0Desired: n0,\n        amount1Desired: n1,\n        amount0Min: n0 * 95 / 100,\n        amount1Min: n1 * 95 / 100,\n        deadline: block.timestamp\n      })\n    );\n    \n    uint256 feeLiquidity;\n\n    // Stack too deep, so localising some variables for feeLiquidity calculations \n    // If we already clawed back fees earlier, do nothing, else we need to adjust returned liquidity\n    if ( newFee0 == 0 && newFee1 == 0 ){\n      uint256 TOKEN0_PRICE = ORACLE.getAssetPrice(address(TOKEN0.token));\n      uint256 TOKEN1_PRICE = ORACLE.getAssetPrice(address(TOKEN1.token));\n      require (TOKEN0_PRICE > 0 && TOKEN1_PRICE > 0, \"Invalid Oracle Price\");\n      // Calculate the equivalent liquidity amount of the non-yet compounded fees\n      // Assume linearity for liquidity in same tick range; calculate feeLiquidity equivalent and consider it part of base liquidity \n      feeLiquidity = newLiquidity * ( (fee0 * TOKEN0_PRICE / 10 ** TOKEN0.decimals) + (fee1 * TOKEN1_PRICE / 10 ** TOKEN1.decimals) )   \n                                    / ( (added0   * TOKEN0_PRICE / 10 ** TOKEN0.decimals) + (added1   * TOKEN1_PRICE / 10 ** TOKEN1.decimals) ); \n    }\n                                     \n    lpAmt = totalSupply() * newLiquidity / (liquidity + feeLiquidity); \n    liquidity = liquidity + newLiquidity;\n    \n    _mint(msg.sender, lpAmt);\n    TOKEN0.token.safeTransfer( msg.sender, n0 - added0);\n    TOKEN1.token.safeTransfer( msg.sender, n1 - added1);\n    emit Deposit(msg.sender, lpAmt);\n  }\n  \n  \n  /// @notice Withdraw assets from a range\n  /// @param lp Amount of tokens withdrawn\n  /// @param amount0Min Minimum amount of quote token withdrawn\n  /// @param amount1Min Minimum amount of base token withdrawn\n  function withdraw(uint256 lp, uint256 amount0Min, uint256 amount1Min) external nonReentrant returns (uint256 removed0, uint256 removed1) {\n    claimFee();\n    uint removedLiquidity = uint(liquidity) * lp / totalSupply();\n    (removed0, removed1) = POS_MGR.decreaseLiquidity(\n      INonfungiblePositionManager.DecreaseLiquidityParams({\n        tokenId: tokenId,\n        liquidity: uint128(removedLiquidity),\n        amount0Min: amount0Min,\n        amount1Min: amount1Min,\n        deadline: block.timestamp\n      })\n    );\n    liquidity = uint128(uint256(liquidity) - removedLiquidity); \n    \n    POS_MGR.collect( \n      INonfungiblePositionManager.CollectParams({\n        tokenId: tokenId,\n        recipient: msg.sender,\n        amount0Max: uint128(removed0),\n        amount1Max: uint128(removed1)\n      })\n    );\n    // Handle uncompounded fees\n    if (fee0 > 0) {\n      TOKEN0.token.safeTransfer( msg.sender, fee0 * lp / totalSupply());\n      removed0 += fee0 * lp / totalSupply();\n      fee0 -= fee0 * lp / totalSupply();\n    } \n    if (fee1 > 0) {\n      TOKEN1.token.safeTransfer(  msg.sender, fee1 * lp / totalSupply());\n      removed1 += fee1 * lp / totalSupply();\n      fee1 -= fee1 * lp / totalSupply();\n    }\n    _burn(msg.sender, lp);\n    emit Withdraw(msg.sender, lp);\n  }\n  \n\n  /// @notice Calculate the balance of underlying assets based on the assets price\n  /// @param TOKEN0_PRICE Base token price\n  /// @param TOKEN1_PRICE Quote token price\n  function returnExpectedBalanceWithoutFees(uint TOKEN0_PRICE, uint TOKEN1_PRICE) internal view returns (uint256 amt0, uint256 amt1) {\n    // if 0 get price from oracle\n    if (TOKEN0_PRICE == 0) TOKEN0_PRICE = ORACLE.getAssetPrice(address(TOKEN0.token));\n    if (TOKEN1_PRICE == 0) TOKEN1_PRICE = ORACLE.getAssetPrice(address(TOKEN1.token));\n\n    (amt0, amt1) = LiquidityAmounts.getAmountsForLiquidity( uint160( sqrt( (2 ** 192 * ((TOKEN0_PRICE * 10 ** TOKEN1.decimals) / TOKEN1_PRICE)) / ( 10 ** TOKEN0.decimals ) ) ), TickMath.getSqrtRatioAtTick(lowerTick), TickMath.getSqrtRatioAtTick(upperTick),  liquidity);\n  }\n    \n    \n  /// @notice Calculate the balance of underlying assets based on the assets price, excluding fees\n  function returnExpectedBalance(uint TOKEN0_PRICE, uint TOKEN1_PRICE) public view returns (uint256 amt0, uint256 amt1) {\n    (amt0, amt1) = returnExpectedBalanceWithoutFees(TOKEN0_PRICE, TOKEN1_PRICE);\n    amt0 += fee0;\n    amt1 += fee1;\n  }\n\n  /// @notice Return the price of LP tokens based on the underlying assets price\n  /// @param TOKEN0_PRICE Base token price\n  /// @param TOKEN1_PRICE Quote token price\n  function getValuePerLPAtPrice(uint TOKEN0_PRICE, uint TOKEN1_PRICE) public view returns (uint256 priceX1e8) {\n    if ( totalSupply() == 0 ) return 0;\n    (uint256 amt0, uint256 amt1) = returnExpectedBalance(TOKEN0_PRICE, TOKEN1_PRICE);\n    uint totalValue = TOKEN0_PRICE * amt0 / (10 ** TOKEN0.decimals) + amt1 * TOKEN1_PRICE / (10 ** TOKEN1.decimals);\n    return totalValue * 1e18 / totalSupply();\n  } \n\n  \n  /// @notice Return the price of the LP token\n  function latestAnswer() public view returns (uint256 priceX1e8) {\n    return getValuePerLPAtPrice(ORACLE.getAssetPrice(address(TOKEN0.token)), ORACLE.getAssetPrice(address(TOKEN1.token)));\n  }\n  \n  \n  /// @notice Return the underlying tokens amounts for a given TR balance excluding the fees\n  /// @param amount Amount of tokens we want the underlying amounts for\n  function getTokenAmountsExcludingFees(uint amount) public view returns (uint token0Amount, uint token1Amount){\n    address pool = V3_FACTORY.getPool(address(TOKEN0.token), address(TOKEN1.token), feeTier * 100);\n    (uint160 sqrtPriceX96,,,,,,)  = IUniswapV3Pool(pool).slot0();\n    (token0Amount, token1Amount) = LiquidityAmounts.getAmountsForLiquidity( sqrtPriceX96, TickMath.getSqrtRatioAtTick(lowerTick), TickMath.getSqrtRatioAtTick(upperTick),  uint128 ( uint(liquidity) * amount / totalSupply() ) );\n  }  \n  \n  \n  /// @notice Return the underlying tokens amounts for a given TR balance\n  /// @param amount Amount of tokens we want the underlying amounts for\n  function getTokenAmounts(uint amount) external view returns (uint token0Amount, uint token1Amount){\n    (token0Amount, token1Amount) = getTokenAmountsExcludingFees(amount);\n    token0Amount += fee0 * amount / totalSupply();\n    token1Amount += fee1 * amount / totalSupply();\n  }\n\n}"
}