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

Generated by: LCOV version 1.16