Line data Source code
1 : // SPDX-License-Identifier: MIT 2 : 3 : pragma solidity ^0.6.12; 4 : pragma experimental ABIEncoderV2; 5 : 6 : import { UniswapV3OracleHelper } from "../libraries/UniswapV3OracleHelper.sol"; 7 : import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; 8 : import { EnsResolve } from "torn-token/contracts/ENS.sol"; 9 : 10 : import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 11 : import "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol"; 12 : import "./InstanceRegistry.sol"; 13 : 14 : /// @dev contract which calculates the fee for each pool 15 : contract FeeManager is EnsResolve { 16 : using SafeMath for uint256; 17 : 18 : uint256 public constant PROTOCOL_FEE_DIVIDER = 10000; 19 : address public immutable torn; 20 : address public immutable governance; 21 : InstanceRegistry public immutable registry; 22 : 23 : uint24 public uniswapTornPoolSwappingFee; 24 : uint32 public uniswapTimePeriod; 25 : 26 : uint24 public updateFeeTimeLimit; 27 : 28 : mapping(ITornadoInstance => uint160) public instanceFee; 29 : mapping(ITornadoInstance => uint256) public instanceFeeUpdated; 30 : 31 : event FeeUpdated(address indexed instance, uint256 newFee); 32 : event UniswapTornPoolSwappingFeeChanged(uint24 newFee); 33 : 34 : modifier onlyGovernance() { 35 : require(msg.sender == governance); 36 : _; 37 : } 38 : 39 : struct Deviation { 40 : address instance; 41 : int256 deviation; // in 10**-1 percents, so it can be like -2.3% if the price of TORN declined 42 : } 43 : 44 : constructor( 45 : address _torn, 46 : address _governance, 47 : bytes32 _registry 48 : ) public { 49 : torn = _torn; 50 : governance = _governance; 51 : registry = InstanceRegistry(resolve(_registry)); 52 : } 53 : 54 : /** 55 : * @notice This function should update the fees of each pool 56 : */ 57 : function updateAllFees() external { 58 0 : updateFees(registry.getAllInstanceAddresses()); 59 : } 60 : 61 : /** 62 : * @notice This function should update the fees for tornado instances 63 : * (here called pools) 64 : * @param _instances pool addresses to update fees for 65 : * */ 66 : function updateFees(ITornadoInstance[] memory _instances) public { 67 0 : for (uint256 i = 0; i < _instances.length; i++) { 68 0 : updateFee(_instances[i]); 69 : } 70 : } 71 : 72 : /** 73 : * @notice This function should update the fee of a specific pool 74 : * @param _instance address of the pool to update fees for 75 : */ 76 : function updateFee(ITornadoInstance _instance) public { 77 0 : uint160 newFee = calculatePoolFee(_instance); 78 0 : instanceFee[_instance] = newFee; 79 0 : instanceFeeUpdated[_instance] = now; 80 0 : emit FeeUpdated(address(_instance), newFee); 81 : } 82 : 83 : /** 84 : * @notice This function should return the fee of a specific pool and update it if the time has come 85 : * @param _instance address of the pool to get fees for 86 : */ 87 : function instanceFeeWithUpdate(ITornadoInstance _instance) public returns (uint160) { 88 0 : if (now - instanceFeeUpdated[_instance] > updateFeeTimeLimit) { 89 0 : updateFee(_instance); 90 : } 91 0 : return instanceFee[_instance]; 92 : } 93 : 94 : /** 95 : * @notice function to update a single fee entry 96 : * @param _instance instance for which to update data 97 : * @return newFee the new fee pool 98 : */ 99 : function calculatePoolFee(ITornadoInstance _instance) public view returns (uint160) { 100 0 : (bool isERC20, IERC20 token, , uint24 uniswapPoolSwappingFee, uint32 protocolFeePercentage) = registry.instances(_instance); 101 0 : if (protocolFeePercentage == 0) { 102 0 : return 0; 103 : } 104 : 105 0 : token = token == IERC20(0) && !isERC20 ? IERC20(UniswapV3OracleHelper.WETH) : token; // for eth instances 106 0 : uint256 tokenPriceRatio = UniswapV3OracleHelper.getPriceRatioOfTokens( 107 : [torn, address(token)], 108 : [uniswapTornPoolSwappingFee, uniswapPoolSwappingFee], 109 : uniswapTimePeriod 110 : ); 111 : // prettier-ignore 112 0 : return 113 0 : uint160( 114 : _instance 115 : .denomination() 116 : .mul(UniswapV3OracleHelper.RATIO_DIVIDER) 117 : .div(tokenPriceRatio) 118 : .mul(uint256(protocolFeePercentage)) 119 : .div(PROTOCOL_FEE_DIVIDER) 120 : ); 121 : } 122 : 123 : /** 124 : * @notice function to update the uniswap fee 125 : * @param _uniswapTornPoolSwappingFee new uniswap fee 126 : */ 127 : function setUniswapTornPoolSwappingFee(uint24 _uniswapTornPoolSwappingFee) public onlyGovernance { 128 0 : uniswapTornPoolSwappingFee = _uniswapTornPoolSwappingFee; 129 0 : emit UniswapTornPoolSwappingFeeChanged(uniswapTornPoolSwappingFee); 130 : } 131 : 132 : /** 133 : * @notice This function should allow governance to set a new period for twap measurement 134 : * @param newPeriod the new period to use 135 : * */ 136 : function setPeriodForTWAPOracle(uint32 newPeriod) external onlyGovernance { 137 0 : uniswapTimePeriod = newPeriod; 138 : } 139 : 140 : /** 141 : * @notice This function should allow governance to set a new update fee time limit for instance fee updating 142 : * @param newLimit the new time limit to use 143 : * */ 144 : function setUpdateFeeTimeLimit(uint24 newLimit) external onlyGovernance { 145 0 : updateFeeTimeLimit = newLimit; 146 : } 147 : 148 : /** 149 : * @notice returns fees deviations for each instance, so it can be easily seen what instance requires an update 150 : */ 151 : function feeDeviations() public view returns (Deviation[] memory results) { 152 0 : ITornadoInstance[] memory instances = registry.getAllInstanceAddresses(); 153 0 : results = new Deviation[](instances.length); 154 : 155 0 : for (uint256 i = 0; i < instances.length; i++) { 156 0 : uint256 marketFee = calculatePoolFee(instances[i]); 157 0 : int256 deviation; 158 0 : if (marketFee != 0) { 159 0 : deviation = int256((instanceFee[instances[i]] * 1000) / marketFee) - 1000; 160 : } 161 : 162 0 : results[i] = Deviation({ instance: address(instances[i]), deviation: deviation }); 163 : } 164 : } 165 : }