@@ -56,6 +56,56 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
56
56
return computeQuote (IEulerSwap (eulerSwap), tokenIn, tokenOut, amountOut, false );
57
57
}
58
58
59
+ /// @inheritdoc IEulerSwapPeriphery
60
+ function getLimits (address eulerSwap , address tokenIn , address tokenOut )
61
+ external
62
+ view
63
+ returns (uint256 inLimit , uint256 outLimit )
64
+ {
65
+ if (
66
+ ! IEVC (IEulerSwap (eulerSwap).EVC ()).isAccountOperatorAuthorized (
67
+ IEulerSwap (eulerSwap).eulerAccount (), eulerSwap
68
+ )
69
+ ) return (0 , 0 );
70
+
71
+ return calcLimits (IEulerSwap (eulerSwap), checkTokens (IEulerSwap (eulerSwap), tokenIn, tokenOut));
72
+ }
73
+
74
+ /// @inheritdoc IEulerSwapPeriphery
75
+ function fInverse (uint256 y , uint256 px , uint256 py , uint256 x0 , uint256 y0 , uint256 c )
76
+ external
77
+ pure
78
+ returns (uint256 )
79
+ {
80
+ // A component of the quadratic formula: a = 2 * c
81
+ uint256 A = 2 * c;
82
+
83
+ // B component of the quadratic formula
84
+ int256 B = int256 ((px * (y - y0) + py - 1 ) / py) - int256 ((x0 * (2 * c - 1e18 ) + 1e18 - 1 ) / 1e18 );
85
+
86
+ // B^2 component, using FullMath for overflow safety
87
+ uint256 absB = B < 0 ? uint256 (- B) : uint256 (B);
88
+ uint256 squaredB = Math.mulDiv (absB, absB, 1e18 , Math.Rounding.Ceil);
89
+
90
+ // 4 * A * C component of the quadratic formula
91
+ uint256 AC4 = Math.mulDiv (
92
+ Math.mulDiv (4 * c, (1e18 - c), 1e18 , Math.Rounding.Ceil),
93
+ Math.mulDiv (x0, x0, 1e18 , Math.Rounding.Ceil),
94
+ 1e18 ,
95
+ Math.Rounding.Ceil
96
+ );
97
+
98
+ // Discriminant: b^2 + 4ac, scaled up to maintain precision
99
+ uint256 discriminant = (squaredB + AC4) * 1e18 ;
100
+
101
+ // Square root of the discriminant (rounded up)
102
+ uint256 sqrt = Math.sqrt (discriminant);
103
+ sqrt = (sqrt * sqrt < discriminant) ? sqrt + 1 : sqrt;
104
+
105
+ // Compute and return x = fInverse(y) using the quadratic formula
106
+ return Math.mulDiv (uint256 (int256 (sqrt) - B), 1e18 , A, Math.Rounding.Ceil);
107
+ }
108
+
59
109
/// @dev Internal function to execute a token swap through EulerSwap
60
110
/// @param eulerSwap The EulerSwap contract address to execute the swap through
61
111
/// @param tokenIn The address of the input token being swapped
@@ -101,7 +151,7 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
101
151
if (exactIn) amount = amount * feeMultiplier / 1e18 ;
102
152
103
153
bool asset0IsInput = checkTokens (eulerSwap, tokenIn, tokenOut);
104
- (uint256 inLimit , uint256 outLimit ) = _getLimits (eulerSwap, asset0IsInput);
154
+ (uint256 inLimit , uint256 outLimit ) = calcLimits (eulerSwap, asset0IsInput);
105
155
106
156
uint256 quote = binarySearch (eulerSwap, reserve0, reserve1, amount, exactIn, asset0IsInput);
107
157
@@ -181,75 +231,22 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
181
231
}
182
232
183
233
/**
184
- * @notice Computes the inverse of the `f()` function for the EulerSwap liquidity curve.
185
- * @dev Solves for `x` given `y` using the quadratic formula derived from the liquidity curve:
186
- * x = (-b + sqrt(b^2 + 4ac)) / 2a
187
- * Utilises mulDiv to avoid overflow and ensures precision with upward rounding.
188
- *
189
- * @param y The y-coordinate input value (must be greater than `y0`).
190
- * @param px Price factor for the x-axis (scaled by 1e18, between 1e18 and 1e36).
191
- * @param py Price factor for the y-axis (scaled by 1e18, between 1e18 and 1e36).
192
- * @param x0 Reference x-value on the liquidity curve (≤ 2^112 - 1).
193
- * @param y0 Reference y-value on the liquidity curve (≤ 2^112 - 1).
194
- * @param c Curve parameter shaping liquidity concentration (scaled by 1e18, between 0 and 1e18).
195
- *
196
- * @return x The computed x-coordinate on the liquidity curve.
234
+ * @notice Calculates the maximum input and output amounts for a swap based on protocol constraints
235
+ * @dev Determines limits by checking multiple factors:
236
+ * 1. Supply caps and existing debt for the input token
237
+ * 2. Available reserves in the EulerSwap for the output token
238
+ * 3. Available cash and borrow caps for the output token
239
+ * 4. Account balances in the respective vaults
197
240
*
198
- * @custom:precision Uses rounding up to maintain precision in all calculations.
199
- * @custom:safety FullMath handles potential overflow in the b^2 computation.
200
- * @custom:requirement Input `y` must be strictly greater than `y0`; otherwise, the function will revert.
241
+ * @param es The EulerSwap contract to calculate limits for
242
+ * @param asset0IsInput Boolean indicating whether asset0 (true) or asset1 (false) is the input token
243
+ * @return uint256 Maximum amount of input token that can be deposited
244
+ * @return uint256 Maximum amount of output token that can be withdrawn
201
245
*/
202
- function fInverse (uint256 y , uint256 px , uint256 py , uint256 x0 , uint256 y0 , uint256 c )
203
- external
204
- pure
205
- returns (uint256 )
206
- {
207
- // A component of the quadratic formula: a = 2 * c
208
- uint256 A = 2 * c;
209
-
210
- // B component of the quadratic formula
211
- int256 B = int256 ((px * (y - y0) + py - 1 ) / py) - int256 ((x0 * (2 * c - 1e18 ) + 1e18 - 1 ) / 1e18 );
212
-
213
- // B^2 component, using FullMath for overflow safety
214
- uint256 absB = B < 0 ? uint256 (- B) : uint256 (B);
215
- uint256 squaredB = Math.mulDiv (absB, absB, 1e18 , Math.Rounding.Ceil);
246
+ function calcLimits (IEulerSwap es , bool asset0IsInput ) internal view returns (uint256 , uint256 ) {
247
+ uint256 inLimit = type (uint112 ).max;
248
+ uint256 outLimit = type (uint112 ).max;
216
249
217
- // 4 * A * C component of the quadratic formula
218
- uint256 AC4 = Math.mulDiv (
219
- Math.mulDiv (4 * c, (1e18 - c), 1e18 , Math.Rounding.Ceil),
220
- Math.mulDiv (x0, x0, 1e18 , Math.Rounding.Ceil),
221
- 1e18 ,
222
- Math.Rounding.Ceil
223
- );
224
-
225
- // Discriminant: b^2 + 4ac, scaled up to maintain precision
226
- uint256 discriminant = (squaredB + AC4) * 1e18 ;
227
-
228
- // Square root of the discriminant (rounded up)
229
- uint256 sqrt = Math.sqrt (discriminant);
230
- sqrt = (sqrt * sqrt < discriminant) ? sqrt + 1 : sqrt;
231
-
232
- // Compute and return x = fInverse(y) using the quadratic formula
233
- return Math.mulDiv (uint256 (int256 (sqrt) - B), 1e18 , A, Math.Rounding.Ceil);
234
- }
235
-
236
- /// @notice Max amount the pool can buy of tokenIn and sell of tokenOut
237
- function getLimits (address eulerSwap , address tokenIn , address tokenOut )
238
- external
239
- view
240
- returns (uint256 inLimit , uint256 outLimit )
241
- {
242
- if (
243
- ! IEVC (IEulerSwap (eulerSwap).EVC ()).isAccountOperatorAuthorized (
244
- IEulerSwap (eulerSwap).eulerAccount (), eulerSwap
245
- )
246
- ) return (0 , 0 );
247
-
248
- return _getLimits (IEulerSwap (eulerSwap), checkTokens (IEulerSwap (eulerSwap), tokenIn, tokenOut));
249
- }
250
-
251
- function _getLimits (IEulerSwap es , bool asset0IsInput ) internal view returns (uint256 inLimit , uint256 outLimit ) {
252
- inLimit = outLimit = type (uint112 ).max;
253
250
address eulerAccount = es.eulerAccount ();
254
251
(IEVault vault0 , IEVault vault1 ) = (IEVault (es.vault0 ()), IEVault (es.vault1 ()));
255
252
// Supply caps on input
@@ -280,8 +277,22 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
280
277
maxWithdraw += vault.convertToAssets (vault.balanceOf (eulerAccount));
281
278
if (maxWithdraw < outLimit) outLimit = maxWithdraw;
282
279
}
280
+
281
+ return (inLimit, outLimit);
283
282
}
284
283
284
+ /**
285
+ * @notice Decodes a compact-format cap value to its actual numerical value
286
+ * @dev The cap uses a compact-format where:
287
+ * - If amountCap == 0, there's no cap (returns max uint256)
288
+ * - Otherwise, the lower 6 bits represent the exponent (10^exp)
289
+ * - The upper bits (>> 6) represent the mantissa
290
+ * - The formula is: (10^exponent * mantissa) / 100
291
+ * @param amountCap The compact-format cap value to decode
292
+ * @return The actual numerical cap value (type(uint256).max if uncapped)
293
+ * @custom:security Uses unchecked math for gas optimization as calculations cannot overflow:
294
+ * maximum possible value 10^(2^6-1) * (2^10-1) ≈ 1.023e+66 < 2^256
295
+ */
285
296
function decodeCap (uint256 amountCap ) internal pure returns (uint256 ) {
286
297
if (amountCap == 0 ) return type (uint256 ).max;
287
298
@@ -292,6 +303,15 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
292
303
}
293
304
}
294
305
306
+ /**
307
+ * @notice Verifies that the given tokens are supported by the EulerSwap pool and determines swap direction
308
+ * @dev Returns a boolean indicating whether the input token is asset0 (true) or asset1 (false)
309
+ * @param eulerSwap The EulerSwap pool contract to check against
310
+ * @param tokenIn The input token address for the swap
311
+ * @param tokenOut The output token address for the swap
312
+ * @return asset0IsInput True if tokenIn is asset0 and tokenOut is asset1, false if reversed
313
+ * @custom:error UnsupportedPair Thrown if the token pair is not supported by the EulerSwap pool
314
+ */
295
315
function checkTokens (IEulerSwap eulerSwap , address tokenIn , address tokenOut )
296
316
internal
297
317
view
0 commit comments