-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathCoreUniswapFeed.sol
145 lines (108 loc) · 4.62 KB
/
CoreUniswapFeed.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
// SPDX-License-Identifier: BUSL 1.1
pragma solidity =0.8.22;
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import "v3-core/interfaces/IUniswapV3Pool.sol";
import "v3-core/libraries/FixedPoint96.sol";
import "v3-core/libraries/TickMath.sol";
import "v3-core/libraries/FullMath.sol";
import "./interfaces/IPriceFeed.sol";
// Returns TWAPs for WBTC and WETH for associated Uniswap v3 pools.
// Prices are returned with 18 decimals.
contract CoreUniswapFeed is IPriceFeed
{
// 30 minute TWAP period to resist price manipulation
uint256 public constant TWAP_PERIOD = 30 minutes;
// Uniswap v3 pool addresses
IUniswapV3Pool immutable public UNISWAP_V3_WBTC_WETH;
IUniswapV3Pool immutable public UNISWAP_V3_WETH_USDC;
IERC20 immutable public wbtc;
IERC20 immutable public weth;
IERC20 immutable public usdc;
bool immutable public wbtc_wethFlipped;
bool immutable public weth_usdcFlipped;
constructor( IERC20 _wbtc, IERC20 _weth, IERC20 _usdc, address _UNISWAP_V3_WBTC_WETH, address _UNISWAP_V3_WETH_USDC )
{
UNISWAP_V3_WBTC_WETH = IUniswapV3Pool(_UNISWAP_V3_WBTC_WETH);
UNISWAP_V3_WETH_USDC = IUniswapV3Pool(_UNISWAP_V3_WETH_USDC);
usdc = _usdc;
wbtc = _wbtc;
weth = _weth;
// Non-flipped is WBTC/WETH order
wbtc_wethFlipped = address(weth) < address(wbtc);
// Non-flipped is WETH/USDC order
weth_usdcFlipped = address(usdc) < address(weth);
}
// Returns amount of token0 * (10**18) given token1
function _getUniswapTwapWei( IUniswapV3Pool pool, uint256 twapInterval ) public view returns (uint256)
{
uint32[] memory secondsAgo = new uint32[](2);
secondsAgo[0] = uint32(twapInterval); // from (before)
secondsAgo[1] = 0; // to (now)
// Get the historical tick data using the observe() function
(int56[] memory tickCumulatives, ) = pool.observe(secondsAgo);
int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(uint56(twapInterval)));
uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick( tick );
uint256 p = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, FixedPoint96.Q96 );
uint8 decimals0 = ( ERC20( pool.token0() ) ).decimals();
uint8 decimals1 = ( ERC20( pool.token1() ) ).decimals();
if ( decimals1 > decimals0 )
return FullMath.mulDiv( 10 ** ( 18 + decimals1 - decimals0 ), FixedPoint96.Q96, p );
if ( decimals0 > decimals1 )
return ( FixedPoint96.Q96 * ( 10 ** 18 ) ) / ( p * ( 10 ** ( decimals0 - decimals1 ) ) );
return ( FixedPoint96.Q96 * ( 10 ** 18 ) ) / p;
}
// Wrap the _getUniswapTwapWei function in a public function that includes a try/catch.
// Returns zero on any type of failure.
// virtual - really just needed for the derived unit tests
function getUniswapTwapWei( IUniswapV3Pool pool, uint256 twapInterval ) public virtual view returns (uint256)
{
// Initialize return value to 0
uint256 twap = 0;
try this._getUniswapTwapWei(pool, twapInterval) returns (uint256 result)
{
twap = result;
}
catch (bytes memory)
{
// In case of failure, twap will remain 0
}
return twap;
}
// Uses WBTC/WETH and WETH/USDC pools because they have much higher TVL than just the Uniswap v3 WBTC/USD pool.
function getTwapWBTC( uint256 twapInterval ) public virtual view returns (uint256)
{
uint256 uniswapWBTC_WETH = getUniswapTwapWei( UNISWAP_V3_WBTC_WETH, twapInterval );
uint256 uniswapWETH_USDC = getUniswapTwapWei( UNISWAP_V3_WETH_USDC, twapInterval );
// Return zero if either is invalid
if ((uniswapWBTC_WETH == 0) || (uniswapWETH_USDC == 0 ))
return 0;
if ( wbtc_wethFlipped )
uniswapWBTC_WETH = 10**36 / uniswapWBTC_WETH;
if ( ! weth_usdcFlipped )
uniswapWETH_USDC = 10**36 / uniswapWETH_USDC;
return ( uniswapWETH_USDC * 10**18) / uniswapWBTC_WETH;
}
// virtual - really just needed for the derived unit tests
function getTwapWETH( uint256 twapInterval ) public virtual view returns (uint256)
{
uint256 uniswapWETH_USDC = getUniswapTwapWei( UNISWAP_V3_WETH_USDC, twapInterval );
// Return zero if invalid
if ( uniswapWETH_USDC == 0 )
return 0;
if ( ! weth_usdcFlipped )
return 10**36 / uniswapWETH_USDC;
else
return uniswapWETH_USDC;
}
// Returned price is the 30 minutes TWAP by default
function getPriceBTC() external view returns (uint256)
{
return getTwapWBTC( TWAP_PERIOD );
}
// Returned price is the 30 minutes TWAP by default.
// For this to be changed the DAO needs to use a new CoreUniswapFeed contract (or other contract that implements IPriceFeed.sol)
function getPriceETH() external view returns (uint256)
{
return getTwapWETH( TWAP_PERIOD );
}
}