all files / contracts/ RewardPool.sol

100% Statements 19/19
68.75% Branches 11/16
90% Functions 9/10
100% Lines 27/27
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148                                                                                                        59×   59×               59× 59× 59×                                                                                                 10×                          
// SPDX-License-Identifier: MIT
 
pragma solidity >=0.6.2;
 
import '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
 
import './interfaces/IRewardPool.sol';
import './utils/Math.sol';
 
/**
 * @title Reward Pool contract
 * @dev Reward Pool keeps slashed tokens, track of who slashed how much tokens, and distributes the reward after protocol fee.
 */
contract RewardPool is IRewardPool, OwnableUpgradeable, UUPSUpgradeable {
    using SafeMath for uint256;
 
    // Token address
    address public token;
 
    // Staking contract address
    address public staking;
 
    // Protocol Fee
    uint256 public fees;
 
    // Rewards per allocation
    mapping(address => Reward[]) public rewards;
 
    uint256 public totalFee;
 
    /**
     * @dev Emitted when a new reward record is created.
     */
    event RewardAdded(
        address indexed escrowAddress,
        address indexed staker,
        address indexed slasher,
        uint256 tokens
    );
 
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }
 
    function initialize(
        address _token,
        address _staking,
        uint256 _fees
    ) external payable virtual Einitializer {
        __Ownable_init_unchained();
 
        __RewardPool_init_unchained(_token, _staking, _fees);
    }
 
    function __RewardPool_init_unchained(
        address _token,
        address _staking,
        uint256 _fees
    ) internal EonlyInitializing {
        token = _token;
        staking = _staking;
        fees = _fees;
    }
 
    /**
     * @dev Add reward record
     * Protocol fee is deducted for each reward
     */
    function addReward(
        address _escrowAddress,
        address _staker,
        address _slasher,
        uint256 _tokens
    ) external override onlyStaking {
        // If the reward is smaller than protocol fee, just keep as fee
        if (_tokens < fees) {
            totalFee = totalFee + _tokens;
            return;
        }
 
        // Deduct protocol fee for each reward
        uint256 rewardAfterFee = _tokens - fees;
        totalFee = totalFee + fees;
 
        // Add reward record
        Reward memory reward = Reward(_escrowAddress, _slasher, rewardAfterFee);
        rewards[_escrowAddress].push(reward);
 
        emit RewardAdded(_escrowAddress, _staker, _slasher, rewardAfterFee);
    }
 
    /**
     * @dev Return rewards for allocation
     */
    function getRewards(
        address _escrowAddress
    ) external view override returns (Reward[] memory) {
        return rewards[_escrowAddress];
    }
 
    /**
     * @dev Distribute rewards for allocation
     */
    function distributeReward(address _escrowAddress) external override {
        Reward[] memory rewardsForEscrow = rewards[_escrowAddress];
 
        require(rewardsForEscrow.length > 0, 'No rewards for escrow');
 
        // Delete rewards for allocation
        delete rewards[_escrowAddress];
 
        // Transfer Tokens
        for (uint256 index = 0; index < rewardsForEscrow.length; index += 1) {
            Reward memory reward = rewardsForEscrow[index];
            _safeTransfer(reward.slasher, reward.tokens);
        }
    }
 
    function withdraw(address to) external override EonlyOwner {
        uint256 amount = totalFee;
        totalFee = 0;
        _safeTransfer(to, amount);
    }
 
    function _safeTransfer(address to, uint256 value) internal {
        SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(token), to, value);
    }
 
    modifier onlyStaking() {
        require(staking == msg.sender, 'Caller is not staking contract');
        _;
    }
 
    // solhint-disable-next-line no-empty-blocks
    function _authorizeUpgrade(address) internal override onlyOwner {}
 
    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[45] private __gap;
}