/
IncentivesController.sol
200 lines (174 loc) · 6.97 KB
/
IncentivesController.sol
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.19;
import {SafeMath} from "../../dependencies/openzeppelin/contracts/SafeMath.sol";
import {DistributionTypes} from '../libraries/types/DistributionTypes.sol';
import {IDistributionManager} from '../../interfaces/IDistributionManager.sol';
import {IAToken} from '../../interfaces/IAToken.sol';
import {IERC20} from "../../dependencies/openzeppelin/contracts/IERC20.sol";
import {IIncentivesController} from '../../interfaces/IIncentivesController.sol';
import {VersionedInitializable} from "../../dependencies/aave-upgradeability/VersionedInitializable.sol";
import {DistributionManager} from './DistributionManager.sol';
/**
* @title IncentivesController
* @notice Distributor contract for rewards to the VMEX protocol
* @author Aave and VMEX
**/
contract IncentivesController is
IIncentivesController,
VersionedInitializable,
DistributionManager
{
using SafeMath for uint256;
uint256 public constant REVISION = 1;
address public immutable REWARDS_VAULT;
constructor(
address rewardsVault,
address emissionManager
) DistributionManager(emissionManager) {
REWARDS_VAULT = rewardsVault;
}
/**
* @dev Called by the proxy contract. Not used at the moment, but for the future
**/
function initialize() external initializer {
// to unlock possibility to stake on behalf of the user
// REWARD_TOKEN.approve(address(PSM), type(uint256).max);
}
/**
* @dev Returns the revision of the implementation contract
* @return uint256, current revision version
*/
function getRevision() internal pure override returns (uint256) {
return REVISION;
}
/**
* @dev Called by the corresponding asset on any update that affects the rewards distribution
* @param user The address of the user
* @param userBalance The (old) balance of the user of the asset in the lending pool
* @param totalSupply The (old) total supply of the asset in the lending pool
**/
function handleAction(address user, uint256 userBalance, uint256 totalSupply) external override {
// note: msg.sender is the incentivized asset (the vToken)
_updateIncentivizedAsset(msg.sender, user, userBalance, totalSupply);
}
function _getUserState(
address[] memory assets,
address user
) internal view returns (DistributionTypes.UserAssetState[] memory) {
DistributionTypes.UserAssetState[] memory userState = new DistributionTypes.UserAssetState[](
assets.length
);
for (uint256 i = 0; i < assets.length; i++) {
userState[i].asset = assets[i];
(userState[i].userBalance, userState[i].totalSupply) = IAToken(assets[i])
.getScaledUserBalanceAndSupply(user);
}
return userState;
}
/**
* @dev Returns the total of rewards of an user, already accrued + not yet accrued
* @param assets List of incentivized assets to check the rewards for
* @param user The address of the user
**/
function getPendingRewards(
address[] calldata assets,
address user
) external view override returns (address[] memory, uint256[] memory) {
address[] memory rewards = _allRewards;
uint256[] memory amounts = new uint256[](_allRewards.length);
DistributionTypes.UserAssetState[] memory balanceData = _getUserState(assets, user);
for (uint256 i = 0; i < assets.length; i++) {
address asset = assets[i];
for (uint256 j = 0; j < _allRewards.length; j++) {
DistributionTypes.Reward storage reward = _incentivizedAssets[asset].rewardData[
_allRewards[j]
];
amounts[j] +=
reward.users[user].accrued +
_getReward(
balanceData[i].userBalance,
_getAssetIndex(reward, balanceData[i].totalSupply, _incentivizedAssets[asset].decimals),
reward.users[user].index,
_incentivizedAssets[asset].decimals
);
}
}
return (rewards, amounts);
}
/**
* @dev Claims reward for an user on all the given incentivized assets, accumulating the accured rewards
* then transferring the reward asset to the user
* @param incentivizedAssets The list of incentivized asset addresses (atoken addresses)
* @param reward The reward to claim (only claims this reward address across all atokens you enter)
* @param amountToClaim The amount of the reward to claim
* @param to The address to send the claimed funds to
* @return rewardAccured The total amount of rewards claimed by the user
**/
function claimReward(
address[] calldata incentivizedAssets,
address reward,
uint256 amountToClaim,
address to
) external override returns (uint256) {
if (amountToClaim == 0) {
return 0;
}
address user = msg.sender;
DistributionTypes.UserAssetState[] memory userState = _getUserState(incentivizedAssets, user);
_batchUpdate(user, userState);
uint256 rewardAccrued;
for (uint256 i = 0; i < incentivizedAssets.length; i++) {
address asset = incentivizedAssets[i];
if (_incentivizedAssets[asset].rewardData[reward].users[user].accrued == 0) {
continue;
}
rewardAccrued += _incentivizedAssets[asset].rewardData[reward].users[user].accrued;
if (rewardAccrued <= amountToClaim) {
_incentivizedAssets[asset].rewardData[reward].users[user].accrued = 0;
} else {
uint256 remainder = rewardAccrued - amountToClaim;
rewardAccrued -= remainder;
_incentivizedAssets[asset].rewardData[reward].users[user].accrued = remainder;
break;
}
}
if (rewardAccrued == 0) {
return 0;
}
IERC20(reward).transferFrom(REWARDS_VAULT, to, rewardAccrued);
emit RewardClaimed(msg.sender, reward, to, rewardAccrued);
return rewardAccrued;
}
/**
* @dev Claims all available rewards on the given incentivized assets
* @param incentivizedAssets The list of incentivized asset addresses
* @param to The address to send the claimed funds to
* @return rewards The list of possible reward addresses
* @return amounts The list of amounts of each reward claimed
**/
function claimAllRewards(
address[] calldata incentivizedAssets,
address to
) external override returns (address[] memory, uint256[] memory) {
address[] memory rewards = _allRewards;
uint256[] memory amounts = new uint256[](_allRewards.length);
address user = msg.sender;
for (uint256 i = 0; i < incentivizedAssets.length; i++) {
address asset = incentivizedAssets[i];
for (uint256 j = 0; j < _allRewards.length; j++) {
uint256 amount = _incentivizedAssets[asset].rewardData[rewards[j]].users[user].accrued;
if (amount != 0) {
amounts[j] += amount;
_incentivizedAssets[asset].rewardData[rewards[j]].users[user].accrued = 0;
}
}
}
for (uint256 i = 0; i < amounts.length; i++) {
if (amounts[i] != 0) {
IERC20(rewards[i]).transferFrom(REWARDS_VAULT, to, amounts[i]);
emit RewardClaimed(msg.sender, rewards[i], to, amounts[i]);
}
}
return (rewards, amounts);
}
}