LCOV - code coverage report
Current view: top level - v2 - FeeOracleManager.sol (source / functions) Hit Total Coverage
Test: lcov.info Lines: 58 58 100.0 %
Date: 2023-06-19 00:07:36 Functions: 18 18 100.0 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.16