-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathPositionValue.sol
166 lines (154 loc) · 6.97 KB
/
PositionValue.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.8 <0.9.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;
}
}
}