Skip to main content

Deposit flow

LT.deposit puts the depositor into a 2× leveraged liquidity provider (LP) position in one transaction. LT is the market's vault contract. The depositor provides only the volatile asset, and LT borrows the matching crvUSD from the allocation of the leveraged AMM (LEVAMM). Use this trace to compute debt, predict the events, and diagnose a revert.

Prerequisites​

Before you call LT.deposit, complete these items:

  • Approve LT for assets of the asset token.
  • Compute debt with the rule in Compute debt before the call.
  • Simulate deposit with eth_call before you send it. preview_deposit checks the deposit cap, but it does not run the LT deviation check. On the first deposit into an empty LT, it does not check the safe band either.

Compute debt before the call​

debt sets the LEVAMM leverage: at leverage 2, the borrowed crvUSD is about equal to the asset value. The price rule is debt = assets * pool.price_oracle() // 10**asset_decimals.

The rule to use depends on the pool policy, a contract that the Curve pool calls to set its swap fee, its price_scale target, or both. YBOraclizedPolicy is the oracle-report policy.

The LEVAMM-book debt is the debt that matches the debt-to-collateral ratio of the LEVAMM book, AMM.get_debt() / AMM.collateral_amount(). The LT deviation check of the policy compares debt with this value, within ratio_tolerance.

Use the price rule unless the pool runs a YBOraclizedPolicy that is not killed; then use the LEVAMM-book debt, or the deposit reverts with LT deviation.

For the policy check, the policy lookup, and the debt solver, read Compute debt on a pool that runs the oracle-report policy in Deposit and withdraw. For a flowchart of the rule, including the case of an empty LEVAMM book, read Choose the debt rule on the same page.

Safe band​

After the deposit, the LEVAMM requires debt / collateral_value between 1/16 (6.25%) and 8.5/16 (53.125%). A deposit outside this band reverts with Unsafe min or Unsafe max.

A first deposit with debt = 0 reverts with Unsafe min.

Steps​

The following figure shows the calls of one deposit, from the depositor to the borrower-interest donation at the end:

Sequence of calls in LT.deposit: LT pulls crvUSD from the LEVAMM and the asset from the depositor and adds both to the Curve pool, which mints LP tokens to the LEVAMM and, if the pool has a policy, calls update_pool_state on it. LT then updates its buckets, books the deposit in the LEVAMM, mints yb-LP to the receiver, checkpoints the gauge, collects borrower interest, and donates it to the pool if it holds crvUSD.Sequence of calls in LT.deposit: LT pulls crvUSD from the LEVAMM and the asset from the depositor and adds both to the Curve pool, which mints LP tokens to the LEVAMM and, if the pool has a policy, calls update_pool_state on it. LT then updates its buckets, books the deposit in the LEVAMM, mints yb-LP to the receiver, checkpoints the gauge, collects borrower interest, and donates it to the pool if it holds crvUSD.

The deposit runs these steps in order:

  1. The depositor calls LT.deposit(assets, debt, min_shares, receiver). receiver defaults to the caller. LT first checks that receiver is not LT.staker(), the gauge of the market.
  2. LT pulls debt crvUSD from the LEVAMM address, which holds the crvUSD allocation of the market. The Factory allocates that crvUSD through LT.allocate_stablecoins. LT then pulls assets from the caller.
  3. LT calls add_liquidity([debt, assets], 0, amm, False) on the Curve pool, and the pool mints the LP tokens to the LEVAMM. On a v3.0.0 pool (markets 7 to 10) with a non-zero POLICY(), the pool calls update_pool_state on the policy. On a pool that runs the oracle-report policy, the policy runs the LT deviation check in this callback.
  4. LT reads the price with _price_oracle_w(). If shares exist, LT runs _calculate_values before it books the deposit. _calculate_values brings the staked, unstaked, and admin buckets up to date. The buckets are the parts of the market value that LT accounts for separately. LT.liquidity() stores total, staked, ideal_staked (the watermark), and admin. For how _calculate_values allocates a value change between the buckets, read _calculate_values() in LT.
  5. LT calls AMM._deposit(lp_tokens, debt). The LEVAMM adds the collateral and the debt, checks the safe band, and returns the position value after the deposit. LT checks that value against AMM.max_debt() / 2.
  6. LT computes the shares and checks the share remainder: the share supply after the deposit must be at least MIN_SHARE_REMAINDER. LT then checks min_shares and mints the shares to receiver.
  7. LT calls _checkpoint_gauge(), which checkpoints the gauge in the GaugeController. If that call reverts, LT tries once more with 200,000 gas. LT ignores a second revert, and then emits Deposit.
  8. LT runs _distribute_borrower_fees. It calls AMM.collect_fees(). If LT then holds crvUSD, it donates the crvUSD to the Curve pool with add_liquidity([amount, 0], min_amount, empty(address), True).

