From 7f2a0c67af9b09600f9268693ce1781cda3f105f Mon Sep 17 00:00:00 2001 From: MazyGio Date: Thu, 3 Jul 2025 19:54:40 -0500 Subject: [PATCH 1/6] feat: add modules for Euler ecosystem contracts. update scripts and remappings --- .gitmodules | 15 +++++++++++++++ chains.js | 30 ++++++++++++++++++++++++++++++ devland.sh | 28 ++++++++++++++++++++++++++++ foundry.toml | 3 ++- remappings.txt | 2 +- script/Counter.s.sol | 19 ------------------- 6 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 chains.js create mode 100755 devland.sh delete mode 100644 script/Counter.s.sol diff --git a/.gitmodules b/.gitmodules index 031cf40..b97dc5a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,18 @@ [submodule "libflat/ethereum-vault-connector"] path = libflat/ethereum-vault-connector url = https://github.com/euler-xyz/ethereum-vault-connector +[submodule "libflat/permit2"] + path = libflat/permit2 + url = https://github.com/Uniswap/permit2 +[submodule "libflat/openzeppelin-contracts"] + path = libflat/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "libflat/v4-core"] + path = libflat/v4-core + url = https://github.com/uniswap/v4-core +[submodule "libflat/v4-periphery"] + path = libflat/v4-periphery + url = https://github.com/uniswap/v4-periphery +[submodule "libflat/solmate"] + path = libflat/solmate + url = https://github.com/rari-capital/solmate diff --git a/chains.js b/chains.js new file mode 100644 index 0000000..95c4199 --- /dev/null +++ b/chains.js @@ -0,0 +1,30 @@ +let chains = [ + //// PRODUCTION + + { + chainId: 31337, + name: 'dev', + safeBaseUrl: 'https://app.safe.global', + safeAddressPrefix: 'dev', + status: 'beta', + }, + ]; + + + + const fs = require("node:fs"); + + for (let c of chains) { + let addrsDir = `./dev-ctx/addresses/${c.chainId}/`; + + c.addresses = {}; + + for (const file of fs.readdirSync(addrsDir)) { + if (!file.endsWith('Addresses.json')) continue; + let section = file.replace(/Addresses[.]json$/, 'Addrs'); + section = (section[0] + '').toLowerCase() + section.substr(1); + c.addresses[section] = JSON.parse(fs.readFileSync(`${addrsDir}/${file}`).toString()); + } + } + + fs.writeFileSync('./dev-ctx/EulerChains.json', JSON.stringify(chains)); \ No newline at end of file diff --git a/devland.sh b/devland.sh new file mode 100755 index 0000000..956d599 --- /dev/null +++ b/devland.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e + +SCENARIO=${1:-EulerSwapBasic} + + +function cleanup { + pkill -P $$ +} + +trap cleanup EXIT + + +PORT=${PORT:-8545} +anvil --port $PORT --code-size-limit 100000 & +sleep 1 + + +RPC_URL=http://127.0.0.1:$PORT bash ./deploy-scenario.sh "$SCENARIO" + + +echo ------------------------------- +echo DEVLAND READY +echo SCENARIO = $SCENARIO +echo ------------------------------- + +wait diff --git a/foundry.toml b/foundry.toml index b812041..ec90863 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,4 +5,5 @@ libs = ["lib"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options -auto_detect_remappings = false \ No newline at end of file +auto_detect_remappings = false +fs_permissions = [{ access = "read-write", path = "./dev-ctx/"}] \ No newline at end of file diff --git a/remappings.txt b/remappings.txt index b6b41c2..78d88e2 100644 --- a/remappings.txt +++ b/remappings.txt @@ -5,7 +5,7 @@ openzeppelin-contracts/=libflat/openzeppelin-contracts/contracts/ @uniswap/v4-periphery/=libflat/v4-periphery/ v4-periphery/=libflat/v4-periphery/ v4-core/=libflat/v4-core/src/ -solmate/=libflat/solmate/ +solmate/=libflat/solmate/src/ ethereum-vault-connector/=libflat/ethereum-vault-connector/src/ evc/=libflat/ethereum-vault-connector/src/ diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index 529964d..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {JITpilot} from "../src/JITpilot.sol"; - -contract JITpilotScript is Script { - // JITpilot public JITpilot; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - // JITpilot = new JITpilot(); - - vm.stopBroadcast(); - } -} From 72df069730c29a107a98be66fff4edddefd43c02 Mon Sep 17 00:00:00 2001 From: MazyGio Date: Thu, 3 Jul 2025 19:57:01 -0500 Subject: [PATCH 2/6] add scripts to deploy an EulerSwap instance that goes into debt --- .gitignore | 2 + deploy-scenario.sh | 14 + libflat/openzeppelin-contracts | 1 + libflat/permit2 | 1 + libflat/solmate | 1 + libflat/v4-core | 1 + libflat/v4-periphery | 1 + script/DeployScenario.s.sol | 330 +++++++++++++++++++ script/scenarios/EulerSwapDebtScenario.s.sol | 167 ++++++++++ 9 files changed, 518 insertions(+) create mode 100644 deploy-scenario.sh create mode 160000 libflat/openzeppelin-contracts create mode 160000 libflat/permit2 create mode 160000 libflat/solmate create mode 160000 libflat/v4-core create mode 160000 libflat/v4-periphery create mode 100644 script/DeployScenario.s.sol create mode 100644 script/scenarios/EulerSwapDebtScenario.s.sol diff --git a/.gitignore b/.gitignore index 3eea1cd..9d01b69 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ docs/ # Dotenv file .env + +/dev-ctx/ \ No newline at end of file diff --git a/deploy-scenario.sh b/deploy-scenario.sh new file mode 100644 index 0000000..ba3ba83 --- /dev/null +++ b/deploy-scenario.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +SCENARIO=$1 + +rm -rf dev-ctx/ +mkdir -p dev-ctx/{addresses,labels,priceapi}/31337 + +forge script --rpc-url ${RPC_URL:-http://127.0.0.1:8545} "script/scenarios/$SCENARIO.s.sol" --broadcast --code-size-limit 100000 -vv +cast rpc evm_increaseTime 86400 || true +cast rpc evm_mine || true + +node chains.js diff --git a/libflat/openzeppelin-contracts b/libflat/openzeppelin-contracts new file mode 160000 index 0000000..0aaa23e --- /dev/null +++ b/libflat/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 0aaa23e57df7eb0949a1d9ae90742c85f771a7cf diff --git a/libflat/permit2 b/libflat/permit2 new file mode 160000 index 0000000..cc56ad0 --- /dev/null +++ b/libflat/permit2 @@ -0,0 +1 @@ +Subproject commit cc56ad0f3439c502c246fc5cfcc3db92bb8b7219 diff --git a/libflat/solmate b/libflat/solmate new file mode 160000 index 0000000..c93f771 --- /dev/null +++ b/libflat/solmate @@ -0,0 +1 @@ +Subproject commit c93f7716c9909175d45f6ef80a34a650e2d24e56 diff --git a/libflat/v4-core b/libflat/v4-core new file mode 160000 index 0000000..b619b67 --- /dev/null +++ b/libflat/v4-core @@ -0,0 +1 @@ +Subproject commit b619b6718e31aa5b4fa0286520c455ceb950276d diff --git a/libflat/v4-periphery b/libflat/v4-periphery new file mode 160000 index 0000000..9628c36 --- /dev/null +++ b/libflat/v4-periphery @@ -0,0 +1 @@ +Subproject commit 9628c36b4f5083d19606e63224e4041fe748edae diff --git a/script/DeployScenario.s.sol b/script/DeployScenario.s.sol new file mode 100644 index 0000000..154466b --- /dev/null +++ b/script/DeployScenario.s.sol @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.27; + +// System + +import {Script, console} from "forge-std/Script.sol"; + +// Deploy base + +import {GenericFactory} from "evk/GenericFactory/GenericFactory.sol"; + +import {EVault} from "evk/EVault/EVault.sol"; +import {ProtocolConfig} from "evk/ProtocolConfig/ProtocolConfig.sol"; + +import {Dispatch} from "evk/EVault/Dispatch.sol"; + +import {Initialize} from "evk/EVault/modules/Initialize.sol"; +import {Token} from "evk/EVault/modules/Token.sol"; +import {Vault} from "evk/EVault/modules/Vault.sol"; +import {Borrowing} from "evk/EVault/modules/Borrowing.sol"; +import {Liquidation} from "evk/EVault/modules/Liquidation.sol"; +import {BalanceForwarder} from "evk/EVault/modules/BalanceForwarder.sol"; +import {Governance} from "evk/EVault/modules/Governance.sol"; +import {RiskManager} from "evk/EVault/modules/RiskManager.sol"; + +import {IEVault, IERC20} from "evk/EVault/IEVault.sol"; +import {TypesLib} from "evk/EVault/shared/types/Types.sol"; +import {Base} from "evk/EVault/shared/Base.sol"; + +import {EthereumVaultConnector} from "ethereum-vault-connector/EthereumVaultConnector.sol"; + +import {TestERC20} from "evk-test/mocks/TestERC20.sol"; +import {MockBalanceTracker} from "evk-test/mocks/MockBalanceTracker.sol"; +import {MockPriceOracle} from "evk-test/mocks/MockPriceOracle.sol"; +import {IRMTestDefault} from "evk-test/mocks/IRMTestDefault.sol"; +import {IHookTarget} from "evk/interfaces/IHookTarget.sol"; +import {SequenceRegistry} from "evk/SequenceRegistry/SequenceRegistry.sol"; + +// Euler swap + +import {TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol"; +import {IEVault} from "evk/EVault/IEVault.sol"; +import {IEulerSwap, IEVC, EulerSwap} from "euler-swap/EulerSwap.sol"; +import {EulerSwapFactory} from "euler-swap/EulerSwapFactory.sol"; +import {EulerSwapPeriphery} from "euler-swap/EulerSwapPeriphery.sol"; +import {PoolManagerDeployer} from "euler-swap/../test/utils/PoolManagerDeployer.sol"; + +// Maglev stuff + +import {MaglevLens} from "src/MaglevLens.sol"; + +struct Asset { + string symbol; + address asset; + address vault; + string price; + uint256 priceNum; +} + +contract DeployScenario is Script { + //////// Users + + uint256 user0PK = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; + uint256 user1PK = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d; + uint256 user2PK = 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a; + uint256 user3PK = 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6; + + address user0 = vm.addr(user0PK); + address user1 = vm.addr(user1PK); + address user2 = vm.addr(user2PK); + address user3 = vm.addr(user3PK); + + //////// Main system + + EthereumVaultConnector public evc; + address admin; + address feeReceiver; + address protocolFeeReceiver; + ProtocolConfig protocolConfig; + address balanceTracker; + MockPriceOracle oracle; + address unitOfAccount; + address permit2; + address sequenceRegistry; + GenericFactory public factory; + + Base.Integrations integrations; + Dispatch.DeployedModules modules; + + address initializeModule; + address tokenModule; + address vaultModule; + address borrowingModule; + address liquidationModule; + address riskManagerModule; + address balanceForwarderModule; + address governanceModule; + + //////// Tokens + + Asset[] assets; + + TestERC20 assetWETH; + IEVault eWETH; + + TestERC20 assetwstETH; + IEVault ewstETH; + + TestERC20 assetUSDC; + IEVault eUSDC; + + TestERC20 assetUSDT; + IEVault eUSDT; + + TestERC20 assetDAI; + IEVault eDAI; + + TestERC20 assetUSDZ; + IEVault eUSDZ; + + //////// EulerSwap + + address poolManager; + address eulerSwapImpl; + EulerSwapFactory eulerSwapFactory; + EulerSwapPeriphery eulerSwapPeriphery; + + //////// Maglev + + MaglevLens maglevLens; + + function run() public { + vm.startBroadcast(user3PK); + + deployEulerSystem(); + deployAssets(); + deployEulerSwap(); + deployMaglevLens(); + + vm.stopBroadcast(); + + addLiquidity(); + + setup(); + } + + function deployEulerSystem() internal { + admin = makeAddr("admin"); + feeReceiver = makeAddr("feeReceiver"); + protocolFeeReceiver = makeAddr("protocolFeeReceiver"); + factory = new GenericFactory(user3); + + evc = new EthereumVaultConnector(); + protocolConfig = new ProtocolConfig(admin, protocolFeeReceiver); + balanceTracker = address(new MockBalanceTracker()); + oracle = new MockPriceOracle(); + unitOfAccount = address(1); + permit2 = address(0); + sequenceRegistry = address(new SequenceRegistry()); + integrations = + Base.Integrations(address(evc), address(protocolConfig), sequenceRegistry, balanceTracker, permit2); + + initializeModule = address(new Initialize(integrations)); + tokenModule = address(new Token(integrations)); + vaultModule = address(new Vault(integrations)); + borrowingModule = address(new Borrowing(integrations)); + liquidationModule = address(new Liquidation(integrations)); + riskManagerModule = address(new RiskManager(integrations)); + balanceForwarderModule = address(new BalanceForwarder(integrations)); + governanceModule = address(new Governance(integrations)); + + modules = Dispatch.DeployedModules({ + initialize: initializeModule, + token: tokenModule, + vault: vaultModule, + borrowing: borrowingModule, + liquidation: liquidationModule, + riskManager: riskManagerModule, + balanceForwarder: balanceForwarderModule, + governance: governanceModule + }); + + address evaultImpl = address(new EVault(integrations, modules)); + + factory.setImplementation(evaultImpl); + + string memory result = vm.serializeAddress("coreAddresses", "evc", address(evc)); + result = vm.serializeAddress("coreAddresses", "eVaultFactory", address(factory)); + vm.writeJson(result, "./dev-ctx/addresses/31337/CoreAddresses.json"); + } + + function genAsset(string memory symbol, uint8 decimals, string memory price, uint256 priceNum) + internal + returns (TestERC20, IEVault) + { + Asset memory a; + + a.symbol = symbol; + a.asset = address(new TestERC20(string(abi.encodePacked(symbol, " Token")), symbol, decimals, false)); + a.vault = factory.createProxy(address(0), true, abi.encodePacked(a.asset, address(oracle), unitOfAccount)); + a.price = price; + a.priceNum = priceNum; + + IEVault(a.vault).setHookConfig(address(0), 0); + IEVault(a.vault).setInterestRateModel(address(new IRMTestDefault())); + IEVault(a.vault).setMaxLiquidationDiscount(0.2e4); + IEVault(a.vault).setFeeReceiver(feeReceiver); + + assets.push(a); + return (TestERC20(a.asset), IEVault(a.vault)); + } + + function deployAssets() internal virtual { + (assetWETH, eWETH) = genAsset("WETH", 18, "2865", 2865e18); + (assetwstETH, ewstETH) = genAsset("wstETH", 18, "3055", 3055e18); + (assetUSDC, eUSDC) = genAsset("USDC", 6, "1.000142", 1e18 * 1e12); + (assetUSDT, eUSDT) = genAsset("USDT", 6, "0.999218", 1e18 * 1e12); + (assetDAI, eDAI) = genAsset("DAI", 18, "1.00123", 1e18); + (assetUSDZ, eUSDZ) = genAsset("USDZ", 6, "1.00081", 1e18 * 1e12); + + for (uint256 i; i < assets.length; ++i) { + oracle.setPrice(assets[i].vault, unitOfAccount, assets[i].priceNum); + + for (uint256 j; j < assets.length; ++j) { + if (i == j) continue; + IEVault(assets[i].vault).setLTV(assets[j].vault, 0.92e4, 0.94e4, 0); + } + } + + eWETH.setLTV(address(ewstETH), 0.5e4, 0.52e4, 0); // lower wstETH/WETH LTV for testing + ewstETH.setLTV(address(eWETH), 0.91e4, 0.93e4, 0); // change WETH/wstETH LTV for testing + ewstETH.setLTV(address(eUSDC), 0.8e4, 0.82e4, 0); // change USDC/wstETH LTV for testing + + eWETH.setLTV(address(eUSDC), 0.65e4, 0.67e4, 0); // change USDC/WETH LTV for testing + eWETH.setLTV(address(eUSDT), 0.85e4, 0.87e4, 0); // change USDT/WETH LTV for testing + + address[] memory vaults = new address[](assets.length); + for (uint256 i; i < assets.length; ++i) { + vaults[i] = assets[i].vault; + } + + { + string memory result = vm.serializeAddress("products", "vaults", vaults); + string memory obj = vm.serializeString("products2", "testing-product", result); + vm.writeJson(obj, "./dev-ctx/labels/31337/products.json"); + } + + { + string memory pricesFile = "./dev-ctx/priceapi/31337/prices.json"; + vm.writeLine(pricesFile, "{"); + + for (uint256 i; i < assets.length; ++i) { + string memory line = string( + abi.encodePacked( + "\"", + vm.toString(assets[i].asset), + "\": {\"price\":", + assets[i].price, + "}", + (i == assets.length - 1 ? "" : ",") + ) + ); + vm.writeLine(pricesFile, line); + } + + vm.writeLine(pricesFile, "}"); + } + } + + function deployEulerSwap() internal { + poolManager = address(PoolManagerDeployer.deploy(address(0))); + eulerSwapImpl = address(new EulerSwap(address(evc), poolManager)); + eulerSwapFactory = new EulerSwapFactory(address(evc), address(factory), eulerSwapImpl, address(0), address(0)); + eulerSwapPeriphery = new EulerSwapPeriphery(); + + string memory result = vm.serializeAddress("eulerSwap", "eulerSwapFactory", address(eulerSwapFactory)); + result = vm.serializeAddress("eulerSwap", "eulerSwapPeriphery", address(eulerSwapPeriphery)); + vm.writeJson(result, "./dev-ctx/addresses/31337/EulerSwapAddresses.json"); + } + + function deployMaglevLens() internal { + maglevLens = new MaglevLens(); + + string memory result = vm.serializeAddress("maglev", "maglevLens", address(maglevLens)); + vm.writeJson(result, "./dev-ctx/addresses/31337/MaglevAddresses.json"); + } + + function getSubaccount(address user, uint8 account) internal pure returns (address) { + return address(uint160(user) ^ account); + } + + function addLiquidity() internal virtual { + // user2 is passive depositor + vm.startBroadcast(user2PK); + + assetUSDC.mint(user2, 1000000e6); + assetUSDT.mint(user2, 1000000e6); + assetWETH.mint(user2, 100000e18); + assetwstETH.mint(user2, 100000e18); + assetDAI.mint(user2, 1000000e18); + assetUSDZ.mint(user2, 1000000e6); + + assetUSDC.approve(address(eUSDC), type(uint256).max); + assetUSDT.approve(address(eUSDT), type(uint256).max); + assetWETH.approve(address(eWETH), type(uint256).max); + assetwstETH.approve(address(ewstETH), type(uint256).max); + assetDAI.approve(address(eDAI), type(uint256).max); + assetUSDZ.approve(address(eUSDZ), type(uint256).max); + + eUSDC.deposit(1000000e6, user2); + eUSDT.deposit(1000000e6, user2); + eWETH.deposit(100000e18, user2); + ewstETH.deposit(100000e18, user2); + eDAI.deposit(1000000e18, user2); + eUSDZ.deposit(1000000e6, user2); + + vm.stopBroadcast(); + } + + function giveLotsOfCash(address user) internal virtual { + assetUSDC.mint(user, 1000000e6); + assetUSDT.mint(user, 1000000e6); + assetWETH.mint(user, 1000e18); + assetwstETH.mint(user, 1000e18); + assetDAI.mint(user, 1000000e18); + assetUSDZ.mint(user, 1000000e18); + } + + function setup() internal virtual {} +} \ No newline at end of file diff --git a/script/scenarios/EulerSwapDebtScenario.s.sol b/script/scenarios/EulerSwapDebtScenario.s.sol new file mode 100644 index 0000000..97a536b --- /dev/null +++ b/script/scenarios/EulerSwapDebtScenario.s.sol @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import {DeployScenario} from "../DeployScenario.s.sol"; +import {JITpilot} from "../../src/JITpilot.sol"; +import {IEulerSwap} from "euler-swap/interfaces/IEulerSwap.sol"; +import {HookMiner} from "../../libflat/euler-swap/test/utils/HookMiner.sol"; +import {Hooks} from "v4-core/libraries/Hooks.sol"; +import {MetaProxyDeployer} from "euler-swap/utils/MetaProxyDeployer.sol"; +import {console2 as console} from "forge-std/console2.sol"; +import {TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol"; +import {IEVault} from "evk/EVault/IEVault.sol"; + +contract EulerSwapDebtScenario is DeployScenario { + + address eulerSwap; + JITpilot jitPilot; + + function setup() internal virtual override { + vm.startBroadcast(user3PK); + eUSDC.setLTV(address(eWETH), 0.65e4, 0.67e4, 0); + eUSDC.setLTV(address(eUSDT), 0.85e4, 0.87e4, 0); + deployJITPilot(); + vm.stopBroadcast(); + + createEulerSwap(); // user 2 + + // create borrow positions for other users + giveTonsOfCash(user0); + giveTonsOfCash(user1); + // giveTonsOfCash(user3); + + depositCollateralIntoVault(user0, user0PK, address(eUSDC), 3_000_000_000e6); + depositCollateralIntoVault(user1, user1PK, address(eWETH), 1_000_000e18); + + borrowFromVault(user0, user0PK, address(eWETH), 500_000e18); + borrowFromVault(user1, user1PK, address(eUSDC), 1_600_000_000e6); + + jitPilot.getData(user2); + console.log("user2", user2); + + // buy USDC so that user2's EulerSwap position has to borrow + uint256 amountOut = 286_500_000e6; + _swapExactOut(address(assetWETH), address(assetUSDC), amountOut, user0, user0PK); + } + + function giveTonsOfCash(address user) internal virtual { + assetUSDC.mint(user, 100_000_000_000e6); + assetUSDT.mint(user, 100_000_000_000e6); + assetWETH.mint(user, 10_000_000e18); + assetwstETH.mint(user, 10_000_000e18); + assetDAI.mint(user, 1_000_000_000e18); + assetUSDZ.mint(user, 1_000_000_000e18); + } + + function deployJITPilot() internal { + jitPilot = new JITpilot(); + jitPilot.setEVC(address(evc)); + // jitPilot.setEVK(address(factory)); + jitPilot.setMaglevLens(address(maglevLens)); + jitPilot.setEulerSwapFactory(address(eulerSwapFactory)); + + string memory result = vm.serializeAddress("jitpilot", "jitPilot", address(jitPilot)); + vm.writeJson(result, "./dev-ctx/addresses/31337/JITpilotAddresses.json"); + } + + function createEulerSwap() internal { + vm.startBroadcast(user2PK); + + // Create pool parameters + IEulerSwap.Params memory poolParams = IEulerSwap.Params({ + vault0: address(eUSDC), + vault1: address(eWETH), + eulerAccount: user2, + equilibriumReserve0: 800_000_000e6, + equilibriumReserve1: 280_000e18, + priceX: 1e18, + priceY: 2865e6, + concentrationX: 0.9e18, + concentrationY: 0.9e18, + fee: 0, + protocolFee: 0, + protocolFeeRecipient: address(0) + }); + + // Define required hook flags + uint160 flags = uint160( + Hooks.BEFORE_INITIALIZE_FLAG | + Hooks.BEFORE_SWAP_FLAG | + Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | + Hooks.BEFORE_DONATE_FLAG | + Hooks.BEFORE_ADD_LIQUIDITY_FLAG + ); + + // Mine salt + bytes memory creationCode = MetaProxyDeployer.creationCodeMetaProxy(eulerSwapImpl, abi.encode(poolParams)); + (address hookAddress, bytes32 salt) = HookMiner.find(address(eulerSwapFactory), flags, creationCode); + + eulerSwap = hookAddress; + evc.setAccountOperator(user2, eulerSwap, true); + + eulerSwapFactory.deployPool(poolParams, IEulerSwap.InitialState({ + currReserve0: 800_000_000e6, + currReserve1: 280_000e18 + }), salt); + + string memory result = vm.serializeAddress("eulerSwap", "eulerSwap", address(eulerSwap)); + vm.writeJson(result, "./dev-ctx/addresses/31337/EulerSwapAddresses.json"); + vm.stopBroadcast(); + } + + function depositCollateralIntoVault(address user, uint256 userPK, address vaultAddress, uint256 amount) internal { + vm.startBroadcast(userPK); + TestERC20(IEVault(vaultAddress).asset()).mint(user, amount); + TestERC20(IEVault(vaultAddress).asset()).approve(vaultAddress, type(uint256).max); + IEVault(vaultAddress).deposit(amount, user); + + if (!evc.isCollateralEnabled(user, vaultAddress)) { + evc.enableCollateral(user, vaultAddress); + } + + vm.stopBroadcast(); + } + + function borrowFromVault(address user, uint256 userPK, address vaultAddress, uint256 amount) internal { + vm.startBroadcast(userPK); + + if (evc.getControllers(user).length > 0) { + require(evc.isControllerEnabled(user, vaultAddress), "Controller not enabled"); + } else { + evc.enableController(user, vaultAddress); + } + + IEVault(vaultAddress).borrow(amount, user); + + vm.stopBroadcast(); + } + + function _swapExactOut(address tokenIn, address tokenOut, uint256 amountOut, address receiver, uint256 userPK) internal { + vm.startBroadcast(userPK); + uint256 amountIn = eulerSwapPeriphery.quoteExactOutput( + eulerSwap, + tokenIn, + tokenOut, + amountOut + ); + console.log("amountIn", amountIn); + + TestERC20(tokenIn).approve(address(eulerSwapPeriphery), type(uint256).max); + console.log("user1's WETH balance (tokenIn)", TestERC20(tokenIn).balanceOf(user1)); + console.log("WETH address (tokenIn)", tokenIn); + console.log("user1's USDC balance (tokenOut)", TestERC20(tokenOut).balanceOf(user1)); + console.log("USDC address (tokenOut)", tokenOut); + console.log("user1's address: ", user1); + console.log("eulerSwap address: ", eulerSwap); + eulerSwapPeriphery.swapExactOut( + eulerSwap, + tokenIn, + tokenOut, + amountOut, + receiver, + amountIn * 101 / 100, // allow up to 1% slippage + 0 + ); + vm.stopBroadcast(); + } +} \ No newline at end of file From b24b9054b1cf75cb3a267d1cf75633f059b15659 Mon Sep 17 00:00:00 2001 From: MazyGio Date: Thu, 3 Jul 2025 19:58:09 -0500 Subject: [PATCH 3/6] fix: run forge fmt --- src/JITpilot.sol | 203 +++++++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 113 deletions(-) diff --git a/src/JITpilot.sol b/src/JITpilot.sol index 3052633..7755df8 100644 --- a/src/JITpilot.sol +++ b/src/JITpilot.sol @@ -15,14 +15,13 @@ import {console2 as console} from "forge-std/console2.sol"; * Tracks Health Factor and Yield as sliding time-weighted averages over 100 blocks */ contract JITpilot { - address public admin; address public owner; - + // Constants uint256 private constant WINDOW_SIZE = 100; uint256 private constant PRECISION = 1e18; - + // Configurable parameters uint256 public rebalanceThreshold = 5e17; // 0.5 in 18 decimals uint256 public weightHF = 6e17; // 0.6 weight for Health Factor @@ -33,7 +32,7 @@ contract JITpilot { address public EVK; address public MaglevLens; address public EulerSwapFactory; - + // Data structure to store block-level data from fetchData struct BlockData { uint256 allowedLTV; @@ -42,32 +41,29 @@ contract JITpilot { int256 netInterest; uint256 depositValue; } - + // LP data structure struct LPData { // Health Factor data uint256[] hfHistory; // Stores HF values for last 100 blocks uint256 twaHF; // Time-weighted average Health Factor - - // Yield data + // Yield data uint256[] yieldHistory; // Stores yield values for last 100 blocks uint256 twaYield; // Time-weighted average Yield - // Configuration uint256 hfMin; // Liquidation threshold uint256 hfDesired; // Target health factor set by LP uint256 yieldTarget; // Target yield set by LP - // Tracking uint256 lastUpdateBlock; // Last block when metrics were updated uint256 startBlock; // Block when LP started bool initialized; // Whether LP data is initialized } - + // Mappings mapping(address => LPData) public lpData; mapping(address => bool) public authorizedCallers; - + // Events event MetricsUpdated( address indexed lp, @@ -78,32 +74,24 @@ contract JITpilot { uint256 twaYield, uint256 compositeScore ); - + event RebalanceTriggered( - address indexed lp, - uint256 indexed blockNumber, - uint256 compositeScore, - uint256 threshold + address indexed lp, uint256 indexed blockNumber, uint256 compositeScore, uint256 threshold ); - - event LPConfigured( - address indexed lp, - uint256 hfMin, - uint256 hfDesired, - uint256 yieldTarget - ); - + + event LPConfigured(address indexed lp, uint256 hfMin, uint256 hfDesired, uint256 yieldTarget); + // Modifiers modifier onlyAuthorized() { require(authorizedCallers[msg.sender], "Not authorized"); _; } - + modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } - + constructor() { authorizedCallers[msg.sender] = true; admin = msg.sender; @@ -114,19 +102,19 @@ contract JITpilot { function setEVC(address _EVC) external onlyOwner { EVC = _EVC; } - + function setEVK(address _EVK) external onlyOwner { EVK = _EVK; } - + function setMaglevLens(address _MaglevLens) external onlyOwner { MaglevLens = _MaglevLens; } - + function setEulerSwapFactory(address _EulerSwapFactory) external onlyOwner { EulerSwapFactory = _EulerSwapFactory; } - + /** * @dev Configure LP parameters * @param lp LP address @@ -134,43 +122,38 @@ contract JITpilot { * @param _hfDesired Target health factor * @param _yieldTarget Target yield */ - function configureLp( - address lp, - uint256 _hfMin, - uint256 _hfDesired, - uint256 _yieldTarget - ) external { + function configureLp(address lp, uint256 _hfMin, uint256 _hfDesired, uint256 _yieldTarget) external { require(lp != address(0), "Invalid LP address"); require(_hfDesired > _hfMin, "HF desired must be > HF min"); - + LPData storage data = lpData[lp]; data.hfMin = _hfMin; data.hfDesired = _hfDesired; data.yieldTarget = _yieldTarget; data.initialized = true; data.startBlock = block.number; - + emit LPConfigured(lp, _hfMin, _hfDesired, _yieldTarget); } - + /** * @dev Update metrics for a specific LP * @param lp LP address to update metrics for */ function updateMetrics(address lp) external { require(lpData[lp].initialized, "LP not configured"); - + LPData storage data = lpData[lp]; - + // Fetch current block data BlockData memory currentData = fetchData(lp); - + // Calculate current Health Factor uint256 currentHF = 0; if (currentData.currentLTV > 0) { currentHF = (currentData.allowedLTV * PRECISION) / currentData.currentLTV; } - + // Calculate current Yield uint256 currentYield = 0; uint256 swapApy = currentData.swapFees / currentData.depositValue; @@ -185,47 +168,39 @@ contract JITpilot { currentYield = 0; } } - + // Update sliding window for Health Factor _updateSlidingWindow(data.hfHistory, currentHF); - + // Update sliding window for Yield _updateSlidingWindow(data.yieldHistory, currentYield); - + // Calculate TWA for Health Factor data.twaHF = _calculateTWA(data.hfHistory, data.startBlock); - + // Calculate TWA for Yield data.twaYield = _calculateTWA(data.yieldHistory, data.startBlock); - + // Calculate normalized values uint256 normalizedHF = _normalizeHealthFactor(data.twaHF, data.hfMin, data.hfDesired); uint256 normalizedYield = _normalizeYield(data.twaYield, data.yieldTarget); - + // Calculate composite score uint256 compositeScore = (weightHF * normalizedHF + weightYield * normalizedYield) / PRECISION; - + // Update last update block data.lastUpdateBlock = block.number; - + // Emit metrics updated event - emit MetricsUpdated( - lp, - block.number, - currentHF, - currentYield, - data.twaHF, - data.twaYield, - compositeScore - ); - + emit MetricsUpdated(lp, block.number, currentHF, currentYield, data.twaHF, data.twaYield, compositeScore); + // Check if rebalancing is needed if (compositeScore < rebalanceThreshold) { emit RebalanceTriggered(lp, block.number, compositeScore, rebalanceThreshold); rebalance(lp); } } - + /** * @dev Update sliding window array with new value * @param history Array storing historical values @@ -243,7 +218,7 @@ contract JITpilot { history[WINDOW_SIZE - 1] = newValue; } } - + /** * @dev Calculate time-weighted average * @param history Array of historical values @@ -253,21 +228,21 @@ contract JITpilot { function _calculateTWA(uint256[] storage history, uint256 startBlock) internal view returns (uint256) { uint256 length = history.length; if (length == 0) return 0; - + // Calculate blocks elapsed since start uint256 blocksElapsed = block.number - startBlock + 1; - + // Determine effective window size uint256 effectiveWindow = blocksElapsed <= WINDOW_SIZE ? blocksElapsed : WINDOW_SIZE; - + uint256 sum = 0; for (uint256 i = 0; i < length; i++) { sum += history[i]; } - + return sum / effectiveWindow; } - + /** * @dev Normalize Health Factor to [0,1] range * @param twaHF Time-weighted average Health Factor @@ -275,17 +250,13 @@ contract JITpilot { * @param hfDesired Desired health factor * @return Normalized HF value */ - function _normalizeHealthFactor( - uint256 twaHF, - uint256 hfMin, - uint256 hfDesired - ) internal pure returns (uint256) { + function _normalizeHealthFactor(uint256 twaHF, uint256 hfMin, uint256 hfDesired) internal pure returns (uint256) { if (twaHF <= hfMin) return 0; if (twaHF >= hfDesired) return PRECISION; - + return ((twaHF - hfMin) * PRECISION) / (hfDesired - hfMin); } - + /** * @dev Normalize Yield to [0,1] range * @param twaYield Time-weighted average yield @@ -295,10 +266,10 @@ contract JITpilot { function _normalizeYield(uint256 twaYield, uint256 yieldTarget) internal pure returns (uint256) { if (yieldTarget == 0) return PRECISION; // Avoid division by zero if (twaYield >= yieldTarget) return PRECISION; - + return (twaYield * PRECISION) / yieldTarget; } - + /** * @dev Fetch current block data (placeholder - to be implemented later) * @param lp LP address @@ -307,10 +278,10 @@ contract JITpilot { function fetchData(address lp) internal view returns (BlockData memory) { // Placeholder implementation - this will fetch real data from Euler contracts // For now, return dummy data to avoid compilation errors - + // get LP's eulerSwap pool address address poolAddr = IEulerSwapFactory(EulerSwapFactory).poolByEulerAccount(lp); - + // get eulerSwap pool data EulerSwapData memory eulerSwapData = getEulerSwapData(poolAddr); @@ -331,7 +302,8 @@ contract JITpilot { // more accurate for our purposes. // uint256 collateralValue0 = getCollateralValue(lp, vault0); // uint256 collateralValue1 = getCollateralValue(lp, vault1); - uint256 collateralValueTotal = getCollateralValue(lp, eulerSwapData.params.vault0) + getCollateralValue(lp, eulerSwapData.params.vault1); + uint256 collateralValueTotal = + getCollateralValue(lp, eulerSwapData.params.vault0) + getCollateralValue(lp, eulerSwapData.params.vault1); // get supply APY data using the MaglevLens contract IMaglevLens maglevLens = IMaglevLens(MaglevLens); @@ -361,7 +333,9 @@ contract JITpilot { } else { console.log("has controller vault: ", controllerVault); // Figure out which vault is the collateralVault - address collateralVault = (controllerVault == eulerSwapData.params.vault0) ? eulerSwapData.params.vault1 : eulerSwapData.params.vault0; + address collateralVault = (controllerVault == eulerSwapData.params.vault0) + ? eulerSwapData.params.vault1 + : eulerSwapData.params.vault0; uint256 debtValue = getDebtValue(lp, controllerVault); console.log("debtValue: ", debtValue); blockData.allowedLTV = uint256(IEVault(controllerVault).LTVLiquidation(collateralVault)) * 1e18 / 1e4; @@ -379,13 +353,15 @@ contract JITpilot { blockData.depositValue = collateralValueTotal - debtValue; if (supplyApyTotal * collateralValueTotal < borrowApyTotal * debtValue) { // avoid overflow - blockData.netInterest = -int256((borrowApyTotal * debtValue - supplyApyTotal * collateralValueTotal) / blockData.depositValue) *1e9; + blockData.netInterest = -int256((borrowApyTotal * debtValue - supplyApyTotal * collateralValueTotal) / blockData.depositValue) + * 1e9; } else { - blockData.netInterest = int256((supplyApyTotal * collateralValueTotal - borrowApyTotal * debtValue) / blockData.depositValue) *1e9; + blockData.netInterest = int256( + (supplyApyTotal * collateralValueTotal - borrowApyTotal * debtValue) / blockData.depositValue + ) * 1e9; } } - // get LP's liquidity status in the controller vault, with regards to liquidation // ( // address[] memory collateralVaults, @@ -401,12 +377,12 @@ contract JITpilot { // uint16 ltv = IEVault(controllerVault).LTVLiquidation(collateralVaults[i]); // collateralValues[i] = collateralValues[i] * 10000 / ltv; // Convert back to non-adjusted value // collateralValueTotal += collateralValues[i]; - + // // Calculate weighted average LTV // allowedLTV += ltv * collateralValues[i]; // totalWeight += collateralValues[i]; // } - + // Finalize weighted average LTV // if (totalWeight > 0) { // allowedLTV = allowedLTV / totalWeight; @@ -422,13 +398,13 @@ contract JITpilot { // #4 calculate net interest // uint256 netInterest = supplyApyTotal - borrowApy; - + // #5 get the depositValue // uint256 depositValue = collateralValueTotal - liabilityValue; return blockData; } - + struct EulerSwapData { address addr; IEulerSwap.Params params; @@ -461,11 +437,12 @@ contract JITpilot { * @dev Rebalance LP position (placeholder - to be implemented later) * @param lp LP address to rebalance */ + function rebalance(address lp) internal { // Placeholder implementation - this will perform actual rebalancing // Will be implemented in later iterations } - + /** * @dev Add authorized caller * @param caller Address to authorize @@ -473,7 +450,7 @@ contract JITpilot { function addAuthorizedCaller(address caller) external onlyAuthorized { authorizedCallers[caller] = true; } - + /** * @dev Remove authorized caller * @param caller Address to remove authorization @@ -481,7 +458,7 @@ contract JITpilot { function removeAuthorizedCaller(address caller) external onlyAuthorized { authorizedCallers[caller] = false; } - + /** * @dev Update rebalance threshold * @param newThreshold New threshold value @@ -490,7 +467,7 @@ contract JITpilot { require(newThreshold <= PRECISION, "Threshold too high"); rebalanceThreshold = newThreshold; } - + /** * @dev Update weights for composite score * @param newWeightHF New weight for Health Factor @@ -501,7 +478,7 @@ contract JITpilot { weightHF = newWeightHF; weightYield = newWeightYield; } - + /** * @dev Get LP data for viewing * @param lp LP address @@ -513,15 +490,19 @@ contract JITpilot { * @return lastUpdateBlock Last block when metrics were updated * @return initialized Whether LP data is initialized */ - function getLPData(address lp) external view returns ( - uint256 twaHF, - uint256 twaYield, - uint256 hfMin, - uint256 hfDesired, - uint256 yieldTarget, - uint256 lastUpdateBlock, - bool initialized - ) { + function getLPData(address lp) + external + view + returns ( + uint256 twaHF, + uint256 twaYield, + uint256 hfMin, + uint256 hfDesired, + uint256 yieldTarget, + uint256 lastUpdateBlock, + bool initialized + ) + { LPData storage data = lpData[lp]; return ( data.twaHF, @@ -533,7 +514,7 @@ contract JITpilot { data.initialized ); } - + /** * @dev Get current composite score for an LP * @param lp LP address @@ -542,10 +523,10 @@ contract JITpilot { function getCompositeScore(address lp) external view returns (uint256) { LPData storage data = lpData[lp]; if (!data.initialized) return 0; - + uint256 normalizedHF = _normalizeHealthFactor(data.twaHF, data.hfMin, data.hfDesired); uint256 normalizedYield = _normalizeYield(data.twaYield, data.yieldTarget); - + return (weightHF * normalizedHF + weightYield * normalizedYield) / PRECISION; } @@ -556,7 +537,7 @@ contract JITpilot { address currentControllerVault; if (controllerVaults.length == 0) return address(0); - + // find which of the LP's controller vaults is the enabled debt vault for (uint256 i; i < controllerVaults.length; ++i) { if (IEVC(EVC).isControllerEnabled(lp, controllerVaults[i])) { @@ -567,12 +548,7 @@ contract JITpilot { return currentControllerVault; } - function getCollateralValue(address account, address collateral) - internal - view - virtual - returns (uint256 value) - { + function getCollateralValue(address account, address collateral) internal view virtual returns (uint256 value) { IEVault collateralVault = IEVault(collateral); uint256 balance = IERC20(collateral).balanceOf(account); if (balance == 0) return 0; @@ -580,7 +556,8 @@ contract JITpilot { uint256 currentCollateralValue; // mid-point price - currentCollateralValue = IPriceOracle(collateralVault.oracle()).getQuote(balance, collateral, collateralVault.unitOfAccount()); + currentCollateralValue = + IPriceOracle(collateralVault.oracle()).getQuote(balance, collateral, collateralVault.unitOfAccount()); return currentCollateralValue; } From c467042f3411a9139eca52a234e5d415a79fbec7 Mon Sep 17 00:00:00 2001 From: MazyGio Date: Thu, 3 Jul 2025 20:02:16 -0500 Subject: [PATCH 4/6] run forge fmt --- script/DeployScenario.s.sol | 2 +- script/scenarios/EulerSwapDebtScenario.s.sol | 40 ++++++++------------ 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/script/DeployScenario.s.sol b/script/DeployScenario.s.sol index 154466b..92319fe 100644 --- a/script/DeployScenario.s.sol +++ b/script/DeployScenario.s.sol @@ -327,4 +327,4 @@ contract DeployScenario is Script { } function setup() internal virtual {} -} \ No newline at end of file +} diff --git a/script/scenarios/EulerSwapDebtScenario.s.sol b/script/scenarios/EulerSwapDebtScenario.s.sol index 97a536b..1070249 100644 --- a/script/scenarios/EulerSwapDebtScenario.s.sol +++ b/script/scenarios/EulerSwapDebtScenario.s.sol @@ -12,7 +12,6 @@ import {TestERC20} from "evk-test/unit/evault/EVaultTestBase.t.sol"; import {IEVault} from "evk/EVault/IEVault.sol"; contract EulerSwapDebtScenario is DeployScenario { - address eulerSwap; JITpilot jitPilot; @@ -38,7 +37,7 @@ contract EulerSwapDebtScenario is DeployScenario { jitPilot.getData(user2); console.log("user2", user2); - + // buy USDC so that user2's EulerSwap position has to borrow uint256 amountOut = 286_500_000e6; _swapExactOut(address(assetWETH), address(assetUSDC), amountOut, user0, user0PK); @@ -66,7 +65,7 @@ contract EulerSwapDebtScenario is DeployScenario { function createEulerSwap() internal { vm.startBroadcast(user2PK); - + // Create pool parameters IEulerSwap.Params memory poolParams = IEulerSwap.Params({ vault0: address(eUSDC), @@ -85,24 +84,20 @@ contract EulerSwapDebtScenario is DeployScenario { // Define required hook flags uint160 flags = uint160( - Hooks.BEFORE_INITIALIZE_FLAG | - Hooks.BEFORE_SWAP_FLAG | - Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | - Hooks.BEFORE_DONATE_FLAG | - Hooks.BEFORE_ADD_LIQUIDITY_FLAG + Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG + | Hooks.BEFORE_DONATE_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG ); - + // Mine salt bytes memory creationCode = MetaProxyDeployer.creationCodeMetaProxy(eulerSwapImpl, abi.encode(poolParams)); (address hookAddress, bytes32 salt) = HookMiner.find(address(eulerSwapFactory), flags, creationCode); - + eulerSwap = hookAddress; evc.setAccountOperator(user2, eulerSwap, true); - eulerSwapFactory.deployPool(poolParams, IEulerSwap.InitialState({ - currReserve0: 800_000_000e6, - currReserve1: 280_000e18 - }), salt); + eulerSwapFactory.deployPool( + poolParams, IEulerSwap.InitialState({currReserve0: 800_000_000e6, currReserve1: 280_000e18}), salt + ); string memory result = vm.serializeAddress("eulerSwap", "eulerSwap", address(eulerSwap)); vm.writeJson(result, "./dev-ctx/addresses/31337/EulerSwapAddresses.json"); @@ -114,7 +109,7 @@ contract EulerSwapDebtScenario is DeployScenario { TestERC20(IEVault(vaultAddress).asset()).mint(user, amount); TestERC20(IEVault(vaultAddress).asset()).approve(vaultAddress, type(uint256).max); IEVault(vaultAddress).deposit(amount, user); - + if (!evc.isCollateralEnabled(user, vaultAddress)) { evc.enableCollateral(user, vaultAddress); } @@ -132,18 +127,15 @@ contract EulerSwapDebtScenario is DeployScenario { } IEVault(vaultAddress).borrow(amount, user); - + vm.stopBroadcast(); } - function _swapExactOut(address tokenIn, address tokenOut, uint256 amountOut, address receiver, uint256 userPK) internal { + function _swapExactOut(address tokenIn, address tokenOut, uint256 amountOut, address receiver, uint256 userPK) + internal + { vm.startBroadcast(userPK); - uint256 amountIn = eulerSwapPeriphery.quoteExactOutput( - eulerSwap, - tokenIn, - tokenOut, - amountOut - ); + uint256 amountIn = eulerSwapPeriphery.quoteExactOutput(eulerSwap, tokenIn, tokenOut, amountOut); console.log("amountIn", amountIn); TestERC20(tokenIn).approve(address(eulerSwapPeriphery), type(uint256).max); @@ -164,4 +156,4 @@ contract EulerSwapDebtScenario is DeployScenario { ); vm.stopBroadcast(); } -} \ No newline at end of file +} From 4530aac5dd7132aea00ebcc8a03e79137db394d7 Mon Sep 17 00:00:00 2001 From: MazyGio Date: Fri, 4 Jul 2025 04:19:52 -0500 Subject: [PATCH 5/6] slight refactor of fetchData function for better readability --- src/JITpilot.sol | 113 +++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/src/JITpilot.sol b/src/JITpilot.sol index 7755df8..b86a4ce 100644 --- a/src/JITpilot.sol +++ b/src/JITpilot.sol @@ -39,7 +39,7 @@ contract JITpilot { uint256 currentLTV; uint256 swapFees; int256 netInterest; - uint256 depositValue; + int256 depositValue; } // LP data structure @@ -156,7 +156,7 @@ contract JITpilot { // Calculate current Yield uint256 currentYield = 0; - uint256 swapApy = currentData.swapFees / currentData.depositValue; + uint256 swapApy = uint256(int256(currentData.swapFees) * 1e18 / currentData.depositValue); if (currentData.depositValue > 0) { if (currentData.netInterest >= 0) { currentYield = (uint256(currentData.netInterest) * PRECISION) + swapApy; @@ -287,37 +287,13 @@ contract JITpilot { BlockData memory blockData; - blockData.allowedLTV = 0; - blockData.currentLTV = 0; - blockData.swapFees = 0; - blockData.netInterest = 0; - blockData.depositValue = 0; - // uint256 swapFees = 0; uint256 supplyApyTotal = 0; uint256 borrowApyTotal = 0; - // We only consider the EulerSwap vaults as collateral for this account, even if - // they have other enabled collaterals. This may differ from Euler's own UI, but it's - // more accurate for our purposes. - // uint256 collateralValue0 = getCollateralValue(lp, vault0); - // uint256 collateralValue1 = getCollateralValue(lp, vault1); - uint256 collateralValueTotal = - getCollateralValue(lp, eulerSwapData.params.vault0) + getCollateralValue(lp, eulerSwapData.params.vault1); - - // get supply APY data using the MaglevLens contract - IMaglevLens maglevLens = IMaglevLens(MaglevLens); - address[] memory collateralVaults = new address[](2); - collateralVaults[0] = eulerSwapData.params.vault0; - collateralVaults[1] = eulerSwapData.params.vault1; - IMaglevLens.VaultGlobal[] memory collateralVaultsGlobal = maglevLens.vaultsGlobal(collateralVaults); - // packed2: shares (160), supply APY (48), borrow APY (48) - for (uint256 i; i < collateralVaultsGlobal.length; ++i) { - uint256 supplyApy = uint256((collateralVaultsGlobal[i].packed2 << (256 - 96)) >> (256 - 48)); - supplyApyTotal += supplyApy * getCollateralValue(lp, collateralVaults[i]) / collateralValueTotal; - console.log("Supply APY for vault", collateralVaults[i], "is", supplyApy); - console.log("Supply APY total is", supplyApyTotal); - } + (uint256 collateralValueTotal, uint256 debtValue) = getDepositValue(lp); + blockData.depositValue = int256(collateralValueTotal) - int256(debtValue); + supplyApyTotal = getSupplyApy(lp); // get the currently enabled controller vault (i.e. the debt vault) address controllerVault = getCurrentControllerVault(lp); @@ -327,8 +303,7 @@ contract JITpilot { console.log("doesn't have controller vault: ", controllerVault); blockData.allowedLTV = 0; blockData.currentLTV = 0; - // If there is no debt, there is no looping or leverage - blockData.depositValue = collateralValueTotal; + // If there is no debt, there is no looping or leverage, so interest is just supplyAPY blockData.netInterest = int256(supplyApyTotal); } else { console.log("has controller vault: ", controllerVault); @@ -336,7 +311,6 @@ contract JITpilot { address collateralVault = (controllerVault == eulerSwapData.params.vault0) ? eulerSwapData.params.vault1 : eulerSwapData.params.vault0; - uint256 debtValue = getDebtValue(lp, controllerVault); console.log("debtValue: ", debtValue); blockData.allowedLTV = uint256(IEVault(controllerVault).LTVLiquidation(collateralVault)) * 1e18 / 1e4; blockData.currentLTV = debtValue * 1e18 / collateralValueTotal; @@ -344,24 +318,27 @@ contract JITpilot { address[] memory controllerVaultArray = new address[](1); controllerVaultArray[0] = controllerVault; - IMaglevLens.VaultGlobal[] memory controllerVaultsGlobal = maglevLens.vaultsGlobal(controllerVaultArray); + IMaglevLens.VaultGlobal[] memory controllerVaultsGlobal = + IMaglevLens(MaglevLens).vaultsGlobal(controllerVaultArray); // borrow APY is on the last 48 bits. Shift left then right to extract it. uint256 borrowApy = uint256((controllerVaultsGlobal[0].packed2 << (256 - 48)) >> (256 - 48)); borrowApyTotal = borrowApy; - - // The EVC already ensures that liabilityValue is less than collateralValueTotal, so this is safe - blockData.depositValue = collateralValueTotal - debtValue; if (supplyApyTotal * collateralValueTotal < borrowApyTotal * debtValue) { // avoid overflow - blockData.netInterest = -int256((borrowApyTotal * debtValue - supplyApyTotal * collateralValueTotal) / blockData.depositValue) - * 1e9; + blockData.netInterest = -int256( + (borrowApyTotal * debtValue - supplyApyTotal * collateralValueTotal) + / uint256(blockData.depositValue) + ) * 1e9; } else { blockData.netInterest = int256( - (supplyApyTotal * collateralValueTotal - borrowApyTotal * debtValue) / blockData.depositValue + (supplyApyTotal * collateralValueTotal - borrowApyTotal * debtValue) + / uint256(blockData.depositValue) ) * 1e9; } } + blockData.depositValue = int256(collateralValueTotal) - int256(debtValue); + // get LP's liquidity status in the controller vault, with regards to liquidation // ( // address[] memory collateralVaults, @@ -416,6 +393,8 @@ contract JITpilot { uint256 outLimit01; uint256 inLimit10; uint256 outLimit10; + uint16 borrowLTV01; + uint16 borrowLTV10; } function getEulerSwapData(address poolAddr) internal view returns (EulerSwapData memory output) { @@ -432,6 +411,9 @@ contract JITpilot { output.asset1 = asset1; (output.inLimit01, output.outLimit01) = pool.getLimits(asset0, asset1); (output.inLimit10, output.outLimit10) = pool.getLimits(asset1, asset0); + // fetch borrow LTVs. These will be used to calculate reserves for rebalancing + output.borrowLTV01 = IEVault(output.params.vault0).LTVBorrow(output.params.vault1); + output.borrowLTV10 = IEVault(output.params.vault1).LTVBorrow(output.params.vault0); } /** * @dev Rebalance LP position (placeholder - to be implemented later) @@ -578,4 +560,57 @@ contract JITpilot { function getData(address lp) external view returns (BlockData memory) { return fetchData(lp); } + + /** + * @dev Get the deposit value of an LP + * @param lp LP address + * @return collateralValueTotal Collateral value of the LP + * @return debtValue Debt value of the LP + */ + function getDepositValue(address lp) + internal + view + virtual + returns (uint256 collateralValueTotal, uint256 debtValue) + { + address poolAddr = IEulerSwapFactory(EulerSwapFactory).poolByEulerAccount(lp); + IEulerSwap.Params memory eulerSwapParams = IEulerSwap(poolAddr).getParams(); + + collateralValueTotal = + getCollateralValue(lp, eulerSwapParams.vault0) + getCollateralValue(lp, eulerSwapParams.vault1); + debtValue = getDebtValue(lp, eulerSwapParams.vault0) + getDebtValue(lp, eulerSwapParams.vault1); + + return (collateralValueTotal, debtValue); + } + + /** + * @dev Get the supply APY of an LP + * @param lp LP address + * @return supplyApyTotal Supply APY of the LP, weighted by collateral value + */ + function getSupplyApy(address lp) internal view virtual returns (uint256 supplyApyTotal) { + address poolAddr = IEulerSwapFactory(EulerSwapFactory).poolByEulerAccount(lp); + IEulerSwap.Params memory eulerSwapParams = IEulerSwap(poolAddr).getParams(); + + // get supply APY data using the MaglevLens contract + IMaglevLens maglevLens = IMaglevLens(MaglevLens); + address[] memory collateralVaults = new address[](2); + collateralVaults[0] = eulerSwapParams.vault0; + collateralVaults[1] = eulerSwapParams.vault1; + IMaglevLens.VaultGlobal[] memory collateralVaultsGlobal = maglevLens.vaultsGlobal(collateralVaults); + // packed2: shares (160), supply APY (48), borrow APY (48) + uint256 collateralValueTotal; + for (uint256 i; i < collateralVaultsGlobal.length; ++i) { + uint256 supplyApy = uint256((collateralVaultsGlobal[i].packed2 << (256 - 96)) >> (256 - 48)); + uint256 collateralValue = getCollateralValue(lp, collateralVaults[i]); + supplyApyTotal += supplyApy * collateralValue; + collateralValueTotal += collateralValue; + console.log("Supply APY for vault", collateralVaults[i], "is", supplyApy); + console.log("Supply APY total is", supplyApyTotal); + } + + supplyApyTotal = supplyApyTotal / collateralValueTotal; + + return supplyApyTotal; + } } From 4b08f6ce49ac70566236f0776803411b876c461e Mon Sep 17 00:00:00 2001 From: MazyGio Date: Fri, 4 Jul 2025 07:53:26 -0500 Subject: [PATCH 6/6] skip scripts in forge build --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36353e3..38341f9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: - name: Run Forge build run: | - forge build --sizes + forge build --sizes --skip script id: build - name: Run Forge tests