Skip to content

Commit 0e1b1a2

Browse files
committed
avoid enabling collateral when not necessary, to prevent E_AccountLiquidity errors when account starts in violation
1 parent 382d44f commit 0e1b1a2

File tree

4 files changed

+142
-4
lines changed

4 files changed

+142
-4
lines changed

src/EulerSwap.sol

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,18 @@ contract EulerSwap is IEulerSwap, EVCUtil, UniswapHook {
139139
FundsLib.approveVault(sParams.borrowVault1);
140140
}
141141

142-
IEVC(evc).enableCollateral(sParams.eulerAccount, sParams.supplyVault0);
143-
IEVC(evc).enableCollateral(sParams.eulerAccount, sParams.supplyVault1);
142+
if (
143+
!IEVC(evc).isCollateralEnabled(sParams.eulerAccount, sParams.supplyVault0)
144+
&& sParams.borrowVault1 != address(0)
145+
) {
146+
IEVC(evc).enableCollateral(sParams.eulerAccount, sParams.supplyVault0);
147+
}
148+
if (
149+
!IEVC(evc).isCollateralEnabled(sParams.eulerAccount, sParams.supplyVault1)
150+
&& sParams.borrowVault0 != address(0)
151+
) {
152+
IEVC(evc).enableCollateral(sParams.eulerAccount, sParams.supplyVault1);
153+
}
144154

145155
// Uniswap hook activation
146156

test/AccountLiquidity.t.sol

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity ^0.8.24;
3+
4+
import {IEVault, IEulerSwap, EulerSwapTestBase, EulerSwap, TestERC20} from "./EulerSwapTestBase.t.sol";
5+
6+
contract CollateralSwap is EulerSwapTestBase {
7+
IEVault public eTST_alt;
8+
IEVault public eTST2_alt;
9+
10+
EulerSwap public eulerSwap;
11+
12+
function setUp() public virtual override {
13+
super.setUp();
14+
15+
// Alt vaults
16+
17+
eTST_alt = IEVault(
18+
factory.createProxy(address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount))
19+
);
20+
21+
eTST2_alt = IEVault(
22+
factory.createProxy(address(0), true, abi.encodePacked(address(assetTST2), address(oracle), unitOfAccount))
23+
);
24+
25+
// Sets up a position and pushes account into health violation
26+
27+
eTST.setLTV(address(eTST2), 0, 0, 0);
28+
eTST2.setLTV(address(eTST), 0, 0, 0);
29+
eTST.setLTV(address(eTST3), 0, 0, 0);
30+
31+
// Set new LTVs
32+
eTST3.setLTV(address(eTST), 0.9e4, 0.9e4, 0);
33+
eTST3.setLTV(address(eTST2), 0.9e4, 0.9e4, 0);
34+
35+
// Mint another 40 of the collateral assets (50 total in each)
36+
mintAndDeposit(holder, eTST, 40e18);
37+
mintAndDeposit(holder, eTST2, 40e18);
38+
39+
// Borrow 80 TST3
40+
vm.startPrank(holder);
41+
42+
evc.enableCollateral(holder, address(eTST));
43+
evc.enableCollateral(holder, address(eTST2));
44+
evc.enableController(holder, address(eTST3));
45+
46+
eTST3.borrow(80e18, address(0xdead)); // burning simulates a looped position
47+
48+
vm.stopPrank();
49+
50+
oracle.setPrice(address(assetTST), unitOfAccount, 0.001e18);
51+
oracle.setPrice(address(eTST), unitOfAccount, 0.001e18);
52+
}
53+
54+
function test_violation() public {
55+
// Would fail if EulerSwap wasn't calling isCollateralEnabled() first, because enableCollateral()
56+
// performs an accountStatusCheck.
57+
eulerSwap = createEulerSwap(49e18, 49e18, 0, 1e18, 1e18, 0.9e18, 0.9e18);
58+
}
59+
60+
function test_violation2() public {
61+
// However, a different vault does require enabling collateral, so this fails with E_AccountLiquidity
62+
63+
uint112 reserve0 = 60e18;
64+
uint112 reserve1 = 60e18;
65+
66+
(IEulerSwap.StaticParams memory sParams, IEulerSwap.DynamicParams memory dParams) =
67+
getEulerSwapParams(reserve0, reserve1, 1e18, 1e18, 0.85e18, 0.85e18, 0, address(0), 0, address(0));
68+
IEulerSwap.InitialState memory initialState = IEulerSwap.InitialState({reserve0: reserve0, reserve1: reserve1});
69+
70+
sParams.supplyVault0 = address(eTST_alt);
71+
72+
expectAccountLiquidityRevert = true;
73+
eulerSwap = createEulerSwapFull(sParams, dParams, initialState);
74+
}
75+
76+
function test_violation3() public {
77+
// Unless the borrow vault of the other asset is disabled, then no collateral enabling is required.
78+
79+
uint112 reserve0 = 60e18;
80+
uint112 reserve1 = 60e18;
81+
82+
(IEulerSwap.StaticParams memory sParams, IEulerSwap.DynamicParams memory dParams) =
83+
getEulerSwapParams(reserve0, reserve1, 1e18, 1e18, 0.85e18, 0.85e18, 0, address(0), 0, address(0));
84+
IEulerSwap.InitialState memory initialState = IEulerSwap.InitialState({reserve0: reserve0, reserve1: reserve1});
85+
86+
sParams.supplyVault0 = address(eTST_alt);
87+
sParams.borrowVault1 = address(0);
88+
89+
eulerSwap = createEulerSwapFull(sParams, dParams, initialState);
90+
}
91+
92+
// Next 2 tests are same as previous 2, but reversed assets
93+
94+
function test_violation4() public {
95+
// However, a different vault does require enabling collateral, so this fails with E_AccountLiquidity
96+
97+
uint112 reserve0 = 60e18;
98+
uint112 reserve1 = 60e18;
99+
100+
(IEulerSwap.StaticParams memory sParams, IEulerSwap.DynamicParams memory dParams) =
101+
getEulerSwapParams(reserve0, reserve1, 1e18, 1e18, 0.85e18, 0.85e18, 0, address(0), 0, address(0));
102+
IEulerSwap.InitialState memory initialState = IEulerSwap.InitialState({reserve0: reserve0, reserve1: reserve1});
103+
104+
sParams.supplyVault1 = address(eTST2_alt);
105+
106+
expectAccountLiquidityRevert = true;
107+
eulerSwap = createEulerSwapFull(sParams, dParams, initialState);
108+
}
109+
110+
function test_violation5() public {
111+
// Unless the borrow vault of the other asset is disabled, then no collateral enabling is required.
112+
113+
uint112 reserve0 = 60e18;
114+
uint112 reserve1 = 60e18;
115+
116+
(IEulerSwap.StaticParams memory sParams, IEulerSwap.DynamicParams memory dParams) =
117+
getEulerSwapParams(reserve0, reserve1, 1e18, 1e18, 0.85e18, 0.85e18, 0, address(0), 0, address(0));
118+
IEulerSwap.InitialState memory initialState = IEulerSwap.InitialState({reserve0: reserve0, reserve1: reserve1});
119+
120+
sParams.supplyVault1 = address(eTST2_alt);
121+
sParams.borrowVault0 = address(0);
122+
123+
eulerSwap = createEulerSwapFull(sParams, dParams, initialState);
124+
}
125+
}

