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 : // Minor code change here we won't resolve the registry by ENS 56 : constructor(address governanceAddress, address tornAddress, address _relayerRegistry) public { 57 : Governance = ITornadoGovernance(governanceAddress); 58 : torn = IERC20(tornAddress); 59 : relayerRegistry = _relayerRegistry; 60 : ratioConstant = IERC20(tornAddress).totalSupply(); 61 : } 62 : 63 : /** 64 : * @notice This function should safely send a user his rewards. 65 : * @dev IMPORTANT FUNCTION: 66 : * We know that rewards are going to be updated every time someone locks or unlocks 67 : * so we know that this function can't be used to falsely increase the amount of 68 : * lockedTorn by locking in governance and subsequently calling it. 69 : * - set rewards to 0 greedily 70 : */ 71 : function getReward() external { 72 0 : uint256 rewards = _updateReward(msg.sender, Governance.lockedBalance(msg.sender)); 73 0 : rewards = rewards.add(accumulatedRewards[msg.sender]); 74 0 : accumulatedRewards[msg.sender] = 0; 75 0 : torn.safeTransfer(msg.sender, rewards); 76 0 : emit RewardsClaimed(msg.sender, rewards); 77 : } 78 : 79 : /** 80 : * @notice This function should increment the proper amount of rewards per torn for the contract 81 : * @dev IMPORTANT FUNCTION: 82 : * - calculation must not overflow with extreme values 83 : * (amount <= 1e25) * 1e25 / (balance of vault <= 1e25) -> (extreme values) 84 : * @param amount amount to add to the rewards 85 : */ 86 : function addBurnRewards(uint256 amount) external { 87 0 : require(msg.sender == address(Governance) || msg.sender == relayerRegistry, "unauthorized"); 88 0 : accumulatedRewardPerTorn = accumulatedRewardPerTorn.add( 89 : amount.mul(ratioConstant).div(torn.balanceOf(address(Governance.userVault()))) 90 : ); 91 : } 92 : 93 : /** 94 : * @notice This function should allow governance to properly update the accumulated rewards rate for an 95 : * account 96 : * @param account address of account to update data for 97 : * @param amountLockedBeforehand the balance locked beforehand in the governance contract 98 : * 99 : */ 100 : function updateRewardsOnLockedBalanceChange(address account, uint256 amountLockedBeforehand) 101 : external 102 : onlyGovernance 103 : { 104 0 : uint256 claimed = _updateReward(account, amountLockedBeforehand); 105 0 : accumulatedRewards[account] = accumulatedRewards[account].add(claimed); 106 : } 107 : 108 : /** 109 : * @notice This function should allow governance rescue tokens from the staking rewards contract 110 : * 111 : */ 112 : function withdrawTorn(uint256 amount) external onlyGovernance { 113 0 : if (amount == type(uint256).max) amount = torn.balanceOf(address(this)); 114 0 : torn.safeTransfer(address(Governance), amount); 115 : } 116 : 117 : /** 118 : * @notice This function should calculated the proper amount of rewards attributed to user since the last 119 : * update 120 : * @dev IMPORTANT FUNCTION: 121 : * - calculation must not overflow with extreme values 122 : * (accumulatedReward <= 1e25) * (lockedBeforehand <= 1e25) / 1e25 123 : * - result may go to 0, since this implies on 1 TORN locked => accumulatedReward <= 1e7, meaning a 124 : * very small reward 125 : * @param account address of account to calculate rewards for 126 : * @param amountLockedBeforehand the balance locked beforehand in the governance contract 127 : * @return claimed the rewards attributed to user since the last update 128 : */ 129 : function _updateReward(address account, uint256 amountLockedBeforehand) 130 : private 131 : returns (uint256 claimed) 132 : { 133 0 : if (amountLockedBeforehand != 0) { 134 0 : claimed = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul( 135 : amountLockedBeforehand 136 : ).div(ratioConstant); 137 : } 138 0 : accumulatedRewardRateOnLastUpdate[account] = accumulatedRewardPerTorn; 139 0 : emit RewardsUpdated(account, claimed); 140 : } 141 : 142 : /** 143 : * @notice This function should show a user his rewards. 144 : * @param account address of account to calculate rewards for 145 : */ 146 : function checkReward(address account) external view returns (uint256 rewards) { 147 0 : uint256 amountLocked = Governance.lockedBalance(account); 148 0 : if (amountLocked != 0) { 149 0 : rewards = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul( 150 : amountLocked 151 : ).div(ratioConstant); 152 : } 153 0 : rewards = rewards.add(accumulatedRewards[account]); 154 : } 155 : }