What happens inside the Curve pool​

When LT calls add_liquidity, the Curve pool can move its price_scale toward its target. This move is not the LEVAMM rebalance. The two corrections differ in this way:

  • Curve pool price_scale move. On markets 7 to 10, only the first operation in a block that calls tweak_price can move price_scale. If that operation fails the profit test (vp_boosted > lp_xcp_profit), price_scale does not move in that block. Without a policy, the target is price_oracle, the internal EMA of the pool. With a policy, the target comes from the policy. The move can spend part of the rebalance budget of the pool, the pool profit that reserved_profit_fraction does not protect.
  • LEVAMM leverage correction. Arbitrage trades through VirtualPool make this correction later. This deposit does not start it. For the trade, read the Rebalance flow.

Events emitted​

A deposit can emit these events:

  • Pool.AddLiquidity(provider indexed, receiver indexed, token_amounts, fee, token_supply, price_scale): field list of the v3.0.0 pools of markets 7 to 10
  • AMM.AddLiquidityRaw(token_amounts, invariant, price_oracle)
  • LT.Transfer between LT.staker() and the zero address: only when the bucket update from step 4 changes the LT balance of a gauge
  • LT.Transfer(sender=0x0, receiver, value): the mint
  • LT.Deposit(sender indexed, owner indexed, assets, shares)
  • AMM.CollectFees(amount, new_supply): in distribute_borrower_fees
  • Pool.Donation(donor indexed, token_amounts), a second Pool.AddLiquidity, and LT.DistributeBorrowerFees(sender indexed, amount, min_amount, discount): only when LT holds crvUSD to donate. The pool events are those of the v3.0.0 pools of markets 7 to 10.

The policy callback emits no event.

Reverts by step​

A deposit can revert at these steps. Each row names the step of the walk-through in which the revert occurs. For the full list of strings, read Error codes.

StepRevertCondition
1"Deposit to staker"receiver == LT.staker()
3"LT deviation"The pool runs YBOraclizedPolicy, the policy is not killed, and debt is more than ratio_tolerance from the proportional debt (from the pool policy, inside add_liquidity)
3"!wl"Markets 7 to 10: the pool LP allowlist is on and LT is not listed (from the Curve pool)
5No messageThe LEVAMM is killed (from AMM._deposit)
5"Unsafe min"debt / collateral_value after the deposit is below 1/16 (from AMM._deposit)
5"Unsafe max"debt / collateral_value after the deposit is above 8.5/16 (from AMM._deposit)
5"Debt too high"The LEVAMM value after the deposit is more than AMM.max_debt() / 2
6"Remainder too small"shares + supply < MIN_SHARE_REMAINDER. The constant is 10**6 share units, which is 10⁻¹² yb-LP
6"Slippage"The computed shares are less than min_shares
7"GAS"The first GaugeController.checkpoint call fails and less than 200,000 gas remains for the retry
8"slippage" or "donation above cap!"Markets 7 to 10: the borrower-interest donation fails in the Curve pool (from the Curve pool). The donation runs only when LT holds crvUSD

Gas​

Gas depends on the pool and its policy. To estimate it, call eth_estimateGas or simulate the transaction in the target block.

  • LT: the full reference for deposit and preview_deposit.
  • LEVAMM: _deposit, the safe band, and max_debt.
  • Pool policy: the LT deviation check and ratio_tolerance.
  • Deposit and withdraw: the call pattern and the debt solver.
  • Compounding leverage: how 2× compounding leverage behaves as the price moves.