Skip to content

Commit de84319

Browse files
feat: move redistribution queue to DM (#4)
* feat: move queue to dm * chore: manually create withdrawal * chore: add todo * chore: update event todo * wip * wip * refactor: comment out `assert_Snap_Increased_BurnableShares` * fix: `DelegationManagerMock` return value * refactor: comment out old `burnShares` tests * chore: make fmt * refactor: remove random json --------- Co-authored-by: Yash Patil <40046473+ypatil12@users.noreply.github.com>
1 parent be4b3a4 commit de84319

14 files changed

+260
-483
lines changed

script/deploy/devnet/deploy_from_scratch.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ contract DeployFromScratch is Script, Test {
235235
SEMVER
236236
);
237237

238-
strategyManagerImplementation = new StrategyManager(allocationManager, delegation, eigenLayerPauserReg, SEMVER);
238+
strategyManagerImplementation = new StrategyManager(delegation, eigenLayerPauserReg, SEMVER);
239239
avsDirectoryImplementation = new AVSDirectory(delegation, eigenLayerPauserReg, SEMVER);
240240
eigenPodManagerImplementation =
241241
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg, SEMVER);

script/deploy/local/deploy_from_scratch.slashing.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ contract DeployFromScratch is Script, Test {
241241
MIN_WITHDRAWAL_DELAY,
242242
SEMVER
243243
);
244-
strategyManagerImplementation = new StrategyManager(allocationManager, delegation, eigenLayerPauserReg, SEMVER);
244+
strategyManagerImplementation = new StrategyManager(delegation, eigenLayerPauserReg, SEMVER);
245245
avsDirectoryImplementation = new AVSDirectory(delegation, eigenLayerPauserReg, SEMVER);
246246
eigenPodManagerImplementation =
247247
new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegation, eigenLayerPauserReg, SEMVER);

script/output/devnet/SLASHING_deploy_from_scratch_deployment_data.json

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/contracts/core/AllocationManager.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ contract AllocationManager is
400400
_updateMaxMagnitude(params.operator, params.strategies[i], info.maxMagnitude);
401401

