From 1b1fd6e5e2eac4ca00bdf938d46c0fa1f6f918c1 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 15 Oct 2020 14:13:46 +0200 Subject: [PATCH] Fixes #68 --- contracts/lendingpool/LendingPool.sol | 5 +-- contracts/libraries/logic/ReserveLogic.sol | 41 +++++++++++-------- contracts/libraries/logic/ValidationLogic.sol | 4 +- contracts/tokenization/AToken.sol | 7 +++- contracts/tokenization/VariableDebtToken.sol | 6 ++- .../interfaces/IScaledBalanceToken.sol | 2 +- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index ab07c5ce..bda7b6ff 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -106,13 +106,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 8a3604f7..e14a8d88 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -147,21 +147,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 @@ -179,7 +180,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()); @@ -227,6 +228,7 @@ library ReserveLogic { uint256 newStableRate; uint256 newVariableRate; uint256 avgStableRate; + uint256 totalVariableDebt; } /** @@ -242,7 +244,7 @@ library ReserveLogic { address aTokenAddress, uint256 liquidityAdded, uint256 liquidityTaken - ) external { + ) internal { UpdateInterestRatesLocalVars memory vars; vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress; @@ -250,6 +252,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); ( @@ -260,7 +269,7 @@ library ReserveLogic { reserveAddress, vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken), vars.totalStableDebt, - IERC20(reserve.variableDebtTokenAddress).totalSupply(), + vars.totalVariableDebt, vars.avgStableRate, reserve.configuration.getReserveFactor() ); @@ -287,7 +296,6 @@ library ReserveLogic { uint256 principalStableDebt; uint256 previousStableDebt; uint256 currentVariableDebt; - uint256 scaledVariableDebt; uint256 previousVariableDebt; uint256 avgStableRate; uint256 cumulatedStableInterest; @@ -301,14 +309,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 @@ -321,9 +329,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, @@ -333,10 +338,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( @@ -361,13 +366,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) { @@ -391,7 +396,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 ee13232b..a2aaecc1 100644 --- a/contracts/libraries/logic/ValidationLogic.sol +++ b/contracts/libraries/logic/ValidationLogic.sol @@ -34,7 +34,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); @@ -57,7 +57,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 3e6d4c76..5a7efd1a 100644 --- a/contracts/tokenization/AToken.sol +++ b/contracts/tokenization/AToken.sol @@ -119,12 +119,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); @@ -132,6 +135,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 f8e48af9..365b9e26 100644 --- a/contracts/tokenization/VariableDebtToken.sol +++ b/contracts/tokenization/VariableDebtToken.sol @@ -54,12 +54,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); @@ -67,6 +69,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