@@ -28,6 +28,8 @@ contract AVSDirectory is
28
28
uint256 internal constant BIPS_FACTOR = 10_000 ;
29
29
/// @dev Delay before allocations take effect and how long until deallocations are completable
30
30
uint32 public constant ALLOCATION_DELAY = 21 days ;
31
+ /// @dev Maximum number of pending updates that can be queued for allocations/deallocations
32
+ uint256 public constant MAX_PENDING_UPDATES = 1 ;
31
33
32
34
/// @dev Returns the chain ID from the time the contract was deployed.
33
35
uint256 internal immutable ORIGINAL_CHAIN_ID;
@@ -370,7 +372,6 @@ contract AVSDirectory is
370
372
}
371
373
372
374
// 2. if there are any pending deallocations then need to update and decrement if they fall within slashable window
373
- uint64 slashedFromDeallocation = 0 ;
374
375
{
375
376
uint256 queuedDeallocationsLen =
376
377
_queuedDeallocations[operator][strategies[i]][msg .sender ][operatorSetId].length ;
@@ -383,17 +384,17 @@ contract AVSDirectory is
383
384
uint64 slashedAmount =
384
385
uint64 (uint256 (bipsToSlash) * uint256 (queuedDeallocation.magnitudeDiff) / BIPS_FACTOR);
385
386
queuedDeallocation.magnitudeDiff -= slashedAmount;
386
- slashedFromDeallocation += slashedAmount;
387
+ slashedMagnitude += slashedAmount;
387
388
} else {
388
389
break ;
389
390
}
390
391
}
391
392
}
392
393
393
- // 3. update totalMagnitude, get total magnitude and subtract slashedMagnitude and slashedFromDeallocation
394
+ // 3. update totalMagnitude, get total magnitude and subtract slashedMagnitude
394
395
_totalMagnitudeUpdate[operator][strategies[i]].push ({
395
396
key: uint32 (block .timestamp ),
396
- value: _totalMagnitudeUpdate[operator][strategies[i]].latest () - slashedMagnitude - slashedFromDeallocation
397
+ value: _totalMagnitudeUpdate[operator][strategies[i]].latest () - slashedMagnitude
397
398
});
398
399
}
399
400
}
@@ -501,6 +502,10 @@ contract AVSDirectory is
501
502
502
503
for (uint256 i = 0 ; i < operatorSets.length ; ++ i) {
503
504
// 1. check freeMagnitude available, that is the allocation of stake is backed and not slashable
505
+ require (
506
+ allocation.magnitudeDiffs[i] > 0 ,
507
+ "AVSDirectory.queueAllocations: magnitudeDiff must be greater than 0 "
508
+ );
504
509
require (
505
510
freeAllocatableMagnitude >= allocation.magnitudeDiffs[i],
506
511
"AVSDirectory.queueAllocations: insufficient available free magnitude to allocate "
@@ -513,10 +518,9 @@ contract AVSDirectory is
513
518
// a pending allocation
514
519
if (value != 0 || pos != 0 ) {
515
520
require (
516
- pos
517
- == _magnitudeUpdate[operator][strategy][operatorSets[i].avs][operatorSets[i].operatorSetId].length ()
518
- - 1 ,
519
- "AVSDirectory.queueAllocations: only one pending allocation allowed for op, opSet, strategy "
521
+ pos + MAX_PENDING_UPDATES
522
+ == _magnitudeUpdate[operator][strategy][operatorSets[i].avs][operatorSets[i].operatorSetId].length (),
523
+ "AVSDirectory.queueAllocations: exceed max pending allocations allowed for op, opSet, strategy "
520
524
);
521
525
}
522
526
@@ -547,7 +551,7 @@ contract AVSDirectory is
547
551
IStrategy strategy = deallocation.strategy;
548
552
require (
549
553
deallocation.operatorSets.length == deallocation.magnitudeDiffs.length ,
550
- "AVSDirectory.queueDeallocation : operatorSets and magnitudeDiffs length mismatch "
554
+ "AVSDirectory._queueDeallocate : operatorSets and magnitudeDiffs length mismatch "
551
555
);
552
556
OperatorSet[] calldata operatorSets = deallocation.operatorSets;
553
557
@@ -558,9 +562,13 @@ contract AVSDirectory is
558
562
_magnitudeUpdate[operator][strategy][operatorSets[i].avs][operatorSets[i].operatorSetId]
559
563
.upperLookupRecent (uint32 (block .timestamp ))
560
564
);
565
+ require (
566
+ deallocation.magnitudeDiffs[i] > 0 ,
567
+ "AVSDirectory._queueDeallocate: magnitudeDiff must be greater than 0 "
568
+ );
561
569
require (
562
570
deallocation.magnitudeDiffs[i] <= currentMagnitude,
563
- "AVSDirectory.queueDeallocation : cannot deallocate more than what is allocated "
571
+ "AVSDirectory._queueDeallocate : cannot deallocate more than what is allocated "
564
572
);
565
573
566
574
// 2. update and decrement current and future queued amounts in case any pending allocations exist
@@ -570,10 +578,10 @@ contract AVSDirectory is
570
578
decrementValue: deallocation.magnitudeDiffs[i]
571
579
});
572
580
573
- // TODO: ensure only 1 queued deallocation per operator, operatorSet, strategy
574
- // _verifySinglePendingDeallocation( );
581
+ // 3. ensure only queued deallocation per operator, operatorSet, strategy
582
+ _checkPendingDeallocations (operator, strategy, operatorSets[i] );
575
583
576
- // 3 . queue deallocation for (op, opSet, strategy) for magnitudeDiff amount
584
+ // 4 . queue deallocation for (op, opSet, strategy) for magnitudeDiff amount
577
585
_queuedDeallocations[operator][strategy][operatorSets[i].avs][operatorSets[i].operatorSetId].push (
578
586
QueuedDeallocation ({
579
587
magnitudeDiff: deallocation.magnitudeDiffs[i],
@@ -611,6 +619,29 @@ contract AVSDirectory is
611
619
return freeMagnitudeToAdd;
612
620
}
613
621
622
+ /// @dev Check for max number of pending deallocations, ensuring <= MAX_PENDING_UPDATES
623
+ function _checkPendingDeallocations (
624
+ address operator ,
625
+ IStrategy strategy ,
626
+ OperatorSet calldata operatorSet
627
+ ) internal view returns (uint64 freeMagnitudeToAdd ) {
628
+ QueuedDeallocation[] memory queuedDeallocations =
629
+ _queuedDeallocations[operator][strategy][operatorSet.avs][operatorSet.operatorSetId];
630
+ uint256 length = queuedDeallocations.length ;
631
+
632
+ for (uint256 i = length; i > 0 ; -- i) {
633
+ // If completableTimestamp is greater than completeUntilTimestamp, break
634
+ if (queuedDeallocations[i - 1 ].completableTimestamp < uint32 (block .timestamp )) {
635
+ require (
636
+ length - i + 1 < MAX_PENDING_UPDATES,
637
+ "AVSDirectory._checkPendingDeallocations: exceeds max pending deallocations "
638
+ );
639
+ } else {
640
+ break ;
641
+ }
642
+ }
643
+ }
644
+
614
645
/**
615
646
*
616
647
* VIEW FUNCTIONS
@@ -681,7 +712,7 @@ contract AVSDirectory is
681
712
function isOperatorSlashable (address operator , OperatorSet memory operatorSet ) public view returns (bool ) {
682
713
OperatorSetRegistrationStatus memory registrationStatus =
683
714
operatorSetStatus[operatorSet.avs][operator][operatorSet.operatorSetId];
684
- return registrationStatus.registered
715
+ return isMember (operator, operatorSet)
685
716
|| registrationStatus.lastDeregisteredTimestamp + ALLOCATION_DELAY >= block .timestamp ;
686
717
}
687
718
0 commit comments