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().
Type: Plain adapter
protocolSubId:
keccak256("kelp-withdrawal")
Positions returned
One Supplied leg per open request, per Kelp-supported payout asset that is registered in NAVCalculator:
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
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 KelpLRTWithdrawalManageraddress)positionKind:
SuppliedpositionInstanceId:
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 inpositionInstanceId.
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
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