|
| 1 | +# Feature Flags in StrategyManager |
| 2 | + |
| 3 | +## Purpose |
| 4 | + |
| 5 | +Introduce a compact, on‑chain mechanism to enable or disable selected features of the `StrategyManager` contract without a full redeploy. By packing multiple boolean toggles into a single `uint32`, the design: |
| 6 | + |
| 7 | +- Minimizes storage footprint and gas cost. |
| 8 | +- Provides an upgrade‑friendly switchboard for safety (e.g., emergency pause). |
| 9 | +- Centralizes feature management under DAO control. |
| 10 | + |
| 11 | +## Technical Explanation |
| 12 | + |
| 13 | +- **Storage**: A `uint32 disabledFeatures` field in `ProtocolStorageLib.Data`. |
| 14 | +- **Bitmask Layout**: |
| 15 | + - Bit 0 (LSB) → Slashing Disabled (`SLASHING_DISABLED = 1 << 0`) |
| 16 | + - Bit 1 → Withdrawals Disabled (`WITHDRAWALS_DISABLED = 1 << 1`) |
| 17 | + - Further bits reserved for future toggles. |
| 18 | + |
| 19 | +- **Checks**: Two internal functions in `StrategyManager`: |
| 20 | + ```solidity |
| 21 | + function _checkSlashingAllowed() internal view { |
| 22 | + if (ProtocolStorageLib.load().disabledFeatures & SLASHING_DISABLED != 0) |
| 23 | + revert SlashingDisabled(); |
| 24 | + } |
| 25 | +
|
| 26 | + function _checkWithdrawalsAllowed() internal view { |
| 27 | + if (ProtocolStorageLib.load().disabledFeatures & WITHDRAWALS_DISABLED != 0) |
| 28 | + revert WithdrawalsDisabled(); |
| 29 | + } |
| 30 | + ``` |
| 31 | + - Called at the entry points of: |
| 32 | + - `slash(...)` |
| 33 | + - `proposeWithdrawal(...)` |
| 34 | + - `finalizeWithdrawal(...)` |
| 35 | + - `proposeWithdrawalETH(...)` |
| 36 | + - `finalizeWithdrawalETH(...)` |
| 37 | + |
| 38 | +## Authorized Accounts |
| 39 | + |
| 40 | +- Only the **DAO (owner)** can update the entire `disabledFeatures` bitmask via: |
| 41 | + ```solidity |
| 42 | + function updateDisabledFeatures(uint32 disabledFeatures) external onlyOwner; |
| 43 | + ``` |
| 44 | +- No per-feature granularity: toggles are applied in bulk. |
| 45 | + |
| 46 | +## Current Features That Can Be Enabled/Disabled |
| 47 | + |
| 48 | +| Bit Position | Feature | Constant | Description | |
| 49 | +|:------------:|:-------------------|:----------------------|:------------------------------------------| |
| 50 | +| 0 | Slashing | `SLASHING_DISABLED` | Stops all calls to `slash(...)` | |
| 51 | +| 1 | Withdrawals | `WITHDRAWALS_DISABLED`| Stops all withdrawal proposals and finalizations | |
| 52 | + |
| 53 | +## Usage & Examples |
| 54 | + |
| 55 | +1. **Disable slashing only**: |
| 56 | + ```js |
| 57 | + // binary: 0b...01 → 1 |
| 58 | + SSVBasedApps.updateDisabledFeatures(1); |
| 59 | + // `slash(...)` now reverts with `SlashingDisabled()`. |
| 60 | + ``` |
| 61 | +2. **Re-enable slashing, disable withdrawals**: |
| 62 | + ```js |
| 63 | + // binary: 0b...10 → 2 |
| 64 | + SSVBasedApps.updateDisabledFeatures(2); |
| 65 | + // `slash(...)` resumes; `proposeWithdrawal(...)` and `finalizeWithdrawal(...)` revert. |
| 66 | + ``` |
| 67 | +3. **Disable both**: |
| 68 | + ```js |
| 69 | + SSVBasedApps.updateDisabledFeatures(3); |
| 70 | + ``` |
| 71 | +4. **Full example:** |
| 72 | + ```js |
| 73 | + // bit-definitions |
| 74 | + const SLASHING_DISABLED = 1 << 0; // 0b0001 |
| 75 | + const WITHDRAWALS_DISABLED = 1 << 1; // 0b0010 |
| 76 | + |
| 77 | + // Suppose you want to disable only withdrawals: |
| 78 | + let flags = 0; |
| 79 | + flags |= WITHDRAWALS_DISABLED; // flags === 0b0010 |
| 80 | + |
| 81 | + // Later you decide to also disable slashing: |
| 82 | + flags |= SLASHING_DISABLED; // flags === 0b0011 |
| 83 | + |
| 84 | + // To re-enable withdrawals but keep slashing disabled: |
| 85 | + flags &= ~WITHDRAWALS_DISABLED; // flags === 0b0001 |
| 86 | + |
| 87 | + // Finally, send the update on-chain: |
| 88 | + await SSVBasedApps.updateDisabledFeatures(flags); |
| 89 | + ``` |
| 90 | + |
| 91 | +## Future Extensions |
| 92 | + |
| 93 | +- Reserve bits 2–31 for other purposes: |
| 94 | + - Feature disabling |
| 95 | + - Emergency pause (global) |
| 96 | + |
| 97 | + |
| 98 | +--- |
| 99 | +*This document outlines the bitmask‑driven feature gating mechanism for `StrategyManager`. It ensures rapid reaction to on‑chain emergencies and fine‑grained control over critical operations.* |
| 100 | + |
0 commit comments