> For the complete documentation index, see [llms.txt](https://docs.kpk.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kpk.io/funds/infrastructure/onchain-accounting/balance-adapters/kelp-withdrawal-queue.md).

# 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`). 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()`.

* **Type:** Plain adapter
* **protocolSubId:** `keccak256("kelp-withdrawal")`
* **Source:** [`KelpWithdrawalQueueBalanceAdapter.sol`](https://github.com/karpatkey/onchain-accounting/blob/main/src/balances/BalanceAdapters/KelpWithdrawalQueueBalanceAdapter.sol)

***

## 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

<figure><img src="/files/bEMqie4gMNdfAtwiKzBg" alt="Flowchart of the Kelp Withdrawal Queue balance adapter: read calls derive position legs into PositionBalance entries."><figcaption><p>Kelp Withdrawal Queue adapter — how the underlying balances reported to the NAV Calculator are derived.</p></figcaption></figure>

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:

```
(begin, end) = userAssociatedNonces(asset, account)
count = end - begin
for i in 0 .. count:
    (_, expectedAssetAmount, withdrawalStartBlock, userNonce) = getUserWithdrawalRequest(asset, account, i)
    unlocked = userNonce < nextLockedNonce(asset)
               and block.number >= withdrawalStartBlock + withdrawalDelayBlocks()
    emit leg { amount = expectedAssetAmount, isLocked = !unlocked, positionInstanceId = bytes32(userNonce) }
```

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

```solidity
constructor(address withdrawalManager_, address navCalculator_)
```

| 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])`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kpk.io/funds/infrastructure/onchain-accounting/balance-adapters/kelp-withdrawal-queue.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
