Skip to main content

Withdraw Flow

Withdraw unwinds the position proportionally. The user receives the volatile asset; LT handles debt repayment and share burning atomically.

sequenceDiagram
participant User
participant LT
participant AMM as LEVAMM
participant Cryptoswap

User->>LT: withdraw(shares, min_assets, receiver)
LT->>LT: _calculate_values (update buckets)
LT->>AMM: _withdraw(frac) %% compute pro-rata collateral + debt
AMM-->>LT: (d_collateral, d_debt)
LT->>Cryptoswap: transferFrom(amm, LT, d_collateral) %% reclaim LP
LT->>Cryptoswap: remove_liquidity_fixed_out(lp, 0, d_debt, 0)
Cryptoswap-->>LT: asset + crvUSD
LT->>LT: _burn(msg.sender, shares)
LT->>AMM: transfer crvUSD (repay debt allocation)
LT->>User: asset (crypto_received)
LT->>LT: distribute_borrower_fees()

remove_liquidity_fixed_out takes a fixed debt-token amount out (pinning the crvUSD side) and lets the asset side float — that is how LT guarantees the AMM's debt is repaid exactly while the user receives whatever asset falls out.

Why you might receive less than PPS × shares

Three independent causes. A single withdrawal can experience any combination of them.

1. Temporary Redemption Discount (TRD)

  • Nature: LEVAMM's invariant-implied state vs. Cryptoswap's current price disagree at the moment of withdrawal.
  • Sign: preview_withdraw(shares) < pricePerShare() × shares / 1e18.
  • Resolution: transient — closes as arbs move through VirtualPool and realign LEVAMM with Cryptoswap. Typical: hours to days.
  • Applies to: both staked and unstaked positions.
  • Reference: Protocol: TRD.

2. PPS drag

  • Nature: cumulative rebalance slippage over the holding period exceeded cumulative fees captured. PPS grew less than a corresponding unleveraged position would suggest — drag is embedded in the vault's accounting, not a transient gap.
  • Sign: even with TRD = 0, the redemption value at unwind is below a hypothetical "pure-fees" expectation.
  • Resolution: only recovers if forward fee capture outpaces forward rebalance cost — not guaranteed.
  • Applies to: unstaked positions (staked has watermark protection).
  • Reference: Protocol: PPS vs Redemption Value.

3. Watermark shortfall

  • Nature: staked < ideal_staked at the time of withdrawal. The staked bucket has absorbed losses that have not yet been paid back by incoming fees.
  • Sign: staked-position redemption below the watermark; visible as 1 − staked/ideal_staked.
  • Resolution: transient — Recovery Mode routes all admin fee to the staked bucket until parity is restored.
  • Applies to: staked positions only.
  • Reference: Protocol: Watermark & Recovery.

Interaction

TRD and watermark shortfall are independent — a withdrawal can have nonzero TRD while also seeing a watermark shortfall. PPS drag is structurally different (accumulated inside PPS itself) and coexists with both. Use preview_withdraw for a pre-trade estimate that includes TRD, and inspect LT.liquidity for the watermark gap separately. PPS drag is visible only by comparing realised PPS to expectation over your holding period — there is no single on-chain value that surfaces it.

Events emitted

  • LT.Withdraw(sender, receiver, owner, assets, shares)
  • AMM.RemoveLiquidityRaw(collateral_change, debt_change)
  • Cryptoswap.RemoveLiquidity (from the pool)
  • LT.DistributeBorrowerFees — on the refuel hook

Edge cases

CaseBehaviour
shares == 0Reverts "Withdrawing nothing"
msg.sender == staker() or receiver == staker()Reverts "Withdraw to/from staker" — unstake from gauge first
AMM.is_killed()Reverts "We're dead. Use emergency_withdraw"
crypto_received < min_assetsReverts "Slippage"
Post-burn supply < MIN_SHARE_REMAINDER + shares and supply ≠ sharesReverts "Remainder too small" — withdraw more or withdraw all

Gas

~ 300–450k on mainnet, dominated by Cryptoswap remove_liquidity_fixed_out and LEVAMM invariant update.