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