168 lines
7.0 KiB
Solidity
168 lines
7.0 KiB
Solidity
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
pragma solidity >=0.6.8 <0.8.0;
|
|
|
|
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
|
|
import '@uniswap/v3-core/contracts/libraries/FixedPoint128.sol';
|
|
import '@uniswap/v3-core/contracts/libraries/TickMath.sol';
|
|
import '@uniswap/v3-core/contracts/libraries/Tick.sol';
|
|
import '../interfaces/INonfungiblePositionManager.sol';
|
|
import './LiquidityAmounts.sol';
|
|
import './PoolAddress.sol';
|
|
import './PositionKey.sol';
|
|
|
|
/// @title Returns information about the token value held in a Uniswap V3 NFT
|
|
library PositionValue {
|
|
/// @notice Returns the total amounts of token0 and token1, i.e. the sum of fees and principal
|
|
/// that a given nonfungible position manager token is worth
|
|
/// @param positionManager The Uniswap V3 NonfungiblePositionManager
|
|
/// @param tokenId The tokenId of the token for which to get the total value
|
|
/// @param sqrtRatioX96 The square root price X96 for which to calculate the principal amounts
|
|
/// @return amount0 The total amount of token0 including principal and fees
|
|
/// @return amount1 The total amount of token1 including principal and fees
|
|
function total(
|
|
INonfungiblePositionManager positionManager,
|
|
uint256 tokenId,
|
|
uint160 sqrtRatioX96
|
|
) internal view returns (uint256 amount0, uint256 amount1) {
|
|
(uint256 amount0Principal, uint256 amount1Principal) = principal(positionManager, tokenId, sqrtRatioX96);
|
|
(uint256 amount0Fee, uint256 amount1Fee) = fees(positionManager, tokenId);
|
|
return (amount0Principal + amount0Fee, amount1Principal + amount1Fee);
|
|
}
|
|
|
|
/// @notice Calculates the principal (currently acting as liquidity) owed to the token owner in the event
|
|
/// that the position is burned
|
|
/// @param positionManager The Uniswap V3 NonfungiblePositionManager
|
|
/// @param tokenId The tokenId of the token for which to get the total principal owed
|
|
/// @param sqrtRatioX96 The square root price X96 for which to calculate the principal amounts
|
|
/// @return amount0 The principal amount of token0
|
|
/// @return amount1 The principal amount of token1
|
|
function principal(
|
|
INonfungiblePositionManager positionManager,
|
|
uint256 tokenId,
|
|
uint160 sqrtRatioX96
|
|
) internal view returns (uint256 amount0, uint256 amount1) {
|
|
(, , , , , int24 tickLower, int24 tickUpper, uint128 liquidity, , , , ) = positionManager.positions(tokenId);
|
|
|
|
return
|
|
LiquidityAmounts.getAmountsForLiquidity(
|
|
sqrtRatioX96,
|
|
TickMath.getSqrtRatioAtTick(tickLower),
|
|
TickMath.getSqrtRatioAtTick(tickUpper),
|
|
liquidity
|
|
);
|
|
}
|
|
|
|
struct FeeParams {
|
|
address token0;
|
|
address token1;
|
|
uint24 fee;
|
|
int24 tickLower;
|
|
int24 tickUpper;
|
|
uint128 liquidity;
|
|
uint256 positionFeeGrowthInside0LastX128;
|
|
uint256 positionFeeGrowthInside1LastX128;
|
|
uint256 tokensOwed0;
|
|
uint256 tokensOwed1;
|
|
}
|
|
|
|
/// @notice Calculates the total fees owed to the token owner
|
|
/// @param positionManager The Uniswap V3 NonfungiblePositionManager
|
|
/// @param tokenId The tokenId of the token for which to get the total fees owed
|
|
/// @return amount0 The amount of fees owed in token0
|
|
/// @return amount1 The amount of fees owed in token1
|
|
function fees(INonfungiblePositionManager positionManager, uint256 tokenId)
|
|
internal
|
|
view
|
|
returns (uint256 amount0, uint256 amount1)
|
|
{
|
|
(
|
|
,
|
|
,
|
|
address token0,
|
|
address token1,
|
|
uint24 fee,
|
|
int24 tickLower,
|
|
int24 tickUpper,
|
|
uint128 liquidity,
|
|
uint256 positionFeeGrowthInside0LastX128,
|
|
uint256 positionFeeGrowthInside1LastX128,
|
|
uint256 tokensOwed0,
|
|
uint256 tokensOwed1
|
|
) = positionManager.positions(tokenId);
|
|
|
|
return
|
|
_fees(
|
|
positionManager,
|
|
FeeParams({
|
|
token0: token0,
|
|
token1: token1,
|
|
fee: fee,
|
|
tickLower: tickLower,
|
|
tickUpper: tickUpper,
|
|
liquidity: liquidity,
|
|
positionFeeGrowthInside0LastX128: positionFeeGrowthInside0LastX128,
|
|
positionFeeGrowthInside1LastX128: positionFeeGrowthInside1LastX128,
|
|
tokensOwed0: tokensOwed0,
|
|
tokensOwed1: tokensOwed1
|
|
})
|
|
);
|
|
}
|
|
|
|
function _fees(INonfungiblePositionManager positionManager, FeeParams memory feeParams)
|
|
private
|
|
view
|
|
returns (uint256 amount0, uint256 amount1)
|
|
{
|
|
(uint256 poolFeeGrowthInside0LastX128, uint256 poolFeeGrowthInside1LastX128) =
|
|
_getFeeGrowthInside(
|
|
IUniswapV3Pool(
|
|
PoolAddress.computeAddress(
|
|
positionManager.factory(),
|
|
PoolAddress.PoolKey({token0: feeParams.token0, token1: feeParams.token1, fee: feeParams.fee})
|
|
)
|
|
),
|
|
feeParams.tickLower,
|
|
feeParams.tickUpper
|
|
);
|
|
|
|
amount0 =
|
|
FullMath.mulDiv(
|
|
poolFeeGrowthInside0LastX128 - feeParams.positionFeeGrowthInside0LastX128,
|
|
feeParams.liquidity,
|
|
FixedPoint128.Q128
|
|
) +
|
|
feeParams.tokensOwed0;
|
|
|
|
amount1 =
|
|
FullMath.mulDiv(
|
|
poolFeeGrowthInside1LastX128 - feeParams.positionFeeGrowthInside1LastX128,
|
|
feeParams.liquidity,
|
|
FixedPoint128.Q128
|
|
) +
|
|
feeParams.tokensOwed1;
|
|
}
|
|
|
|
function _getFeeGrowthInside(
|
|
IUniswapV3Pool pool,
|
|
int24 tickLower,
|
|
int24 tickUpper
|
|
) private view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
|
|
(, int24 tickCurrent, , , , , ) = pool.slot0();
|
|
(, , uint256 lowerFeeGrowthOutside0X128, uint256 lowerFeeGrowthOutside1X128, , , , ) = pool.ticks(tickLower);
|
|
(, , uint256 upperFeeGrowthOutside0X128, uint256 upperFeeGrowthOutside1X128, , , , ) = pool.ticks(tickUpper);
|
|
|
|
if (tickCurrent < tickLower) {
|
|
feeGrowthInside0X128 = lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128;
|
|
feeGrowthInside1X128 = lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128;
|
|
} else if (tickCurrent < tickUpper) {
|
|
uint256 feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128();
|
|
uint256 feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128();
|
|
feeGrowthInside0X128 = feeGrowthGlobal0X128 - lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128;
|
|
feeGrowthInside1X128 = feeGrowthGlobal1X128 - lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128;
|
|
} else {
|
|
feeGrowthInside0X128 = upperFeeGrowthOutside0X128 - lowerFeeGrowthOutside0X128;
|
|
feeGrowthInside1X128 = upperFeeGrowthOutside1X128 - lowerFeeGrowthOutside1X128;
|
|
}
|
|
}
|
|
}
|