Skip to content

feat: clear queued beacon-chain-ETH withdrawals on pod disable#2

Open
MC1823315 wants to merge 3 commits into
feat/eigenpod-disablefrom
feat/eigenpod-disable-cleanup
Open

feat: clear queued beacon-chain-ETH withdrawals on pod disable#2
MC1823315 wants to merge 3 commits into
feat/eigenpod-disablefrom
feat/eigenpod-disable-cleanup

Conversation

@MC1823315

Copy link
Copy Markdown
Owner

Motivation:

Builds on the inert-withdrawal approach. With share re-crediting blocked and REL zeroed on disable, a pod owner's queued beacon-chain-ETH withdrawals become permanently uncompletable but linger in the DelegationManager queue forever, polluting queue views and leaving a standing "pending but uncompletable" invariant violation.

Modifications:

  • Added DelegationManager.clearQueuedWithdrawalsForDisabledPod (onlyEigenPodManager), which removes the staker's queued beacon-chain-ETH withdrawal entries without re-crediting shares or changing delegation (both were already decremented at queue time). Pure-LST withdrawals are left untouched.
  • Added an EigenPodManager passthrough (onlyEigenPod) and wired the call into EigenPod.permanentlyDisableRestaking, after zeroing REL.
  • Added QueuedWithdrawalClearedForDisabledPod event and interface entries.
  • Updated mocks, docs, and added unit + integration tests.

Result:

Disabling a pod cleans up its dead beacon-chain-ETH queue entries instead of leaving them inert. Value is still recovered via withdrawNonRestakedBalance. Compare against the inert-withdrawal PR to weigh the added cross-contract surface against cleaner queue state.

🤖 Generated with Claude Code

Motivation:
With share re-crediting blocked and REL zeroed on disable, a pod owner's queued
beacon-chain-ETH withdrawals become permanently uncompletable but linger in the
DelegationManager queue forever, polluting queue views and leaving a standing
"pending but uncompletable" invariant violation.

Modifications:
- Added DelegationManager.clearQueuedWithdrawalsForDisabledPod (onlyEigenPodManager),
  which removes the staker's queued beacon-chain-ETH withdrawal entries without
  re-crediting shares or changing delegation (both were already decremented at
  queue time). Pure-LST withdrawals are left untouched.
- Added an EigenPodManager passthrough (onlyEigenPod) and wired the call into
  EigenPod.permanentlyDisableRestaking, after zeroing REL.
- Added QueuedWithdrawalClearedForDisabledPod event and interface entries.
- Updated mocks, docs, and added unit + integration tests. The disable
  integration test now asserts the withdrawal is removed from the queue.

Result:
Disabling a pod cleans up its dead beacon-chain-ETH queue entries instead of
leaving them inert. Value is still recovered via withdrawNonRestakedBalance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MC1823315 MC1823315 force-pushed the feat/eigenpod-disable-cleanup branch from 4a291cb to 893ef3d Compare June 18, 2026 18:02
MC1823315 and others added 2 commits June 25, 2026 13:22
Motivation:
clearQueuedWithdrawalsForDisabledPod performs an irreversible delete of a
staker's beacon-chain-ETH queue entries. The non-beacon-chain leg of a mixed
withdrawal is unrecoverable once cleared (its deposit shares were already
decremented at queue time, and the pod's non-restaked sweep only recovers ETH).
The safety check that prevents this lived solely in EigenPod's disable
preconditions (MixedWithdrawalPending), one contract away from the destructive
operation.

Modifications:
- Added a defense-in-depth require(!hasOther, MixedWithdrawalNotClearable()) in
  DelegationManager.clearQueuedWithdrawalsForDisabledPod, before the delete, so
  the value-destroying operation is self-guarding rather than dependent on a
  caller invariant.
- Added the MixedWithdrawalNotClearable error to IDelegationManager.
- Added test_Revert_mixedWithdrawal unit test asserting a mixed bc-ETH + LST
  withdrawal reverts instead of being cleared.

Result:
If a mixed withdrawal ever reaches this function through a future caller or an
upstream regression, it reverts instead of silently destroying unrecoverable
value. Reachable behavior is unchanged today.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion

Motivation:
A review of the disabling-restaking documentation surfaced terminology, accuracy,
and completeness gaps between the docs/comments and the implementation.

Modifications:
- Renamed "cross-pod consolidation" to "external consolidation" across EigenPod.sol,
  IEigenPod.sol, EigenPod.md, and tests (including the integration test name), since
  the target may be any validator, not only one in another pod.
- Completed the permanentlyDisableRestaking precondition narrative (beacon-chain
  slashing and per-withdrawal AVS-slashing checks) and synced the Effects/Requirements
  lists to the implementation (REL zeroing, queue clearing, MixedWithdrawalPending,
  the delegatedTo == address(0) skip). Trimmed redundant rationale prose.
- Fixed EIP-7521 -> EIP-7251 typo.
- Clarified withdrawNonRestakedBalance: restakedExecutionLayerGwei is always 0 once
  disabled, so the method sweeps the full pod balance; the subtraction is retained only
  to mirror the general free-balance formula. Updated the code comment to match.

Result:
The disabling-restaking documentation and code comments accurately and consistently
describe the implemented behavior. No production logic changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant