Skip to content

Commit 9f91f03

Browse files
committed
chore: add check for pending deallocations
1 parent 7058424 commit 9f91f03

File tree

2 files changed

+46
-14
lines changed

2 files changed

+46
-14
lines changed

src/contracts/core/AVSDirectory.sol

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ contract AVSDirectory is
2828
uint256 internal constant BIPS_FACTOR = 10_000;
2929
/// @dev Delay before allocations take effect and how long until deallocations are completable
3030
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;
3133

3234
/// @dev Returns the chain ID from the time the contract was deployed.
3335
uint256 internal immutable ORIGINAL_CHAIN_ID;
@@ -370,7 +372,6 @@ contract AVSDirectory is
370372
}
371373

372374
// 2. if there are any pending deallocations then need to update and decrement if they fall within slashable window
373-
uint64 slashedFromDeallocation = 0;
374375
{
375376
uint256 queuedDeallocationsLen =
376377
_queuedDeallocations[operator][strategies[i]][msg.sender][operatorSetId].length;
@@ -383,17 +384,17 @@ contract AVSDirectory is
383384
uint64 slashedAmount =
384385
uint64(uint256(bipsToSlash) * uint256(queuedDeallocation.magnitudeDiff) / BIPS_FACTOR);
385386
queuedDeallocation.magnitudeDiff -= slashedAmount;
386-
slashedFromDeallocation += slashedAmount;
387+
slashedMagnitude += slashedAmount;
387388
} else {
388389
break;
389390
}
390391
}
391392
}
392393

393-
// 3. update totalMagnitude, get total magnitude and subtract slashedMagnitude and slashedFromDeallocation
394+
// 3. update totalMagnitude, get total magnitude and subtract slashedMagnitude
394395
_totalMagnitudeUpdate[operator][strategies[i]].push({
395396
key: uint32(block.timestamp),
396-
value: _totalMagnitudeUpdate[operator][strategies[i]].latest() - slashedMagnitude - slashedFromDeallocation
397+
value: _totalMagnitudeUpdate[operator][strategies[i]].latest() - slashedMagnitude
397398
});
398399
}
399400
}
@@ -501,6 +502,10 @@ contract AVSDirectory is
501502

502503
for (uint256 i = 0; i < operatorSets.length; ++i) {
503504
// 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+
);
504509
require(
505510
freeAllocatableMagnitude >= allocation.magnitudeDiffs[i],
506511
"AVSDirectory.queueAllocations: insufficient available free magnitude to allocate"
@@ -513,10 +518,9 @@ contract AVSDirectory is
513518
// a pending allocation
514519
if (value != 0 || pos != 0) {
515520
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"
520524
);
521525
}
522526

@@ -547,7 +551,7 @@ contract AVSDirectory is
547551
IStrategy strategy = deallocation.strategy;
548552
require(
549553
deallocation.operatorSets.length == deallocation.magnitudeDiffs.length,
550-
"AVSDirectory.queueDeallocation: operatorSets and magnitudeDiffs length mismatch"
554+
"AVSDirectory._queueDeallocate: operatorSets and magnitudeDiffs length mismatch"
551555
);
552556
OperatorSet[] calldata operatorSets = deallocation.operatorSets;
553557

@@ -558,9 +562,13 @@ contract AVSDirectory is
558562
_magnitudeUpdate[operator][strategy][operatorSets[i].avs][operatorSets[i].operatorSetId]
559563
.upperLookupRecent(uint32(block.timestamp))
560564
);
565+
require(
566+
deallocation.magnitudeDiffs[i] > 0,
567+
"AVSDirectory._queueDeallocate: magnitudeDiff must be greater than 0"
568+
);
561569
require(
562570
deallocation.magnitudeDiffs[i] <= currentMagnitude,
563-
"AVSDirectory.queueDeallocation: cannot deallocate more than what is allocated"
571+
"AVSDirectory._queueDeallocate: cannot deallocate more than what is allocated"
564572
);
565573

566574
// 2. update and decrement current and future queued amounts in case any pending allocations exist
@@ -570,10 +578,10 @@ contract AVSDirectory is
570578
decrementValue: deallocation.magnitudeDiffs[i]
571579
});
572580

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]);
575583

576-
// 3. queue deallocation for (op, opSet, strategy) for magnitudeDiff amount
584+
// 4. queue deallocation for (op, opSet, strategy) for magnitudeDiff amount
577585
_queuedDeallocations[operator][strategy][operatorSets[i].avs][operatorSets[i].operatorSetId].push(
578586
QueuedDeallocation({
579587
magnitudeDiff: deallocation.magnitudeDiffs[i],
@@ -611,6 +619,29 @@ contract AVSDirectory is
611619
return freeMagnitudeToAdd;
612620
}
613621

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+
614645
/**
615646
*
616647
* VIEW FUNCTIONS
@@ -681,7 +712,7 @@ contract AVSDirectory is
681712
function isOperatorSlashable(address operator, OperatorSet memory operatorSet) public view returns (bool) {
682713
OperatorSetRegistrationStatus memory registrationStatus =
683714
operatorSetStatus[operatorSet.avs][operator][operatorSet.operatorSetId];
684-
return registrationStatus.registered
715+
return isMember(operator, operatorSet)
685716
|| registrationStatus.lastDeregisteredTimestamp + ALLOCATION_DELAY >= block.timestamp;
686717
}
687718

src/contracts/core/AVSDirectoryStorage.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ abstract contract AVSDirectoryStorage is IAVSDirectory {
5454
/// @notice Mapping: operator => List of operator sets that operator is registered to.
5555
/// @dev Each item is formatted as such: bytes32(abi.encodePacked(avs, uint96(operatorSetId)))
5656
mapping(address => EnumerableSet.Bytes32Set) internal _operatorSetsMemberOf;
57+
5758
/// @notice Mapping: operator => avs => operatorSetId => operator registration status
5859
mapping(address => mapping(address => mapping(uint32 => OperatorSetRegistrationStatus))) public operatorSetStatus;
5960

0 commit comments

Comments
 (0)