You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**Motivation:**
Burn and redistribution of slashed shares previously settled instantly
once an AVS triggered a clear. This left no window to react before funds
moved, which is risky for both stakers and AVSs. We want a built-in
safety buffer plus a way to halt these flows in an emergency.
**Modifications:**
- Introduced a 7-day (`50,400` blocks) slash resolution delay before
slashed shares can be burned or redistributed.
- Added a dedicated pause flag (`PAUSED_BURN_OR_REDISTRIBUTABLE_SHARES`)
so burn/redistribution can be paused independently of other flows.
- Grandfathered pre-upgrade slashes: existing pending slashes have a
zero resolution block and remain immediately clearable after the
upgrade.
- Added unit, integration, and upgrade tests covering the new behavior.
**Result:**
- Slashed shares can no longer be burned or redistributed until the
7-day delay has elapsed.
- Pauser can pause burn/redistribution specifically without affecting
unrelated functionality.
---------
Co-authored-by: Nadir Akhtar <9601075+nadir-akhtar@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Copy file name to clipboardExpand all lines: docs/core/EigenPodManager.md
+8-8Lines changed: 8 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -134,12 +134,12 @@ The `EigenPodManager` tracks a staker's _deposit shares_ and _beacon chain slash
134
134
```solidity
135
135
/**
136
136
* @notice mapping from pod owner to the deposit shares they have in the virtual beacon chain ETH strategy
137
-
*
137
+
*
138
138
* @dev When an EigenPod registers a balance increase, deposit shares are increased. When registering a balance
139
139
* decrease, however, deposit shares are NOT decreased. Instead, the pod owner's beacon chain slashing factor
140
140
* is decreased proportional to the balance decrease. This impacts the number of shares that will be withdrawn
141
141
* when the deposit shares are queued for withdrawal in the DelegationManager.
142
-
*
142
+
*
143
143
* Note that prior to the slashing release, deposit shares were decreased when balance decreases occurred.
144
144
* In certain cases, a combination of queueing a withdrawal plus registering a balance decrease could result
145
145
* in a staker having negative deposit shares in this mapping. This negative value would be corrected when the
@@ -320,9 +320,9 @@ function recordBeaconChainETHBalanceUpdate(
320
320
nonReentrant
321
321
```
322
322
323
-
This method is called by an `EigenPod` to report a change in its pod owner's shares. It accepts a positive or negative `balanceDeltaWei`. A positive delta is added to the pod owner's _deposit shares,_ and delegated to their operator if applicable. A negative delta is NOT removed from the pod owner's deposit shares. Instead, the proportion of the balance decrease is used to update the pod owner's beacon chain slashing factor and decrease the number of shares delegated to their operator (if applicable). A zero delta results in no change.
323
+
This method is called by an `EigenPod` to report a change in its pod owner's shares. It accepts a positive or negative `balanceDeltaWei`. A positive delta is added to the pod owner's _deposit shares,_ and delegated to their operator if applicable. A negative delta is NOT removed from the pod owner's deposit shares. Instead, the proportion of the balance decrease is used to update the pod owner's beacon chain slashing factor and decrease the number of shares delegated to their operator (if applicable). A zero delta results in no change.
324
324
325
-
**Note** that prior to the slashing release, negative balance deltas subtracted from the pod owner's shares, and could, in certain cases, result in a negative share balance. As of the slashing release, negative balance deltas no longer subtract from share balances, updating the beacon chain slashing factor instead.
325
+
**Note** that prior to the slashing release, negative balance deltas subtracted from the pod owner's shares, and could, in certain cases, result in a negative share balance. As of the slashing release, negative balance deltas no longer subtract from share balances, updating the beacon chain slashing factor instead.
326
326
327
327
If a staker has negative shares as of the slashing release, this method will REVERT, preventing any further balance updates from their pod while the negative share balance persists. In order to fix this and restore the use of their pod, the staker should complete any outstanding withdrawals in the `DelegationManager` "as shares," which will correct the share deficit.
328
328
@@ -365,12 +365,12 @@ function increaseBurnOrRedistributableShares(
365
365
366
366
The `DelegationManager` calls this method when an operator is slashed, calculating the number of slashable shares and marking them for burning here.
367
367
368
-
Unlike in the `StrategyManager`, there is no current mechanism to burn these shares, as burning requires the Pectra hard fork to be able to eject validators. This will be added in a future update.
368
+
Unlike in the `StrategyManager`, there is no current slash-resolution delay, redistribution, or clearing mechanism for these shares. Burning native ETH requires the Pectra hard fork to be able to eject validators.
369
369
370
-
We also do not distinguish burnable shares on a per operatorSet/slashId basis, like the `StrategyManager` does.
370
+
We also do not distinguish burnable ETH shares on a per operatorSet/slashId basis, like the `StrategyManager` does.
371
371
372
372
*Effects*:
373
-
* Increases `burnableShares` for the beacon chain ETH strategy by `addedSharesToBurn`
373
+
* Increases `burnableETHShares` for the beacon chain ETH strategy by `addedSharesToBurn`
Copy file name to clipboardExpand all lines: docs/core/StrategyManager.md
+21-12Lines changed: 21 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -250,11 +250,14 @@ This method directs the `strategy` to convert the input deposit shares to tokens
250
250
251
251
## Increasing/Clearing Slashed Shares
252
252
253
-
Slashes shares are marked as burnable or redistributable. Anybody can call
254
-
`clearBurnOrRedistributableShares` to send tokens to the slash's `redistributionRecipient`. Shares to clear are stored in `_burnOrRedistributableShares`, a nested [EnumerableMap](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.0/contracts/utils/structs/EnumerableMap.sol). The operatorSet and slashId are used to index into the enumerableMap of strategies to shares. The following methods handle clearing burn or redistributable shares:
253
+
Slashed shares are marked as burnable or redistributable. Shares to clear are stored in `_burnOrRedistributableShares`, a nested [EnumerableMap](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.0/contracts/utils/structs/EnumerableMap.sol). The `operatorSet` and `slashId` are used to index into the enumerableMap of strategies to shares.
254
+
255
+
For slashes recorded after the slash resolution delay was introduced, clearing is delayed until after `SLASH_RESOLUTION_DELAY_BLOCKS` have elapsed. The resolution block for a slash can be queried with `getSlashResolutionBlock(operatorSet, slashId)`, and clearing reverts with `SlashResolutionDelayNotElapsed` until `block.number` is greater than that value. Slashes recorded before this delay was introduced have a resolution block of `0` and remain immediately clearable as grandfathered state.
256
+
257
+
Anybody can call `clearBurnOrRedistributableShares` or `clearBurnOrRedistributableSharesByStrategy` after the delay to send tokens to the slash's `redistributionRecipient`. Clearing can be paused with `PAUSED_BURNING_AND_REDISTRIBUTION`. The following methods handle clearing burn or redistributable shares:
*[`StrategyManager.burnShares`](#burnshares) - Legacy burnShares function
259
262
260
263
#### `increaseBurnOrRedistributableShares`
@@ -279,10 +282,11 @@ function increaseBurnOrRedistributableShares(
279
282
280
283
The `DelegationManager` calls this method when an operator is slashed, calculating the number of slashable shares and marking them for burn or redistribution here.
281
284
282
-
Anyone can then convert the shares to tokens and trigger a burn via `burnShares`. This asynchronous method was added to mitigate potential DoS vectors when slashing.
285
+
The first strategy recorded for an `operatorSet` and `slashId` also sets the slash's resolution block. Additional strategies added for the same slash do not reset the delay. This asynchronous clearing flow was added to mitigate potential DoS vectors when slashing.
283
286
284
287
*Effects*:
285
288
* Sets `burnOrRedistributableShares` for the given `operatorSet`, `slashId`, and `strategy`
289
+
* Sets the slash resolution block for the given `operatorSet` and `slashId` if this is the first strategy recorded for that slash
286
290
* Emits a `BurnOrRedistributableSharesIncreased` event
287
291
288
292
@@ -296,22 +300,26 @@ Anyone can then convert the shares to tokens and trigger a burn via `burnShares`
296
300
297
301
```solidity
298
302
/**
299
-
* @notice Removes burned shares from storage and transfers the underlying tokens for the slashId to the slash escrow.
303
+
* @notice Removes burned shares from storage and transfers the underlying tokens for the slashId to the redistribution recipient.
304
+
* @dev Reverts if SLASH_RESOLUTION_DELAY_BLOCKS has not elapsed since the slash was recorded.
305
+
* @dev Reverts if clearing is paused via PAUSED_BURNING_AND_REDISTRIBUTION.
300
306
* @param operatorSet The operator set to burn shares in.
301
307
* @param slashId The slash ID to burn shares in.
302
-
* @return The amounts of tokens transferred to the slash escrow for each strategy
308
+
* @return The amounts of tokens transferred to the redistribution recipient for each strategy
303
309
*/
304
310
function clearBurnOrRedistributableShares(
305
311
OperatorSet calldata operatorSet,
306
312
uint256 slashId)
307
313
external returns (uint256[] memory);
308
314
309
315
/**
310
-
* @notice Removes a single strategy's shares from storage and transfers the underlying tokens for the slashId to the slash escrow.
316
+
* @notice Removes a single strategy's shares from storage and transfers the underlying tokens for the slashId to the redistribution recipient.
317
+
* @dev Reverts if SLASH_RESOLUTION_DELAY_BLOCKS has not elapsed since the slash was recorded.
318
+
* @dev Reverts if clearing is paused via PAUSED_BURNING_AND_REDISTRIBUTION.
311
319
* @param operatorSet The operator set to burn shares in.
312
320
* @param slashId The slash ID to burn shares in.
313
321
* @param strategy The strategy to burn shares in.
314
-
* @return The amount of shares that were burned.
322
+
* @return The amount of tokens transferred to the redistribution recipient.
315
323
*/
316
324
function clearBurnOrRedistributableSharesByStrategy(
317
325
OperatorSet calldata operatorSet,
@@ -320,13 +328,14 @@ function clearBurnOrRedistributableSharesByStrategy(
320
328
) external returns (uint256);
321
329
```
322
330
323
-
Anyone can call this method to transfer slashed shares to the operator sets's `redistributionRecipient`. This method sets the `burnOrRedistributableShares` for the given `slashId` and `operatorSet` to 0. To accommodate the unlimited number of strategies that can be added to an operatorSet, users can also pass in a strategy to clear via `clearBurnOrRedistributableSharesByStrategy`. The strategies that haven not been cleared can be retrieved by calling `getBurnOrRedistributableShares(operatorSet, slashId)`.
331
+
Anyone can call these methods after the slash resolution delay to transfer slashed shares to the operator set's `redistributionRecipient`. `clearBurnOrRedistributableShares` clears every strategy recorded for the `operatorSet` and `slashId`. To accommodate the unlimited number of strategies that can be added to an operator set, callers can also clear one strategy at a time with `clearBurnOrRedistributableSharesByStrategy`. The strategies that have not been cleared can be retrieved by calling `getBurnOrRedistributableShares(operatorSet, slashId)`.
324
332
325
333
*Effects*:
326
-
*Resets the strategy's burn or redistributable shares for the operatorSet and slashId to 0
334
+
*Removes the strategy's burn or redistributable shares for the `operatorSet` and `slashId`
327
335
* If the shares to remove are nonzero:
328
-
* Calls `withdraw` on the `strategy`, withdrawing shares and sending a corresponding amount of tokens to the operator sets's `redistributionRecipient`
336
+
* Calls `withdraw` on the `strategy`, withdrawing shares and sending a corresponding amount of tokens to the operator set's `redistributionRecipient`
329
337
* Emits a `BurnOrRedistributableSharesDecreased`
338
+
* Deletes the slash resolution block once all strategies for the slash have been cleared
0 commit comments