forked from Layr-Labs/eigenlayer-middleware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStakeRegistry.sol
809 lines (717 loc) · 30.7 KB
/
StakeRegistry.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import {IDelegationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol";
import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol";
import {IStakeRegistry, IStakeRegistryTypes} from "./interfaces/IStakeRegistry.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
/**
* @title A `Registry` that keeps track of stakes of operators for up to 256 quorums.
* Specifically, it keeps track of
* 1) The stake of each operator in all the quorums they are a part of for block ranges
* 2) The total stake of all operators in each quorum for block ranges
* 3) The minimum stake required to register for each quorum
* It allows an additional functionality (in addition to registering and deregistering) to update the stake of an operator.
* @author Layr Labs, Inc.
*/
contract StakeRegistry is StakeRegistryStorage {
using BitmapUtils for *;
modifier onlySlashingRegistryCoordinator() {
_checkSlashingRegistryCoordinator();
_;
}
modifier onlyCoordinatorOwner() {
_checkSlashingRegistryCoordinatorOwner();
_;
}
modifier quorumExists(
uint8 quorumNumber
) {
_checkQuorumExists(quorumNumber);
_;
}
constructor(
ISlashingRegistryCoordinator _slashingRegistryCoordinator,
IDelegationManager _delegationManager,
IAVSDirectory _avsDirectory,
IAllocationManager _allocationManager
)
StakeRegistryStorage(
_slashingRegistryCoordinator,
_delegationManager,
_avsDirectory,
_allocationManager
)
{}
/**
*
* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR
*
*/
/// @inheritdoc IStakeRegistry
function registerOperator(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlySlashingRegistryCoordinator returns (uint96[] memory, uint96[] memory) {
uint96[] memory currentStakes = new uint96[](quorumNumbers.length);
uint96[] memory totalStakes = new uint96[](quorumNumbers.length);
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
_checkQuorumExists(quorumNumber);
// Retrieve the operator's current weighted stake for the quorum, reverting if they have not met
// the minimum.
(uint96 currentStake, bool hasMinimumStake) =
_weightOfOperatorForQuorum(quorumNumber, operator);
require(hasMinimumStake, BelowMinimumStakeRequirement());
// Update the operator's stake
int256 stakeDelta = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
newStake: currentStake
});
// Update this quorum's total stake by applying the operator's delta
currentStakes[i] = currentStake;
totalStakes[i] = _recordTotalStakeUpdate(quorumNumber, stakeDelta);
}
return (currentStakes, totalStakes);
}
/// @inheritdoc IStakeRegistry
function deregisterOperator(
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlySlashingRegistryCoordinator {
/**
* For each quorum, remove the operator's stake for the quorum and update
* the quorum's total stake to account for the removal
*/
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
_checkQuorumExists(quorumNumber);
// Update the operator's stake for the quorum and retrieve the shares removed
int256 stakeDelta = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
newStake: 0
});
// Apply the operator's stake delta to the total stake for this quorum
_recordTotalStakeUpdate(quorumNumber, stakeDelta);
}
}
/// @inheritdoc IStakeRegistry
function updateOperatorStake(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) external onlySlashingRegistryCoordinator returns (uint192) {
uint192 quorumsToRemove;
/**
* For each quorum, update the operator's stake and record the delta
* in the quorum's total stake.
*
* If the operator no longer has the minimum stake required to be registered
* in the quorum, the quorum number is added to `quorumsToRemove`, which
* is returned to the registry coordinator.
*/
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
_checkQuorumExists(quorumNumber);
// Fetch the operator's current stake, applying weighting parameters and checking
// against the minimum stake requirements for the quorum.
(uint96 stakeWeight, bool hasMinimumStake) =
_weightOfOperatorForQuorum(quorumNumber, operator);
// If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal
/// also handle setting the operator's stake to 0 and remove them from the quorum
if (!hasMinimumStake) {
stakeWeight = 0;
quorumsToRemove = uint192(quorumsToRemove.setBit(quorumNumber));
}
// Update the operator's stake and retrieve the delta
// If we're deregistering them, their weight is set to 0
int256 stakeDelta = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
newStake: stakeWeight
});
// Apply the delta to the quorum's total stake
_recordTotalStakeUpdate(quorumNumber, stakeDelta);
}
return quorumsToRemove;
}
/// @inheritdoc IStakeRegistry
function initializeDelegatedStakeQuorum(
uint8 quorumNumber,
uint96 minimumStake,
StrategyParams[] memory _strategyParams
) public virtual onlySlashingRegistryCoordinator {
require(!_quorumExists(quorumNumber), QuorumAlreadyExists());
_addStrategyParams(quorumNumber, _strategyParams);
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
_setStakeType(quorumNumber, IStakeRegistryTypes.StakeType.TOTAL_DELEGATED);
_totalStakeHistory[quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: 0
})
);
}
/// @inheritdoc IStakeRegistry
function initializeSlashableStakeQuorum(
uint8 quorumNumber,
uint96 minimumStake,
uint32 lookAheadPeriod,
StrategyParams[] memory _strategyParams
) public virtual onlySlashingRegistryCoordinator {
require(!_quorumExists(quorumNumber), QuorumAlreadyExists());
_addStrategyParams(quorumNumber, _strategyParams);
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
_setStakeType(quorumNumber, IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE);
_setLookAheadPeriod(quorumNumber, lookAheadPeriod);
_totalStakeHistory[quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: 0
})
);
}
/// @inheritdoc IStakeRegistry
function setMinimumStakeForQuorum(
uint8 quorumNumber,
uint96 minimumStake
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
}
/// @inheritdoc IStakeRegistry
function setSlashableStakeLookahead(
uint8 quorumNumber,
uint32 _lookAheadBlocks
) external onlyCoordinatorOwner quorumExists(quorumNumber) {
_setLookAheadPeriod(quorumNumber, _lookAheadBlocks);
}
/// @inheritdoc IStakeRegistry
function addStrategies(
uint8 quorumNumber,
StrategyParams[] memory _strategyParams
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
_addStrategyParams(quorumNumber, _strategyParams);
uint256 numStratsToAdd = _strategyParams.length;
if (isOperatorSetQuorum(quorumNumber)) {
IStrategy[] memory strategiesToAdd = new IStrategy[](numStratsToAdd);
for (uint256 i = 0; i < numStratsToAdd; i++) {
strategiesToAdd[i] = _strategyParams[i].strategy;
}
allocationManager.addStrategiesToOperatorSet({
avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(),
operatorSetId: quorumNumber,
strategies: strategiesToAdd
});
}
}
/// @inheritdoc IStakeRegistry
function removeStrategies(
uint8 quorumNumber,
uint256[] memory indicesToRemove
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
uint256 toRemoveLength = indicesToRemove.length;
require(toRemoveLength > 0, InputArrayLengthZero());
StrategyParams[] storage _strategyParams = strategyParams[quorumNumber];
IStrategy[] storage _strategiesPerQuorum = strategiesPerQuorum[quorumNumber];
IStrategy[] memory _strategiesToRemove = new IStrategy[](toRemoveLength);
for (uint256 i = 0; i < toRemoveLength; i++) {
_strategiesToRemove[i] = _strategyParams[indicesToRemove[i]].strategy;
emit StrategyRemovedFromQuorum(
quorumNumber, _strategyParams[indicesToRemove[i]].strategy
);
emit StrategyMultiplierUpdated(
quorumNumber, _strategyParams[indicesToRemove[i]].strategy, 0
);
// Replace index to remove with the last item in the list, then pop the last item
_strategyParams[indicesToRemove[i]] = _strategyParams[_strategyParams.length - 1];
_strategyParams.pop();
_strategiesPerQuorum[indicesToRemove[i]] =
_strategiesPerQuorum[_strategiesPerQuorum.length - 1];
_strategiesPerQuorum.pop();
}
if (isOperatorSetQuorum(quorumNumber)) {
allocationManager.removeStrategiesFromOperatorSet({
avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(),
operatorSetId: quorumNumber,
strategies: _strategiesToRemove
});
}
}
/// @inheritdoc IStakeRegistry
function modifyStrategyParams(
uint8 quorumNumber,
uint256[] calldata strategyIndices,
uint96[] calldata newMultipliers
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
uint256 numStrats = strategyIndices.length;
require(numStrats > 0, InputArrayLengthZero());
require(newMultipliers.length == numStrats, InputArrayLengthMismatch());
StrategyParams[] storage _strategyParams = strategyParams[quorumNumber];
for (uint256 i = 0; i < numStrats; i++) {
// Change the strategy's associated multiplier
_strategyParams[strategyIndices[i]].multiplier = newMultipliers[i];
emit StrategyMultiplierUpdated(
quorumNumber, _strategyParams[strategyIndices[i]].strategy, newMultipliers[i]
);
}
}
/**
*
* INTERNAL FUNCTIONS
*
*/
function _getStakeUpdateIndexForOperatorAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) internal view returns (uint32) {
uint256 length = operatorStakeHistory[operatorId][quorumNumber].length;
// Iterate backwards through operatorStakeHistory until we find an update that preceeds blockNumber
for (uint256 i = length; i > 0; i--) {
if (
operatorStakeHistory[operatorId][quorumNumber][i - 1].updateBlockNumber
<= blockNumber
) {
return uint32(i - 1);
}
}
// If we hit this point, no stake update exists at blockNumber
revert(
"StakeRegistry._getStakeUpdateIndexForOperatorAtBlockNumber: no stake update found for operatorId and quorumNumber at block number"
);
}
function _setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) internal {
minimumStakeForQuorum[quorumNumber] = minimumStake;
emit MinimumStakeForQuorumUpdated(quorumNumber, minimumStake);
}
/**
* @notice Records that `operatorId`'s current stake for `quorumNumber` is now `newStake`
* @return The change in the operator's stake as a signed int256
*/
function _recordOperatorStakeUpdate(
bytes32 operatorId,
uint8 quorumNumber,
uint96 newStake
) internal returns (int256) {
uint96 prevStake;
uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length;
if (historyLength == 0) {
// No prior stake history - push our first entry
operatorStakeHistory[operatorId][quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: newStake
})
);
} else {
// We have prior stake history - fetch our last-recorded stake
StakeUpdate storage lastUpdate =
operatorStakeHistory[operatorId][quorumNumber][historyLength - 1];
prevStake = lastUpdate.stake;
// Short-circuit in case there's no change in stake
if (prevStake == newStake) {
return 0;
}
/**
* If our last stake entry was made in the current block, update the entry
* Otherwise, push a new entry and update the previous entry's "next" field
*/
if (lastUpdate.updateBlockNumber == uint32(block.number)) {
lastUpdate.stake = newStake;
} else {
lastUpdate.nextUpdateBlockNumber = uint32(block.number);
operatorStakeHistory[operatorId][quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: newStake
})
);
}
}
// Log update and return stake delta
emit OperatorStakeUpdate(operatorId, quorumNumber, newStake);
return _calculateDelta({prev: prevStake, cur: newStake});
}
/// @notice Applies a delta to the total stake recorded for `quorumNumber`
/// @return Returns the new total stake for the quorum
function _recordTotalStakeUpdate(
uint8 quorumNumber,
int256 stakeDelta
) internal returns (uint96) {
// Get our last-recorded stake update
uint256 historyLength = _totalStakeHistory[quorumNumber].length;
StakeUpdate storage lastStakeUpdate = _totalStakeHistory[quorumNumber][historyLength - 1];
// Return early if no update is needed
if (stakeDelta == 0) {
return lastStakeUpdate.stake;
}
// Calculate the new total stake by applying the delta to our previous stake
uint96 newStake = _applyDelta(lastStakeUpdate.stake, stakeDelta);
/**
* If our last stake entry was made in the current block, update the entry
* Otherwise, push a new entry and update the previous entry's "next" field
*/
if (lastStakeUpdate.updateBlockNumber == uint32(block.number)) {
lastStakeUpdate.stake = newStake;
} else {
lastStakeUpdate.nextUpdateBlockNumber = uint32(block.number);
_totalStakeHistory[quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: newStake
})
);
}
return newStake;
}
/**
* @notice Adds `strategyParams` to the `quorumNumber`-th quorum.
* @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies).
* @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a conscious choice,
* since a middleware may want, e.g., a stablecoin quorum that accepts USDC, USDT, DAI, etc. as underlying assets and trades them as "equivalent".
*/
function _addStrategyParams(
uint8 quorumNumber,
StrategyParams[] memory _strategyParams
) internal {
require(_strategyParams.length > 0, InputArrayLengthZero());
uint256 numStratsToAdd = _strategyParams.length;
uint256 numStratsExisting = strategyParams[quorumNumber].length;
require(
numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH,
InputArrayLengthMismatch()
);
for (uint256 i = 0; i < numStratsToAdd; i++) {
// fairly gas-expensive internal loop to make sure that the *same* strategy cannot be added multiple times
for (uint256 j = 0; j < (numStratsExisting + i); j++) {
require(
strategyParams[quorumNumber][j].strategy != _strategyParams[i].strategy,
InputDuplicateStrategy()
);
}
require(_strategyParams[i].multiplier > 0, InputMultiplierZero());
strategyParams[quorumNumber].push(_strategyParams[i]);
strategiesPerQuorum[quorumNumber].push(_strategyParams[i].strategy);
emit StrategyAddedToQuorum(quorumNumber, _strategyParams[i].strategy);
emit StrategyMultiplierUpdated(
quorumNumber, _strategyParams[i].strategy, _strategyParams[i].multiplier
);
}
}
/// @notice Returns the change between a previous and current value as a signed int
function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) {
return int256(uint256(cur)) - int256(uint256(prev));
}
/// @notice Adds or subtracts delta from value, according to its sign
function _applyDelta(uint96 value, int256 delta) internal pure returns (uint96) {
if (delta < 0) {
return value - uint96(uint256(-delta));
} else {
return value + uint96(uint256(delta));
}
}
/// @notice Checks that the `stakeUpdate` was valid at the given `blockNumber`
function _validateStakeUpdateAtBlockNumber(
StakeUpdate memory stakeUpdate,
uint32 blockNumber
) internal pure {
/**
* Check that the update is valid for the given blockNumber:
* - blockNumber should be >= the update block number
* - the next update block number should be either 0 or strictly greater than blockNumber
*/
require(blockNumber >= stakeUpdate.updateBlockNumber, InvalidBlockNumber());
require(
stakeUpdate.nextUpdateBlockNumber == 0
|| blockNumber < stakeUpdate.nextUpdateBlockNumber,
InvalidBlockNumber()
);
}
/// Returns total Slashable stake for an operator per strategy that can have the weights applied based on strategy multipliers
function _getSlashableStakePerStrategy(
uint8 quorumNumber,
address operator
) internal view returns (uint256[] memory) {
address[] memory operators = new address[](1);
operators[0] = operator;
uint32 beforeTimestamp =
uint32(block.number + slashableStakeLookAheadPerQuorum[quorumNumber]);
uint256[][] memory slashableShares = allocationManager.getMinimumSlashableStake(
OperatorSet(
ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), quorumNumber
),
operators,
strategiesPerQuorum[quorumNumber],
beforeTimestamp
);
return slashableShares[0];
}
/**
* @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber.
* @dev this method DOES NOT check that the quorum exists
* @return `uint96` The weighted sum of the operator's shares across each strategy considered by the quorum
* @return `bool` True if the operator meets the quorum's minimum stake
*/
function _weightOfOperatorForQuorum(
uint8 quorumNumber,
address operator
) internal view virtual returns (uint96, bool) {
uint96 weight;
uint256 stratsLength = strategyParamsLength(quorumNumber);
StrategyParams memory strategyAndMultiplier;
uint256[] memory strategyShares;
if (stakeTypePerQuorum[quorumNumber] == IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE) {
strategyShares = _getSlashableStakePerStrategy(quorumNumber, operator);
for (uint256 i = 0; i < stratsLength; i++) {
strategyAndMultiplier = strategyParams[quorumNumber][i];
if (strategyShares[i] > 0) {
weight += uint96(
strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR
);
}
}
} else {
/// M2 Concept of delegated stake
strategyShares =
delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]);
for (uint256 i = 0; i < stratsLength; i++) {
// accessing i^th StrategyParams struct for the quorumNumber
strategyAndMultiplier = strategyParams[quorumNumber][i];
// add the weight from the shares for this strategy to the total weight
if (strategyShares[i] > 0) {
weight += uint96(
strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR
);
}
}
}
// Return the weight, and `true` if the operator meets the quorum's minimum stake
bool hasMinimumStake = weight >= minimumStakeForQuorum[quorumNumber];
return (weight, hasMinimumStake);
}
/// @notice Returns `true` if the quorum has been initialized
function _quorumExists(
uint8 quorumNumber
) internal view returns (bool) {
return _totalStakeHistory[quorumNumber].length != 0;
}
/**
*
* VIEW FUNCTIONS
*
*/
/// @inheritdoc IStakeRegistry
function isOperatorSetQuorum(
uint8 quorumNumber
) public view returns (bool) {
bool isM2 = ISlashingRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber);
bool isOperatorSet = ISlashingRegistryCoordinator(registryCoordinator).operatorSetsEnabled();
return isOperatorSet && !isM2;
}
/// @inheritdoc IStakeRegistry
function weightOfOperatorForQuorum(
uint8 quorumNumber,
address operator
) public view virtual quorumExists(quorumNumber) returns (uint96) {
(uint96 stake,) = _weightOfOperatorForQuorum(quorumNumber, operator);
return stake;
}
/// @inheritdoc IStakeRegistry
function strategyParamsLength(
uint8 quorumNumber
) public view returns (uint256) {
return strategyParams[quorumNumber].length;
}
/// @inheritdoc IStakeRegistry
function strategyParamsByIndex(
uint8 quorumNumber,
uint256 index
) public view returns (StrategyParams memory) {
return strategyParams[quorumNumber][index];
}
/**
*
* VIEW FUNCTIONS - Operator Stake History
*
*/
/// @inheritdoc IStakeRegistry
function getStakeHistoryLength(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (uint256) {
return operatorStakeHistory[operatorId][quorumNumber].length;
}
/// @inheritdoc IStakeRegistry
function getStakeHistory(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (StakeUpdate[] memory) {
return operatorStakeHistory[operatorId][quorumNumber];
}
/// @inheritdoc IStakeRegistry
function getCurrentStake(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (uint96) {
StakeUpdate memory operatorStakeUpdate = getLatestStakeUpdate(operatorId, quorumNumber);
return operatorStakeUpdate.stake;
}
/// @inheritdoc IStakeRegistry
function getLatestStakeUpdate(
bytes32 operatorId,
uint8 quorumNumber
) public view returns (StakeUpdate memory) {
uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length;
StakeUpdate memory operatorStakeUpdate;
if (historyLength == 0) {
return operatorStakeUpdate;
} else {
operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength - 1];
return operatorStakeUpdate;
}
}
/// @inheritdoc IStakeRegistry
function getStakeUpdateAtIndex(
uint8 quorumNumber,
bytes32 operatorId,
uint256 index
) external view returns (StakeUpdate memory) {
return operatorStakeHistory[operatorId][quorumNumber][index];
}
/// @inheritdoc IStakeRegistry
function getStakeAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint96) {
return operatorStakeHistory[operatorId][quorumNumber][_getStakeUpdateIndexForOperatorAtBlockNumber(
operatorId, quorumNumber, blockNumber
)].stake;
}
/// @inheritdoc IStakeRegistry
function getStakeUpdateIndexAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint32) {
return _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber);
}
/// @inheritdoc IStakeRegistry
function getStakeAtBlockNumberAndIndex(
uint8 quorumNumber,
uint32 blockNumber,
bytes32 operatorId,
uint256 index
) external view returns (uint96) {
StakeUpdate memory operatorStakeUpdate =
operatorStakeHistory[operatorId][quorumNumber][index];
_validateStakeUpdateAtBlockNumber(operatorStakeUpdate, blockNumber);
return operatorStakeUpdate.stake;
}
/**
*
* VIEW FUNCTIONS - Total Stake History
*
*/
/// @inheritdoc IStakeRegistry
function getTotalStakeHistoryLength(
uint8 quorumNumber
) external view returns (uint256) {
return _totalStakeHistory[quorumNumber].length;
}
/// @inheritdoc IStakeRegistry
function getCurrentTotalStake(
uint8 quorumNumber
) external view returns (uint96) {
return _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake;
}
/// @inheritdoc IStakeRegistry
function getTotalStakeUpdateAtIndex(
uint8 quorumNumber,
uint256 index
) external view returns (StakeUpdate memory) {
return _totalStakeHistory[quorumNumber][index];
}
/// @inheritdoc IStakeRegistry
function getTotalStakeAtBlockNumberFromIndex(
uint8 quorumNumber,
uint32 blockNumber,
uint256 index
) external view returns (uint96) {
StakeUpdate memory totalStakeUpdate = _totalStakeHistory[quorumNumber][index];
_validateStakeUpdateAtBlockNumber(totalStakeUpdate, blockNumber);
return totalStakeUpdate.stake;
}
/// @inheritdoc IStakeRegistry
function getTotalStakeIndicesAtBlockNumber(
uint32 blockNumber,
bytes calldata quorumNumbers
) external view returns (uint32[] memory) {
uint32[] memory indices = new uint32[](quorumNumbers.length);
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
_checkQuorumExists(quorumNumber);
require(
_totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber,
EmptyStakeHistory()
);
uint256 length = _totalStakeHistory[quorumNumber].length;
for (uint256 j = 0; j < length; j++) {
if (
_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber
<= blockNumber
) {
indices[i] = uint32(length - j - 1);
break;
}
}
}
return indices;
}
/**
* @notice Sets the stake type for the registry for a specific quorum
* @param quorumNumber The quorum number to set the stake type for
* @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH)
*/
function _setStakeType(uint8 quorumNumber, IStakeRegistryTypes.StakeType _stakeType) internal {
stakeTypePerQuorum[quorumNumber] = _stakeType;
emit StakeTypeSet(_stakeType);
}
/**
* @notice Sets the look ahead time for checking operator shares for a specific quorum
* @param quorumNumber The quorum number to set the look ahead period for
* @param _lookAheadBlocks The number of blocks to look ahead when checking shares
*/
function _setLookAheadPeriod(uint8 quorumNumber, uint32 _lookAheadBlocks) internal {
uint32 oldLookAheadDays = slashableStakeLookAheadPerQuorum[quorumNumber];
slashableStakeLookAheadPerQuorum[quorumNumber] = _lookAheadBlocks;
emit LookAheadPeriodChanged(oldLookAheadDays, _lookAheadBlocks);
}
function _checkSlashingRegistryCoordinator() internal view {
require(msg.sender == registryCoordinator, OnlySlashingRegistryCoordinator());
}
function _checkSlashingRegistryCoordinatorOwner() internal view {
require(
msg.sender == ISlashingRegistryCoordinator(registryCoordinator).owner(),
OnlySlashingRegistryCoordinatorOwner()
);
}
function _checkQuorumExists(
uint8 quorumNumber
) internal view {
require(_quorumExists(quorumNumber), QuorumDoesNotExist());
}
}