Branch data Line data Source code
1 : : // SPDX-License-Identifier: BUSL-1.1
2 : : pragma solidity ^0.8.10;
3 : :
4 : : import {IScaledBalanceToken} from '../interfaces/IScaledBalanceToken.sol';
5 : : import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
6 : : import {SafeCast} from '../dependencies/openzeppelin/contracts/SafeCast.sol';
7 : : import {IRewardsDistributor} from './interfaces/IRewardsDistributor.sol';
8 : : import {RewardsDataTypes} from './libraries/RewardsDataTypes.sol';
9 : :
10 : : /**
11 : : * @title RewardsDistributor
12 : : * @notice Accounting contract to manage multiple staking distributions with multiple rewards
13 : : * @author Aave
14 : : **/
15 : : abstract contract RewardsDistributor is IRewardsDistributor {
16 : : using SafeCast for uint256;
17 : :
18 : : // Manager of incentives
19 : : address public immutable EMISSION_MANAGER;
20 : : // Deprecated: This storage slot is kept for backwards compatibility purposes.
21 : : address internal _emissionManager;
22 : :
23 : : // Map of rewarded asset addresses and their data (assetAddress => assetData)
24 : : mapping(address => RewardsDataTypes.AssetData) internal _assets;
25 : :
26 : : // Map of reward assets (rewardAddress => enabled)
27 : : mapping(address => bool) internal _isRewardEnabled;
28 : :
29 : : // Rewards list
30 : : address[] internal _rewardsList;
31 : :
32 : : // Assets list
33 : : address[] internal _assetsList;
34 : :
35 : : modifier onlyEmissionManager() {
36 : 2 : require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER');
37 : : _;
38 : : }
39 : :
40 : : constructor(address emissionManager) {
41 : 0 : EMISSION_MANAGER = emissionManager;
42 : : }
43 : :
44 : : /// @inheritdoc IRewardsDistributor
45 : : function getRewardsData(
46 : : address asset,
47 : : address reward
48 : : ) external view override returns (uint256, uint256, uint256, uint256) {
49 : 3 : return (
50 : : _assets[asset].rewards[reward].index,
51 : : _assets[asset].rewards[reward].emissionPerSecond,
52 : : _assets[asset].rewards[reward].lastUpdateTimestamp,
53 : : _assets[asset].rewards[reward].distributionEnd
54 : : );
55 : : }
56 : :
57 : : /// @inheritdoc IRewardsDistributor
58 : : function getAssetIndex(
59 : : address asset,
60 : : address reward
61 : : ) external view override returns (uint256, uint256) {
62 : 37954 : RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward];
63 : 37954 : return
64 : 37954 : _getAssetIndex(
65 : : rewardData,
66 : : IScaledBalanceToken(asset).scaledTotalSupply(),
67 : : 10 ** _assets[asset].decimals
68 : : );
69 : : }
70 : :
71 : : /// @inheritdoc IRewardsDistributor
72 : : function getDistributionEnd(
73 : : address asset,
74 : : address reward
75 : : ) external view override returns (uint256) {
76 : 1 : return _assets[asset].rewards[reward].distributionEnd;
77 : : }
78 : :
79 : : /// @inheritdoc IRewardsDistributor
80 : : function getRewardsByAsset(address asset) external view override returns (address[] memory) {
81 : 9118 : uint128 rewardsCount = _assets[asset].availableRewardsCount;
82 : 9118 : address[] memory availableRewards = new address[](rewardsCount);
83 : :
84 : 9118 : for (uint128 i = 0; i < rewardsCount; i++) {
85 : 9033 : availableRewards[i] = _assets[asset].availableRewards[i];
86 : : }
87 : 9118 : return availableRewards;
88 : : }
89 : :
90 : : /// @inheritdoc IRewardsDistributor
91 : : function getRewardsList() external view override returns (address[] memory) {
92 : 30 : return _rewardsList;
93 : : }
94 : :
95 : : /// @inheritdoc IRewardsDistributor
96 : : function getUserAssetIndex(
97 : : address user,
98 : : address asset,
99 : : address reward
100 : : ) external view override returns (uint256) {
101 : 1 : return _assets[asset].rewards[reward].usersData[user].index;
102 : : }
103 : :
104 : : /// @inheritdoc IRewardsDistributor
105 : : function getUserAccruedRewards(
106 : : address user,
107 : : address reward
108 : : ) external view override returns (uint256) {
109 : 1 : uint256 totalAccrued;
110 : 1 : for (uint256 i = 0; i < _assetsList.length; i++) {
111 : 1 : totalAccrued += _assets[_assetsList[i]].rewards[reward].usersData[user].accrued;
112 : : }
113 : :
114 : 1 : return totalAccrued;
115 : : }
116 : :
117 : : /// @inheritdoc IRewardsDistributor
118 : : function getUserRewards(
119 : : address[] calldata assets,
120 : : address user,
121 : : address reward
122 : : ) external view override returns (uint256) {
123 : 1011 : return _getUserReward(user, reward, _getUserAssetBalances(assets, user));
124 : : }
125 : :
126 : : /// @inheritdoc IRewardsDistributor
127 : : function getAllUserRewards(
128 : : address[] calldata assets,
129 : : address user
130 : : )
131 : : external
132 : : view
133 : : override
134 : : returns (address[] memory rewardsList, uint256[] memory unclaimedAmounts)
135 : : {
136 : 1 : RewardsDataTypes.UserAssetBalance[] memory userAssetBalances = _getUserAssetBalances(
137 : : assets,
138 : : user
139 : : );
140 : 1 : rewardsList = new address[](_rewardsList.length);
141 : 1 : unclaimedAmounts = new uint256[](rewardsList.length);
142 : :
143 : : // Add unrealized rewards from user to unclaimedRewards
144 : 1 : for (uint256 i = 0; i < userAssetBalances.length; i++) {
145 : 1 : for (uint256 r = 0; r < rewardsList.length; r++) {
146 : 1 : rewardsList[r] = _rewardsList[r];
147 : 1 : unclaimedAmounts[r] += _assets[userAssetBalances[i].asset]
148 : : .rewards[rewardsList[r]]
149 : : .usersData[user]
150 : : .accrued;
151 : :
152 : 1 : if (userAssetBalances[i].userBalance == 0) {
153 : 1 : continue;
154 : : }
155 : 1 : unclaimedAmounts[r] += _getPendingRewards(user, rewardsList[r], userAssetBalances[i]);
156 : : }
157 : : }
158 : 1 : return (rewardsList, unclaimedAmounts);
159 : : }
160 : :
161 : : /// @inheritdoc IRewardsDistributor
162 : : function setDistributionEnd(
163 : : address asset,
164 : : address reward,
165 : : uint32 newDistributionEnd
166 : : ) external override onlyEmissionManager {
167 : 2 : uint256 oldDistributionEnd = _assets[asset].rewards[reward].distributionEnd;
168 : 2 : _assets[asset].rewards[reward].distributionEnd = newDistributionEnd;
169 : :
170 : 2 : emit AssetConfigUpdated(
171 : : asset,
172 : : reward,
173 : : _assets[asset].rewards[reward].emissionPerSecond,
174 : : _assets[asset].rewards[reward].emissionPerSecond,
175 : : oldDistributionEnd,
176 : : newDistributionEnd,
177 : : _assets[asset].rewards[reward].index
178 : : );
179 : : }
180 : :
181 : : /// @inheritdoc IRewardsDistributor
182 : : function setEmissionPerSecond(
183 : : address asset,
184 : : address[] calldata rewards,
185 : : uint88[] calldata newEmissionsPerSecond
186 : : ) external override onlyEmissionManager {
187 : 2 : require(rewards.length == newEmissionsPerSecond.length, 'INVALID_INPUT');
188 : 2 : for (uint256 i = 0; i < rewards.length; i++) {
189 : 2 : RewardsDataTypes.AssetData storage assetConfig = _assets[asset];
190 : 2 : RewardsDataTypes.RewardData storage rewardConfig = _assets[asset].rewards[rewards[i]];
191 : 2 : uint256 decimals = assetConfig.decimals;
192 : 2 : require(
193 : : decimals != 0 && rewardConfig.lastUpdateTimestamp != 0,
194 : : 'DISTRIBUTION_DOES_NOT_EXIST'
195 : : );
196 : :
197 : 2 : (uint256 newIndex, ) = _updateRewardData(
198 : : rewardConfig,
199 : : IScaledBalanceToken(asset).scaledTotalSupply(),
200 : : 10 ** decimals
201 : : );
202 : :
203 : 2 : uint256 oldEmissionPerSecond = rewardConfig.emissionPerSecond;
204 : 2 : rewardConfig.emissionPerSecond = newEmissionsPerSecond[i];
205 : :
206 : 2 : emit AssetConfigUpdated(
207 : : asset,
208 : : rewards[i],
209 : : oldEmissionPerSecond,
210 : : newEmissionsPerSecond[i],
211 : : rewardConfig.distributionEnd,
212 : : rewardConfig.distributionEnd,
213 : : newIndex
214 : : );
215 : : }
216 : : }
217 : :
218 : : /**
219 : : * @dev Configure the _assets for a specific emission
220 : : * @param rewardsInput The array of each asset configuration
221 : : **/
222 : : function _configureAssets(RewardsDataTypes.RewardsConfigInput[] memory rewardsInput) internal {
223 : 9023 : for (uint256 i = 0; i < rewardsInput.length; i++) {
224 : 9023 : if (_assets[rewardsInput[i].asset].decimals == 0) {
225 : : //never initialized before, adding to the list of assets
226 : 9023 : _assetsList.push(rewardsInput[i].asset);
227 : : }
228 : :
229 : 9023 : uint256 decimals = _assets[rewardsInput[i].asset].decimals = IERC20Detailed(
230 : : rewardsInput[i].asset
231 : : ).decimals();
232 : :
233 : 9023 : RewardsDataTypes.RewardData storage rewardConfig = _assets[rewardsInput[i].asset].rewards[
234 : : rewardsInput[i].reward
235 : : ];
236 : :
237 : : // Add reward address to asset available rewards if latestUpdateTimestamp is zero
238 : 9023 : if (rewardConfig.lastUpdateTimestamp == 0) {
239 : 9023 : _assets[rewardsInput[i].asset].availableRewards[
240 : : _assets[rewardsInput[i].asset].availableRewardsCount
241 : : ] = rewardsInput[i].reward;
242 : 9023 : _assets[rewardsInput[i].asset].availableRewardsCount++;
243 : : }
244 : :
245 : : // Add reward address to global rewards list if still not enabled
246 : 9023 : if (_isRewardEnabled[rewardsInput[i].reward] == false) {
247 : 9023 : _isRewardEnabled[rewardsInput[i].reward] = true;
248 : 9023 : _rewardsList.push(rewardsInput[i].reward);
249 : : }
250 : :
251 : : // Due emissions is still zero, updates only latestUpdateTimestamp
252 : 9023 : (uint256 newIndex, ) = _updateRewardData(
253 : : rewardConfig,
254 : : rewardsInput[i].totalSupply,
255 : : 10 ** decimals
256 : : );
257 : :
258 : : // Configure emission and distribution end of the reward per asset
259 : 9023 : uint88 oldEmissionsPerSecond = rewardConfig.emissionPerSecond;
260 : 9023 : uint32 oldDistributionEnd = rewardConfig.distributionEnd;
261 : 9023 : rewardConfig.emissionPerSecond = rewardsInput[i].emissionPerSecond;
262 : 9023 : rewardConfig.distributionEnd = rewardsInput[i].distributionEnd;
263 : :
264 : 9023 : emit AssetConfigUpdated(
265 : : rewardsInput[i].asset,
266 : : rewardsInput[i].reward,
267 : : oldEmissionsPerSecond,
268 : : rewardsInput[i].emissionPerSecond,
269 : : oldDistributionEnd,
270 : : rewardsInput[i].distributionEnd,
271 : : newIndex
272 : : );
273 : : }
274 : : }
275 : :
276 : : /**
277 : : * @dev Updates the state of the distribution for the specified reward
278 : : * @param rewardData Storage pointer to the distribution reward config
279 : : * @param totalSupply Current total of underlying assets for this distribution
280 : : * @param assetUnit One unit of asset (10**decimals)
281 : : * @return The new distribution index
282 : : * @return True if the index was updated, false otherwise
283 : : **/
284 : : function _updateRewardData(
285 : : RewardsDataTypes.RewardData storage rewardData,
286 : : uint256 totalSupply,
287 : : uint256 assetUnit
288 : : ) internal returns (uint256, bool) {
289 : 11558 : (uint256 oldIndex, uint256 newIndex) = _getAssetIndex(rewardData, totalSupply, assetUnit);
290 : 11558 : bool indexUpdated;
291 : 11558 : if (newIndex != oldIndex) {
292 : 1903 : require(newIndex <= type(uint104).max, 'INDEX_OVERFLOW');
293 : 1903 : indexUpdated = true;
294 : :
295 : : //optimization: storing one after another saves one SSTORE
296 : 1903 : rewardData.index = uint104(newIndex);
297 : 1903 : rewardData.lastUpdateTimestamp = block.timestamp.toUint32();
298 : : } else {
299 : 9655 : rewardData.lastUpdateTimestamp = block.timestamp.toUint32();
300 : : }
301 : :
302 : 11558 : return (newIndex, indexUpdated);
303 : : }
304 : :
305 : : /**
306 : : * @dev Updates the state of the distribution for the specific user
307 : : * @param rewardData Storage pointer to the distribution reward config
308 : : * @param user The address of the user
309 : : * @param userBalance The user balance of the asset
310 : : * @param newAssetIndex The new index of the asset distribution
311 : : * @param assetUnit One unit of asset (10**decimals)
312 : : * @return The rewards accrued since the last update
313 : : **/
314 : : function _updateUserData(
315 : : RewardsDataTypes.RewardData storage rewardData,
316 : : address user,
317 : : uint256 userBalance,
318 : : uint256 newAssetIndex,
319 : : uint256 assetUnit
320 : : ) internal returns (uint256, bool) {
321 : 2533 : uint256 userIndex = rewardData.usersData[user].index;
322 : 2533 : uint256 rewardsAccrued;
323 : 2533 : bool dataUpdated;
324 : 2533 : if ((dataUpdated = userIndex != newAssetIndex)) {
325 : : // already checked for overflow in _updateRewardData
326 : 1903 : rewardData.usersData[user].index = uint104(newAssetIndex);
327 : 1903 : if (userBalance != 0) {
328 : 1903 : rewardsAccrued = _getRewards(userBalance, newAssetIndex, userIndex, assetUnit);
329 : :
330 : 1903 : rewardData.usersData[user].accrued += rewardsAccrued.toUint128();
331 : : }
332 : : }
333 : 2533 : return (rewardsAccrued, dataUpdated);
334 : : }
335 : :
336 : : /**
337 : : * @dev Iterates and accrues all the rewards for asset of the specific user
338 : : * @param asset The address of the reference asset of the distribution
339 : : * @param user The user address
340 : : * @param userBalance The current user asset balance
341 : : * @param totalSupply Total supply of the asset
342 : : **/
343 : : function _updateData(
344 : : address asset,
345 : : address user,
346 : : uint256 userBalance,
347 : : uint256 totalSupply
348 : : ) internal {
349 : 141340 : uint256 assetUnit;
350 : 141340 : uint256 numAvailableRewards = _assets[asset].availableRewardsCount;
351 : : unchecked {
352 : 141340 : assetUnit = 10 ** _assets[asset].decimals;
353 : : }
354 : :
355 : 141340 : if (numAvailableRewards == 0) {
356 : 138807 : return;
357 : : }
358 : : unchecked {
359 : 2533 : for (uint128 r = 0; r < numAvailableRewards; r++) {
360 : 2533 : address reward = _assets[asset].availableRewards[r];
361 : 2533 : RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward];
362 : :
363 : 2533 : (uint256 newAssetIndex, bool rewardDataUpdated) = _updateRewardData(
364 : : rewardData,
365 : : totalSupply,
366 : : assetUnit
367 : : );
368 : :
369 : 2533 : (uint256 rewardsAccrued, bool userDataUpdated) = _updateUserData(
370 : : rewardData,
371 : : user,
372 : : userBalance,
373 : : newAssetIndex,
374 : : assetUnit
375 : : );
376 : :
377 : 2533 : if (rewardDataUpdated || userDataUpdated) {
378 : 1903 : emit Accrued(asset, reward, user, newAssetIndex, newAssetIndex, rewardsAccrued);
379 : : }
380 : : }
381 : : }
382 : : }
383 : :
384 : : /**
385 : : * @dev Accrues all the rewards of the assets specified in the userAssetBalances list
386 : : * @param user The address of the user
387 : : * @param userAssetBalances List of structs with the user balance and total supply of a set of assets
388 : : **/
389 : : function _updateDataMultiple(
390 : : address user,
391 : : RewardsDataTypes.UserAssetBalance[] memory userAssetBalances
392 : : ) internal {
393 : 2525 : for (uint256 i = 0; i < userAssetBalances.length; i++) {
394 : 2525 : _updateData(
395 : : userAssetBalances[i].asset,
396 : : user,
397 : : userAssetBalances[i].userBalance,
398 : : userAssetBalances[i].totalSupply
399 : : );
400 : : }
401 : : }
402 : :
403 : : /**
404 : : * @dev Return the accrued unclaimed amount of a reward from a user over a list of distribution
405 : : * @param user The address of the user
406 : : * @param reward The address of the reward token
407 : : * @param userAssetBalances List of structs with the user balance and total supply of a set of assets
408 : : * @return unclaimedRewards The accrued rewards for the user until the moment
409 : : **/
410 : : function _getUserReward(
411 : : address user,
412 : : address reward,
413 : : RewardsDataTypes.UserAssetBalance[] memory userAssetBalances
414 : : ) internal view returns (uint256 unclaimedRewards) {
415 : : // Add unrealized rewards
416 : 1011 : for (uint256 i = 0; i < userAssetBalances.length; i++) {
417 : 1011 : if (userAssetBalances[i].userBalance == 0) {
418 : 3 : unclaimedRewards += _assets[userAssetBalances[i].asset]
419 : : .rewards[reward]
420 : : .usersData[user]
421 : : .accrued;
422 : : } else {
423 : 1008 : unclaimedRewards +=
424 : : _getPendingRewards(user, reward, userAssetBalances[i]) +
425 : : _assets[userAssetBalances[i].asset].rewards[reward].usersData[user].accrued;
426 : : }
427 : : }
428 : :
429 : 0 : return unclaimedRewards;
430 : : }
431 : :
432 : : /**
433 : : * @dev Calculates the pending (not yet accrued) rewards since the last user action
434 : : * @param user The address of the user
435 : : * @param reward The address of the reward token
436 : : * @param userAssetBalance struct with the user balance and total supply of the incentivized asset
437 : : * @return The pending rewards for the user since the last user action
438 : : **/
439 : : function _getPendingRewards(
440 : : address user,
441 : : address reward,
442 : : RewardsDataTypes.UserAssetBalance memory userAssetBalance
443 : : ) internal view returns (uint256) {
444 : 1009 : RewardsDataTypes.RewardData storage rewardData = _assets[userAssetBalance.asset].rewards[
445 : : reward
446 : : ];
447 : 1009 : uint256 assetUnit = 10 ** _assets[userAssetBalance.asset].decimals;
448 : 1009 : (, uint256 nextIndex) = _getAssetIndex(rewardData, userAssetBalance.totalSupply, assetUnit);
449 : :
450 : 1009 : return
451 : 1009 : _getRewards(
452 : : userAssetBalance.userBalance,
453 : : nextIndex,
454 : : rewardData.usersData[user].index,
455 : : assetUnit
456 : : );
457 : : }
458 : :
459 : : /**
460 : : * @dev Internal function for the calculation of user's rewards on a distribution
461 : : * @param userBalance Balance of the user asset on a distribution
462 : : * @param reserveIndex Current index of the distribution
463 : : * @param userIndex Index stored for the user, representation his staking moment
464 : : * @param assetUnit One unit of asset (10**decimals)
465 : : * @return The rewards
466 : : **/
467 : : function _getRewards(
468 : : uint256 userBalance,
469 : : uint256 reserveIndex,
470 : : uint256 userIndex,
471 : : uint256 assetUnit
472 : : ) internal pure returns (uint256) {
473 : 2912 : uint256 result = userBalance * (reserveIndex - userIndex);
474 : : assembly {
475 : 2912 : result := div(result, assetUnit)
476 : : }
477 : 2912 : return result;
478 : : }
479 : :
480 : : /**
481 : : * @dev Calculates the next value of an specific distribution index, with validations
482 : : * @param rewardData Storage pointer to the distribution reward config
483 : : * @param totalSupply of the asset being rewarded
484 : : * @param assetUnit One unit of asset (10**decimals)
485 : : * @return The new index.
486 : : **/
487 : : function _getAssetIndex(
488 : : RewardsDataTypes.RewardData storage rewardData,
489 : : uint256 totalSupply,
490 : : uint256 assetUnit
491 : : ) internal view returns (uint256, uint256) {
492 : 50521 : uint256 oldIndex = rewardData.index;
493 : 50521 : uint256 distributionEnd = rewardData.distributionEnd;
494 : 50521 : uint256 emissionPerSecond = rewardData.emissionPerSecond;
495 : 50521 : uint256 lastUpdateTimestamp = rewardData.lastUpdateTimestamp;
496 : :
497 : : if (
498 : 50521 : emissionPerSecond == 0 ||
499 : 40167 : totalSupply == 0 ||
500 : 31462 : lastUpdateTimestamp == block.timestamp ||
501 : 20033 : lastUpdateTimestamp >= distributionEnd
502 : : ) {
503 : 31961 : return (oldIndex, oldIndex);
504 : : }
505 : :
506 : 18560 : uint256 currentTimestamp = block.timestamp > distributionEnd
507 : : ? distributionEnd
508 : : : block.timestamp;
509 : 18560 : uint256 timeDelta = currentTimestamp - lastUpdateTimestamp;
510 : 18560 : uint256 firstTerm = emissionPerSecond * timeDelta * assetUnit;
511 : : assembly {
512 : 18560 : firstTerm := div(firstTerm, totalSupply)
513 : : }
514 : 18560 : return (oldIndex, (firstTerm + oldIndex));
515 : : }
516 : :
517 : : /**
518 : : * @dev Get user balances and total supply of all the assets specified by the assets parameter
519 : : * @param assets List of assets to retrieve user balance and total supply
520 : : * @param user Address of the user
521 : : * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets
522 : : */
523 : : function _getUserAssetBalances(
524 : : address[] calldata assets,
525 : : address user
526 : : ) internal view virtual returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances);
527 : :
528 : : /// @inheritdoc IRewardsDistributor
529 : : function getAssetDecimals(address asset) external view returns (uint8) {
530 : 2 : return _assets[asset].decimals;
531 : : }
532 : :
533 : : /// @inheritdoc IRewardsDistributor
534 : : function getEmissionManager() external view returns (address) {
535 : 5 : return EMISSION_MANAGER;
536 : : }
537 : : }
|