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-slashing-and-oracles";
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 : ITornadoInstance[] memory _instances,
137 : uint256[] memory _percents
138 : ) external onlyGovernance initializer {
139 : // Get num of existing instances
140 4 : uint256 numInstances = _instances.length;
141 :
142 4 : for (uint256 i = 0; i < numInstances; i++) {
143 : // For each instance
144 68 : ITornadoInstance instance = _instances[i];
145 :
146 : // Store it's old data and the percent fees which Governance will command
147 68 : feesByInstance[instance] = FeeData({
148 : amount: _oldFeesForInstance[instance],
149 : percent: uint32(_percents[i]),
150 : updateInterval: 6 hours, // TODO: Bubble this up to proposal
151 : lastUpdateTime: uint32(_oldFeesForInstanceUpdateTime[instance])
152 : });
153 :
154 : // All old pools use the uniswap fee oracle
155 68 : instanceFeeOracles[instance] = IFeeOracle(_uniswapFeeOracle);
156 : }
157 :
158 : // Finally also store the instance registry
159 4 : instanceRegistry = InstanceRegistry(_instanceRegistryAddress);
160 : }
161 :
162 : function updateAllFees(bool _respectFeeUpdateInterval)
163 : public
164 : virtual
165 : returns (uint160[] memory newFees)
166 : {
167 4 : return updateFees(instanceRegistry.getAllInstances(), _respectFeeUpdateInterval);
168 : }
169 :
170 : function updateFees(ITornadoInstance[] memory _instances, bool _respectFeeUpdateInterval)
171 : public
172 : virtual
173 : returns (uint160[] memory newFees)
174 : {
175 6 : uint256 numInstances = _instances.length;
176 :
177 6 : newFees = new uint160[](numInstances);
178 :
179 6 : for (uint256 i = 0; i < numInstances; i++) {
180 86 : newFees[i] = updateFee(_instances[i], _respectFeeUpdateInterval);
181 : }
182 : }
183 :
184 : function updateFee(ITornadoInstance _instance, bool _respectFeeUpdateInterval)
185 : public
186 : virtual
187 : onlyFeeUpdater
188 : returns (uint160)
189 : {
190 : // Get fee data & oracle
191 112 : FeeData memory fee = feesByInstance[_instance];
192 112 : IFeeOracle oracle = instanceFeeOracles[_instance];
193 :
194 : // Check whether the instance is registered
195 112 : require(address(oracle) != address(0), "FeeOracleManager: instance has no oracle");
196 :
197 : // Now update if we do not respect the interval or we respect it and are in the interval
198 112 : if (!_respectFeeUpdateInterval || (fee.updateInterval <= now - fee.lastUpdateTime)) {
199 : // Prepare data for the process
200 94 : InstanceWithFee memory _feeInstance = populateInstanceWithFeeData(_instance, fee);
201 :
202 : // Allow oracle to gate for fee manager and implement own logic
203 94 : oracle.update(torn, _feeInstance);
204 :
205 : // There must a be a fee set otherwise it's just 0
206 94 : fee.amount = fee.percent != 0 ? oracle.getFee(torn, _feeInstance) : 0;
207 :
208 : // Store
209 94 : feesByInstance[_instance] = FeeData({
210 : amount: fee.amount,
211 : percent: fee.percent,
212 : updateInterval: fee.updateInterval,
213 : lastUpdateTime: uint32(now)
214 : });
215 :
216 : // Log
217 94 : emit FeeUpdated(address(_instance), fee.amount);
218 : }
219 :
220 : // On update, we still return out the old amount to avoid sandwiching shenanigans
221 112 : return fee.amount;
222 : }
223 :
224 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
225 :
226 : function setInstanceRegistry(address _newInstanceRegistryProxyAddress) external onlyGovernance {
227 1 : instanceRegistry = InstanceRegistry(_newInstanceRegistryProxyAddress);
228 1 : emit InstanceRegistryUpdated(_newInstanceRegistryProxyAddress);
229 : }
230 :
231 : function setFeeOracle(address _instanceAddress, address _oracleAddress) external onlyGovernance {
232 : // Prepare all contracts
233 12 : ITornadoInstance instance = ITornadoInstance(_instanceAddress);
234 12 : IFeeOracle oracle = IFeeOracle(_oracleAddress);
235 :
236 : // Nominally fee percent should be set first for an instance, but we cannot be sure
237 : // whether fee percent 0 is intentional, so we don't check
238 12 : FeeData memory fee = feesByInstance[instance];
239 :
240 : // An address(0) oracle means we're removing an oracle for an instance
241 12 : if (_oracleAddress != address(0)) {
242 : // Prepare data for the process
243 11 : InstanceWithFee memory _feeInstance = populateInstanceWithFeeData(instance, fee);
244 :
245 : // Reverts if oracle doesn't implement
246 11 : oracle.update(torn, _feeInstance);
247 :
248 : // Reverts if oracle doesn't implement
249 11 : fee.amount = oracle.getFee(torn, _feeInstance);
250 :
251 : // Note down updated fee
252 11 : feesByInstance[instance] = FeeData({
253 : amount: fee.amount,
254 : percent: fee.percent,
255 : updateInterval: fee.updateInterval,
256 : lastUpdateTime: uint32(now)
257 : });
258 :
259 : // Log fee update
260 11 : emit FeeUpdated(_instanceAddress, fee.amount);
261 : }
262 :
263 : // Ok, set the oracle
264 12 : instanceFeeOracles[instance] = oracle;
265 :
266 : // Log oracle update
267 12 : emit OracleUpdated(_instanceAddress, _oracleAddress);
268 : }
269 :
270 : function setFeePercentForInstance(ITornadoInstance _instance, uint32 _newFeePercent)
271 : external
272 : onlyGovernance
273 : {
274 14 : feesByInstance[_instance].percent = _newFeePercent;
275 14 : emit InstanceFeePercentUpdated(address(_instance), _newFeePercent);
276 : }
277 :
278 : function setFeeUpdateIntervalForInstance(ITornadoInstance _instance, uint24 newLimit)
279 : external
280 : onlyGovernance
281 : {
282 12 : feesByInstance[_instance].updateInterval = newLimit;
283 12 : emit FeeUpdateIntervalUpdated(newLimit);
284 : }
285 :
286 : function setFeeUpdater(address _newFeeUpdaterAddress) external onlyGovernance {
287 12 : feeUpdaterAddress = _newFeeUpdaterAddress;
288 12 : emit FeeUpdaterUpdated(_newFeeUpdaterAddress);
289 : }
290 :
291 : /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
292 :
293 : function getUpdatedFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
294 1 : return instanceFeeOracles[instance].getFee(torn, populateInstanceWithFeeData(instance));
295 : }
296 :
297 : function populateInstanceWithFeeData(ITornadoInstance _regularInstance)
298 : public
299 : view
300 : virtual
301 : returns (InstanceWithFee memory)
302 : {
303 3 : return populateInstanceWithFeeData(_regularInstance, feesByInstance[_regularInstance]);
304 : }
305 :
306 : function populateInstanceWithFeeData(ITornadoInstance _regularInstance, FeeData memory _fee)
307 : public
308 : view
309 : virtual
310 : returns (InstanceWithFee memory)
311 : {
312 126 : return InstanceWithFee({
313 : logic: _regularInstance,
314 : state: instanceRegistry.getInstanceState(_regularInstance),
315 : fee: FeeDataForOracle({
316 : amount: _fee.amount,
317 : percent: _fee.percent,
318 : divisor: FEE_PERCENT_DIVISOR,
319 : updateInterval: _fee.updateInterval,
320 : lastUpdateTime: _fee.lastUpdateTime
321 : })
322 : });
323 : }
324 :
325 : function getLastFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
326 15 : return feesByInstance[instance].amount;
327 : }
328 :
329 : function getLastUpdatedTimeForInstance(ITornadoInstance instance) public view virtual returns (uint32) {
330 3 : return feesByInstance[instance].lastUpdateTime;
331 : }
332 :
333 : function getFeePercentForInstance(ITornadoInstance instance) public view virtual returns (uint32) {
334 1 : return feesByInstance[instance].percent;
335 : }
336 :
337 : function getFeeUpdateIntervalForInstance(ITornadoInstance instance)
338 : public
339 : view
340 : virtual
341 : returns (uint32)
342 : {
343 1 : return feesByInstance[instance].updateInterval;
344 : }
345 :
346 : function getAllFeeDeviations() public view virtual returns (int256[] memory) {
347 1 : return getFeeDeviationsForInstances(instanceRegistry.getAllInstances());
348 : }
349 :
350 : function getFeeDeviationsForInstances(ITornadoInstance[] memory _instances)
351 : public
352 : view
353 : virtual
354 : returns (int256[] memory deviations)
355 : {
356 2 : uint256 numInstances = _instances.length;
357 :
358 2 : deviations = new int256[](numInstances);
359 :
360 2 : for (uint256 i = 0; i < numInstances; i++) {
361 18 : ITornadoInstance instance = _instances[i];
362 :
363 18 : FeeData memory fee = feesByInstance[instance];
364 :
365 18 : uint256 marketFee =
366 18 : instanceFeeOracles[instance].getFee(torn, populateInstanceWithFeeData(instance, fee));
367 :
368 18 : if (marketFee != 0) {
369 9 : deviations[i] = int256((fee.amount * 1000) / marketFee) - 1000;
370 : }
371 : }
372 : }
373 : }
|