test/Challenge.t.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import {IEVault, IEulerSwap, EulerSwapTestBase, EulerSwap, TestERC20} from "./Eu
66
contract ChallengeTest is EulerSwapTestBase {
77
EulerSwap public eulerSwap;
88

9-
error E_AccountLiquidity();
10-
119
function setUp() public virtual override {
1210
super.setUp();
1311

test/EulerSwapTestBase.t.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ contract EulerSwapTestBase is EVaultTestBase {
3535
uint256 currSalt = 0;
3636
address installedOperator;
3737
bool expectInsufficientValidityBondRevert = false;
38+
bool expectAccountLiquidityRevert = false;
39+
40+
error E_AccountLiquidity();
3841

3942
modifier monotonicHolderNAV() {
4043
int256 orig = getHolderNAV();
@@ -158,7 +161,9 @@ contract EulerSwapTestBase is EVaultTestBase {
158161

159162
uint256 ethBalance = holder.balance;
160163
vm.prank(holder);
164+
if (expectAccountLiquidityRevert) vm.expectRevert(E_AccountLiquidity.selector);
161165
EulerSwap eulerSwap = EulerSwap(eulerSwapFactory.deployPool(sParams, dParams, initialState, salt));
166+
if (expectAccountLiquidityRevert) return eulerSwap;
162167

163168
vm.prank(holder);
164169
if (expectInsufficientValidityBondRevert) vm.expectRevert(EulerSwapRegistry.InsufficientValidityBond.selector);

0 commit comments

Comments
 (0)