Withdraw flow
LT.withdraw unwinds a position in proportion and sends the volatile asset to the receiver. LT is the market's vault contract. In the same transaction, LT repays the debt of the leveraged AMM (LEVAMM) and burns the shares. Use this trace to set min_assets, predict the events, and explain a short payout.
Prerequisites
Before you call LT.withdraw, complete these items:
- Staked shares must leave the gauge before the holder calls
LT.withdraw. To unstake, follow Unstake in the Stake and unstake flow. - Set
min_assetsfrompreview_withdraw. For the call pattern withpreview_withdrawandmin_assets, read Deposit and withdraw.
Steps
The following figure shows the calls of one withdrawal, from the holder to the borrower-interest donation at the end:
The withdrawal runs these steps in order:
- The holder calls
LT.withdraw(shares, min_assets, receiver).receiverdefaults to the caller.LTchecks thatsharesis not 0, that neither the caller norreceiverisLT.staker(), and that the LEVAMM is not killed. LTreads the price with_price_oracle_w()and runs_calculate_valuesto bring the buckets up to date. The buckets are the staked, unstaked, and admin parts of the market value;LT.liquidity()holds their stored values.LTthen checks the share remainder: after the burn, the share supply must be 0 or at leastMIN_SHARE_REMAINDER.LTcallsAMM._withdraw(frac).fracis the fraction of the LEVAMM position thatsharesclaim.LTexcludes a positive admin balance, the admin bucket, from the claim. The LEVAMM removes that fraction of its collateral and debt and returns both amounts.LTpulls the LP collateral from the LEVAMM and callsremove_liquidity_fixed_out(lp, 0, debt, 0)on the Curve pool. The pool returns exactlydebtcrvUSD and the rest of the LP value as the asset. On a v3.0.0 pool (markets 7 to 10) with a non-zeroPOLICY(), the pool callsupdate_pool_stateon the policy. The policy does not check exits.LTburns the shares and updates the total and admin buckets. It checks the asset amount received againstmin_assets.LTsends the crvUSD to the LEVAMM, which repays the debt that step 3 removed.LTthen sends the asset toreceiver.LTcalls_checkpoint_gauge(), emitsWithdraw, and runs_distribute_borrower_fees, as in step 8 of the Deposit flow.
Withdrawal fee
The removal takes more of one coin than the pool ratio. A yb-LP deposit or withdrawal pays half the pool fee rate on the imbalanced part. preview_deposit and preview_withdraw include this fee.
The fee rate depends on the policy that the pool runs:
- On a pool with no policy, or one that runs
YBTwocryptoPolicy, the fee is the native fee of the pool. - On a pool that runs the oracle-report policy (
YBOraclizedPolicy), while the policy is not killed, the policy returnsfallback_feefor every liquidity operation.fallback_feeis a field ofconfig()on the policy.
The pools of markets 0 to 6 run older pool code without a policy hook. For the fee formula and the fee that each policy sets on a liquidity operation, read get_fee in Pool policy.
Why a withdrawal can return less than its Fundamental Value
PPS is the price per share, LT.pricePerShare(), with 18 decimals. The Fundamental Value of a position is PPS × shares. Three independent causes can make a withdrawal pay less than PPS × shares:
- Temporary Redemption Discount (TRD). TRD is the signed gap between Redemption Value (
preview_withdraw) and Fundamental Value (pricePerShare()). During a TRD,preview_withdraw(shares) * 10**(18 - asset_decimals) < pricePerShare() * shares // 10**18. The factor scales the raw asset amount to the 18 decimals of PPS. - PPS drag. PPS drag is slow or negative Fundamental Value growth because the rebalancing cost outran fee income.
- Watermark shortfall. The watermark (
ideal_staked) is the staked value that the gauge must regain after losses. The gap below it is the watermark shortfall. A watermark shortfall applies to staked positions only;LT.liquidity()shows it asstaked < ideal_staked.
For TRD and PPS drag, read Fundamental Value, Redemption Value, and TRD. For how later gains repay a staked loss, read Watermark and recovery.
Events emitted
A withdrawal can emit these events:
LT.TransferbetweenLT.staker()and the zero address: only when the bucket update in step 2 changes the LT balance of a gaugeAMM.RemoveLiquidityRaw(collateral_change, debt_change)Pool.RemoveLiquidityImbalance(provider indexed, lp_token_amount, token_amounts, approx_fee, price_scale): on markets 7 to 10, fromremove_liquidity_fixed_outwith a non-zero debt (RemoveLiquidityOneif the debt is 0)LT.Transfer(sender, receiver=0x0, value): the burnLT.Withdraw(sender indexed, receiver indexed, owner indexed, assets, shares)AMM.CollectFees(amount, new_supply): indistribute_borrower_feesLT.DistributeBorrowerFees(sender indexed, amount, min_amount, discount): only whenLTholds crvUSD to donate
Reverts by step
A withdrawal 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.
| Step | Revert | Condition |
|---|---|---|
| 1 | "Withdrawing nothing" | shares == 0 |
| 1 | "Withdraw to/from staker" | msg.sender == staker() or receiver == staker(). Unstake from the gauge first |
| 1 | "We're dead. Use emergency_withdraw" | AMM.is_killed() |
| 2 | "Remainder too small" | Before the burn, supply < MIN_SHARE_REMAINDER + shares and supply ≠ shares. Withdraw less, or withdraw all remaining shares |
| 5 | "Slippage" | crypto_received < min_assets |
| 7 | "GAS" | The first GaugeController.checkpoint call fails and less than 200,000 gas remains for the retry |
| 7 | "slippage" or "donation above cap!" | Markets 7 to 10: the borrower-interest donation at the end of the call 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.
Related
- LT: the full reference for
withdraw,preview_withdraw, andemergency_withdraw. - Pool policy: the fee that each policy sets on a liquidity operation.
- Deposit and withdraw: the call pattern and the unstake step for staked positions.
- StakeZap:
withdraw_and_unstake, which redeems gauge shares and withdraws fromLTin one call. - Withdraw liquidity: the user steps in the app.
- Fundamental Value, Redemption Value, and TRD: the value concepts behind a short withdrawal.