1
+ /*
2
+ Copyright 2022 Index Cooperative
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+ Unless required by applicable law or agreed to in writing, software
9
+ distributed under the License is distributed on an "AS IS" BASIS,
10
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ See the License for the specific language governing permissions and
12
+ limitations under the License.
13
+
14
+ SPDX-License-Identifier: Apache License, Version 2.0
15
+ */
16
+
17
+ pragma solidity 0.6.10 ;
18
+ pragma experimental ABIEncoderV2;
19
+
20
+ import { IQuoter } from "../interfaces/IQuoter.sol " ;
21
+ import { IUniswapV3SwapCallback } from "../interfaces/IUniswapV3SwapCallback.sol " ;
22
+ import { ISwapRouter} from "../interfaces/external/ISwapRouter.sol " ;
23
+ import { Address } from "@openzeppelin/contracts/utils/Address.sol " ;
24
+ import { Context } from "@openzeppelin/contracts/GSN/Context.sol " ;
25
+ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol " ;
26
+ import { SignedSafeMath } from "@openzeppelin/contracts/math/SignedSafeMath.sol " ;
27
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
28
+ import { TransferHelper } from "../lib/TransferHelper.sol " ;
29
+ import { ISetToken } from "../interfaces/ISetToken.sol " ;
30
+ import { IDebtIssuanceModule } from "../interfaces/IDebtIssuanceModule.sol " ;
31
+ import { ISlippageIssuanceModule } from "../interfaces/ISlippageIssuanceModule.sol " ;
32
+ import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol " ;
33
+ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol " ;
34
+ import { Withdrawable } from "external/contracts/aaveV2/utils/Withdrawable.sol " ;
35
+ import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol " ;
36
+ import { PreciseUnitMath } from "../lib/PreciseUnitMath.sol " ;
37
+
38
+
39
+ /**
40
+ * @title FlashMintPerp
41
+ *
42
+ * Flash issue basis trading products using SlippageIssuanceModule
43
+ *
44
+ */
45
+ contract FlashMintPerp is Withdrawable {
46
+ using PreciseUnitMath for uint256 ;
47
+ using SafeMath for uint256 ;
48
+ using SafeCast for int256 ;
49
+
50
+
51
+ ////////////// State //////////////
52
+
53
+ ISlippageIssuanceModule public immutable slippageIssuanceModule;
54
+ ISwapRouter public immutable uniV3Router;
55
+ IQuoter public immutable uniV3Quoter;
56
+ IERC20 public immutable usdc;
57
+ mapping (ISetToken => SetPoolInfo) public setPoolInfo;
58
+ mapping (ISetToken => bool ) public initializedSets;
59
+
60
+ ////////////// Structs //////////////
61
+
62
+ struct SetPoolInfo {
63
+ bytes spotToUsdcRoute;
64
+ address spotToken;
65
+ }
66
+
67
+ ////////////// Constructor //////////////
68
+
69
+ constructor (
70
+ ISwapRouter _uniV3Router ,
71
+ IQuoter _uniV3Quoter ,
72
+ ISlippageIssuanceModule _slippageIssuanceModule ,
73
+ IERC20 _usdc
74
+ )
75
+ public
76
+ {
77
+ uniV3Router = _uniV3Router;
78
+ uniV3Quoter = _uniV3Quoter;
79
+ slippageIssuanceModule = _slippageIssuanceModule;
80
+ usdc = _usdc;
81
+
82
+ // Approve USDC to SlippageIssuanceModule
83
+ _usdc.approve (address (_slippageIssuanceModule), PreciseUnitMath.maxUint256 ());
84
+
85
+ // Approve USDC
86
+ _usdc.approve (address (_uniV3Router), PreciseUnitMath.maxUint256 ());
87
+ }
88
+
89
+ ///////////// Modifier ///////////////
90
+
91
+ modifier isInitializedSet (ISetToken _setToken ) {
92
+ require (initializedSets[_setToken], "Set not initialized " );
93
+ _;
94
+ }
95
+
96
+ ////////// Helper functions /////////////
97
+
98
+ /**
99
+ * @dev Approve specific amount of token to spender
100
+ *
101
+ * @param _token Address of the token which needs approval
102
+ * @param _spender Address of the spender which will be approved to spend token. (Must be a whitlisted issuance module)
103
+ * @param _amount The amount of tokens to approve
104
+ */
105
+ function approve (address _token , address _spender , uint256 _amount ) external onlyOwner {
106
+ TransferHelper.safeApprove (_token, _spender, _amount);
107
+ }
108
+
109
+ /**
110
+ * @dev Enable the SetToken issuance
111
+ *
112
+ * @param _setToken Address of the SetToken to be issued
113
+ * @param _spotToUsdcRoute Uniswap V3 Path to be used for exchange
114
+ * @param _spotToken Address of the spot token
115
+ */
116
+ function initializeSet (
117
+ ISetToken _setToken ,
118
+ bytes calldata _spotToUsdcRoute ,
119
+ address _spotToken
120
+ )
121
+ external
122
+ onlyOwner
123
+ {
124
+ // Approve spot token to V3 and SIM
125
+ TransferHelper.safeApprove (_spotToken, address (uniV3Router), PreciseUnitMath.maxUint256 ());
126
+ TransferHelper.safeApprove (_spotToken, address (slippageIssuanceModule), PreciseUnitMath.maxUint256 ());
127
+
128
+ // Store SetToken pool data in mapping
129
+ setPoolInfo[_setToken] = SetPoolInfo ({
130
+ spotToUsdcRoute: _spotToUsdcRoute,
131
+ spotToken: _spotToken
132
+ });
133
+
134
+ initializedSets[_setToken] = true ;
135
+ }
136
+
137
+ /**
138
+ * @dev Disable the SetToken issuance
139
+ *
140
+ * @param _setToken Address of the SetToken to be issued
141
+ */
142
+ function removeSet (ISetToken _setToken ) external onlyOwner {
143
+ delete setPoolInfo[_setToken];
144
+ initializedSets[_setToken] = false ;
145
+ }
146
+
147
+ ///////////////// Getter Functions /////////////////////
148
+
149
+ /**
150
+ * Returns USDC amount required for issuance
151
+ *
152
+ * @param _setToken Address of the SetToken
153
+ * @param _amountOut The issuance amount of the SetToken
154
+ */
155
+ function getUsdcAmountInForFixedSetOffChain (
156
+ ISetToken _setToken ,
157
+ uint256 _amountOut
158
+ )
159
+ external
160
+ returns (uint256 totalUsdcAmountIn )
161
+ {
162
+ // Get units and components
163
+ (
164
+ address [] memory slippageIssuanceComponents ,
165
+ uint256 [] memory slippageIssuanceUnits ,
166
+ ) = slippageIssuanceModule.getRequiredComponentIssuanceUnitsOffChain (
167
+ _setToken,
168
+ _amountOut
169
+ );
170
+
171
+ // Assert assumptions
172
+ require (slippageIssuanceComponents.length <= 2 , "invalid set " );
173
+
174
+ // calculate total usdc amount in and usdcForSpot
175
+ for (uint256 i = 0 ; i < slippageIssuanceComponents.length ; i++ ) {
176
+
177
+ if (slippageIssuanceComponents[i] == address (usdc)) {
178
+
179
+ totalUsdcAmountIn = totalUsdcAmountIn.add (slippageIssuanceUnits[i]);
180
+
181
+ } else {
182
+
183
+ uint256 usdcForSpot = uniV3Quoter.quoteExactOutput (
184
+ setPoolInfo[_setToken].spotToUsdcRoute,
185
+ slippageIssuanceUnits[i].add (1 ) // Add 1 wei
186
+ );
187
+ totalUsdcAmountIn = totalUsdcAmountIn.add (usdcForSpot);
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Returns USDC amount required for redemption
194
+ *
195
+ * @param _setToken Address of the SetToken
196
+ * @param _amountIn The redeem amount of the SetToken
197
+ */
198
+ function getUsdcAmountOutForFixedSetOffChain (
199
+ ISetToken _setToken ,
200
+ uint256 _amountIn
201
+ )
202
+ external
203
+ returns (uint256 totalUsdcAmountOut )
204
+ {
205
+ // Get underlying spot and usdc units
206
+ (
207
+ address [] memory slippageIssuanceComponents ,
208
+ uint256 [] memory slippageIssuanceUnits ,
209
+ ) = slippageIssuanceModule.getRequiredComponentRedemptionUnitsOffChain (
210
+ _setToken,
211
+ _amountIn
212
+ );
213
+
214
+ // Assert assumptions
215
+ require (slippageIssuanceComponents.length <= 2 , "invalid set " );
216
+
217
+ // calculate total usdc amount in and usdcFromSpot
218
+ for (uint256 i = 0 ; i < slippageIssuanceComponents.length ; i++ ) {
219
+ if (slippageIssuanceComponents[i] == address (usdc)) {
220
+
221
+ totalUsdcAmountOut = totalUsdcAmountOut.add (slippageIssuanceUnits[i]);
222
+
223
+ } else {
224
+
225
+ uint256 usdcFromSpot = uniV3Quoter.quoteExactInput (
226
+ setPoolInfo[_setToken].spotToUsdcRoute,
227
+ slippageIssuanceUnits[i].sub (1 ) // Leave 1 wei
228
+ );
229
+ totalUsdcAmountOut = totalUsdcAmountOut.add (usdcFromSpot);
230
+ }
231
+ }
232
+ }
233
+
234
+ //////////////// External Functions ////////////////////
235
+
236
+ /**
237
+ * Issue expected amount of SetToken using USDC
238
+ *
239
+ * @param _setToken Address of the SetToken
240
+ * @param _amount The expected issuance amount of the SetToken
241
+ * @param _maxAmountIn The maximum input amount of USDC
242
+ */
243
+ function issueFixedSetFromUsdc (
244
+ ISetToken _setToken ,
245
+ uint256 _amount ,
246
+ uint256 _maxAmountIn
247
+ )
248
+ external
249
+ isInitializedSet (_setToken)
250
+ {
251
+ // Transfer max amount in
252
+ TransferHelper.safeTransferFrom (address (usdc), msg .sender , address (this ), _maxAmountIn);
253
+
254
+ // calculate spot asset quantity
255
+ uint256 spotAssetQuantity = _spotAssetQuantity (_setToken, _amount);
256
+
257
+ // Trade USDC for exact spot token
258
+ ISwapRouter.ExactOutputParams memory spotTokenParams = ISwapRouter.ExactOutputParams (
259
+ setPoolInfo[_setToken].spotToUsdcRoute,
260
+ address (this ),
261
+ block .timestamp ,
262
+ spotAssetQuantity.add (1 ), // Add 1 wei
263
+ PreciseUnitMath.maxUint256 () // No need for slippage check
264
+ );
265
+
266
+ // Executes the swap
267
+ uniV3Router.exactOutput (spotTokenParams);
268
+
269
+ // Issue Set with spot tokens and USDC
270
+ slippageIssuanceModule.issueWithSlippage (
271
+ _setToken,
272
+ _amount,
273
+ new address [](0 ), // No need to check for slippage cause L2; If not enough USDC then issue would fail
274
+ new uint256 [](0 ),
275
+ msg .sender
276
+ );
277
+
278
+ // Return unused USDC
279
+ uint256 usdcBalance = usdc.balanceOf (address (this ));
280
+ TransferHelper.safeTransfer (address (usdc), msg .sender , usdcBalance);
281
+ }
282
+
283
+ /**
284
+ * Redeem expected amount of SetToken using USDC
285
+ *
286
+ * @param _setToken Address of the SetToken
287
+ * @param _amount The expected redeem amount of the SetToken
288
+ * @param _minAmountOut The minimum output amount of USDC
289
+ */
290
+ function redeemFixedSetForUsdc (
291
+ ISetToken _setToken ,
292
+ uint256 _amount ,
293
+ uint256 _minAmountOut
294
+ )
295
+ external
296
+ isInitializedSet (_setToken)
297
+ {
298
+
299
+ TransferHelper.safeTransferFrom (address (_setToken), msg .sender , address (this ), _amount);
300
+
301
+ // Redeem Set to spot tokens and USDC
302
+ slippageIssuanceModule.redeemWithSlippage (
303
+ _setToken,
304
+ _amount,
305
+ new address [](0 ), // No need to check for slippage as there is no risk of sandwiching due to flashloans
306
+ new uint256 [](0 ),
307
+ address (this )
308
+ );
309
+
310
+ // calculate spot asset quantity
311
+ uint256 spotAssetQuantity = _spotAssetQuantity (_setToken, _amount);
312
+
313
+ // check with actual spot token balance
314
+ uint256 spotTokenBalance = IERC20 (setPoolInfo[_setToken].spotToken).balanceOf (address (this ));
315
+ if (spotAssetQuantity > spotTokenBalance) {
316
+ spotAssetQuantity = spotTokenBalance;
317
+ }
318
+
319
+ ISwapRouter.ExactInputParams memory spotTokenParams = ISwapRouter.ExactInputParams (
320
+ setPoolInfo[_setToken].spotToUsdcRoute,
321
+ address (this ),
322
+ block .timestamp ,
323
+ spotAssetQuantity.sub (1 ), // Leave 1 wei
324
+ 0 // No need for slippage check
325
+ );
326
+
327
+ // Executes the swap
328
+ uniV3Router.exactInput (spotTokenParams);
329
+
330
+ // Return the USDC
331
+ uint256 usdcBalance = usdc.balanceOf (address (this ));
332
+
333
+ require (usdcBalance >= _minAmountOut, "Not enough USDC " );
334
+
335
+ TransferHelper.safeTransfer (address (usdc), msg .sender , usdcBalance);
336
+ }
337
+
338
+
339
+ /////////////// Internal functions //////////////////
340
+
341
+ function _spotAssetQuantity (ISetToken _setToken , uint256 _amount ) internal view returns (uint256 ) {
342
+ address spotAsset = setPoolInfo[_setToken].spotToken;
343
+
344
+ uint256 spotAssetQuantity = _setToken
345
+ .getDefaultPositionRealUnit (spotAsset)
346
+ .toUint256 ()
347
+ .preciseMul (_amount);
348
+
349
+ return spotAssetQuantity;
350
+ }
351
+ }
0 commit comments