Skip to content

Commit 02db36d

Browse files
committed
periphery: getLimits
1 parent 957d3b6 commit 02db36d

File tree

2 files changed

+131
-9
lines changed

2 files changed

+131
-9
lines changed

src/EulerSwapPeriphery.sol

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,7 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
102102
// exactIn: decrease received amountIn, rounding down
103103
if (exactIn) amount = amount * feeMultiplier / 1e18;
104104

105-
bool asset0IsInput;
106-
{
107-
address asset0 = eulerSwap.asset0();
108-
address asset1 = eulerSwap.asset1();
109-
110-
if (tokenIn == asset0 && tokenOut == asset1) asset0IsInput = true;
111-
else if (tokenIn == asset1 && tokenOut == asset0) asset0IsInput = false;
112-
else revert UnsupportedPair();
113-
}
105+
bool asset0IsInput = checkTokens(eulerSwap, tokenIn, tokenOut);
114106

115107
uint256 quote = binarySearch(eulerSwap, reserve0, reserve1, amount, exactIn, asset0IsInput);
116108

@@ -242,4 +234,71 @@ contract EulerSwapPeriphery is IEulerSwapPeriphery {
242234
// Compute and return x = fInverse(y) using the quadratic formula
243235
return Math.mulDiv(uint256(int256(sqrt) - B), 1e18, A, Math.Rounding.Ceil);
244236
}
237+
238+
/// @dev Max amount the pool can buy of tokenIn and sell of tokenOut
239+
function getLimits(address eulerSwap, address tokenIn, address tokenOut)
240+
external
241+
view
242+
returns (uint256 inLimit, uint256 outLimit)
243+
{
244+
IEulerSwap es = IEulerSwap(eulerSwap);
245+
if (!IEVC(es.EVC()).isAccountOperatorAuthorized(es.eulerAccount(), eulerSwap)) return (0, 0);
246+
247+
inLimit = outLimit = type(uint112).max;
248+
bool asset0IsInput = checkTokens(es, tokenIn, tokenOut);
249+
250+
// Supply caps on input
251+
{
252+
IEVault vault = IEVault(asset0IsInput ? es.vault0() : es.vault1());
253+
uint256 maxDeposit = vault.debtOf(es.eulerAccount()) + vault.maxDeposit(es.eulerAccount());
254+
if (maxDeposit < inLimit) inLimit = maxDeposit;
255+
}
256+
257+
// Remaining reserves of output
258+
259+
{
260+
(uint112 reserve0, uint112 reserve1,) = es.getReserves();
261+
uint112 reserveLimit = asset0IsInput ? reserve1 : reserve0;
262+
if (reserveLimit < outLimit) outLimit = reserveLimit;
263+
}
264+
265+
// Remaining cash and borrow caps in output
266+
267+
{
268+
IEVault vault = IEVault(asset0IsInput ? es.vault1() : es.vault0());
269+
270+
uint256 cash = vault.cash();
271+
if (cash < outLimit) outLimit = cash;
272+
273+
(, uint16 borrowCap) = vault.caps();
274+
uint256 maxWithdraw = decodeCap(uint256(borrowCap));
275+
maxWithdraw = vault.totalBorrows() > maxWithdraw ? 0 : maxWithdraw - vault.totalBorrows();
276+
if (maxWithdraw > cash) maxWithdraw = cash;
277+
maxWithdraw += vault.convertToAssets(vault.balanceOf(es.eulerAccount()));
278+
if (maxWithdraw < outLimit) outLimit = maxWithdraw;
279+
}
280+
}
281+
282+
function decodeCap(uint256 amountCap) internal pure returns (uint256) {
283+
if (amountCap == 0) return type(uint256).max;
284+
285+
unchecked {
286+
// Cannot overflow because this is less than 2**256:
287+
// 10**(2**6 - 1) * (2**10 - 1) = 1.023e+66
288+
return 10 ** (amountCap & 63) * (amountCap >> 6) / 100;
289+
}
290+
}
291+
292+
function checkTokens(IEulerSwap eulerSwap, address tokenIn, address tokenOut)
293+
internal
294+
view
295+
returns (bool asset0IsInput)
296+
{
297+
address asset0 = eulerSwap.asset0();
298+
address asset1 = eulerSwap.asset1();
299+
300+
if (tokenIn == asset0 && tokenOut == asset1) asset0IsInput = true;
301+
else if (tokenIn == asset1 && tokenOut == asset0) asset0IsInput = false;
302+
else revert UnsupportedPair();
303+
}
245304
}

test/Limits.t.sol

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity ^0.8.24;
3+
4+
import {EulerSwapTestBase, EulerSwap, EulerSwapPeriphery, IEulerSwap} from "./EulerSwapTestBase.t.sol";
5+
6+
contract LimitsTest is EulerSwapTestBase {
7+
EulerSwap public eulerSwap;
8+
9+
function setUp() public virtual override {
10+
super.setUp();
11+
12+
eulerSwap = createEulerSwap(50e18, 50e18, 0, 1e18, 1e18, 0.4e18, 0.85e18);
13+
}
14+
15+
function test_basicLimits() public {
16+
(uint256 inLimit, uint256 outLimit) =
17+
periphery.getLimits(address(eulerSwap), address(assetTST), address(assetTST2));
18+
19+
assertEq(inLimit, type(uint112).max - 110e18); // max uint minus 110 (100 deposited by depositor, 10 by holder)
20+
assertEq(outLimit, 60e18);
21+
}
22+
23+
function test_supplyCapExceeded() public {
24+
eTST.setCaps(uint16(2.72e2 << 6) | 18, 0);
25+
26+
(uint256 inLimit, uint256 outLimit) =
27+
periphery.getLimits(address(eulerSwap), address(assetTST), address(assetTST2));
28+
29+
assertEq(inLimit, 0); // cap exceeded
30+
assertEq(outLimit, 60e18);
31+
}
32+
33+
function test_supplyCapExtra() public {
34+
eTST.setCaps(uint16(2.72e2 << 6) | (18 + 2), 0);
35+
36+
(uint256 inLimit, uint256 outLimit) =
37+
periphery.getLimits(address(eulerSwap), address(assetTST), address(assetTST2));
38+
39+
assertEq(inLimit, 162e18); // 272 - 110
40+
assertEq(outLimit, 60e18);
41+
}
42+
43+
function test_utilisation() public {
44+
vm.prank(depositor);
45+
eTST2.withdraw(95e18, address(depositor), address(depositor));
46+
47+
(uint256 inLimit, uint256 outLimit) =
48+
periphery.getLimits(address(eulerSwap), address(assetTST), address(assetTST2));
49+
50+
assertEq(inLimit, type(uint112).max - 110e18);
51+
assertEq(outLimit, 15e18); // 110 - 95
52+
}
53+
54+
function test_borrowCap() public {
55+
eTST2.setCaps(0, uint16(8.5e2 << 6) | 18);
56+
57+
(uint256 inLimit, uint256 outLimit) =
58+
periphery.getLimits(address(eulerSwap), address(assetTST), address(assetTST2));
59+
60+
assertEq(inLimit, type(uint112).max - 110e18);
61+
assertEq(outLimit, 18.5e18); // 10 in balance, plus 8.5 borrow cap
62+
}
63+
}

0 commit comments

Comments
 (0)