-
Notifications
You must be signed in to change notification settings - Fork 9
/
UniswapOracleLibrary.sol
169 lines (143 loc) · 8.58 KB
/
UniswapOracleLibrary.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
167
168
169
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
/// @title Oracle library
/// @notice Provides functions to integrate with V3 pool oracle
library UniswapOracleLibrary {
/// @notice Calculates time-weighted means of tick and liquidity for a given Uniswap V3 pool
/// @param pool Address of the pool that we want to observe
/// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means
/// @return arithmeticMeanTick The arithmetic mean tick from (block.timestamp - secondsAgo) to block.timestamp
/// @return harmonicMeanLiquidity The harmonic mean liquidity from (block.timestamp - secondsAgo) to block.timestamp
function consult(
address pool,
uint32 secondsAgo
)
internal
view
returns (int24 arithmeticMeanTick, uint128 harmonicMeanLiquidity)
{
require(secondsAgo != 0, "BP");
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = secondsAgo;
secondsAgos[1] = 0;
(int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) =
IUniswapV3Pool(pool).observe(secondsAgos);
int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
uint160 secondsPerLiquidityCumulativesDelta =
secondsPerLiquidityCumulativeX128s[1] - secondsPerLiquidityCumulativeX128s[0];
// NOTE: Changed to match versions
// arithmeticMeanTick = int24(tickCumulativesDelta / secondsAgo);
arithmeticMeanTick = int24(tickCumulativesDelta) / int24(int32(secondsAgo));
// Always round to negative infinity
// NOTE: Changed to match versions
// if (tickCumulativesDelta < 0 && (tickCumulativesDelta % secondsAgo != 0)) arithmeticMeanTick--;
if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int56(int32(secondsAgo)) != 0)) arithmeticMeanTick--;
// We are multiplying here instead of shifting to ensure that harmonicMeanLiquidity doesn't overflow uint128
uint192 secondsAgoX160 = uint192(secondsAgo) * type(uint160).max;
harmonicMeanLiquidity = uint128(secondsAgoX160 / (uint192(secondsPerLiquidityCumulativesDelta) << 32));
}
// /// @notice Given a tick and a token amount, calculates the amount of token received in exchange
// /// @param tick Tick value used to calculate the quote
// /// @param baseAmount Amount of token to be converted
// /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination
// /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination
// /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken
// function getQuoteAtTick(
// int24 tick,
// uint128 baseAmount,
// address baseToken,
// address quoteToken
// ) internal pure returns (uint256 quoteAmount) {
// uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);
// // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself
// if (sqrtRatioX96 <= type(uint128).max) {
// uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96;
// quoteAmount = baseToken < quoteToken
// ? Math.mulDiv(ratioX192, baseAmount, 1 << 192)
// : Math.mulDiv(1 << 192, baseAmount, ratioX192);
// } else {
// uint256 ratioX128 = Math.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64);
// quoteAmount = baseToken < quoteToken
// ? Math.mulDiv(ratioX128, baseAmount, 1 << 128)
// : Math.mulDiv(1 << 128, baseAmount, ratioX128);
// }
// }
// /// @notice Given a pool, it returns the number of seconds ago of the oldest stored observation
// /// @param pool Address of Uniswap V3 pool that we want to observe
// /// @return secondsAgo The number of seconds ago of the oldest observation stored for the pool
// function getOldestObservationSecondsAgo(address pool) internal view returns (uint32 secondsAgo) {
// (, , uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0();
// require(observationCardinality > 0, 'NI');
// (uint32 observationTimestamp, , , bool initialized) =
// IUniswapV3Pool(pool).observations((observationIndex + 1) % observationCardinality);
// // The next index might not be initialized if the cardinality is in the process of increasing
// // In this case the oldest observation is always in index 0
// if (!initialized) {
// (observationTimestamp, , , ) = IUniswapV3Pool(pool).observations(0);
// }
// secondsAgo = uint32(block.timestamp) - observationTimestamp;
// }
// /// @notice Given a pool, it returns the tick value as of the start of the current block
// /// @param pool Address of Uniswap V3 pool
// /// @return The tick that the pool was in at the start of the current block
// function getBlockStartingTickAndLiquidity(address pool) internal view returns (int24, uint128) {
// (, int24 tick, uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0();
// // 2 observations are needed to reliably calculate the block starting tick
// require(observationCardinality > 1, 'NEO');
// // If the latest observation occurred in the past, then no tick-changing trades have happened in this block
// // therefore the tick in `slot0` is the same as at the beginning of the current block.
// // We don't need to check if this observation is initialized - it is guaranteed to be.
// (uint32 observationTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, ) =
// IUniswapV3Pool(pool).observations(observationIndex);
// if (observationTimestamp != uint32(block.timestamp)) {
// return (tick, IUniswapV3Pool(pool).liquidity());
// }
// uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality;
// (
// uint32 prevObservationTimestamp,
// int56 prevTickCumulative,
// uint160 prevSecondsPerLiquidityCumulativeX128,
// bool prevInitialized
// ) = IUniswapV3Pool(pool).observations(prevIndex);
// require(prevInitialized, 'ONI');
// uint32 delta = observationTimestamp - prevObservationTimestamp;
// tick = int24((tickCumulative - prevTickCumulative) / delta);
// uint128 liquidity =
// uint128(
// (uint192(delta) * type(uint160).max) /
// (uint192(secondsPerLiquidityCumulativeX128 - prevSecondsPerLiquidityCumulativeX128) << 32)
// );
// return (tick, liquidity);
// }
// /// @notice Information for calculating a weighted arithmetic mean tick
// struct WeightedTickData {
// int24 tick;
// uint128 weight;
// }
// /// @notice Given an array of ticks and weights, calculates the weighted arithmetic mean tick
// /// @param weightedTickData An array of ticks and weights
// /// @return weightedArithmeticMeanTick The weighted arithmetic mean tick
// /// @dev Each entry of `weightedTickData` should represents ticks from pools with the same underlying pool
// tokens. If they do not,
// /// extreme care must be taken to ensure that ticks are comparable (including decimal differences).
// /// @dev Note that the weighted arithmetic mean tick corresponds to the weighted geometric mean price.
// function getWeightedArithmeticMeanTick(WeightedTickData[] memory weightedTickData)
// internal
// pure
// returns (int24 weightedArithmeticMeanTick)
// {
// // Accumulates the sum of products between each tick and its weight
// int256 numerator;
// // Accumulates the sum of the weights
// uint256 denominator;
// // Products fit in 152 bits, so it would take an array of length ~2**104 to overflow this logic
// for (uint256 i; i < weightedTickData.length; i++) {
// numerator += weightedTickData[i].tick * int256(weightedTickData[i].weight);
// denominator += weightedTickData[i].weight;
// }
// weightedArithmeticMeanTick = int24(numerator / int256(denominator));
// // Always round to negative infinity
// if (numerator < 0 && (numerator % int256(denominator) != 0)) weightedArithmeticMeanTick--;
// }
}