Skip to content

feat(release): redistribution #1355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7920d5c
feat(draft): `AllocationManager` redistribution support (#1346)
0xClandestine May 6, 2025
ef62cbd
feat: simplify removeDepositShares in StrategyManager (#1373)
bowenli86 May 19, 2025
dc8c855
feat: add `SlashingWithdrawalRouter` (#1358)
0xClandestine May 21, 2025
098e8ca
test(redistribution): add unit tests (#1383)
0xClandestine May 22, 2025
27f0961
refactor: remove `v` prefix from `SemVerMixin` (#1385)
0xClandestine May 22, 2025
318b657
feat: escrow funds in unique clone contracts (#1387)
0xClandestine May 23, 2025
126044d
fix: review issues (#1391)
0xClandestine May 23, 2025
407a02b
fix: storage checker (#1394)
0xClandestine May 24, 2025
96e5eda
chore: use internal getters; update `isOperatorRedistributable` (#1401)
ypatil12 May 24, 2025
43072c2
chore: rename burnable -> burnOrRedistributable; fix storage gap; rem…
ypatil12 May 24, 2025
c79ae54
chore: remove dm/alm code size optimizations (#1398)
ypatil12 May 24, 2025
9eac860
test: full coverage `SlashEscrowFactory` + `SlashEscrow` (#1403)
0xClandestine May 24, 2025
452e3ab
chore: decrease dm diff further (#1404)
ypatil12 May 26, 2025
72723ec
fix: enumerable map overwrite (#1399)
ypatil12 May 26, 2025
5b383c7
feat: simplify escrow delay; add convenience functions (#1406)
ypatil12 May 27, 2025
44bd8d2
chore: update naming (#1408)
ypatil12 May 27, 2025
840dfd8
feat: deploy escrow in `initiateSlashEscrow` (#1413)
ypatil12 May 27, 2025
a384a97
refactor: `decreaseBurnOrRedistributableShares` (#1414)
0xClandestine May 27, 2025
76043fa
refactor: review changes (#1411)
0xClandestine May 27, 2025
5530ead
feat: release escrow by strategy (#1412)
ypatil12 May 28, 2025
9664fa5
perf: avoid binary search (#1417)
0xClandestine May 28, 2025
b081d12
chore: style updates (#1416)
ypatil12 May 28, 2025
cfdfc3a
chore: address redistribution nits (#1420)
ypatil12 May 29, 2025
e5431a9
docs: redistribution (#1409)
ypatil12 May 29, 2025
d8747c6
test: redistribution integration (#1415)
0xClandestine May 30, 2025
610c751
test: redistribution upgrade (#1410)
ypatil12 May 30, 2025
0c1af9a
chore: bindings (#1422)
ypatil12 May 30, 2025
b0cbf0b
feat: redistribution upgrade script (#1396)
ypatil12 May 30, 2025
da4de65
docs: redistribution changelog (#1418)
ypatil12 May 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/configs/storage-diff.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"contracts": [
{
"name": "AllocationManager",
"address": "0x948a420b8CC1d6BFd0B6087C2E7c344a2CD0bc39"
},
{
"name": "AVSDirectory",
"address": "0x135dda560e946695d6f155dacafc6f1f25c1f5af"
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ jobs:
- name: Run ${{ matrix.suite }} tests
run: |
case "${{ matrix.suite }}" in
Unit) forge test --no-match-contract Integration ;;
Integration) forge test --match-contract Integration ;;
Fork) forge test --match-contract Integration ;;
Unit) forge test --no-match-contract Integration -vvv ;;
Integration) forge test --match-contract Integration -vvv ;;
Fork) forge test --match-contract Integration -vvv ;;
esac
env:
FOUNDRY_PROFILE: ${{ matrix.suite == 'Fork' && 'forktest' || 'medium' }}
Expand Down
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
[submodule "lib/ds-test"]
path = lib/ds-test
url = https://github.com/dapphub/ds-test
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts-v4.9.0"]
path = lib/openzeppelin-contracts-v4.9.0
url = https://github.com/OpenZeppelin/openzeppelin-contracts
Expand All @@ -13,3 +10,6 @@
[submodule "lib/zeus-templates"]
path = lib/zeus-templates
url = https://github.com/Layr-Labs/zeus-templates
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
97 changes: 69 additions & 28 deletions CHANGELOG/CHANGELOG-1.5.0.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,85 @@
# v1.5.0 Redistribution

**Use this template to draft changelog and submit PR to review by the team**

## Release Manager

@0xClandestine

@0xClandestine @ypatil12

## Highlights

🚀 New Features – Highlight major new functionality
- ...
- ...
🚀 New features

⛔ Breaking Changes – Call out backward-incompatible changes.
- ...
- ...
- Redistribution is a feature that gives Service Builders a means to not just burn, but repurpose slashed funds.
- We introduce a new operatorSet creation mechanism: [`AllocationManager.createRedistributingOperatorSets`](../docs/core/AllocationManager.md#createredistributingoperatorsets), which allows slashed funds to be redistributed to a `RedistributionRecipient`. *Note: The redistribution recipient can be set only once and is immutable*.
- *All slashed funds will now be routed to individual `SlashEscrow` contracts.* The release of funds from escrow is gated by the `SlashEscrowFactory`. The `SlashEscrowFactory` deploys individual `SlashEscrow` contracts per slash, enforces a global delay for all escrowed funds, and handles pausing/unpausing of escrowed funds.
- The original `createOperatorSets` function still exists. This function creates operatorSets whose slashed funds will eventually be burned. There is no mechanism to convert an operatorSet to be redistributing.
- See [ELIP-006](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-006.md) for a full description.

📌 Deprecations – Mention features that are being phased out.
- ...
- ...
⛔ Breaking changes
- Funds marked for burning now go through a 4-day escrow period via `SlashEscrow` contracts. These funds are burned by calling [`SlashEscrowFactory.releaseSlashEscrow`](../docs/core/SlashEscrowFactory.md#releaseslashescrow).

🛠️ Security Fixes – Specify patched vulnerabilities.
- ...
- ...
📌 Future Deprecations
- The pre-redistribution burn pathway [`StrategyManager.decreaseBurnableShares`](../docs/core/StrategyManager.md#burnshares) will be deprecated in an upgrade *after* the redistribution release. This function can still be used to burn shares that have been slashed at any point prior to the redistribution upgrade.

🔧 Improvements – Enhancements to existing features.
- ...
- ...
🛠️ Security Updates
- The slashing of burned funds is no longer instantaneous. All slashed funds (burned or redistributed) now go through a 4-day escrow delay. The eventual burning or redistribution of slashed funds can be paused by the `PauserMultisig`.
- The upgradability of the `SlashEscrowFactory` is controlled by the `CommunityMultisig`. The contract will have a separate `ProxyAdmin` from the rest of the EigenLayer core protocol. Each individual `SlashEscrow` contract is an immutable clone.

🐛 Bug Fixes – List resolved issues.
- ...
- ...
🔧 Improvements
- The [`AllocationManager.slashOperator`](../docs/core/AllocationManager.md#slashoperator) function now returns a `slashId` and array of `shares` to be burned/redistributed. **The function selector remains the same.**
- OperatorSets now have a `slashCount` field, which returns the number of slashes completed by the operatorSet. This value only reflects the number of slashes after the redistribution upgrade.
- `StrategyBase` returns an `amountOut` upon withdrawal to comply with standard ERC-4626 vaults.
- The `AllocationManager` and `DelegationManager` no longer use ownable. Thus, they now inherit the `Deprecated_OwnableUpgradeable` mixin in its place to reduce codesize.

🐛 Bug Fixes
- `SemVerMixin` is updated to only return the first character of `majorVersion`. We currently return `1.` and will return `1` after this upgrade.

## Changelog

Copy the one that's auto generated from github by default to here, and submit PR for review

## Changelog

- merged PRs in diff from last release
- contributors
- etc
- feat(draft): `AllocationManager` redistribution support [PR #1346](https://github.com/layr-labs/eigenlayer-contracts/pull/1346)
- feat: redistribution upgrade script [PR #1396](https://github.com/layr-labs/eigenlayer-contracts/pull/1396)
- chore: bindings [PR #1422](https://github.com/layr-labs/eigenlayer-contracts/pull/1422)
- test: redistribution upgrade [PR #1410](https://github.com/layr-labs/eigenlayer-contracts/pull/1410)
- test: redistribution integration [PR #1415](https://github.com/layr-labs/eigenlayer-contracts/pull/1415)
- docs: redistribution [PR #1409](https://github.com/layr-labs/eigenlayer-contracts/pull/1409)
- chore: address redistribution nits [PR #1420](https://github.com/layr-labs/eigenlayer-contracts/pull/1420)
- chore: style updates [PR #1416](https://github.com/layr-labs/eigenlayer-contracts/pull/1416)
- perf: avoid binary search [PR #1417](https://github.com/layr-labs/eigenlayer-contracts/pull/1417)
- feat: release escrow by strategy [PR #1412](https://github.com/layr-labs/eigenlayer-contracts/pull/1412)
- refactor: review changes [PR #1411](https://github.com/layr-labs/eigenlayer-contracts/pull/1411)
- refactor: `decreaseBurnOrRedistributableShares` [PR #1414](https://github.com/layr-labs/eigenlayer-contracts/pull/1414)
- feat: deploy escrow in `initiateSlashEscrow` [PR #1413](https://github.com/layr-labs/eigenlayer-contracts/pull/1413)
- chore: update naming [PR #1408](https://github.com/layr-labs/eigenlayer-contracts/pull/1408)
- feat: simplify escrow delay; add convenience functions [PR #1406](https://github.com/layr-labs/eigenlayer-contracts/pull/1406)
- fix: enumerable map overwrite [PR #1399](https://github.com/layr-labs/eigenlayer-contracts/pull/1399)
- chore: decrease dm diff further [PR #1404](https://github.com/layr-labs/eigenlayer-contracts/pull/1404)
- test: full coverage `SlashEscrowFactory` + `SlashEscrow` [PR #1403](https://github.com/layr-labs/eigenlayer-contracts/pull/1403)
- chore: remove dm/alm code size optimizations [PR #1398](https://github.com/layr-labs/eigenlayer-contracts/pull/1398)
- chore: rename burnable -> burnOrRedistributable; fix storage gap; remove poc code [PR #1397](https://github.com/layr-labs/eigenlayer-contracts/pull/1397)
- chore: use internal getters; update `isOperatorRedistributable` [PR #1401](https://github.com/layr-labs/eigenlayer-contracts/pull/1401)
- fix: storage checker [PR #1394](https://github.com/layr-labs/eigenlayer-contracts/pull/1394)
- fix: review issues [PR #1391](https://github.com/layr-labs/eigenlayer-contracts/pull/1391)
- feat: escrow funds in unique clone contracts [PR #1387](https://github.com/layr-labs/eigenlayer-contracts/pull/1387)
- refactor: remove `v` prefix from `SemVerMixin` [PR #1385](https://github.com/layr-labs/eigenlayer-contracts/pull/1385)
- test(redistribution): add unit tests [PR #1383](https://github.com/layr-labs/eigenlayer-contracts/pull/1383)
- feat: add `SlashingWithdrawalRouter` [PR #1358](https://github.com/layr-labs/eigenlayer-contracts/pull/1358)
- feat: simplify removeDepositShares in StrategyManager [PR #1373](https://github.com/layr-labs/eigenlayer-contracts/pull/1373)
- feat(draft): `AllocationManager` redistribution support [PR #1346](https://github.com/layr-labs/eigenlayer-contracts/pull/1346)
- ci: add explicit permissions to workflows to mitigate security concerns [PR #1392](https://github.com/layr-labs/eigenlayer-contracts/pull/1392)
- ci: remove branch constraint for foundry coverage job
- docs: add release managers to changelogs
- docs: add templates for changelog and release notes [PR #1382](https://github.com/layr-labs/eigenlayer-contracts/pull/1382)
- docs: add doc for steps to write deploy scripts [PR #1380](https://github.com/layr-labs/eigenlayer-contracts/pull/1380)
- ci: add testnet envs sepolia and hoodi to validate-deployment-scripts [PR #1378](https://github.com/layr-labs/eigenlayer-contracts/pull/1378)
- docs: update MAINTENANCE to include practices of merging multiple release-dev branches
- docs: updating readme for dead links, readability, new language, and more [PR #1377](https://github.com/layr-labs/eigenlayer-contracts/pull/1377)
- docs: bump deployment matrix to top of README [PR #1376](https://github.com/layr-labs/eigenlayer-contracts/pull/1376)
- ci: add CI to auto validate deployment scripts [PR #1360](https://github.com/layr-labs/eigenlayer-contracts/pull/1360)
- chore: update readme for v1.4.1 [PR #1361](https://github.com/layr-labs/eigenlayer-contracts/pull/1361)
- ci: add cron to auto remove stale branches [PR #1348](https://github.com/layr-labs/eigenlayer-contracts/pull/1348)
- chore: Update README for Holesky v1.4.2 release [PR #1351](https://github.com/layr-labs/eigenlayer-contracts/pull/1351)
- docs: remove fork-pr instructions from CONTRIBUTING.md and MAINTENANCE.md
- ci: disable delete unauthorized branches
- docs: update addresses for mainnet [PR #1341](https://github.com/layr-labs/eigenlayer-contracts/pull/1341)
- docs: enrich MAINTENANCE.md re: release branches [PR #1340](https://github.com/layr-labs/eigenlayer-contracts/pull/1340)
- ci: enable auto delete branch upon eigengit launch [PR #1339](https://github.com/layr-labs/eigenlayer-contracts/pull/1339)
47 changes: 44 additions & 3 deletions docs/core/AllocationManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Libraries and Mixins:
| File | Notes |
| -------- | -------- |
| [`PermissionControllerMixin.sol`](../../src/contracts/mixins/PermissionControllerMixin.sol) | account delegation |
| [`Deprecated_OwnableUpgradeable`](../../src/contracts/mixins/Deprecated_OwnableUpgradeable.sol) | deprecated ownable logic |
| [`Pausable.sol`](../../src/contracts/permissions/Pausable.sol) | |
| [`SlashingLib.sol`](../../src/contracts/libraries/SlashingLib.sol) | slashing math |
| [`OperatorSetLib.sol`](../../src/contracts/libraries/OperatorSetLib.sol) | encode/decode operator sets |
Expand Down Expand Up @@ -170,13 +171,14 @@ mapping(address avs => EnumerableSet.UintSet) internal _operatorSets;
mapping(bytes32 operatorSetKey => EnumerableSet.AddressSet) internal _operatorSetMembers;
```

Every `OperatorSet` corresponds to a single AVS, as indicated by the `avs` parameter. On creation, the AVS provides an `id` (unique to that AVS), as well as a list of `strategies` the `OperatorSet` includes. Together, the `avs` and `id` form the `key` that uniquely identifies a given `OperatorSet`. Operators can register to and deregister from operator sets. In combination with allocating slashable magnitude, operator set registration forms the basis of operator slashability (discussed further in [Allocations and Slashing](#allocations-and-slashing)).
Every `OperatorSet` corresponds to a single AVS, as indicated by the `avs` parameter. On creation, the AVS provides an `id` (unique to that AVS), as well as a list of `strategies` the `OperatorSet` includes. Together, the `avs` and `id` form the `key` that uniquely identifies a given `OperatorSet`. Operators can register to and deregister from operator sets. In combination with allocating slashable magnitude, operator set registration forms the basis of operator slashability (discussed further in [Allocations and Slashing](#allocations-and-slashing)). There are two types of operatorSets, redistributing and non-redistributing.

**Concepts:**
* [Registration Status](#registration-status)

**Methods:**
* [`createOperatorSets`](#createoperatorsets)
* [`createRedistributingOperatorSets`](#createredistributingoperatorsets)
* [`addStrategiesToOperatorSet`](#addstrategiestooperatorset)
* [`removeStrategiesFromOperatorSet`](#removestrategiesfromoperatorset)
* [`registerForOperatorSets`](#registerforoperatorsets)
Expand Down Expand Up @@ -237,7 +239,7 @@ function createOperatorSets(

_Note: this method can be called directly by an AVS, or by a caller authorized by the AVS. See [`PermissionController.md`](../permissions/PermissionController.md) for details._

AVSs use this method to create new operator sets. An AVS can create as many operator sets as they desire, depending on their needs. Once created, operators can [allocate slashable stake to](#modifyallocations) and [register for](#registerforoperatorsets) these operator sets.
AVSs use this method to create new operator sets. An AVS can create as many operator sets as they desire, depending on their needs. Once created, operators can [allocate slashable stake to](#modifyallocations) and [register for](#registerforoperatorsets) these operator sets. The `redistributionRecipient` is the `DEFAULT_BURN_ADDRESS`, where slashed funds are sent.

On creation, the `avs` specifies an `operatorSetId` unique to the AVS. Together, the `avs` address and `operatorSetId` create a `key` that uniquely identifies this operator set throughout the `AllocationManager`.

Expand All @@ -254,6 +256,38 @@ Optionally, the `avs` can provide a list of `strategies`, specifying which strat
* AVS MUST have registered metadata via calling `updateAVSMetadataURI`
* For each `CreateSetParams` element:
* Each `params.operatorSetId` MUST NOT already exist in `_operatorSets[avs]`

#### `createRedistributingOperatorSets`

```solidity
/**
* @notice Allows an AVS to create new redistributing operator sets, defining strategies and the redistribution recipient the operator set uses
*/
function createRedistributingOperatorSets(
address avs,
CreateSetParams[] calldata params,
address[] calldata redistributionRecipients
)
external
checkCanCall(avs)
```

AVSs use this method to create new redistributing operatorSets. Unlike the previous function, slashed funds for this operatorSet are sent to a `redistributionRecipient`. This value is set only once, upon creation. Note that redistributing operatorSets may not have Native ETH, as the protocol does not support native eth redistribution. See [ELIP-006](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-006.md) for additional context.

*Effects*:
* For each `CreateSetParams` element:
* For each `params.strategies` element:
* Add `strategy` to `_operatorSetStrategies[operatorSetKey]`
* Emits `StrategyAddedToOperatorSet` event
* Sets the `redistributionRecipient` of the operatorSet
* Emits the `RedistributionAddressSet`

*Requirements*:
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
* AVS MUST have registered metadata via calling `updateAVSMetadataURI`
* The `redistributionRecipient` MUST NOT be the 0 address
* For each `CreateSetParams` element:
* Each `params.operatorSetId` MUST NOT already exist in `_operatorSets[avs]`

#### `addStrategiesToOperatorSet`

Expand Down Expand Up @@ -287,6 +321,7 @@ This function allows an AVS to add slashable strategies to a given operator set.
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
* The operator set MUST be registered for the AVS
* Each proposed strategy MUST NOT be registered for the operator set
* If the operatorSet is redistributing, the `BEACONCHAIN_ETH_STRAT` may not be added, since redistribution is not supported for native eth

#### `removeStrategiesFromOperatorSet`

Expand Down Expand Up @@ -717,6 +752,9 @@ struct SlashingParams {
* - wadsToSlash: Array of proportions to slash from each strategy (must be between 0 and 1e18).
* - description: Description of why the operator was slashed.
*
* @return slashId The ID of the slash.
* @return shares The amount of shares that were slashed for each strategy.
*
* @dev For each strategy:
* 1. Reduces the operator's current allocation magnitude by wadToSlash proportion.
* 2. Reduces the strategy's max and encumbered magnitudes proportionally.
Expand All @@ -734,6 +772,7 @@ function slashOperator(
external
onlyWhenNotPaused(PAUSED_OPERATOR_SLASHING)
checkCanCall(avs)
returns (uint256, uint256[] memory)
```

_Note: this method can be called directly by an AVS, or by a caller authorized by the AVS. See [`PermissionController.md`](../permissions/PermissionController.md) for details._
Expand All @@ -748,7 +787,7 @@ There are two edge cases to note for this method:
1. In the process of slashing an `operator` for a given `strategy`, if the `Allocation` being slashed has a `currentMagnitude` of 0, the call will NOT revert. Instead, the `strategy` is skipped and slashing continues with the next `strategy` listed. This is to prevent an edge case where slashing occurs on or around a deallocation's `effectBlock` -- if the call reverted, the entire slash would fail. Skipping allows any valid slashes to be processed without requiring resubmission.
2. If the `operator` has a pending, non-completable deallocation, the deallocation's `pendingDiff` is reduced proportional to the slash. This ensures that when the deallocation is completed, less `encumberedMagnitude` is freed.

Once slashing is processed for a strategy, [slashed stake is burned via the `DelegationManager`](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md#burning-of-slashed-funds).
Once slashing is processed for a strategy, [slashed stake is burned or redistributed via the `DelegationManager`](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md#burning-of-slashed-funds).

*Effects*:
* Given an `operator` and `operatorSet`, then for each `params.strategies` element and its corresponding `allocation`:
Expand All @@ -765,6 +804,8 @@ Once slashing is processed for a strategy, [slashed stake is burned via the `Del
* If this list now has a length of 0, remove `operatorSetKey` from `allocatedSets[operator]`
* Calls [`DelegationManager.slashOperatorShares`](./DelegationManager.md#slashoperatorshares)
* Emit an `OperatorSlashed` event
* Increments the `slashId` for the operatorSet
* Returns `slashId` and the number of shares slashed for each strategy

*Requirements*:
* Pause status MUST NOT be set: `PAUSED_OPERATOR_SLASHING`
Expand Down
Loading