LCOV - code coverage report
Current view: top level - v2 - TornadoStakingRewards.sol (source / functions) Hit Total Coverage
Test: lcov.info Lines: 6 20 30.0 %
Date: 2023-06-19 00:07:36 Functions: 2 7 28.6 %
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             : import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
       7             : import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
       8             : import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
       9             : import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
      10             : import { EnsResolve } from "torn-token/contracts/ENS.sol";
      11             : 
      12             : interface ITornadoVault {
      13             :     function withdrawTorn(address recipient, uint256 amount) external;
      14             : }
      15             : 
      16             : interface ITornadoGovernance {
      17             :     function lockedBalance(address account) external view returns (uint256);
      18             : 
      19             :     function userVault() external view returns (ITornadoVault);
      20             : }
      21             : 
      22             : /**
      23             :  * @notice This is the staking contract of the governance staking upgrade.
      24             :  *         This contract should hold the staked funds which are received upon relayer registration,
      25             :  *         and properly attribute rewards to addresses without security issues.
      26             :  * @dev CONTRACT RISKS:
      27             :  *      - Relayer staked TORN at risk if contract is compromised.
      28             :  *
      29             :  */
      30             : contract TornadoStakingRewards is Initializable, EnsResolve {
      31             :     using SafeMath for uint256;
      32             :     using SafeERC20 for IERC20;
      33             : 
      34             :     /// @notice 1e25
      35             :     uint256 public immutable ratioConstant;
      36             :     ITornadoGovernance public immutable Governance;
      37             :     IERC20 public immutable torn;
      38             :     address public immutable relayerRegistry;
      39             : 
      40             :     /// @notice the sum torn_burned_i/locked_amount_i*coefficient where i is incremented at each burn
      41             :     uint256 public accumulatedRewardPerTorn;
      42             :     /// @notice notes down accumulatedRewardPerTorn for an address on a lock/unlock/claim
      43             :     mapping(address => uint256) public accumulatedRewardRateOnLastUpdate;
      44             :     /// @notice notes down how much an account may claim
      45             :     mapping(address => uint256) public accumulatedRewards;
      46             : 
      47             :     event RewardsUpdated(address indexed account, uint256 rewards);
      48             :     event RewardsClaimed(address indexed account, uint256 rewardsClaimed);
      49             : 
      50             :     modifier onlyGovernance() {
      51             :         require(msg.sender == address(Governance), "only governance");
      52             :         _;
      53             :     }
      54             : 
      55             :     modifier onlyRelayerRegistry() {
      56             :         require(msg.sender == relayerRegistry, "only relayer registry");
      57             :         _;
      58             :     }
      59             : 
      60             :     // Minor code change here we won't resolve the registry by ENS
      61             :     constructor(address governanceAddress, address tornAddress, address _relayerRegistry) public {
      62             :         Governance = ITornadoGovernance(governanceAddress);
      63             :         torn = IERC20(tornAddress);
      64             :         relayerRegistry = _relayerRegistry;
      65             :         ratioConstant = IERC20(tornAddress).totalSupply();
      66             :     }
      67             : 
      68             :     /**
      69             :      * @notice This function should safely send a user his rewards.
      70             :      * @dev IMPORTANT FUNCTION:
      71             :      *      We know that rewards are going to be updated every time someone locks or unlocks
      72             :      *      so we know that this function can't be used to falsely increase the amount of
      73             :      *      lockedTorn by locking in governance and subsequently calling it.
      74             :      *      - set rewards to 0 greedily
      75             :      */
      76             :     function getReward() external {
      77           0 :         uint256 rewards = _updateReward(msg.sender, Governance.lockedBalance(msg.sender));
      78           0 :         rewards = rewards.add(accumulatedRewards[msg.sender]);
      79           0 :         accumulatedRewards[msg.sender] = 0;
      80           0 :         torn.safeTransfer(msg.sender, rewards);
      81           0 :         emit RewardsClaimed(msg.sender, rewards);
      82             :     }
      83             : 
      84             :     /**
      85             :      * @notice This function should increment the proper amount of rewards per torn for the contract
      86             :      * @dev IMPORTANT FUNCTION:
      87             :      *      - calculation must not overflow with extreme values
      88             :      *        (amount <= 1e25) * 1e25 / (balance of vault <= 1e25) -> (extreme values)
      89             :      * @param amount amount to add to the rewards
      90             :      */
      91             :     function addBurnRewards(uint256 amount) external {
      92           0 :         require(msg.sender == address(Governance) || msg.sender == relayerRegistry, "unauthorized");
      93           0 :         accumulatedRewardPerTorn = accumulatedRewardPerTorn.add(
      94             :             amount.mul(ratioConstant).div(torn.balanceOf(address(Governance.userVault())))
      95             :         );
      96             :     }
      97             : 
      98             :     /**
      99             :      * @notice This function should allow governance to properly update the accumulated rewards rate for an
     100             :      * account
     101             :      * @param account address of account to update data for
     102             :      * @param amountLockedBeforehand the balance locked beforehand in the governance contract
     103             :      *
     104             :      */
     105             :     function updateRewardsOnLockedBalanceChange(address account, uint256 amountLockedBeforehand)
     106             :         external
     107             :         onlyGovernance
     108             :     {
     109           4 :         uint256 claimed = _updateReward(account, amountLockedBeforehand);
     110           4 :         accumulatedRewards[account] = accumulatedRewards[account].add(claimed);
     111             :     }
     112             : 
     113             :     /**
     114             :      * @notice This function should allow governance rescue tokens from the staking rewards contract
     115             :      */
     116             :     function withdrawTorn(uint256 amount) external onlyGovernance {
     117           0 :         if (amount == type(uint256).max) amount = torn.balanceOf(address(this));
     118           0 :         torn.safeTransfer(address(Governance), amount);
     119             :     }
     120             : 
     121             :     /**
     122             :      * @notice This function may be used to reward a slasher (since it is registry gated)
     123             :      */
     124             :     function rewardSlasher(address _slasher, uint256 _amount) external onlyRelayerRegistry {
     125           0 :         torn.safeTransfer(_slasher, _amount);
     126             :     }
     127             : 
     128             :     /**
     129             :      * @notice This function should calculated the proper amount of rewards attributed to user since the last
     130             :      * update
     131             :      * @dev IMPORTANT FUNCTION:
     132             :      *      - calculation must not overflow with extreme values
     133             :      *        (accumulatedReward <= 1e25) * (lockedBeforehand <= 1e25) / 1e25
     134             :      *      - result may go to 0, since this implies on 1 TORN locked => accumulatedReward <= 1e7, meaning a
     135             :      * very small reward
     136             :      * @param account address of account to calculate rewards for
     137             :      * @param amountLockedBeforehand the balance locked beforehand in the governance contract
     138             :      * @return claimed the rewards attributed to user since the last update
     139             :      */
     140             :     function _updateReward(address account, uint256 amountLockedBeforehand)
     141             :         private
     142             :         returns (uint256 claimed)
     143             :     {
     144           4 :         if (amountLockedBeforehand != 0) {
     145           4 :             claimed = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(
     146             :                 amountLockedBeforehand
     147             :             ).div(ratioConstant);
     148             :         }
     149           4 :         accumulatedRewardRateOnLastUpdate[account] = accumulatedRewardPerTorn;
     150           4 :         emit RewardsUpdated(account, claimed);
     151             :     }
     152             : 
     153             :     /**
     154             :      * @notice This function should show a user his rewards.
     155             :      * @param account address of account to calculate rewards for
     156             :      */
     157             :     function checkReward(address account) external view returns (uint256 rewards) {
     158           0 :         uint256 amountLocked = Governance.lockedBalance(account);
     159           0 :         if (amountLocked != 0) {
     160           0 :             rewards = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(
     161             :                 amountLocked
     162             :             ).div(ratioConstant);
     163             :         }
     164           0 :         rewards = rewards.add(accumulatedRewards[account]);
     165             :     }
     166             : }

Generated by: LCOV version 1.16