StakedToken vs ERC20
219 lines
// SPDX-License-Identifier: agpl-3.0
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;
pragma experimental ABIEncoderV2;
import {SafeERC20} from '@aave/aave-stake/contracts/lib/SafeERC20.sol';
import {SafeERC20} from '@aave/aave-stake/contracts/lib/SafeERC20.sol';
import {SafeMath} from '../lib/SafeMath.sol';
import {SafeMath} from '../lib/SafeMath.sol';
import {DistributionTypes} from '../lib/DistributionTypes.sol';
import {DistributionTypes} from '../lib/DistributionTypes.sol';
import {VersionedInitializable} from '@aave/aave-stake/contracts/utils/VersionedInitializable.sol';
import {VersionedInitializable} from '@aave/aave-stake/contracts/utils/VersionedInitializable.sol';
import {DistributionManager} from './DistributionManager.sol';
import {DistributionManager} from './DistributionManager.sol';
import {IStakedTokenWithConfig} from '../interfaces/IStakedTokenWithConfig.sol';
import {IERC20} from '@aave/aave-stake/contracts/interfaces/IERC20.sol';
import {IERC20} from '@aave/aave-stake/contracts/interfaces/IERC20.sol';
import {IScaledBalanceToken} from '../interfaces/IScaledBalanceToken.sol';
import {IScaledBalanceToken} from '../interfaces/IScaledBalanceToken.sol';
import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol';
import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol';
/**
/**
* @title StakedTokenIncentivesController
* @title ERC20TokenIncentivesController
* @notice Distributor contract for rewards to the Aave protocol, using a staked token as rewards asset.
* @notice Distributor contract for rewards to the Aave protocol, using a regular ERC20 token as rewards asset.
* The contract stakes the rewards before redistributing them to the Aave protocol participants.
* @author Aave, Joey Santoro
* The reference staked token implementation is at https://github.com/aave/aave-stake-v2
* @author Aave
**/
**/
contract StakedTokenIncentivesController is
contract ERC20TokenIncentivesController is
IAaveIncentivesController,
IAaveIncentivesController,
VersionedInitializable,
VersionedInitializable,
DistributionManager
DistributionManager
{
{
using SafeMath for uint256;
using SafeMath for uint256;
using SafeERC20 for IERC20;
using SafeERC20 for IERC20;
uint256 public constant REVISION = 1;
uint256 public constant REVISION = 1;
IStakedTokenWithConfig public immutable STAKE_TOKEN;
/// @inheritdoc IAaveIncentivesController
address public immutable override REWARD_TOKEN;
mapping(address => uint256) internal _usersUnclaimedRewards;
mapping(address => uint256) internal _usersUnclaimedRewards;
// this mapping allows whitelisted addresses to claim on behalf of others
// 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
// 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;
mapping(address => address) internal _authorizedClaimers;
modifier onlyAuthorizedClaimers(address claimer, address user) {
modifier onlyAuthorizedClaimers(address claimer, address user) {
require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED');
require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED');
_;
_;
}
}
constructor(IStakedTokenWithConfig stakeToken, address emissionManager)
constructor(address rewardToken, address emissionManager)
DistributionManager(emissionManager)
DistributionManager(emissionManager)
{
{
STAKE_TOKEN = stakeToken;
REWARD_TOKEN = rewardToken;
}
/**
* @dev Initialize IStakedTokenIncentivesController
* @param addressesProvider the address of the corresponding addresses provider
**/
function initialize(address addressesProvider) external initializer {
//approves the safety module to allow staking
IERC20(STAKE_TOKEN.STAKED_TOKEN()).safeApprove(address(STAKE_TOKEN), type(uint256).max);
}
}
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function configureAssets(address[] calldata assets, uint256[] calldata emissionsPerSecond)
function configureAssets(address[] calldata assets, uint256[] calldata emissionsPerSecond)
external
external
override
override
onlyEmissionManager
onlyEmissionManager
{
{
require(assets.length == emissionsPerSecond.length, 'INVALID_CONFIGURATION');
require(assets.length == emissionsPerSecond.length, 'INVALID_CONFIGURATION');
DistributionTypes.AssetConfigInput[] memory assetsConfig =
DistributionTypes.AssetConfigInput[] memory assetsConfig =
new DistributionTypes.AssetConfigInput[](assets.length);
new DistributionTypes.AssetConfigInput[](assets.length);
for (uint256 i = 0; i < assets.length; i++) {
for (uint256 i = 0; i < assets.length; i++) {
assetsConfig[i].underlyingAsset = assets[i];
assetsConfig[i].underlyingAsset = assets[i];
assetsConfig[i].emissionPerSecond = uint104(emissionsPerSecond[i]);
assetsConfig[i].emissionPerSecond = uint104(emissionsPerSecond[i]);
require(assetsConfig[i].emissionPerSecond == emissionsPerSecond[i], 'INVALID_CONFIGURATION');
require(assetsConfig[i].emissionPerSecond == emissionsPerSecond[i], 'INVALID_CONFIGURATION');
assetsConfig[i].totalStaked = IScaledBalanceToken(assets[i]).scaledTotalSupply();
assetsConfig[i].totalStaked = IScaledBalanceToken(assets[i]).scaledTotalSupply();
}
}
_configureAssets(assetsConfig);
_configureAssets(assetsConfig);
}
}
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function handleAction(
function handleAction(
address user,
address user,
uint256 totalSupply,
uint256 totalSupply,
uint256 userBalance
uint256 userBalance
) external override {
) external override {
uint256 accruedRewards = _updateUserAssetInternal(user, msg.sender, userBalance, totalSupply);
uint256 accruedRewards = _updateUserAssetInternal(user, msg.sender, userBalance, totalSupply);
if (accruedRewards != 0) {
if (accruedRewards != 0) {
_usersUnclaimedRewards[user] = _usersUnclaimedRewards[user].add(accruedRewards);
_usersUnclaimedRewards[user] = _usersUnclaimedRewards[user].add(accruedRewards);
emit RewardsAccrued(user, accruedRewards);
emit RewardsAccrued(user, accruedRewards);
}
}
}
}
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function getRewardsBalance(address[] calldata assets, address user)
function getRewardsBalance(address[] calldata assets, address user)
external
external
view
view
override
override
returns (uint256)
returns (uint256)
{
{
uint256 unclaimedRewards = _usersUnclaimedRewards[user];
uint256 unclaimedRewards = _usersUnclaimedRewards[user];
DistributionTypes.UserStakeInput[] memory userState =
DistributionTypes.UserStakeInput[] memory userState =
new DistributionTypes.UserStakeInput[](assets.length);
new DistributionTypes.UserStakeInput[](assets.length);
for (uint256 i = 0; i < assets.length; i++) {
for (uint256 i = 0; i < assets.length; i++) {
userState[i].underlyingAsset = assets[i];
userState[i].underlyingAsset = assets[i];
(userState[i].stakedByUser, userState[i].totalStaked) = IScaledBalanceToken(assets[i])
(userState[i].stakedByUser, userState[i].totalStaked) = IScaledBalanceToken(assets[i])
.getScaledUserBalanceAndSupply(user);
.getScaledUserBalanceAndSupply(user);
}
}
unclaimedRewards = unclaimedRewards.add(_getUnclaimedRewards(user, userState));
unclaimedRewards = unclaimedRewards.add(_getUnclaimedRewards(user, userState));
return unclaimedRewards;
return unclaimedRewards;
}
}
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function claimRewards(
function claimRewards(
address[] calldata assets,
address[] calldata assets,
uint256 amount,
uint256 amount,
address to
address to
) external override returns (uint256) {
) external override returns (uint256) {
require(to != address(0), 'INVALID_TO_ADDRESS');
require(to != address(0), 'INVALID_TO_ADDRESS');
return _claimRewards(assets, amount, msg.sender, msg.sender, to);
return _claimRewards(assets, amount, msg.sender, msg.sender, to);
}
}
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function claimRewardsOnBehalf(
function claimRewardsOnBehalf(
address[] calldata assets,
address[] calldata assets,
uint256 amount,
uint256 amount,
address user,
address user,
address to
address to
) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) {
) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) {
require(user != address(0), 'INVALID_USER_ADDRESS');
require(user != address(0), 'INVALID_USER_ADDRESS');
require(to != address(0), 'INVALID_TO_ADDRESS');
require(to != address(0), 'INVALID_TO_ADDRESS');
return _claimRewards(assets, amount, msg.sender, user, to);
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.
* @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 amount Amount of rewards to claim
* @param user Address to check and claim rewards
* @param user Address to check and claim rewards
* @param to Address that will be receiving the rewards
* @param to Address that will be receiving the rewards
* @return Rewards claimed
* @return Rewards claimed
**/
**/
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function setClaimer(address user, address caller) external override onlyEmissionManager {
function setClaimer(address user, address caller) external override onlyEmissionManager {
_authorizedClaimers[user] = caller;
_authorizedClaimers[user] = caller;
emit ClaimerSet(user, caller);
emit ClaimerSet(user, caller);
}
}
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function getClaimer(address user) external view override returns (address) {
function getClaimer(address user) external view override returns (address) {
return _authorizedClaimers[user];
return _authorizedClaimers[user];
}
}
/// @inheritdoc IAaveIncentivesController
/// @inheritdoc IAaveIncentivesController
function getUserUnclaimedRewards(address _user) external view override returns (uint256) {
function getUserUnclaimedRewards(address _user) external view override returns (uint256) {
return _usersUnclaimedRewards[_user];
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
* @dev returns the revision of the implementation contract
*/
*/
function getRevision() internal pure override returns (uint256) {
function getRevision() internal pure override returns (uint256) {
return REVISION;
return REVISION;
}
}
/**
/**
* @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards.
* @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 amount Amount of rewards to claim
* @param user Address to check and claim rewards
* @param user Address to check and claim rewards
* @param to Address that will be receiving the rewards
* @param to Address that will be receiving the rewards
* @return Rewards claimed
* @return Rewards claimed
**/
**/
function _claimRewards(
function _claimRewards(
address[] calldata assets,
address[] calldata assets,
uint256 amount,
uint256 amount,
address claimer,
address claimer,
address user,
address user,
address to
address to
) internal returns (uint256) {
) internal returns (uint256) {
if (amount == 0) {
if (amount == 0) {
return 0;
return 0;
}
}
uint256 unclaimedRewards = _usersUnclaimedRewards[user];
uint256 unclaimedRewards = _usersUnclaimedRewards[user];
DistributionTypes.UserStakeInput[] memory userState =
DistributionTypes.UserStakeInput[] memory userState =
new DistributionTypes.UserStakeInput[](assets.length);
new DistributionTypes.UserStakeInput[](assets.length);
for (uint256 i = 0; i < assets.length; i++) {
for (uint256 i = 0; i < assets.length; i++) {
userState[i].underlyingAsset = assets[i];
userState[i].underlyingAsset = assets[i];
(userState[i].stakedByUser, userState[i].totalStaked) = IScaledBalanceToken(assets[i])
(userState[i].stakedByUser, userState[i].totalStaked) = IScaledBalanceToken(assets[i])
.getScaledUserBalanceAndSupply(user);
.getScaledUserBalanceAndSupply(user);
}
}
uint256 accruedRewards = _claimRewards(user, userState);
uint256 accruedRewards = _claimRewards(user, userState);
if (accruedRewards != 0) {
if (accruedRewards != 0) {
unclaimedRewards = unclaimedRewards.add(accruedRewards);
unclaimedRewards = unclaimedRewards.add(accruedRewards);
emit RewardsAccrued(user, accruedRewards);
emit RewardsAccrued(user, accruedRewards);
}
}
if (unclaimedRewards == 0) {
if (unclaimedRewards == 0) {
return 0;
return 0;
}
}
uint256 amountToClaim = amount > unclaimedRewards ? unclaimedRewards : amount;
uint256 amountToClaim = amount > unclaimedRewards ? unclaimedRewards : amount;
_usersUnclaimedRewards[user] = unclaimedRewards - amountToClaim; // Safe due to the previous line
_usersUnclaimedRewards[user] = unclaimedRewards - amountToClaim; // Safe due to the previous line
STAKE_TOKEN.stake(to, amountToClaim);
IERC20(REWARD_TOKEN).transfer(to, amountToClaim);
emit RewardsClaimed(user, to, claimer, amountToClaim);
emit RewardsClaimed(user, to, claimer, amountToClaim);
return amountToClaim;
return amountToClaim;
}
}
}
}