Line data Source code
1 : // SPDX-License-Identifier: MIT 2 : 3 : pragma solidity ^0.6.12; 4 : pragma experimental ABIEncoderV2; 5 : 6 : import { console2 } from "forge-std/console2.sol"; 7 : // OZ imports 8 : 9 : import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; 10 : import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 11 : 12 : // Uniswap imports 13 : 14 : import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; 15 : import { IUniswapV3PoolState } from "@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol"; 16 : 17 : // Tornado imports 18 : 19 : import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol"; 20 : 21 : // Local imports 22 : 23 : import { IUniswapV2Pair } from "./interfaces/IUniswapV2Pair.sol"; 24 : 25 : import { IFeeOracle, InstanceWithFee } from "./interfaces/IFeeOracle.sol"; 26 : 27 : import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol"; 28 : 29 : import { UniswapV2OracleLibrary } from "./libraries/UniswapV2OracleLibrary.sol"; 30 : 31 : import { FixedPoint } from "./libraries/FixedPoint.sol"; 32 : 33 : import { InstanceState } from "./InstanceRegistry.sol"; 34 : 35 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OUR CONTRACT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 36 : 37 : struct TWAPData { 38 : FixedPoint.uq112x112 averagePrice; 39 : uint32 updatedTimestamp; 40 : } 41 : 42 : contract UniswapFeeOracle is IFeeOracle { 43 : using SafeMath for uint256; 44 : using FixedPoint for *; 45 : 46 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 47 : 48 : /** 49 : * @notice The precision when calculating TWAP without some factor is 3 digits. We use this to scale, 50 : * while being aware of uint224 max size. 51 : */ 52 : uint256 public constant Q112Ratio = 1_000_000_000; 53 : 54 : /** 55 : * @notice Address of the Uniswap V2 TORN/ETH pool 56 : */ 57 : address public constant UNIV2_TORN_ETH_ADDR = 0x0C722a487876989Af8a05FFfB6e32e45cc23FB3A; 58 : 59 : /** 60 : * @notice The Governance Proxy address 61 : */ 62 : address public immutable governanceProxyAddress; 63 : 64 : /** 65 : * @notice The Fee Oracle Manager address 66 : */ 67 : address public feeOracleManagerAddress; 68 : 69 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ V3 ORACLE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 70 : 71 : /** 72 : * @notice Uniswap V3 pool fees for each token registered 73 : */ 74 : mapping(IERC20 => uint24) public poolFeesByToken; 75 : 76 : /** 77 : * @notice The minimum observation cardinality for other pools 78 : */ 79 : uint16 public minObservationCardinality; 80 : 81 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ V2 TWAP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 82 : 83 : /** 84 : * @notice Uniswap V2 torn pool cumulative price last 85 : */ 86 : uint256 public lastCumulativeTORNPriceInETH; 87 : 88 : /** 89 : * @notice Last TWAP data 90 : */ 91 : TWAPData public last; 92 : 93 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 94 : 95 : event PoolFeeUpdated(IERC20 token, uint24 newPoolFee); 96 : event MinObservationCardinalityUpdated(uint16 newMinObservationCardinality); 97 : event FeeOracleManagerAddressUpdated(address newFeeOracleManagerAddress); 98 : 99 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 100 : 101 : constructor(address _governanceProxyAddress, address _feeOracleManagerAddress) public { 102 : governanceProxyAddress = _governanceProxyAddress; 103 : feeOracleManagerAddress = _feeOracleManagerAddress; 104 : 105 : // We're immediately doing this because we want to immediately be ready with a price on execution 106 : 107 : (, uint256 _price1CumulativeLast, uint32 _timestamp) = 108 : UniswapV2OracleLibrary.currentCumulativePrices(UNIV2_TORN_ETH_ADDR); 109 : 110 : lastCumulativeTORNPriceInETH = _price1CumulativeLast; 111 : 112 : last.updatedTimestamp = _timestamp; 113 : } 114 : 115 : modifier onlyGovernance() { 116 : require(msg.sender == governanceProxyAddress, "UniswapFeeOracle: onlyGovernance"); 117 : _; 118 : } 119 : 120 : modifier onlyFeeOracleManager() { 121 : require(msg.sender == feeOracleManagerAddress, "UniswapFeeOracle: onlyFeeOracleManager"); 122 : _; 123 : } 124 : 125 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MAIN ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 126 : 127 : function getFee(IERC20, InstanceWithFee memory _instance) 128 : public 129 : view 130 : virtual 131 : override 132 : returns (uint160) 133 : { 134 : // If 0%, 0 135 58 : if (_instance.fee.percent == 0) return 0; 136 : 137 : // If it's not an ERC20 it has to be ETH, use the WETH token 138 49 : _instance.state.token = 139 : _instance.state.isERC20 ? _instance.state.token : IERC20(UniswapV3OracleHelper.WETH); 140 : 141 : // Now get the number of TORN per TOKEN 142 49 : uint256 tornPerToken = getTORNPerToken(_instance.state.token, _instance.fee.updateInterval); 143 : 144 : // DENOMINATION IN TOKEN * TORN PER TOKEN * FEE_NUMERATOR / FEE_DIVISOR = FEE IN TORN 145 49 : return uint160( 146 : _instance.logic.denomination().mul(tornPerToken).div(1e18).mul(uint256(_instance.fee.percent)).div( 147 : uint256(_instance.fee.divisor) 148 : ) 149 : ); 150 : } 151 : 152 : function update(IERC20, InstanceWithFee memory _instance) public virtual override onlyFeeOracleManager { 153 : // Get the timestamp of the last update 154 77 : uint32 timestampLastUpdate = last.updatedTimestamp; 155 : 156 : // Get the current one by Uniswaps logic, has inbuilt overflow 157 77 : uint32 currentTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp(); 158 : 159 : // Calculate elapsed time, no matter whether overflow (uniswap logic) 160 77 : uint32 elapsed = currentTimestamp - timestampLastUpdate; 161 : 162 : // Multiple instances may refer to this oracle 163 : // For this reason we will basically allow only one to update 164 77 : if (_instance.fee.updateInterval <= elapsed) { 165 : // Get last token0/token1 meaning TORN per ETH counterfactually 166 8 : (, uint256 _price1CumulativeLast,) = 167 8 : UniswapV2OracleLibrary.currentCumulativePrices(UNIV2_TORN_ETH_ADDR); 168 : 169 : // Save TWAP data, meaning the average price (TORN per ETH) 170 8 : last = TWAPData({ 171 : averagePrice: FixedPoint.uq112x112( 172 : uint224(((_price1CumulativeLast - lastCumulativeTORNPriceInETH) * Q112Ratio) / elapsed) 173 : ), 174 : updatedTimestamp: currentTimestamp 175 : }); 176 : 177 : // Update the cumulative value 178 8 : lastCumulativeTORNPriceInETH = _price1CumulativeLast; 179 : } 180 : } 181 : 182 : function getTORNPerToken(IERC20 _token, uint32 _interval) public view virtual returns (uint256) { 183 : // Get the average price of TOKEN in WETH 184 64 : uint256 ethPerTokenAverage = 185 64 : UniswapV3OracleHelper.getPriceOfTokenInWETH(address(_token), poolFeesByToken[_token], _interval); 186 : 187 : // TORN PER ETH * ETH PER TOKEN = TORN per TOKEN 188 64 : return ethPerTokenAverage * last.averagePrice.decode() / Q112Ratio; 189 : } 190 : 191 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 192 : 193 : function setPoolFeeForToken(IERC20 _token, uint24 _tokenPoolFee) public virtual onlyGovernance { 194 : // Check whether cardinality is initialized 195 68 : require(minObservationCardinality != 0, "UniswapFeeOracle: cardinality not initialized"); 196 : 197 : // Only do this if not zeroing out 198 68 : if (_tokenPoolFee != 0) { 199 : // Check whether a pool exists for the token + fee combination 200 : 201 52 : address poolAddress = UniswapV3OracleHelper.UniswapV3Factory.getPool( 202 : address(_token), UniswapV3OracleHelper.WETH, _tokenPoolFee 203 : ); 204 : 205 52 : require(poolAddress != address(0), "UniswapFeeOracle: pool for token and fee does not exist"); 206 : 207 : // Check whether the pool has a large enough observation cardinality 208 : 209 52 : (,,,, uint16 observationCardinalityNext,,) = IUniswapV3PoolState(poolAddress).slot0(); 210 : 211 52 : require( 212 : minObservationCardinality <= observationCardinalityNext, 213 : "UniswapFeeOracle: pool observation cardinality low" 214 : ); 215 : } 216 : 217 : // Store & log 218 : 219 68 : poolFeesByToken[_token] = _tokenPoolFee; 220 : 221 68 : emit PoolFeeUpdated(_token, _tokenPoolFee); 222 : } 223 : 224 : function setMinObservationCardinality(uint16 _newGlobalMinObservationCardinality) 225 : public 226 : virtual 227 : onlyGovernance 228 : { 229 8 : minObservationCardinality = _newGlobalMinObservationCardinality; 230 8 : emit MinObservationCardinalityUpdated(_newGlobalMinObservationCardinality); 231 : } 232 : 233 : function setFeeOracleManagerAddress(address _newFeeOracleManagerAddress) public virtual onlyGovernance { 234 1 : feeOracleManagerAddress = _newFeeOracleManagerAddress; 235 1 : emit FeeOracleManagerAddressUpdated(_newFeeOracleManagerAddress); 236 : } 237 : 238 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 239 : 240 : function getTWAPData() public view virtual returns (TWAPData memory) { 241 1 : return last; 242 : } 243 : 244 : function getAverageTORNPerETH() public view virtual returns (uint256) { 245 1 : return last.averagePrice.decode(); 246 : } 247 : 248 : function getLastUpdatedTime() public view virtual returns (uint32) { 249 1 : return last.updatedTimestamp; 250 : } 251 : }