402402
// 6. Slash operators shares in the DelegationManager
403-
delegation.slashOperatorShares({
403+
shares[i] = delegation.slashOperatorShares({
404404
operator: params.operator,
405405
operatorSet: operatorSet,
406406
slashId: slashId,

src/contracts/core/DelegationManager.sol

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ contract DelegationManager is
285285
IStrategy strategy,
286286
uint64 prevMaxMagnitude,
287287
uint64 newMaxMagnitude
288-
) external onlyAllocationManager nonReentrant {
288+
) external onlyAllocationManager nonReentrant returns (uint256) {
289289
/// forgefmt: disable-next-item
290290
uint256 operatorSharesSlashed = SlashingLib.calcSlashedAmount({
291291
operatorShares: operatorShares[operator][strategy],
@@ -316,6 +316,27 @@ contract DelegationManager is
316316
emit OperatorSharesSlashed(operator, strategy, totalDepositSharesToBurn);
317317

318318
_getShareManager(strategy).increaseBurnableShares(operatorSet, slashId, strategy, totalDepositSharesToBurn);
319+
320+
IStrategy[] memory singleStrategy = new IStrategy[](1);
321+
uint256[] memory singleDepositShares = new uint256[](1);
322+
singleStrategy[0] = strategy;
323+
singleDepositShares[0] = totalDepositSharesToBurn;
324+
325+
Withdrawal memory withdrawal = Withdrawal({
326+
staker: address(0), // TODO: pass in redistribution recipient in call
327+
delegatedTo: address(this),
328+
withdrawer: address(0), // We use address(0) as the withdrawer to special case for allowing anybody to complete the withdrawal
329+
nonce: slashId,
330+
startBlock: uint32(block.number),
331+
strategies: singleStrategy,
332+
scaledShares: singleDepositShares
333+
});
334+
335+
emit RedistributionQueued(calculateWithdrawalRoot(withdrawal), withdrawal);
336+
337+
_addWithdrawalToQueue(withdrawal);
338+
339+
return totalDepositSharesToBurn;
319340
}
320341

321342
/**
@@ -519,13 +540,21 @@ contract DelegationManager is
519540
scaledShares: scaledShares
520541
});
521542

543+
bytes32 withdrawalRoot = _addWithdrawalToQueue(withdrawal);
544+
545+
emit SlashingWithdrawalQueued(withdrawalRoot, withdrawal, withdrawableShares);
546+
return withdrawalRoot;
547+
}
548+
549+
function _addWithdrawalToQueue(
550+
Withdrawal memory withdrawal
551+
) internal returns (bytes32) {
522552
bytes32 withdrawalRoot = calculateWithdrawalRoot(withdrawal);
523553

524554
pendingWithdrawals[withdrawalRoot] = true;
525555
_queuedWithdrawals[withdrawalRoot] = withdrawal;
526-
_stakerQueuedWithdrawalRoots[staker].add(withdrawalRoot);
556+
_stakerQueuedWithdrawalRoots[withdrawal.staker].add(withdrawalRoot);
527557

528-
emit SlashingWithdrawalQueued(withdrawalRoot, withdrawal, withdrawableShares);
529558
return withdrawalRoot;
530559
}
531560

@@ -544,15 +573,22 @@ contract DelegationManager is
544573
bool receiveAsTokens
545574
) internal {
546575
_checkInputArrayLengths(tokens.length, withdrawal.strategies.length);
547-
require(msg.sender == withdrawal.withdrawer, WithdrawerNotCaller());
576+
if (withdrawal.delegatedTo == address(this)) {
577+
// If this is a redistribution withdrawal // TODO: make this cleaner
578+
require(receiveAsTokens, WithdrawerNotCaller());
579+
} else {
580+
require(msg.sender == withdrawal.withdrawer, WithdrawerNotCaller());
581+
}
548582
bytes32 withdrawalRoot = calculateWithdrawalRoot(withdrawal);
549583
require(pendingWithdrawals[withdrawalRoot], WithdrawalNotQueued());
550584

551585
uint256[] memory prevSlashingFactors;
552586
{
553587
// slashableUntil is block inclusive so we need to check if the current block is strictly greater than the slashableUntil block
554588
// meaning the withdrawal can be completed.
555-
uint32 slashableUntil = withdrawal.startBlock + MIN_WITHDRAWAL_DELAY_BLOCKS;
589+
// TODO: update delay blocks for redistribution + EIGEN_REDISTRIBUTION_DELAY_BLOCKS
590+
uint32 delayBlocks = withdrawal.delegatedTo == address(this) ? 3.5 days : MIN_WITHDRAWAL_DELAY_BLOCKS;
591+
uint32 slashableUntil = withdrawal.startBlock + delayBlocks;
556592
require(uint32(block.number) > slashableUntil, WithdrawalDelayNotElapsed());
557593

558594
// Given the max magnitudes of the operator the staker was originally delegated to, calculate

src/contracts/core/StrategyManager.sol

Lines changed: 18 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
88

99
import "../mixins/SignatureUtilsMixin.sol";
1010
import "../interfaces/IEigenPodManager.sol";
11-
import "../libraries/OperatorSetLib.sol";
1211
import "../permissions/Pausable.sol";
1312
import "./StrategyManagerStorage.sol";
1413

@@ -31,8 +30,6 @@ contract StrategyManager is
3130
{
3231
using SlashingLib for *;
3332
using SafeERC20 for IERC20;
34-
using OperatorSetLib for OperatorSet;
35-
using EnumerableMap for EnumerableMap.AddressToUintMap;
3633

3734
modifier onlyStrategyWhitelister() {
3835
require(msg.sender == strategyWhitelister, OnlyStrategyWhitelister());
@@ -55,11 +52,10 @@ contract StrategyManager is
5552
* @param _delegation The delegation contract of EigenLayer.
5653
*/
5754
constructor(
58-
IAllocationManager _allocationManager,
5955
IDelegationManager _delegation,
6056
IPauserRegistry _pauserRegistry,
6157
string memory _version
62-
) StrategyManagerStorage(_allocationManager, _delegation) Pausable(_pauserRegistry) SignatureUtilsMixin(_version) {
58+
) StrategyManagerStorage(_delegation) Pausable(_pauserRegistry) SignatureUtilsMixin(_version) {
6359
_disableInitializers();
6460
}
6561

@@ -151,63 +147,23 @@ contract StrategyManager is
151147
IStrategy strategy,
152148
uint256 addedSharesToBurn
153149
) external onlyDelegationManager nonReentrant {
154-
EnumerableMap.AddressToUintMap storage operatorSetBurnableShares =
155-
_operatorSetBurnableShares[operatorSet.key()][slashId];
156-
(, uint256 currentShares) = operatorSetBurnableShares.tryGet(address(strategy));
157-
operatorSetBurnableShares.set(address(strategy), currentShares + addedSharesToBurn);
158-
150+
// (, uint256 currentShares) = EnumerableMap.tryGet(burnableShares, address(strategy));
151+
// EnumerableMap.set(burnableShares, address(strategy), currentShares + addedSharesToBurn);
159152
emit BurnableSharesIncreased(operatorSet, slashId, strategy, addedSharesToBurn);
160153
}
161154

162155
/// @inheritdoc IStrategyManager
163156
function burnShares(
164157
IStrategy strategy
165158
) external nonReentrant {
166-
EnumerableMap.AddressToUintMap storage burnableShares = _burnableShares;
167-
(, uint256 sharesToBurn) = burnableShares.tryGet(address(strategy));
168-
burnableShares.remove(address(strategy));
169-
170-
// NOTE: We use max values for the operatorSet and slashId to indicate that this is a pre-distribution burn.
171-
_burnShares({
172-
operatorSet: OperatorSet(address(this), type(uint32).max),
173-
slashId: type(uint256).max,
174-
strategy: strategy,
175-
recipient: DEFAULT_BURN_ADDRESS,
176-
sharesToBurn: sharesToBurn
177-
});
178-
}
179-
180-
/// @inheritdoc IStrategyManager
181-
function burnOrDistributeShares(
182-
OperatorSet calldata operatorSet,
183-
uint256 slashId,
184-
IStrategy strategy
185-
) external nonReentrant {
186-
EnumerableMap.AddressToUintMap storage operatorSetBurnableShares =
187-
_operatorSetBurnableShares[operatorSet.key()][slashId];
188-
189-
(, uint256 sharesToBurn) = operatorSetBurnableShares.tryGet(address(strategy));
190-
191-
address recipient = allocationManager.getRedistributionRecipient(operatorSet);
192-
193-
_burnOrDistributeShares(operatorSetBurnableShares, operatorSet, strategy, slashId, sharesToBurn, recipient);
194-
}
195-
196-
/// @inheritdoc IStrategyManager
197-
function burnOrDistributeShares(OperatorSet calldata operatorSet, uint256 slashId) external nonReentrant {
198-
EnumerableMap.AddressToUintMap storage operatorSetBurnableShares =
199-
_operatorSetBurnableShares[operatorSet.key()][slashId];
200-
201-
uint256 totalEntries = operatorSetBurnableShares.length();
159+
(, uint256 sharesToBurn) = EnumerableMap.tryGet(burnableShares, address(strategy));
160+
EnumerableMap.remove(burnableShares, address(strategy));
161+
emit BurnableSharesDecreased(OperatorSet(address(this), 0), 0, strategy, sharesToBurn);
202162

203-
address recipient = allocationManager.getRedistributionRecipient(operatorSet);
204-
205-
for (uint256 i = 0; i < totalEntries; ++i) {
206-
(address strategy, uint256 sharesToBurn) = operatorSetBurnableShares.at(i);
207-
208-
_burnOrDistributeShares(
209-
operatorSetBurnableShares, operatorSet, IStrategy(strategy), slashId, sharesToBurn, recipient
210-
);
163+
// Burning acts like withdrawing, except that the destination is to the burn address.
164+
// If we have no shares to burn, we don't need to call the strategy.
165+
if (sharesToBurn != 0) {
166+
strategy.withdraw(DEFAULT_BURN_ADDRESS, strategy.underlyingToken(), sharesToBurn);
211167
}
212168
}
213169

@@ -348,62 +304,6 @@ contract StrategyManager is
348304
return userDepositShares;
349305
}
350306

351-
/**
352-
* @notice Burns `sharesToBurn` shares from `strategy` and sends the underlying tokens to `recipient`.
353-
* @param operatorSet The operator set associated with this burn/redistribution.
354-
* @param slashId The unique identifier for the slashing event relative to the operator set.
355-
* @param strategy The strategy contract from which shares will be burned.
356-
* @param recipient The address that will receive the underlying tokens from the burned shares.
357-
* @param sharesToBurn The number of shares to burn from the strategy.
358-
*/
359-
function _burnShares(
360-
OperatorSet memory operatorSet,
361-
uint256 slashId,
362-
IStrategy strategy,
363-
address recipient,
364-
uint256 sharesToBurn
365-
) internal {
366-
emit BurnableSharesDecreased(operatorSet, slashId, strategy, recipient, sharesToBurn);
367-
368-
// Burning acts like withdrawing, except that the destination is to the burn address.
369-
// If we have no shares to burn, we don't need to call the strategy.
370-
if (sharesToBurn != 0) {
371-
strategy.withdraw(recipient, strategy.underlyingToken(), sharesToBurn);
372-
}
373-
}
374-
375-
/**
376-
* @notice Burns `sharesToBurn` shares from `strategy` and sends the underlying tokens to redistribution recipient or `DEFAULT_BURN_ADDRESS`.
377-
* @param ptr The pointer to the operator set burnable shares map.
378-
* @param operatorSet The operator set associated with this burn/redistribution.
379-
* @param slashId The unique identifier for the slashing event relative to the operator set.
380-
* @param strategy The strategy contract from which shares will be burned.
381-
* @param sharesToBurn The number of shares to burn from the strategy.
382-
*/
383-
function _burnOrDistributeShares(
384-
EnumerableMap.AddressToUintMap storage ptr,
385-
OperatorSet calldata operatorSet,
386-
IStrategy strategy,
387-
uint256 slashId,
388-
uint256 sharesToBurn,
389-
address recipient
390-
) internal {
391-
require(
392-
block.timestamp >= allocationManager.getBurnOrRedistributionBlock(operatorSet, strategy, slashId),
393-
BurnOrRedistributionDelayNotElapsed()
394-
);
395-
396-
ptr.remove(address(strategy));
397-
398-
_burnShares({
399-
operatorSet: operatorSet,
400-
slashId: slashId,
401-
strategy: IStrategy(strategy),
402-
recipient: recipient,
403-
sharesToBurn: sharesToBurn
404-
});
405-
}
406-
407307
/**
408308
* @notice Removes `strategy` from `staker`'s dynamic array of strategies, i.e. from `stakerStrategyList[staker]`
409309
* @param staker The user whose array will have an entry removed
@@ -493,48 +393,22 @@ contract StrategyManager is
493393
/// @inheritdoc IStrategyManager
494394
function getBurnableShares(
495395
IStrategy strategy
496-
) external view returns (uint256 shares) {
497-
(, shares) = _burnableShares.tryGet(address(strategy));
498-
}
499-
500-
/// @inheritdoc IStrategyManager
501-
function getOperatorSetBurnableShares(
502-
OperatorSet calldata operatorSet,
503-
uint256 slashId,
504-
IStrategy strategy
505-
) external view returns (uint256 shares) {
506-
(, shares) = _operatorSetBurnableShares[operatorSet.key()][slashId].tryGet(address(strategy));
396+
) external view returns (uint256) {
397+
(, uint256 shares) = EnumerableMap.tryGet(burnableShares, address(strategy));
398+
return shares;
507399
}
508400

509401
/// @inheritdoc IStrategyManager
510402
function getStrategiesWithBurnableShares() external view returns (address[] memory, uint256[] memory) {
511-
EnumerableMap.AddressToUintMap storage burnableShares = _burnableShares;
512-
uint256 totalEntries = burnableShares.length();
513-
514-
address[] memory strategies = new address[](totalEntries);
515-
uint256[] memory shares = new uint256[](totalEntries);
516-
517-
for (uint256 i = 0; i < totalEntries; ++i) {
518-
(strategies[i], shares[i]) = burnableShares.at(i);
519-
}
520-
521-
return (strategies, shares);
522-
}
523-
524-
/// @inheritdoc IStrategyManager
525-
function getOperatorSetStrategiesWithBurnableShares(
526-
OperatorSet calldata operatorSet,
527-
uint256 slashId
528-
) external view returns (address[] memory, uint256[] memory) {
529-
EnumerableMap.AddressToUintMap storage operatorSetBurnableShares =
530-
_operatorSetBurnableShares[operatorSet.key()][slashId];
531-
uint256 totalEntries = operatorSetBurnableShares.length();
403+
uint256 totalEntries = EnumerableMap.length(burnableShares);
532404

533405
address[] memory strategies = new address[](totalEntries);
534406
uint256[] memory shares = new uint256[](totalEntries);
535407

536-
for (uint256 i = 0; i < totalEntries; ++i) {
537-
(strategies[i], shares[i]) = operatorSetBurnableShares.at(i);
408+
for (uint256 i = 0; i < totalEntries; i++) {
409+
(address strategy, uint256 shareAmount) = EnumerableMap.at(burnableShares, i);
410+
strategies[i] = strategy;
411+
shares[i] = shareAmount;
538412
}
539413

540414
return (strategies, shares);

0 commit comments

Comments
 (0)