Line data Source code
1 : // SPDX-License-Identifier: MIT
2 :
3 : pragma solidity ^0.6.12;
4 : pragma experimental ABIEncoderV2;
5 :
6 : // OZ imports
7 :
8 : import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
9 : import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
10 :
11 : // Tornado imports
12 :
13 : import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
14 :
15 : // Local imports
16 :
17 : import { IFeeOracle, InstanceWithFee } from "./interfaces/IFeeOracle.sol";
18 :
19 : import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol";
20 :
21 : import { UniswapFeeOracle } from "./UniswapFeeOracle.sol";
22 :
23 : import { InstanceState } from "./InstanceRegistry.sol";
24 :
25 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CURVE FINANCE INTERFACE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
26 :
27 : interface ICurvePriceOracle {
28 : /// @dev For Plain2 and CryptoSwap2
29 : function price_oracle() external view returns (uint256);
30 : /// @dev For Tricrypto pools
31 : function price_oracle(uint256 k) external view returns (uint256);
32 : }
33 :
34 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CURVE FEE ORACLE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
35 :
36 : /**
37 : * @title CurveChainedOracles
38 : * @author AlienTornadosaurusHex
39 : * @notice This library is designed to help us simplify the workflow and introduce at least some packing to
40 : * not waste large amounts of gas due to simplifying. Obviously, the best solution would be to just deploy a
41 : * per-coin oracle, but I've wanted to have a general implementation because it makes the Governance process
42 : * easier.
43 : */
44 : library CurveChainedOracles {
45 : bytes4 public constant PRICE_ORACLE_SELECTOR = bytes4(keccak256(("price_oracle()")));
46 : bytes4 public constant PRICE_ORACLE_UINT256_SELECTOR = bytes4(keccak256(("price_oracle(uint256)")));
47 :
48 : /**
49 : * @notice This function prepares data for chain calling Curve oracles.
50 : * @dev The returns is called oracle because it contains sufficient data to execute the entire process,
51 : * meaning it represents an oracle in a sense.
52 : * @param _oracles Curve contracts compatible with either `price_oracle()` or `price_oracle(uint256)`.
53 : * @param _selectors Selectors for contract, each is only either `keccak256()` or
54 : * `keccak256('price_oracle(uint256)')`
55 : * @param _coins Curve pool coin ids which are used for `price_oracle(uint256)`
56 : * @param _invertPrice For each oracle capable pool, whether the token we are searching the price for is
57 : * token0 or not, or in general whether we want to take the inverse of the price.
58 : * @return oracle Packed bytes data which represents full data for a chained call to Curve oracles.
59 : */
60 : function createChainedOracle(
61 : ICurvePriceOracle[] memory _oracles,
62 : bytes4[] memory _selectors,
63 : uint8[] memory _coins,
64 : bool[] memory _invertPrice
65 : ) internal view returns (bytes memory oracle) {
66 0 : uint256 numOracles = _oracles.length;
67 :
68 0 : for (uint256 o = 0; o < numOracles; o++) {
69 : // The oracle which will be providing the price
70 0 : ICurvePriceOracle _oracle = _oracles[o];
71 :
72 : // The selector which determines the call
73 0 : bytes4 _selector = _selectors[o];
74 :
75 : // Index of the coin in the curve pool for second UINT256 selector
76 0 : uint8 _coin = _coins[o];
77 :
78 : // Check whether the config actually works
79 0 : if (_selector == PRICE_ORACLE_SELECTOR) {
80 0 : try _oracle.price_oracle() returns (uint256) { /* Works, do nothing */ }
81 : catch {
82 : require(false, "CurveChainedOracles: test call price_oracle() failed");
83 : }
84 0 : } else if (_selector == PRICE_ORACLE_UINT256_SELECTOR) {
85 0 : try _oracle.price_oracle(_coin) returns (uint256) { /* Works, do nothing */ }
86 : catch {
87 : require(false, "CurveChainedOracles: test call price_oracle(uint256) failed");
88 : }
89 : } else {
90 0 : require(false, "CurveChainedOracles: none or invalid selector");
91 : }
92 :
93 : // We prepend the former iteration, to have it in proper left to right order
94 0 : oracle = abi.encodePacked(oracle, _oracle, _selector, _coin, _invertPrice[o]);
95 : }
96 : }
97 :
98 : /**
99 : * @notice Function which unpacks `_oracle` data and chains the price calls to return one final price.
100 : * @dev The name `price_oracle` was chosen because it reflects the Curve signature of the same name.
101 : * @param _oracle The packed bytes data.
102 : * @return Price of some token in some other token E18.
103 : */
104 : function price_oracle(bytes memory _oracle) internal view returns (uint256) {
105 0 : uint256 price = 1e18;
106 0 : uint256 priceDivisor = 1e18;
107 0 : uint256 inverseNumerator = 1e36;
108 :
109 : // We know that each oracle addition encodes to exactly 26 bytes
110 0 : uint256 numOracles = _oracle.length / 26;
111 :
112 0 : for (uint256 o = 0; o < numOracles; o++) {
113 0 : bytes32 chunk;
114 :
115 : // Packed bytes can't be decoded, have to do it ourselves
116 : // mload always reads bytes32, always offset by 26 bytes because data is always stored fully
117 : // packed from left to right
118 : assembly {
119 0 : chunk := mload(add(_oracle, add(32, mul(26, o))))
120 : }
121 :
122 : // Properly read in all of the packed data
123 :
124 : // Always packed from left to right, first 20 bytes is address
125 0 : ICurvePriceOracle oracle = ICurvePriceOracle(address(bytes20(chunk)));
126 :
127 : // Then from 20 to 24 is selector, shift by 20x8=160
128 0 : bytes4 selector = bytes4(chunk << 160);
129 :
130 : // From 24 to 25 is coin index, shift by 160+4x8=192
131 0 : uint8 coin = uint8(bytes1(chunk << 192));
132 :
133 : // From 25 to 26 is whether to invert price, shift by 192+1x8=200
134 0 : bool invertPrice = bytes1(chunk << 200) == 0x01;
135 :
136 0 : uint256 priceFromOracle;
137 :
138 : // Execute according to selector
139 : // These are guaranteed to all be 1e18
140 0 : if (selector == PRICE_ORACLE_SELECTOR) {
141 0 : priceFromOracle = ICurvePriceOracle(oracle).price_oracle();
142 0 : } else if (selector == PRICE_ORACLE_UINT256_SELECTOR) {
143 0 : priceFromOracle = ICurvePriceOracle(oracle).price_oracle(coin);
144 : }
145 :
146 : // Check whether we need to invert price
147 0 : if (invertPrice) {
148 0 : priceFromOracle = inverseNumerator / priceFromOracle;
149 : }
150 :
151 : // Calc price
152 0 : price = (price * priceFromOracle) / priceDivisor;
153 : }
154 :
155 0 : return price;
156 : }
157 : }
158 :
159 : /**
160 : * @title CurveFeeOracle
161 : * @author AlienTornadosaurusHex
162 : * @notice Many Curve contracts now have an EMA/TWAP Oracle which can be used instead of having to rely on
163 : * Chainlink (Offchain) Pricefeeds or Uniswap Twap Oracles. We do not use Metapools because they do not
164 : * have a safe enough TWAP oracle, instead we use the `price_oracle` capable contracts, which use a safer
165 : * analytical solution.
166 : */
167 : contract CurveFeeOracle is IFeeOracle {
168 : using SafeMath for uint256;
169 : using CurveChainedOracles for *;
170 :
171 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
172 :
173 : /**
174 : * @notice The Governance Proxy address
175 : */
176 : address public immutable governanceProxyAddress;
177 :
178 : /**
179 : * @notice For each instance, a set of data which translates to a set of chained price oracle calls, we
180 : * call this data "chainedPriceOracles" because it encodes all data necessary to execute the chain and
181 : * calc the price
182 : */
183 : mapping(ITornadoInstance => bytes) public chainedPriceOracles;
184 :
185 : /**
186 : * @notice When setting, store the names as a historical record, key is keccak256(bytes)
187 : */
188 : mapping(bytes32 => string) public chainedPriceOracleNames;
189 :
190 : /**
191 : * @notice Our Uniswap Fee Oracle, we will need it some time for TORN/ETH
192 : */
193 : UniswapFeeOracle public uniswapFeeOracle;
194 :
195 : /**
196 : * @notice We will not need the Uniswap Oracle forever though, TORN/ETH pools are possible on Curve too
197 : */
198 : bool public tornOracleIsUniswap;
199 :
200 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
201 :
202 : event TornOracleIsCurve();
203 : event TornOracleIsUniswapV3();
204 : event UniswapFeeOracleUpdated(address _newUniswapFeeOracleAddress);
205 : event ChainedOracleUpdated(address indexed instance, bytes32 newOracleHash, string newName);
206 :
207 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
208 :
209 : constructor(address _governanceProxyAddress) public {
210 : governanceProxyAddress = _governanceProxyAddress;
211 : tornOracleIsUniswap = true;
212 : }
213 :
214 : modifier onlyGovernance() {
215 : require(msg.sender == governanceProxyAddress, "UniswapFeeOracle: onlyGovernance");
216 : _;
217 : }
218 :
219 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
220 :
221 : function update(IERC20, InstanceWithFee memory) public virtual override { }
222 :
223 : function getFee(IERC20, InstanceWithFee memory _instance)
224 : public
225 : view
226 : virtual
227 : override
228 : returns (uint160)
229 : {
230 : // If fee is 0 return
231 26 : if (_instance.fee.percent == 0) return 0;
232 :
233 : // Either ETH PER TOKEN or TORN PER TOKEN
234 16 : uint256 price = chainedPriceOracles[_instance.logic].price_oracle();
235 :
236 : // ETH PER TOKEN * TORN PER ETH = TORN PER TOKEN
237 16 : if (tornOracleIsUniswap) {
238 15 : price = price
239 : * uniswapFeeOracle.getTORNPerToken(
240 : IERC20(UniswapV3OracleHelper.WETH), _instance.fee.updateInterval
241 : ) / UniswapV3OracleHelper.RATIO_DIVIDER;
242 : }
243 :
244 : // DENOMINATION IN TOKEN * TORN PER TOKEN * FEE_NUMERATOR / FEE_DIVISOR = FEE IN TORN
245 16 : return uint160(
246 : _instance.logic.denomination().mul(price).div(UniswapV3OracleHelper.RATIO_DIVIDER).mul(
247 : uint256(_instance.fee.percent)
248 : ).div(uint256(_instance.fee.divisor))
249 : );
250 : }
251 :
252 : function getChainedOracleNameForInstance(ITornadoInstance _instance)
253 : public
254 : view
255 : virtual
256 : returns (string memory)
257 : {
258 1 : return chainedPriceOracleNames[getChainedOracleHashForInstance(_instance)];
259 : }
260 :
261 : function getChainedOracleNameForOracleHash(bytes32 _oracleHash)
262 : public
263 : view
264 : virtual
265 : returns (string memory)
266 : {
267 1 : return chainedPriceOracleNames[_oracleHash];
268 : }
269 :
270 : function getChainedOracleHashForInstance(ITornadoInstance _instance)
271 : public
272 : view
273 : virtual
274 : returns (bytes32)
275 : {
276 2 : return keccak256(chainedPriceOracles[_instance]);
277 : }
278 :
279 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
280 :
281 : function modifyChainedOracleForInstance(
282 : ITornadoInstance _instance,
283 : ICurvePriceOracle[] memory _oracles,
284 : bytes4[] memory _selectors,
285 : uint8[] memory _coins,
286 : bool[] memory _invertPrice,
287 : string memory _name
288 : ) public virtual onlyGovernance {
289 12 : bytes memory oracle = _oracles.createChainedOracle(_selectors, _coins, _invertPrice);
290 12 : bytes32 oracleHash = keccak256(oracle);
291 :
292 12 : chainedPriceOracles[_instance] = oracle;
293 12 : chainedPriceOracleNames[oracleHash] = _name;
294 :
295 12 : emit ChainedOracleUpdated(address(_instance), oracleHash, _name);
296 : }
297 :
298 : function setUniswapFeeOracle(UniswapFeeOracle _uniswapFeeOracle) public virtual onlyGovernance {
299 2 : uniswapFeeOracle = _uniswapFeeOracle;
300 2 : emit UniswapFeeOracleUpdated(address(_uniswapFeeOracle));
301 : }
302 :
303 : function setTornOracleIsUniswap(bool _is) public virtual onlyGovernance {
304 1 : tornOracleIsUniswap = _is;
305 1 : if (_is) emit TornOracleIsUniswapV3();
306 0 : else emit TornOracleIsCurve();
307 : }
308 : }
|