Skip to content

Commit 67736d3

Browse files
committed
feat: add rebalancing calcs, and arbitrage steps to the example script
1 parent 9dc543d commit 67736d3

2 files changed

Lines changed: 313 additions & 117 deletions

File tree

script/scenarios/EulerSwapDebtScenario.s.sol

Lines changed: 156 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import {Hooks} from "v4-core/libraries/Hooks.sol";
99
import {MetaProxyDeployer} from "euler-swap/utils/MetaProxyDeployer.sol";
1010
import {TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol";
1111
import {IEVault} from "evk/EVault/IEVault.sol";
12+
import {IEVC} from "evc/interfaces/IEthereumVaultConnector.sol";
13+
import {IEulerSwapFactory} from "euler-swap/interfaces/IEulerSwapFactory.sol";
14+
import {CurveLib} from "euler-swap/libraries/CurveLib.sol";
1215

1316
import {console2 as console} from "forge-std/console2.sol";
1417

@@ -20,11 +23,15 @@ contract EulerSwapDebtScenario is DeployScenario {
2023
vm.startBroadcast(user3PK);
2124
eUSDC.setLTV(address(eWETH), 0.65e4, 0.67e4, 0);
2225
eUSDC.setLTV(address(eUSDT), 0.85e4, 0.87e4, 0);
26+
eWETH.setLTV(address(eUSDC), 0.65e4, 0.67e4, 0);
27+
eWETH.setLTV(address(eUSDT), 0.85e4, 0.87e4, 0);
28+
eUSDT.setLTV(address(eWETH), 0.85e4, 0.87e4, 0);
29+
eUSDT.setLTV(address(eUSDC), 0.85e4, 0.87e4, 0);
2330
deployJITpilot();
2431
vm.stopBroadcast();
2532

2633
vm.startBroadcast(user2PK);
27-
createEulerSwap();
34+
deployEulerSwap(getInitialEulerSwapParams(), false);
2835
authorizeJITpilot(user2);
2936
jitPilot.configureLp(user2, 1.4e18, 1.5e18);
3037
vm.stopBroadcast();
@@ -40,26 +47,14 @@ contract EulerSwapDebtScenario is DeployScenario {
4047
borrowFromVault(user0, user0PK, address(eWETH), 500_000e18);
4148
borrowFromVault(user1, user1PK, address(eUSDC), 1_600_000_000e6);
4249

43-
console.log("user2 vault deposits: ", eWETH.balanceOf(user2));
44-
console.log("user2 vault deposits: ", eUSDC.balanceOf(user2));
45-
4650
// buy USDC so that user2's EulerSwap position has to borrow USDC
4751
uint256 amountOut = 286_500_000e6;
4852
uint256 amountIn = _swapExactOut(address(assetWETH), address(assetUSDC), amountOut, user0, user0PK);
49-
console.log("SOLD %s WETH FOR %s USDC", amountIn, amountOut);
53+
console.log("marketUser SOLD %s WETH FOR %s USDC", amountIn, amountOut);
5054

5155
// user2 is in debt now. Let's fetch that data
52-
JITpilot.BlockData memory blockData = jitPilot.getData(user2);
53-
54-
console.log("==========================================================");
55-
console.log("EulerSwap instance is in debt now:");
56-
console.log("allowedLTV: ", blockData.allowedLTV);
57-
console.log("currentLTV: ", blockData.currentLTV);
58-
console.log("swapFees: ", blockData.swapFees);
59-
console.log("netInterest: ", blockData.netInterest);
60-
console.log("depositValue: ", blockData.depositValue);
61-
console.log("controllerVault: ", blockData.controllerVault);
62-
console.log("==========================================================");
56+
console.log("EulerSwap state now after servicing trades: ");
57+
printEulerSwapData(user2);
6358

6459
// Get a quote for the current price of ETH in the EulerSwap instance
6560
uint256 ethPrice = eulerSwapPeriphery.quoteExactOutput(eulerSwap, address(assetUSDC), address(assetWETH), 1e18);
@@ -72,6 +67,33 @@ contract EulerSwapDebtScenario is DeployScenario {
7267
// Let's see what the rebalancing params would be
7368
IEulerSwap.Params memory newParams = jitPilot.getRebalancingParams(user2);
7469
printEulerSwapParams(newParams);
70+
71+
// let's do the actual rebalancing then
72+
vm.startBroadcast(user2PK);
73+
jitPilot.updateMetrics(user2);
74+
IEVC(evc).setAccountOperator(user2, eulerSwap, false);
75+
IEulerSwapFactory(eulerSwapFactory).uninstallPool();
76+
deployEulerSwap(newParams, true);
77+
vm.stopBroadcast();
78+
79+
// Now let's see if the rebalancing was successful
80+
address poolAddr = eulerSwapFactory.poolByEulerAccount(user2);
81+
newParams = IEulerSwap(poolAddr).getParams();
82+
printEulerSwapParams(newParams);
83+
84+
// Let's buy some WETH on the EulerSwap pool and see the effect on the debt
85+
// sell USDC so that user2's EulerSwap position has to borrow USDC
86+
uint256 newEthPrice = eulerSwapPeriphery.quoteExactOutput(eulerSwap, address(assetUSDC), address(assetWETH), 1e18);
87+
console.log("price of ETH (market): ", ethPrice);
88+
console.log("price of ETH (EulerSwap): ", newEthPrice);
89+
amountIn = 105_000_000e6;
90+
amountOut = _swapExactIn(address(assetUSDC), address(assetWETH), amountIn, user0, user0PK);
91+
console.log("marketUser BOUGHT %s WETH FOR %s USDC (price: %s)", amountOut, amountIn, amountIn * 1e18 / amountOut);
92+
93+
// Let's see the new state of the EulerSwap pool after arbitrage
94+
console.log("EulerSwap state now (after arbitrage):");
95+
printEulerSwapData(user2);
96+
console.log("price of ETH (EulerSwap, after arbitrage): ", eulerSwapPeriphery.quoteExactOutput(eulerSwap, address(assetUSDC), address(assetWETH), 1e18));
7597
}
7698

7799
function giveTonsOfCash(address user) internal virtual {
@@ -98,26 +120,39 @@ contract EulerSwapDebtScenario is DeployScenario {
98120
evc.setAccountOperator(user, address(jitPilot), true);
99121
}
100122

101-
function createEulerSwap() internal {
102-
// Create pool parameters
103-
IEulerSwap.Params memory poolParams = IEulerSwap.Params({
104-
vault0: address(eUSDC),
105-
vault1: address(eWETH),
106-
eulerAccount: user2,
107-
equilibriumReserve0: 800_000_000e6,
108-
equilibriumReserve1: 280_000e18,
109-
priceX: 1e18,
110-
priceY: 2865e6,
111-
concentrationX: 0.9e18,
112-
concentrationY: 0.9e18,
113-
fee: 0,
114-
protocolFee: 0,
115-
protocolFeeRecipient: address(0)
116-
});
123+
function deployEulerSwap(IEulerSwap.Params memory poolParams, bool rebalancing) internal {
117124

118-
console.log("INITIAL EULERSWAP PARAMS");
125+
console.log("DEPLOYING EULERSWAP WITH PARAMS");
119126
printEulerSwapParams(poolParams);
120127

128+
bool asset0IsDebt = getCurrentControllerVault(poolParams.eulerAccount) == poolParams.vault0;
129+
uint112 currentReserve0 = poolParams.equilibriumReserve0;
130+
uint112 currentReserve1 = poolParams.equilibriumReserve1;
131+
132+
IEulerSwap.InitialState memory initialState;
133+
if (!rebalancing) {
134+
initialState = IEulerSwap.InitialState({currReserve0: poolParams.equilibriumReserve0, currReserve1: poolParams.equilibriumReserve1});
135+
} else {
136+
if (asset0IsDebt) {
137+
{
138+
uint256 deltaReservesAsset0 = poolParams.equilibriumReserve0 * 1/3;
139+
// uint256 deltaReservesAsset1 = deltaReservesAsset0 * poolParams.priceX / poolParams.priceY;
140+
currentReserve0 = uint112(poolParams.equilibriumReserve0 - deltaReservesAsset0);
141+
// currentReserve1 = uint112(poolParams.equilibriumReserve1 + deltaReservesAsset1);
142+
currentReserve1 = uint112(CurveLib.f(uint256(currentReserve0), uint256(poolParams.priceX), uint256(poolParams.priceY), uint256(poolParams.equilibriumReserve0), uint256(poolParams.equilibriumReserve1), uint256(poolParams.concentrationX)));
143+
}
144+
} else {
145+
{
146+
uint256 deltaReservesAsset1 = poolParams.equilibriumReserve1 * 1/3;
147+
// uint256 deltaReservesAsset0 = deltaReservesAsset1 * poolParams.priceY / poolParams.priceX;
148+
currentReserve1 = uint112(poolParams.equilibriumReserve1 - deltaReservesAsset1);
149+
// currentReserve0 = uint112(poolParams.equilibriumReserve0 + deltaReservesAsset0);
150+
currentReserve0 = uint112(CurveLib.fInverse(uint256(currentReserve1), uint256(poolParams.priceY), uint256(poolParams.priceX), uint256(poolParams.equilibriumReserve1), uint256(poolParams.equilibriumReserve0), uint256(poolParams.concentrationY)));
151+
}
152+
}
153+
initialState = IEulerSwap.InitialState({currReserve0: currentReserve0, currReserve1: currentReserve1});
154+
}
155+
121156
// Define required hook flags
122157
uint160 flags = uint160(
123158
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
@@ -129,11 +164,21 @@ contract EulerSwapDebtScenario is DeployScenario {
129164
(address hookAddress, bytes32 salt) = HookMiner.find(address(eulerSwapFactory), flags, creationCode);
130165

131166
eulerSwap = hookAddress;
132-
evc.setAccountOperator(user2, eulerSwap, true);
133-
134-
eulerSwapFactory.deployPool(
135-
poolParams, IEulerSwap.InitialState({currReserve0: 800_000_000e6, currReserve1: 280_000e18}), salt
136-
);
167+
// Deploy pool via EVC batch
168+
IEVC.BatchItem[] memory items = new IEVC.BatchItem[](2);
169+
items[0] = IEVC.BatchItem({
170+
onBehalfOfAccount: address(0),
171+
targetContract: address(evc),
172+
value: 0,
173+
data: abi.encodeCall(evc.setAccountOperator, (user2, eulerSwap, true))
174+
});
175+
items[1] = IEVC.BatchItem({
176+
onBehalfOfAccount: user2,
177+
targetContract: address(eulerSwapFactory),
178+
value: 0,
179+
data: abi.encodeCall(IEulerSwapFactory.deployPool, (poolParams, initialState, salt))
180+
});
181+
evc.batch(items);
137182

138183
string memory result = vm.serializeAddress("eulerSwap", "eulerSwap", address(eulerSwap));
139184
vm.writeJson(result, "./dev-ctx/addresses/31337/EulerSwapAddresses.json");
@@ -172,7 +217,7 @@ contract EulerSwapDebtScenario is DeployScenario {
172217
{
173218
vm.startBroadcast(userPK);
174219
amountIn = eulerSwapPeriphery.quoteExactOutput(eulerSwap, tokenIn, tokenOut, amountOut);
175-
console.log("amountIn", amountIn);
220+
// console.log("amountIn", amountIn);
176221

177222
TestERC20(tokenIn).approve(address(eulerSwapPeriphery), type(uint256).max);
178223
eulerSwapPeriphery.swapExactOut(
@@ -189,7 +234,47 @@ contract EulerSwapDebtScenario is DeployScenario {
189234
return amountIn;
190235
}
191236

192-
function printEulerSwapParams(IEulerSwap.Params memory params) internal {
237+
function _swapExactIn(address tokenIn, address tokenOut, uint256 amountIn, address receiver, uint256 userPK)
238+
internal
239+
returns (uint256 amountOut)
240+
{
241+
vm.startBroadcast(userPK);
242+
amountOut = eulerSwapPeriphery.quoteExactInput(eulerSwap, tokenIn, tokenOut, amountIn);
243+
// console.log("amountOut", amountOut);
244+
245+
TestERC20(tokenIn).approve(address(eulerSwapPeriphery), type(uint256).max);
246+
eulerSwapPeriphery.swapExactIn(
247+
eulerSwap,
248+
tokenIn,
249+
tokenOut,
250+
amountIn,
251+
receiver,
252+
amountOut * 99 / 100, // allow up to 1% slippage
253+
0
254+
);
255+
vm.stopBroadcast();
256+
257+
return amountOut;
258+
}
259+
260+
function getInitialEulerSwapParams() internal view returns (IEulerSwap.Params memory) {
261+
return IEulerSwap.Params({
262+
vault0: address(eUSDC),
263+
vault1: address(eWETH),
264+
eulerAccount: user2,
265+
equilibriumReserve0: 800_000_000e6,
266+
equilibriumReserve1: 280_000e18,
267+
priceX: 1e18,
268+
priceY: 2865e6,
269+
concentrationX: 0.9e18,
270+
concentrationY: 0.9e18,
271+
fee: 0,
272+
protocolFee: 0,
273+
protocolFeeRecipient: address(0)
274+
});
275+
}
276+
277+
function printEulerSwapParams(IEulerSwap.Params memory params) internal pure {
193278
console.log("==========================================================");
194279
console.log("vault0: ", params.vault0);
195280
console.log("vault1: ", params.vault1);
@@ -205,4 +290,35 @@ contract EulerSwapDebtScenario is DeployScenario {
205290
console.log("protocolFeeRecipient: ", params.protocolFeeRecipient);
206291
console.log("==========================================================");
207292
}
293+
294+
function printEulerSwapData(address user) internal view {
295+
296+
JITpilot.BlockData memory blockData = jitPilot.getData(user);
297+
298+
console.log("==========================================================");
299+
console.log("healthFactor: ", blockData.allowedLTV * 1e4 / blockData.currentLTV / 100, "%");
300+
console.log("allowedLTV: ", blockData.allowedLTV);
301+
console.log("currentLTV: ", blockData.currentLTV);
302+
console.log("swapFees: ", blockData.swapFees);
303+
console.log("netInterest: ", blockData.netInterest);
304+
console.log("depositValue: ", blockData.depositValue);
305+
console.log("controllerVault: ", blockData.controllerVault);
306+
console.log("==========================================================");
307+
}
308+
309+
function getCurrentControllerVault(address lp) internal view returns (address) {
310+
address[] memory controllerVaults = evc.getControllers(lp);
311+
address currentControllerVault;
312+
313+
if (controllerVaults.length == 0) return address(0);
314+
315+
// find which of the LP's controller vaults is the enabled debt vault
316+
for (uint256 i; i < controllerVaults.length; ++i) {
317+
if (evc.isControllerEnabled(lp, controllerVaults[i])) {
318+
currentControllerVault = controllerVaults[i];
319+
break;
320+
}
321+
}
322+
return currentControllerVault;
323+
}
208324
}

0 commit comments

Comments
 (0)