Skip to main content

Deposit Flow

Deposit places the user into a 2×-leveraged LP position in one atomic call. The borrow is internal (LT holds the debt, not the user); the user only provides the volatile asset.

sequenceDiagram
participant User
participant LT
participant AMM as LEVAMM
participant Cryptoswap

User->>LT: deposit(assets, debt, min_shares, receiver)
LT->>AMM: transferFrom(amm, LT, debt) %% crvUSD allocation
LT->>Cryptoswap: add_liquidity([debt, assets], 0, receiver=amm)
Cryptoswap-->>AMM: LP tokens (receiver)
LT->>AMM: _deposit(lp_tokens, debt) %% updates invariant state
LT->>User: mint yb-LP shares
LT->>LT: distribute_borrower_fees() %% refuel hook

Why the borrowed leg matches the deposit value

The debt parameter targets L = 2 inside the AMM. At equilibrium, AMM collateral value is about 2 × debt in crvUSD-denominated units. An LT deposit creates that collateral by adding the user's volatile asset plus the borrowed stable leg into Cryptoswap, so the LT-call estimate is:

debtassets×oracle price10asset decimals\text{debt} \approx \frac{\text{assets} \times \text{oracle price}}{10^{\text{asset decimals}}}

Example: if the asset has 18 decimals, assets = 1e18, and the oracle price is 100_000e18 crvUSD per asset, then debt_estimate = 100_000e18 crvUSD. The resulting Cryptoswap LP collateral is roughly the volatile leg plus the borrowed stable leg, so AMM debt is about half of collateral value.

The tradeoff: leverage amplifies Cryptoswap fee capture (you hold 2× the LP exposure of a flat deposit) but also amplifies any IL-related drawdown if markets move before arbitrageurs have rebalanced LEVAMM back to L=2. Continuous rebalancing via arb (see Compounding Leverage) is what keeps the amplification bounded.

Safety band

At L = 2, the AMM enforces a band on coll_vs_debt = p_o × y / d of roughly 6.25% to 53.125% around the target. A debt that would push post-deposit state outside this band reverts — including debt = 0 in almost all cases. The edge-cases table below shows the concrete failure modes. Use LT.preview_deposit(assets, debt) to check before sending the transaction.

What happens inside Cryptoswap (separate from LEVAMM)

When LT calls add_liquidity, Cryptoswap may internally rebalance its price_scale if the deposit moves the pool's target price. This is not the same as LEVAMM rebalancing:

  • Cryptoswap price_scale rebalance — internal to the pool, fee-funded. May consume rebalance reserve when price_scale shifts. Can impact all Cryptoswap LPs (including LEVAMM as holder).
  • LEVAMM L=2 drift correction — happens later via arb trades through VirtualPool. Not triggered by this deposit directly.

The two are decoupled. Your deposit interacts with the first; the second cleans up any oracle-vs-LEVAMM drift afterwards.

Contracts touched

  • LT.vy — entry; manages share accounting and bucket reallocation.
  • AMM.vy (LEVAMM) — receives LP as collateral, updates debt and invariant state via _deposit.
  • Curve Cryptoswap — add_liquidity with LEVAMM as receiver.
  • The AMM address holds the borrowing budget (allocated by Factory.set_allocator); crvUSD flows from the AMM to LT to Cryptoswap.

Events emitted

  • LT.Deposit(sender, owner, assets, shares)
  • AMM.AddLiquidityRaw(token_amounts, invariant, price_oracle)
  • Cryptoswap.AddLiquidity (from the pool)
  • LT.DistributeBorrowerFees(sender, amount, min_amount, discount) — on the refuel hook

Edge cases

CaseBehaviour
receiver == LT.staker()Reverts "Deposit to staker"
Post-deposit value > AMM.max_debt() / 2Reverts "Debt too high"
Computed shares < min_sharesReverts "Slippage"
Post-mint shares + supply < MIN_SHARE_REMAINDER (1e6)Reverts "Remainder too small"
Cryptoswap balance too imbalanced for the requested depositReverts from Cryptoswap

Gas

~ 300–500k on mainnet, dominated by add_liquidity and LEVAMM invariant solve.

Computing debt before the call

p_o = PriceOracle(oracle).price()
debt_estimate = assets * p_o // 10**asset_decimals

# Preview to check safety bounds before committing
shares_estimate = lt.preview_deposit(assets, debt_estimate)

preview_deposit reverts with "Debt too high" if debt_estimate would breach AMM.max_debt() / 2. To get a soft quote (return 0 instead of reverting), pass a third argument raise_overflow=False. LTMigrator and HybridVault use this path.