aave-protocol-v2/contracts/protocol/tokenization/RewardsAwareAToken.sol
2021-05-12 12:55:13 +02:00

516 lines
19 KiB
Solidity

// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
import {IDelegationToken} from '../../interfaces/IDelegationToken.sol';
import {Errors} from '../libraries/helpers/Errors.sol';
import {AToken} from './AToken.sol';
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
import {IRewardsAwareAToken} from '../../interfaces/IRewardsAwareAToken.sol';
import {IAToken} from '../../interfaces/IRewardsAwareAToken.sol';
import {WadRayMath} from '../../protocol/libraries/math/WadRayMath.sol';
import {PercentageMath} from '../../protocol/libraries/math/PercentageMath.sol';
/**
* @title Rewards Aware AToken
* @notice AToken aware to claim and distribute rewards from an external rewards controller.
* @author Aave
*/
abstract contract RewardsAwareAToken is AToken, IRewardsAwareAToken {
using SafeERC20 for IERC20;
using WadRayMath for uint256;
using PercentageMath for uint256;
// Precision of the multiplier for calculating distribution percentages
uint256 public constant PRECISION = 24;
// Max rewards allowed
uint256 public constant MAX_REWARD_TOKENS = 9;
// Multiplier for calculating share indexes
uint256 public constant MULTIPLIER = 10**PRECISION;
// Max rewards reserve factor
uint256 public constant MAX_VALID_RESERVE_FACTOR = 10000;
// Rewards Reserve Factor
uint256 internal rewardsReserveFactor;
// token => user => userIndex
mapping(address => mapping(address => uint256)) private _userIndex;
// token => user => userRewardsAccrued
mapping(address => mapping(address => uint256)) private _userRewardsAccrued;
// token => user => userRewardsClaimed
mapping(address => mapping(address => uint256)) private _userRewardsClaimed;
// token => rewardIndex
mapping(address => uint256) private _rewardIndex;
// reward address => lifetime rewards
mapping(address => uint256) private _lifetimeRewards;
// token => lifetimeMinted
mapping(address => uint256) private _lifetimeClaimed;
// reward tokens
address[MAX_REWARD_TOKENS] internal _rewardTokens;
modifier onlyPoolAdmin {
address poolAdmin =
ILendingPoolAddressesProvider(ILendingPool(_pool).getAddressesProvider()).getPoolAdmin();
require(_msgSender() == poolAdmin, Errors.AT_CALLER_MUST_BE_POOL_ADMIN);
_;
}
/**
* @dev Get the current claimable rewards dinamically by calling the external rewards contract and simulate the rewards without storage
* @param token Address of the rewards token
* @param user Address of the account to get current claimable rewards
* @return The claimable rewards of the address passed at the "user" argument
*/
function getClaimableRewards(address token, address user) public view override returns (uint256) {
return _getPendingRewards(token, user);
}
/**
* @dev Get the total lifetime rewards of an address from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the total lifetime rewards
* @return The total lifetime rewards of an address, this includes claimed and pending rewards
*/
function getUserRewardsAccrued(address token, address user)
external
view
override
returns (uint256)
{
return _userRewardsAccrued[token][user];
}
/**
* @dev Get the claimed rewards of an address from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the claimed rewards
* @return The claimed rewards of an address
*/
function getUserClaimedRewards(address token, address user)
external
view
override
returns (uint256)
{
return _userRewardsClaimed[token][user];
}
/**
* @dev Get lifetime rewards of a rewards token
* @param token Address of the rewards token
* @return The total of lifetime rewards
*/
function getLifetimeRewards(address token) external view override returns (uint256) {
return _getLifetimeRewards(token);
}
/**
* @dev Get lifetime minted rewards of a rewards token
* @param token Address of the rewards token
* @return The lifetime rewards claimed
*/
function getLifetimeClaimed(address token) external view override returns (uint256) {
return _lifetimeClaimed[token];
}
/**
* @dev Get the user checkpoint of the aToken contract itself, from contract storage
* @param token Address of the rewards token
* @param user Address of the account to get the claimed rewards
* @return The total lifetime rewards, this includes claimed and pending rewards from the aToken contract
*/
function getUserIndex(address token, address user) external view override returns (uint256) {
return _userIndex[token][user];
}
/**
* @dev Get the rewards ERC20 token address by index position
* @param index The position of the rewards, starting from zero up
* @return The rewards ERC20 token address
*/
function getRewardsTokenAddress(uint256 index) external view override returns (address) {
return _getRewardsTokenAddress(index);
}
/**
* @dev Get all the rewards token addresses
* @return The list of rewards token addresseses
*/
function getRewardsTokenAddressList()
external
view
override
returns (address[MAX_REWARD_TOKENS] memory)
{
return _rewardTokens;
}
/**
* @dev Claim the available token rewards from the caller and transfers to `msg.sender`
*/
function claim(address token) external override {
_claim(token);
}
/**
* @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
* - Only callable by the LendingPool, as extra state updates there need to be managed
* @param user The owner of the aTokens, getting them burned
* @param receiverOfUnderlying The address that will receive the underlying
* @param amount The amount being burned
* @param index The new liquidity index of the reserve
**/
function burn(
address user,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) public virtual override(AToken, IAToken) onlyLendingPool {
// Unstake
_unstake(UNDERLYING_ASSET_ADDRESS(), amount);
// Update distribution of rewards
_updateDistribution(user);
// burns aTokens
return super.burn(user, receiverOfUnderlying, amount, index);
}
/**
* @dev Mints `amount` aTokens to `user`
* - Only callable by the LendingPool, as extra state updates there need to be managed
* @param user The address receiving the minted tokens
* @param amount The amount of tokens getting minted
* @param index The new liquidity index of the reserve
* @return `true` if the the previous balance of the user was 0
*/
function mint(
address user,
uint256 amount,
uint256 index
) public virtual override(AToken, IAToken) onlyLendingPool returns (bool) {
// Stake
_stake(UNDERLYING_ASSET_ADDRESS(), amount);
// Update distribution of rewards
_updateDistribution(user);
// mint aTokens
return super.mint(user, amount, index);
}
/**
* @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer
* assets in borrow(), withdraw() and flashLoan()
* @param target The recipient of the aTokens
* @param amount The amount getting transferred
* @return The amount transferred
**/
function transferUnderlyingTo(address target, uint256 amount)
external
virtual
override(AToken, IAToken)
onlyLendingPool
returns (uint256)
{
_unstake(UNDERLYING_ASSET_ADDRESS(), amount);
_updateDistribution(target);
IERC20(UNDERLYING_ASSET_ADDRESS()).safeTransfer(target, amount);
return amount;
}
/**
* @dev Invoked to execute actions on the aToken side after a repayment.
* @param user The user executing the repayment
* @param amount The amount getting repaid
**/
function handleRepayment(address user, uint256 amount)
external
virtual
override(AToken, IAToken)
onlyLendingPool
{
_stake(UNDERLYING_ASSET_ADDRESS(), amount);
_updateDistribution(user);
}
/**
* @dev Set the rewards reserve factor to the treasury, only allowed by LendingPoolAddressesProvider pool admin
* @param reserveFactor reserve factor in percent value
*/
function setRewardsReserveFactor(uint256 reserveFactor) external override onlyPoolAdmin {
_setRewardsReserveFactor(reserveFactor);
}
/**
* @dev Return the rewards reserve factor to the treasury
* @return reserve factor in percent value
*/
function getRewardsReserveFactor() external view override returns (uint256) {
return rewardsReserveFactor;
}
/**
* @dev Hook to update distributions before token transfer. Called at "burn" and "transferUnderlyingTo".
* @param from address of the `from`
* @param to address of the `to`
*/
function _beforeTokenTransfer(
address from,
address to,
uint256
) internal override {
_updateDistribution(from);
_updateDistribution(to);
}
/**
* @dev Get lifetime rewards of a rewards token
* @param token Address of the rewards token
* @return The total of lifetime rewards
*/
function _getLifetimeRewards(address token) internal view returns (uint256) {
return _lifetimeRewards[token];
}
/**
* @dev Get the rewards ERC20 token address by index position
* @param index The position of the rewards, starting from zero up
* @return The rewards ERC20 token address
*/
function _getRewardsTokenAddress(uint256 index) internal view returns (address) {
if (index > MAX_REWARD_TOKENS) return address(0);
return _rewardTokens[index];
}
/**
* @dev Calculate rewards distribution in proportion of aTokens balance during a timeframe.
* @param aTokensBalance The amounts of aTokens of the user
* @param lastRewardIndex The last reward token index of the distribution percentage
* @param lastUserIndex The last user index of the distribution percentage
* @return The proportional rewards between two distribution percentages indexes
*/
function _getRewards(
uint256 aTokensBalance,
uint256 lastRewardIndex,
uint256 lastUserIndex
) internal pure returns (uint256) {
return aTokensBalance.mul(lastRewardIndex.sub(lastUserIndex)).div(MULTIPLIER);
}
/**
* @dev Calculate the next reward token index, that contains the sums of the percentages of the distribution of the token rewards.
* @param latestRewardIndex The latest reward token index
* @param accruedRewards The accrued rewards during the current distribution
* @param aTokenSupply The total supply of this aToken contract
* @return The sums of the reward indexes plus the latest percentage distribution index of the reward
*/
function _getRewardIndex(
uint256 latestRewardIndex,
uint256 accruedRewards,
uint256 aTokenSupply
) internal pure returns (uint256) {
return latestRewardIndex.add(accruedRewards.mul(MULTIPLIER).div(aTokenSupply));
}
/**
* @dev Getter for virtually calculating the pending rewards of an user without altering the storage of the contract
* @param user Address of the user to lookup for rewards
* @param token The rewards token to lookup for user rewards
* @return Expected token rewards for an user
*/
function _getPendingRewards(address token, address user) internal view returns (uint256) {
uint256 aTokenTotalSupply = totalSupply();
uint256 externalLifetimeRewards = _getExternalLifetimeRewards(token);
uint256 rewardAccrued = externalLifetimeRewards.sub(_lifetimeRewards[token]);
uint256 rewardAssetIndex = _rewardIndex[token];
if (aTokenTotalSupply != 0 && rewardAccrued != 0) {
rewardAssetIndex = _getRewardIndex(_rewardIndex[token], rewardAccrued, aTokenTotalSupply);
}
uint256 userLatestRewards =
_getRewards(balanceOf(user), rewardAssetIndex, _userIndex[token][user]);
return
_userRewardsAccrued[token][user].add(userLatestRewards).sub(_userRewardsClaimed[token][user]);
}
/**
* @dev Retrieve the latest rewards distribution to an user and update the distribution of a token rewards at storage
* @param user The `user` address to update the rewards index and retrieve latest reward distribution
* @param token The reward token to lookup the distribution
* @param aTokenBalance The aToken balance of the user determines his share during the distribution
* @param aTokenSupply The current total supply of this aToken
*/
function _updateRewardDistribution(
address user,
address token,
uint256 aTokenBalance,
uint256 aTokenSupply
) private {
uint256 userRewardsIndex = _userIndex[token][user];
uint256 externalLifetimeRewards = _computeExternalLifetimeRewards(token);
uint256 rewardAccrued = externalLifetimeRewards.sub(_lifetimeRewards[token]);
uint256 previousRewardIndex = _rewardIndex[token];
uint256 rewardIndex = _getRewardIndex(previousRewardIndex, rewardAccrued, aTokenSupply);
// Check reward index
if (rewardIndex != previousRewardIndex) {
_rewardIndex[token] = rewardIndex;
_setLifetimeRewards(token, externalLifetimeRewards);
emit RewardIndexUpdate(token, rewardIndex);
}
// Check user reward index
if (userRewardsIndex != rewardIndex) {
_userIndex[token][user] = rewardIndex;
emit UserIndexUpdate(user, token, rewardIndex);
uint256 userRewardAccrued = _getRewards(aTokenBalance, rewardIndex, userRewardsIndex);
if (userRewardAccrued != 0) {
_userRewardsAccrued[token][user] = _userRewardsAccrued[token][user].add(userRewardAccrued);
emit UserRewardSnapshot(user, token, userRewardAccrued);
}
}
}
/**
* @dev Update the distribution of each rewards configured at this aToken
* @param user The `user` address to update the rewards index and retrieve latest reward distribution
*/
function _updateDistribution(address user) internal {
uint256 aTokenBalance = balanceOf(user);
if (aTokenBalance == 0) {
return;
}
uint256 aTokenSupply = totalSupply();
if (aTokenSupply == 0) {
return;
}
for (uint256 index; index < MAX_REWARD_TOKENS; index++) {
address rewardToken = _rewardTokens[index];
if (rewardToken == address(0)) break;
_updateRewardDistribution(user, rewardToken, aTokenBalance, aTokenSupply);
}
}
/**
* @dev Set token rewards claimed from an `user` address
* @param user The `user` address to sum and set the claimed rewards
* @param token The claimed reward `token` address
* @param claimed The amount of `claimed` rewards by the `user`
*/
function _setUserRewardsClaimed(
address user,
address token,
uint256 claimed
) private {
_userRewardsClaimed[token][user] = _userRewardsClaimed[token][user].add(claimed);
_lifetimeClaimed[token] = _lifetimeClaimed[token].add(claimed);
}
/**
* @dev Claim the available rewards from the caller and transfers to `msg.sender`
* @param token Reward adresss to lookup rewards and claim
*/
function _claim(address token) private {
// Mint the available rewards from the external rewards controller to this aToken contract implementation
_claimRewardsFromController();
// Update aToken and user rewards
_updateDistribution(msg.sender);
// Get the remaining tokens to claim
uint256 accruedRewards =
_userRewardsAccrued[token][msg.sender].sub(_userRewardsClaimed[token][msg.sender]);
if (accruedRewards > 0) {
// Track the claimed rewards
_setUserRewardsClaimed(msg.sender, token, accruedRewards);
// Unstake reward token, if needed
uint256 unstaked = _unstake(token, accruedRewards);
uint256 userRewards = unstaked;
// Transfer rewards to treasury
if (_treasury != address(0)) {
uint256 reserveRewards = unstaked.percentMul(rewardsReserveFactor);
if (reserveRewards > 0) {
userRewards = unstaked.sub(reserveRewards);
IERC20(token).transfer(_treasury, reserveRewards);
}
}
// Transfer rewards to user
IERC20(token).transfer(msg.sender, userRewards);
emit Claim(msg.sender, token, accruedRewards, userRewards);
}
}
/**
* @dev Set the lifetime token lifetime rewards
* @param token The reward token address
* @param amount The amount of lifetime rewards
*/
function _setLifetimeRewards(address token, uint256 amount) private {
_lifetimeRewards[token] = amount;
}
/**
* @dev External call to retrieve the lifetime rewards of the aToken contract to the external Rewards Controller contract
* @notice Should only mutate the state outside RewardsAwareAToken abstract contract
* @param token Reward adresss to lookup available rewards
* @notice To be implemented by the contract that inherits this abstract contract RewardsAwareAToken
*/
function _computeExternalLifetimeRewards(address token) internal virtual returns (uint256);
/**
* @dev External view call to retrieve the lifetime rewards of the aToken contract to the external Rewards Controller contract
* @param token Reward adresss to lookup available rewards
* @notice To be implemented by the contract that inherits this abstract contract RewardsAwareAToken
*/
function _getExternalLifetimeRewards(address token) internal view virtual returns (uint256);
/**
* @dev External call to claim the lifetime accrued rewards of the aToken contract to the external Rewards Controller contract
* @notice To be implemented by the contract that inherits this abstract contract RewardsAwareAToken
* @notice WIP pending to check virtual balance versus claimed
*/
function _claimRewardsFromController() internal virtual;
/**
* @dev External call to stake a token of the aToken contract
* @notice Optional, to be implemented by the contract that inherits this abstract contract RewardsAwareAToken,if needed.
* @param amount to stake
*/
function _stake(address, uint256 amount) internal virtual returns (uint256) {
return amount;
}
/**
* @dev External call to unstake a token of the aToken contract
* @notice Optional, to be implemented by the contract that inherits this abstract contract RewardsAwareAToken, if needed.
* @param amount to unstake
*/
function _unstake(address, uint256 amount) internal virtual returns (uint256) {
return amount;
}
/**
* @dev Set the rewards reserve factor to the treasury
* @param reserveFactor reserve factor in percent value
*/
function _setRewardsReserveFactor(uint256 reserveFactor) internal {
require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR);
rewardsReserveFactor = reserveFactor;
emit RewardsReserveFactorUpdate(rewardsReserveFactor);
}
}