@@ -56,6 +56,52 @@ 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 ) external view returns (uint256 , uint256 ) {
61
+ if (
62
+ ! IEVC (IEulerSwap (eulerSwap).EVC ()).isAccountOperatorAuthorized (
63
+ IEulerSwap (eulerSwap).eulerAccount (), eulerSwap
64
+ )
65
+ ) return (0 , 0 );
66
+
67
+ return calcLimits (IEulerSwap (eulerSwap), checkTokens (IEulerSwap (eulerSwap), tokenIn, tokenOut));
68
+ }
69
+
70
+ /// @inheritdoc IEulerSwapPeriphery
71
+ function fInverse (uint256 y , uint256 px , uint256 py , uint256 x0 , uint256 y0 , uint256 c )
72
+ external
73
+ pure
74
+ returns (uint256 )
75
+ {
76
+ // A component of the quadratic formula: a = 2 * c
77
+ uint256 A = 2 * c;
78
+
79
+ // B component of the quadratic formula
80
+ int256 B = int256 ((px * (y - y0) + py - 1 ) / py) - int256 ((x0 * (2 * c - 1e18 ) + 1e18 - 1 ) / 1e18 );
81
+
82
+ // B^2 component, using FullMath for overflow safety
83
+ uint256 absB = B < 0 ? uint256 (- B) : uint256 (B);
84
+ uint256 squaredB = Math.mulDiv (absB, absB, 1e18 , Math.Rounding.Ceil);
85
+
86
+ // 4 * A * C component of the quadratic formula
87
+ uint256 AC4 = Math.mulDiv (
88
+ Math.mulDiv (4 * c, (1e18 - c), 1e18 , Math.Rounding.Ceil),
89
+ Math.mulDiv (x0, x0, 1e18 , Math.Rounding.Ceil),
90
+ 1e18 ,
91
+ Math.Rounding.Ceil
92
+ );
93
+
94
+ // Discriminant: b^2 + 4ac, scaled up to maintain precision
95
+ uint256 discriminant = (squaredB + AC4) * 1e18 ;
96
+
97
+ // Square root of the discriminant (rounded up)
98
+ uint256 sqrt = Math.sqrt (discriminant);
99
+ sqrt = (sqrt * sqrt < discriminant) ? sqrt + 1 : sqrt;
100
+
101
+ // Compute and return x = fInverse(y) using the quadratic formula
102
+ return Math.mulDiv (uint256 (int256 (sqrt) - B), 1e18 , A, Math.Rounding.Ceil);
103
+ }
104
+
59
105
/// @dev Internal function to execute a token swap through EulerSwap
60
106
/// @param eulerSwap The EulerSwap contract address to execute the swap through
61
107
/// @param tokenIn The address of the input token being swapped
@@ -101,7 +147,7 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
101
147
if (exactIn) amount = amount * feeMultiplier / 1e18 ;
102
148
103
149
bool asset0IsInput = checkTokens (eulerSwap, tokenIn, tokenOut);
104
- (uint256 inLimit , uint256 outLimit ) = _getLimits (eulerSwap, asset0IsInput);
150
+ (uint256 inLimit , uint256 outLimit ) = calcLimits (eulerSwap, asset0IsInput);
105
151
106
152
uint256 quote = binarySearch (eulerSwap, reserve0, reserve1, amount, exactIn, asset0IsInput);
107
153
@@ -181,92 +227,41 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
181
227
}
182
228
183
229
/**
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).
230
+ * @notice Calculates the maximum input and output amounts for a swap based on protocol constraints
231
+ * @dev Determines limits by checking multiple factors:
232
+ * 1. Supply caps and existing debt for the input token
233
+ * 2. Available reserves in the EulerSwap for the output token
234
+ * 3. Available cash and borrow caps for the output token
235
+ * 4. Account balances in the respective vaults
195
236
*
196
- * @return x The computed x-coordinate on the liquidity curve.
197
- *
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.
237
+ * @param es The EulerSwap contract to calculate limits for
238
+ * @param asset0IsInput Boolean indicating whether asset0 (true) or asset1 (false) is the input token
239
+ * @return uint256 Maximum amount of input token that can be deposited
240
+ * @return uint256 Maximum amount of output token that can be withdrawn
201
241
*/
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);
216
-
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
- return _getLimits (IEulerSwap (eulerSwap), checkTokens (IEulerSwap (eulerSwap), tokenIn, tokenOut));
243
- }
244
-
245
- function _getLimits (IEulerSwap es , bool asset0IsInput ) internal view returns (uint256 inLimit , uint256 outLimit ) {
246
- if (! IEVC (es.EVC ()).isAccountOperatorAuthorized (es.eulerAccount (), address (es))) return (0 , 0 );
247
-
248
- inLimit = outLimit = type (uint112 ).max;
242
+ function calcLimits (IEulerSwap es , bool asset0IsInput ) internal view returns (uint256 , uint256 ) {
243
+ uint256 inLimit = type (uint112 ).max;
244
+ uint256 outLimit = type (uint112 ).max;
249
245
246
+ address eulerAccount = es.eulerAccount ();
247
+ (IEVault vault0 , IEVault vault1 ) = (IEVault (es.vault0 ()), IEVault (es.vault1 ()));
250
248
// Supply caps on input
251
-
252
249
{
253
- IEVault vault = IEVault (asset0IsInput ? es. vault0 () : es. vault1 () );
254
- uint256 maxDeposit = vault.debtOf (es. eulerAccount ()) + vault.maxDeposit (es. eulerAccount () );
250
+ IEVault vault = (asset0IsInput ? vault0 : vault1);
251
+ uint256 maxDeposit = vault.debtOf (eulerAccount) + vault.maxDeposit (eulerAccount);
255
252
if (maxDeposit < inLimit) inLimit = maxDeposit;
256
253
}
257
254
258
255
// Remaining reserves of output
259
-
260
256
{
261
257
(uint112 reserve0 , uint112 reserve1 ,) = es.getReserves ();
262
258
uint112 reserveLimit = asset0IsInput ? reserve1 : reserve0;
263
259
if (reserveLimit < outLimit) outLimit = reserveLimit;
264
260
}
265
261
266
262
// Remaining cash and borrow caps in output
267
-
268
263
{
269
- IEVault vault = IEVault (asset0IsInput ? es. vault1 () : es. vault0 () );
264
+ IEVault vault = (asset0IsInput ? vault1 : vault0);
270
265
271
266
uint256 cash = vault.cash ();
272
267
if (cash < outLimit) outLimit = cash;
@@ -275,11 +270,25 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
275
270
uint256 maxWithdraw = decodeCap (uint256 (borrowCap));
276
271
maxWithdraw = vault.totalBorrows () > maxWithdraw ? 0 : maxWithdraw - vault.totalBorrows ();
277
272
if (maxWithdraw > cash) maxWithdraw = cash;
278
- maxWithdraw += vault.convertToAssets (vault.balanceOf (es. eulerAccount () ));
273
+ maxWithdraw += vault.convertToAssets (vault.balanceOf (eulerAccount));
279
274
if (maxWithdraw < outLimit) outLimit = maxWithdraw;
280
275
}
276
+
277
+ return (inLimit, outLimit);
281
278
}
282
279
280
+ /**
281
+ * @notice Decodes a compact-format cap value to its actual numerical value
282
+ * @dev The cap uses a compact-format where:
283
+ * - If amountCap == 0, there's no cap (returns max uint256)
284
+ * - Otherwise, the lower 6 bits represent the exponent (10^exp)
285
+ * - The upper bits (>> 6) represent the mantissa
286
+ * - The formula is: (10^exponent * mantissa) / 100
287
+ * @param amountCap The compact-format cap value to decode
288
+ * @return The actual numerical cap value (type(uint256).max if uncapped)
289
+ * @custom:security Uses unchecked math for gas optimization as calculations cannot overflow:
290
+ * maximum possible value 10^(2^6-1) * (2^10-1) ≈ 1.023e+66 < 2^256
291
+ */
283
292
function decodeCap (uint256 amountCap ) internal pure returns (uint256 ) {
284
293
if (amountCap == 0 ) return type (uint256 ).max;
285
294
@@ -290,6 +299,15 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
290
299
}
291
300
}
292
301
302
+ /**
303
+ * @notice Verifies that the given tokens are supported by the EulerSwap pool and determines swap direction
304
+ * @dev Returns a boolean indicating whether the input token is asset0 (true) or asset1 (false)
305
+ * @param eulerSwap The EulerSwap pool contract to check against
306
+ * @param tokenIn The input token address for the swap
307
+ * @param tokenOut The output token address for the swap
308
+ * @return asset0IsInput True if tokenIn is asset0 and tokenOut is asset1, false if reversed
309
+ * @custom:error UnsupportedPair Thrown if the token pair is not supported by the EulerSwap pool
310
+ */
293
311
function checkTokens (IEulerSwap eulerSwap , address tokenIn , address tokenOut )
294
312
internal
295
313
view
0 commit comments