Fix conflicts

This commit is contained in:
David Racero 2020-10-27 12:12:05 +01:00
commit 1ef22f036b
16 changed files with 250 additions and 198 deletions

View File

@ -36,13 +36,15 @@ contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesP
* @return the list of addressesProviders * @return the list of addressesProviders
**/ **/
function getAddressesProvidersList() external override view returns (address[] memory) { function getAddressesProvidersList() external override view returns (address[] memory) {
uint256 maxLength = _addressesProvidersList.length; address[] memory addressesProvidersList = _addressesProvidersList;
uint256 maxLength = addressesProvidersList.length;
address[] memory activeProviders = new address[](maxLength); address[] memory activeProviders = new address[](maxLength);
for (uint256 i = 0; i < _addressesProvidersList.length; i++) { for (uint256 i = 0; i < maxLength; i++) {
if (_addressesProviders[_addressesProvidersList[i]] > 0) { if (_addressesProviders[addressesProvidersList[i]] > 0) {
activeProviders[i] = _addressesProvidersList[i]; activeProviders[i] = addressesProvidersList[i];
} }
} }
@ -76,7 +78,9 @@ contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesP
* @param provider the pool address to be added * @param provider the pool address to be added
**/ **/
function _addToAddressesProvidersList(address provider) internal { function _addToAddressesProvidersList(address provider) internal {
for (uint256 i = 0; i < _addressesProvidersList.length; i++) { uint256 providersCount = _addressesProvidersList.length;
for (uint256 i = 0; i < providersCount; i++) {
if (_addressesProvidersList[i] == provider) { if (_addressesProvidersList[i] == provider) {
return; return;
} }

View File

@ -9,9 +9,9 @@ pragma solidity ^0.6.8;
**/ **/
interface IFlashLoanReceiver { interface IFlashLoanReceiver {
function executeOperation( function executeOperation(
address reserve, address[] calldata assets,
uint256 amount, uint256[] calldata amounts,
uint256 fee, uint256[] calldata premiums,
bytes calldata params bytes calldata params
) external returns (bool); ) external returns (bool);
} }

View File

@ -99,27 +99,35 @@ interface ILendingPool {
/** /**
* @dev emitted when a flashloan is executed * @dev emitted when a flashloan is executed
* @param target the address of the flashLoanReceiver * @param target the address of the flashLoanReceiver
* @param reserve the address of the reserve * @param assets the address of the assets being flashborrowed
* @param amount the amount requested * @param amounts the amount requested
* @param totalPremium the total fee on the amount * @param premiums the total fee on the amount
* @param referralCode the referral code of the caller * @param referralCode the referral code of the caller
**/ **/
event FlashLoan( event FlashLoan(
address indexed target, address indexed target,
address indexed reserve, uint256 mode,
uint256 amount, address[] assets,
uint256 totalPremium, uint256[] amounts,
uint256[] premiums,
uint16 referralCode uint16 referralCode
); );
/**
* @dev these events are not emitted directly by the LendingPool
* but they are declared here as the LendingPoolCollateralManager
* is executed using a delegateCall().
* This allows to have the events in the generated ABI for LendingPool.
**/
/** /**
* @dev emitted when a borrower is liquidated * @dev Emitted when the pause is triggered.
*/
event Paused();
/**
* @dev Emitted when the pause is lifted.
*/
event Unpaused();
/**
* @dev emitted when a borrower is liquidated. Thos evemt is emitted directly by the LendingPool
* but it's declared here as the LendingPoolCollateralManager
* is executed using a delegateCall().
* This allows to have the events in the generated ABI for LendingPool.
* @param collateral the address of the collateral being liquidated * @param collateral the address of the collateral being liquidated
* @param principal the address of the reserve * @param principal the address of the reserve
* @param user the address of the user being liquidated * @param user the address of the user being liquidated
@ -137,15 +145,28 @@ interface ILendingPool {
address liquidator, address liquidator,
bool receiveAToken bool receiveAToken
); );
/**
* @dev Emitted when the pause is triggered.
*/
event Paused();
/** /**
* @dev Emitted when the pause is lifted. * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
*/ * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
event Unpaused(); * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
* gets added to the LendingPool ABI
* @param reserve the address 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
**/
event ReserveDataUpdated(
address indexed reserve,
uint256 liquidityRate,
uint256 stableBorrowRate,
uint256 averageStableBorrowRate,
uint256 variableBorrowRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex
);
/** /**
* @dev deposits The underlying asset into the reserve. A corresponding amount of the overlying asset (aTokens) * @dev deposits The underlying asset into the reserve. A corresponding amount of the overlying asset (aTokens)
@ -264,16 +285,17 @@ interface ILendingPool {
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts * as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
* that must be kept into consideration. For further details please visit https://developers.aave.com * that must be kept into consideration. For further details please visit https://developers.aave.com
* @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface. * @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
* @param reserve the address of the principal reserve * @param assets the address of the principal reserve
* @param amount the amount requested for this flashloan * @param amounts the amount requested for this flashloan
* @param mode the flashloan mode
* @param params a bytes array to be sent to the flashloan executor * @param params a bytes array to be sent to the flashloan executor
* @param referralCode the referral code of the caller * @param referralCode the referral code of the caller
**/ **/
function flashLoan( function flashLoan(
address receiver, address receiver,
address reserve, address[] calldata assets,
uint256 amount, uint256[] calldata amounts,
uint256 debtType, uint256 mode,
bytes calldata params, bytes calldata params,
uint16 referralCode uint16 referralCode
) external; ) external;

View File

@ -157,7 +157,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex); IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex);
emit Withdraw(asset, msg.sender, amount); emit Withdraw(asset, msg.sender, amountToWithdraw);
} }
/** /**
@ -262,15 +262,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
ReserveLogic.InterestRateMode interestRateMode = ReserveLogic.InterestRateMode(rateMode); ReserveLogic.InterestRateMode interestRateMode = ReserveLogic.InterestRateMode(rateMode);
//default to max amount
uint256 paybackAmount = interestRateMode == ReserveLogic.InterestRateMode.STABLE
? stableDebt
: variableDebt;
if (amount < paybackAmount) {
paybackAmount = amount;
}
ValidationLogic.validateRepay( ValidationLogic.validateRepay(
reserve, reserve,
amount, amount,
@ -280,6 +271,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
variableDebt variableDebt
); );
//default to max amount
uint256 paybackAmount = interestRateMode == ReserveLogic.InterestRateMode.STABLE
? stableDebt
: variableDebt;
if (amount < paybackAmount) {
paybackAmount = amount;
}
reserve.updateState(); reserve.updateState();
//burns an equivalent amount of debt tokens //burns an equivalent amount of debt tokens
@ -356,9 +356,10 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
} }
/** /**
* @dev rebalances the stable interest rate of a user if current liquidity rate > user stable rate. * @dev rebalances the stable interest rate of a user. Users can be rebalanced if the following conditions are satisfied:
* this is regulated by Aave to ensure that the protocol is not abused, and the user is paying a fair * 1. Usage ratio is above 95%
* rate. Anyone can call this function. * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
* borrowed at a stable rate and depositors are not earning enough.
* @param asset the address of the reserve * @param asset the address of the reserve
* @param user the address of the user to be rebalanced * @param user the address of the user to be rebalanced
**/ **/
@ -373,7 +374,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
uint256 stableBorrowBalance = IERC20(stableDebtToken).balanceOf(user); uint256 stableBorrowBalance = IERC20(stableDebtToken).balanceOf(user);
//if the utilization rate is below 95%, no rebalances are needed //if the usage ratio is below 95%, no rebalances are needed
uint256 totalBorrows = stableDebtToken uint256 totalBorrows = stableDebtToken
.totalSupply() .totalSupply()
.add(variableDebtToken.totalSupply()) .add(variableDebtToken.totalSupply())
@ -417,7 +418,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
/** /**
* @dev allows depositors to enable or disable a specific deposit as collateral. * @dev allows depositors to enable or disable a specific deposit as collateral.
* @param asset the address of the reserve * @param asset the address of the reserve
* @param useAsCollateral true if the user wants to user the deposit as collateral, false otherwise. * @param useAsCollateral true if the user wants to use the deposit as collateral, false otherwise.
**/ **/
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override { function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override {
_whenNotPaused(); _whenNotPaused();
@ -483,11 +484,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
} }
struct FlashLoanLocalVars { struct FlashLoanLocalVars {
uint256 premium;
uint256 amountPlusPremium;
IFlashLoanReceiver receiver; IFlashLoanReceiver receiver;
address aTokenAddress;
address oracle; address oracle;
ReserveLogic.InterestRateMode debtMode;
uint256 i;
address currentAsset;
address currentATokenAddress;
uint256 currentAmount;
uint256 currentPremium;
uint256 currentAmountPlusPremium;
} }
/** /**
@ -495,68 +500,90 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts * as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
* that must be kept into consideration. For further details please visit https://developers.aave.com * that must be kept into consideration. For further details please visit https://developers.aave.com
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface. * @param receiverAddress The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
* @param asset The address of the principal reserve * @param assets The addresss of the assets being flashborrowed
* @param amount The amount requested for this flashloan * @param amounts The amounts requested for this flashloan for each asset
* @param mode Type of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable * @param mode Type of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
* @param params Variadic packed params to pass to the receiver as extra information * @param params Variadic packed params to pass to the receiver as extra information
* @param referralCode Referral code of the flash loan * @param referralCode Referral code of the flash loan
**/ **/
function flashLoan( function flashLoan(
address receiverAddress, address receiverAddress,
address asset, address[] calldata assets,
uint256 amount, uint256[] calldata amounts,
uint256 mode, uint256 mode,
bytes calldata params, bytes calldata params,
uint16 referralCode uint16 referralCode
) external override { ) external override {
_whenNotPaused(); _whenNotPaused();
ReserveLogic.ReserveData storage reserve = _reserves[asset];
FlashLoanLocalVars memory vars; FlashLoanLocalVars memory vars;
vars.aTokenAddress = reserve.aTokenAddress; ValidationLogic.validateFlashloan(assets, amounts, mode);
vars.premium = amount.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000); address[] memory aTokenAddresses = new address[](assets.length);
uint256[] memory premiums = new uint256[](assets.length);
ValidationLogic.validateFlashloan(mode, vars.premium);
ReserveLogic.InterestRateMode debtMode = ReserveLogic.InterestRateMode(mode);
vars.receiver = IFlashLoanReceiver(receiverAddress); vars.receiver = IFlashLoanReceiver(receiverAddress);
vars.debtMode = ReserveLogic.InterestRateMode(mode);
//transfer funds to the receiver for (vars.i = 0; vars.i < assets.length; vars.i++) {
IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount); aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
premiums[vars.i] = amounts[vars.i].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000);
//transfer funds to the receiver
IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
}
//execute action of the receiver //execute action of the receiver
require( require(
vars.receiver.executeOperation(asset, amount, vars.premium, params), vars.receiver.executeOperation(assets, amounts, premiums, params),
Errors.INVALID_FLASH_LOAN_EXECUTOR_RETURN Errors.INVALID_FLASH_LOAN_EXECUTOR_RETURN
); );
vars.amountPlusPremium = amount.add(vars.premium); for (vars.i = 0; vars.i < assets.length; vars.i++) {
vars.currentAsset = assets[vars.i];
vars.currentAmount = amounts[vars.i];
vars.currentPremium = premiums[vars.i];
vars.currentATokenAddress = aTokenAddresses[vars.i];
if (debtMode == ReserveLogic.InterestRateMode.NONE) { vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium);
IERC20(asset).safeTransferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium);
reserve.updateState(); if (vars.debtMode == ReserveLogic.InterestRateMode.NONE) {
reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium); _reserves[vars.currentAsset].updateState();
reserve.updateInterestRates(asset, vars.aTokenAddress, vars.premium, 0); _reserves[vars.currentAsset].cumulateToLiquidityIndex(
IERC20(vars.currentATokenAddress).totalSupply(),
vars.currentPremium
);
_reserves[vars.currentAsset].updateInterestRates(
vars.currentAsset,
vars.currentATokenAddress,
vars.currentPremium,
0
);
emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode); IERC20(vars.currentAsset).safeTransferFrom(
} else { receiverAddress,
//if the user didn't choose to return the funds, the system checks if there vars.currentATokenAddress,
//is enough collateral and eventually open a position vars.currentAmountPlusPremium
_executeBorrow( );
ExecuteBorrowParams( } else {
asset, //if the user didn't choose to return the funds, the system checks if there
msg.sender, //is enough collateral and eventually open a position
msg.sender, _executeBorrow(
vars.amountPlusPremium, ExecuteBorrowParams(
mode, vars.currentAsset,
vars.aTokenAddress, msg.sender,
referralCode, msg.sender,
false vars.currentAmount,
) mode,
); vars.currentATokenAddress,
referralCode,
false
)
);
}
emit FlashLoan(receiverAddress, mode, assets, amounts, premiums, referralCode);
} }
} }

