For the complete documentation index, see llms.txt. This page is also available as Markdown.

Kelp Withdrawal Queue

A plain adapter that reports assets owed to an account from open requests in Kelp DAO's rsETH exit queue (the LRTWithdrawalManager).

A plain adapter that reports assets owed to an account from open requests in Kelp DAO's rsETH exit queue (the LRTWithdrawalManager). When a user unstakes rsETH, the rsETH is burned and a per-(asset, user) request is recorded denominated in the LST the user chose to receive (stETH, ETHx, or native ETH). The supported-asset set is discovered at runtime from lrtConfig().getSupportedAssetList().


Positions returned

One Supplied leg per open request, per Kelp-supported payout asset that is registered in NAVCalculator:

Leg
PositionKind
isDebt
isLocked
Description

Per unlocked request

Supplied

false

false

Owed for that payout asset from an unlocked request, withdrawable now

Per cooling request

Supplied

false

true

Owed for that payout asset from a still-cooling request

Each open request becomes its own leg, valued at its expectedAssetAmount, with isLocked set from that request's state. Every leg shares one positionId regardless of payout asset, so legs are told apart by balanceAsset.asset (which payout asset), isLocked (claimable vs still-cooling) and positionInstanceId (the per-request userNonce). A zero-amount leg is omitted; requests for assets not registered in NAVCalculator are dropped, as are assets filtered out by assetFilter.


Balance calculation

Flowchart of the Kelp Withdrawal Queue balance adapter: read calls derive position legs into PositionBalance entries.
Kelp Withdrawal Queue adapter — how the underlying balances reported to the NAV Calculator are derived.

For each supported asset, the open-request count is end - begin from the per-(asset, user) deque — the full set, with no adapter-level cap. Each open request is emitted as its own leg, valued at its expectedAssetAmount, with isLocked set by checking whether it has unlocked:

A request is unlocked (claimable, isLocked=false) iff userNonce < nextLockedNonce(asset) and block.number >= withdrawalStartBlock + withdrawalDelayBlocks(); otherwise it is still cooling and its leg gets isLocked=true. Claimed requests are popped off the front of the deque, so only live claims are counted — no out-of-bounds probing is needed. Kelp keys requests to the unstaking msg.sender (no arbitrary-owner parameter), so a third party cannot inflate another account's queue; a large count is self-inflicted, and reading it fully (rather than truncating) is always the honest answer.


Identity

  • positionId: abi.encode(withdrawalManager) (static; the Kelp LRTWithdrawalManager address)

  • positionKind: Supplied

  • positionInstanceId: bytes32(userNonce) (the per-request absolute nonce; ephemeral — the request is popped off the deque on claim)

  • Labels: the adapter implements positionLabels(positionId), returning ["Withdrawal Queue", "rsETH"] (surfaced only on the verbose read path; full breadcrumb = ["Kelp DAO", "Withdrawal Queue", "rsETH"]). The request nonce is not in the label — it rides in positionInstanceId.

This single positionId is the same across all supported payout assets; the payout asset rides in balanceAsset, so legs sharing the id are distinguished for display by balanceAsset.asset, isLocked (false = claimable now, true = still cooling) and positionInstanceId. The reconciliation key is keccak256(abi.encode(chainId, protocolSubId, positionId, balanceAsset.asset, positionKind)) — it includes balanceAsset.asset but excludes both isLocked and positionInstanceId, so for each payout asset all of that asset's legs (claimable and cooling alike) reconcile into one coordinate and sum; read isLocked per leg for the breakdown.


Constructor

Parameter
Description

withdrawalManager_

Kelp LRTWithdrawalManager contract (the rsETH exit queue). Must be a contract

navCalculator_

NAVCalculator address, used for asset-registry filtering. Must be a contract

The payout asset of each position is determined per-request, not at construction; there is no single underlyingToken_ parameter.


Registration

Registered as a plain adapter with addBalanceAdapters([adapter]) and removed with removeBalanceAdapters([adapter]).

Last updated