LCOV - code coverage report
Current view: top level - rewards - RewardsController.sol (source / functions) Coverage Total Hit
Test: lcov.info.p Lines: 98.8 % 80 79
Test Date: 2024-09-24 09:34:24 Functions: 96.0 % 25 24
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: BUSL-1.1
       2                 :             : pragma solidity ^0.8.10;
       3                 :             : 
       4                 :             : import {VersionedInitializable} from '../misc/aave-upgradeability/VersionedInitializable.sol';
       5                 :             : import {SafeCast} from '../dependencies/openzeppelin/contracts/SafeCast.sol';
       6                 :             : import {IScaledBalanceToken} from '../interfaces/IScaledBalanceToken.sol';
       7                 :             : import {RewardsDistributor} from './RewardsDistributor.sol';
       8                 :             : import {IRewardsController} from './interfaces/IRewardsController.sol';
       9                 :             : import {ITransferStrategyBase} from './interfaces/ITransferStrategyBase.sol';
      10                 :             : import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
      11                 :             : import {IEACAggregatorProxy} from '../helpers/interfaces/IEACAggregatorProxy.sol';
      12                 :             : 
      13                 :             : /**
      14                 :             :  * @title RewardsController
      15                 :             :  * @notice Abstract contract template to build Distributors contracts for ERC20 rewards to protocol participants
      16                 :             :  * @author Aave
      17                 :             :  **/
      18                 :             : contract RewardsController is RewardsDistributor, VersionedInitializable, IRewardsController {
      19                 :             :   using SafeCast for uint256;
      20                 :             : 
      21                 :             :   uint256 public constant REVISION = 1;
      22                 :             : 
      23                 :             :   // This mapping allows whitelisted addresses to claim on behalf of others
      24                 :             :   // useful for contracts that hold tokens to be rewarded but don't have any native logic to claim Liquidity Mining rewards
      25                 :             :   mapping(address => address) internal _authorizedClaimers;
      26                 :             : 
      27                 :             :   // reward => transfer strategy implementation contract
      28                 :             :   // The TransferStrategy contract abstracts the logic regarding
      29                 :             :   // the source of the reward and how to transfer it to the user.
      30                 :             :   mapping(address => ITransferStrategyBase) internal _transferStrategy;
      31                 :             : 
      32                 :             :   // This mapping contains the price oracle per reward.
      33                 :             :   // A price oracle is enforced for integrators to be able to show incentives at
      34                 :             :   // the current Aave UI without the need to setup an external price registry
      35                 :             :   // At the moment of reward configuration, the Incentives Controller performs
      36                 :             :   // a check to see if the provided reward oracle contains `latestAnswer`.
      37                 :             :   mapping(address => IEACAggregatorProxy) internal _rewardOracle;
      38                 :             : 
      39                 :             :   modifier onlyAuthorizedClaimers(address claimer, address user) {
      40                 :           1 :     require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED');
      41                 :             :     _;
      42                 :             :   }
      43                 :             : 
      44                 :             :   constructor(address emissionManager) RewardsDistributor(emissionManager) {}
      45                 :             : 
      46                 :             :   /**
      47                 :             :    * @dev Initialize for RewardsController
      48                 :             :    * @dev It expects an address as argument since its initialized via PoolAddressesProvider._updateImpl()
      49                 :             :    **/
      50                 :             :   function initialize(address) external initializer {}
      51                 :             : 
      52                 :             :   /// @inheritdoc IRewardsController
      53                 :             :   function getClaimer(address user) external view override returns (address) {
      54                 :        2002 :     return _authorizedClaimers[user];
      55                 :             :   }
      56                 :             : 
      57                 :             :   /**
      58                 :             :    * @dev Returns the revision of the implementation contract
      59                 :             :    * @return uint256, current revision version
      60                 :             :    */
      61                 :             :   function getRevision() internal pure override returns (uint256) {
      62                 :        1387 :     return REVISION;
      63                 :             :   }
      64                 :             : 
      65                 :             :   /// @inheritdoc IRewardsController
      66                 :             :   function getRewardOracle(address reward) external view override returns (address) {
      67                 :           1 :     return address(_rewardOracle[reward]);
      68                 :             :   }
      69                 :             : 
      70                 :             :   /// @inheritdoc IRewardsController
      71                 :             :   function getTransferStrategy(address reward) external view override returns (address) {
      72                 :           1 :     return address(_transferStrategy[reward]);
      73                 :             :   }
      74                 :             : 
      75                 :             :   /// @inheritdoc IRewardsController
      76                 :             :   function configureAssets(
      77                 :             :     RewardsDataTypes.RewardsConfigInput[] memory config
      78                 :             :   ) external override onlyEmissionManager {
      79                 :        9023 :     for (uint256 i = 0; i < config.length; i++) {
      80                 :             :       // Get the current Scaled Total Supply of AToken or Debt token
      81                 :        9023 :       config[i].totalSupply = IScaledBalanceToken(config[i].asset).scaledTotalSupply();
      82                 :             : 
      83                 :             :       // Install TransferStrategy logic at IncentivesController
      84                 :        9023 :       _installTransferStrategy(config[i].reward, config[i].transferStrategy);
      85                 :             : 
      86                 :             :       // Set reward oracle, enforces input oracle to have latestPrice function
      87                 :        9023 :       _setRewardOracle(config[i].reward, config[i].rewardOracle);
      88                 :             :     }
      89                 :        9023 :     _configureAssets(config);
      90                 :             :   }
      91                 :             : 
      92                 :             :   /// @inheritdoc IRewardsController
      93                 :             :   function setTransferStrategy(
      94                 :             :     address reward,
      95                 :             :     ITransferStrategyBase transferStrategy
      96                 :             :   ) external onlyEmissionManager {
      97                 :           2 :     _installTransferStrategy(reward, transferStrategy);
      98                 :             :   }
      99                 :             : 
     100                 :             :   /// @inheritdoc IRewardsController
     101                 :             :   function setRewardOracle(
     102                 :             :     address reward,
     103                 :             :     IEACAggregatorProxy rewardOracle
     104                 :             :   ) external onlyEmissionManager {
     105                 :           2 :     _setRewardOracle(reward, rewardOracle);
     106                 :             :   }
     107                 :             : 
     108                 :             :   /// @inheritdoc IRewardsController
     109                 :             :   function handleAction(address user, uint256 totalSupply, uint256 userBalance) external override {
     110                 :      138815 :     _updateData(msg.sender, user, userBalance, totalSupply);
     111                 :             :   }
     112                 :             : 
     113                 :             :   /// @inheritdoc IRewardsController
     114                 :             :   function claimRewards(
     115                 :             :     address[] calldata assets,
     116                 :             :     uint256 amount,
     117                 :             :     address to,
     118                 :             :     address reward
     119                 :             :   ) external override returns (uint256) {
     120                 :        2521 :     require(to != address(0), 'INVALID_TO_ADDRESS');
     121                 :        2521 :     return _claimRewards(assets, amount, msg.sender, msg.sender, to, reward);
     122                 :             :   }
     123                 :             : 
     124                 :             :   /// @inheritdoc IRewardsController
     125                 :             :   function claimRewardsOnBehalf(
     126                 :             :     address[] calldata assets,
     127                 :             :     uint256 amount,
     128                 :             :     address user,
     129                 :             :     address to,
     130                 :             :     address reward
     131                 :             :   ) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) {
     132                 :           1 :     require(user != address(0), 'INVALID_USER_ADDRESS');
     133                 :           1 :     require(to != address(0), 'INVALID_TO_ADDRESS');
     134                 :           1 :     return _claimRewards(assets, amount, msg.sender, user, to, reward);
     135                 :             :   }
     136                 :             : 
     137                 :             :   /// @inheritdoc IRewardsController
     138                 :             :   function claimRewardsToSelf(
     139                 :             :     address[] calldata assets,
     140                 :             :     uint256 amount,
     141                 :             :     address reward
     142                 :             :   ) external override returns (uint256) {
     143                 :           1 :     return _claimRewards(assets, amount, msg.sender, msg.sender, msg.sender, reward);
     144                 :             :   }
     145                 :             : 
     146                 :             :   /// @inheritdoc IRewardsController
     147                 :             :   function claimAllRewards(
     148                 :             :     address[] calldata assets,
     149                 :             :     address to
     150                 :             :   ) external override returns (address[] memory rewardsList, uint256[] memory claimedAmounts) {
     151                 :           1 :     require(to != address(0), 'INVALID_TO_ADDRESS');
     152                 :           1 :     return _claimAllRewards(assets, msg.sender, msg.sender, to);
     153                 :             :   }
     154                 :             : 
     155                 :             :   /// @inheritdoc IRewardsController
     156                 :             :   function claimAllRewardsOnBehalf(
     157                 :             :     address[] calldata assets,
     158                 :             :     address user,
     159                 :             :     address to
     160                 :             :   )
     161                 :             :     external
     162                 :             :     override
     163                 :             :     onlyAuthorizedClaimers(msg.sender, user)
     164                 :             :     returns (address[] memory rewardsList, uint256[] memory claimedAmounts)
     165                 :             :   {
     166                 :           1 :     require(user != address(0), 'INVALID_USER_ADDRESS');
     167                 :           1 :     require(to != address(0), 'INVALID_TO_ADDRESS');
     168                 :           1 :     return _claimAllRewards(assets, msg.sender, user, to);
     169                 :             :   }
     170                 :             : 
     171                 :             :   /// @inheritdoc IRewardsController
     172                 :             :   function claimAllRewardsToSelf(
     173                 :             :     address[] calldata assets
     174                 :             :   ) external override returns (address[] memory rewardsList, uint256[] memory claimedAmounts) {
     175                 :           1 :     return _claimAllRewards(assets, msg.sender, msg.sender, msg.sender);
     176                 :             :   }
     177                 :             : 
     178                 :             :   /// @inheritdoc IRewardsController
     179                 :             :   function setClaimer(address user, address caller) external override onlyEmissionManager {
     180                 :        1004 :     _authorizedClaimers[user] = caller;
     181                 :        1004 :     emit ClaimerSet(user, caller);
     182                 :             :   }
     183                 :             : 
     184                 :             :   /**
     185                 :             :    * @dev Get user balances and total supply of all the assets specified by the assets parameter
     186                 :             :    * @param assets List of assets to retrieve user balance and total supply
     187                 :             :    * @param user Address of the user
     188                 :             :    * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets
     189                 :             :    */
     190                 :             :   function _getUserAssetBalances(
     191                 :             :     address[] calldata assets,
     192                 :             :     address user
     193                 :             :   ) internal view override returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances) {
     194                 :        3537 :     userAssetBalances = new RewardsDataTypes.UserAssetBalance[](assets.length);
     195                 :        3537 :     for (uint256 i = 0; i < assets.length; i++) {
     196                 :        3537 :       userAssetBalances[i].asset = assets[i];
     197                 :        3537 :       (userAssetBalances[i].userBalance, userAssetBalances[i].totalSupply) = IScaledBalanceToken(
     198                 :             :         assets[i]
     199                 :             :       ).getScaledUserBalanceAndSupply(user);
     200                 :             :     }
     201                 :           0 :     return userAssetBalances;
     202                 :             :   }
     203                 :             : 
     204                 :             :   /**
     205                 :             :    * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards.
     206                 :             :    * @param assets List of assets to check eligible distributions before claiming rewards
     207                 :             :    * @param amount Amount of rewards to claim
     208                 :             :    * @param claimer Address of the claimer who claims rewards on behalf of user
     209                 :             :    * @param user Address to check and claim rewards
     210                 :             :    * @param to Address that will be receiving the rewards
     211                 :             :    * @param reward Address of the reward token
     212                 :             :    * @return Rewards claimed
     213                 :             :    **/
     214                 :             :   function _claimRewards(
     215                 :             :     address[] calldata assets,
     216                 :             :     uint256 amount,
     217                 :             :     address claimer,
     218                 :             :     address user,
     219                 :             :     address to,
     220                 :             :     address reward
     221                 :             :   ) internal returns (uint256) {
     222                 :        2523 :     if (amount == 0) {
     223                 :           1 :       return 0;
     224                 :             :     }
     225                 :        2522 :     uint256 totalRewards;
     226                 :             : 
     227                 :        2522 :     _updateDataMultiple(user, _getUserAssetBalances(assets, user));
     228                 :        2522 :     for (uint256 i = 0; i < assets.length; i++) {
     229                 :        2522 :       address asset = assets[i];
     230                 :        2522 :       totalRewards += _assets[asset].rewards[reward].usersData[user].accrued;
     231                 :             : 
     232                 :        2522 :       if (totalRewards <= amount) {
     233                 :        2521 :         _assets[asset].rewards[reward].usersData[user].accrued = 0;
     234                 :             :       } else {
     235                 :           1 :         uint256 difference = totalRewards - amount;
     236                 :           1 :         totalRewards -= difference;
     237                 :           1 :         _assets[asset].rewards[reward].usersData[user].accrued = difference.toUint128();
     238                 :           1 :         break;
     239                 :             :       }
     240                 :             :     }
     241                 :             : 
     242                 :        2522 :     if (totalRewards == 0) {
     243                 :         623 :       return 0;
     244                 :             :     }
     245                 :             : 
     246                 :        1899 :     _transferRewards(to, reward, totalRewards);
     247                 :        1899 :     emit RewardsClaimed(user, reward, to, claimer, totalRewards);
     248                 :             : 
     249                 :        1899 :     return totalRewards;
     250                 :             :   }
     251                 :             : 
     252                 :             :   /**
     253                 :             :    * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards.
     254                 :             :    * @param assets List of assets to check eligible distributions before claiming rewards
     255                 :             :    * @param claimer Address of the claimer on behalf of user
     256                 :             :    * @param user Address to check and claim rewards
     257                 :             :    * @param to Address that will be receiving the rewards
     258                 :             :    * @return
     259                 :             :    *   rewardsList List of reward addresses
     260                 :             :    *   claimedAmount List of claimed amounts, follows "rewardsList" items order
     261                 :             :    **/
     262                 :             :   function _claimAllRewards(
     263                 :             :     address[] calldata assets,
     264                 :             :     address claimer,
     265                 :             :     address user,
     266                 :             :     address to
     267                 :             :   ) internal returns (address[] memory rewardsList, uint256[] memory claimedAmounts) {
     268                 :           3 :     uint256 rewardsListLength = _rewardsList.length;
     269                 :           3 :     rewardsList = new address[](rewardsListLength);
     270                 :           3 :     claimedAmounts = new uint256[](rewardsListLength);
     271                 :             : 
     272                 :           3 :     _updateDataMultiple(user, _getUserAssetBalances(assets, user));
     273                 :             : 
     274                 :           3 :     for (uint256 i = 0; i < assets.length; i++) {
     275                 :           3 :       address asset = assets[i];
     276                 :           3 :       for (uint256 j = 0; j < rewardsListLength; j++) {
     277                 :           3 :         if (rewardsList[j] == address(0)) {
     278                 :           3 :           rewardsList[j] = _rewardsList[j];
     279                 :             :         }
     280                 :           3 :         uint256 rewardAmount = _assets[asset].rewards[rewardsList[j]].usersData[user].accrued;
     281                 :           3 :         if (rewardAmount != 0) {
     282                 :           3 :           claimedAmounts[j] += rewardAmount;
     283                 :           3 :           _assets[asset].rewards[rewardsList[j]].usersData[user].accrued = 0;
     284                 :             :         }
     285                 :             :       }
     286                 :             :     }
     287                 :           3 :     for (uint256 i = 0; i < rewardsListLength; i++) {
     288                 :           3 :       _transferRewards(to, rewardsList[i], claimedAmounts[i]);
     289                 :           3 :       emit RewardsClaimed(user, rewardsList[i], to, claimer, claimedAmounts[i]);
     290                 :             :     }
     291                 :           3 :     return (rewardsList, claimedAmounts);
     292                 :             :   }
     293                 :             : 
     294                 :             :   /**
     295                 :             :    * @dev Function to transfer rewards to the desired account using delegatecall and
     296                 :             :    * @param to Account address to send the rewards
     297                 :             :    * @param reward Address of the reward token
     298                 :             :    * @param amount Amount of rewards to transfer
     299                 :             :    */
     300                 :             :   function _transferRewards(address to, address reward, uint256 amount) internal {
     301                 :        1902 :     ITransferStrategyBase transferStrategy = _transferStrategy[reward];
     302                 :             : 
     303                 :        1902 :     bool success = transferStrategy.performTransfer(to, reward, amount);
     304                 :             : 
     305                 :        1902 :     require(success == true, 'TRANSFER_ERROR');
     306                 :             :   }
     307                 :             : 
     308                 :             :   /**
     309                 :             :    * @dev Returns true if `account` is a contract.
     310                 :             :    * @param account The address of the account
     311                 :             :    * @return bool, true if contract, false otherwise
     312                 :             :    */
     313                 :             :   function _isContract(address account) internal view returns (bool) {
     314                 :             :     // This method relies on extcodesize, which returns 0 for contracts in
     315                 :             :     // construction, since the code is only stored at the end of the
     316                 :             :     // constructor execution.
     317                 :             : 
     318                 :        9025 :     uint256 size;
     319                 :             :     // solhint-disable-next-line no-inline-assembly
     320                 :             :     assembly {
     321                 :        9025 :       size := extcodesize(account)
     322                 :             :     }
     323                 :        9025 :     return size > 0;
     324                 :             :   }
     325                 :             : 
     326                 :             :   /**
     327                 :             :    * @dev Internal function to call the optional install hook at the TransferStrategy
     328                 :             :    * @param reward The address of the reward token
     329                 :             :    * @param transferStrategy The address of the reward TransferStrategy
     330                 :             :    */
     331                 :             :   function _installTransferStrategy(
     332                 :             :     address reward,
     333                 :             :     ITransferStrategyBase transferStrategy
     334                 :             :   ) internal {
     335                 :        9025 :     require(address(transferStrategy) != address(0), 'STRATEGY_CAN_NOT_BE_ZERO');
     336                 :        9025 :     require(_isContract(address(transferStrategy)) == true, 'STRATEGY_MUST_BE_CONTRACT');
     337                 :             : 
     338                 :        9025 :     _transferStrategy[reward] = transferStrategy;
     339                 :             : 
     340                 :        9025 :     emit TransferStrategyInstalled(reward, address(transferStrategy));
     341                 :             :   }
     342                 :             : 
     343                 :             :   /**
     344                 :             :    * @dev Update the Price Oracle of a reward token. The Price Oracle must follow Chainlink IEACAggregatorProxy interface.
     345                 :             :    * @notice The Price Oracle of a reward is used for displaying correct data about the incentives at the UI frontend.
     346                 :             :    * @param reward The address of the reward token
     347                 :             :    * @param rewardOracle The address of the price oracle
     348                 :             :    */
     349                 :             : 
     350                 :             :   function _setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) internal {
     351                 :        9025 :     require(rewardOracle.latestAnswer() > 0, 'ORACLE_MUST_RETURN_PRICE');
     352                 :        9025 :     _rewardOracle[reward] = rewardOracle;
     353                 :        9025 :     emit RewardOracleUpdated(reward, address(rewardOracle));
     354                 :             :   }
     355                 :             : }
        

Generated by: LCOV version 2.1-1