Stakers' Emergency Guide
A withdrawal request settles when someone calls finalize on the pool's withdrawal queue, and only the
holder of FINALIZE_ROLE can make that call — the Node Operator, by default. Nothing else in the system
settles a request, and nothing does it on a schedule. If that address stops acting, requests accumulate and
the queue stands still.
This page covers what can be done about it: what a depositor can do alone, and what to ask the Vault Owner or curator for. The ordinary flow is described in Supply and withdraw.
Step 1. Check whether the request is already finalized
Finalization and claiming are separate steps, so a finalized request may just be waiting to be collected. Claiming cannot be paused and keeps working after the stVault is disconnected, so check this first.
using the DeFi Wrapper widget
Connect your wallet and open the pool's status panel. Requests are split in two: Pending withdrawal requests are still waiting on the operator, while Available to claim lists the finalized ones with a Claim button on each.
using Command-line Interface
# status of one request
yarn start dw c wq r w-status <withdrawalQueueAddress> <requestId>
# every request this address owns
yarn start dw c wq r withdrawalRequestsOf <withdrawalQueueAddress> <ownerAddress>
If a request reports as finalized, claim it:
yarn start dw c wq w claim-withdrawal <withdrawalQueueAddress> <requestId> <recipientAddress>
Step 2. Clear what a depositor can clear
None of the calls below finalizes anything. They clear conditions that can block the queue from the outside, and all of them are permissionless — a depositor can make them without holding any role.
The oracle report may be stale. Without a fresh one, new requests and deposits are refused. A request also needs a report that landed after it was created: finalization stops at the first request newer than the latest report. Anyone can apply a report.
Through the DeFi Wrapper widget this is already handled: every deposit, withdrawal, mint and boost prepends the report to the same transaction batch, so the two are signed together. If no report is available to apply and the on-chain one has gone stale, the widget refuses to build the transaction at all rather than let it revert. The command below is for the CLI:
yarn start report w submit -v <vaultAddress>
Unassigned liability freezes the whole pool. While the stVault owes more stETH than the pool has on record, every movement of stv reverts. Finalization burns the stv behind each request it settles, so the whole call reverts with it, and no new request can be filed either. See Unassigned liability.
Anyone can clear it — neither route has a role check, and both are capped at the outstanding amount. They differ in who pays:
# out of the stVault's own ETH: lowers Total Value, so every stv holder pays a share
yarn start dw c stv-steth w rebalance-unassigned-liability <poolAddress> <stethShares>
# out of the caller's ETH
yarn start dw c stv-steth w rebalance-unassigned-liability-with-ether <poolAddress> <ether>
An unhealthy stVault has nothing to pay out with. Finalization draws on the stVault's withdrawable value, and an stVault below its Forced Rebalance Threshold has none — the whole Total Value is locked as collateral, so a run stops on its first request. Restoring the stVault is permissionless, and it is the one force-rebalance that unblocks the queue:
yarn start contracts hub w v-force-rebalance <vaultAddress>
See Rebalance for what this costs the stVault.
Step 3. Ask for what needs a role
Everything past this point needs an address a depositor does not control. Each action below names the role it requires, which is also what identifies who can perform it.
Hand FINALIZE_ROLE to someone else
This is the remedy for an operator who has stopped acting. FINALIZE_ROLE is administered by
DEFAULT_ADMIN_ROLE on the queue, which the Factory grants to the Timelock Controller, so governance can take
the role off the unresponsive holder and give it to another address. Both steps are timelock proposals:
propose, wait out the delay, execute with the same salt.
yarn start dw uc tg wq w propose-grant-role \
<timelockAddress> <withdrawalQueueAddress> FINALIZE_ROLE <newFinalizerAddress> --salt <salt>
yarn start dw uc tg wq w execute-grant-role \
<timelockAddress> <withdrawalQueueAddress> FINALIZE_ROLE <newFinalizerAddress> --salt <salt>
There is no timeout after which anyone may finalize, and no automatic escape hatch. A request does not become
claimable by waiting long enough — it becomes claimable when someone with FINALIZE_ROLE acts, or when
governance moves that role to an address that will. Escalate rather than wait.
Bring ETH back from validators
Finalization pays out of the stVault's balance, so a queue that has outrun its ETH stays stuck whoever holds the role. Validators have to be exited, and the Vault Owner can do that without the Node Operator through the Dashboard, which submits an EIP-7002 withdrawal request directly:
yarn start contracts dashboard w trigger-validator-withdrawal \
<dashboardAddress> <pubkeys> <amounts> <recipientAddress>
The call needs ETH for the protocol's withdrawal fee, which is dynamic. The CLI reads the fee and sends exactly that; when calling the contract directly, send a surplus — the excess is refunded to the recipient.
There are two ways to get the permission, and they suit different situations:
Grant TRIGGER_VALIDATOR_WITHDRAWAL_ROLE to an address. One timelock proposal, after which that address exits validators whenever it needs
to, through the CLI or the Web UI. Worth it when the stVault has many validators or exits will be repeated — the delay is paid once.
Call through the Timelock Controller itself. No grant needed: the timelock holds DEFAULT_ADMIN_ROLE on the Dashboard, and the guard is
onlyRoleMemberOrAdmin, which admits a role's admin. Every exit is then its own proposal and its own delay, and the scheduled operation has to
carry the fee as its value, so the timelock must hold ETH.
See Control validators.