Branch data Line data Source code
1 : : // SPDX-License-Identifier: BUSL-1.1
2 : : pragma solidity ^0.8.10;
3 : :
4 : : import {DataTypes} from '../../protocol/libraries/types/DataTypes.sol';
5 : : import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
6 : : import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
7 : : import {IERC20WithPermit} from '../../interfaces/IERC20WithPermit.sol';
8 : : import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';
9 : : import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
10 : : import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
11 : : import {BaseParaSwapBuyAdapter} from './BaseParaSwapBuyAdapter.sol';
12 : : import {IParaSwapAugustusRegistry} from './interfaces/IParaSwapAugustusRegistry.sol';
13 : : import {IParaSwapAugustus} from './interfaces/IParaSwapAugustus.sol';
14 : : import {ReentrancyGuard} from '../../dependencies/openzeppelin/ReentrancyGuard.sol';
15 : :
16 : : /**
17 : : * @title ParaSwapRepayAdapter
18 : : * @notice ParaSwap Adapter to perform a repay of a debt with collateral.
19 : : * @author Aave
20 : : **/
21 : : contract ParaSwapRepayAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard {
22 : : using SafeMath for uint256;
23 : : using SafeERC20 for IERC20;
24 : :
25 : : struct RepayParams {
26 : : address collateralAsset;
27 : : uint256 collateralAmount;
28 : : uint256 rateMode;
29 : : PermitSignature permitSignature;
30 : : bool useEthPath;
31 : : }
32 : :
33 : : constructor(
34 : : IPoolAddressesProvider addressesProvider,
35 : : IParaSwapAugustusRegistry augustusRegistry,
36 : : address owner
37 : : ) BaseParaSwapBuyAdapter(addressesProvider, augustusRegistry) {
38 : 0 : transferOwnership(owner);
39 : : }
40 : :
41 : : /**
42 : : * @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls
43 : : * the collateral from the user and swaps it to the debt asset to repay the flash loan.
44 : : * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it
45 : : * and repay the flash loan.
46 : : * Supports only one asset on the flash loan.
47 : : * @param asset The address of the flash-borrowed asset
48 : : * @param amount The amount of the flash-borrowed asset
49 : : * @param premium The fee of the flash-borrowed asset
50 : : * @param initiator The address of the flashloan initiator
51 : : * @param params The byte-encoded params passed when initiating the flashloan
52 : : * @return True if the execution of the operation succeeds, false otherwise
53 : : * IERC20Detailed debtAsset Address of the debt asset
54 : : * uint256 debtAmount Amount of debt to be repaid
55 : : * uint256 rateMode Rate modes of the debt to be repaid
56 : : * uint256 deadline Deadline for the permit signature
57 : : * uint256 debtRateMode Rate mode of the debt to be repaid
58 : : * bytes paraswapData Paraswap Data
59 : : * * bytes buyCallData Call data for augustus
60 : : * * IParaSwapAugustus augustus Address of Augustus Swapper
61 : : * PermitSignature permitParams Struct containing the permit signatures, set to all zeroes if not used
62 : : */
63 : : function executeOperation(
64 : : address asset,
65 : : uint256 amount,
66 : : uint256 premium,
67 : : address initiator,
68 : : bytes calldata params
69 : : ) external override nonReentrant returns (bool) {
70 : 2 : require(msg.sender == address(POOL), 'CALLER_MUST_BE_POOL');
71 : :
72 : 2 : uint256 collateralAmount = amount;
73 : 2 : address initiatorLocal = initiator;
74 : :
75 : 2 : IERC20Detailed collateralAsset = IERC20Detailed(asset);
76 : :
77 : 2 : _swapAndRepay(params, premium, initiatorLocal, collateralAsset, collateralAmount);
78 : :
79 : 2 : return true;
80 : : }
81 : :
82 : : /**
83 : : * @dev Swaps the user collateral for the debt asset and then repay the debt on the protocol on behalf of the user
84 : : * without using flash loans. This method can be used when the temporary transfer of the collateral asset to this
85 : : * contract does not affect the user position.
86 : : * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset
87 : : * @param collateralAsset Address of asset to be swapped
88 : : * @param debtAsset Address of debt asset
89 : : * @param collateralAmount max Amount of the collateral to be swapped
90 : : * @param debtRepayAmount Amount of the debt to be repaid, or maximum amount when repaying entire debt
91 : : * @param debtRateMode Rate mode of the debt to be repaid
92 : : * @param buyAllBalanceOffset Set to offset of toAmount in Augustus calldata if wanting to pay entire debt, otherwise 0
93 : : * @param paraswapData Data for Paraswap Adapter
94 : : * @param permitSignature struct containing the permit signature
95 : : */
96 : : function swapAndRepay(
97 : : IERC20Detailed collateralAsset,
98 : : IERC20Detailed debtAsset,
99 : : uint256 collateralAmount,
100 : : uint256 debtRepayAmount,
101 : : uint256 debtRateMode,
102 : : uint256 buyAllBalanceOffset,
103 : : bytes calldata paraswapData,
104 : : PermitSignature calldata permitSignature
105 : : ) external nonReentrant {
106 : 4 : debtRepayAmount = getDebtRepayAmount(
107 : : debtAsset,
108 : : debtRateMode,
109 : : buyAllBalanceOffset,
110 : : debtRepayAmount,
111 : : msg.sender
112 : : );
113 : :
114 : : // Pull aTokens from user
115 : 4 : _pullATokenAndWithdraw(address(collateralAsset), msg.sender, collateralAmount, permitSignature);
116 : : //buy debt asset using collateral asset
117 : 4 : (uint256 amountSold, uint256 amountBought) = _buyOnParaSwap(
118 : : buyAllBalanceOffset,
119 : : paraswapData,
120 : : collateralAsset,
121 : : debtAsset,
122 : : collateralAmount,
123 : : debtRepayAmount
124 : : );
125 : :
126 : 3 : uint256 collateralBalanceLeft = collateralAmount - amountSold;
127 : :
128 : : //deposit collateral back in the pool, if left after the swap(buy)
129 : 3 : if (collateralBalanceLeft > 0) {
130 : 1 : IERC20(collateralAsset).safeApprove(address(POOL), collateralBalanceLeft);
131 : 1 : POOL.deposit(address(collateralAsset), collateralBalanceLeft, msg.sender, 0);
132 : 1 : IERC20(collateralAsset).safeApprove(address(POOL), 0);
133 : : }
134 : :
135 : : // Repay debt. Approves 0 first to comply with tokens that implement the anti frontrunning approval fix
136 : 3 : IERC20(debtAsset).safeApprove(address(POOL), debtRepayAmount);
137 : 3 : POOL.repay(address(debtAsset), debtRepayAmount, debtRateMode, msg.sender);
138 : 3 : IERC20(debtAsset).safeApprove(address(POOL), 0);
139 : :
140 : : {
141 : : //transfer excess of debtAsset back to the user, if any
142 : 3 : uint256 debtAssetExcess = amountBought - debtRepayAmount;
143 : 3 : if (debtAssetExcess > 0) {
144 : 0 : IERC20(debtAsset).safeTransfer(msg.sender, debtAssetExcess);
145 : : }
146 : : }
147 : : }
148 : :
149 : : /**
150 : : * @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan
151 : : * @param premium Fee of the flash loan
152 : : * @param initiator Address of the user
153 : : * @param collateralAsset Address of token to be swapped
154 : : * @param collateralAmount Amount of the reserve to be swapped(flash loan amount)
155 : : */
156 : :
157 : : function _swapAndRepay(
158 : : bytes calldata params,
159 : : uint256 premium,
160 : : address initiator,
161 : : IERC20Detailed collateralAsset,
162 : : uint256 collateralAmount
163 : : ) private {
164 : 2 : (
165 : : IERC20Detailed debtAsset,
166 : : uint256 debtRepayAmount,
167 : : uint256 buyAllBalanceOffset,
168 : : uint256 rateMode,
169 : : bytes memory paraswapData,
170 : : PermitSignature memory permitSignature
171 : 2 : ) = abi.decode(params, (IERC20Detailed, uint256, uint256, uint256, bytes, PermitSignature));
172 : :
173 : 2 : debtRepayAmount = getDebtRepayAmount(
174 : : debtAsset,
175 : : rateMode,
176 : : buyAllBalanceOffset,
177 : : debtRepayAmount,
178 : : initiator
179 : : );
180 : :
181 : 2 : (uint256 amountSold, uint256 amountBought) = _buyOnParaSwap(
182 : : buyAllBalanceOffset,
183 : : paraswapData,
184 : : collateralAsset,
185 : : debtAsset,
186 : : collateralAmount,
187 : : debtRepayAmount
188 : : );
189 : :
190 : : // Repay debt. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
191 : 2 : IERC20(debtAsset).safeApprove(address(POOL), debtRepayAmount);
192 : 2 : POOL.repay(address(debtAsset), debtRepayAmount, rateMode, initiator);
193 : 2 : IERC20(debtAsset).safeApprove(address(POOL), 0);
194 : :
195 : 2 : uint256 neededForFlashLoanRepay = amountSold.add(premium);
196 : :
197 : : // Pull aTokens from user
198 : 2 : _pullATokenAndWithdraw(
199 : : address(collateralAsset),
200 : : initiator,
201 : : neededForFlashLoanRepay,
202 : : permitSignature
203 : : );
204 : :
205 : : {
206 : : //transfer excess of debtAsset back to the user, if any
207 : 2 : uint256 debtAssetExcess = amountBought - debtRepayAmount;
208 : 2 : if (debtAssetExcess > 0) {
209 : 0 : IERC20(debtAsset).safeTransfer(initiator, debtAssetExcess);
210 : : }
211 : : }
212 : :
213 : : // Repay flashloan. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix.
214 : 2 : IERC20(collateralAsset).safeApprove(address(POOL), 0);
215 : 2 : IERC20(collateralAsset).safeApprove(address(POOL), collateralAmount.add(premium));
216 : : }
217 : :
218 : : function getDebtRepayAmount(
219 : : IERC20Detailed debtAsset,
220 : : uint256 rateMode,
221 : : uint256 buyAllBalanceOffset,
222 : : uint256 debtRepayAmount,
223 : : address initiator
224 : : ) private view returns (uint256) {
225 : 6 : require(
226 : : DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.VARIABLE,
227 : : 'INVALID_RATE_MODE'
228 : : );
229 : 6 : DataTypes.ReserveDataLegacy memory debtReserveData = _getReserveData(address(debtAsset));
230 : :
231 : 6 : uint256 currentDebt = IERC20(debtReserveData.variableDebtTokenAddress).balanceOf(initiator);
232 : :
233 : 6 : if (buyAllBalanceOffset != 0) {
234 : 1 : require(currentDebt <= debtRepayAmount, 'INSUFFICIENT_AMOUNT_TO_REPAY');
235 : 1 : debtRepayAmount = currentDebt;
236 : : } else {
237 : 5 : require(debtRepayAmount <= currentDebt, 'INVALID_DEBT_REPAY_AMOUNT');
238 : : }
239 : :
240 : 6 : return debtRepayAmount;
241 : : }
242 : : }
|