Skip to content

Commit

Permalink
Merge pull request #6 from aave/feat/base-incentives-controller
Browse files Browse the repository at this point in the history
Abstract IncentivesContract logic, adapt Staked Incentives Controller and Pull Reward Incentives Controller
  • Loading branch information
Steven Valeri authored Nov 16, 2021
2 parents ad534cd + c02a737 commit 2492c20
Show file tree
Hide file tree
Showing 35 changed files with 1,928 additions and 300 deletions.
59 changes: 59 additions & 0 deletions contracts/incentives/PullRewardsIncentivesController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {IERC20} from '@aave/aave-stake/contracts/interfaces/IERC20.sol';
import {SafeERC20} from '@aave/aave-stake/contracts/lib/SafeERC20.sol';

import {BaseIncentivesController} from './base/BaseIncentivesController.sol';

/**
* @title PullRewardsIncentivesController
* @notice Distributor contract for ERC20 rewards to the Aave protocol participants that pulls ERC20 from external account
* @author Aave
**/
contract PullRewardsIncentivesController is
BaseIncentivesController
{
using SafeERC20 for IERC20;

address internal _rewardsVault;

event RewardsVaultUpdated(address indexed vault);

constructor(IERC20 rewardToken, address emissionManager)
BaseIncentivesController(rewardToken, emissionManager)
{}

/**
* @dev Initialize AaveIncentivesController
* @param rewardsVault rewards vault to pull ERC20 funds
**/
function initialize(address rewardsVault) external initializer {
_rewardsVault = rewardsVault;
emit RewardsVaultUpdated(_rewardsVault);
}

/**
* @dev returns the current rewards vault contract
* @return address
*/
function getRewardsVault() external view returns (address) {
return _rewardsVault;
}

/**
* @dev update the rewards vault address, only allowed by the Rewards admin
* @param rewardsVault The address of the rewards vault
**/
function setRewardsVault(address rewardsVault) external onlyEmissionManager {
_rewardsVault = rewardsVault;
emit RewardsVaultUpdated(rewardsVault);
}


/// @inheritdoc BaseIncentivesController
function _transferRewards(address to, uint256 amount) internal override {
IERC20(REWARD_TOKEN).safeTransferFrom(_rewardsVault, to, amount);
}
}
194 changes: 8 additions & 186 deletions contracts/incentives/StakedTokenIncentivesController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,9 @@ pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {SafeERC20} from '@aave/aave-stake/contracts/lib/SafeERC20.sol';
import {SafeMath} from '../lib/SafeMath.sol';
import {DistributionTypes} from '../lib/DistributionTypes.sol';
import {VersionedInitializable} from '@aave/aave-stake/contracts/utils/VersionedInitializable.sol';
import {DistributionManager} from './DistributionManager.sol';
import {IStakedTokenWithConfig} from '../interfaces/IStakedTokenWithConfig.sol';
import {IERC20} from '@aave/aave-stake/contracts/interfaces/IERC20.sol';
import {IScaledBalanceToken} from '../interfaces/IScaledBalanceToken.sol';
import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol';
import {BaseIncentivesController} from './base/BaseIncentivesController.sol';
import {IStakedTokenWithConfig} from '../interfaces/IStakedTokenWithConfig.sol';

