-
Notifications
You must be signed in to change notification settings - Fork 10
/
UniHelper.sol
148 lines (117 loc) · 5.53 KB
/
UniHelper.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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import "@uniswap/v3-periphery/contracts/libraries/PositionKey.sol";
import "../vendors/IUniswapV3PoolOracle.sol";
import "./Constants.sol";
library UniHelper {
uint256 internal constant _ORACLE_PERIOD = 30 minutes;
function getSqrtPrice(address uniswapPoolAddress) internal view returns (uint160 sqrtPrice) {
(sqrtPrice,,,,,,) = IUniswapV3Pool(uniswapPoolAddress).slot0();
}
/**
* Gets square root of time weighted average price.
*/
function getSqrtTWAP(address uniswapPoolAddress) internal view returns (uint160 sqrtTwapX96) {
(sqrtTwapX96,) = callUniswapObserve(IUniswapV3Pool(uniswapPoolAddress), _ORACLE_PERIOD);
}
/**
* sqrt price in stable token
*/
function convertSqrtPrice(uint160 sqrtPriceX96, bool isQuoteZero) internal pure returns (uint160) {
if (isQuoteZero) {
return uint160((Constants.Q96 << Constants.RESOLUTION) / sqrtPriceX96);
} else {
return sqrtPriceX96;
}
}
function callUniswapObserve(IUniswapV3Pool uniswapPool, uint256 ago) internal view returns (uint160, uint256) {
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = uint32(ago);
secondsAgos[1] = 0;
(bool success, bytes memory data) =
address(uniswapPool).staticcall(abi.encodeWithSelector(IUniswapV3PoolOracle.observe.selector, secondsAgos));
if (!success) {
if (keccak256(data) != keccak256(abi.encodeWithSignature("Error(string)", "OLD"))) {
revertBytes(data);
}
(,, uint16 index, uint16 cardinality,,,) = uniswapPool.slot0();
(uint32 oldestAvailableAge,,, bool initialized) = uniswapPool.observations((index + 1) % cardinality);
if (!initialized) {
(oldestAvailableAge,,,) = uniswapPool.observations(0);
}
ago = block.timestamp - oldestAvailableAge;
secondsAgos[0] = uint32(ago);
(success, data) = address(uniswapPool).staticcall(
abi.encodeWithSelector(IUniswapV3PoolOracle.observe.selector, secondsAgos)
);
if (!success) {
revertBytes(data);
}
}
int56[] memory tickCumulatives = abi.decode(data, (int56[]));
int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int256(ago)));
uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(tick);
return (sqrtPriceX96, ago);
}
function revertBytes(bytes memory errMsg) internal pure {
if (errMsg.length > 0) {
assembly {
revert(add(32, errMsg), mload(errMsg))
}
}
revert("e/empty-error");
}
function getFeeGrowthInsideLast(address uniswapPoolAddress, int24 tickLower, int24 tickUpper)
internal
view
returns (uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128)
{
bytes32 positionKey = PositionKey.compute(address(this), tickLower, tickUpper);
// this is now updated to the current transaction
(, feeGrowthInside0LastX128, feeGrowthInside1LastX128,,) =
IUniswapV3Pool(uniswapPoolAddress).positions(positionKey);
}
function getFeeGrowthInside(address uniswapPoolAddress, int24 tickLower, int24 tickUpper)
internal
view
returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128)
{
(, int24 tickCurrent,,,,,) = IUniswapV3Pool(uniswapPoolAddress).slot0();
uint256 feeGrowthGlobal0X128 = IUniswapV3Pool(uniswapPoolAddress).feeGrowthGlobal0X128();
uint256 feeGrowthGlobal1X128 = IUniswapV3Pool(uniswapPoolAddress).feeGrowthGlobal1X128();
// calculate fee growth below
uint256 feeGrowthBelow0X128;
uint256 feeGrowthBelow1X128;
unchecked {
{
(,, uint256 lowerFeeGrowthOutside0X128, uint256 lowerFeeGrowthOutside1X128,,,,) =
IUniswapV3Pool(uniswapPoolAddress).ticks(tickLower);
if (tickCurrent >= tickLower) {
feeGrowthBelow0X128 = lowerFeeGrowthOutside0X128;
feeGrowthBelow1X128 = lowerFeeGrowthOutside1X128;
} else {
feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lowerFeeGrowthOutside0X128;
feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lowerFeeGrowthOutside1X128;
}
}
// calculate fee growth above
uint256 feeGrowthAbove0X128;
uint256 feeGrowthAbove1X128;
{
(,, uint256 upperFeeGrowthOutside0X128, uint256 upperFeeGrowthOutside1X128,,,,) =
IUniswapV3Pool(uniswapPoolAddress).ticks(tickUpper);
if (tickCurrent < tickUpper) {
feeGrowthAbove0X128 = upperFeeGrowthOutside0X128;
feeGrowthAbove1X128 = upperFeeGrowthOutside1X128;
} else {
feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upperFeeGrowthOutside0X128;
feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upperFeeGrowthOutside1X128;
}
}
feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128;
feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128;
}
}
}