From 2a531c1ef262cebfe6d110918e56f6f0b1b5ba61 Mon Sep 17 00:00:00 2001 From: mo-hak Date: Mon, 7 Jul 2025 19:05:49 +0530 Subject: [PATCH] add install script and refactored code --- eulerswap_curve_mechanics.md | 317 ----------------------------------- install.sh | 7 + rebalance.md | 186 -------------------- src/JITpilot.sol | 35 ---- 4 files changed, 7 insertions(+), 538 deletions(-) delete mode 100644 eulerswap_curve_mechanics.md create mode 100644 install.sh delete mode 100644 rebalance.md diff --git a/eulerswap_curve_mechanics.md b/eulerswap_curve_mechanics.md deleted file mode 100644 index 6841710..0000000 --- a/eulerswap_curve_mechanics.md +++ /dev/null @@ -1,317 +0,0 @@ -# EulerSwap Curve Mechanics and Mathematical Framework - -## 5. Curve Mechanics and Virtual Reserves - -While the operator logic manages execution and capital flow, the behaviour of swaps is ultimately governed by a customisable AMM curve. EulerSwap features a unique curve that allows different amounts of liquidity and different concentrations of liquidity on different sides of the pool based on the reserves available. The space of possible reserves is determined by how much real liquidity an LP has and how much debt their operator is allowed to hold. - -Since EulerSwap AMMs do not always hold the assets used to service swaps at all times, they perform calculations based on **virtual reserves and debt limits**, rather than on strictly real reserves. Each EulerSwap LP can independently configure virtual reserve levels. These reserves define the maximum debt exposure an AMM will take on. Note that the effective LTV must always remain below the borrowing LTV of the lending vault to prevent liquidation. - -### Key Considerations - -- EulerSwap account owners are responsible for monitoring the health of their vaults -- Proactive steps should be taken if collateral accrues bad debt or drops in value -- Liquidation can occur independently of swap activity if someone performs a large swap on their curve -- Different AMM curves influence whether the maximum virtual reserves are achievable - -### Equilibrium Point and Pricing - -The EulerSwap curve passes through a reserve equilibrium point (x₀, y₀), at which the marginal price is defined by: - -``` -dy/dx|(x₀,y₀) = -px/py -``` - -where px and py are pricing parameters that determine the exchange rate at equilibrium. - -### Piecewise Curve Definition - -Unlike most AMM curves, which are usually defined by a single convex function, EulerSwap uses a **piecewise-defined curve**, with different functions guiding trading behaviour either side of the equilibrium point. - -#### Left Side (0 < x ≤ x₀) -In the domain 0 < x ≤ x₀, the curve is defined by: - -``` -y = y₀ + (px/py)(x₀ - x)[cx + (1 - cx)(x₀/x)] -``` - -where y depends on x. - -#### Right Side (x₀ < x) -In the region x₀ < x, we let x become the dependent variable, so that the domain is 0 < y ≤ y₀, and the curve is defined by: - -``` -x = x₀ + (py/px)(y₀ - y)[cy + (1 - cy)(y₀/y)] -``` - -where x depends on y. - -### Concentration Parameters - -The cx, cy parameters in the equations are **liquidity concentration parameters** that control how liquidity is distributed along the curve: - -- **Values closer to 1**: Concentrate liquidity around the equilibrium point, similar to a Curve StableSwap pool -- **Values closer to 0**: Distribute liquidity across a wider price range, similar to a classic Bancor or Uniswap v2 pool -- **When cx = cy = 1**: The AMM becomes a constant-sum AMM -- **When cx = cy = 0**: The AMM becomes a constant-product AMM - -### 5.1 Novel Use-Cases - -This flexibility enables EulerSwap to be used for entirely new use cases or to simulate the behaviour of atypical AMM protocols, such as Maker's peg stability module. - -#### Token Launchpad Configuration -By configuring asymmetric liquidity curves, EulerSwap can be used as a launchpad for new tokens: -- **Concentrate liquidity on the quote asset side** (e.g., USDC) -- **Distribute broadly on the base asset side** (e.g., a new token) -- This allows projects to establish a price floor while supporting price discovery -- Proceeds from sales can immediately begin earning yield -- AMM's debt mechanics enable community-backed liquidity bootstrapping without requiring large treasury allocations upfront - ---- - -## 7. Appendix - -### 7.1 Curve Description - -This section describes how the EulerSwap curve generalises the behaviours of both the constant-sum (CSMM) and constant-product market maker (CPMM) curves using liquidity concentration parameters. - -We begin with an automated market maker (AMM) holding initial liquidity reserves of two assets, X and Y, denoted as x₀ and y₀, respectively. In the absence of trading, the AMM remains at equilibrium at the point (x₀, y₀). - -#### Design Goals - -Our goal is to find a curve for a constant-function trading market maker (CFMM) that supports swaps between the two assets with the following properties: - -- Passes through the equilibrium point (x₀, y₀) -- Maintains an exchange rate, given by the slope of the AMM curve, of -px/py at (x₀, y₀) -- Allows liquidity concentration to be adjusted via parameters cx and cy, which control the liquidity available for swaps to the left and right of the equilibrium point - -### 7.1.1 Constant-Sum and Constant-Product Curves - -The canonical CSMM and CPMM curves are given by: - -**Constant-Sum (CSMM):** -``` -x + y = x₀ + y₀ -``` - -**Constant-Product (CPMM):** -``` -xy = x₀y₀ -``` - -#### Characteristics -- **CSMM**: Simply a line - concentrates liquidity at a single exchange rate -- **CPMM**: A hyperbola - distributes liquidity across a wide range of different exchange rates - -By default, these curves intersect at the equilibrium point (x₀, y₀), where their slopes are: - -**CSMM slope:** -``` -dy/dx = -1 -``` - -**CPMM slope:** -``` -dy/dx = -y/x -``` - -#### Custom Pricing Parameters - -Since real-world markets often operate at variable exchange rates at equilibrium, we introduce custom pricing parameters px and py to allow flexibility in defining the slope at the equilibrium point: - -**Weighted CSMM:** -``` -px·x + py·y = px·x₀ + py·y₀ -``` - -**Weighted CPMM:** -``` -x^(py·y₀) · y^(px·x₀) = x₀^(py·y₀) · y₀^(px·x₀) -``` - -Taking the derivatives of these equations with respect to x: - -**CSMM derivative:** -``` -dy/dx = -px/py -``` - -**CPMM derivative:** -``` -dy/dx = -(px/py) · (x₀/y) · (y₀/x) -``` - -At equilibrium (x₀, y₀), both functions have the slope: -``` -dy/dx|(x₀,y₀) = -px/py -``` - -### 7.1.2 Introducing Artificial Reserves - -The weighted CPMM introduces significant computational complexity due to exponential forms involving power functions of reserves, which are expensive to compute on-chain in the EVM. To address this, we construct an alternative using **artificial reserves**. - -#### Domain Splitting Strategy - -In the interval 0 < x ≤ x₀, swaps should only increase liquidity beyond y₀ and deplete x₀ liquidity. This suggests that we can split the domain of the AMM curves into two, and replace the real reserve y₀ in the interval 0 < x ≤ x₀ with a carefully chosen artificial reserve yᵥ. - -Re-arranging the weighted curves into explicit functions of y: - -**CSMM explicit form:** -``` -y = y₀ + (px/py)(x₀ - x) -``` - -**CPMM explicit form:** -``` -y = y₀ · (x₀/x)^(py·y₀)/(px·x₀) -``` - -#### Artificial Reserve Substitution - -A substitution of y₀ → yᵥ, given by: -``` -yᵥ = x₀ · px/py -``` - -eliminates the exponential form. This leads to: - -**Simplified CSMM:** -``` -y = (px/py)(2x₀ - x) -``` - -**Simplified CPMM:** -``` -y = (px/py) · x₀²/x -``` - -#### Correction for Equilibrium Point - -Since these curves no longer pass through (x₀, y₀), we correct them by adding back the difference y₀ - (px/py)x₀: - -**Corrected CSMM:** -``` -y = y₀ + (px/py)(x₀ - x) -``` - -**Corrected CPMM:** -``` -y = y₀ + (px/py)(x₀ - x)(x₀/x) -``` - -### 7.1.3 Unified Curve for Region 0 < x ≤ x₀ - -To create a single unified curve, we introduce a liquidity concentration parameter cx ∈ [0, 1]: - -- **cx = 1**: AMM functions as a constant-sum AMM -- **cx = 0**: AMM behaves as a constant-product-like AMM -- **Intermediate values**: Create a hybrid trading function with liquidity more or less concentrated around the equilibrium point - -#### Unified Equation - -This parameterisation leads to: - -``` -y = y₀ + (px/py)(x₀ - x)[cx + (1 - cx)(x₀/x)] -``` - -#### Marginal Price - -The marginal price anywhere along the curve is given by: - -``` -dy/dx = -(px/py)[cx + (1 - cx)(x₀/x)]² -``` - -#### Price-to-Coordinate Mapping - -The equation can be re-arranged to solve for the x-coordinate corresponding to a particular price p = dy/dx: - -``` -x = x₀ / √[-(py/px) · (p - cx)/(1 - cx)] -``` - -#### Inverse Function - -To perform as a complete trading function, the equation can be inverted to compute x given y using the quadratic formula: - -``` -cx·x² + [(py/px)(y - y₀) - (2cx - 1)x₀]·x - (1 - cx)x₀² = 0 -``` - -**Quadratic components:** -- A = cx -- B = (py/px)(y - y₀) - (2cx - 1)x₀ -- C = -(1 - cx)x₀² - -**Numerically stable solution:** -``` -x = { - (B + √(B² + 4AC))/(2A), if B ≤ 0 - 2C/(B + √(B² + 4AC)), if B > 0 -} -``` - -### 7.1.4 Extending the Curve to x ≥ x₀ Region - -To support swaps where the input token lies in the region x ≥ x₀, we need a symmetric extension. Rather than defining a new function, we reflect the existing function by interchanging the roles of x and y with appropriate reparameterisation: - -``` -x = x₀ + (py/px)(y₀ - y)[cy + (1 - cy)(y₀/y)] -``` - -This provides pricing for swaps where x is given and y is unknown, in the domain 0 < y ≤ y₀ (corresponding to x ≥ x₀). - -### 7.2 Invariant - -In traditional AMM protocols, the curve is typically defined as an implicit function. For example, the classic Uniswap AMM follows: - -``` -xy = x₀y₀ -``` - -This defines an invariant condition ensuring any valid swap must satisfy: -``` -xy ≥ x₀y₀ -``` - -#### EulerSwap Invariant Extension - -For EulerSwap, we apply a similar principle with piecewise conditions: - -**For 0 < x ≤ x₀:** -``` -y ≥ y₀ + (px/py)(x₀ - x)[cx + (1 - cx)(x₀/x)] -``` - -**For 0 < y ≤ y₀ (equivalent to x > x₀):** -``` -x ≥ x₀ + (py/px)(y₀ - y)[cy + (1 - cy)(y₀/y)] -``` - -These conditions together define the valid liquidity states in EulerSwap, ensuring that the AMM remains balanced while allowing for greater flexibility in liquidity provisioning. - ---- - -## Mathematical Implementation Notes - -### Solidity Implementation - -The mathematical functions are implemented in Solidity within the 'CurveLib.sol' contract: - -- **f()**: Implements the main curve equation for swap quoting and system invariant -- **fInverse()**: Implements the inverse function for providing quotes to swappers -- **Citardauq formula**: Alternative numerically stable form of the quadratic equation used when B ≥ 0 - -### Computational Efficiency - -The artificial reserves approach significantly reduces computational complexity compared to the weighted CPMM with exponential forms, making the system practical for on-chain deployment while maintaining the desired mathematical properties. - -### Visual Representation - -The EulerSwap curve can be visualized as two connected pieces: -- Left side: Controlled by cx parameter -- Right side: Controlled by cy parameter -- Both sides meeting at equilibrium point (x₀, y₀) -- Pricing parameters px, py determining the slope at equilibrium - -This piecewise design enables asymmetric liquidity distribution, allowing different concentration levels on each side of the equilibrium point. \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..c75842c --- /dev/null +++ b/install.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +forge install +git submodule update --init +forge build \ No newline at end of file diff --git a/rebalance.md b/rebalance.md deleted file mode 100644 index 28962fa..0000000 --- a/rebalance.md +++ /dev/null @@ -1,186 +0,0 @@ -# JIT-Pilot Re-balancing Framework -*Designing a one-shot EulerSwap reinstall that attracts arbitrage to pay down debt* - ---- - -## 1. Problem Statement - -Given an LP that is over-leveraged (high debt, low Health Factor), we want to: -1. Re-deploy the EulerSwap pool with a **new curve** whose quoted price makes it profitable for arbitrageurs to trade *against* the LP’s debt side. -2. Allow those incoming trades to deliver the asset we owe (reduce liability), thereby: - • decreasing LTV → raising Health Factor - • raising net interest (less borrow, more supply) → improving Yield - • pushing the composite score up to the stored `rebalanceDesired` target. - -We assume the LP has two assets -`asset0 ≡ collateralVault0` -`asset1 ≡ collateralVault1` - -and that the *debt* is denominated in `asset1` (w.l.o.g. – swap x/y if opposite). - ---- - -## 2. EulerSwap Parameters Recap - -```solidity -struct Params { - // Entities - address vault0; // asset0 supply vault - address vault1; // asset1 supply vault - address eulerAccount; // LP’s sub-account - // Curve - uint112 equilibriumReserve0; // x₀ - uint112 equilibriumReserve1; // y₀ - uint256 priceX; // pₓ (numerator) - uint256 priceY; // p_y (denominator) - uint256 concentrationX; // cₓ ∈ [0,1] - uint256 concentrationY; // c_y ∈ [0,1] - // Fees - uint256 fee; - uint256 protocolFee; - address protocolFeeRecipient; -} -``` - -Mathematically the **equilibrium slope** of the piece-wise curve is -\($\frac{dy}{dx}\Big|_{(x₀,y₀)} = -\,\frac{p_x}{p_y}.$) - -Changing $p_x/p_y$ therefore changes the *quoted* cross price at the pool's centre. -Changing $c_x,c_y$ changes **how quickly price moves** away from the centre (liquidity concentration). - ---- - -## 3. High-Level Algorithm - -1. **Input data** - * Market mid-price $P_m = \frac{\text{price(asset1)}}{\text{price(asset0)}}$ - * Current debt $D$ in asset1 units - * Desired post-rebalance debt $D^*$ (derived from `rebalanceDesired` HF target) - Required incoming asset1 volume - $Q = D - D^*\,(>0)$ - -2. **Choose price premium $\epsilon$** so that arbitrageurs are incented to *sell* `asset1` into the pool: - $P_{eq} = P_m \,(1 + \epsilon),\qquad 0 < \epsilon \ll 1$ - - Typical $\epsilon$ range: 0.2–1 %. - Larger $\epsilon$ ⇒ faster fill, higher divergence loss; smaller $\epsilon$ ⇒ slower/uncertain fill. - -3. **Pick pricing integers** - Let $p_x = 10^{18}$, $p_y = \frac{10^{18}}{P_{eq}}$ (scaled for 18 decimals). - This sets the *centre* price. - -4. **Compute equilibrium reserves** - Keep $x_0,y_0$ close to *current real reserves* $R_x,R_y$ but adjust $y_0$ upward so that - the first $Q$ of `asset1` can be absorbed before price returns to $P_m$. - For the left-hand piece (users selling asset1 to pool) we use the closed-form derived in §7.1.3: - - For $0 < x \le x_0$ - $y = y_0 + \frac{p_x}{p_y}(x_0 - x)\,\big[ c_x + (1 - c_x)\frac{x_0}{x} \big]$ - - Invert the marginal price formula - $\frac{dy}{dx} = -\frac{p_x}{p_y}\,[\,c_x + (1-c_x)\frac{x_0}{x}\,]^2$ - - to find the **x-coordinate** $x_Q$ where price re-hits $P_m$: - $x_Q = \frac{x_0}{\sqrt{1+\epsilon}} - \quad (\text{for } c_x \approx 1)$ - - The asset1 inflow volume is then $Q = x_0 - x_Q$. - Solve numerically (cheap off-chain) for the smallest $x_0 \geq R_x$ that satisfies this $Q$. - -5. **Set concentration** - * Use **high $c_x$ (≈0.9)** on the *debt* side → near-linear curve ⇒ low slippage until $Q$ filled. - * Keep $c_y$ as previous value (or symmetrical choice) to avoid breaking LP's profitability on the opposite side. - -6. **Fees** - Optionally set a *temporary* maker fee rebate (lower `fee`) to further attract volume. - -7. **Reinstall Pool** - * Close old instance (or withdraw/rescind operator). - * Deploy new Params with computed $x_0,y_0,p_x,p_y,c_x,c_y$. - * Once $Q$ is filled (monitored via Health Factor), operator can revert to neutral parameters. - ---- - -## 4. Mathematical Derivations - -### 4.1 Price Premium → Required Reserve Shift - -Assuming $c_x \approx 1$ (constant-sum like), the price along the left piece is: - -$P(x) = -\frac{dy}{dx} = \frac{p_x}{p_y}$ - -Because slope is constant, any $x < x_0$ still quotes $P_{eq}$. -Thus we **must** move along the curve into the **right** piece (or relax $c_x$) to allow price to decay back to $P_m$. - -With $c_x < 1$ the decay factor becomes -$P(x) = \frac{p_x}{p_y}\left[\,c_x + (1-c_x)\frac{x_0}{x}\,\right]^2$ - -Set $P(x_Q)=P_m$ ⇒ -$\left[\,c_x + (1-c_x)\frac{x_0}{x_Q}\,\right] = \sqrt{1+\epsilon}$ - -Given a chosen $c_x$ we solve for $x_Q$. -Volume $Q = x_0 - x_Q$ as above. - -### 4.2 Impact on Health Factor - -Let collateral value $C$, liability value $L$. -HF ≈ $\frac{C}{L}$ -Debt reduction $ΔL = Q·P_m$ implies new HF - -$HF' = \frac{C}{L - ΔL}$ - -We pre-compute $Q$ such that -$HF' \geq HF_{desired}$ (stored in LP configuration). - -### 4.3 Impact on Yield - -Yield improvement comes from: -1. Lower borrow APY (less debt). -2. Increased supply APY (larger net deposit after arbitrage). - -Since both APYs are approximately linear in notional, composite score rises. - ---- - -## 5. Practical Parameter Selection Cheat-Sheet - -| Situation | Goal | Suggested Tweaks | -|-----------|------|------------------| -| High debt, small shortfall (HF just below threshold) | Small $Q$ | $\epsilon \approx 0.25\%$, $c_x = 0.8$ | -| High debt, large shortfall | Large $Q$ | $\epsilon \approx 1\%$, $c_x = 0.95$ (flatten) | -| Need to pull in *other* asset | Swap roles of x/y + mirror logic | - ---- - -## 6. Monitoring & Exit Criteria - -* **Trigger**: Contract already triggers when `compositeScore < rebalanceThreshold`. -* **Exit**: Watch `compositeScore ≥ rebalanceDesired` OR elapsed blocks/time-window. -* Fallback: Manual override by LP owner. - ---- - -## 7. Limitations & Further Research - -1. Accurate market price $P_m$ feed is critical (oracle lag risk). -2. Numerical solve for $x_0$ can be done off-chain and passed as calldata. -3. The effect of $c_y$ when arbitrage swings to the opposite side needs more simulation. -4. Multi-hop arbitrage paths (via other DEXs) could require larger $\epsilon$. -5. Gas cost of re-deploying pool vs. adjusting in-place parameters (future EulerSwap versions may allow). - ---- - -## 8. Summary Flowchart - -1. `updateMetrics()` detects score < threshold → emits `RebalanceTriggered`. -2. Off-chain keeper: - * Reads LP config & current market data - * Computes $(x_0,y_0,p_x,p_y,c_x,c_y)$ using §3 algorithm - * Calls `rebalance(lp, data.rebalanceDesired)` which - – closes old pool, installs new Params -3. Arbitrageurs trade → debt asset flows into LP -4. Composite score crosses `rebalanceDesired` → keeper restores neutral Params. - ---- - -*Prepared for JIT-Pilot research – v0.1* \ No newline at end of file diff --git a/src/JITpilot.sol b/src/JITpilot.sol index 12e9d6a..c4926ec 100644 --- a/src/JITpilot.sol +++ b/src/JITpilot.sol @@ -290,45 +290,12 @@ contract JITpilot { return (twaYield * PRECISION) / yieldTarget; } - /** - * @dev Calculate dynamic rebalance threshold based on LP configuration (placeholder) - * @param lp LP address - * @return rebalanceThreshold Dynamic threshold based on hfMin and safety margin - */ - function calculateRebalanceThreshold(address lp) internal view returns (uint256) { - LPData storage data = lpData[lp]; - if (!data.initialized) return 0; - - // Placeholder implementation - to be researched and implemented - // Should calculate threshold based on hfMin as main parameter - // with thresholdSafetyMargin for fine-tuning - return data.hfDesired; // Default hfDesired for now - } - - /** - * @dev Calculate dynamic rebalance desired target based on LP configuration (placeholder) - * @param lp LP address - * @return rebalanceDesired Target score to achieve after rebalancing - */ - function calculateRebalanceDesired(address lp) internal view returns (uint256) { - LPData storage data = lpData[lp]; - if (!data.initialized) return 0; - - // Placeholder implementation - to be researched and implemented - // Should calculate target based on hfDesired and yieldTarget as main parameters - // with desiredTargetRatio for fine-tuning - return 8e17; // Default 0.8 for now - } - /** * @dev Fetch current block data (placeholder - to be implemented later) * @param lp LP address * @return BlockData struct with current metrics */ 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 - // update EulerSwap pool data address poolAddr = IEulerSwapFactory(eulerSwapFactoryAddress).poolByEulerAccount(lp); EulerSwapData memory eulerSwapData = getEulerSwapData(poolAddr); @@ -422,8 +389,6 @@ contract JITpilot { * @param lp LP address to rebalance */ function _rebalance(address lp) internal { - // Placeholder implementation - this will perform actual rebalancing - // Will be implemented in later iterations // This function is only called after confirming that a rebalance is needed // #1 Fetch current EulerSwap data