Branch data Line data Source code
1 : : // SPDX-License-Identifier: BUSL-1.1
2 : : pragma solidity ^0.8.10;
3 : :
4 : : import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
5 : : import {SafeCast} from '../../../dependencies/openzeppelin/contracts/SafeCast.sol';
6 : : import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
7 : : import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
8 : : import {IAToken} from '../../../interfaces/IAToken.sol';
9 : : import {UserConfiguration} from '../configuration/UserConfiguration.sol';
10 : : import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
11 : : import {DataTypes} from '../types/DataTypes.sol';
12 : : import {ValidationLogic} from './ValidationLogic.sol';
13 : : import {ReserveLogic} from './ReserveLogic.sol';
14 : : import {IsolationModeLogic} from './IsolationModeLogic.sol';
15 : :
16 : : /**
17 : : * @title BorrowLogic library
18 : : * @author Aave
19 : : * @notice Implements the base logic for all the actions related to borrowing
20 : : */
21 : : library BorrowLogic {
22 : : using ReserveLogic for DataTypes.ReserveCache;
23 : : using ReserveLogic for DataTypes.ReserveData;
24 : : using GPv2SafeERC20 for IERC20;
25 : : using UserConfiguration for DataTypes.UserConfigurationMap;
26 : : using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
27 : : using SafeCast for uint256;
28 : :
29 : : // See `IPool` for descriptions
30 : : event Borrow(
31 : : address indexed reserve,
32 : : address user,
33 : : address indexed onBehalfOf,
34 : : uint256 amount,
35 : : DataTypes.InterestRateMode interestRateMode,
36 : : uint256 borrowRate,
37 : : uint16 indexed referralCode
38 : : );
39 : : event Repay(
40 : : address indexed reserve,
41 : : address indexed user,
42 : : address indexed repayer,
43 : : uint256 amount,
44 : : bool useATokens
45 : : );
46 : : event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
47 : : event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
48 : :
49 : : /**
50 : : * @notice Implements the borrow feature. Borrowing allows users that provided collateral to draw liquidity from the
51 : : * Aave protocol proportionally to their collateralization power. For isolated positions, it also increases the
52 : : * isolated debt.
53 : : * @dev Emits the `Borrow()` event
54 : : * @param reservesData The state of all the reserves
55 : : * @param reservesList The addresses of all the active reserves
56 : : * @param eModeCategories The configuration of all the efficiency mode categories
57 : : * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
58 : : * @param params The additional parameters needed to execute the borrow function
59 : : */
60 : : function executeBorrow(
61 : : mapping(address => DataTypes.ReserveData) storage reservesData,
62 : : mapping(uint256 => address) storage reservesList,
63 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
64 : : DataTypes.UserConfigurationMap storage userConfig,
65 : : DataTypes.ExecuteBorrowParams memory params
66 : : ) external {
67 : 16110 : DataTypes.ReserveData storage reserve = reservesData[params.asset];
68 : 16110 : DataTypes.ReserveCache memory reserveCache = reserve.cache();
69 : :
70 : 16110 : reserve.updateState(reserveCache);
71 : :
72 : 16110 : (
73 : : bool isolationModeActive,
74 : : address isolationModeCollateralAddress,
75 : : uint256 isolationModeDebtCeiling
76 : 16110 : ) = userConfig.getIsolationModeState(reservesData, reservesList);
77 : :
78 : 16110 : ValidationLogic.validateBorrow(
79 : : reservesData,
80 : : reservesList,
81 : : eModeCategories,
82 : : DataTypes.ValidateBorrowParams({
83 : : reserveCache: reserveCache,
84 : : userConfig: userConfig,
85 : : asset: params.asset,
86 : : userAddress: params.onBehalfOf,
87 : : amount: params.amount,
88 : : interestRateMode: params.interestRateMode,
89 : : reservesCount: params.reservesCount,
90 : : oracle: params.oracle,
91 : : userEModeCategory: params.userEModeCategory,
92 : : priceOracleSentinel: params.priceOracleSentinel,
93 : : isolationModeActive: isolationModeActive,
94 : : isolationModeCollateralAddress: isolationModeCollateralAddress,
95 : : isolationModeDebtCeiling: isolationModeDebtCeiling
96 : : })
97 : : );
98 : :
99 : 15091 : bool isFirstBorrowing = false;
100 : :
101 : 15091 : (isFirstBorrowing, reserveCache.nextScaledVariableDebt) = IVariableDebtToken(
102 : : reserveCache.variableDebtTokenAddress
103 : : ).mint(params.user, params.onBehalfOf, params.amount, reserveCache.nextVariableBorrowIndex);
104 : :
105 : 15091 : if (isFirstBorrowing) {
106 : 15090 : userConfig.setBorrowing(reserve.id, true);
107 : : }
108 : :
109 : 15091 : if (isolationModeActive) {
110 : 8 : uint256 nextIsolationModeTotalDebt = reservesData[isolationModeCollateralAddress]
111 : : .isolationModeTotalDebt += (params.amount /
112 : : 10 **
113 : : (reserveCache.reserveConfiguration.getDecimals() -
114 : : ReserveConfiguration.DEBT_CEILING_DECIMALS)).toUint128();
115 : 8 : emit IsolationModeTotalDebtUpdated(
116 : : isolationModeCollateralAddress,
117 : : nextIsolationModeTotalDebt
118 : : );
119 : : }
120 : :
121 : 15091 : reserve.updateInterestRatesAndVirtualBalance(
122 : : reserveCache,
123 : : params.asset,
124 : : 0,
125 : : params.releaseUnderlying ? params.amount : 0
126 : : );
127 : :
128 : 15090 : if (params.releaseUnderlying) {
129 : 15089 : IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(params.user, params.amount);
130 : : }
131 : :
132 : 15090 : emit Borrow(
133 : : params.asset,
134 : : params.user,
135 : : params.onBehalfOf,
136 : : params.amount,
137 : : DataTypes.InterestRateMode.VARIABLE,
138 : : reserve.currentVariableBorrowRate,
139 : : params.referralCode
140 : : );
141 : : }
142 : :
143 : : /**
144 : : * @notice Implements the repay feature. Repaying transfers the underlying back to the aToken and clears the
145 : : * equivalent amount of debt for the user by burning the corresponding debt token. For isolated positions, it also
146 : : * reduces the isolated debt.
147 : : * @dev Emits the `Repay()` event
148 : : * @param reservesData The state of all the reserves
149 : : * @param reservesList The addresses of all the active reserves
150 : : * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
151 : : * @param params The additional parameters needed to execute the repay function
152 : : * @return The actual amount being repaid
153 : : */
154 : : function executeRepay(
155 : : mapping(address => DataTypes.ReserveData) storage reservesData,
156 : : mapping(uint256 => address) storage reservesList,
157 : : DataTypes.UserConfigurationMap storage userConfig,
158 : : DataTypes.ExecuteRepayParams memory params
159 : : ) external returns (uint256) {
160 : 5030 : DataTypes.ReserveData storage reserve = reservesData[params.asset];
161 : 5030 : DataTypes.ReserveCache memory reserveCache = reserve.cache();
162 : 5030 : reserve.updateState(reserveCache);
163 : :
164 : 5030 : uint256 variableDebt = IERC20(reserveCache.variableDebtTokenAddress).balanceOf(
165 : : params.onBehalfOf
166 : : );
167 : :
168 : 5030 : ValidationLogic.validateRepay(
169 : : reserveCache,
170 : : params.amount,
171 : : params.interestRateMode,
172 : : params.onBehalfOf,
173 : : variableDebt
174 : : );
175 : :
176 : 5024 : uint256 paybackAmount = variableDebt;
177 : :
178 : : // Allows a user to repay with aTokens without leaving dust from interest.
179 : 5024 : if (params.useATokens && params.amount == type(uint256).max) {
180 : 4 : params.amount = IAToken(reserveCache.aTokenAddress).balanceOf(msg.sender);
181 : : }
182 : :
183 : 5024 : if (params.amount < paybackAmount) {
184 : 4971 : paybackAmount = params.amount;
185 : : }
186 : :
187 : 5024 : reserveCache.nextScaledVariableDebt = IVariableDebtToken(reserveCache.variableDebtTokenAddress)
188 : : .burn(params.onBehalfOf, paybackAmount, reserveCache.nextVariableBorrowIndex);
189 : :
190 : 5024 : reserve.updateInterestRatesAndVirtualBalance(
191 : : reserveCache,
192 : : params.asset,
193 : : params.useATokens ? 0 : paybackAmount,
194 : : 0
195 : : );
196 : :
197 : 5024 : if (variableDebt - paybackAmount == 0) {
198 : 53 : userConfig.setBorrowing(reserve.id, false);
199 : : }
200 : :
201 : 5024 : IsolationModeLogic.updateIsolatedDebtIfIsolated(
202 : : reservesData,
203 : : reservesList,
204 : : userConfig,
205 : : reserveCache,
206 : : paybackAmount
207 : : );
208 : :
209 : 5024 : if (params.useATokens) {
210 : 1006 : IAToken(reserveCache.aTokenAddress).burn(
211 : : msg.sender,
212 : : reserveCache.aTokenAddress,
213 : : paybackAmount,
214 : : reserveCache.nextLiquidityIndex
215 : : );
216 : : // in case of aToken repayment the msg.sender must always repay on behalf of itself
217 : 1005 : if (IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender) == 0) {
218 : 2 : userConfig.setUsingAsCollateral(reserve.id, false);
219 : 2 : emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
220 : : }
221 : : } else {
222 : 4018 : IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount);
223 : 3018 : IAToken(reserveCache.aTokenAddress).handleRepayment(
224 : : msg.sender,
225 : : params.onBehalfOf,
226 : : paybackAmount
227 : : );
228 : : }
229 : :
230 : 4023 : emit Repay(params.asset, params.onBehalfOf, msg.sender, paybackAmount, params.useATokens);
231 : :
232 : 4023 : return paybackAmount;
233 : : }
234 : : }
|