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 { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
9 : import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.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, FeeData, FeeDataForOracle, InstanceWithFee } from "./interfaces/IFeeOracle.sol";
18 :
19 : import { InstanceRegistry } from "./InstanceRegistry.sol";
20 :
21 : /**
22 : * @title FeeManagerLegacyStorage
23 : * @author AlienTornadosaurusHex
24 : * @dev This is contract will help us layout storage properly for a proxy upgrade for the impl
25 : * FeeOracleManager (formerly FeeManager).
26 : */
27 : contract FeeManagerLegacyStorage {
28 : /**
29 : * @dev From first contract
30 : */
31 : uint24 private _deprecatedUniswapTornPoolSwappingFee;
32 :
33 : /**
34 : * @dev From first contract
35 : */
36 : uint32 private _deprecatedUniswapTimePeriod;
37 :
38 : /**
39 : * @dev From first contract
40 : */
41 : uint24 private _deprecatedFeeUpdateInterval;
42 :
43 : /**
44 : * @dev From first contract, only used for initialization to preserve old values
45 : */
46 : mapping(ITornadoInstance => uint160) internal _oldFeesForInstance;
47 :
48 : /**
49 : * @dev From first contract, only used for initialization to preserve old values
50 : */
51 : mapping(ITornadoInstance => uint256) internal _oldFeesForInstanceUpdateTime;
52 : }
53 :
54 : /**
55 : * @title FeeOracleManager
56 : * @author AlienTornadosaurusHex
57 : * @notice A contract which manages fee oracles and received data for other contracts to consume.
58 : * @dev This is an improved version of the FeeManager with a modified design from the original contract.
59 : */
60 : contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
61 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
62 :
63 : /**
64 : * @notice Divide protocol fee by this to get the percent value
65 : */
66 : uint32 public constant FEE_PERCENT_DIVISOR = 10_000;
67 :
68 : /**
69 : * @notice The Governance Proxy address
70 : */
71 : address public immutable governanceProxyAddress;
72 :
73 : /**
74 : * @notice The TORN token
75 : */
76 : IERC20 public immutable torn;
77 :
78 : /**
79 : * @notice The contract which is allowed to update instance fees
80 : */
81 : address public feeUpdaterAddress;
82 :
83 : /**
84 : * @notice The InstanceRegistry contract
85 : */
86 : InstanceRegistry public instanceRegistry;
87 :
88 : /**
89 : * @notice Each instance has a dedicated fee oracle, these only compute the values
90 : */
91 : mapping(ITornadoInstance => IFeeOracle) public instanceFeeOracles;
92 :
93 : /**
94 : * @notice The data for each instance will be stored in this contract
95 : */
96 : mapping(ITornadoInstance => FeeData) public feesByInstance;
97 :
98 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
99 :
100 : event FeeUpdated(address indexed instance, uint256 newFee);
101 : event FeeUpdateIntervalUpdated(uint24 newLimit);
102 : event InstanceFeePercentUpdated(address indexed instance, uint32 newFeePercent);
103 :
104 : event OracleUpdated(address indexed instance, address oracle);
105 : event InstanceRegistryUpdated(address newAddress);
106 : event FeeUpdaterUpdated(address newFeeUpdaterAddress);
107 :
108 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
109 :
110 : constructor(address _tornTokenAddress, address _governanceProxyAddress) public {
111 : torn = IERC20(_tornTokenAddress);
112 : governanceProxyAddress = _governanceProxyAddress;
113 : }
114 :
115 : modifier onlyGovernance() {
116 : require(msg.sender == governanceProxyAddress, "FeeOracleManager: onlyGovernance");
117 : _;
118 : }
119 :
120 : modifier onlyFeeUpdater() {
121 : require(msg.sender == feeUpdaterAddress, "FeeOracleManager: onlyFeeUpdater");
122 : _;
123 : }
124 :
125 : function version() public pure virtual returns (string memory) {
126 1 : return "v2-oracle-manager";
127 : }
128 :
129 : /**
130 : * @dev If there will be a need to initialize the proxy again, simply pad storage and inherit again,
131 : * making sure to not reference old data anywhere.
132 : */
133 : function initialize(
134 : address _uniswapFeeOracle,
135 : address _instanceRegistryAddress,
136 : uint32 _feeUpdateInterval,
137 : ITornadoInstance[] memory _instances,
138 : uint256[] memory _percents
139 : ) external onlyGovernance initializer {
140 : // Get num of existing instances
141 4 : uint256 numInstances = _instances.length;
142 :
143 4 : for (uint256 i = 0; i < numInstances; i++) {
144 : // For each instance
145 68 : ITornadoInstance instance = _instances[i];
146 :
147 : // Store it's old data and the percent fees which Governance will command
148 68 : feesByInstance[instance] = FeeData({
149 : amount: _oldFeesForInstance[instance],
150 : percent: uint32(_percents[i]),
151 : updateInterval: _feeUpdateInterval,
152 : lastUpdateTime: uint32(_oldFeesForInstanceUpdateTime[instance])
153 : });
154 :
155 : // All old pools use the uniswap fee oracle
156 68 : instanceFeeOracles[instance] = IFeeOracle(_uniswapFeeOracle);
157 : }
158 :
159 : // Finally also store the instance registry
160 4 : instanceRegistry = InstanceRegistry(_instanceRegistryAddress);
161 : }
162 :
163 : function updateAllFees(bool _respectFeeUpdateInterval)
164 : public
165 : virtual
166 : returns (uint160[] memory newFees)
167 : {
168 4 : return updateFees(instanceRegistry.getAllInstances(), _respectFeeUpdateInterval);
169 : }
170 :
171 : function updateFees(ITornadoInstance[] memory _instances, bool _respectFeeUpdateInterval)
172 : public
173 : virtual
174 : returns (uint160[] memory newFees)
175 : {
176 6 : uint256 numInstances = _instances.length;
177 :
178 6 : newFees = new uint160[](numInstances);
179 :
180 6 : for (uint256 i = 0; i < numInstances; i++) {
181 86 : newFees[i] = updateFee(_instances[i], _respectFeeUpdateInterval);
182 : }
183 : }
184 :
185 : function updateFee(ITornadoInstance _instance, bool _respectFeeUpdateInterval)
186 : public
187 : virtual
188 : onlyFeeUpdater
189 : returns (uint160)
190 : {
191 : // Get fee data & oracle
192 113 : FeeData memory fee = feesByInstance[_instance];
193 113 : IFeeOracle oracle = instanceFeeOracles[_instance];
194 :
195 : // Check whether the instance is registered
196 113 : require(address(oracle) != address(0), "FeeOracleManager: instance has no oracle");
197 :
198 : // Now update if we do not respect the interval or we respect it and are in the interval
199 113 : if (!_respectFeeUpdateInterval || (fee.updateInterval <= now - fee.lastUpdateTime)) {
200 : // Prepare data for the process
201 94 : InstanceWithFee memory _feeInstance = populateInstanceWithFeeData(_instance, fee);
202 :
203 : // Allow oracle to gate for fee manager and implement own logic
204 94 : oracle.update(torn, _feeInstance);
205 :
206 : // There must a be a fee set otherwise it's just 0
207 94 : fee.amount = fee.percent != 0 ? oracle.getFee(torn, _feeInstance) : 0;
208 :
209 : // Store
210 94 : feesByInstance[_instance] = FeeData({
211 : amount: fee.amount,
212 : percent: fee.percent,
213 : updateInterval: fee.updateInterval,
214 : lastUpdateTime: uint32(now)
215 : });
216 :
217 : // Log
218 94 : emit FeeUpdated(address(_instance), fee.amount);
219 : }
220 :
221 : // On update, we still return out the old amount to avoid sandwiching shenanigans
222 113 : return fee.amount;
223 : }
224 :
225 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
226 :
227 : function setInstanceRegistry(address _newInstanceRegistryProxyAddress) external onlyGovernance {
228 1 : instanceRegistry = InstanceRegistry(_newInstanceRegistryProxyAddress);
229 1 : emit InstanceRegistryUpdated(_newInstanceRegistryProxyAddress);
230 : }
231 :
232 : function setFeeOracle(address _instanceAddress, address _oracleAddress) external onlyGovernance {
233 : // Prepare all contracts
234 12 : ITornadoInstance instance = ITornadoInstance(_instanceAddress);
235 12 : IFeeOracle oracle = IFeeOracle(_oracleAddress);
236 :
237 : // Nominally fee percent should be set first for an instance, but we cannot be sure
238 : // whether fee percent 0 is intentional, so we don't check
239 12 : FeeData memory fee = feesByInstance[instance];
240 :
241 : // An address(0) oracle means we're removing an oracle for an instance
242 12 : if (_oracleAddress != address(0)) {
243 : // Prepare data for the process
244 11 : InstanceWithFee memory _feeInstance = populateInstanceWithFeeData(instance, fee);
245 :
246 : // Reverts if oracle doesn't implement
247 11 : oracle.update(torn, _feeInstance);
248 :
249 : // Reverts if oracle doesn't implement
250 11 : fee.amount = oracle.getFee(torn, _feeInstance);
251 :
252 : // Note down updated fee
253 11 : feesByInstance[instance] = FeeData({
254 : amount: fee.amount,
255 : percent: fee.percent,
256 : updateInterval: fee.updateInterval,
257 : lastUpdateTime: uint32(now)
258 : });
259 :
260 : // Log fee update
261 11 : emit FeeUpdated(_instanceAddress, fee.amount);
262 : }
263 :
264 : // Ok, set the oracle
265 12 : instanceFeeOracles[instance] = oracle;
266 :
267 : // Log oracle update
268 12 : emit OracleUpdated(_instanceAddress, _oracleAddress);
269 : }
270 :
271 : function setFeePercentForInstance(ITornadoInstance _instance, uint32 _newFeePercent)
272 : external
273 : onlyGovernance
274 : {
275 14 : feesByInstance[_instance].percent = _newFeePercent;
276 14 : emit InstanceFeePercentUpdated(address(_instance), _newFeePercent);
277 : }
278 :
279 : function setFeeUpdateIntervalForInstance(ITornadoInstance _instance, uint24 newLimit)
280 : external
281 : onlyGovernance
282 : {
283 12 : feesByInstance[_instance].updateInterval = newLimit;
284 12 : emit FeeUpdateIntervalUpdated(newLimit);
285 : }
286 :
287 : function setFeeUpdater(address _newFeeUpdaterAddress) external onlyGovernance {
288 12 : feeUpdaterAddress = _newFeeUpdaterAddress;
289 12 : emit FeeUpdaterUpdated(_newFeeUpdaterAddress);
290 : }
291 :
292 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
293 :
294 : function getUpdatedFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
295 1 : return instanceFeeOracles[instance].getFee(torn, populateInstanceWithFeeData(instance));
296 : }
297 :
298 : function populateInstanceWithFeeData(ITornadoInstance _regularInstance)
299 : public
300 : view
301 : virtual
302 : returns (InstanceWithFee memory)
303 : {
304 3 : return populateInstanceWithFeeData(_regularInstance, feesByInstance[_regularInstance]);
305 : }
306 :
307 : function populateInstanceWithFeeData(ITornadoInstance _regularInstance, FeeData memory _fee)
308 : public
309 : view
310 : virtual
311 : returns (InstanceWithFee memory)
312 : {
313 126 : return InstanceWithFee({
314 : logic: _regularInstance,
315 : state: instanceRegistry.getInstanceState(_regularInstance),
316 : fee: FeeDataForOracle({
317 : amount: _fee.amount,
318 : percent: _fee.percent,
319 : divisor: FEE_PERCENT_DIVISOR,
320 : updateInterval: _fee.updateInterval,
321 : lastUpdateTime: _fee.lastUpdateTime
322 : })
323 : });
324 : }
325 :
326 : function getLastFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
327 13 : return feesByInstance[instance].amount;
328 : }
329 :
330 : function getLastUpdatedTimeForInstance(ITornadoInstance instance) public view virtual returns (uint32) {
331 3 : return feesByInstance[instance].lastUpdateTime;
332 : }
333 :
334 : function getFeePercentForInstance(ITornadoInstance instance) public view virtual returns (uint32) {
335 1 : return feesByInstance[instance].percent;
336 : }
337 :
338 : function getFeeUpdateIntervalForInstance(ITornadoInstance instance)
339 : public
340 : view
341 : virtual
342 : returns (uint32)
343 : {
344 1 : return feesByInstance[instance].updateInterval;
345 : }
346 :
347 : function getAllFeeDeviations() public view virtual returns (int256[] memory) {
348 1 : return getFeeDeviationsForInstances(instanceRegistry.getAllInstances());
349 : }
350 :
351 : function getFeeDeviationsForInstances(ITornadoInstance[] memory _instances)
352 : public
353 : view
354 : virtual
355 : returns (int256[] memory deviations)
356 : {
357 2 : uint256 numInstances = _instances.length;
358 :
359 2 : deviations = new int256[](numInstances);
360 :
361 2 : for (uint256 i = 0; i < numInstances; i++) {
362 18 : ITornadoInstance instance = _instances[i];
363 :
364 18 : FeeData memory fee = feesByInstance[instance];
365 :
366 18 : uint256 marketFee =
367 18 : instanceFeeOracles[instance].getFee(torn, populateInstanceWithFeeData(instance, fee));
368 :
369 18 : if (marketFee != 0) {
370 9 : deviations[i] = int256((fee.amount * 1000) / marketFee) - 1000;
371 : }
372 : }
373 : }
374 : }
|