Skip to content

Commit 6024532

Browse files
clean
1 parent 065cf6c commit 6024532

File tree

2 files changed

+117
-67
lines changed

2 files changed

+117
-67
lines changed

src/EulerSwapPeriphery.sol

Lines changed: 87 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,56 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
5656
return computeQuote(IEulerSwap(eulerSwap), tokenIn, tokenOut, amountOut, false);
5757
}
5858

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+
59109
/// @dev Internal function to execute a token swap through EulerSwap
60110
/// @param eulerSwap The EulerSwap contract address to execute the swap through
61111
/// @param tokenIn The address of the input token being swapped
@@ -101,7 +151,7 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
101151
if (exactIn) amount = amount * feeMultiplier / 1e18;
102152

103153
bool asset0IsInput = checkTokens(eulerSwap, tokenIn, tokenOut);
104-
(uint256 inLimit, uint256 outLimit) = _getLimits(eulerSwap, asset0IsInput);
154+
(uint256 inLimit, uint256 outLimit) = calcLimits(eulerSwap, asset0IsInput);
105155

106156
uint256 quote = binarySearch(eulerSwap, reserve0, reserve1, amount, exactIn, asset0IsInput);
107157

@@ -181,75 +231,22 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
181231
}
182232

183233
/**
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
197240
*
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
201245
*/
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;
216249

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;
253250
address eulerAccount = es.eulerAccount();
254251
(IEVault vault0, IEVault vault1) = (IEVault(es.vault0()), IEVault(es.vault1()));
255252
// Supply caps on input
@@ -280,8 +277,22 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
280277
maxWithdraw += vault.convertToAssets(vault.balanceOf(eulerAccount));
281278
if (maxWithdraw < outLimit) outLimit = maxWithdraw;
282279
}
280+
281+
return (inLimit, outLimit);
283282
}
284283

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+
*/
285296
function decodeCap(uint256 amountCap) internal pure returns (uint256) {
286297
if (amountCap == 0) return type(uint256).max;
287298

@@ -292,6 +303,15 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
292303
}
293304
}
294305

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+
*/
295315
function checkTokens(IEulerSwap eulerSwap, address tokenIn, address tokenOut)
296316
internal
297317
view

src/interfaces/IEulerSwapPeriphery.sol

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,34 @@ interface IEulerSwapPeriphery {
2121
external
2222
view
2323
returns (uint256);
24+
25+
/// @notice Max amount the pool can buy of tokenIn and sell of tokenOut
26+
function getLimits(address eulerSwap, address tokenIn, address tokenOut)
27+
external
28+
view
29+
returns (uint256 inLimit, uint256 outLimit);
30+
31+
/**
32+
* @notice Computes the inverse of the `f()` function for the EulerSwap liquidity curve.
33+
* @dev Solves for `x` given `y` using the quadratic formula derived from the liquidity curve:
34+
* x = (-b + sqrt(b^2 + 4ac)) / 2a
35+
* Utilises mulDiv to avoid overflow and ensures precision with upward rounding.
36+
*
37+
* @param y The y-coordinate input value (must be greater than `y0`).
38+
* @param px Price factor for the x-axis (scaled by 1e18, between 1e18 and 1e36).
39+
* @param py Price factor for the y-axis (scaled by 1e18, between 1e18 and 1e36).
40+
* @param x0 Reference x-value on the liquidity curve (≤ 2^112 - 1).
41+
* @param y0 Reference y-value on the liquidity curve (≤ 2^112 - 1).
42+
* @param c Curve parameter shaping liquidity concentration (scaled by 1e18, between 0 and 1e18).
43+
*
44+
* @return x The computed x-coordinate on the liquidity curve.
45+
*
46+
* @custom:precision Uses rounding up to maintain precision in all calculations.
47+
* @custom:safety FullMath handles potential overflow in the b^2 computation.
48+
* @custom:requirement Input `y` must be strictly greater than `y0`; otherwise, the function will revert.
49+
*/
50+
function fInverse(uint256 y, uint256 px, uint256 py, uint256 x0, uint256 y0, uint256 c)
51+
external
52+
pure
53+
returns (uint256);
2454
}

0 commit comments

Comments
 (0)