WithdrawTicket and is paid out FIFO as borrowers repay, deposits arrive, or liquidations free liquidity.
The queue is opt-in per market, controlled by three boolean flags on the LendingMarket account.
When to enable the queue
| Curator situation | Enable the queue |
|---|---|
| Reserves run at high utilization (>90% sustained) | Yes |
| A vault sits on top, holding institutional capital subject to redemption requests | Yes |
| Reserves typically run at moderate utilization with deep buffers | Optional |
| Long-tail asset where exits are rare but should be predictable | Yes |
| Reserves where you want every withdrawal to be instant or revert | Leave disabled |
Three flags, three operations
| Flag | What it gates |
|---|---|
withdraw_ticket_issuance_enabled | enqueue-to-withdraw: a depositor can submit collateral and receive a ticket |
withdraw_ticket_redemption_enabled | withdraw-queued-liquidity: a ticket holder can claim their share when capacity is available |
withdraw_ticket_cancellation_enabled | cancel-withdraw-ticket: a ticket holder can exit the queue |
issuance + redemption enabled, cancellation disabled — depositors can join the line and be paid, but cannot back out, which simplifies accounting for vaults sitting on top.
The minimum queued value threshold
min_withdraw_queued_liquidity_value sets the minimum USD value (scaled fraction) a single ticket may represent. Below the threshold, enqueue-to-withdraw reverts with WithdrawTicketValueTooSmall.
| Value | Effect |
|---|---|
0 | No floor. Any-size tickets allowed (risk: dust spam) |
| Reasonable floor (e.g., $1 equivalent) | Filters dust, keeps the queue economically meaningful |
| Aggressive floor (e.g., $1000 equivalent) | Restricts the queue to sizable depositors only |
- SDK
- API
- Kamino CLI
Configure the queue via SDK
Enabling the queue is a market-level config update. UsekaminoManager.updateLendingMarketIxs and toggle the relevant fields.User-side queue operations
The user-facing queue instructions (enqueueToWithdraw, withdrawQueuedLiquidity, cancelWithdrawTicket, recoverInvalidTicketCollateral) are part of the on-chain klend program. The current public klend-sdk does not yet expose dedicated TypeScript helpers for these.Until the helpers ship, end-user apps integrate the queue via:- A direct kvault deposit/redeem flow when a vault sits on top of the market (the vault submits the ticket on the user’s behalf via its own kvault tooling)
- The Kamino webapp surface for queue-enabled markets that are listed in
resources.json - Building the ixes manually from the program IDL
Anatomy of a ticket
When a depositor callsenqueue-to-withdraw, the program:
- Burns their collateral cTokens from their wallet
- Increments the reserve’s
WithdrawQueue.queued_collateral_amount - Creates a
WithdrawTicketaccount with:sequence_number(monotonically increasing per reserve)owner(the depositor’s wallet)reserve(the reserve being withdrawn from)queued_collateral_amount(the cTokens held in queue)created_at_timestampprogress_callback_typeandprogress_callback_custom_accounts(optional callback hook)
Sequence numbers and FIFO order
The reserve tracks two counters:| Counter | What it tracks |
|---|---|
next_issued_ticket_sequence_number | The number the next new ticket will receive. Increments on enqueue-to-withdraw |
next_withdrawable_ticket_sequence_number | The smallest number that has not yet been redeemed. Increments on withdraw-queued-liquidity after a ticket is fully closed |
N is redeemable when next_withdrawable_ticket_sequence_number == N, provided the reserve has enough liquidity to satisfy it.
The redemption flow
The reserve gains liquidity over time through borrower repayments, new deposits, liquidations, and (for markets with a vault sitting on top) the vault’sinvest operation routing capital in. Whenever liquidity is available, the holder of the next-eligible ticket can call withdraw-queued-liquidity. The program checks the ticket’s sequence_number against the reserve’s next_withdrawable_ticket_sequence_number; if they match, it pays out as much of the queued collateral as the available liquidity covers. Partial fills are allowed: a ticket that asks for 30k free liquidity is paid $30k now and waits for the rest.
When a ticket is fully drained, the program closes its account and increments next_withdrawable_ticket_sequence_number so the next ticket in line becomes eligible. Holders watch the queue and submit the redemption transaction when liquidity allows; in practice, off-chain bots monitor and submit on behalf of users, which is how the standard Kamino UX surfaces the queue to depositors.
Cancellation
Whenwithdraw_ticket_cancellation_enabled = 1, a ticket holder can call cancel-withdraw-ticket to back out of the queue:
- The remaining
queued_collateral_amountis converted back to cTokens and returned to the holder - The ticket is marked
invalid = 1and closed next_withdrawable_ticket_sequence_numberdoes not advance — the cancelled slot is skipped on the next redemption
Recovering invalid tickets
If a ticket gets stuck in an invalid state (the program detects this on certain edge cases), the curator or emergency authority can callrecover-invalid-ticket-collateral to drain its queued collateral and restore queue progression.
Vault integration: progress callback
AWithdrawTicket can carry an optional progress callback. When the ticket completes redemption, the program invokes the callback to notify a corresponding program — most commonly a Kamino vault.
enqueue-to-withdraw on behalf of one of its depositors, the ticket can carry KlendQueueAccountingHandlerOnKvault so that on redemption, the kvault is automatically notified to credit the depositor’s vault share. This is the standard pattern for institutional vaults sitting on top of a queue-enabled market.
Common errors
| Error | Cause |
|---|---|
WithdrawTicketValueTooSmall | The ticket’s value is below min_withdraw_queued_liquidity_value. Either reduce the threshold or have the depositor combine smaller withdrawals |
WithdrawTicketIssuanceDisabled | withdraw_ticket_issuance_enabled = 0. Enable the flag |
WithdrawTicketRedemptionDisabled | withdraw_ticket_redemption_enabled = 0. Enable the flag |
WithdrawTicketCancellationDisabled | withdraw_ticket_cancellation_enabled = 0. Enable the flag, or accept that cancellation is intentionally disabled |
| Redemption returns less than expected | Reserve liquidity is partially available — the program pays out what it can; the ticket carries the remainder |
Reference
- Market config reference — queue-related fields
- Troubleshooting —
WithdrawTicketValueTooSmalland related dust edge cases - Permissioned markets — gated venues where queue mechanics often pair with allowlist enforcement