Branch data Line data Source code
1 : : // SPDX-License-Identifier: BUSL-1.1
2 : : pragma solidity ^0.8.10;
3 : :
4 : : import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
5 : : import {IERC20WithPermit} from '../../interfaces/IERC20WithPermit.sol';
6 : : import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol';
7 : : import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
8 : : import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
9 : : import {BaseParaSwapSellAdapter} from './BaseParaSwapSellAdapter.sol';
10 : : import {IParaSwapAugustusRegistry} from './interfaces/IParaSwapAugustusRegistry.sol';
11 : : import {IParaSwapAugustus} from './interfaces/IParaSwapAugustus.sol';
12 : : import {ReentrancyGuard} from '../../dependencies/openzeppelin/ReentrancyGuard.sol';
13 : :
14 : : /**
15 : : * @title ParaSwapLiquiditySwapAdapter
16 : : * @notice Adapter to swap liquidity using ParaSwap.
17 : : * @author Jason Raymond Bell
18 : : */
19 : : contract ParaSwapLiquiditySwapAdapter is BaseParaSwapSellAdapter, ReentrancyGuard {
20 : : using SafeMath for uint256;
21 : : using SafeERC20 for IERC20Detailed;
22 : :
23 : : constructor(
24 : : IPoolAddressesProvider addressesProvider,
25 : : IParaSwapAugustusRegistry augustusRegistry,
26 : : address owner
27 : : ) BaseParaSwapSellAdapter(addressesProvider, augustusRegistry) {
28 : 0 : transferOwnership(owner);
29 : : }
30 : :
31 : : /**
32 : : * @dev Swaps the received reserve amount from the flash loan into the asset specified in the params.
33 : : * The received funds from the swap are then deposited into the protocol on behalf of the user.
34 : : * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and repay the flash loan.
35 : : * @param asset The address of the flash-borrowed asset
36 : : * @param amount The amount of the flash-borrowed asset
37 : : * @param premium The fee of the flash-borrowed asset
38 : : * @param initiator The address of the flashloan initiator
39 : : * @param params The byte-encoded params passed when initiating the flashloan
40 : : * @return True if the execution of the operation succeeds, false otherwise
41 : : * address assetToSwapTo Address of the underlying asset to be swapped to and deposited
42 : : * uint256 minAmountToReceive Min amount to be received from the swap
43 : : * uint256 swapAllBalanceOffset Set to offset of fromAmount in Augustus calldata if wanting to swap all balance, otherwise 0
44 : : * bytes swapCalldata Calldata for ParaSwap's AugustusSwapper contract
45 : : * address augustus Address of ParaSwap's AugustusSwapper contract
46 : : * PermitSignature permitParams Struct containing the permit signatures, set to all zeroes if not used
47 : : */
48 : : function executeOperation(
49 : : address asset,
50 : : uint256 amount,
51 : : uint256 premium,
52 : : address initiator,
53 : : bytes calldata params
54 : : ) external override nonReentrant returns (bool) {
55 : 3 : require(msg.sender == address(POOL), 'CALLER_MUST_BE_POOL');
56 : :
57 : 3 : uint256 flashLoanAmount = amount;
58 : 3 : uint256 premiumLocal = premium;
59 : 3 : address initiatorLocal = initiator;
60 : 3 : IERC20Detailed assetToSwapFrom = IERC20Detailed(asset);
61 : 3 : (
62 : : IERC20Detailed assetToSwapTo,
63 : : uint256 minAmountToReceive,
64 : : uint256 swapAllBalanceOffset,
65 : : bytes memory swapCalldata,
66 : : IParaSwapAugustus augustus,
67 : : PermitSignature memory permitParams
68 : 3 : ) = abi.decode(
69 : : params,
70 : : (IERC20Detailed, uint256, uint256, bytes, IParaSwapAugustus, PermitSignature)
71 : : );
72 : :
73 : 3 : _swapLiquidity(
74 : : swapAllBalanceOffset,
75 : : swapCalldata,
76 : : augustus,
77 : : permitParams,
78 : : flashLoanAmount,
79 : : premiumLocal,
80 : : initiatorLocal,
81 : : assetToSwapFrom,
82 : : assetToSwapTo,
83 : : minAmountToReceive
84 : : );
85 : :
86 : 2 : return true;
87 : : }
88 : :
89 : : /**
90 : : * @dev Swaps an amount of an asset to another and deposits the new asset amount on behalf of the user without using a flash loan.
91 : : * This method can be used when the temporary transfer of the collateral asset to this contract does not affect the user position.
92 : : * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and perform the swap.
93 : : * @param assetToSwapFrom Address of the underlying asset to be swapped from
94 : : * @param assetToSwapTo Address of the underlying asset to be swapped to and deposited
95 : : * @param amountToSwap Amount to be swapped, or maximum amount when swapping all balance
96 : : * @param minAmountToReceive Minimum amount to be received from the swap
97 : : * @param swapAllBalanceOffset Set to offset of fromAmount in Augustus calldata if wanting to swap all balance, otherwise 0
98 : : * @param swapCalldata Calldata for ParaSwap's AugustusSwapper contract
99 : : * @param augustus Address of ParaSwap's AugustusSwapper contract
100 : : * @param permitParams Struct containing the permit signatures, set to all zeroes if not used
101 : : */
102 : : function swapAndDeposit(
103 : : IERC20Detailed assetToSwapFrom,
104 : : IERC20Detailed assetToSwapTo,
105 : : uint256 amountToSwap,
106 : : uint256 minAmountToReceive,
107 : : uint256 swapAllBalanceOffset,
108 : : bytes calldata swapCalldata,
109 : : IParaSwapAugustus augustus,
110 : : PermitSignature calldata permitParams
111 : : ) external nonReentrant {
112 : 3 : IERC20WithPermit aToken = IERC20WithPermit(
113 : : _getReserveData(address(assetToSwapFrom)).aTokenAddress
114 : : );
115 : :
116 : 3 : if (swapAllBalanceOffset != 0) {
117 : 1 : uint256 balance = aToken.balanceOf(msg.sender);
118 : 1 : require(balance <= amountToSwap, 'INSUFFICIENT_AMOUNT_TO_SWAP');
119 : 1 : amountToSwap = balance;
120 : : }
121 : :
122 : 3 : _pullATokenAndWithdraw(
123 : : address(assetToSwapFrom),
124 : : aToken,
125 : : msg.sender,
126 : : amountToSwap,
127 : : permitParams
128 : : );
129 : :
130 : 3 : uint256 amountReceived = _sellOnParaSwap(
131 : : swapAllBalanceOffset,
132 : : swapCalldata,
133 : : augustus,
134 : : assetToSwapFrom,
135 : : assetToSwapTo,
136 : : amountToSwap,
137 : : minAmountToReceive
138 : : );
139 : :
140 : 2 : assetToSwapTo.safeApprove(address(POOL), 0);
141 : 2 : assetToSwapTo.safeApprove(address(POOL), amountReceived);
142 : 2 : POOL.deposit(address(assetToSwapTo), amountReceived, msg.sender, 0);
143 : : }
144 : :
145 : : /**
146 : : * @dev Swaps an amount of an asset to another and deposits the funds on behalf of the initiator.
147 : : * @param swapAllBalanceOffset Set to offset of fromAmount in Augustus calldata if wanting to swap all balance, otherwise 0
148 : : * @param swapCalldata Calldata for ParaSwap's AugustusSwapper contract
149 : : * @param augustus Address of ParaSwap's AugustusSwapper contract
150 : : * @param permitParams Struct containing the permit signatures, set to all zeroes if not used
151 : : * @param flashLoanAmount Amount of the flash loan i.e. maximum amount to swap
152 : : * @param premium Fee of the flash loan
153 : : * @param initiator Account that initiated the flash loan
154 : : * @param assetToSwapFrom Address of the underyling asset to be swapped from
155 : : * @param assetToSwapTo Address of the underlying asset to be swapped to and deposited
156 : : * @param minAmountToReceive Min amount to be received from the swap
157 : : */
158 : : function _swapLiquidity(
159 : : uint256 swapAllBalanceOffset,
160 : : bytes memory swapCalldata,
161 : : IParaSwapAugustus augustus,
162 : : PermitSignature memory permitParams,
163 : : uint256 flashLoanAmount,
164 : : uint256 premium,
165 : : address initiator,
166 : : IERC20Detailed assetToSwapFrom,
167 : : IERC20Detailed assetToSwapTo,
168 : : uint256 minAmountToReceive
169 : : ) internal {
170 : 3 : IERC20WithPermit aToken = IERC20WithPermit(
171 : : _getReserveData(address(assetToSwapFrom)).aTokenAddress
172 : : );
173 : 3 : uint256 amountToSwap = flashLoanAmount;
174 : :
175 : 3 : uint256 balance = aToken.balanceOf(initiator);
176 : 3 : if (swapAllBalanceOffset != 0) {
177 : 1 : uint256 balanceToSwap = balance.sub(premium);
178 : 1 : require(balanceToSwap <= amountToSwap, 'INSUFFICIENT_AMOUNT_TO_SWAP');
179 : 1 : amountToSwap = balanceToSwap;
180 : : } else {
181 : 2 : require(balance >= amountToSwap.add(premium), 'INSUFFICIENT_ATOKEN_BALANCE');
182 : : }
183 : :
184 : 3 : uint256 amountReceived = _sellOnParaSwap(
185 : : swapAllBalanceOffset,
186 : : swapCalldata,
187 : : augustus,
188 : : assetToSwapFrom,
189 : : assetToSwapTo,
190 : : amountToSwap,
191 : : minAmountToReceive
192 : : );
193 : :
194 : 2 : assetToSwapTo.safeApprove(address(POOL), 0);
195 : 2 : assetToSwapTo.safeApprove(address(POOL), amountReceived);
196 : 2 : POOL.deposit(address(assetToSwapTo), amountReceived, initiator, 0);
197 : :
198 : 2 : _pullATokenAndWithdraw(
199 : : address(assetToSwapFrom),
200 : : aToken,
201 : : initiator,
202 : : amountToSwap.add(premium),
203 : : permitParams
204 : : );
205 : :
206 : : // Repay flash loan
207 : 2 : assetToSwapFrom.safeApprove(address(POOL), 0);
208 : 2 : assetToSwapFrom.safeApprove(address(POOL), flashLoanAmount.add(premium));
209 : : }
210 : : }
|