LCOV - code coverage report
Current view: top level - v2 - RelayerRegistry.sol (source / functions) Hit Total Coverage
Test: lcov.info Lines: 6 65 9.2 %
Date: 2023-06-19 00:07:36 Functions: 1 24 4.2 %
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 { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
       9             : import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      10             : import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
      11             : import { Address } from "@openzeppelin/contracts/utils/Address.sol";
      12             : import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
      13             : 
      14             : // Tornado imports
      15             : 
      16             : import { TORN } from "torn-token/contracts/TORN.sol";
      17             : import { EnsResolve } from "torn-token/contracts/ENS.sol";
      18             : 
      19             : // Local imports
      20             : 
      21             : import { IENS } from "./interfaces/IENS.sol";
      22             : 
      23             : import { ENSNamehash } from "./libraries/ENSNamehash.sol";
      24             : 
      25             : import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
      26             : 
      27             : import { FeeOracleManager } from "./FeeOracleManager.sol";
      28             : 
      29             : contract RegistryLegacyStorage {
      30             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LEGACY STORAGE, ABANDONED ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
      31             : 
      32             :     /**
      33             :      * @dev From Initializable.sol of first contract
      34             :      */
      35             :     bool private _deprecatedInitialized;
      36             : 
      37             :     /**
      38             :      * @dev From Initializable.sol of first contract
      39             :      */
      40             :     bool private _deprecatedInitializing;
      41             : 
      42             :     /**
      43             :      * @dev This one will be moved for visibility
      44             :      */
      45             :     address private _deprecatedRouterAddress;
      46             : 
      47             :     /**
      48             :      * @dev We are not using this one because we will pull some magic
      49             :      */
      50             :     uint256 private _deprecatedMinStakeAmount;
      51             : 
      52             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LEGACY STORAGE, IN USE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
      53             : 
      54             :     /**
      55             :      * @notice Relayer metadata: their (non-refundable) TORN balances and ENS node
      56             :      */
      57             :     mapping(address => RelayerMetadata) public metadata;
      58             : 
      59             :     /**
      60             :      * @notice Subaddresses which may work for a given relayer
      61             :      */
      62             :     mapping(address => address) public workers;
      63             : }
      64             : 
      65             : interface MinimumStakingAmountOracle {
      66             :     function calculateMinimumStakingAmount() external view returns (uint256);
      67             : }
      68             : 
      69             : struct RelayerMetadata {
      70             :     uint256 balance;
      71             :     bytes32 node;
      72             : }
      73             : 
      74             : contract RelayerRegistry is RegistryLegacyStorage, EnsResolve, Initializable {
      75             :     using SafeMath for uint256;
      76             :     using SafeERC20 for IERC20;
      77             : 
      78             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NEW STORAGE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
      79             : 
      80             :     /**
      81             :      * @notice Basis points are 1/100th of a %
      82             :      */
      83             :     uint256 public constant BIP_DIVISOR = 10_000;
      84             : 
      85             :     /**
      86             :      * @notice The address of the Governance proxy.
      87             :      */
      88             :     address public immutable governanceProxyAddress;
      89             : 
      90             :     /**
      91             :      * @notice The TORN token.
      92             :      */
      93             :     TORN public immutable torn;
      94             : 
      95             :     /**
      96             :      * @notice The ENS Registry.
      97             :      */
      98             :     IENS public ens;
      99             : 
     100             :     /**
     101             :      * @notice The Fee Oracle Manager contract.
     102             :      */
     103             :     FeeOracleManager public feeOracleManager;
     104             : 
     105             :     /**
     106             :      * @notice The Staking Rewards contract.
     107             :      */
     108             :     TornadoStakingRewards public stakingRewards;
     109             : 
     110             :     /**
     111             :      * @notice The address from which slashes may come from.
     112             :      */
     113             :     address public routerAddress;
     114             : 
     115             :     /**
     116             :      * @notice Since registrations are maybe relatively frequent and this boils down to a view getter, we can
     117             :      * use a bytes20 to either read out a min stake amount as a number or to fetch it from an address.
     118             :      */
     119             :     bytes20 public minStakeAmountOracle;
     120             : 
     121             :     /**
     122             :      * @notice What the kickback to stakers is on a slash
     123             :      */
     124             :     uint256 public stakerKickbackBips;
     125             : 
     126             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
     127             : 
     128             :     event ENSUpdated(address ens);
     129             :     event RouterAddressUpdated(address routerAddress);
     130             :     event StakingRewardsUpdated(address stakingRewards);
     131             :     event FeeOracleManagerUpdated(address feeOracleManagerAddress);
     132             :     event MinimumStakingAmountOracleUpdated(bytes20 oracle, bool isContract);
     133             : 
     134             :     event WorkerRegistered(address relayer, address worker);
     135             :     event WorkerUnregistered(address relayer, address worker);
     136             : 
     137             :     event StakeAddedToRelayer(address relayer, uint256 amountStakeAdded);
     138             :     event StakeBurned(address relayer, uint256 amountBurned);
     139             : 
     140             :     event RelayerRegistered(string ensName, bytes32 relayer, address relayerAddress, uint256 stakedAmount);
     141             :     event RelayerSlashed(
     142             :         address indexed relayer, address beneficiary, uint256 slashedAmount, uint256 stakerKickbackBips
     143             :     );
     144             : 
     145             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
     146             : 
     147             :     constructor(address _governanceProxyAddress, address _tornTokenAddress) public {
     148             :         governanceProxyAddress = _governanceProxyAddress;
     149             :         torn = TORN(_tornTokenAddress);
     150             :     }
     151             : 
     152             :     modifier onlyGovernance() {
     153             :         require(msg.sender == governanceProxyAddress, "RelayerRegistry: onlyGovernance");
     154             :         _;
     155             :     }
     156             : 
     157             :     modifier onlyRouter() {
     158             :         require(msg.sender == routerAddress, "RelayerRegistry: onlyRouter");
     159             :         _;
     160             :     }
     161             : 
     162             :     modifier onlyRelayer(address _possibleRelayer, address _possibleWorker) {
     163             :         require(isRegisteredRelayer(_possibleRelayer, _possibleWorker), "RelayerRegistry: onlyRelayer");
     164             :         _;
     165             :     }
     166             : 
     167             :     modifier onlyENSOwner(address _relayer, bytes32 _node) {
     168             :         require(_relayer == ens.owner(_node), "RelayerRegistry: onlyENSOwner");
     169             :         _;
     170             :     }
     171             : 
     172             :     function version() public pure virtual returns (string memory) {
     173           0 :         return "v2-slashing-and-oracles";
     174             :     }
     175             : 
     176             :     function initialize(
     177             :         address _ensAddress,
     178             :         address _routerAddress,
     179             :         address _feeOracleManagerAddress,
     180             :         address _stakingRewardsAddress,
     181             :         bytes20 _minStakeAmountOracle,
     182             :         uint256 _stakerKickbackBips
     183             :     ) public virtual initializer {
     184           4 :         feeOracleManager = FeeOracleManager(_feeOracleManagerAddress);
     185           4 :         stakingRewards = TornadoStakingRewards(_stakingRewardsAddress);
     186           4 :         minStakeAmountOracle = _minStakeAmountOracle;
     187           4 :         stakerKickbackBips = _stakerKickbackBips;
     188           4 :         routerAddress = _routerAddress;
     189           4 :         ens = IENS(_ensAddress);
     190             :     }
     191             : 
     192             :     function register(string calldata _name, uint256 _staked, address[] calldata _workers) public virtual {
     193           0 :         _register(_name, ENSNamehash.namehash(bytes(_name)), msg.sender, _staked, _workers);
     194             :     }
     195             : 
     196             :     function registerPermit(
     197             :         string calldata _name,
     198             :         uint256 _staked,
     199             :         address[] calldata _workers,
     200             :         address _relayer,
     201             :         uint256 _deadline,
     202             :         uint8 v,
     203             :         bytes32 r,
     204             :         bytes32 s
     205             :     ) public virtual {
     206           0 :         torn.permit(_relayer, address(this), _staked, _deadline, v, r, s);
     207           0 :         _register(_name, ENSNamehash.namehash(bytes(_name)), _relayer, _staked, _workers);
     208             :     }
     209             : 
     210             :     function _register(
     211             :         string memory _name,
     212             :         bytes32 _node,
     213             :         address _relayer,
     214             :         uint256 _staked,
     215             :         address[] calldata _workers
     216             :     ) internal onlyENSOwner(_relayer, _node) {
     217           0 :         bytes32 currentNodeValue = metadata[_relayer].node;
     218             : 
     219           0 :         require(
     220             :             workers[_relayer] == address(0) && currentNodeValue == bytes32(0),
     221             :             "RelayerRegistry: already registered"
     222             :         );
     223             : 
     224           0 :         require(getMinimumStakingAmount() <= _staked, "RelayerRegistry: stake too low");
     225             : 
     226           0 :         IERC20(torn).safeTransferFrom(_relayer, address(stakingRewards), _staked);
     227             : 
     228           0 :         metadata[_relayer] = RelayerMetadata({ balance: _staked, node: _node });
     229             : 
     230           0 :         workers[_relayer] = _relayer;
     231             : 
     232           0 :         for (uint256 i = 0; i < _workers.length; i++) {
     233           0 :             _registerWorker(_relayer, _workers[i]);
     234             :         }
     235             : 
     236           0 :         emit RelayerRegistered(_name, _node, _relayer, _staked);
     237             :     }
     238             : 
     239             :     function registerWorker(address _relayer, address _worker)
     240             :         public
     241             :         virtual
     242             :         onlyRelayer(_relayer, msg.sender)
     243             :     {
     244           0 :         _registerWorker(_relayer, _worker);
     245             :     }
     246             : 
     247             :     function _registerWorker(address _relayer, address _worker) internal {
     248           0 :         require(workers[_worker] == address(0), "RelayerRegistry: already registered");
     249           0 :         workers[_worker] = _relayer;
     250           0 :         emit WorkerRegistered(_relayer, _worker);
     251             :     }
     252             : 
     253             :     function unregisterWorker(address _worker) public virtual {
     254           0 :         if (msg.sender != _worker) {
     255           0 :             require(workers[_worker] == msg.sender, "RelayerRegistry: sender must own worker");
     256             :         }
     257             : 
     258           0 :         require(workers[_worker] != _worker, "RelayerRegistry: cant remove owner");
     259             : 
     260           0 :         workers[_worker] = address(0);
     261             : 
     262           0 :         emit WorkerUnregistered(workers[_worker], _worker);
     263             :     }
     264             : 
     265             :     function stakeToRelayer(address _relayer, uint256 _staked) public virtual {
     266           0 :         _addStake(msg.sender, _relayer, _staked);
     267             :     }
     268             : 
     269             :     function stakeToRelayerPermit(
     270             :         address _relayer,
     271             :         uint256 _staked,
     272             :         address _staker,
     273             :         uint256 _deadline,
     274             :         uint8 v,
     275             :         bytes32 r,
     276             :         bytes32 s
     277             :     ) public virtual {
     278           0 :         torn.permit(_staker, address(this), _staked, _deadline, v, r, s);
     279           0 :         _addStake(_staker, _relayer, _staked);
     280             :     }
     281             : 
     282             :     function _addStake(address _staker, address _relayer, uint256 _staked)
     283             :         internal
     284             :         onlyRelayer(_relayer, _relayer)
     285             :     {
     286           0 :         IERC20(torn).safeTransferFrom(_staker, address(stakingRewards), _staked);
     287           0 :         metadata[_relayer].balance = _staked.add(metadata[_relayer].balance);
     288           0 :         emit StakeAddedToRelayer(_relayer, _staked);
     289             :     }
     290             : 
     291             :     function deductBalance(address _sender, address _relayer, uint256 _burned)
     292             :         public
     293             :         virtual
     294             :         onlyRouter
     295             :         onlyRelayer(_relayer, _sender)
     296             :     {
     297           0 :         metadata[_relayer].balance = metadata[_relayer].balance.sub(_burned);
     298           0 :         stakingRewards.addBurnRewards(_burned);
     299           0 :         emit StakeBurned(_relayer, _burned);
     300             :     }
     301             : 
     302             :     function slashRelayer(address _beneficiary, address _relayer) public virtual onlyRouter {
     303             :         // Store to reward
     304           0 :         uint256 balToSlash = metadata[_relayer].balance;
     305             : 
     306             :         // Slash
     307           0 :         metadata[_relayer].balance = 0;
     308             : 
     309             :         // We can't give the slasher the full balance because in that case the relayer will just slash himself
     310           0 :         stakingRewards.rewardSlasher(
     311             :             _beneficiary, (balToSlash * (BIP_DIVISOR - stakerKickbackBips)) / BIP_DIVISOR
     312             :         );
     313             : 
     314             :         // Give rest to Gov stakers
     315           0 :         stakingRewards.addBurnRewards((balToSlash * stakerKickbackBips) / BIP_DIVISOR);
     316             : 
     317           0 :         emit RelayerSlashed(_relayer, _beneficiary, balToSlash, stakerKickbackBips);
     318             :     }
     319             : 
     320             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
     321             : 
     322             :     function setENS(address _newENSAddress) public virtual onlyGovernance {
     323           0 :         ens = IENS(_newENSAddress);
     324           0 :         emit ENSUpdated(_newENSAddress);
     325             :     }
     326             : 
     327             :     function setRouterAddress(address _newRouterAddress) public virtual onlyGovernance {
     328           0 :         routerAddress = _newRouterAddress;
     329           0 :         emit RouterAddressUpdated(_newRouterAddress);
     330             :     }
     331             : 
     332             :     function setStakingRewards(address _newStakingRewardsAddress) public virtual onlyGovernance {
     333           0 :         stakingRewards = TornadoStakingRewards(_newStakingRewardsAddress);
     334           0 :         emit StakingRewardsUpdated(_newStakingRewardsAddress);
     335             :     }
     336             : 
     337             :     function setFeeOracleManager(address _newFeeOracleManagerAddress) public virtual onlyGovernance {
     338           0 :         feeOracleManager = FeeOracleManager(_newFeeOracleManagerAddress);
     339           0 :         emit FeeOracleManagerUpdated(_newFeeOracleManagerAddress);
     340             :     }
     341             : 
     342             :     function setMinimumStakingAmountOracle(bytes20 _newOracle, bool _isContractOracle)
     343             :         public
     344             :         virtual
     345             :         onlyGovernance
     346             :     {
     347           0 :         bool oracleIsContract = Address.isContract(address(_newOracle));
     348           0 :         if (_isContractOracle) {
     349           0 :             require(oracleIsContract, "RelayerRegistry: oracle is not contract");
     350             :         } else {
     351           0 :             require(!oracleIsContract, "RelayerRegistry: oracle is contract");
     352             :         }
     353           0 :         minStakeAmountOracle = _newOracle;
     354           0 :         emit MinimumStakingAmountOracleUpdated(_newOracle, oracleIsContract);
     355             :     }
     356             : 
     357             :     /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
     358             : 
     359             :     function isWorker(address _possibleWorker) public view virtual returns (bool) {
     360           0 :         return workers[_possibleWorker] != address(0);
     361             :     }
     362             : 
     363             :     function isRegisteredRelayer(address _possibleRelayer, address _possibleWorker)
     364             :         public
     365             :         view
     366             :         virtual
     367             :         returns (bool)
     368             :     {
     369           0 :         return _possibleRelayer == workers[_possibleWorker];
     370             :     }
     371             : 
     372             :     function getRelayerENSNode(address _relayer) public view virtual returns (bytes32) {
     373           0 :         return metadata[workers[_relayer]].node;
     374             :     }
     375             : 
     376             :     function getRelayerBalance(address _relayer) public view virtual returns (uint256) {
     377           0 :         return metadata[workers[_relayer]].balance;
     378             :     }
     379             : 
     380             :     function getOwningRelayerOfWorker(address _worker) public view virtual returns (address) {
     381           0 :         return workers[_worker];
     382             :     }
     383             : 
     384             :     function getMinimumStakingAmount() public view virtual returns (uint256) {
     385           0 :         bytes20 _minStakeAmountOracle = minStakeAmountOracle;
     386           0 :         if (Address.isContract(address(_minStakeAmountOracle))) {
     387           0 :             return MinimumStakingAmountOracle(address(_minStakeAmountOracle)).calculateMinimumStakingAmount();
     388             :         }
     389           0 :         return uint160(_minStakeAmountOracle);
     390             :     }
     391             : }

Generated by: LCOV version 1.16