Deposit & Withdraw
LT.vyis ERC-4626-like but not strictly compliant. It emits ERC-4626Deposit/Withdrawevents, but itsdeposittakes an explicitdebtparameter (not computed fromassets) and it does not implementconvertToAssets,convertToShares,mint,redeem,previewMint, orpreviewRedeem. It implementsIERC20only. Transfers to/from thestakeraddress are rejected via explicit revert. For strict ERC-4626 semantics, use theLiquidityGauge(staker) instead, which wraps LT shares.
This page covers the integration-side call pattern. For the on-chain sequence (what LT does with your deposit), see Action Flow: Deposit and Action Flow: Withdraw.
Deposit
Signature
def deposit(
assets: uint256,
debt: uint256,
min_shares: uint256,
receiver: address = msg.sender
) -> uint256
assets— amount of the volatile asset (e.g. WBTC) you are depositing.debt— crvUSD amount LEVAMM will borrow to maintain . For an LT deposit, this is typically the oracle value of the volatile leg:assets × oracle_price / 10**asset_decimals, denominated in crvUSD wei.min_shares— sandwich/slippage protection.receiver— destination for minted yb-LP. Must not equalLT.staker(); useStakeZapfor atomic deposit + stake.
Call pattern
# 1. Compute expected debt at current oracle price
p_o = PriceOracle(oracle).price()
debt_estimate = assets * p_o // 10**asset_decimals
# 2. Preview to get expected shares (reverts if Debt too high)
shares_estimate = lt.preview_deposit(assets, debt_estimate)
# Soft quote alternative: returns 0 instead of reverting on overflow.
# Used by LTMigrator and HybridVault.
# shares_estimate = lt.preview_deposit(assets, debt_estimate, False)
# 3. Approve + deposit
asset.approve(lt, assets)
shares = lt.deposit(assets, debt_estimate, min_shares, receiver)
Worked example: if the asset uses 18 decimals, assets = 1e18, and the oracle price is 100_000e18 crvUSD per asset, then debt_estimate = 100_000e18 crvUSD. The AMM's L = 2 target is applied to the resulting Cryptoswap LP collateral value; because the deposit adds both the volatile leg and the borrowed stable leg, the borrowed leg is roughly equal to the volatile leg's oracle value.
Error modes
| Error | Cause |
|---|---|
"Deposit to staker" | receiver == LT.staker(). Either pass a different receiver or use StakeZap. |
"Debt too high" | debt exceeds AMM.max_debt() / 2. Reduce debt or wait for allocation. |
"Slippage" | shares < min_shares. Re-quote via preview_deposit and retry. |
"Remainder too small" | Final supply after mint would be below MIN_SHARE_REMAINDER (1e6). Increase deposit or combine with another. |
Gas
~ 300–500k on mainnet. Dominated by Cryptoswap add_liquidity and LEVAMM invariant solve.
Withdraw
Signature
def withdraw(
shares: uint256,
min_assets: uint256,
receiver: address = msg.sender
) -> uint256
Returns the amount of underlying asset received.
Call pattern
# If staked, unstake first
if gauge.balanceOf(user) > 0:
gauge.withdraw(gauge.balanceOf(user)) # returns yb-LP to user
# Quote redemption (includes TRD)
expected_assets = lt.preview_withdraw(shares)
# Withdraw with slippage bound
assets = lt.withdraw(shares, min_assets, receiver)
Error modes
| Error | Cause |
|---|---|
"Withdrawing nothing" | shares == 0. |
"Withdraw to/from staker" | Either msg.sender or receiver equals LT.staker(). Unstake from gauge first. |
"We're dead. Use emergency_withdraw" | AMM.is_killed() == True. Use emergency_withdraw instead; repay debt from wallet. |
"Slippage" | crypto_received < min_assets. Re-quote and retry. |
"Remainder too small" | Post-burn, supply would fall below MIN_SHARE_REMAINDER. Withdraw less, or (if full exit) withdraw all shares atomically — the last-withdrawer path is special-cased. |
preview_withdraw vs pricePerShare
Both speak to per-share value but via different reference prices:
pricePerShare()— returnsv.total × 1e18 / v.supply_tokensfrom_calculate_valuesat oracle price. The fundamental per-share value; sandwich-resistant; does not include TRD. Multiply byshares / 1e18for per-position PPS.preview_withdraw(shares)— realistic redemption: runscalc_withdraw_fixed_outagainst the current Cryptoswap state, factoring the fixed-debt-output unwind. Includes TRD.
TRD = preview_withdraw(1e18) / pricePerShare() − 1 for one share, or generally preview_withdraw(shares) × 1e18 / (pricePerShare() × shares) − 1. Negative during a discount (the common case); positive in the rare premium case. See Temporary Redemption Discount for resolution dynamics.
Note: LT does not expose convertToAssets — it implements IERC20 only. The ERC-4626 convertToAssets/previewRedeem shape lives on LiquidityGauge instead, where it maps gauge-shares to underlying LT-shares.
Gas
~ 300–450k. Dominated by Cryptoswap remove_liquidity_fixed_out and LEVAMM accounting.
Staked positions
If the user has yb-LP staked in LiquidityGauge:
- Call
gauge.withdraw(amount)— transfers yb-LP back to user, forfeiting future emissions. Accrued emissions are claimable separately. - Then call
LT.withdrawas above.
There is no combined "unstake + withdraw" helper. Use multicall if atomicity matters.
Preview reliability
Preview functions are view calls that read current state. In the same block, they are accurate. Across blocks, state may change (oracle updates, rebalance trades, other LPs' deposits/withdrawals).
For MEV-sensitive flows:
- Quote in the simulation, then execute with tight
min_*bounds. preview_withdrawvaries with Cryptoswap state — re-quote if you observe large trades in the mempool.preview_depositvaries with LEVAMM invariant state — same advice.
Events
| Event | When |
|---|---|
Deposit(sender, owner, assets, shares) | Every deposit |
Withdraw(sender, receiver, owner, assets, shares) | Every withdraw |
Transfer(from, to, value) | ERC-20 transfers of yb-LP, plus bucket-reallocation transfers on stake/unstake |
DistributeBorrowerFees(sender, amount, min_amount, discount) | When LT routes borrower interest to the donation path |
Related
- Core: LT — full contract reference.
- Action Flow: Deposit — what LT does on-chain.
- Action Flow: Withdraw — on-chain withdraw sequence.
- Quote & Route — shared preview patterns.
- Error Codes — full error-string catalog.
- Protocol: Temporary Redemption Discount — why
preview_withdraw<pricePerShare × shares / 1e18.