diff --git a/contracts/protocol/libraries/logic/PoolBaseLogic.sol b/contracts/protocol/libraries/logic/PoolBaseLogic.sol new file mode 100644 index 000000000..a7178d7d1 --- /dev/null +++ b/contracts/protocol/libraries/logic/PoolBaseLogic.sol @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.8.6; + +import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; +import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; + +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IAToken} from '../../../interfaces/IAToken.sol'; + +import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol'; + +import {ValidationLogic} from './ValidationLogic.sol'; +import {ReserveLogic} from './ReserveLogic.sol'; + +import {Helpers} from '../helpers/Helpers.sol'; + +import {UserConfiguration} from './../configuration/UserConfiguration.sol'; + +import {DataTypes} from '../types/DataTypes.sol'; + +import {WadRayMath} from '../math/WadRayMath.sol'; + +library PoolBaseLogic { + using ReserveLogic for DataTypes.ReserveCache; + using ReserveLogic for DataTypes.ReserveData; + using SafeERC20 for IERC20; + using UserConfiguration for DataTypes.UserConfigurationMap; + using WadRayMath for uint256; + + event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); + event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); + event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); + event Deposit( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint16 indexed referral + ); + event Borrow( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint256 borrowRateMode, + uint256 borrowRate, + uint16 indexed referral + ); + event Repay( + address indexed reserve, + address indexed user, + address indexed repayer, + uint256 amount + ); + + function _validateHFAndLtv( + address asset, + mapping(address => DataTypes.ReserveData) storage reserves, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reservesList, + uint256 reservesCount, + address priceOracle + ) internal view { + ValidationLogic.validateHFAndLtv( + asset, + msg.sender, + reserves, + userConfig, + reservesList, + reservesCount, + priceOracle + ); + } + + function _executeDeposit( + DataTypes.ReserveData storage reserve, + DataTypes.UserConfigurationMap storage userConfig, + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) public { + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + + reserve.updateState(reserveCache); + + ValidationLogic.validateDeposit(reserveCache, amount); + + reserve.updateInterestRates(reserveCache, asset, amount, 0); + + IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, amount); + + bool isFirstDeposit = + IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.nextLiquidityIndex); + + if (isFirstDeposit) { + userConfig.setUsingAsCollateral(reserve.id, true); + emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); + } + + emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode); + } + + function _executeWithdraw( + mapping(address => DataTypes.ReserveData) storage reserves, + DataTypes.UserConfigurationMap storage userConfig, + address asset, + uint256 amount, + address to, + mapping(uint256 => address) storage reservesList, + uint256 reservesCount, + address priceOracle + ) public returns (uint256) { + DataTypes.ReserveData storage reserve = reserves[asset]; + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + + reserve.updateState(reserveCache); + + uint256 userBalance = + IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( + reserveCache.nextLiquidityIndex + ); + + uint256 amountToWithdraw = amount; + + if (amount == type(uint256).max) { + amountToWithdraw = userBalance; + } + + ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance); + + reserve.updateInterestRates(reserveCache, asset, 0, amountToWithdraw); + + IAToken(reserveCache.aTokenAddress).burn( + msg.sender, + to, + amountToWithdraw, + reserveCache.nextLiquidityIndex + ); + + if (userConfig.isUsingAsCollateral(reserve.id)) { + if (userConfig.isBorrowingAny()) { + _validateHFAndLtv(asset, reserves, userConfig, reservesList, reservesCount, priceOracle); + } + + if (amountToWithdraw == userBalance) { + userConfig.setUsingAsCollateral(reserve.id, false); + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); + } + } + + emit Withdraw(asset, msg.sender, to, amountToWithdraw); + + return amountToWithdraw; + } + + function _executeBorrow( + mapping(address => DataTypes.ReserveData) storage reserves, + DataTypes.ReserveCache memory reserveCache, + DataTypes.UserConfigurationMap storage userConfig, + DataTypes.ExecuteBorrowParams memory vars + ) public { + DataTypes.ReserveData storage reserve = reserves[vars.asset]; + + uint256 currentStableRate = 0; + bool isFirstBorrowing = false; + + if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { + currentStableRate = reserve.currentStableBorrowRate; + isFirstBorrowing = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( + vars.user, + vars.onBehalfOf, + vars.amount, + currentStableRate + ); + reserveCache.refreshDebt(vars.amount, 0, 0, 0); + } else { + isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( + vars.user, + vars.onBehalfOf, + vars.amount, + reserveCache.nextVariableBorrowIndex + ); + reserveCache.refreshDebt(0, 0, vars.amount, 0); + } + + if (isFirstBorrowing) { + userConfig.setBorrowing(reserve.id, true); + } + + reserve.updateInterestRates( + reserveCache, + vars.asset, + 0, + vars.releaseUnderlying ? vars.amount : 0 + ); + + if (vars.releaseUnderlying) { + IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); + } + + emit Borrow( + vars.asset, + vars.user, + vars.onBehalfOf, + vars.amount, + vars.interestRateMode, + DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE + ? currentStableRate + : reserve.currentVariableBorrowRate, + vars.referralCode + ); + } + + function _executeRepay( + DataTypes.ReserveData storage reserve, + DataTypes.UserConfigurationMap storage userConfig, + address asset, + uint256 amount, + uint256 rateMode, + address onBehalfOf, + address lastBorrower, + uint40 lastBorrowTimestamp + ) public returns (uint256) { + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); + DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); + + ValidationLogic.validateRepay( + lastBorrower, + lastBorrowTimestamp, + reserveCache, + amount, + interestRateMode, + onBehalfOf, + stableDebt, + variableDebt + ); + + uint256 paybackAmount = + interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt; + + if (amount < paybackAmount) { + paybackAmount = amount; + } + + reserve.updateState(reserveCache); + + if (interestRateMode == DataTypes.InterestRateMode.STABLE) { + IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); + reserveCache.refreshDebt(0, paybackAmount, 0, 0); + } else { + IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( + onBehalfOf, + paybackAmount, + reserveCache.nextVariableBorrowIndex + ); + reserveCache.refreshDebt(0, 0, 0, paybackAmount); + } + + return + _executeRepayHelper( + reserve, + reserveCache, + userConfig, + asset, + onBehalfOf, + paybackAmount, + variableDebt, + stableDebt + ); + } + + function _executeRepayHelper( + DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, + DataTypes.UserConfigurationMap storage userConfig, + address asset, + address onBehalfOf, + uint256 paybackAmount, + uint256 variableDebt, + uint256 stableDebt + ) public returns (uint256) { + reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0); + + if (stableDebt + variableDebt - paybackAmount == 0) { + userConfig.setBorrowing(reserve.id, false); + } + + IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount); + + IAToken(reserveCache.aTokenAddress).handleRepayment(msg.sender, paybackAmount); + + emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); + + return paybackAmount; + } +} diff --git a/contracts/protocol/libraries/logic/PoolHelperLogic.sol b/contracts/protocol/libraries/logic/PoolHelperLogic.sol new file mode 100644 index 000000000..db0a73d89 --- /dev/null +++ b/contracts/protocol/libraries/logic/PoolHelperLogic.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.8.6; + +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {Helpers} from '../helpers/Helpers.sol'; +import {ValidationLogic} from './ValidationLogic.sol'; +import {ReserveLogic} from './ReserveLogic.sol'; +import {DataTypes} from './../types/DataTypes.sol'; + +import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; +import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; + +import {UserConfiguration} from './../configuration/UserConfiguration.sol'; + +library PoolHelperLogic { + using ReserveLogic for DataTypes.ReserveCache; + using ReserveLogic for DataTypes.ReserveData; + using UserConfiguration for DataTypes.UserConfigurationMap; + using SafeERC20 for IERC20; + + event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); + event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); + event RebalanceStableBorrowRate(address indexed reserve, address indexed user); + event Swap(address indexed reserve, address indexed user, uint256 rateMode); + + function rebalanceStableBorrowRate( + DataTypes.ReserveData storage reserve, + address asset, + address user + ) public { + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + + IERC20 stableDebtToken = IERC20(reserveCache.stableDebtTokenAddress); + IERC20 variableDebtToken = IERC20(reserveCache.variableDebtTokenAddress); + uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); + + ValidationLogic.validateRebalanceStableBorrowRate( + reserve, + reserveCache, + asset, + stableDebtToken, + variableDebtToken, + reserveCache.aTokenAddress + ); + + reserve.updateState(reserveCache); + + IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); + IStableDebtToken(address(stableDebtToken)).mint( + user, + user, + stableDebt, + reserve.currentStableBorrowRate + ); + + reserveCache.refreshDebt(stableDebt, stableDebt, 0, 0); + + reserve.updateInterestRates(reserveCache, asset, 0, 0); + + emit RebalanceStableBorrowRate(asset, user); + } + + function swapBorrowRateMode( + DataTypes.ReserveData storage reserve, + DataTypes.UserConfigurationMap storage userConfig, + address asset, + uint256 rateMode + ) public { + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + + (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); + + DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); + + ValidationLogic.validateSwapRateMode( + reserve, + reserveCache, + userConfig, + stableDebt, + variableDebt, + interestRateMode + ); + + reserve.updateState(reserveCache); + + if (interestRateMode == DataTypes.InterestRateMode.STABLE) { + IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt); + IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( + msg.sender, + msg.sender, + stableDebt, + reserveCache.nextVariableBorrowIndex + ); + reserveCache.refreshDebt(0, stableDebt, stableDebt, 0); + } else { + IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( + msg.sender, + variableDebt, + reserveCache.nextVariableBorrowIndex + ); + IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( + msg.sender, + msg.sender, + variableDebt, + reserve.currentStableBorrowRate + ); + reserveCache.refreshDebt(variableDebt, 0, 0, variableDebt); + } + + reserve.updateInterestRates(reserveCache, asset, 0, 0); + + emit Swap(asset, msg.sender, rateMode); + } + + function setUserUseReserveAsCollateral( + mapping(address => DataTypes.ReserveData) storage reserves, + DataTypes.UserConfigurationMap storage userConfig, + address asset, + bool useAsCollateral, + mapping(uint256 => address) storage reservesList, + uint256 reservesCount, + address priceOracle + ) public { + DataTypes.ReserveData storage reserve = reserves[asset]; + DataTypes.ReserveCache memory reserveCache = reserve.cache(); + + uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); + + ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance); + + userConfig.setUsingAsCollateral(reserve.id, useAsCollateral); + + if (useAsCollateral) { + emit ReserveUsedAsCollateralEnabled(asset, msg.sender); + } else { + ValidationLogic.validateHFAndLtv( + asset, + msg.sender, + reserves, + userConfig, + reservesList, + reservesCount, + priceOracle + ); + + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); + } + } +} diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index 295187895..bdda76766 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -73,4 +73,14 @@ library DataTypes { uint40 reserveLastUpdateTimestamp; uint40 stableDebtLastUpdateTimestamp; } + + struct ExecuteBorrowParams { + address asset; + address user; + address onBehalfOf; + uint256 amount; + uint256 interestRateMode; + uint16 referralCode; + bool releaseUnderlying; + } } diff --git a/contracts/protocol/pool/Pool.sol b/contracts/protocol/pool/Pool.sol index 16c377f9c..d9bf48255 100644 --- a/contracts/protocol/pool/Pool.sol +++ b/contracts/protocol/pool/Pool.sol @@ -25,6 +25,9 @@ import {UserConfiguration} from '../libraries/configuration/UserConfiguration.so import {DataTypes} from '../libraries/types/DataTypes.sol'; import {PoolStorage} from './PoolStorage.sol'; +import {PoolBaseLogic} from '../libraries/logic/PoolBaseLogic.sol'; +import {PoolHelperLogic} from '../libraries/logic/PoolHelperLogic.sol'; + /** * @title Pool contract * @dev Main point of interaction with an Aave protocol's market @@ -135,7 +138,7 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { address onBehalfOf ) external override { _executeBorrow( - ExecuteBorrowParams( + DataTypes.ExecuteBorrowParams( asset, msg.sender, onBehalfOf, @@ -183,113 +186,28 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { ///@inheritdoc IPool function swapBorrowRateMode(address asset, uint256 rateMode) external override { DataTypes.ReserveData storage reserve = _reserves[asset]; - DataTypes.ReserveCache memory reserveCache = reserve.cache(); - - (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); - - DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); - - ValidationLogic.validateSwapRateMode( - reserve, - reserveCache, - _usersConfig[msg.sender], - stableDebt, - variableDebt, - interestRateMode - ); - - reserve.updateState(reserveCache); - - if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt); - IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( - msg.sender, - msg.sender, - stableDebt, - reserveCache.nextVariableBorrowIndex - ); - reserveCache.refreshDebt(0, stableDebt, stableDebt, 0); - } else { - IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( - msg.sender, - variableDebt, - reserveCache.nextVariableBorrowIndex - ); - IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( - msg.sender, - msg.sender, - variableDebt, - reserve.currentStableBorrowRate - ); - reserveCache.refreshDebt(variableDebt, 0, 0, variableDebt); - } - - reserve.updateInterestRates(reserveCache, asset, 0, 0); - - emit Swap(asset, msg.sender, rateMode); + DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; + PoolHelperLogic.swapBorrowRateMode(reserve, userConfig, asset, rateMode); } ///@inheritdoc IPool function rebalanceStableBorrowRate(address asset, address user) external override { DataTypes.ReserveData storage reserve = _reserves[asset]; - DataTypes.ReserveCache memory reserveCache = reserve.cache(); - - IERC20 stableDebtToken = IERC20(reserveCache.stableDebtTokenAddress); - IERC20 variableDebtToken = IERC20(reserveCache.variableDebtTokenAddress); - uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); - - ValidationLogic.validateRebalanceStableBorrowRate( - reserve, - reserveCache, - asset, - stableDebtToken, - variableDebtToken, - reserveCache.aTokenAddress - ); - - reserve.updateState(reserveCache); - - IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); - IStableDebtToken(address(stableDebtToken)).mint( - user, - user, - stableDebt, - reserve.currentStableBorrowRate - ); - - reserveCache.refreshDebt(stableDebt, stableDebt, 0, 0); - - reserve.updateInterestRates(reserveCache, asset, 0, 0); - - emit RebalanceStableBorrowRate(asset, user); + PoolHelperLogic.rebalanceStableBorrowRate(reserve, asset, user); } ///@inheritdoc IPool function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override { - DataTypes.ReserveData storage reserve = _reserves[asset]; - DataTypes.ReserveCache memory reserveCache = reserve.cache(); - - uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); - - ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance); - - _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); - - if (useAsCollateral) { - emit ReserveUsedAsCollateralEnabled(asset, msg.sender); - } else { - ValidationLogic.validateHFAndLtv( - asset, - msg.sender, - _reserves, - _usersConfig[msg.sender], - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); - - emit ReserveUsedAsCollateralDisabled(asset, msg.sender); - } + DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; + PoolHelperLogic.setUserUseReserveAsCollateral( + _reserves, + userConfig, + asset, + useAsCollateral, + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); } ///@inheritdoc IPool @@ -412,7 +330,7 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { // If the user chose to not return the funds, the system checks if there is enough collateral and // eventually opens a debt position _executeBorrow( - ExecuteBorrowParams( + DataTypes.ExecuteBorrowParams( vars.currentAsset, msg.sender, onBehalfOf, @@ -716,17 +634,7 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { _flashLoanPremiumToProtocol = flashLoanPremiumToProtocol; } - struct ExecuteBorrowParams { - address asset; - address user; - address onBehalfOf; - uint256 amount; - uint256 interestRateMode; - uint16 referralCode; - bool releaseUnderlying; - } - - function _executeBorrow(ExecuteBorrowParams memory vars) internal { + function _executeBorrow(DataTypes.ExecuteBorrowParams memory vars) internal { DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); @@ -747,58 +655,10 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { _addressesProvider.getPriceOracle() ); - uint256 currentStableRate = 0; - bool isFirstBorrowing = false; - - if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { - currentStableRate = reserve.currentStableBorrowRate; - - isFirstBorrowing = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( - vars.user, - vars.onBehalfOf, - vars.amount, - currentStableRate - ); - reserveCache.refreshDebt(vars.amount, 0, 0, 0); - } else { - isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( - vars.user, - vars.onBehalfOf, - vars.amount, - reserveCache.nextVariableBorrowIndex - ); - reserveCache.refreshDebt(0, 0, vars.amount, 0); - } - - if (isFirstBorrowing) { - userConfig.setBorrowing(reserve.id, true); - } - - reserve.updateInterestRates( - reserveCache, - vars.asset, - 0, - vars.releaseUnderlying ? vars.amount : 0 - ); + PoolBaseLogic._executeBorrow(_reserves, reserveCache, userConfig, vars); _lastBorrower = vars.user; _lastBorrowTimestamp = uint40(block.timestamp); - - if (vars.releaseUnderlying) { - IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); - } - - emit Borrow( - vars.asset, - vars.user, - vars.onBehalfOf, - vars.amount, - vars.interestRateMode, - DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE - ? currentStableRate - : reserve.currentVariableBorrowRate, - vars.referralCode - ); } function _executeDeposit( @@ -808,25 +668,15 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { uint16 referralCode ) internal { DataTypes.ReserveData storage reserve = _reserves[asset]; - DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(reserveCache); - - ValidationLogic.validateDeposit(reserveCache, amount); - - reserve.updateInterestRates(reserveCache, asset, amount, 0); - - IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, amount); - - bool isFirstDeposit = - IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.nextLiquidityIndex); - - if (isFirstDeposit) { - _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); - emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); - } - - emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode); + PoolBaseLogic._executeDeposit( + reserve, + _usersConfig[onBehalfOf], + asset, + amount, + onBehalfOf, + referralCode + ); } function _executeWithdraw( @@ -834,56 +684,18 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { uint256 amount, address to ) internal returns (uint256) { - DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; - DataTypes.ReserveCache memory reserveCache = reserve.cache(); - - reserve.updateState(reserveCache); - - uint256 userBalance = - IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( - reserveCache.nextLiquidityIndex + return + PoolBaseLogic._executeWithdraw( + _reserves, + userConfig, + asset, + amount, + to, + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() ); - - uint256 amountToWithdraw = amount; - - if (amount == type(uint256).max) { - amountToWithdraw = userBalance; - } - - ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance); - - reserve.updateInterestRates(reserveCache, asset, 0, amountToWithdraw); - - IAToken(reserveCache.aTokenAddress).burn( - msg.sender, - to, - amountToWithdraw, - reserveCache.nextLiquidityIndex - ); - - if (userConfig.isUsingAsCollateral(reserve.id)) { - if (userConfig.isBorrowingAny()) { - ValidationLogic.validateHFAndLtv( - asset, - msg.sender, - _reserves, - userConfig, - _reservesList, - _reservesCount, - _addressesProvider.getPriceOracle() - ); - } - - if (amountToWithdraw == userBalance) { - userConfig.setUsingAsCollateral(reserve.id, false); - emit ReserveUsedAsCollateralDisabled(asset, msg.sender); - } - } - - emit Withdraw(asset, msg.sender, to, amountToWithdraw); - - return amountToWithdraw; } function _executeRepay( @@ -893,57 +705,18 @@ contract Pool is VersionedInitializable, IPool, PoolStorage { address onBehalfOf ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; - DataTypes.ReserveCache memory reserveCache = reserve.cache(); - - (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); - - DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); - - ValidationLogic.validateRepay( - _lastBorrower, - _lastBorrowTimestamp, - reserveCache, - amount, - interestRateMode, - onBehalfOf, - stableDebt, - variableDebt - ); - - uint256 paybackAmount = - interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt; - - if (amount < paybackAmount) { - paybackAmount = amount; - } - - reserve.updateState(reserveCache); - - if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); - reserveCache.refreshDebt(0, paybackAmount, 0, 0); - } else { - IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( + DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; + return + PoolBaseLogic._executeRepay( + reserve, + userConfig, + asset, + amount, + rateMode, onBehalfOf, - paybackAmount, - reserveCache.nextVariableBorrowIndex + _lastBorrower, + _lastBorrowTimestamp ); - reserveCache.refreshDebt(0, 0, 0, paybackAmount); - } - - reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0); - - if (stableDebt + variableDebt - paybackAmount == 0) { - _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); - } - - IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount); - - IAToken(reserveCache.aTokenAddress).handleRepayment(msg.sender, paybackAmount); - - emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); - - return paybackAmount; } function _addReserveToList(address asset) internal returns (uint8) { diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index d4ba03212..a2a8c033c 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -49,6 +49,7 @@ import { WETH9MockedFactory, WETHGatewayFactory, FlashLiquidationAdapterFactory, + PoolBaseLogic, } from '../types'; import { withSaveAndVerify, @@ -102,19 +103,9 @@ export const deployPoolAddressesProviderRegistry = async (verify?: boolean) => ); export const deployPoolConfigurator = async (verify?: boolean) => { - const poolConfiguratorImpl = await new PoolConfiguratorFactory( - await getFirstSigner() - ).deploy(); - await insertContractAddressInDb( - eContractid.PoolConfiguratorImpl, - poolConfiguratorImpl.address - ); - return withSaveAndVerify( - poolConfiguratorImpl, - eContractid.PoolConfigurator, - [], - verify - ); + const poolConfiguratorImpl = await new PoolConfiguratorFactory(await getFirstSigner()).deploy(); + await insertContractAddressInDb(eContractid.PoolConfiguratorImpl, poolConfiguratorImpl.address); + return withSaveAndVerify(poolConfiguratorImpl, eContractid.PoolConfigurator, [], verify); }; export const deployReserveLogicLibrary = async (verify?: boolean) => @@ -167,13 +158,54 @@ export const deployValidationLogic = async ( return withSaveAndVerify(validationLogic, eContractid.ValidationLogic, [], verify); }; -export const deployAaveLibraries = async ( +export const deployPoolBaseLogic = async ( + validationLogic: Contract, + reserveLogic: Contract, verify?: boolean -): Promise => { +) => { + const poolBaseLogicArtifact = await readArtifact(eContractid.PoolBaseLogic); + const linkedPoolBaseLogicByteCode = linkBytecode(poolBaseLogicArtifact, { + [eContractid.ValidationLogic]: validationLogic.address, + [eContractid.ReserveLogic]: reserveLogic.address, + }); + const poolBaseLogicFactory = await DRE.ethers.getContractFactory( + poolBaseLogicArtifact.abi, + linkedPoolBaseLogicByteCode + ); + const poolBaseLogic = await ( + await poolBaseLogicFactory.connect(await getFirstSigner()).deploy() + ).deployed(); + + return withSaveAndVerify(poolBaseLogic, eContractid.PoolBaseLogic, [], verify); +}; + +export const deployPoolHelperLogic = async ( + validationLogic: Contract, + reserveLogic: Contract, + verify?: boolean +) => { + const poolHelperLogicArtifact = await readArtifact(eContractid.PoolHelperLogic); + const linkedPoolHelperLogicByteCode = linkBytecode(poolHelperLogicArtifact, { + [eContractid.ValidationLogic]: validationLogic.address, + [eContractid.ReserveLogic]: reserveLogic.address, + }); + const poolHelperLogicFactory = await DRE.ethers.getContractFactory( + poolHelperLogicArtifact.abi, + linkedPoolHelperLogicByteCode + ); + const poolHelperLogic = await ( + await poolHelperLogicFactory.connect(await getFirstSigner()).deploy() + ).deployed(); + + return withSaveAndVerify(poolHelperLogic, eContractid.PoolHelperLogic, [], verify); +}; + +export const deployAaveLibraries = async (verify?: boolean): Promise => { const reserveLogic = await deployReserveLogicLibrary(verify); const genericLogic = await deployGenericLogic(reserveLogic, verify); const validationLogic = await deployValidationLogic(reserveLogic, genericLogic, verify); - + const poolBaseLogic = await deployPoolBaseLogic(validationLogic, reserveLogic, verify); + const poolHelperLogic = await deployPoolHelperLogic(validationLogic, reserveLogic, verify); // Hardcoded solidity placeholders, if any library changes path this will fail. // The '__$PLACEHOLDER$__ can be calculated via solidity keccak, but the PoolLibraryAddresses Type seems to // require a hardcoded string. @@ -186,6 +218,8 @@ export const deployAaveLibraries = async ( // libPath example: contracts/libraries/logic/GenericLogic.sol // libName example: GenericLogic return { + ['__$56265c55042e83ee819cd4de36b013885b$__']: poolHelperLogic.address, + ['__$f5cc2bc164fcad054d46ecbbc8bf13ff3e$__']: poolBaseLogic.address, ['__$de8c0cf1a7d7c36c802af9a64fb9d86036$__']: validationLogic.address, ['__$22cd43a9dda9ce44e9b92ba393b88fb9ac$__']: reserveLogic.address, ['__$52a8a86ab43135662ff256bbc95497e8e3$__']: genericLogic.address, @@ -242,12 +276,7 @@ export const deployPoolCollateralManager = async (verify?: boolean) => { eContractid.PoolCollateralManagerImpl, collateralManagerImpl.address ); - return withSaveAndVerify( - collateralManagerImpl, - eContractid.PoolCollateralManager, - [], - verify - ); + return withSaveAndVerify(collateralManagerImpl, eContractid.PoolCollateralManager, [], verify); }; export const deployInitializableAdminUpgradeabilityProxy = async (verify?: boolean) => @@ -518,13 +547,8 @@ export const deployWETHGateway = async (args: [tEthereumAddress], verify?: boole verify ); -export const authorizeWETHGateway = async ( - wethGateWay: tEthereumAddress, - pool: tEthereumAddress -) => - await new WETHGatewayFactory(await getFirstSigner()) - .attach(wethGateWay) - .authorizePool(pool); +export const authorizeWETHGateway = async (wethGateWay: tEthereumAddress, pool: tEthereumAddress) => + await new WETHGatewayFactory(await getFirstSigner()).attach(wethGateWay).authorizePool(pool); export const deployMockStableDebtToken = async ( args: [tEthereumAddress, tEthereumAddress, tEthereumAddress, string, string, string], diff --git a/helpers/types.ts b/helpers/types.ts index 76ea843bb..74a7a32bd 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -51,6 +51,8 @@ export enum eContractid { ValidationLogic = 'ValidationLogic', ReserveLogic = 'ReserveLogic', GenericLogic = 'GenericLogic', + PoolBaseLogic = 'PoolBaseLogic', + PoolHelperLogic = 'PoolHelperLogic', Pool = 'Pool', PriceOracle = 'PriceOracle', Proxy = 'Proxy', diff --git a/package.json b/package.json index 4357f3bca..4a739aa18 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ ], "scripts": { "size": "npm run compile && npm run hardhat size-contracts", - "small:test": "npm run compile && TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-aave/__setup.spec.ts ./test-suites/test-aave/atoken-permit.spec.ts", + "small:test": "npm run compile && TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-aave/__setup.spec.ts ./test-suites/test-aave/flashloan.spec.ts", "run-env": "npm i && tail -f /dev/null", "hardhat": "hardhat", "hardhat:kovan": "hardhat --network kovan",