forked from sherlock-audit/2023-04-splits
-
Notifications
You must be signed in to change notification settings - Fork 0
/
UniV3OracleImpl.sol
310 lines (251 loc) · 10.8 KB
/
UniV3OracleImpl.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {IUniswapV3Factory} from "v3-core/interfaces/IUniswapV3Factory.sol";
import {OracleLibrary} from "v3-periphery/libraries/OracleLibrary.sol";
import {TokenUtils} from "splits-utils/TokenUtils.sol";
import {OracleImpl} from "./OracleImpl.sol";
import {QuotePair, ConvertedQuotePair, SortedConvertedQuotePair} from "./utils/QuotePair.sol";
/// @title UniV3 Oracle Implementation
/// @author 0xSplits
/// @notice A clone-implementation of an oracle using UniswapV3 TWAP
contract UniV3OracleImpl is OracleImpl {
/// -----------------------------------------------------------------------
/// libraries
/// -----------------------------------------------------------------------
using TokenUtils for address;
/// -----------------------------------------------------------------------
/// errors
/// -----------------------------------------------------------------------
error Pool_DoesNotExist();
/// -----------------------------------------------------------------------
/// structs
/// -----------------------------------------------------------------------
struct InitParams {
address owner;
bool paused;
uint24 defaultFee;
uint32 defaultPeriod;
uint32 defaultScaledOfferFactor;
SetPairOverrideParams[] pairOverrides;
}
struct SetPairOverrideParams {
QuotePair quotePair;
PairOverride pairOverride;
}
struct PairOverride {
uint24 fee;
uint32 period;
uint32 scaledOfferFactor;
}
/// -----------------------------------------------------------------------
/// events
/// -----------------------------------------------------------------------
event SetDefaultFee(uint24 defaultFee);
event SetDefaultPeriod(uint32 defaultPeriod);
event SetDefaultScaledOfferFactor(uint32 defaultScaledOfferFactor);
event SetPairOverrides(SetPairOverrideParams[] params);
/// -----------------------------------------------------------------------
/// storage
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// storage - constants & immutables
/// -----------------------------------------------------------------------
/// @dev percentages measured in hundredths of basis points
uint32 internal constant PERCENTAGE_SCALE = 100_00_00; // = 100%
address public immutable uniV3OracleFactory;
IUniswapV3Factory public immutable uniswapV3Factory;
address public immutable weth9;
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// slot 0 - 0 byte free
/// OwnableImpl storage
/// address internal $owner;
/// 20 bytes
/// PausableImpl storage
/// bool internal $paused;
/// 1 byte
/// default uniswap pool fee
/// @dev PERCENTAGE_SCALE = 1e6 = 100_00_00 = 100%;
/// fee = 30_00 = 0.3% is the uniswap default
/// unless overriden, getQuoteAmounts will revert if a non-permitted pool fee is used
/// 3 bytes
uint24 internal $defaultFee;
/// default twap period
/// @dev unless overriden, getQuoteAmounts will revert if zero
/// 4 bytes
uint32 internal $defaultPeriod;
/// default price scaling factor
/// @dev PERCENTAGE_SCALE = 1e6 = 100_00_00 = 100% = no discount or premium
/// 99_00_00 = 99% = 1% discount to oracle; 101_00_00 = 101% = 1% premium to oracle
/// 4 bytes
uint32 internal $defaultScaledOfferFactor;
/// slot 1 - 0 bytes free
/// overrides for specific quote pairs
/// 32 bytes
mapping(address => mapping(address => PairOverride)) internal $_pairOverrides;
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
constructor(IUniswapV3Factory uniswapV3Factory_, address weth9_) {
uniV3OracleFactory = msg.sender;
uniswapV3Factory = uniswapV3Factory_;
weth9 = weth9_;
}
function initializer(InitParams calldata params_) external {
// only uniV3OracleFactory may call `initializer`
if (msg.sender != uniV3OracleFactory) revert Unauthorized();
__initOwnable(params_.owner);
$paused = params_.paused;
$defaultFee = params_.defaultFee;
$defaultPeriod = params_.defaultPeriod;
$defaultScaledOfferFactor = params_.defaultScaledOfferFactor;
_setPairOverrides(params_.pairOverrides);
}
/// -----------------------------------------------------------------------
/// functions
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external - onlyOwner
/// -----------------------------------------------------------------------
/// set defaultFee
function setDefaultFee(uint24 defaultFee_) external onlyOwner {
$defaultFee = defaultFee_;
emit SetDefaultFee(defaultFee_);
}
/// set defaultPeriod
function setDefaultPeriod(uint32 defaultPeriod_) external onlyOwner {
$defaultPeriod = defaultPeriod_;
emit SetDefaultPeriod(defaultPeriod_);
}
/// set defaultScaledOfferFactor
function setDefaultScaledOfferFactor(uint32 defaultScaledOfferFactor_) external onlyOwner {
$defaultScaledOfferFactor = defaultScaledOfferFactor_;
emit SetDefaultScaledOfferFactor(defaultScaledOfferFactor_);
}
/// set pair overrides
function setPairOverrides(SetPairOverrideParams[] calldata params_) external onlyOwner {
_setPairOverrides(params_);
emit SetPairOverrides(params_);
}
/// -----------------------------------------------------------------------
/// functions - public & external - view
/// -----------------------------------------------------------------------
function defaultFee() external view returns (uint24) {
return $defaultFee;
}
function defaultPeriod() external view returns (uint32) {
return $defaultPeriod;
}
function defaultScaledOfferFactor() external view returns (uint32) {
return $defaultScaledOfferFactor;
}
/// get pair override for an array of quote pairs
function getPairOverrides(QuotePair[] calldata quotePairs_)
external
view
returns (PairOverride[] memory pairOverrides)
{
uint256 length = quotePairs_.length;
pairOverrides = new PairOverride[](length);
for (uint256 i; i < length;) {
pairOverrides[i] = _getPairOverride(quotePairs_[i]);
unchecked {
++i;
}
}
}
/// get amounts for an array of quotes
function getQuoteAmounts(QuoteParams[] calldata quoteParams_)
external
view
override
pausable
returns (uint256[] memory quoteAmounts)
{
uint256 length = quoteParams_.length;
quoteAmounts = new uint256[](length);
for (uint256 i; i < length;) {
quoteAmounts[i] = _getQuoteAmount(quoteParams_[i]);
unchecked {
++i;
}
}
}
/// -----------------------------------------------------------------------
/// functions - private & internal
/// -----------------------------------------------------------------------
/// set pair overrides
function _setPairOverrides(SetPairOverrideParams[] calldata params_) internal {
uint256 length = params_.length;
for (uint256 i; i < length;) {
_setPairOverride(params_[i]);
unchecked {
++i;
}
}
}
/// set pair override
function _setPairOverride(SetPairOverrideParams calldata params_) internal {
SortedConvertedQuotePair memory scqp = _convertAndSortQuotePair(params_.quotePair);
$_pairOverrides[scqp.cToken0][scqp.cToken1] = params_.pairOverride;
}
/// -----------------------------------------------------------------------
/// functions - private & internal - views
/// -----------------------------------------------------------------------
/// get quote amount for a trade
function _getQuoteAmount(QuoteParams calldata quoteParams_) internal view returns (uint256) {
ConvertedQuotePair memory cqp = quoteParams_.quotePair._convert(_convertToken);
SortedConvertedQuotePair memory scqp = cqp._sort();
PairOverride memory po = _getPairOverride(scqp);
if (po.scaledOfferFactor == 0) {
po.scaledOfferFactor = $defaultScaledOfferFactor;
}
// skip oracle if converted tokens are equal
if (cqp.cBase == cqp.cQuote) {
return quoteParams_.baseAmount * po.scaledOfferFactor / PERCENTAGE_SCALE;
}
if (po.fee == 0) {
po.fee = $defaultFee;
}
if (po.period == 0) {
po.period = $defaultPeriod;
}
address pool = uniswapV3Factory.getPool(scqp.cToken0, scqp.cToken1, po.fee);
if (pool == address(0)) {
revert Pool_DoesNotExist();
}
// reverts if period is zero or > oldest observation
(int24 arithmeticMeanTick,) = OracleLibrary.consult({pool: pool, secondsAgo: po.period});
uint256 unscaledAmountToBeneficiary = OracleLibrary.getQuoteAtTick({
tick: arithmeticMeanTick,
baseAmount: quoteParams_.baseAmount,
baseToken: cqp.cBase,
quoteToken: cqp.cQuote
});
return unscaledAmountToBeneficiary * po.scaledOfferFactor / PERCENTAGE_SCALE;
}
/// get pair override
function _getPairOverride(QuotePair calldata quotePair_) internal view returns (PairOverride memory) {
return _getPairOverride(_convertAndSortQuotePair(quotePair_));
}
/// get pair overrides
function _getPairOverride(SortedConvertedQuotePair memory scqp_) internal view returns (PairOverride memory) {
return $_pairOverrides[scqp_.cToken0][scqp_.cToken1];
}
/// convert & sort tokens into canonical order
function _convertAndSortQuotePair(QuotePair calldata quotePair_)
internal
view
returns (SortedConvertedQuotePair memory)
{
return quotePair_._convert(_convertToken)._sort();
}
/// convert eth (0x0) to weth
function _convertToken(address token_) internal view returns (address) {
return token_._isETH() ? weth9 : token_;
}
}