Skip to content

Commit

Permalink
feat: Split pool logic into libraries
Browse files Browse the repository at this point in the history
- Add `PoolBaseLogic`
- Add `PoolHelperLogic`
- Move `ExecuteBorrowParams` to `DataTypes`
- Update Pool to use libraries
  • Loading branch information
LHerskind committed Aug 2, 2021
1 parent a72b5ad commit a78e6cc
Show file tree
Hide file tree
Showing 7 changed files with 562 additions and 305 deletions.
298 changes: 298 additions & 0 deletions contracts/protocol/libraries/logic/PoolBaseLogic.sol
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit a78e6cc

Please sign in to comment.