mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
441 lines
16 KiB
Solidity
441 lines
16 KiB
Solidity
// SPDX-License-Identifier: agpl-3.0
|
|
pragma solidity ^0.6.8;
|
|
|
|
import "@openzeppelin/contracts/math/SafeMath.sol";
|
|
import "./WadRayMath.sol";
|
|
|
|
/**
|
|
* @title CoreLibrary library
|
|
* @author Aave
|
|
* @notice Defines the data structures of the reserves and the user data
|
|
**/
|
|
library CoreLibrary {
|
|
using SafeMath for uint256;
|
|
using WadRayMath for uint256;
|
|
|
|
enum InterestRateMode {NONE, STABLE, VARIABLE}
|
|
|
|
uint256 internal constant SECONDS_PER_YEAR = 365 days;
|
|
|
|
struct UserReserveData {
|
|
//principal amount borrowed by the user.
|
|
uint256 principalBorrowBalance;
|
|
//cumulated variable borrow index for the user. Expressed in ray
|
|
uint256 lastVariableBorrowCumulativeIndex;
|
|
//origination fee cumulated by the user
|
|
uint256 originationFee;
|
|
// stable borrow rate at which the user has borrowed. Expressed in ray
|
|
uint256 stableBorrowRate;
|
|
uint40 lastUpdateTimestamp;
|
|
//defines if a specific deposit should or not be used as a collateral in borrows
|
|
bool useAsCollateral;
|
|
}
|
|
|
|
struct ReserveData {
|
|
/**
|
|
* @dev refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
|
|
**/
|
|
//the liquidity index. Expressed in ray
|
|
uint256 lastLiquidityCumulativeIndex;
|
|
//the current supply rate. Expressed in ray
|
|
uint256 currentLiquidityRate;
|
|
//the total borrows of the reserve at a stable rate. Expressed in the currency decimals
|
|
uint256 totalBorrowsStable;
|
|
//the total borrows of the reserve at a variable rate. Expressed in the currency decimals
|
|
uint256 totalBorrowsVariable;
|
|
//the current variable borrow rate. Expressed in ray
|
|
uint256 currentVariableBorrowRate;
|
|
//the current stable borrow rate. Expressed in ray
|
|
uint256 currentStableBorrowRate;
|
|
//the current average stable borrow rate (weighted average of all the different stable rate loans). Expressed in ray
|
|
uint256 currentAverageStableBorrowRate;
|
|
//variable borrow index. Expressed in ray
|
|
uint256 lastVariableBorrowCumulativeIndex;
|
|
//the ltv of the reserve. Expressed in percentage (0-100)
|
|
uint256 baseLTVasCollateral;
|
|
//the liquidation threshold of the reserve. Expressed in percentage (0-100)
|
|
uint256 liquidationThreshold;
|
|
//the liquidation bonus of the reserve. Expressed in percentage
|
|
uint256 liquidationBonus;
|
|
//the decimals of the reserve asset
|
|
uint256 decimals;
|
|
/**
|
|
* @dev address of the aToken representing the asset
|
|
**/
|
|
address aTokenAddress;
|
|
/**
|
|
* @dev address of the interest rate strategy contract
|
|
**/
|
|
address interestRateStrategyAddress;
|
|
uint40 lastUpdateTimestamp;
|
|
// borrowingEnabled = true means users can borrow from this reserve
|
|
bool borrowingEnabled;
|
|
// usageAsCollateralEnabled = true means users can use this reserve as collateral
|
|
bool usageAsCollateralEnabled;
|
|
// isStableBorrowRateEnabled = true means users can borrow at a stable rate
|
|
bool isStableBorrowRateEnabled;
|
|
// isActive = true means the reserve has been activated and properly configured
|
|
bool isActive;
|
|
// isFreezed = true means the reserve only allows repays and redeems, but not deposits, new borrowings or rate swap
|
|
bool isFreezed;
|
|
}
|
|
|
|
/**
|
|
* @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 that the income of the reserve is double the initial amount.
|
|
* @param _reserve the reserve object
|
|
* @return the normalized income. expressed in ray
|
|
**/
|
|
function getNormalizedIncome(CoreLibrary.ReserveData storage _reserve)
|
|
internal
|
|
view
|
|
returns (uint256)
|
|
{
|
|
uint256 cumulated = calculateLinearInterest(
|
|
_reserve
|
|
.currentLiquidityRate,
|
|
_reserve
|
|
.lastUpdateTimestamp
|
|
)
|
|
.rayMul(_reserve.lastLiquidityCumulativeIndex);
|
|
|
|
return cumulated;
|
|
|
|
}
|
|
|
|
/**
|
|
* @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for
|
|
* a formal specification.
|
|
* @param _self the reserve object
|
|
**/
|
|
function updateCumulativeIndexes(ReserveData storage _self) internal {
|
|
uint256 totalBorrows = getTotalBorrows(_self);
|
|
|
|
if (totalBorrows > 0) {
|
|
//only cumulating if there is any income being produced
|
|
uint256 cumulatedLiquidityInterest = calculateLinearInterest(
|
|
_self.currentLiquidityRate,
|
|
_self.lastUpdateTimestamp
|
|
);
|
|
|
|
_self.lastLiquidityCumulativeIndex = cumulatedLiquidityInterest.rayMul(
|
|
_self.lastLiquidityCumulativeIndex
|
|
);
|
|
|
|
uint256 cumulatedVariableBorrowInterest = calculateCompoundedInterest(
|
|
_self.currentVariableBorrowRate,
|
|
_self.lastUpdateTimestamp
|
|
);
|
|
_self.lastVariableBorrowCumulativeIndex = cumulatedVariableBorrowInterest.rayMul(
|
|
_self.lastVariableBorrowCumulativeIndex
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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.
|
|
* @param _self the reserve object
|
|
* @param _totalLiquidity the total liquidity available in the reserve
|
|
* @param _amount the amount to accomulate
|
|
**/
|
|
function cumulateToLiquidityIndex(
|
|
ReserveData storage _self,
|
|
uint256 _totalLiquidity,
|
|
uint256 _amount
|
|
) internal {
|
|
uint256 amountToLiquidityRatio = _amount.wadToRay().rayDiv(_totalLiquidity.wadToRay());
|
|
|
|
uint256 cumulatedLiquidity = amountToLiquidityRatio.add(WadRayMath.ray());
|
|
|
|
_self.lastLiquidityCumulativeIndex = cumulatedLiquidity.rayMul(
|
|
_self.lastLiquidityCumulativeIndex
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dev initializes a reserve
|
|
* @param _self the reserve object
|
|
* @param _aTokenAddress the address of the overlying atoken contract
|
|
* @param _decimals the number of decimals of the underlying asset
|
|
* @param _interestRateStrategyAddress the address of the interest rate strategy contract
|
|
**/
|
|
function init(
|
|
ReserveData storage _self,
|
|
address _aTokenAddress,
|
|
uint256 _decimals,
|
|
address _interestRateStrategyAddress
|
|
) external {
|
|
require(_self.aTokenAddress == address(0), "Reserve has already been initialized");
|
|
|
|
if (_self.lastLiquidityCumulativeIndex == 0) {
|
|
//if the reserve has not been initialized yet
|
|
_self.lastLiquidityCumulativeIndex = WadRayMath.ray();
|
|
}
|
|
|
|
if (_self.lastVariableBorrowCumulativeIndex == 0) {
|
|
_self.lastVariableBorrowCumulativeIndex = WadRayMath.ray();
|
|
}
|
|
|
|
_self.aTokenAddress = _aTokenAddress;
|
|
_self.decimals = _decimals;
|
|
|
|
_self.interestRateStrategyAddress = _interestRateStrategyAddress;
|
|
_self.isActive = true;
|
|
_self.isFreezed = false;
|
|
|
|
}
|
|
|
|
/**
|
|
* @dev enables borrowing on a reserve
|
|
* @param _self the reserve object
|
|
* @param _stableBorrowRateEnabled true if the stable borrow rate must be enabled by default, false otherwise
|
|
**/
|
|
function enableBorrowing(ReserveData storage _self, bool _stableBorrowRateEnabled) external {
|
|
require(_self.borrowingEnabled == false, "Reserve is already enabled");
|
|
|
|
_self.borrowingEnabled = true;
|
|
_self.isStableBorrowRateEnabled = _stableBorrowRateEnabled;
|
|
|
|
}
|
|
|
|
/**
|
|
* @dev disables borrowing on a reserve
|
|
* @param _self the reserve object
|
|
**/
|
|
function disableBorrowing(ReserveData storage _self) external {
|
|
_self.borrowingEnabled = false;
|
|
}
|
|
|
|
/**
|
|
* @dev enables a reserve to be used as collateral
|
|
* @param _self the reserve object
|
|
* @param _baseLTVasCollateral the loan to value of the asset when used as collateral
|
|
* @param _liquidationThreshold the threshold at which loans using this asset as collateral will be considered undercollateralized
|
|
* @param _liquidationBonus the bonus liquidators receive to liquidate this asset
|
|
**/
|
|
function enableAsCollateral(
|
|
ReserveData storage _self,
|
|
uint256 _baseLTVasCollateral,
|
|
uint256 _liquidationThreshold,
|
|
uint256 _liquidationBonus
|
|
) external {
|
|
require(
|
|
_self.usageAsCollateralEnabled == false,
|
|
"Reserve is already enabled as collateral"
|
|
);
|
|
|
|
_self.usageAsCollateralEnabled = true;
|
|
_self.baseLTVasCollateral = _baseLTVasCollateral;
|
|
_self.liquidationThreshold = _liquidationThreshold;
|
|
_self.liquidationBonus = _liquidationBonus;
|
|
|
|
if (_self.lastLiquidityCumulativeIndex == 0)
|
|
_self.lastLiquidityCumulativeIndex = WadRayMath.ray();
|
|
|
|
}
|
|
|
|
/**
|
|
* @dev disables a reserve as collateral
|
|
* @param _self the reserve object
|
|
**/
|
|
function disableAsCollateral(ReserveData storage _self) external {
|
|
_self.usageAsCollateralEnabled = false;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @dev calculates the compounded borrow balance of a user
|
|
* @param _self the userReserve object
|
|
* @param _reserve the reserve object
|
|
* @return the user compounded borrow balance
|
|
**/
|
|
function getCompoundedBorrowBalance(
|
|
CoreLibrary.UserReserveData storage _self,
|
|
CoreLibrary.ReserveData storage _reserve
|
|
) internal view returns (uint256) {
|
|
if (_self.principalBorrowBalance == 0) return 0;
|
|
|
|
uint256 principalBorrowBalanceRay = _self.principalBorrowBalance.wadToRay();
|
|
uint256 compoundedBalance = 0;
|
|
uint256 cumulatedInterest = 0;
|
|
|
|
if (_self.stableBorrowRate > 0) {
|
|
cumulatedInterest = calculateCompoundedInterest(
|
|
_self.stableBorrowRate,
|
|
_self.lastUpdateTimestamp
|
|
);
|
|
} else {
|
|
//variable interest
|
|
cumulatedInterest = calculateCompoundedInterest(
|
|
_reserve
|
|
.currentVariableBorrowRate,
|
|
_reserve
|
|
.lastUpdateTimestamp
|
|
)
|
|
.rayMul(_reserve.lastVariableBorrowCumulativeIndex)
|
|
.rayDiv(_self.lastVariableBorrowCumulativeIndex);
|
|
}
|
|
|
|
compoundedBalance = principalBorrowBalanceRay.rayMul(cumulatedInterest).rayToWad();
|
|
|
|
if (compoundedBalance == _self.principalBorrowBalance) {
|
|
//solium-disable-next-line
|
|
if (_self.lastUpdateTimestamp != block.timestamp) {
|
|
//no interest cumulation because of the rounding - we add 1 wei
|
|
//as symbolic cumulated interest to avoid interest free loans.
|
|
|
|
return _self.principalBorrowBalance.add(1 wei);
|
|
}
|
|
}
|
|
|
|
return compoundedBalance;
|
|
}
|
|
|
|
/**
|
|
* @dev increases the total borrows at a stable rate on a specific reserve and updates the
|
|
* average stable rate consequently
|
|
* @param _reserve the reserve object
|
|
* @param _amount the amount to add to the total borrows stable
|
|
* @param _rate the rate at which the amount has been borrowed
|
|
**/
|
|
function increaseTotalBorrowsStableAndUpdateAverageRate(
|
|
ReserveData storage _reserve,
|
|
uint256 _amount,
|
|
uint256 _rate
|
|
) internal {
|
|
uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable;
|
|
//updating reserve borrows stable
|
|
_reserve.totalBorrowsStable = _reserve.totalBorrowsStable.add(_amount);
|
|
|
|
//update the average stable rate
|
|
//weighted average of all the borrows
|
|
uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate);
|
|
uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul(
|
|
_reserve.currentAverageStableBorrowRate
|
|
);
|
|
|
|
_reserve.currentAverageStableBorrowRate = weightedLastBorrow
|
|
.add(weightedPreviousTotalBorrows)
|
|
.rayDiv(_reserve.totalBorrowsStable.wadToRay());
|
|
}
|
|
|
|
/**
|
|
* @dev decreases the total borrows at a stable rate on a specific reserve and updates the
|
|
* average stable rate consequently
|
|
* @param _reserve the reserve object
|
|
* @param _amount the amount to substract to the total borrows stable
|
|
* @param _rate the rate at which the amount has been repaid
|
|
**/
|
|
function decreaseTotalBorrowsStableAndUpdateAverageRate(
|
|
ReserveData storage _reserve,
|
|
uint256 _amount,
|
|
uint256 _rate
|
|
) internal {
|
|
require(_reserve.totalBorrowsStable >= _amount, "Invalid amount to decrease");
|
|
|
|
uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable;
|
|
|
|
//updating reserve borrows stable
|
|
_reserve.totalBorrowsStable = _reserve.totalBorrowsStable.sub(_amount);
|
|
|
|
if (_reserve.totalBorrowsStable == 0) {
|
|
_reserve.currentAverageStableBorrowRate = 0; //no income if there are no stable rate borrows
|
|
return;
|
|
}
|
|
|
|
//update the average stable rate
|
|
//weighted average of all the borrows
|
|
uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate);
|
|
uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul(
|
|
_reserve.currentAverageStableBorrowRate
|
|
);
|
|
|
|
require(
|
|
weightedPreviousTotalBorrows >= weightedLastBorrow,
|
|
"The amounts to subtract don't match"
|
|
);
|
|
|
|
_reserve.currentAverageStableBorrowRate = weightedPreviousTotalBorrows
|
|
.sub(weightedLastBorrow)
|
|
.rayDiv(_reserve.totalBorrowsStable.wadToRay());
|
|
}
|
|
|
|
/**
|
|
* @dev increases the total borrows at a variable rate
|
|
* @param _reserve the reserve object
|
|
* @param _amount the amount to add to the total borrows variable
|
|
**/
|
|
function increaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal {
|
|
_reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.add(_amount);
|
|
}
|
|
|
|
/**
|
|
* @dev decreases the total borrows at a variable rate
|
|
* @param _reserve the reserve object
|
|
* @param _amount the amount to substract to the total borrows variable
|
|
**/
|
|
function decreaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal {
|
|
require(
|
|
_reserve.totalBorrowsVariable >= _amount,
|
|
"The amount that is being subtracted from the variable total borrows is incorrect"
|
|
);
|
|
_reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.sub(_amount);
|
|
}
|
|
|
|
/**
|
|
* @dev function to calculate the interest using a linear interest rate formula
|
|
* @param _rate the interest rate, in ray
|
|
* @param _lastUpdateTimestamp the timestamp of the last update of the interest
|
|
* @return the interest rate linearly accumulated during the timeDelta, in ray
|
|
**/
|
|
|
|
function calculateLinearInterest(uint256 _rate, uint40 _lastUpdateTimestamp)
|
|
internal
|
|
view
|
|
returns (uint256)
|
|
{
|
|
//solium-disable-next-line
|
|
uint256 timeDifference = block.timestamp.sub(uint256(_lastUpdateTimestamp));
|
|
|
|
uint256 timeDelta = timeDifference.wadToRay().rayDiv(SECONDS_PER_YEAR.wadToRay());
|
|
|
|
return _rate.rayMul(timeDelta).add(WadRayMath.ray());
|
|
}
|
|
|
|
/**
|
|
* @dev function to calculate the interest using a compounded interest rate formula
|
|
* @param _rate the interest rate, in ray
|
|
* @param _lastUpdateTimestamp the timestamp of the last update of the interest
|
|
* @return the interest rate compounded during the timeDelta, in ray
|
|
**/
|
|
function calculateCompoundedInterest(uint256 _rate, uint40 _lastUpdateTimestamp)
|
|
internal
|
|
view
|
|
returns (uint256)
|
|
{
|
|
//solium-disable-next-line
|
|
uint256 timeDifference = block.timestamp.sub(uint256(_lastUpdateTimestamp));
|
|
|
|
uint256 ratePerSecond = _rate.div(SECONDS_PER_YEAR);
|
|
|
|
return ratePerSecond.add(WadRayMath.ray()).rayPow(timeDifference);
|
|
}
|
|
|
|
/**
|
|
* @dev returns the total borrows on the reserve
|
|
* @param _reserve the reserve object
|
|
* @return the total borrows (stable + variable)
|
|
**/
|
|
function getTotalBorrows(CoreLibrary.ReserveData storage _reserve)
|
|
internal
|
|
view
|
|
returns (uint256)
|
|
{
|
|
return _reserve.totalBorrowsStable.add(_reserve.totalBorrowsVariable);
|
|
}
|
|
|
|
}
|