View File

@ -194,13 +194,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
//update the principal reserve //update the principal reserve
principalReserve.updateState(); principalReserve.updateState();
principalReserve.updateInterestRates(
principal,
principalReserve.aTokenAddress,
vars.actualAmountToLiquidate,
0
);
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) { if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn( IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn(
user, user,
@ -223,6 +216,13 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
); );
} }
principalReserve.updateInterestRates(
principal,
principalReserve.aTokenAddress,
vars.actualAmountToLiquidate,
0
);
//if liquidator reclaims the aToken, he receives the equivalent atoken amount //if liquidator reclaims the aToken, he receives the equivalent atoken amount
if (receiveAToken) { if (receiveAToken) {
vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate); vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate);
@ -306,8 +306,8 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
.principalCurrencyPrice .principalCurrencyPrice
.mul(purchaseAmount) .mul(purchaseAmount)
.mul(10**vars.collateralDecimals) .mul(10**vars.collateralDecimals)
.div(vars.collateralPrice.mul(10**vars.principalDecimals)) .percentMul(vars.liquidationBonus)
.percentMul(vars.liquidationBonus); .div(vars.collateralPrice.mul(10**vars.principalDecimals));
if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) { if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) {
collateralAmount = userCollateralBalance; collateralAmount = userCollateralBalance;

View File

@ -45,6 +45,7 @@ library Errors {
string public constant INVALID_EQUAL_ASSETS_TO_SWAP = '56'; string public constant INVALID_EQUAL_ASSETS_TO_SWAP = '56';
string public constant NO_MORE_RESERVES_ALLOWED = '59'; string public constant NO_MORE_RESERVES_ALLOWED = '59';
string public constant INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60'; string public constant INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60';
string public constant INCONSISTENT_FLASHLOAN_PARAMS = '69';
// require error messages - aToken - DebtTokens // require error messages - aToken - DebtTokens
string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool' string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'

View File

@ -25,7 +25,6 @@ library GenericLogic {
using UserConfiguration for UserConfiguration.Map; using UserConfiguration for UserConfiguration.Map;
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether; uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether;
uint256 public constant HEALTH_FACTOR_CRITICAL_THRESHOLD = 0.98 ether;
struct balanceDecreaseAllowedLocalVars { struct balanceDecreaseAllowedLocalVars {
uint256 decimals; uint256 decimals;

View File

@ -327,11 +327,16 @@ library ValidationLogic {
/** /**
* @dev validates a flashloan action * @dev validates a flashloan action
* @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan) * @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan)
* @param premium the premium paid on the flashloan * @param assets the assets being flashborrowed
* @param amounts the amounts for each asset being borrowed
**/ **/
function validateFlashloan(uint256 mode, uint256 premium) internal pure { function validateFlashloan(
require(premium > 0, Errors.REQUESTED_AMOUNT_TOO_SMALL); address[] memory assets,
uint256[] memory amounts,
uint256 mode
) internal pure {
require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE); require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE);
require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
} }
/** /**

View File

@ -14,8 +14,8 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
ILendingPoolAddressesProvider internal _provider; ILendingPoolAddressesProvider internal _provider;
event ExecutedWithFail(address _reserve, uint256 _amount, uint256 _fee); event ExecutedWithFail(address[] _assets, uint256[] _amounts, uint256[] _premiums);
event ExecutedWithSuccess(address _reserve, uint256 _amount, uint256 _fee); event ExecutedWithSuccess(address[] _assets, uint256[] _amounts, uint256[] _premiums);
bool _failExecution; bool _failExecution;
uint256 _amountToApprove; uint256 _amountToApprove;
@ -44,33 +44,40 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
} }
function executeOperation( function executeOperation(
address reserve, address[] memory assets,
uint256 amount, uint256[] memory amounts,
uint256 fee, uint256[] memory premiums,
bytes memory params bytes memory params
) public override returns (bool) { ) public override returns (bool) {
params; params;
//mint to this contract the specific amount
MintableERC20 token = MintableERC20(reserve);
//check the contract has the specified balance
require(amount <= IERC20(reserve).balanceOf(address(this)), 'Invalid balance for the contract');
uint256 amountToReturn = (_amountToApprove != 0) ? _amountToApprove : amount.add(fee);
if (_failExecution) { if (_failExecution) {
emit ExecutedWithFail(reserve, amount, fee); emit ExecutedWithFail(assets, amounts, premiums);
return !_simulateEOA; return !_simulateEOA;
} }
//execution does not fail - mint tokens and return them to the _destination for (uint256 i = 0; i < assets.length; i++) {
//note: if the reserve is eth, the mock contract must receive at least _fee ETH before calling executeOperation //mint to this contract the specific amount
MintableERC20 token = MintableERC20(assets[i]);
token.mint(fee); //check the contract has the specified balance
require(
amounts[i] <= IERC20(assets[i]).balanceOf(address(this)),
'Invalid balance for the contract'
);
IERC20(reserve).approve(_addressesProvider.getLendingPool(), amountToReturn); uint256 amountToReturn = (_amountToApprove != 0)
? _amountToApprove
: amounts[i].add(premiums[i]);
//execution does not fail - mint tokens and return them to the _destination
//note: if the reserve is eth, the mock contract must receive at least _fee ETH before calling executeOperation
emit ExecutedWithSuccess(reserve, amount, fee); token.mint(premiums[i]);
IERC20(assets[i]).approve(_addressesProvider.getLendingPool(), amountToReturn);
}
emit ExecutedWithSuccess(assets, amounts, premiums);
return true; return true;
} }

View File

@ -128,7 +128,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
_totalSupplyTimestamp = _timestamps[user] = uint40(block.timestamp); _totalSupplyTimestamp = _timestamps[user] = uint40(block.timestamp);
//calculates the updated average stable rate //calculates the updated average stable rate
_avgStableRate = vars vars.currentAvgStableRate = _avgStableRate = vars
.currentAvgStableRate .currentAvgStableRate
.rayMul(vars.previousSupply.wadToRay()) .rayMul(vars.previousSupply.wadToRay())
.add(rate.rayMul(vars.amountInRay)) .add(rate.rayMul(vars.amountInRay))
@ -139,7 +139,15 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
// transfer event to track balances // transfer event to track balances
emit Transfer(address(0), user, amount); emit Transfer(address(0), user, amount);
emit Mint(user, amount, previousBalance, currentBalance, balanceIncrease, vars.newStableRate); emit Mint(
user,
amount,
previousBalance,
currentBalance,
balanceIncrease,
vars.newStableRate,
vars.currentAvgStableRate
);
} }
/** /**
@ -155,17 +163,18 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
) = _calculateBalanceIncrease(user); ) = _calculateBalanceIncrease(user);
uint256 previousSupply = totalSupply(); uint256 previousSupply = totalSupply();
uint256 newStableRate = 0;
//since the total supply and each single user debt accrue separately, //since the total supply and each single user debt accrue separately,
//there might be accumulation errors so that the last borrower repaying //there might be accumulation errors so that the last borrower repaying
//might actually try to repay more than the available debt supply. //might actually try to repay more than the available debt supply.
//in this case we simply set the total supply and the avg stable rate to 0 //in this case we simply set the total supply and the avg stable rate to 0
if (previousSupply <= amount) { if (previousSupply <= amount) {
_avgStableRate = 0; newStableRate = _avgStableRate = 0;
_totalSupply = 0; _totalSupply = 0;
} else { } else {
uint256 nextSupply = _totalSupply = previousSupply.sub(amount); uint256 nextSupply = _totalSupply = previousSupply.sub(amount);
_avgStableRate = _avgStableRate newStableRate = _avgStableRate = _avgStableRate
.rayMul(previousSupply.wadToRay()) .rayMul(previousSupply.wadToRay())
.sub(_usersData[user].rayMul(amount.wadToRay())) .sub(_usersData[user].rayMul(amount.wadToRay()))
.rayDiv(nextSupply.wadToRay()); .rayDiv(nextSupply.wadToRay());
@ -190,14 +199,13 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
// transfer event to track balances // transfer event to track balances
emit Transfer(user, address(0), amount); emit Transfer(user, address(0), amount);
emit Burn(user, amount, previousBalance, currentBalance, balanceIncrease); emit Burn(user, amount, previousBalance, currentBalance, balanceIncrease, newStableRate);
} }
/** /**
* @dev Calculates the increase in balance since the last user interaction * @dev Calculates the increase in balance since the last user interaction
* @param user The address of the user for which the interest is being accumulated * @param user The address of the user for which the interest is being accumulated
* @return The previous principal balance, the new principal balance, the balance increase * @return The previous principal balance, the new principal balance and the balance increase
* and the new user index
**/ **/
function _calculateBalanceIncrease(address user) function _calculateBalanceIncrease(address user)
internal internal

View File

@ -21,6 +21,7 @@ interface IStableDebtToken {
* @param currentBalance the current balance of the user * @param currentBalance the current balance of the user
* @param balanceIncrease the debt increase since the last update * @param balanceIncrease the debt increase since the last update
* @param newRate the rate of the debt after the minting * @param newRate the rate of the debt after the minting
* @param avgStableRate the new average stable rate after the minting
**/ **/
event Mint( event Mint(
address indexed user, address indexed user,
@ -28,7 +29,8 @@ interface IStableDebtToken {
uint256 previousBalance, uint256 previousBalance,
uint256 currentBalance, uint256 currentBalance,
uint256 balanceIncrease, uint256 balanceIncrease,
uint256 newRate uint256 newRate,
uint256 avgStableRate
); );
/** /**
@ -38,13 +40,15 @@ interface IStableDebtToken {
* @param previousBalance the previous balance of the user * @param previousBalance the previous balance of the user
* @param currentBalance the current balance of the user * @param currentBalance the current balance of the user
* @param balanceIncrease the debt increase since the last update * @param balanceIncrease the debt increase since the last update
* @param avgStableRate the new average stable rate after the minting
**/ **/
event Burn( event Burn(
address indexed user, address indexed user,
uint256 amount, uint256 amount,
uint256 previousBalance, uint256 previousBalance,
uint256 currentBalance, uint256 currentBalance,
uint256 balanceIncrease uint256 balanceIncrease,
uint256 avgStableRate
); );
/** /**

View File

@ -86,8 +86,7 @@
}, },
"LendingPool": { "LendingPool": {
"buidlerevm": { "buidlerevm": {
"address": "0x35c1419Da7cf0Ff885B8Ef8EA9242FEF6800c99b", "address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e"
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
}, },
"localhost": { "localhost": {
"address": "0x3EE716e38f21e5FC16BFDB773db24D63C637A5d8" "address": "0x3EE716e38f21e5FC16BFDB773db24D63C637A5d8"
@ -711,7 +710,7 @@
}, },
"ReserveLogic": { "ReserveLogic": {
"buidlerevm": { "buidlerevm": {
"address": "0x78Ee8Fb9fE5abD5e347Fc94c2fb85596d1f60e3c", "address": "0xFAe0fd738dAbc8a0426F47437322b6d026A9FD95",
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
}, },
"kovan": { "kovan": {
@ -721,7 +720,7 @@
}, },
"GenericLogic": { "GenericLogic": {
"buidlerevm": { "buidlerevm": {
"address": "0x920d847fE49E54C19047ba8bc236C45A8068Bca7", "address": "0x6082731fdAba4761277Fb31299ebC782AD3bCf24",
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
}, },
"kovan": { "kovan": {
@ -731,7 +730,7 @@
}, },
"ValidationLogic": { "ValidationLogic": {
"buidlerevm": { "buidlerevm": {
"address": "0xA4765Ff72A9F3CfE73089bb2c3a41B838DF71574", "address": "0x8456161947DFc1fC159A0B26c025cD2b4bba0c3e",
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
}, },
"kovan": { "kovan": {

View File

@ -3,8 +3,6 @@ import {
ATokenFactory, ATokenFactory,
ATokensAndRatesHelperFactory, ATokensAndRatesHelperFactory,
DefaultReserveInterestRateStrategyFactory, DefaultReserveInterestRateStrategyFactory,
DeployATokensAndRatesFactory,
DeployStableAndVariableTokensFactory,
GenericLogicFactory, GenericLogicFactory,
LendingPoolAddressesProviderFactory, LendingPoolAddressesProviderFactory,
LendingPoolAddressesProviderRegistryFactory, LendingPoolAddressesProviderRegistryFactory,

34
package-lock.json generated
View File

@ -15177,7 +15177,6 @@
"requires": { "requires": {
"anymatch": "~3.1.1", "anymatch": "~3.1.1",
"braces": "~3.0.2", "braces": "~3.0.2",
"fsevents": "~2.1.1",
"glob-parent": "~5.1.0", "glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
@ -15216,12 +15215,6 @@
"locate-path": "^3.0.0" "locate-path": "^3.0.0"
} }
}, },
"fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"optional": true
},
"get-caller-file": { "get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -17305,16 +17298,6 @@
"ajv-keywords": "^3.4.1" "ajv-keywords": "^3.4.1"
} }
}, },
"scrypt": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/scrypt/-/scrypt-6.0.3.tgz",
"integrity": "sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0=",
"dev": true,
"optional": true,
"requires": {
"nan": "^2.0.8"
}
},
"scrypt-js": { "scrypt-js": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz",
@ -19211,7 +19194,6 @@
"requires": { "requires": {
"anymatch": "~3.1.1", "anymatch": "~3.1.1",
"braces": "~3.0.2", "braces": "~3.0.2",
"fsevents": "~2.1.2",
"glob-parent": "~5.1.0", "glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
@ -19228,12 +19210,6 @@
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
} }
}, },
"fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"optional": true
},
"glob-parent": { "glob-parent": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
@ -23928,6 +23904,16 @@
} }
} }
}, },
"scrypt": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/scrypt/-/scrypt-6.0.3.tgz",
"integrity": "sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0=",
"dev": true,
"optional": true,
"requires": {
"nan": "^2.0.8"
}
},
"scrypt-js": { "scrypt-js": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz",

View File

@ -45,8 +45,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await pool.flashLoan( await pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, [weth.address],
ethers.utils.parseEther('0.8'), [ethers.utils.parseEther('0.8')],
0, 0,
'0x10', '0x10',
'0' '0'
@ -74,8 +74,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
const reserveDataBefore = await helpersContract.getReserveData(weth.address); const reserveDataBefore = await helpersContract.getReserveData(weth.address);
const txResult = await pool.flashLoan( const txResult = await pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, [weth.address],
'1000720000000000000', ['1000720000000000000'],
0, 0,
'0x10', '0x10',
'0' '0'
@ -105,8 +105,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
.connect(caller.signer) .connect(caller.signer)
.flashLoan( .flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, [weth.address],
ethers.utils.parseEther('0.8'), [ethers.utils.parseEther('0.8')],
0, 0,
'0x10', '0x10',
'0' '0'
@ -125,8 +125,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
.connect(caller.signer) .connect(caller.signer)
.flashLoan( .flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, [weth.address],
ethers.utils.parseEther('0.8'), [ethers.utils.parseEther('0.8')],
0, 0,
'0x10', '0x10',
'0' '0'
@ -145,8 +145,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
.connect(caller.signer) .connect(caller.signer)
.flashLoan( .flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, [weth.address],
ethers.utils.parseEther('0.8'), [ethers.utils.parseEther('0.8')],
4, 4,
'0x10', '0x10',
'0' '0'
@ -173,8 +173,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
.connect(caller.signer) .connect(caller.signer)
.flashLoan( .flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, [weth.address],
ethers.utils.parseEther('0.8'), [ethers.utils.parseEther('0.8')],
2, 2,
'0x10', '0x10',
'0' '0'
@ -190,22 +190,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
const callerDebt = await wethDebtToken.balanceOf(caller.address); const callerDebt = await wethDebtToken.balanceOf(caller.address);
expect(callerDebt.toString()).to.be.equal('800720000000000000', 'Invalid user debt'); expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
});
it('tries to take a very small flashloan, which would result in 0 fees (revert expected)', async () => {
const {pool, weth} = testEnv;
await expect(
pool.flashLoan(
_mockFlashLoanReceiver.address,
weth.address,
'1', //1 wei loan
2,
'0x10',
'0'
)
).to.be.revertedWith(REQUESTED_AMOUNT_TOO_SMALL);
}); });
it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => { it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
@ -214,8 +199,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await expect( await expect(
pool.flashLoan( pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
weth.address, [weth.address],
'1004415000000000000', //slightly higher than the available liquidity ['1004415000000000000'], //slightly higher than the available liquidity
2, 2,
'0x10', '0x10',
'0' '0'
@ -228,7 +213,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
const {pool, deployer, weth} = testEnv; const {pool, deployer, weth} = testEnv;
await expect( await expect(
pool.flashLoan(deployer.address, weth.address, '1000000000000000000', 2, '0x10', '0') pool.flashLoan(deployer.address, [weth.address], ['1000000000000000000'], 2, '0x10', '0')
).to.be.reverted; ).to.be.reverted;
}); });
@ -254,8 +239,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await pool.flashLoan( await pool.flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
usdc.address, [usdc.address],
flashloanAmount, [flashloanAmount],
0, 0,
'0x10', '0x10',
'0' '0'
@ -294,7 +279,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await expect( await expect(
pool pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, usdc.address, flashloanAmount, 2, '0x10', '0') .flashLoan(
_mockFlashLoanReceiver.address,
[usdc.address],
[flashloanAmount],
2,
'0x10',
'0'
)
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0); ).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
}); });
@ -317,7 +309,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await pool await pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, usdc.address, flashloanAmount, 2, '0x10', '0'); .flashLoan(_mockFlashLoanReceiver.address, [usdc.address], [flashloanAmount], 2, '0x10', '0');
const {variableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses( const {variableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(
usdc.address usdc.address
); );
@ -329,7 +321,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
const callerDebt = await usdcDebtToken.balanceOf(caller.address); const callerDebt = await usdcDebtToken.balanceOf(caller.address);
expect(callerDebt.toString()).to.be.equal('500450000', 'Invalid user debt'); expect(callerDebt.toString()).to.be.equal('500000000', 'Invalid user debt');
}); });
it('Caller deposits 1000 DAI as collateral, Takes a WETH flashloan with mode = 0, does not approve the transfer of the funds', async () => { it('Caller deposits 1000 DAI as collateral, Takes a WETH flashloan with mode = 0, does not approve the transfer of the funds', async () => {
@ -352,7 +344,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await expect( await expect(
pool pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, weth.address, flashAmount, 0, '0x10', '0') .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 0, '0x10', '0')
).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
}); });
@ -367,7 +359,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await pool await pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, weth.address, flashAmount, 1, '0x10', '0'); .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, '0x10', '0');
const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address); const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address);
@ -378,6 +370,6 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
const callerDebt = await wethDebtToken.balanceOf(caller.address); const callerDebt = await wethDebtToken.balanceOf(caller.address);
expect(callerDebt.toString()).to.be.equal('800720000000000000', 'Invalid user debt'); expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
}); });
}); });

View File

@ -187,7 +187,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
await expect( await expect(
pool pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, weth.address, flashAmount, 1, '0x10', '0') .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, '0x10', '0')
).revertedWith(IS_PAUSED); ).revertedWith(IS_PAUSED);
// Unpause pool // Unpause pool