@@ -89,25 +89,57 @@ contract SlashEscrowFactory is Initializable, SlashEscrowFactoryStorage, Ownable
8989 ) external onlyWhenNotPaused (PAUSED_RELEASE_ESCROW) {
9090 address redistributionRecipient = allocationManager.getRedistributionRecipient (operatorSet);
9191
92- // If the redistribution recipient is not the default burn address...
93- if (redistributionRecipient != DEFAULT_BURN_ADDRESS) {
94- require (msg .sender == redistributionRecipient, OnlyRedistributionRecipient ());
95- }
96-
97- // Assert that the slash ID is not paused
98- require (! isEscrowPaused (operatorSet, slashId), IPausable.CurrentlyPaused ());
99-
100- // Assert that the escrow delay has elapsed
101- require (block .number >= getEscrowCompleteBlock (operatorSet, slashId), EscrowDelayNotElapsed ());
92+ _checkReleaseSlashEscrow (operatorSet, slashId, redistributionRecipient);
10293
10394 // Calling `clearBurnOrRedistributableShares` will transfer the underlying tokens to the `SlashEscrow`.
10495 // NOTE: While `clearBurnOrRedistributableShares` may have already been called, we call it again to ensure that the
10596 // underlying tokens are actually in escrow before processing and removing storage (which would otherwise prevent
10697 // the tokens from being released).
10798 strategyManager.clearBurnOrRedistributableShares (operatorSet, slashId);
10899
109- // Release the slashEscrow. The `SlashEscrow` is deployed in `initiateSlashEscrow`.
110- _processSlashEscrow (operatorSet, slashId, getSlashEscrow (operatorSet, slashId), redistributionRecipient);
100+ // Process the slash escrow for each strategy.
101+ address [] memory strategies = _pendingStrategiesForSlashId[operatorSet.key ()][slashId].values ();
102+ for (uint256 i = 0 ; i < strategies.length ; ++ i) {
103+ _processSlashEscrowByStrategy ({
104+ operatorSet: operatorSet,
105+ slashId: slashId,
106+ slashEscrow: getSlashEscrow (operatorSet, slashId),
107+ redistributionRecipient: redistributionRecipient,
108+ strategy: IStrategy (strategies[i])
109+ });
110+ }
111+
112+ // Update the slash escrow storage.
113+ _updateSlashEscrowStorage (operatorSet, slashId);
114+ }
115+
116+ /// @inheritdoc ISlashEscrowFactory
117+ function releaseSlashEscrowByStrategy (
118+ OperatorSet calldata operatorSet ,
119+ uint256 slashId ,
120+ IStrategy strategy
121+ ) external virtual onlyWhenNotPaused (PAUSED_RELEASE_ESCROW) {
122+ address redistributionRecipient = allocationManager.getRedistributionRecipient (operatorSet);
123+
124+ _checkReleaseSlashEscrow (operatorSet, slashId, redistributionRecipient);
125+
126+ // Calling `clearBurnOrRedistributableSharesByStrategy` will transfer the underlying tokens to the `SlashEscrow`.
127+ // NOTE: While the strategy may have already been cleared, we call it again to ensure that the
128+ // underlying tokens are actually in escrow before processing and removing storage (which would otherwise prevent
129+ // the tokens from being released).
130+ strategyManager.clearBurnOrRedistributableSharesByStrategy (operatorSet, slashId, strategy);
131+
132+ // Release the slashEscrow.
133+ _processSlashEscrowByStrategy ({
134+ operatorSet: operatorSet,
135+ slashId: slashId,
136+ slashEscrow: getSlashEscrow (operatorSet, slashId),
137+ redistributionRecipient: redistributionRecipient,
138+ strategy: strategy
139+ });
140+
141+ // Update the slash escrow storage.
142+ _updateSlashEscrowStorage (operatorSet, slashId);
111143 }
112144
113145 /**
@@ -155,48 +187,68 @@ contract SlashEscrowFactory is Initializable, SlashEscrowFactoryStorage, Ownable
155187 *
156188 */
157189
158- /// @notice Processes the slash escrow.
159- function _processSlashEscrow (
190+ /// @notice Checks that the slash escrow can be released .
191+ function _checkReleaseSlashEscrow (
160192 OperatorSet calldata operatorSet ,
161193 uint256 slashId ,
162- ISlashEscrow slashEscrow ,
163194 address redistributionRecipient
195+ ) internal view {
196+ // If the redistribution recipient is not the default burn address...
197+ if (redistributionRecipient != DEFAULT_BURN_ADDRESS) {
198+ require (msg .sender == redistributionRecipient, OnlyRedistributionRecipient ());
199+ }
200+
201+ // Assert that the slash ID is not paused
202+ require (! isEscrowPaused (operatorSet, slashId), IPausable.CurrentlyPaused ());
203+
204+ // Assert that the escrow delay has elapsed
205+ require (block .number >= getEscrowCompleteBlock (operatorSet, slashId), EscrowDelayNotElapsed ());
206+ }
207+
208+ /// @notice Processes the slash escrow for a single strategy.
209+ function _processSlashEscrowByStrategy (
210+ OperatorSet calldata operatorSet ,
211+ uint256 slashId ,
212+ ISlashEscrow slashEscrow ,
213+ address redistributionRecipient ,
214+ IStrategy strategy
164215 ) internal {
165- // Create storage pointers for readability.
166- EnumerableSet.Bytes32Set storage pendingOperatorSets = _pendingOperatorSets;
167- EnumerableSet.UintSet storage pendingSlashIds = _pendingSlashIds[operatorSet.key ()];
216+ // Create storage pointer for readability.
168217 EnumerableSet.AddressSet storage pendingStrategiesForSlashId =
169218 _pendingStrategiesForSlashId[operatorSet.key ()][slashId];
170219
171- // Iterate over the escrow array in reverse order and pop the processed entries from storage.
172- uint256 totalPendingForSlashId = pendingStrategiesForSlashId.length ();
173- for (uint256 i = totalPendingForSlashId; i > 0 ; -- i) {
174- address strategy = pendingStrategiesForSlashId.at (i - 1 );
175-
176- // Burn or redistribute the underlying tokens for the strategy.
177- slashEscrow.releaseTokens (
178- ISlashEscrowFactory (address (this )),
179- slashEscrowImplementation,
180- operatorSet,
181- slashId,
182- redistributionRecipient,
183- IStrategy (strategy)
184- );
185-
186- // Remove the strategy and underlying amount from the pending burn or redistributions map.
187- pendingStrategiesForSlashId.remove (strategy);
188- emit EscrowComplete (operatorSet, slashId, IStrategy (strategy), redistributionRecipient);
189- }
220+ // Burn or redistribute the underlying tokens for the strategy.
221+ slashEscrow.releaseTokens ({
222+ slashEscrowFactory: ISlashEscrowFactory (address (this )),
223+ slashEscrowImplementation: slashEscrowImplementation,
224+ operatorSet: operatorSet,
225+ slashId: slashId,
226+ recipient: redistributionRecipient,
227+ strategy: strategy
228+ });
229+
230+ // Remove the strategy and underlying amount from the pending strategies escrow map.
231+ pendingStrategiesForSlashId.remove (address (strategy));
232+ emit EscrowComplete (operatorSet, slashId, strategy, redistributionRecipient);
233+ }
234+
235+ function _updateSlashEscrowStorage (OperatorSet calldata operatorSet , uint256 slashId ) internal {
236+ // Create storage pointers for readability.
237+ EnumerableSet.Bytes32Set storage pendingOperatorSets = _pendingOperatorSets;
238+ EnumerableSet.UintSet storage pendingSlashIds = _pendingSlashIds[operatorSet.key ()];
239+ uint256 totalPendingForSlashId = _pendingStrategiesForSlashId[operatorSet.key ()][slashId].length ();
190240
191- // Remove the slash ID from the pending slash IDs set.
192- pendingSlashIds.remove (slashId);
241+ // If there are no more strategies to process, remove the slash ID from the pending slash IDs set.
242+ if (totalPendingForSlashId == 0 ) {
243+ pendingSlashIds.remove (slashId);
193244
194- // Delete the start block for the slash ID.
195- delete _slashIdToStartBlock[operatorSet.key ()][slashId];
245+ // Delete the start block for the slash ID.
246+ delete _slashIdToStartBlock[operatorSet.key ()][slashId];
196247
197- // Remove the operator set from the pending operator sets set if there are no more pending slash IDs.
198- if (pendingSlashIds.length () == 0 ) {
199- pendingOperatorSets.remove (operatorSet.key ());
248+ // If there are no more slash IDs for the operator set, remove the operator set from the pending operator sets set.
249+ if (pendingSlashIds.length () == 0 ) {
250+ pendingOperatorSets.remove (operatorSet.key ());
251+ }
200252 }
201253 }
202254
0 commit comments