Branch data Line data Source code
1 : : // SPDX-License-Identifier: BUSL-1.1
2 : : pragma solidity ^0.8.10;
3 : :
4 : : import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
5 : : import {GPv2SafeERC20} from '../../../dependencies/gnosis/contracts/GPv2SafeERC20.sol';
6 : : import {IAToken} from '../../../interfaces/IAToken.sol';
7 : : import {Errors} from '../helpers/Errors.sol';
8 : : import {UserConfiguration} from '../configuration/UserConfiguration.sol';
9 : : import {DataTypes} from '../types/DataTypes.sol';
10 : : import {WadRayMath} from '../math/WadRayMath.sol';
11 : : import {PercentageMath} from '../math/PercentageMath.sol';
12 : : import {ValidationLogic} from './ValidationLogic.sol';
13 : : import {ReserveLogic} from './ReserveLogic.sol';
14 : : import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
15 : :
16 : : /**
17 : : * @title SupplyLogic library
18 : : * @author Aave
19 : : * @notice Implements the base logic for supply/withdraw
20 : : */
21 : : library SupplyLogic {
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 WadRayMath for uint256;
28 : : using PercentageMath for uint256;
29 : :
30 : : // See `IPool` for descriptions
31 : : event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
32 : : event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
33 : : event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
34 : : event Supply(
35 : : address indexed reserve,
36 : : address user,
37 : : address indexed onBehalfOf,
38 : : uint256 amount,
39 : : uint16 indexed referralCode
40 : : );
41 : :
42 : : /**
43 : : * @notice Implements the supply feature. Through `supply()`, users supply assets to the Aave protocol.
44 : : * @dev Emits the `Supply()` event.
45 : : * @dev In the first supply action, `ReserveUsedAsCollateralEnabled()` is emitted, if the asset can be enabled as
46 : : * collateral.
47 : : * @param reservesData The state of all the reserves
48 : : * @param reservesList The addresses of all the active reserves
49 : : * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
50 : : * @param params The additional parameters needed to execute the supply function
51 : : */
52 : : function executeSupply(
53 : : mapping(address => DataTypes.ReserveData) storage reservesData,
54 : : mapping(uint256 => address) storage reservesList,
55 : : DataTypes.UserConfigurationMap storage userConfig,
56 : : DataTypes.ExecuteSupplyParams memory params
57 : : ) external {
58 : 44515 : DataTypes.ReserveData storage reserve = reservesData[params.asset];
59 : 44515 : DataTypes.ReserveCache memory reserveCache = reserve.cache();
60 : :
61 : 44515 : reserve.updateState(reserveCache);
62 : :
63 : 44515 : ValidationLogic.validateSupply(reserveCache, reserve, params.amount, params.onBehalfOf);
64 : :
65 : 44507 : reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, params.amount, 0);
66 : :
67 : 44506 : IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount);
68 : :
69 : 43506 : bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
70 : : msg.sender,
71 : : params.onBehalfOf,
72 : : params.amount,
73 : : reserveCache.nextLiquidityIndex
74 : : );
75 : :
76 : 43506 : if (isFirstSupply) {
77 : : if (
78 : 43501 : ValidationLogic.validateAutomaticUseAsCollateral(
79 : : reservesData,
80 : : reservesList,
81 : : userConfig,
82 : : reserveCache.reserveConfiguration,
83 : : reserveCache.aTokenAddress
84 : : )
85 : : ) {
86 : 43488 : userConfig.setUsingAsCollateral(reserve.id, true);
87 : 43488 : emit ReserveUsedAsCollateralEnabled(params.asset, params.onBehalfOf);
88 : : }
89 : : }
90 : :
91 : 43506 : emit Supply(params.asset, msg.sender, params.onBehalfOf, params.amount, params.referralCode);
92 : : }
93 : :
94 : : /**
95 : : * @notice Implements the withdraw feature. Through `withdraw()`, users redeem their aTokens for the underlying asset
96 : : * previously supplied in the Aave protocol.
97 : : * @dev Emits the `Withdraw()` event.
98 : : * @dev If the user withdraws everything, `ReserveUsedAsCollateralDisabled()` is emitted.
99 : : * @param reservesData The state of all the reserves
100 : : * @param reservesList The addresses of all the active reserves
101 : : * @param eModeCategories The configuration of all the efficiency mode categories
102 : : * @param userConfig The user configuration mapping that tracks the supplied/borrowed assets
103 : : * @param params The additional parameters needed to execute the withdraw function
104 : : * @return The actual amount withdrawn
105 : : */
106 : : function executeWithdraw(
107 : : mapping(address => DataTypes.ReserveData) storage reservesData,
108 : : mapping(uint256 => address) storage reservesList,
109 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
110 : : DataTypes.UserConfigurationMap storage userConfig,
111 : : DataTypes.ExecuteWithdrawParams memory params
112 : : ) external returns (uint256) {
113 : 2045 : DataTypes.ReserveData storage reserve = reservesData[params.asset];
114 : 2045 : DataTypes.ReserveCache memory reserveCache = reserve.cache();
115 : :
116 : 2045 : require(params.to != reserveCache.aTokenAddress, Errors.WITHDRAW_TO_ATOKEN);
117 : :
118 : 2044 : reserve.updateState(reserveCache);
119 : :
120 : 2044 : uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul(
121 : : reserveCache.nextLiquidityIndex
122 : : );
123 : :
124 : 2044 : uint256 amountToWithdraw = params.amount;
125 : :
126 : 2044 : if (params.amount == type(uint256).max) {
127 : 5 : amountToWithdraw = userBalance;
128 : : }
129 : :
130 : 2044 : ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance);
131 : :
132 : 2040 : reserve.updateInterestRatesAndVirtualBalance(reserveCache, params.asset, 0, amountToWithdraw);
133 : :
134 : 2037 : bool isCollateral = userConfig.isUsingAsCollateral(reserve.id);
135 : :
136 : 2037 : if (isCollateral && amountToWithdraw == userBalance) {
137 : 2030 : userConfig.setUsingAsCollateral(reserve.id, false);
138 : 2030 : emit ReserveUsedAsCollateralDisabled(params.asset, msg.sender);
139 : : }
140 : :
141 : 2037 : IAToken(reserveCache.aTokenAddress).burn(
142 : : msg.sender,
143 : : params.to,
144 : : amountToWithdraw,
145 : : reserveCache.nextLiquidityIndex
146 : : );
147 : :
148 : 2037 : if (isCollateral && userConfig.isBorrowingAny()) {
149 : 1 : ValidationLogic.validateHFAndLtv(
150 : : reservesData,
151 : : reservesList,
152 : : eModeCategories,
153 : : userConfig,
154 : : params.asset,
155 : : msg.sender,
156 : : params.reservesCount,
157 : : params.oracle,
158 : : params.userEModeCategory
159 : : );
160 : : }
161 : :
162 : 2036 : emit Withdraw(params.asset, msg.sender, params.to, amountToWithdraw);
163 : :
164 : 2036 : return amountToWithdraw;
165 : : }
166 : :
167 : : /**
168 : : * @notice Validates a transfer of aTokens. The sender is subjected to health factor validation to avoid
169 : : * collateralization constraints violation.
170 : : * @dev Emits the `ReserveUsedAsCollateralEnabled()` event for the `to` account, if the asset is being activated as
171 : : * collateral.
172 : : * @dev In case the `from` user transfers everything, `ReserveUsedAsCollateralDisabled()` is emitted for `from`.
173 : : * @param reservesData The state of all the reserves
174 : : * @param reservesList The addresses of all the active reserves
175 : : * @param eModeCategories The configuration of all the efficiency mode categories
176 : : * @param usersConfig The users configuration mapping that track the supplied/borrowed assets
177 : : * @param params The additional parameters needed to execute the finalizeTransfer function
178 : : */
179 : : function executeFinalizeTransfer(
180 : : mapping(address => DataTypes.ReserveData) storage reservesData,
181 : : mapping(uint256 => address) storage reservesList,
182 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
183 : : mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
184 : : DataTypes.FinalizeTransferParams memory params
185 : : ) external {
186 : 17042 : DataTypes.ReserveData storage reserve = reservesData[params.asset];
187 : :
188 : 17042 : ValidationLogic.validateTransfer(reserve);
189 : :
190 : 17042 : uint256 reserveId = reserve.id;
191 : 17042 : uint256 scaledAmount = params.amount.rayDiv(reserve.getNormalizedIncome());
192 : :
193 : 17042 : if (params.from != params.to && scaledAmount != 0) {
194 : 16898 : DataTypes.UserConfigurationMap storage fromConfig = usersConfig[params.from];
195 : :
196 : 16898 : if (fromConfig.isUsingAsCollateral(reserveId)) {
197 : 16898 : if (fromConfig.isBorrowingAny()) {
198 : 6 : ValidationLogic.validateHFAndLtv(
199 : : reservesData,
200 : : reservesList,
201 : : eModeCategories,
202 : : usersConfig[params.from],
203 : : params.asset,
204 : : params.from,
205 : : params.reservesCount,
206 : : params.oracle,
207 : : params.fromEModeCategory
208 : : );
209 : : }
210 : 16897 : if (params.balanceFromBefore == params.amount) {
211 : 15013 : fromConfig.setUsingAsCollateral(reserveId, false);
212 : 15013 : emit ReserveUsedAsCollateralDisabled(params.asset, params.from);
213 : : }
214 : : }
215 : :
216 : 16897 : if (params.balanceToBefore == 0) {
217 : 15893 : DataTypes.UserConfigurationMap storage toConfig = usersConfig[params.to];
218 : : if (
219 : 15893 : ValidationLogic.validateAutomaticUseAsCollateral(
220 : : reservesData,
221 : : reservesList,
222 : : toConfig,
223 : : reserve.configuration,
224 : : reserve.aTokenAddress
225 : : )
226 : : ) {
227 : 15893 : toConfig.setUsingAsCollateral(reserveId, true);
228 : 15893 : emit ReserveUsedAsCollateralEnabled(params.asset, params.to);
229 : : }
230 : : }
231 : : }
232 : : }
233 : :
234 : : /**
235 : : * @notice Executes the 'set as collateral' feature. A user can choose to activate or deactivate an asset as
236 : : * collateral at any point in time. Deactivating an asset as collateral is subjected to the usual health factor
237 : : * checks to ensure collateralization.
238 : : * @dev Emits the `ReserveUsedAsCollateralEnabled()` event if the asset can be activated as collateral.
239 : : * @dev In case the asset is being deactivated as collateral, `ReserveUsedAsCollateralDisabled()` is emitted.
240 : : * @param reservesData The state of all the reserves
241 : : * @param reservesList The addresses of all the active reserves
242 : : * @param eModeCategories The configuration of all the efficiency mode categories
243 : : * @param userConfig The users configuration mapping that track the supplied/borrowed assets
244 : : * @param asset The address of the asset being configured as collateral
245 : : * @param useAsCollateral True if the user wants to set the asset as collateral, false otherwise
246 : : * @param reservesCount The number of initialized reserves
247 : : * @param priceOracle The address of the price oracle
248 : : * @param userEModeCategory The eMode category chosen by the user
249 : : */
250 : : function executeUseReserveAsCollateral(
251 : : mapping(address => DataTypes.ReserveData) storage reservesData,
252 : : mapping(uint256 => address) storage reservesList,
253 : : mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
254 : : DataTypes.UserConfigurationMap storage userConfig,
255 : : address asset,
256 : : bool useAsCollateral,
257 : : uint256 reservesCount,
258 : : address priceOracle,
259 : : uint8 userEModeCategory
260 : : ) external {
261 : 43 : DataTypes.ReserveData storage reserve = reservesData[asset];
262 : 43 : DataTypes.ReserveCache memory reserveCache = reserve.cache();
263 : :
264 : 43 : uint256 userBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender);
265 : :
266 : 43 : ValidationLogic.validateSetUseReserveAsCollateral(reserveCache, userBalance);
267 : :
268 : 37 : if (useAsCollateral == userConfig.isUsingAsCollateral(reserve.id)) return;
269 : :
270 : 35 : if (useAsCollateral) {
271 : 19 : require(
272 : : ValidationLogic.validateUseAsCollateral(
273 : : reservesData,
274 : : reservesList,
275 : : userConfig,
276 : : reserveCache.reserveConfiguration
277 : : ),
278 : : Errors.USER_IN_ISOLATION_MODE_OR_LTV_ZERO
279 : : );
280 : :
281 : 15 : userConfig.setUsingAsCollateral(reserve.id, true);
282 : 15 : emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
283 : : } else {
284 : 16 : userConfig.setUsingAsCollateral(reserve.id, false);
285 : 16 : ValidationLogic.validateHFAndLtv(
286 : : reservesData,
287 : : reservesList,
288 : : eModeCategories,
289 : : userConfig,
290 : : asset,
291 : : msg.sender,
292 : : reservesCount,
293 : : priceOracle,
294 : : userEModeCategory
295 : : );
296 : :
297 : 14 : emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
298 : : }
299 : : }
300 : : }
|