diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 9bb5bc90..ac052e33 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -104,13 +104,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.updateState(); reserve.updateInterestRates(asset, aToken, amount, 0); - bool isFirstDeposit = IAToken(aToken).balanceOf(onBehalfOf) == 0; + bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); + if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); } - IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); - //transfer to the aToken contract IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); diff --git a/contracts/libraries/logic/ReserveLogic.sol b/contracts/libraries/logic/ReserveLogic.sol index 0852891f..c253d6a9 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -146,21 +146,22 @@ library ReserveLogic { * a formal specification. * @param reserve the reserve object **/ - function updateState(ReserveData storage reserve) external { - address variableDebtToken = reserve.variableDebtTokenAddress; + function updateState(ReserveData storage reserve) internal { + uint256 scaledVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) + .scaledTotalSupply(); uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; uint256 previousLiquidityIndex = reserve.liquidityIndex; (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = _updateIndexes( reserve, - variableDebtToken, + scaledVariableDebt, previousLiquidityIndex, previousVariableBorrowIndex ); _mintToTreasury( reserve, - variableDebtToken, + scaledVariableDebt, previousVariableBorrowIndex, newLiquidityIndex, newVariableBorrowIndex @@ -178,7 +179,7 @@ library ReserveLogic { ReserveData storage reserve, uint256 totalLiquidity, uint256 amount - ) external { + ) internal { uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay()); uint256 result = amountToLiquidityRatio.add(WadRayMath.ray()); @@ -226,6 +227,7 @@ library ReserveLogic { uint256 newStableRate; uint256 newVariableRate; uint256 avgStableRate; + uint256 totalVariableDebt; } /** @@ -241,7 +243,7 @@ library ReserveLogic { address aTokenAddress, uint256 liquidityAdded, uint256 liquidityTaken - ) external { + ) internal { UpdateInterestRatesLocalVars memory vars; vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress; @@ -249,6 +251,13 @@ library ReserveLogic { (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress) .getTotalSupplyAndAvgRate(); + //calculates the total variable debt locally using the scaled total supply instead + //of totalSupply(), as it's noticeably cheaper. Also, the index has been + //updated by the previous updateState() call + vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) + .scaledTotalSupply() + .rayMul(reserve.variableBorrowIndex); + vars.availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress); ( @@ -259,7 +268,7 @@ library ReserveLogic { reserveAddress, vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken), vars.totalStableDebt, - IERC20(reserve.variableDebtTokenAddress).totalSupply(), + vars.totalVariableDebt, vars.avgStableRate, reserve.configuration.getReserveFactor() ); @@ -286,7 +295,6 @@ library ReserveLogic { uint256 principalStableDebt; uint256 previousStableDebt; uint256 currentVariableDebt; - uint256 scaledVariableDebt; uint256 previousVariableDebt; uint256 avgStableRate; uint256 cumulatedStableInterest; @@ -300,14 +308,14 @@ library ReserveLogic { * @dev mints part of the repaid interest to the reserve treasury, depending on the reserveFactor for the * specific asset. * @param reserve the reserve reserve to be updated - * @param variableDebtToken the debt token address + * @param scaledVariableDebt the current scaled total variable debt * @param previousVariableBorrowIndex the variable borrow index before the last accumulation of the interest * @param newLiquidityIndex the new liquidity index * @param newVariableBorrowIndex the variable borrow index after the last accumulation of the interest **/ function _mintToTreasury( ReserveData storage reserve, - address variableDebtToken, + uint256 scaledVariableDebt, uint256 previousVariableBorrowIndex, uint256 newLiquidityIndex, uint256 newVariableBorrowIndex @@ -320,9 +328,6 @@ library ReserveLogic { return; } - //fetching the last scaled total variable debt - vars.scaledVariableDebt = IVariableDebtToken(variableDebtToken).scaledTotalSupply(); - //fetching the principal, total stable debt and the avg stable rate ( vars.principalStableDebt, @@ -332,10 +337,10 @@ library ReserveLogic { ) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData(); //calculate the last principal variable debt - vars.previousVariableDebt = vars.scaledVariableDebt.rayMul(previousVariableBorrowIndex); + vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); //calculate the new total supply after accumulation of the index - vars.currentVariableDebt = vars.scaledVariableDebt.rayMul(newVariableBorrowIndex); + vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex); //calculate the stable debt until the last timestamp update vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( @@ -360,13 +365,13 @@ library ReserveLogic { /** * @dev updates the reserve indexes and the timestamp of the update * @param reserve the reserve reserve to be updated - * @param variableDebtToken the debt token address + * @param scaledVariableDebt the scaled variable debt * @param liquidityIndex the last stored liquidity index * @param variableBorrowIndex the last stored variable borrow index **/ function _updateIndexes( ReserveData storage reserve, - address variableDebtToken, + uint256 scaledVariableDebt, uint256 liquidityIndex, uint256 variableBorrowIndex ) internal returns (uint256, uint256) { @@ -390,7 +395,7 @@ library ReserveLogic { //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(variableDebtToken).totalSupply() > 0) { + if (scaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest( reserve.currentVariableBorrowRate, timestamp diff --git a/contracts/libraries/logic/ValidationLogic.sol b/contracts/libraries/logic/ValidationLogic.sol index dc505afa..e5691fda 100644 --- a/contracts/libraries/logic/ValidationLogic.sol +++ b/contracts/libraries/logic/ValidationLogic.sol @@ -33,7 +33,7 @@ library ValidationLogic { * @param reserve the reserve state on which the user is depositing * @param amount the amount to be deposited */ - function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) external view { + function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) internal view { (bool isActive, bool isFreezed, , ) = reserve.configuration.getFlags(); require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0); @@ -56,7 +56,7 @@ library ValidationLogic { mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle - ) external view { + ) internal view { require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0); require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE); diff --git a/contracts/tokenization/AToken.sol b/contracts/tokenization/AToken.sol index b1ddbd03..13aa905c 100644 --- a/contracts/tokenization/AToken.sol +++ b/contracts/tokenization/AToken.sol @@ -117,12 +117,15 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { * @param user the address receiving the minted tokens * @param amount the amount of tokens to mint * @param index the the last index of the reserve + * @return true if the the previous balance of the user is 0 */ function mint( address user, uint256 amount, uint256 index - ) external override onlyLendingPool { + ) external override onlyLendingPool returns (bool) { + uint256 previousBalance = super.balanceOf(user); + uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT); _mint(user, amountScaled); @@ -130,6 +133,8 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { //transfer event to track balances emit Transfer(address(0), user, amount); emit Mint(user, amount, index); + + return previousBalance == 0; } /** diff --git a/contracts/tokenization/VariableDebtToken.sol b/contracts/tokenization/VariableDebtToken.sol index 3da59f9e..814ad8e7 100644 --- a/contracts/tokenization/VariableDebtToken.sol +++ b/contracts/tokenization/VariableDebtToken.sol @@ -51,12 +51,14 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { * @param user the user receiving the debt * @param amount the amount of debt being minted * @param index the variable debt index of the reserve + * @return true if the the previous balance of the user is 0 **/ function mint( address user, uint256 amount, uint256 index - ) external override onlyLendingPool { + ) external override onlyLendingPool returns (bool) { + uint256 previousBalance = super.balanceOf(user); uint256 amountScaled = amount.rayDiv(index); require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT); @@ -64,6 +66,8 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { emit Transfer(address(0), user, amount); emit Mint(user, amount, index); + + return previousBalance == 0; } /** diff --git a/contracts/tokenization/interfaces/IScaledBalanceToken.sol b/contracts/tokenization/interfaces/IScaledBalanceToken.sol index 5d2796e9..604706c2 100644 --- a/contracts/tokenization/interfaces/IScaledBalanceToken.sol +++ b/contracts/tokenization/interfaces/IScaledBalanceToken.sol @@ -21,7 +21,7 @@ interface IScaledBalanceToken { address user, uint256 amount, uint256 index - ) external; + ) external returns (bool); /** * @dev returns the principal balance of the user. The principal balance is the last