diff --git a/contracts/configuration/LendingPoolAddressesProviderRegistry.sol b/contracts/configuration/LendingPoolAddressesProviderRegistry.sol index 77c260bf..53ef4d10 100644 --- a/contracts/configuration/LendingPoolAddressesProviderRegistry.sol +++ b/contracts/configuration/LendingPoolAddressesProviderRegistry.sol @@ -36,13 +36,15 @@ contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesP * @return the list of addressesProviders **/ 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); - for (uint256 i = 0; i < _addressesProvidersList.length; i++) { - if (_addressesProviders[_addressesProvidersList[i]] > 0) { - activeProviders[i] = _addressesProvidersList[i]; + for (uint256 i = 0; i < maxLength; i++) { + if (_addressesProviders[addressesProvidersList[i]] > 0) { + activeProviders[i] = addressesProvidersList[i]; } } @@ -76,7 +78,9 @@ contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesP * @param provider the pool address to be added **/ 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) { return; } diff --git a/contracts/flashloan/interfaces/IFlashLoanReceiver.sol b/contracts/flashloan/interfaces/IFlashLoanReceiver.sol index 5c92236a..784d0fa3 100644 --- a/contracts/flashloan/interfaces/IFlashLoanReceiver.sol +++ b/contracts/flashloan/interfaces/IFlashLoanReceiver.sol @@ -9,9 +9,9 @@ pragma solidity ^0.6.8; **/ interface IFlashLoanReceiver { function executeOperation( - address reserve, - uint256 amount, - uint256 fee, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, bytes calldata params ) external returns (bool); } diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index 2b4ead37..17e75fa6 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -99,27 +99,35 @@ interface ILendingPool { /** * @dev emitted when a flashloan is executed * @param target the address of the flashLoanReceiver - * @param reserve the address of the reserve - * @param amount the amount requested - * @param totalPremium the total fee on the amount + * @param assets the address of the assets being flashborrowed + * @param amounts the amount requested + * @param premiums the total fee on the amount * @param referralCode the referral code of the caller **/ event FlashLoan( address indexed target, - address indexed reserve, - uint256 amount, - uint256 totalPremium, + uint256 mode, + address[] assets, + uint256[] amounts, + uint256[] premiums, 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 principal the address of the reserve * @param user the address of the user being liquidated @@ -137,15 +145,28 @@ interface ILendingPool { address liquidator, bool receiveAToken ); - /** - * @dev Emitted when the pause is triggered. - */ - event Paused(); /** - * @dev Emitted when the pause is lifted. - */ - event Unpaused(); + * @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, + * 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) @@ -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 * 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 reserve the address of the principal reserve - * @param amount the amount requested for this flashloan + * @param assets the address of the principal reserve + * @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 referralCode the referral code of the caller **/ function flashLoan( address receiver, - address reserve, - uint256 amount, - uint256 debtType, + address[] calldata assets, + uint256[] calldata amounts, + uint256 mode, bytes calldata params, uint16 referralCode ) external; diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 963ef7cf..c63ff348 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -157,7 +157,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage 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); - //default to max amount - uint256 paybackAmount = interestRateMode == ReserveLogic.InterestRateMode.STABLE - ? stableDebt - : variableDebt; - - if (amount < paybackAmount) { - paybackAmount = amount; - } - ValidationLogic.validateRepay( reserve, amount, @@ -280,6 +271,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage variableDebt ); + //default to max amount + uint256 paybackAmount = interestRateMode == ReserveLogic.InterestRateMode.STABLE + ? stableDebt + : variableDebt; + + if (amount < paybackAmount) { + paybackAmount = amount; + } + reserve.updateState(); //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. - * this is regulated by Aave to ensure that the protocol is not abused, and the user is paying a fair - * rate. Anyone can call this function. + * @dev rebalances the stable interest rate of a user. Users can be rebalanced if the following conditions are satisfied: + * 1. Usage ratio is above 95% + * 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 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); - //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 .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. * @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 { _whenNotPaused(); @@ -483,11 +484,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } struct FlashLoanLocalVars { - uint256 premium; - uint256 amountPlusPremium; IFlashLoanReceiver receiver; - address aTokenAddress; 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 * 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 asset The address of the principal reserve - * @param amount The amount requested for this flashloan + * @param assets The addresss of the assets being flashborrowed + * @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 params Variadic packed params to pass to the receiver as extra information * @param referralCode Referral code of the flash loan **/ function flashLoan( address receiverAddress, - address asset, - uint256 amount, + address[] calldata assets, + uint256[] calldata amounts, uint256 mode, bytes calldata params, uint16 referralCode ) external override { _whenNotPaused(); - ReserveLogic.ReserveData storage reserve = _reserves[asset]; + FlashLoanLocalVars memory vars; - vars.aTokenAddress = reserve.aTokenAddress; + ValidationLogic.validateFlashloan(assets, amounts, mode); - vars.premium = amount.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000); - - ValidationLogic.validateFlashloan(mode, vars.premium); - - ReserveLogic.InterestRateMode debtMode = ReserveLogic.InterestRateMode(mode); + address[] memory aTokenAddresses = new address[](assets.length); + uint256[] memory premiums = new uint256[](assets.length); vars.receiver = IFlashLoanReceiver(receiverAddress); + vars.debtMode = ReserveLogic.InterestRateMode(mode); - //transfer funds to the receiver - IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount); + for (vars.i = 0; vars.i < assets.length; vars.i++) { + 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 require( - vars.receiver.executeOperation(asset, amount, vars.premium, params), + vars.receiver.executeOperation(assets, amounts, premiums, params), 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) { - IERC20(asset).safeTransferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium); + vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium); - reserve.updateState(); - reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium); - reserve.updateInterestRates(asset, vars.aTokenAddress, vars.premium, 0); + if (vars.debtMode == ReserveLogic.InterestRateMode.NONE) { + _reserves[vars.currentAsset].updateState(); + _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); - } else { - //if the user didn't choose to return the funds, the system checks if there - //is enough collateral and eventually open a position - _executeBorrow( - ExecuteBorrowParams( - asset, - msg.sender, - msg.sender, - vars.amountPlusPremium, - mode, - vars.aTokenAddress, - referralCode, - false - ) - ); + IERC20(vars.currentAsset).safeTransferFrom( + receiverAddress, + vars.currentATokenAddress, + vars.currentAmountPlusPremium + ); + } else { + //if the user didn't choose to return the funds, the system checks if there + //is enough collateral and eventually open a position + _executeBorrow( + ExecuteBorrowParams( + vars.currentAsset, + msg.sender, + msg.sender, + vars.currentAmount, + mode, + vars.currentATokenAddress, + referralCode, + false + ) + ); + } + emit FlashLoan(receiverAddress, mode, assets, amounts, premiums, referralCode); } } diff --git a/contracts/lendingpool/LendingPoolCollateralManager.sol b/contracts/lendingpool/LendingPoolCollateralManager.sol index 0341748e..16abdd10 100644 --- a/contracts/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/lendingpool/LendingPoolCollateralManager.sol @@ -194,13 +194,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor //update the principal reserve principalReserve.updateState(); - principalReserve.updateInterestRates( - principal, - principalReserve.aTokenAddress, - vars.actualAmountToLiquidate, - 0 - ); - if (vars.userVariableDebt >= vars.actualAmountToLiquidate) { IVariableDebtToken(principalReserve.variableDebtTokenAddress).burn( 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 (receiveAToken) { vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate); @@ -306,8 +306,8 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor .principalCurrencyPrice .mul(purchaseAmount) .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) { collateralAmount = userCollateralBalance; diff --git a/contracts/libraries/helpers/Errors.sol b/contracts/libraries/helpers/Errors.sol index 985a2bc3..91dd385f 100644 --- a/contracts/libraries/helpers/Errors.sol +++ b/contracts/libraries/helpers/Errors.sol @@ -45,6 +45,7 @@ library Errors { string public constant INVALID_EQUAL_ASSETS_TO_SWAP = '56'; string public constant NO_MORE_RESERVES_ALLOWED = '59'; string public constant INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60'; + string public constant INCONSISTENT_FLASHLOAN_PARAMS = '69'; // require error messages - aToken - DebtTokens string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool' diff --git a/contracts/libraries/logic/GenericLogic.sol b/contracts/libraries/logic/GenericLogic.sol index ac379dc8..a5801b9d 100644 --- a/contracts/libraries/logic/GenericLogic.sol +++ b/contracts/libraries/logic/GenericLogic.sol @@ -25,7 +25,6 @@ library GenericLogic { using UserConfiguration for UserConfiguration.Map; uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether; - uint256 public constant HEALTH_FACTOR_CRITICAL_THRESHOLD = 0.98 ether; struct balanceDecreaseAllowedLocalVars { uint256 decimals; diff --git a/contracts/libraries/logic/ValidationLogic.sol b/contracts/libraries/logic/ValidationLogic.sol index 93219ce8..947d84ea 100644 --- a/contracts/libraries/logic/ValidationLogic.sol +++ b/contracts/libraries/logic/ValidationLogic.sol @@ -327,11 +327,16 @@ library ValidationLogic { /** * @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 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 { - require(premium > 0, Errors.REQUESTED_AMOUNT_TOO_SMALL); + function validateFlashloan( + address[] memory assets, + uint256[] memory amounts, + uint256 mode + ) internal pure { require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE); + require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS); } /** diff --git a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol index 0a767712..7b72c2cb 100644 --- a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol +++ b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol @@ -14,8 +14,8 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase { ILendingPoolAddressesProvider internal _provider; - event ExecutedWithFail(address _reserve, uint256 _amount, uint256 _fee); - event ExecutedWithSuccess(address _reserve, uint256 _amount, uint256 _fee); + event ExecutedWithFail(address[] _assets, uint256[] _amounts, uint256[] _premiums); + event ExecutedWithSuccess(address[] _assets, uint256[] _amounts, uint256[] _premiums); bool _failExecution; uint256 _amountToApprove; @@ -44,33 +44,40 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase { } function executeOperation( - address reserve, - uint256 amount, - uint256 fee, + address[] memory assets, + uint256[] memory amounts, + uint256[] memory premiums, bytes memory params ) public override returns (bool) { 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) { - emit ExecutedWithFail(reserve, amount, fee); + emit ExecutedWithFail(assets, amounts, premiums); return !_simulateEOA; } - //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 + for (uint256 i = 0; i < assets.length; i++) { + //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; } diff --git a/contracts/tokenization/StableDebtToken.sol b/contracts/tokenization/StableDebtToken.sol index d24228ea..346d9719 100644 --- a/contracts/tokenization/StableDebtToken.sol +++ b/contracts/tokenization/StableDebtToken.sol @@ -128,7 +128,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { _totalSupplyTimestamp = _timestamps[user] = uint40(block.timestamp); //calculates the updated average stable rate - _avgStableRate = vars + vars.currentAvgStableRate = _avgStableRate = vars .currentAvgStableRate .rayMul(vars.previousSupply.wadToRay()) .add(rate.rayMul(vars.amountInRay)) @@ -139,7 +139,15 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { // transfer event to track balances 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); uint256 previousSupply = totalSupply(); + uint256 newStableRate = 0; //since the total supply and each single user debt accrue separately, //there might be accumulation errors so that the last borrower repaying //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 if (previousSupply <= amount) { - _avgStableRate = 0; + newStableRate = _avgStableRate = 0; _totalSupply = 0; } else { uint256 nextSupply = _totalSupply = previousSupply.sub(amount); - _avgStableRate = _avgStableRate + newStableRate = _avgStableRate = _avgStableRate .rayMul(previousSupply.wadToRay()) .sub(_usersData[user].rayMul(amount.wadToRay())) .rayDiv(nextSupply.wadToRay()); @@ -190,14 +199,13 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { // transfer event to track balances 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 * @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 - * and the new user index + * @return The previous principal balance, the new principal balance and the balance increase **/ function _calculateBalanceIncrease(address user) internal diff --git a/contracts/tokenization/interfaces/IStableDebtToken.sol b/contracts/tokenization/interfaces/IStableDebtToken.sol index 8862c30d..4c0d5940 100644 --- a/contracts/tokenization/interfaces/IStableDebtToken.sol +++ b/contracts/tokenization/interfaces/IStableDebtToken.sol @@ -21,6 +21,7 @@ interface IStableDebtToken { * @param currentBalance the current balance of the user * @param balanceIncrease the debt increase since the last update * @param newRate the rate of the debt after the minting + * @param avgStableRate the new average stable rate after the minting **/ event Mint( address indexed user, @@ -28,7 +29,8 @@ interface IStableDebtToken { uint256 previousBalance, uint256 currentBalance, uint256 balanceIncrease, - uint256 newRate + uint256 newRate, + uint256 avgStableRate ); /** @@ -38,13 +40,15 @@ interface IStableDebtToken { * @param previousBalance the previous balance of the user * @param currentBalance the current balance of the user * @param balanceIncrease the debt increase since the last update + * @param avgStableRate the new average stable rate after the minting **/ event Burn( address indexed user, uint256 amount, uint256 previousBalance, uint256 currentBalance, - uint256 balanceIncrease + uint256 balanceIncrease, + uint256 avgStableRate ); /** diff --git a/deployed-contracts.json b/deployed-contracts.json index 3aa2b559..64729df5 100644 --- a/deployed-contracts.json +++ b/deployed-contracts.json @@ -86,8 +86,7 @@ }, "LendingPool": { "buidlerevm": { - "address": "0x35c1419Da7cf0Ff885B8Ef8EA9242FEF6800c99b", - "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" + "address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e" }, "localhost": { "address": "0x3EE716e38f21e5FC16BFDB773db24D63C637A5d8" @@ -711,7 +710,7 @@ }, "ReserveLogic": { "buidlerevm": { - "address": "0x78Ee8Fb9fE5abD5e347Fc94c2fb85596d1f60e3c", + "address": "0xFAe0fd738dAbc8a0426F47437322b6d026A9FD95", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "kovan": { @@ -721,7 +720,7 @@ }, "GenericLogic": { "buidlerevm": { - "address": "0x920d847fE49E54C19047ba8bc236C45A8068Bca7", + "address": "0x6082731fdAba4761277Fb31299ebC782AD3bCf24", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "kovan": { @@ -731,7 +730,7 @@ }, "ValidationLogic": { "buidlerevm": { - "address": "0xA4765Ff72A9F3CfE73089bb2c3a41B838DF71574", + "address": "0x8456161947DFc1fC159A0B26c025cD2b4bba0c3e", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "kovan": { diff --git a/helpers/contracts-getters.ts b/helpers/contracts-getters.ts index 3aefdab5..0b03cb63 100644 --- a/helpers/contracts-getters.ts +++ b/helpers/contracts-getters.ts @@ -3,8 +3,6 @@ import { ATokenFactory, ATokensAndRatesHelperFactory, DefaultReserveInterestRateStrategyFactory, - DeployATokensAndRatesFactory, - DeployStableAndVariableTokensFactory, GenericLogicFactory, LendingPoolAddressesProviderFactory, LendingPoolAddressesProviderRegistryFactory, diff --git a/package-lock.json b/package-lock.json index 8cd9d2e2..5b9d2c4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15177,7 +15177,6 @@ "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -15216,12 +15215,6 @@ "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": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -17305,16 +17298,6 @@ "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": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", @@ -19211,7 +19194,6 @@ "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -19228,12 +19210,6 @@ "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": { "version": "5.1.1", "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": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", diff --git a/test/flashloan.spec.ts b/test/flashloan.spec.ts index 1f5907a9..305d674f 100644 --- a/test/flashloan.spec.ts +++ b/test/flashloan.spec.ts @@ -45,8 +45,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await pool.flashLoan( _mockFlashLoanReceiver.address, - weth.address, - ethers.utils.parseEther('0.8'), + [weth.address], + [ethers.utils.parseEther('0.8')], 0, '0x10', '0' @@ -74,8 +74,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { const reserveDataBefore = await helpersContract.getReserveData(weth.address); const txResult = await pool.flashLoan( _mockFlashLoanReceiver.address, - weth.address, - '1000720000000000000', + [weth.address], + ['1000720000000000000'], 0, '0x10', '0' @@ -105,8 +105,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { .connect(caller.signer) .flashLoan( _mockFlashLoanReceiver.address, - weth.address, - ethers.utils.parseEther('0.8'), + [weth.address], + [ethers.utils.parseEther('0.8')], 0, '0x10', '0' @@ -125,8 +125,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { .connect(caller.signer) .flashLoan( _mockFlashLoanReceiver.address, - weth.address, - ethers.utils.parseEther('0.8'), + [weth.address], + [ethers.utils.parseEther('0.8')], 0, '0x10', '0' @@ -145,8 +145,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { .connect(caller.signer) .flashLoan( _mockFlashLoanReceiver.address, - weth.address, - ethers.utils.parseEther('0.8'), + [weth.address], + [ethers.utils.parseEther('0.8')], 4, '0x10', '0' @@ -173,8 +173,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { .connect(caller.signer) .flashLoan( _mockFlashLoanReceiver.address, - weth.address, - ethers.utils.parseEther('0.8'), + [weth.address], + [ethers.utils.parseEther('0.8')], 2, '0x10', '0' @@ -190,22 +190,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { const callerDebt = await wethDebtToken.balanceOf(caller.address); - expect(callerDebt.toString()).to.be.equal('800720000000000000', '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); + expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt'); }); 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( pool.flashLoan( _mockFlashLoanReceiver.address, - weth.address, - '1004415000000000000', //slightly higher than the available liquidity + [weth.address], + ['1004415000000000000'], //slightly higher than the available liquidity 2, '0x10', '0' @@ -228,7 +213,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { const {pool, deployer, weth} = testEnv; 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; }); @@ -254,8 +239,8 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await pool.flashLoan( _mockFlashLoanReceiver.address, - usdc.address, - flashloanAmount, + [usdc.address], + [flashloanAmount], 0, '0x10', '0' @@ -294,7 +279,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await expect( pool .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); }); @@ -317,7 +309,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await pool .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( usdc.address ); @@ -329,7 +321,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { 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 () => { @@ -352,7 +344,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await expect( pool .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); }); @@ -367,7 +359,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await pool .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); @@ -378,6 +370,6 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { 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'); }); }); diff --git a/test/pausable-functions.spec.ts b/test/pausable-functions.spec.ts index 62394969..2bf8be21 100644 --- a/test/pausable-functions.spec.ts +++ b/test/pausable-functions.spec.ts @@ -187,7 +187,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => { await expect( pool .connect(caller.signer) - .flashLoan(_mockFlashLoanReceiver.address, weth.address, flashAmount, 1, '0x10', '0') + .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, '0x10', '0') ).revertedWith(IS_PAUSED); // Unpause pool