2020-06-20 23:40:03 +00:00
|
|
|
// SPDX-License-Identifier: agpl-3.0
|
|
|
|
pragma solidity ^0.6.8;
|
|
|
|
|
2020-07-08 22:16:05 +00:00
|
|
|
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
|
|
|
|
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
2020-08-20 07:51:21 +00:00
|
|
|
import {MathUtils} from '../math/MathUtils.sol';
|
|
|
|
import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
|
2020-08-19 15:56:51 +00:00
|
|
|
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
|
2020-08-20 07:51:21 +00:00
|
|
|
import {IStableDebtToken} from '../../tokenization/interfaces/IStableDebtToken.sol';
|
|
|
|
import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
|
|
|
|
import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol';
|
|
|
|
import {WadRayMath} from '../math/WadRayMath.sol';
|
2020-09-04 08:27:32 +00:00
|
|
|
import {Errors} from '../helpers/Errors.sol';
|
2020-06-27 02:13:32 +00:00
|
|
|
|
|
|
|
/**
|
2020-07-08 22:16:05 +00:00
|
|
|
* @title ReserveLogic library
|
|
|
|
* @author Aave
|
|
|
|
* @notice Implements the logic to update the state of the reserves
|
|
|
|
*/
|
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-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
|
|
|
|
* @param reserve the address of the reserve
|
|
|
|
* @param liquidityRate the new liquidity rate
|
|
|
|
* @param stableBorrowRate the new stable borrow rate
|
|
|
|
* @param averageStableBorrowRate the new average stable borrow rate
|
|
|
|
* @param variableBorrowRate the new variable borrow rate
|
|
|
|
* @param liquidityIndex the new liquidity index
|
|
|
|
* @param variableBorrowIndex the new variable borrow index
|
|
|
|
**/
|
|
|
|
event ReserveDataUpdated(
|
|
|
|
address indexed reserve,
|
|
|
|
uint256 liquidityRate,
|
|
|
|
uint256 stableBorrowRate,
|
|
|
|
uint256 averageStableBorrowRate,
|
|
|
|
uint256 variableBorrowRate,
|
|
|
|
uint256 liquidityIndex,
|
|
|
|
uint256 variableBorrowIndex
|
|
|
|
);
|
|
|
|
|
2020-07-08 22:16:05 +00:00
|
|
|
using ReserveLogic for ReserveLogic.ReserveData;
|
2020-07-23 15:18:06 +00:00
|
|
|
using ReserveConfiguration for ReserveConfiguration.Map;
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-07-08 22:16:05 +00:00
|
|
|
enum InterestRateMode {NONE, STABLE, VARIABLE}
|
2020-06-20 23:40:03 +00:00
|
|
|
|
2020-08-19 14:36:58 +00:00
|
|
|
// refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
|
2020-07-08 22:16:05 +00:00
|
|
|
struct ReserveData {
|
2020-08-19 14:36:58 +00:00
|
|
|
//stores the reserve configuration
|
2020-07-23 15:18:06 +00:00
|
|
|
ReserveConfiguration.Map configuration;
|
2020-08-23 09:13:43 +00:00
|
|
|
//the liquidity index. Expressed in ray
|
2020-09-12 11:18:17 +00:00
|
|
|
uint128 liquidityIndex;
|
2020-09-13 09:58:45 +00:00
|
|
|
//variable borrow index. Expressed in ray
|
|
|
|
uint128 variableBorrowIndex;
|
2020-08-23 09:13:43 +00:00
|
|
|
//the current supply rate. Expressed in ray
|
|
|
|
uint128 currentLiquidityRate;
|
|
|
|
//the current variable borrow rate. Expressed in ray
|
|
|
|
uint128 currentVariableBorrowRate;
|
|
|
|
//the current stable borrow rate. Expressed in ray
|
|
|
|
uint128 currentStableBorrowRate;
|
2020-07-08 22:16:05 +00:00
|
|
|
uint40 lastUpdateTimestamp;
|
2020-08-23 09:13:43 +00:00
|
|
|
|
2020-09-13 09:58:45 +00:00
|
|
|
//tokens addresses
|
|
|
|
address aTokenAddress;
|
|
|
|
address stableDebtTokenAddress;
|
|
|
|
address variableDebtTokenAddress;
|
2020-09-14 08:43:30 +00:00
|
|
|
//address of the interest rate strategy
|
2020-09-13 09:58:45 +00:00
|
|
|
address interestRateStrategyAddress;
|
|
|
|
|
|
|
|
//the id of the reserve. Represents the position in the list of the active reserves
|
|
|
|
uint8 id;
|
2020-07-08 22:16:05 +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.
|
2020-08-19 14:36:58 +00:00
|
|
|
* A value of 2*1e27 means for each unit of assset two units of income have been accrued.
|
2020-08-21 13:13:08 +00:00
|
|
|
* @param reserve the reserve object
|
2020-07-08 22:16:05 +00:00
|
|
|
* @return the normalized income. expressed in ray
|
|
|
|
**/
|
2020-08-21 13:13:08 +00:00
|
|
|
function getNormalizedIncome(ReserveData storage reserve) internal view returns (uint256) {
|
|
|
|
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;
|
2020-08-09 22:11:06 +00:00
|
|
|
}
|
|
|
|
|
2020-07-08 22:16:05 +00:00
|
|
|
uint256 cumulated = MathUtils
|
2020-08-21 13:13:08 +00:00
|
|
|
.calculateLinearInterest(reserve.currentLiquidityRate, timestamp)
|
2020-09-12 11:18:17 +00:00
|
|
|
.rayMul(reserve.liquidityIndex);
|
2020-07-08 22:16:05 +00:00
|
|
|
|
|
|
|
return cumulated;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 the debt of the reserve is double the initial amount.
|
2020-08-21 13:13:08 +00:00
|
|
|
* @param reserve the reserve object
|
2020-07-08 22:16:05 +00:00
|
|
|
* @return the normalized variable debt. expressed in ray
|
|
|
|
**/
|
2020-08-21 13:13:08 +00:00
|
|
|
function getNormalizedDebt(ReserveData storage reserve) internal view returns (uint256) {
|
|
|
|
uint40 timestamp = reserve.lastUpdateTimestamp;
|
2020-08-09 22:11:06 +00:00
|
|
|
|
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-13 09:58:45 +00:00
|
|
|
return reserve.variableBorrowIndex;
|
2020-08-09 22:11:06 +00:00
|
|
|
}
|
|
|
|
|
2020-07-08 22:16:05 +00:00
|
|
|
uint256 cumulated = MathUtils
|
2020-08-21 13:13:08 +00:00
|
|
|
.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp)
|
2020-09-13 09:58:45 +00:00
|
|
|
.rayMul(reserve.variableBorrowIndex);
|
2020-07-08 22:16:05 +00:00
|
|
|
|
|
|
|
return cumulated;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-09-14 08:43:30 +00:00
|
|
|
* @dev Updates the state of the reserve by minting to the reserve treasury and calculate the new
|
|
|
|
* reserve indexes
|
2020-08-21 13:13:08 +00:00
|
|
|
* @param reserve the reserve object
|
2020-07-08 22:16:05 +00:00
|
|
|
**/
|
2020-09-14 07:53:21 +00:00
|
|
|
function updateState(ReserveData storage reserve) internal {
|
2020-09-14 08:43:30 +00:00
|
|
|
_mintToTreasury(reserve);
|
|
|
|
_updateIndexes(reserve);
|
2020-07-08 22:16:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev accumulates a predefined amount of asset to the reserve as a fixed, one time income. Used for example to accumulate
|
|
|
|
* the flashloan fee to the reserve, and spread it through the depositors.
|
2020-08-21 13:13:08 +00:00
|
|
|
* @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(
|
2020-08-21 13:13:08 +00:00
|
|
|
ReserveData storage reserve,
|
|
|
|
uint256 totalLiquidity,
|
|
|
|
uint256 amount
|
2020-07-08 22:16:05 +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
|
|
|
|
2020-08-23 09:13:43 +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);
|
2020-09-04 10:48:29 +00:00
|
|
|
require(result < (1 << 128), Errors.LIQUIDITY_INDEX_OVERFLOW);
|
2020-08-23 09:13:43 +00:00
|
|
|
|
2020-09-12 11:18:17 +00:00
|
|
|
reserve.liquidityIndex = uint128(result);
|
2020-07-08 22:16:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev initializes a reserve
|
2020-08-21 13:13:08 +00:00
|
|
|
* @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(
|
2020-08-21 13:13:08 +00:00
|
|
|
ReserveData storage reserve,
|
|
|
|
address aTokenAddress,
|
2020-08-21 16:18:12 +00:00
|
|
|
address stableDebtTokenAddress,
|
|
|
|
address variableDebtTokenAddress,
|
2020-08-21 13:13:08 +00:00
|
|
|
address interestRateStrategyAddress
|
2020-07-08 22:16:05 +00:00
|
|
|
) external {
|
2020-09-04 08:27:32 +00:00
|
|
|
require(reserve.aTokenAddress == address(0), Errors.RESERVE_ALREADY_INITIALIZED);
|
2020-09-12 11:18:17 +00:00
|
|
|
if (reserve.liquidityIndex == 0) {
|
2020-07-08 22:16:05 +00:00
|
|
|
//if the reserve has not been initialized yet
|
2020-09-12 11:18:17 +00:00
|
|
|
reserve.liquidityIndex = uint128(WadRayMath.ray());
|
2020-06-20 23:40:03 +00:00
|
|
|
}
|
|
|
|
|
2020-09-13 09:58:45 +00:00
|
|
|
if (reserve.variableBorrowIndex == 0) {
|
|
|
|
reserve.variableBorrowIndex = uint128(WadRayMath.ray());
|
2020-06-20 23:40:03 +00:00
|
|
|
}
|
|
|
|
|
2020-08-21 13:13:08 +00:00
|
|
|
reserve.aTokenAddress = aTokenAddress;
|
2020-08-21 16:18:12 +00:00
|
|
|
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 {
|
|
|
|
uint256 currentAvgStableRate;
|
|
|
|
uint256 availableLiquidity;
|
|
|
|
address stableDebtTokenAddress;
|
|
|
|
uint256 newLiquidityRate;
|
|
|
|
uint256 newStableRate;
|
|
|
|
uint256 newVariableRate;
|
|
|
|
}
|
2020-09-09 19:26:52 +00:00
|
|
|
|
2020-07-08 22:16:05 +00:00
|
|
|
/**
|
|
|
|
* @dev Updates the reserve current stable borrow rate Rf, the current variable borrow rate Rv and the current liquidity rate Rl.
|
|
|
|
* Also updates the lastUpdateTimestamp value. Please refer to the whitepaper for further information.
|
2020-08-21 13:13:08 +00:00
|
|
|
* @param reserve the address of the reserve to be updated
|
2020-08-21 16:18:12 +00:00
|
|
|
* @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(
|
2020-08-21 13:13:08 +00:00
|
|
|
ReserveData storage reserve,
|
|
|
|
address reserveAddress,
|
2020-09-04 08:27:32 +00:00
|
|
|
address aTokenAddress,
|
2020-08-21 16:18:12 +00:00
|
|
|
uint256 liquidityAdded,
|
|
|
|
uint256 liquidityTaken
|
2020-07-08 22:16:05 +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-09 19:26:52 +00:00
|
|
|
vars.currentAvgStableRate = IStableDebtToken(vars.stableDebtTokenAddress)
|
2020-07-08 22:16:05 +00:00
|
|
|
.getAverageStableRate();
|
2020-09-04 08:27:32 +00:00
|
|
|
vars.availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress);
|
2020-06-20 23:40:03 +00:00
|
|
|
|
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,
|
2020-08-22 11:01:41 +00:00
|
|
|
vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken),
|
|
|
|
IERC20(vars.stableDebtTokenAddress).totalSupply(),
|
2020-08-21 13:13:08 +00:00
|
|
|
IERC20(reserve.variableDebtTokenAddress).totalSupply(),
|
2020-09-10 10:51:52 +00:00
|
|
|
vars.currentAvgStableRate,
|
|
|
|
reserve.configuration.getReserveFactor()
|
2020-07-08 22:16:05 +00:00
|
|
|
);
|
2020-09-09 19:26:52 +00:00
|
|
|
require(vars.newLiquidityRate < (1 << 128), 'ReserveLogic: Liquidity rate overflow');
|
|
|
|
require(vars.newStableRate < (1 << 128), 'ReserveLogic: Stable borrow rate overflow');
|
|
|
|
require(vars.newVariableRate < (1 << 128), 'ReserveLogic: Variable borrow rate overflow');
|
2020-07-08 22:16:05 +00:00
|
|
|
|
2020-08-23 09:13:43 +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.currentAvgStableRate,
|
|
|
|
vars.newVariableRate,
|
2020-09-12 11:18:17 +00:00
|
|
|
reserve.liquidityIndex,
|
2020-09-13 09:58:45 +00:00
|
|
|
reserve.variableBorrowIndex
|
2020-07-08 22:16:05 +00:00
|
|
|
);
|
|
|
|
}
|
2020-09-14 08:43:30 +00:00
|
|
|
|
|
|
|
function _updateIndexes(ReserveData storage reserve) internal {
|
|
|
|
uint256 currentLiquidityRate = reserve.currentLiquidityRate;
|
|
|
|
|
|
|
|
//only cumulating if there is any income being produced
|
|
|
|
if (currentLiquidityRate > 0) {
|
|
|
|
uint40 lastUpdateTimestamp = reserve.lastUpdateTimestamp;
|
|
|
|
uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(
|
|
|
|
currentLiquidityRate,
|
|
|
|
lastUpdateTimestamp
|
|
|
|
);
|
|
|
|
uint256 index = cumulatedLiquidityInterest.rayMul(reserve.liquidityIndex);
|
|
|
|
require(index < (1 << 128), Errors.LIQUIDITY_INDEX_OVERFLOW);
|
|
|
|
|
|
|
|
reserve.liquidityIndex = uint128(index);
|
|
|
|
|
|
|
|
//as the liquidity rate might come only from stable rate loans, we need to ensure
|
|
|
|
//that there is actual variable debt before accumulating
|
|
|
|
if (IERC20(reserve.variableDebtTokenAddress).totalSupply() > 0) {
|
|
|
|
uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest(
|
|
|
|
reserve.currentVariableBorrowRate,
|
|
|
|
lastUpdateTimestamp
|
|
|
|
);
|
|
|
|
index = cumulatedVariableBorrowInterest.rayMul(reserve.variableBorrowIndex);
|
|
|
|
require(index < (1 << 128), Errors.VARIABLE_BORROW_INDEX_OVERFLOW);
|
|
|
|
reserve.variableBorrowIndex = uint128(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//solium-disable-next-line
|
|
|
|
reserve.lastUpdateTimestamp = uint40(block.timestamp);
|
|
|
|
}
|
2020-06-20 23:40:03 +00:00
|
|
|
}
|