/**
* @title StakedTokenIncentivesController
Expand All @@ -19,200 +14,27 @@ import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController
* The reference staked token implementation is at https://github.com/aave/aave-stake-v2
* @author Aave
**/
contract StakedTokenIncentivesController is
IAaveIncentivesController,
VersionedInitializable,
DistributionManager
{
using SafeMath for uint256;
contract StakedTokenIncentivesController is BaseIncentivesController {
using SafeERC20 for IERC20;

uint256 public constant REVISION = 1;

IStakedTokenWithConfig public immutable STAKE_TOKEN;

mapping(address => uint256) internal _usersUnclaimedRewards;

// this mapping allows whitelisted addresses to claim on behalf of others
// useful for contracts that hold tokens to be rewarded but don't have any native logic to claim Liquidity Mining rewards
mapping(address => address) internal _authorizedClaimers;

modifier onlyAuthorizedClaimers(address claimer, address user) {
require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED');
_;
}

constructor(IStakedTokenWithConfig stakeToken, address emissionManager)
DistributionManager(emissionManager)
BaseIncentivesController(IERC20(address(stakeToken)), emissionManager)
{
STAKE_TOKEN = stakeToken;
}

/**
* @dev Initialize IStakedTokenIncentivesController
* @param addressesProvider the address of the corresponding addresses provider
**/
function initialize(address addressesProvider) external initializer {
function initialize() external initializer {
//approves the safety module to allow staking
IERC20(STAKE_TOKEN.STAKED_TOKEN()).safeApprove(address(STAKE_TOKEN), type(uint256).max);
}

/// @inheritdoc IAaveIncentivesController
function configureAssets(address[] calldata assets, uint256[] calldata emissionsPerSecond)
external
override
onlyEmissionManager
{
require(assets.length == emissionsPerSecond.length, 'INVALID_CONFIGURATION');

DistributionTypes.AssetConfigInput[] memory assetsConfig =
new DistributionTypes.AssetConfigInput[](assets.length);

for (uint256 i = 0; i < assets.length; i++) {
assetsConfig[i].underlyingAsset = assets[i];
assetsConfig[i].emissionPerSecond = uint104(emissionsPerSecond[i]);

require(assetsConfig[i].emissionPerSecond == emissionsPerSecond[i], 'INVALID_CONFIGURATION');

assetsConfig[i].totalStaked = IScaledBalanceToken(assets[i]).scaledTotalSupply();
}
_configureAssets(assetsConfig);
}

/// @inheritdoc IAaveIncentivesController
function handleAction(
address user,
uint256 totalSupply,
uint256 userBalance
) external override {
uint256 accruedRewards = _updateUserAssetInternal(user, msg.sender, userBalance, totalSupply);
if (accruedRewards != 0) {
_usersUnclaimedRewards[user] = _usersUnclaimedRewards[user].add(accruedRewards);
emit RewardsAccrued(user, accruedRewards);
}
}

/// @inheritdoc IAaveIncentivesController
function getRewardsBalance(address[] calldata assets, address user)
external
view
override
returns (uint256)
{
uint256 unclaimedRewards = _usersUnclaimedRewards[user];

DistributionTypes.UserStakeInput[] memory userState =
new DistributionTypes.UserStakeInput[](assets.length);
for (uint256 i = 0; i < assets.length; i++) {
userState[i].underlyingAsset = assets[i];
(userState[i].stakedByUser, userState[i].totalStaked) = IScaledBalanceToken(assets[i])
.getScaledUserBalanceAndSupply(user);
}
unclaimedRewards = unclaimedRewards.add(_getUnclaimedRewards(user, userState));
return unclaimedRewards;
}

/// @inheritdoc IAaveIncentivesController
function claimRewards(
address[] calldata assets,
uint256 amount,
address to
) external override returns (uint256) {
require(to != address(0), 'INVALID_TO_ADDRESS');
return _claimRewards(assets, amount, msg.sender, msg.sender, to);
}

/// @inheritdoc IAaveIncentivesController
function claimRewardsOnBehalf(
address[] calldata assets,
uint256 amount,
address user,
address to
) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) {
require(user != address(0), 'INVALID_USER_ADDRESS');
require(to != address(0), 'INVALID_TO_ADDRESS');
return _claimRewards(assets, amount, msg.sender, user, to);
}

/**
* @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards.
* @param amount Amount of rewards to claim
* @param user Address to check and claim rewards
* @param to Address that will be receiving the rewards
* @return Rewards claimed
**/

/// @inheritdoc IAaveIncentivesController
function setClaimer(address user, address caller) external override onlyEmissionManager {
_authorizedClaimers[user] = caller;
emit ClaimerSet(user, caller);
}

/// @inheritdoc IAaveIncentivesController
function getClaimer(address user) external view override returns (address) {
return _authorizedClaimers[user];
}

/// @inheritdoc IAaveIncentivesController
function getUserUnclaimedRewards(address _user) external view override returns (uint256) {
return _usersUnclaimedRewards[_user];
}

/// @inheritdoc IAaveIncentivesController
function REWARD_TOKEN() external view override returns (address) {
return address(STAKE_TOKEN);
}

/**
* @dev returns the revision of the implementation contract
*/
function getRevision() internal pure override returns (uint256) {
return REVISION;
}

/**
* @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards.
* @param amount Amount of rewards to claim
* @param user Address to check and claim rewards
* @param to Address that will be receiving the rewards
* @return Rewards claimed
**/
function _claimRewards(
address[] calldata assets,
uint256 amount,
address claimer,
address user,
address to
) internal returns (uint256) {
if (amount == 0) {
return 0;
}
uint256 unclaimedRewards = _usersUnclaimedRewards[user];

DistributionTypes.UserStakeInput[] memory userState =
new DistributionTypes.UserStakeInput[](assets.length);
for (uint256 i = 0; i < assets.length; i++) {
userState[i].underlyingAsset = assets[i];
(userState[i].stakedByUser, userState[i].totalStaked) = IScaledBalanceToken(assets[i])
.getScaledUserBalanceAndSupply(user);
}

uint256 accruedRewards = _claimRewards(user, userState);
if (accruedRewards != 0) {
unclaimedRewards = unclaimedRewards.add(accruedRewards);
emit RewardsAccrued(user, accruedRewards);
}

if (unclaimedRewards == 0) {
return 0;
}

uint256 amountToClaim = amount > unclaimedRewards ? unclaimedRewards : amount;
_usersUnclaimedRewards[user] = unclaimedRewards - amountToClaim; // Safe due to the previous line

STAKE_TOKEN.stake(to, amountToClaim);
emit RewardsClaimed(user, to, claimer, amountToClaim);

return amountToClaim;
/// @inheritdoc BaseIncentivesController
function _transferRewards(address to, uint256 amount) internal override {
STAKE_TOKEN.stake(to, amount);
}
}
Loading

0 comments on commit 2492c20

Please sign in to comment.