-
Notifications
You must be signed in to change notification settings - Fork 47
/
SwapsUser.sol
276 lines (248 loc) · 9.43 KB
/
SwapsUser.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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/**
* Copyright 2017-2021, bZeroX, LLC <https://bzx.network/>. All Rights Reserved.
* Licensed under the Apache License, Version 2.0.
*/
pragma solidity 0.5.17;
import "../core/State.sol";
import "../feeds/IPriceFeeds.sol";
import "../events/SwapsEvents.sol";
import "../mixins/FeesHelper.sol";
import "./connectors/SwapsImplSovrynSwapLib.sol";
/**
* @title Perform token swaps for loans and trades.
* */
contract SwapsUser is State, SwapsEvents, FeesHelper {
/**
* @notice Internal loan swap.
*
* @param loanId The ID of the loan.
* @param sourceToken The address of the source tokens.
* @param destToken The address of destination tokens.
* @param user The user address.
* @param minSourceTokenAmount The minimum amount of source tokens to swap.
* @param maxSourceTokenAmount The maximum amount of source tokens to swap.
* @param requiredDestTokenAmount The required amount of destination tokens.
* @param bypassFee To bypass or not the fee.
* @param loanDataBytes The payload for the call. These loan DataBytes are
* additional loan data (not in use for token swaps).
*
* @return destTokenAmountReceived
* @return sourceTokenAmountUsed
* @return sourceToDestSwapRate
* */
function _loanSwap(
bytes32 loanId,
address sourceToken,
address destToken,
address user,
uint256 minSourceTokenAmount,
uint256 maxSourceTokenAmount,
uint256 requiredDestTokenAmount,
bool bypassFee,
bytes memory loanDataBytes
)
internal
returns (
uint256 destTokenAmountReceived,
uint256 sourceTokenAmountUsed,
uint256 sourceToDestSwapRate
)
{
(destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall(
[
sourceToken,
destToken,
address(this), // receiver
address(this), // returnToSender
user
],
[minSourceTokenAmount, maxSourceTokenAmount, requiredDestTokenAmount],
loanId,
bypassFee,
loanDataBytes,
false // swap external flag, set to false so that it will use the tradingFeePercent
);
/// Will revert if swap size too large.
_checkSwapSize(sourceToken, sourceTokenAmountUsed);
/// Will revert if disagreement found.
sourceToDestSwapRate = IPriceFeeds(priceFeeds).checkPriceDisagreement(
sourceToken,
destToken,
sourceTokenAmountUsed,
destTokenAmountReceived,
maxDisagreement
);
emit LoanSwap(
loanId,
sourceToken,
destToken,
user,
sourceTokenAmountUsed,
destTokenAmountReceived
);
}
/**
* @notice Calculate amount of source and destination tokens.
*
* @dev Wrapper for _swapsCall_internal function.
*
* @param addrs The array of addresses.
* @param vals The array of values.
* @param loanId The Id of the associated loan.
* @param miscBool True/false to bypassFee.
* @param loanDataBytes Additional loan data (not in use yet).
*
* @return destTokenAmountReceived The amount of destination tokens received.
* @return sourceTokenAmountUsed The amount of source tokens used.
* */
function _swapsCall(
address[5] memory addrs,
uint256[3] memory vals,
bytes32 loanId,
bool miscBool, /// bypassFee
bytes memory loanDataBytes,
bool isSwapExternal
) internal returns (uint256, uint256) {
/// addrs[0]: sourceToken
/// addrs[1]: destToken
/// addrs[2]: receiver
/// addrs[3]: returnToSender
/// addrs[4]: user
/// vals[0]: minSourceTokenAmount
/// vals[1]: maxSourceTokenAmount
/// vals[2]: requiredDestTokenAmount
require(vals[0] != 0 || vals[1] != 0, "min or max source token amount needs to be set");
if (vals[1] == 0) {
vals[1] = vals[0];
}
require(vals[0] <= vals[1], "sourceAmount larger than max");
uint256 destTokenAmountReceived;
uint256 sourceTokenAmountUsed;
uint256 tradingFee;
if (!miscBool) {
/// bypassFee
if (vals[2] == 0) {
/// condition: vals[0] will always be used as sourceAmount
if (isSwapExternal) {
tradingFee = _getSwapExternalFee(vals[0]);
} else {
tradingFee = _getTradingFee(vals[0]);
}
if (tradingFee != 0) {
_payTradingFee(
addrs[4], /// user
loanId,
addrs[0], /// sourceToken (feeToken)
addrs[1], /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward
tradingFee
);
vals[0] = vals[0].sub(tradingFee);
}
} else {
/// Condition: unknown sourceAmount will be used.
if (isSwapExternal) {
tradingFee = _getSwapExternalFee(vals[2]);
} else {
tradingFee = _getTradingFee(vals[2]);
}
if (tradingFee != 0) {
vals[2] = vals[2].add(tradingFee);
}
}
}
require(loanDataBytes.length == 0, "invalid state");
(destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall_internal(addrs, vals);
if (vals[2] == 0) {
/// There's no minimum destTokenAmount, but all of vals[0]
/// (minSourceTokenAmount) must be spent.
require(sourceTokenAmountUsed == vals[0], "swap too large to fill");
if (tradingFee != 0) {
sourceTokenAmountUsed = sourceTokenAmountUsed.add(tradingFee);
}
} else {
/// There's a minimum destTokenAmount required, but
/// sourceTokenAmountUsed won't be greater
/// than vals[1] (maxSourceTokenAmount)
require(sourceTokenAmountUsed <= vals[1], "swap fill too large");
require(destTokenAmountReceived >= vals[2], "insufficient swap liquidity");
if (tradingFee != 0) {
_payTradingFee(
addrs[4], /// user
loanId, /// loanId,
addrs[1], /// destToken (feeToken)
addrs[0], /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward
tradingFee
);
destTokenAmountReceived = destTokenAmountReceived.sub(tradingFee);
}
}
return (destTokenAmountReceived, sourceTokenAmountUsed);
}
/**
* @notice Calculate amount of source and destination tokens.
*
* @dev Calls swapsImpl::internalSwap
*
* @param addrs The array of addresses.
* @param vals The array of values.
*
* @return destTokenAmountReceived The amount of destination tokens received.
* @return sourceTokenAmountUsed The amount of source tokens used.
* */
function _swapsCall_internal(
address[5] memory addrs,
uint256[3] memory vals
) internal returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) {
SwapsImplSovrynSwapLib.SwapParams memory swapParams;
swapParams.sourceTokenAddress = addrs[0];
swapParams.destTokenAddress = addrs[1];
swapParams.receiverAddress = addrs[2];
swapParams.returnToSenderAddress = addrs[3];
swapParams.minSourceTokenAmount = vals[0];
swapParams.maxSourceTokenAmount = vals[1];
swapParams.requiredDestTokenAmount = vals[2];
(destTokenAmountReceived, sourceTokenAmountUsed) = SwapsImplSovrynSwapLib.swap(swapParams);
}
/**
* @notice Calculate expected amount of destination tokens.
*
* @dev Calls swapsImpl::internalExpectedReturn
*
* @param sourceToken The address of the source tokens.
* @param destToken The address of the destination tokens.
* @param sourceTokenAmount The amount of the source tokens.
*
* @param destTokenAmount The amount of destination tokens.
* */
function _swapsExpectedReturn(
address sourceToken,
address destToken,
uint256 sourceTokenAmount
) internal view returns (uint256 destTokenAmount) {
destTokenAmount = SwapsImplSovrynSwapLib.getExpectedReturn(
sourceToken,
destToken,
sourceTokenAmount
);
}
/**
* @notice Verify that the amount of tokens are under the swap limit.
*
* @dev Calls priceFeeds::amountInEth
*
* @param tokenAddress The address of the token to calculate price.
* @param amount The amount of tokens to calculate price.
* */
function _checkSwapSize(address tokenAddress, uint256 amount) internal view {
uint256 _maxSwapSize = maxSwapSize;
if (_maxSwapSize != 0) {
uint256 amountInEth;
if (tokenAddress == address(wrbtcToken)) {
amountInEth = amount;
} else {
amountInEth = IPriceFeeds(priceFeeds).amountInEth(tokenAddress, amount);
}
require(amountInEth <= _maxSwapSize, "swap too large");
}
}
}