# Euler Maglev > VCISO Spearbit 2024-12-09 - cmichel > Commit: https://github.com/euler-xyz/euler-maglev/tree/392383785f7b9f0f9124a6ea52da9f05b015a4af # What to do when NAV changes? ## Net asset value > Net asset value = `vault0+vault1` collateral value - `vault0+vault1` debt At deployment time, the contract reads the currently deployed collateral amounts of the vaults and sets debt limits. This can be seen as defining a **max LTV for the AMM**. > For example, if the initial investment has a NAV of $1000, and the debt limit is configured at $5000, then the maximum LTV loan that the AMM will support will be `5000/6000 = 0.8333`. In order to leave a safety buffer, it is recommended to choose a maximum LTV that is below the borrowing LTV of the vault. This net asset value can change for several reasons: 1. Interest is accrued to collateral shares (increases NAV) 2. Debt is accrued to the borrow position (decreases NAV) 3. The position is (partially) liquidated (decreases NAV) 4. Swap fees and "slippage" is earned. The NAV increases by `amountInValue - amountOutValue`. This is curve dependant. The constant-sum curve does not earn any slippage. Constant-product / Euler curve earns slippage. (increases NAV) 5. The user account holding the position performs an action that changes the NAV (withdraw,deposit,borrow,repay). ## Current solution & problem Currently, the contract does not directly react to any NAV changes besides slippage & swap fees. This leads to the issue of the **reserves getting out of sync** when interest accrues or when the position gets liquidated. 1. Swaps can fail as they overestimate the max swap output amount, meaning, the max amount they can still borrow while being healthy. The entire reserves cannot be traded out anymore for constant-sum. 2. It enables swaps that end up in a CDP that goes **above the max LTV**. An attacker can swap past the max LTV, right up to the position becoming liquidatable in the next block. In the next block, the attacker liquidates the AMM for profit. ### Example Let $C_i$ define the collateral asset value deposited into $\rm{vault}_i$ and $D_i$ its debt value. Let $R_i$ be the reserve for vault $i$'s asset (corresponding to the `MaglevBase.reserve0/1` state variables). Imagine the example above with an initial NAV of $1000, a `maxLTV = 83.33%`, and the _constant-sum_ curve. A user performs a swap, a CDP is created. Borrow interest accrues increasing the debt **but the reserves do not change**. Another user performs another swap and the contract ends up with an LTV of $89.29\%$, higher than the max LTV. The CDP is close to liquidation at this point. | After action | $R_0$ | $C_0$ | $D_0$ | $R_1$ | $C_1$ | $D_1$ | LTV | |-----| -----|-----|-----|-----|-----|-----|-----| | deployment | 6000 | 1000 | 0 | 5000 | 0 | 0 | 0 | | $\rm{swap}_{0\rightarrow 1}$ 2000 | 8000 | 3000 | 0 | 3000 | 0 | 2000 | $\frac{2}{3}$ | | $400 debt accrues | 8000 | 3000 | 0 | 3000 | 0 | 2400 | 80% | | $\rm{swap}_{1 \rightarrow 0}$ 8000 | 0 | 0 | 5000 | 11000 | 5600 | 0 | $\frac{5000}{5600} = 89.29\%$ | ## Potential solution The above attack can be avoided by ensuring that no swap can be performed that would end up with the CDP having a higher LTV than the `maxLTV`. Before performing any swap, the vaults' interest is accrued, and the **reserves are dynamically adjusted** based on the current NAV (and CDP) in two steps: 1. Financially speaking, we adjust (add/remove) the liquidity of the AMM to match this new NAV. Geometrically speaking, we linearly scale the curve. Note that this results in the same curve that a newly deployed AMM with this initial NAV and `maxLTV` would also use. 2. The only problem is that the point we arrive at after scaling is the point that keeps the spot price $\frac{R_1}{R_0}$ constant. However, this point does not correspond to the point that matches the CDP amounts that would allow us to trade the entire range of the new curve. We need to **move to the correct point** on the curve. Financially speaking, this is a (fee-free) swap. ### Example 1. At the last swap of the previous example, we first recompute the new NAV of $C_0 - D_1 = 600$ ($400 of debt accrued). The reserves scaling factor is the solution of keeping $\frac{\rm{NAV}}{R_0 + R_1}$ constant. As the NAV was scaled down to $60\%$, the sum of the reserves should scale by the same factor of `0.6` from `11000` to `6600`: $R_0 + R_1 = 6600$ 2. We compute the `maxDebt` that we can take on for our AMM's `maxLTV` and **the new NAV**. It's the solution to $\frac{\rm{maxDebt}}{\rm{maxDebt} + NAV} = \rm{maxLTV}$. In the example, that's `maxDebt = 3000`. As our debt is $2400 in $\rm{vault}_1$, the most we can further swap out for $\rm{asset}_1 = \rm{maxDebt} - D_1 = 600$; giving us $R_1 = 600$. To be on the new curve, the other coordinate needs to be $R_0 = 6600 - 600 = 6000$. Our new reserves are $(R_0, R_1) = (6000, 600)$. | After action | $R_0$ | $C_0$ | $D_0$ | $R_1$ | $C_1$ | $D_1$ | LTV | |-----| -----|-----|-----|-----|-----|-----|-----| | after first swap \(P) | 8000 | 3000 | 0 | 3000 | 0 | 2000 | $\frac{2}{3}$ | | $400 debt accrues \(P) | 8000 | 3000 | 0 | 3000 | 0 | 2400 | 80% | | **Reserves adjustment** (Q) | 6000 | 3000 | 0 | 600 | 0 | 2400 | 80% | | option 1: $\rm{swap}_{1 \rightarrow 0}$ 6000 | 0 | 0 | 3000 | 6600 | 3600 | 0 | $\frac{3000}{3600} = 83.33\%$ | | option 2: $\rm{swap}_{0 \rightarrow 1}$ 600 | 6600 | 3600 | 0 | 0 | 0 | 3000 | $\frac{3000}{3600} = 83.33\%$ | See this [desmos](https://www.desmos.com/calculator/zheielos4e). ![image](https://hackmd.io/_uploads/B1A9AchVJl.png) ### Benefits 1. A single solution for all types of NAV changes: collateral interest, debt interest, partial liquidations, fees. 2. If the AMM is doing well (NAV increases) it automatically expands and allows for larger trades 3. If the AMM is doing badly (NAV decreases) it automatically scales back protecting against swapping above defined `maxLTV` and getting liquidated in the next block. ### Downsides / TBD 1. It's unclear yet if/how this generalizes to non-constant-sum curves: While scaling the curve is acceptable, moving on the curve changes the spot price which is bad for constant-product and the Euler curve. Such an update could likely be sandwiched for profit. 2. what to do if we're already at or past `maxLTV` a. but still healthy b. under water # Invariants & Fuzz tests Here's a list of invariants I would check and try to verify: 1. NAV (deposits - borrows) does not decrease after a swap (compared to right before a swap, can decrease with borrow interest) 2. At most one of vault0 or vault1 has debt (unless the CDP is under water). 3. AMM never tries to enable both vault0 and vault1 as controllers. (should follow from 2.) Fuzz tests: 1. Swapping back and forth does not lead to a profit. (Test with Euler curve ~~because it changes its shape after a swap and is asymmetric~~?)