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 : }