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-20 21:04:08 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-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             : }

Generated by: LCOV version 1.16