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
The `AllocationManager` manages registration and deregistration of operators to operator sets, handles allocation and slashing of operators' slashable stake, and is the entry point an AVS uses to slash an operator. The `AllocationManager's` responsibilities are broken down into the following concepts:
25
+
The `AllocationManager` manages AVS metadata registration, registration and deregistration of operators to operator sets, handles allocation and slashing of operators' slashable stake, and is the entry point an AVS uses to slash an operator. The `AllocationManager's` responsibilities are broken down into the following concepts:
26
+
27
+
*[AVS Metadata](#avs-metadata)
26
28
*[Operator Sets](#operator-sets)
27
29
*[Allocations and Slashing](#allocations-and-slashing)
28
30
*[Config](#config)
@@ -38,6 +40,104 @@ The `AllocationManager` manages registration and deregistration of operators to
38
40
39
41
---
40
42
43
+
## AVS Metadata
44
+
45
+
AVSs must register their metadata to declare themselves who they are as the first step, before they can create operator sets or register operators into operator sets. `AllocationManager` keeps track of AVSs that have registered metadata.
46
+
47
+
**Methods:**
48
+
*[`updateAVSMetadataURI`](#updateavsmetadatauri)
49
+
50
+
51
+
#### `updateAVSMetadataURI`
52
+
53
+
```solidity
54
+
/**
55
+
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
56
+
*
57
+
* @param metadataURI The URI for metadata associated with an AVS.
58
+
*
59
+
* @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event.
60
+
*/
61
+
function updateAVSMetadataURI(
62
+
address avs,
63
+
string calldata metadataURI
64
+
)
65
+
external
66
+
checkCanCall(avs)
67
+
```
68
+
69
+
_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._
70
+
71
+
Below is the format AVSs should use when updating their metadata URI initially. This is not validated onchain.
72
+
73
+
```json
74
+
{
75
+
"name": "AVS",
76
+
"website": "https.avs.xyz/",
77
+
"description": "Some description about",
78
+
"logo": "http://github.com/logo.png",
79
+
"twitter": "https://twitter.com/avs",
80
+
}
81
+
```
82
+
83
+
84
+
Later on, once AVSs have created operator sets, content in their metadata URI can be updated subsequently.
85
+
86
+
```json
87
+
{
88
+
"name": "AVS",
89
+
"website": "https.avs.xyz/",
90
+
"description": "Some description about",
91
+
"logo": "http://github.com/logo.png",
92
+
"twitter": "https://twitter.com/avs",
93
+
"operatorSets": [
94
+
{
95
+
"name": "ETH Set",
96
+
"id": "1", // Note: we use this param to match the opSet id in the Allocation Manager
* Emits an `AVSMetadataURIUpdated` event for use in offchain services
136
+
137
+
*Requirements*:
138
+
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
139
+
140
+
41
141
## Operator Sets
42
142
43
143
Operator sets, as described in [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md#operator-sets), are useful for AVSs to configure operator groupings which can be assigned different tasks, rewarded based on their strategy allocations, and slashed according to different rules. Operator sets are defined in [`libraries/OperatorSetLib.sol`](../../src/contracts/libraries/OperatorSetLib.sol):
@@ -145,6 +245,7 @@ Optionally, the `avs` can provide a list of `strategies`, specifying which strat
145
245
146
246
*Requirements*:
147
247
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
248
+
* AVS MUST have registered metadata
148
249
* For each `CreateSetParams` element:
149
250
* Each `params.operatorSetId` MUST NOT already exist in `_operatorSets[avs]`
150
251
@@ -585,7 +686,26 @@ struct SlashingParams {
585
686
}
586
687
587
688
/**
588
-
* @notice Called by an AVS to slash an operator in a given operator set
689
+
* @notice Called by an AVS to slash an operator in a given operator set. The operator must be registered
690
+
* and have slashable stake allocated to the operator set.
691
+
*
692
+
* @param avs The AVS address initiating the slash.
693
+
* @param params The slashing parameters, containing:
694
+
* - operator: The operator to slash.
695
+
* - operatorSetId: The ID of the operator set the operator is being slashed from.
696
+
* - strategies: Array of strategies to slash allocations from (must be in ascending order).
697
+
* - wadsToSlash: Array of proportions to slash from each strategy (must be between 0 and 1e18).
698
+
* - description: Description of why the operator was slashed.
699
+
*
700
+
* @dev For each strategy:
701
+
* 1. Reduces the operator's current allocation magnitude by wadToSlash proportion.
702
+
* 2. Reduces the strategy's max and encumbered magnitudes proportionally.
703
+
* 3. If there is a pending deallocation, reduces it proportionally.
704
+
* 4. Updates the operator's shares in the DelegationManager.
705
+
*
706
+
* @dev Small slashing amounts may not result in actual token burns due to
707
+
* rounding, which will result in small amounts of tokens locked in the contract
708
+
* rather than fully burning through the burn mechanism.
589
709
*/
590
710
function slashOperator(
591
711
address avs,
@@ -646,7 +766,6 @@ Once slashing is processed for a strategy, [slashed stake is burned via the `Del
646
766
**Methods:**
647
767
*[`setAllocationDelay`](#setallocationdelay)
648
768
*[`setAVSRegistrar`](#setavsregistrar)
649
-
*[`updateAVSMetadataURI`](#updateavsmetadatauri)
650
769
651
770
#### `setAllocationDelay`
652
771
@@ -737,79 +856,3 @@ Note that when an operator registers, registration will FAIL if the call to `IAV
737
856
738
857
*Requirements*:
739
858
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
740
-
741
-
#### `updateAVSMetadataURI`
742
-
743
-
```solidity
744
-
/**
745
-
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
746
-
*
747
-
* @param metadataURI The URI for metadata associated with an AVS.
748
-
*
749
-
* @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event.
750
-
*/
751
-
function updateAVSMetadataURI(
752
-
address avs,
753
-
string calldata metadataURI
754
-
)
755
-
external
756
-
checkCanCall(avs)
757
-
```
758
-
759
-
_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._
760
-
761
-
Below is the format AVSs should use when updating their metadata URI. This is not validated onchain.
762
-
763
-
```json
764
-
{
765
-
"name": "AVS",
766
-
"website": "https.avs.xyz/",
767
-
"description": "Some description about",
768
-
"logo": "http://github.com/logo.png",
769
-
"twitter": "https://twitter.com/avs",
770
-
"operatorSets": [
771
-
{
772
-
"name": "ETH Set",
773
-
"id": "1", // Note: we use this param to match the opSet id in the Allocation Manager
Copy file name to clipboardExpand all lines: docs/core/StrategyManager.md
+3-4Lines changed: 3 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -190,13 +190,11 @@ Note that the amount of deposit shares removed while in the withdrawal queue may
190
190
```solidity
191
191
/// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue
192
192
/// @dev strategy must be beaconChainETH when talking to the EigenPodManager
193
-
/// @dev token is not validated; it is only emitted as an event
194
193
/// @return existingDepositShares the shares the staker had before any were added
195
194
/// @return addedShares the new shares added to the staker's balance
196
195
function addShares(
197
196
address staker,
198
197
IStrategy strategy,
199
-
IERC20 token,
200
198
uint256 shares
201
199
)
202
200
external
@@ -252,7 +250,7 @@ This method directs the `strategy` to convert the input deposit shares to tokens
252
250
253
251
## Burning Slashed Shares
254
252
255
-
The following methods handle burning of slashed shares:
253
+
Slashed shares are marked as burnable, and anyone can call `burnShares` to transfer them to the default burn address. Burnable shares are stored in `burnableShares`, an [EnumerableMap](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.0/contracts/utils/structs/EnumerableMap.sol) with strategy contract addresses as keys and associated view functions. The following methods handle burning of slashed shares:
Anyone can call this method to burn slashed shares previously added by the `DelegationManager` via `increaseBurnableShares`. This method resets the strategy's burnable shares to 0, and directs the corresponding `strategy` to convert the shares to tokens and transfer them to `DEFAULT_BURN_ADDRESS`, rendering them unrecoverable.
303
300
301
+
The `strategy` is not called if the strategy had no burnable shares.
302
+
304
303
*Effects*:
305
304
* Resets the strategy's burnable shares to 0
306
305
* Calls `withdraw` on the `strategy`, withdrawing shares and sending a corresponding amount of tokens to the `DEFAULT_BURN_ADDRESS`
Suppose we have an undelegated staker who decides to delegate to an operator.
171
+
We have the following properties that should be preserved.
172
+
173
+
#### Operator Level
174
+
175
+
Operator shares should be increased by the amount of delegatable shares the staker has, this is synonymous to their withdrawable shares $a_n$. Therefore,
176
+
177
+
$$
178
+
op_{n+1} = op_{n} + a_n
179
+
$$
180
+
181
+
$$
182
+
= op_{n} + s_n k_n l_n m_n
183
+
$$
184
+
185
+
186
+
#### Staker Level
187
+
188
+
withdrawable shares should remain unchanged
189
+
190
+
$$
191
+
a_{n+1} = a_n
192
+
$$
193
+
194
+
deposit shares should remain unchanged
195
+
196
+
$$
197
+
s_{n+1} = s_n
198
+
$$
199
+
200
+
beaconChainSlashingFactor and maxMagnitude should also remain unchanged. In this case, since the staker is not delegated, then their maxMagnitude should by default be equal to 1.
201
+
202
+
$$
203
+
l_{n+1} = l_n
204
+
$$
205
+
206
+
Now the question is what is the new depositScalingFactor equal to?
Notice how the staker variables that update $k_{n+1}$ and $m_{n+1}$ do not affect previously queued withdrawals and shares received upon withdrawal completion. This is because the maxMagnitude that is looked up is dependent on the operator at the time of the queued withdrawal and the $k_n$ is effectively stored in the scaled shares field.
225
+
165
226
---
166
227
167
228
### Slashing
@@ -297,6 +358,8 @@ $$
297
358
298
359
Note that when a withdrawal is queued, a `Withdrawal` struct is created with _scaled shares_ defined as $q_t = x_t k_t$ where $t$ is the time of the queuing. The reason we define and store scaled shares like this will be clearer in [Complete Withdrawal](#complete-withdrawal) below.
299
360
361
+
Additionally, we reset the depositScalingFactor when a user queues a withdrawal for all their shares, either through un/redelegation or directly. This is because the DSF at the time of withdrawal is stored in the scaled shares, and any "new" deposits or delegations by the staker should be considered as new. Note that withdrawal completion is treated as a kind of deposit when done as shares, which again will be clearer below.
Copy file name to clipboardExpand all lines: docs/core/accounting/SharesAccountingEdgeCases.md
+22Lines changed: 22 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -145,6 +145,28 @@ Since the operatorShares are simply incrementing by the exact depositShares, the
145
145
Granted the initial deposit amount was `4.418e28` which is magnitudes larger than the discrepancy here but this its important to note the side effects of the redesigned accounting model.
146
146
Instead of purely incremented/decremented amounts, we have introduced magnitudes and scaling factor variables which now result in small amounts of rounding error from division in several places. We deem this rounding behavior to be tolerable given the costs associated for the number of transactions to emulate this and the proportional error is very small.
147
147
148
+
### Slashing and Rounding Up Operator Shares and Rounding down on Staker Withdrawable Shares
149
+
150
+
As can be observed in the `SlashingLib.sol` library, we round up on the operatorShares when slashing and round down on the staker's withdrawableShares. If we look at a core invariant of the shares accounting model, we ideally want to preserve the following:
151
+
152
+
$$
153
+
op_n = \sum_{i=1}^{k} a_{n,i}
154
+
$$
155
+
156
+
where $op_n$ is the operatorShares at time $n$ and $a_{n,i}$ is the staker's withdrawableShares at time $n$ for the $i^{th}$ staker.
157
+
158
+
However due to rounding limitations, there will be some error introduced in calculating the amount of operator shares to slash above and also in calculating the staker's withdrawableShares. To prevent a situation where all stakers were to attempt to withdraw and the operatorShares underflows, we round up on the operatorShares when slashing and round down on the staker's withdrawableShares.
159
+
160
+
So in practice, the above invariant becomes.
161
+
162
+
$$
163
+
op_n \geq \sum_{i=1}^{k} a_{n,i}
164
+
$$
165
+
166
+
Upwards rounding on calculating the amount of operatorShares to give to an operator after slashing is intentionally performed in `SlashingLib.calcSlashedAmount`.
167
+
For calculating a staker's withdrawableShares, there are many different factors to consider such as calculating their depositScalingFactor, their slashingFactor, and calculating the amount of withdrawable shares altogether with their depositShares. These variables are all by default rounded down in calculation and is expected behavior for stakers.
168
+
169
+
148
170
## Upper bound on Residual Operator Shares
149
171
150
172
Related to the above rounding error on deposits, we want to calculate what is the worst case rounding error for a staker depositing shares into EigenLayer.
0 commit comments