LCOV - code coverage report
Current view: top level - v2 - TornadoRouter.sol (source / functions) Hit Total Coverage
Test: lcov.info Lines: 37 37 100.0 %
Date: 2023-06-20 21:04:08 Functions: 10 10 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             : import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
      11             : import { Address } from "@openzeppelin/contracts/utils/Address.sol";
      12             : import { Math } from "@openzeppelin/contracts/math/Math.sol";
      13             : 
      14             : // Tornado Imports
      15             : 
      16             : import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
      17             : 
      18             : // Local imports
      19             : 
      20             : import { RelayerRegistry } from "./RelayerRegistry.sol";
      21             : import { FeeOracleManager } from "./FeeOracleManager.sol";
      22             : import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
      23             : import { InstanceRegistry, InstanceState } from "./InstanceRegistry.sol";
      24             : 
      25             : /**
      26             :  * @title TornadoRouter
      27             :  * @author AlienTornadosaurusHex
      28             :  * @notice This contract is a router for all Tornado Cash deposits and withdrawals
      29             :  * @dev This is an improved version of the TornadoRouter with a modified design from the original contract.
      30             :  */
      31             : contract TornadoRouter is Initializable {
      32             :     using SafeERC20 for IERC20;
      33             : 
      34             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
      35             : 
      36             :     /**
      37             :      * @notice The address of the Governance proxy
      38             :      */
      39             :     address public immutable governanceProxyAddress;
      40             : 
      41             :     /**
      42             :      * @notice The Instance Registry
      43             :      */
      44             :     InstanceRegistry public instanceRegistry;
      45             : 
      46             :     /**
      47             :      * @notice The Relayer Registry
      48             :      */
      49             :     RelayerRegistry public relayerRegistry;
      50             : 
      51             :     /**
      52             :      * @notice The Fee Oracle Manager
      53             :      */
      54             :     FeeOracleManager public feeOracleManager;
      55             : 
      56             :     /**
      57             :      * @notice The Staking Rewards contract
      58             :      */
      59             :     TornadoStakingRewards public stakingRewards;
      60             : 
      61             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
      62             : 
      63             :     event EncryptedNote(address indexed sender, bytes encryptedNote);
      64             :     event TokenApproved(address indexed spender, uint256 amount);
      65             : 
      66             :     event WithdrawalWithRelayer(
      67             :         address sender, address relayer, address instanceAddress, bytes32 nullifierHash
      68             :     );
      69             : 
      70             :     event InstanceRegistryUpdated(address newInstanceRegistryProxyAddress);
      71             :     event RelayerRegistryUpdated(address newRelayerRegistryProxyAddress);
      72             :     event FeeOracleManagerUpdated(address newFeeOracleManagerProxyAddress);
      73             :     event StakingRewardsUpdated(address newStakingRewardsProxyAddress);
      74             : 
      75             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
      76             : 
      77             :     constructor(address _governanceProxyAddress) public {
      78             :         governanceProxyAddress = _governanceProxyAddress;
      79             :     }
      80             : 
      81             :     modifier onlyInstanceRegistry() {
      82             :         require(msg.sender == address(instanceRegistry), "TornadoRouter: onlyInstanceRegistry");
      83             :         _;
      84             :     }
      85             : 
      86             :     modifier onlyGovernance() {
      87             :         require(msg.sender == governanceProxyAddress, "TornadoRouter: onlyGovernance");
      88             :         _;
      89             :     }
      90             : 
      91             :     function version() public pure virtual returns (string memory) {
      92           2 :         return "v2-oracle-manager";
      93             :     }
      94             : 
      95             :     function initialize(
      96             :         address _instanceRegistryProxyAddress,
      97             :         address _relayerRegistryProxyAddress,
      98             :         address _feeOracleManagerProxyAddress,
      99             :         address _stakingRewardsProxyAddress
     100             :     ) external onlyGovernance initializer {
     101           4 :         instanceRegistry = InstanceRegistry(_instanceRegistryProxyAddress);
     102           4 :         relayerRegistry = RelayerRegistry(_relayerRegistryProxyAddress);
     103           4 :         feeOracleManager = FeeOracleManager(_feeOracleManagerProxyAddress);
     104           4 :         stakingRewards = TornadoStakingRewards(_stakingRewardsProxyAddress);
     105             :     }
     106             : 
     107             :     /**
     108             :      * @notice Function to deposit into a Tornado instance. We don't really case if an external contract
     109             :      * breaks for deposit since deposits can go through the instances too if need be.
     110             :      * @param _tornado The instance to deposit into (address).
     111             :      * @param _commitment The commitment which will be added to the Merkle Tree.
     112             :      * @param _encryptedNote An encrypted note tied to the commitment which may be logged.
     113             :      */
     114             :     function deposit(ITornadoInstance _tornado, bytes32 _commitment, bytes calldata _encryptedNote)
     115             :         public
     116             :         payable
     117             :         virtual
     118             :     {
     119           1 :         (IERC20 token,, bool isERC20, bool isEnabled) = instanceRegistry.instanceData(_tornado);
     120             : 
     121             :         // Better than having it revert at safeTransferFrom
     122           1 :         require(isEnabled, "TornadoRouter: instance not enabled");
     123             : 
     124           1 :         if (isERC20) {
     125           1 :             token.safeTransferFrom(msg.sender, address(this), _tornado.denomination());
     126             :         }
     127             : 
     128           1 :         _tornado.deposit{ value: msg.value }(_commitment);
     129             : 
     130           1 :         emit EncryptedNote(msg.sender, _encryptedNote);
     131             :     }
     132             : 
     133             :     /**
     134             :      * @notice Withdraw from a Tornado Instance. The registry proxy will be set to 0, this will then
     135             :      * guarantee that the contract is not upgradeable and thus that it will never be able to block
     136             :      * withdrawals via the `isWorker` function. In any case, both manual users, Governance relayers
     137             :      * and non-Governance relayers may use this function to process a withdrawal. Only withdrawals
     138             :      * which include a `_relayer` field in the proof which belongs to a registered relayer, but which
     139             :      * attempt to withdraw via an unregistered worker are not allowed to withdraw.
     140             :      * @param _tornado The Tornado instance to withdraw from.
     141             :      * @param _proof Bytes proof data.
     142             :      * @param _root A current or historical bytes32 root of the Merkle Tree within the proofs context.
     143             :      * @param _nullifierHash The bytes32 nullifierHash for the deposit.
     144             :      * @param _recipient The address of recipient for withdrawn funds.
     145             :      * @param _relayer The address of the relayer which will be making the withdrawal.
     146             :      * @param _fee The fee in bips to pay the relayer.
     147             :      * @param _refund If swapping into ETH on the other side, use this to specify how much should be paid for
     148             :      * it.
     149             :      */
     150             :     function withdraw(
     151             :         ITornadoInstance _tornado,
     152             :         bytes calldata _proof,
     153             :         bytes32 _root,
     154             :         bytes32 _nullifierHash,
     155             :         address payable _recipient,
     156             :         address payable _relayer,
     157             :         uint256 _fee,
     158             :         uint256 _refund
     159             :     ) public payable virtual {
     160             :         // The registry proxy admin has been tossed meaning that below function can't be
     161             :         // upgraded to break withdrawals from the instances
     162             :         //
     163             :         // The next check confirms that `_relayer` is registered with Governance
     164           1 :         if (relayerRegistry.isWorker(_relayer)) {
     165             :             // Check whether someone is impersonating a relayer or a relayer is avoiding
     166           1 :             require(relayerRegistry.isWorkerOfRelayer(_relayer, msg.sender), "TornadoRouter: invalid relayer");
     167             : 
     168             :             // Check whether the instance is enabled
     169           1 :             require(instanceRegistry.isEnabledInstance(_tornado), "TornadoRouter: instance not enabled");
     170             : 
     171             :             // Get the fee for the instance
     172           1 :             uint256 fee = feeOracleManager.updateFee(_tornado, true);
     173             : 
     174             :             // Deduct the relayers balance
     175           1 :             relayerRegistry.deductBalance(msg.sender, _relayer, fee);
     176             : 
     177             :             // Add burn rewards
     178           1 :             stakingRewards.addBurnRewards(fee);
     179             :         }
     180             : 
     181             :         // In any case withdraw, again, can't break, above logic is based on an "immutable conditional"
     182           1 :         _tornado.withdraw{ value: msg.value }(
     183             :             _proof, _root, _nullifierHash, _recipient, _relayer, _fee, _refund
     184             :         );
     185             :     }
     186             : 
     187             :     function backupNotes(bytes[] calldata _encryptedNotes) public virtual {
     188           1 :         for (uint256 i = 0; i < _encryptedNotes.length; i++) {
     189           1 :             emit EncryptedNote(msg.sender, _encryptedNotes[i]);
     190             :         }
     191             :     }
     192             : 
     193             :     /**
     194             :      * @notice Note that this contract doesn't leave dust unless someone sends dust to it.
     195             :      */
     196             :     function rescueTokens(IERC20 _token, address payable _to, uint256 _amount)
     197             :         public
     198             :         virtual
     199             :         onlyGovernance
     200             :     {
     201           2 :         require(_to != address(0), "TORN: can not send to zero address");
     202             : 
     203           2 :         if (_token == IERC20(0)) {
     204             :             // For Ether
     205           1 :             uint256 totalBalance = address(this).balance;
     206           1 :             uint256 balance = Math.min(totalBalance, _amount);
     207           1 :             _to.transfer(balance);
     208             :         } else {
     209             :             // For any other ERC20
     210           1 :             uint256 totalBalance = _token.balanceOf(address(this));
     211           1 :             uint256 balance = Math.min(totalBalance, _amount);
     212           1 :             require(balance > 0, "TORN: trying to send 0 balance");
     213           1 :             _token.safeTransfer(_to, balance);
     214             :         }
     215             :     }
     216             : 
     217             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
     218             : 
     219             :     function approveTokenForInstance(IERC20 _token, address _spender, uint256 _amount)
     220             :         external
     221             :         onlyInstanceRegistry
     222             :     {
     223          64 :         _token.safeApprove(_spender, _amount);
     224          64 :         emit TokenApproved(_spender, _amount);
     225             :     }
     226             : 
     227             :     function setInstanceRegistry(address _newInstanceRegistryProxyAddress) external onlyGovernance {
     228           1 :         instanceRegistry = InstanceRegistry(_newInstanceRegistryProxyAddress);
     229           1 :         emit InstanceRegistryUpdated(_newInstanceRegistryProxyAddress);
     230             :     }
     231             : 
     232             :     function setFeeOracleManager(address _newFeeOracleManagerProxyAddress) external onlyGovernance {
     233           1 :         feeOracleManager = FeeOracleManager(_newFeeOracleManagerProxyAddress);
     234           1 :         emit FeeOracleManagerUpdated(_newFeeOracleManagerProxyAddress);
     235             :     }
     236             : 
     237             :     function setStakingRewards(address _newStakingRewardsProxyAddress) external onlyGovernance {
     238           1 :         stakingRewards = TornadoStakingRewards(_newStakingRewardsProxyAddress);
     239           1 :         emit StakingRewardsUpdated(_newStakingRewardsProxyAddress);
     240             :     }
     241             : }

Generated by: LCOV version 1.16