aave-protocol-v2/contracts/protocol/libraries/logic/ReserveLogic.sol

374 lines
14 KiB
Solidity
Raw Normal View History

2020-06-20 23:40:03 +00:00
// SPDX-License-Identifier: agpl-3.0
2020-11-20 10:45:20 +00:00
pragma solidity 0.6.12;
2020-06-20 23:40:03 +00:00
import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
import {IAToken} from '../../../interfaces/IAToken.sol';
import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
import {MathUtils} from '../math/MathUtils.sol';
2020-08-20 07:51:21 +00:00
import {WadRayMath} from '../math/WadRayMath.sol';
2020-09-14 13:09:16 +00:00
import {PercentageMath} from '../math/PercentageMath.sol';
2020-09-04 08:27:32 +00:00
import {Errors} from '../helpers/Errors.sol';
import {DataTypes} from '../types/DataTypes.sol';
2020-06-27 02:13:32 +00:00
/**
2020-07-08 22:16:05 +00:00
* @title ReserveLogic library
* @author Aave
2020-11-25 17:33:49 +00:00
* @notice Implements the logic to update the reserves state
2020-07-08 22:16:05 +00:00
*/
2020-06-20 23:40:03 +00:00
library ReserveLogic {
2020-07-08 22:16:05 +00:00
using SafeMath for uint256;
using WadRayMath for uint256;
2020-09-14 13:09:16 +00:00
using PercentageMath for uint256;
2020-08-12 17:36:58 +00:00
using SafeERC20 for IERC20;
2020-08-19 14:36:58 +00:00
/**
* @dev Emitted when the state of a reserve is updated
2020-11-25 17:33:49 +00:00
* @param asset The address of the underlying asset of the reserve
* @param liquidityRate The new liquidity rate
* @param stableBorrowRate The new stable borrow rate
* @param variableBorrowRate The new variable borrow rate
* @param liquidityIndex The new liquidity index
* @param variableBorrowIndex The new variable borrow index
2020-08-19 14:36:58 +00:00
**/
event ReserveDataUpdated(
2020-11-25 17:33:49 +00:00
address indexed asset,
2020-08-19 14:36:58 +00:00
uint256 liquidityRate,
uint256 stableBorrowRate,
uint256 variableBorrowRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex
);
using ReserveLogic for DataTypes.ReserveData;
2020-11-24 15:17:27 +00:00
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
2020-07-08 22:16:05 +00:00
/**
2020-11-25 17:33:49 +00:00
* @dev Returns the ongoing normalized income for the reserve
* A value of 1e27 means there is no income. As time passes, the income is accrued
* A value of 2*1e27 means for each unit of asset one unit of income has been accrued
* @param reserve The reserve object
2020-07-08 22:16:05 +00:00
* @return the normalized income. expressed in ray
**/
function getNormalizedIncome(DataTypes.ReserveData storage reserve)
internal
view
returns (uint256)
{
2020-08-21 13:13:08 +00:00
uint40 timestamp = reserve.lastUpdateTimestamp;
2020-08-10 18:20:08 +00:00
//solium-disable-next-line
if (timestamp == uint40(block.timestamp)) {
//if the index was updated in the same block, no need to perform any calculation
2020-09-12 11:18:17 +00:00
return reserve.liquidityIndex;
}
uint256 cumulated =
MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
reserve.liquidityIndex
);
2020-07-08 22:16:05 +00:00
return cumulated;
}
/**
2020-11-25 17:33:49 +00:00
* @dev Returns the ongoing normalized variable debt for the reserve
* A value of 1e27 means there is no debt. As time passes, the income is accrued
* A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated
* @param reserve The reserve object
* @return The normalized variable debt. expressed in ray
2020-07-08 22:16:05 +00:00
**/
function getNormalizedDebt(DataTypes.ReserveData storage reserve)
internal
view
returns (uint256)
{
2020-08-21 13:13:08 +00:00
uint40 timestamp = reserve.lastUpdateTimestamp;
2020-08-10 18:20:08 +00:00
//solium-disable-next-line
if (timestamp == uint40(block.timestamp)) {
//if the index was updated in the same block, no need to perform any calculation
return reserve.variableBorrowIndex;
}
uint256 cumulated =
MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul(
reserve.variableBorrowIndex
);
2020-07-08 22:16:05 +00:00
return cumulated;
}
/**
2020-11-25 17:33:49 +00:00
* @dev Updates the liquidity cumulative index and the variable borrow index.
2020-08-21 13:13:08 +00:00
* @param reserve the reserve object
2020-07-08 22:16:05 +00:00
**/
function updateState(DataTypes.ReserveData storage reserve) internal {
uint256 scaledVariableDebt =
IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply();
2020-09-17 08:53:55 +00:00
uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex;
uint256 previousLiquidityIndex = reserve.liquidityIndex;
2020-10-27 16:22:51 +00:00
uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp;
2020-09-14 13:09:16 +00:00
(uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) =
_updateIndexes(
reserve,
scaledVariableDebt,
previousLiquidityIndex,
previousVariableBorrowIndex,
lastUpdatedTimestamp
);
2020-06-27 02:13:32 +00:00
2020-09-17 08:53:55 +00:00
_mintToTreasury(
reserve,
2020-10-15 12:13:46 +00:00
scaledVariableDebt,
2020-09-17 08:53:55 +00:00
previousVariableBorrowIndex,
newLiquidityIndex,
2020-10-27 16:22:51 +00:00
newVariableBorrowIndex,
lastUpdatedTimestamp
2020-09-17 08:53:55 +00:00
);
2020-07-08 22:16:05 +00:00
}
/**
2020-11-25 17:33:49 +00:00
* @dev Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example to accumulate
* the flashloan fee to the reserve, and spread it between all the depositors
* @param reserve The reserve object
* @param totalLiquidity The total liquidity available in the reserve
* @param amount The amount to accomulate
2020-07-08 22:16:05 +00:00
**/
function cumulateToLiquidityIndex(
DataTypes.ReserveData storage reserve,
2020-08-21 13:13:08 +00:00
uint256 totalLiquidity,
uint256 amount
2020-10-15 12:13:46 +00:00
) internal {
2020-08-21 13:13:08 +00:00
uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay());
2020-06-20 23:40:03 +00:00
uint256 result = amountToLiquidityRatio.add(WadRayMath.ray());
2020-07-08 22:16:05 +00:00
2020-09-12 11:18:17 +00:00
result = result.rayMul(reserve.liquidityIndex);
require(result <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
2020-09-12 11:18:17 +00:00
reserve.liquidityIndex = uint128(result);
2020-07-08 22:16:05 +00:00
}
/**
2020-11-25 17:33:49 +00:00
* @dev Initializes a reserve
* @param reserve The reserve object
* @param aTokenAddress The address of the overlying atoken contract
* @param interestRateStrategyAddress The address of the interest rate strategy contract
2020-07-08 22:16:05 +00:00
**/
function init(
DataTypes.ReserveData storage reserve,
2020-08-21 13:13:08 +00:00
address aTokenAddress,
address stableDebtTokenAddress,
address variableDebtTokenAddress,
2020-08-21 13:13:08 +00:00
address interestRateStrategyAddress
2020-07-08 22:16:05 +00:00
) external {
require(reserve.aTokenAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED);
2020-11-10 16:15:36 +00:00
reserve.liquidityIndex = uint128(WadRayMath.ray());
reserve.variableBorrowIndex = uint128(WadRayMath.ray());
2020-08-21 13:13:08 +00:00
reserve.aTokenAddress = aTokenAddress;
reserve.stableDebtTokenAddress = stableDebtTokenAddress;
reserve.variableDebtTokenAddress = variableDebtTokenAddress;
2020-08-21 13:13:08 +00:00
reserve.interestRateStrategyAddress = interestRateStrategyAddress;
2020-07-08 22:16:05 +00:00
}
2020-08-22 11:01:41 +00:00
struct UpdateInterestRatesLocalVars {
address stableDebtTokenAddress;
2020-09-14 17:25:45 +00:00
uint256 availableLiquidity;
2020-09-17 08:53:55 +00:00
uint256 totalStableDebt;
2020-08-22 11:01:41 +00:00
uint256 newLiquidityRate;
uint256 newStableRate;
uint256 newVariableRate;
2020-09-14 17:25:45 +00:00
uint256 avgStableRate;
2020-10-15 12:13:46 +00:00
uint256 totalVariableDebt;
2020-08-22 11:01:41 +00:00
}
2020-09-09 19:26:52 +00:00
2020-07-08 22:16:05 +00:00
/**
2020-11-25 17:33:49 +00:00
* @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate
* @param reserve The address of the reserve to be updated
* @param liquidityAdded The amount of liquidity added to the protocol (deposit or repay) in the previous action
* @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow)
2020-07-08 22:16:05 +00:00
**/
function updateInterestRates(
DataTypes.ReserveData storage reserve,
2020-08-21 13:13:08 +00:00
address reserveAddress,
2020-08-25 10:37:38 +00:00
address aTokenAddress,
uint256 liquidityAdded,
uint256 liquidityTaken
2020-10-15 12:13:46 +00:00
) internal {
2020-08-22 11:01:41 +00:00
UpdateInterestRatesLocalVars memory vars;
2020-07-08 22:16:05 +00:00
2020-08-22 11:01:41 +00:00
vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress;
2020-09-14 17:25:45 +00:00
(vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress)
.getTotalSupplyAndAvgRate();
2020-10-15 12:13:46 +00:00
//calculates the total variable debt locally using the scaled total supply instead
//of totalSupply(), as it's noticeably cheaper. Also, the index has been
//updated by the previous updateState() call
vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress)
.scaledTotalSupply()
.rayMul(reserve.variableBorrowIndex);
2020-07-08 22:16:05 +00:00
(
2020-08-22 11:01:41 +00:00
vars.newLiquidityRate,
vars.newStableRate,
vars.newVariableRate
2020-08-21 13:13:08 +00:00
) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates(
reserveAddress,
aTokenAddress,
liquidityAdded,
liquidityTaken,
2020-09-14 17:25:45 +00:00
vars.totalStableDebt,
2020-10-15 12:13:46 +00:00
vars.totalVariableDebt,
2020-09-14 17:25:45 +00:00
vars.avgStableRate,
2020-09-10 10:51:52 +00:00
reserve.configuration.getReserveFactor()
2020-07-08 22:16:05 +00:00
);
require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW);
require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW);
require(vars.newVariableRate <= type(uint128).max, Errors.RL_VARIABLE_BORROW_RATE_OVERFLOW);
2020-07-08 22:16:05 +00:00
reserve.currentLiquidityRate = uint128(vars.newLiquidityRate);
reserve.currentStableBorrowRate = uint128(vars.newStableRate);
reserve.currentVariableBorrowRate = uint128(vars.newVariableRate);
2020-07-08 22:16:05 +00:00
emit ReserveDataUpdated(
2020-08-21 13:13:08 +00:00
reserveAddress,
2020-08-22 11:01:41 +00:00
vars.newLiquidityRate,
vars.newStableRate,
vars.newVariableRate,
2020-09-12 11:18:17 +00:00
reserve.liquidityIndex,
reserve.variableBorrowIndex
2020-07-08 22:16:05 +00:00
);
}
2020-09-14 13:09:16 +00:00
struct MintToTreasuryLocalVars {
2020-09-17 08:53:55 +00:00
uint256 currentStableDebt;
2020-09-14 13:09:16 +00:00
uint256 principalStableDebt;
2020-09-17 08:53:55 +00:00
uint256 previousStableDebt;
uint256 currentVariableDebt;
uint256 previousVariableDebt;
uint256 avgStableRate;
2020-09-14 13:09:16 +00:00
uint256 cumulatedStableInterest;
2020-09-17 08:53:55 +00:00
uint256 totalDebtAccrued;
2020-09-14 13:09:16 +00:00
uint256 amountToMint;
uint256 reserveFactor;
2020-09-21 16:51:51 +00:00
uint40 stableSupplyUpdatedTimestamp;
2020-09-14 13:09:16 +00:00
}
2020-09-14 09:41:14 +00:00
2020-09-21 18:19:28 +00:00
/**
2020-11-25 17:33:49 +00:00
* @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the
2020-09-21 16:51:51 +00:00
* specific asset.
2020-11-25 17:33:49 +00:00
* @param reserve The reserve reserve to be updated
* @param scaledVariableDebt The current scaled total variable debt
* @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest
* @param newLiquidityIndex The new liquidity index
* @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest
2020-09-21 16:51:51 +00:00
**/
2020-09-14 13:09:16 +00:00
function _mintToTreasury(
DataTypes.ReserveData storage reserve,
2020-10-15 12:13:46 +00:00
uint256 scaledVariableDebt,
2020-09-17 08:53:55 +00:00
uint256 previousVariableBorrowIndex,
uint256 newLiquidityIndex,
2020-10-27 16:22:51 +00:00
uint256 newVariableBorrowIndex,
uint40 timestamp
2020-09-14 13:09:16 +00:00
) internal {
MintToTreasuryLocalVars memory vars;
2020-09-14 09:41:14 +00:00
2020-09-14 13:09:16 +00:00
vars.reserveFactor = reserve.configuration.getReserveFactor();
2020-09-17 08:53:55 +00:00
if (vars.reserveFactor == 0) {
2020-09-14 13:09:16 +00:00
return;
}
2020-09-17 08:53:55 +00:00
//fetching the principal, total stable debt and the avg stable rate
2020-09-21 16:51:51 +00:00
(
vars.principalStableDebt,
vars.currentStableDebt,
vars.avgStableRate,
vars.stableSupplyUpdatedTimestamp
) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData();
2020-09-14 13:09:16 +00:00
2020-09-17 08:53:55 +00:00
//calculate the last principal variable debt
2020-10-15 12:13:46 +00:00
vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex);
2020-09-14 13:09:16 +00:00
2020-09-17 08:53:55 +00:00
//calculate the new total supply after accumulation of the index
2020-10-15 12:13:46 +00:00
vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex);
2020-09-14 13:09:16 +00:00
2020-09-17 08:53:55 +00:00
//calculate the stable debt until the last timestamp update
2020-09-14 13:09:16 +00:00
vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest(
vars.avgStableRate,
2020-10-27 16:22:51 +00:00
vars.stableSupplyUpdatedTimestamp,
timestamp
2020-09-14 13:09:16 +00:00
);
2020-09-17 08:53:55 +00:00
vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest);
2020-09-14 13:09:16 +00:00
2020-09-17 08:53:55 +00:00
//debt accrued is the sum of the current debt minus the sum of the debt at the last update
vars.totalDebtAccrued = vars
.currentVariableDebt
.add(vars.currentStableDebt)
.sub(vars.previousVariableDebt)
.sub(vars.previousStableDebt);
2020-09-14 13:09:16 +00:00
2020-09-17 08:53:55 +00:00
vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor);
if (vars.amountToMint != 0) {
IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex);
}
2020-07-08 22:16:05 +00:00
}
2020-09-21 18:19:28 +00:00
/**
2020-11-25 17:33:49 +00:00
* @dev Updates the reserve indexes and the timestamp of the update
* @param reserve The reserve reserve to be updated
* @param scaledVariableDebt The scaled variable debt
* @param liquidityIndex The last stored liquidity index
* @param variableBorrowIndex The last stored variable borrow index
2020-09-21 16:51:51 +00:00
**/
2020-09-14 13:09:16 +00:00
function _updateIndexes(
DataTypes.ReserveData storage reserve,
2020-10-15 12:13:46 +00:00
uint256 scaledVariableDebt,
2020-09-14 13:09:16 +00:00
uint256 liquidityIndex,
2020-10-27 16:22:51 +00:00
uint256 variableBorrowIndex,
uint40 timestamp
2020-09-17 08:53:55 +00:00
) internal returns (uint256, uint256) {
uint256 currentLiquidityRate = reserve.currentLiquidityRate;
2020-09-17 08:53:55 +00:00
uint256 newLiquidityIndex = liquidityIndex;
uint256 newVariableBorrowIndex = variableBorrowIndex;
//only cumulating if there is any income being produced
if (currentLiquidityRate > 0) {
uint256 cumulatedLiquidityInterest =
MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp);
2020-09-17 08:53:55 +00:00
newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex);
require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
2020-09-17 08:53:55 +00:00
reserve.liquidityIndex = uint128(newLiquidityIndex);
//as the liquidity rate might come only from stable rate loans, we need to ensure
//that there is actual variable debt before accumulating
2020-10-15 12:13:46 +00:00
if (scaledVariableDebt != 0) {
uint256 cumulatedVariableBorrowInterest =
MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp);
2020-09-17 08:53:55 +00:00
newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex);
require(
newVariableBorrowIndex <= type(uint128).max,
Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW
);
2020-09-17 08:53:55 +00:00
reserve.variableBorrowIndex = uint128(newVariableBorrowIndex);
}
}
//solium-disable-next-line
reserve.lastUpdateTimestamp = uint40(block.timestamp);
2020-09-17 08:53:55 +00:00
return (newLiquidityIndex, newVariableBorrowIndex);
}
2020-06-20 23:40:03 +00:00
}