-
Notifications
You must be signed in to change notification settings - Fork 96
/
Copy pathVoteWeigherBase.sol
226 lines (203 loc) · 11.2 KB
/
VoteWeigherBase.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
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import "./VoteWeigherBaseStorage.sol";
/**
* @title A simple implementation of the `IVoteWeigher` interface.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice This contract is used for
* - computing the total weight of an operator for any of the quorums that are considered
* by the middleware
* - addition and removal of strategies and the associated weighting criteria that are assigned
* by the middleware for each of the quorum(s)
*/
contract VoteWeigherBase is VoteWeigherBaseStorage {
/// @notice when applied to a function, ensures that the function is only callable by the current `owner` of the `serviceManager`
modifier onlyServiceManagerOwner() {
require(msg.sender == serviceManager.owner(), "VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager");
_;
}
/// @notice when applied to a function, ensures that the `quorumNumber` corresponds to a valid quorum added to the VoteWeigher
modifier validQuorumNumber(uint8 quorumNumber) {
require(quorumNumber < quorumCount, "VoteWeigherBase.validQuorumNumber: quorumNumber is not valid");
_;
}
/// @notice Sets the (immutable) `strategyManager` and `serviceManager` addresses
constructor(
IStrategyManager _strategyManager,
IServiceManager _serviceManager
) VoteWeigherBaseStorage(_strategyManager, _serviceManager) {}
/*******************************************************************************
EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER
*******************************************************************************/
/// @notice Create a new quorum and add the strategies and their associated weights to the quorum.
function createQuorum(
StrategyAndWeightingMultiplier[] memory _strategiesConsideredAndMultipliers
) external virtual onlyServiceManagerOwner {
_createQuorum(_strategiesConsideredAndMultipliers);
}
/// @notice Adds new strategies and the associated multipliers to the @param quorumNumber.
function addStrategiesConsideredAndMultipliers(
uint8 quorumNumber,
StrategyAndWeightingMultiplier[] memory _newStrategiesConsideredAndMultipliers
) external virtual onlyServiceManagerOwner validQuorumNumber(quorumNumber) {
_addStrategiesConsideredAndMultipliers(quorumNumber, _newStrategiesConsideredAndMultipliers);
}
/**
* @notice This function is used for removing strategies and their associated weights from the
* mapping strategiesConsideredAndMultipliers for a specific @param quorumNumber.
* @dev higher indices should be *first* in the list of @param indicesToRemove, since otherwise
* the removal of lower index entries will cause a shift in the indices of the other strategiesToRemove
*/
function removeStrategiesConsideredAndMultipliers(
uint8 quorumNumber,
uint256[] calldata indicesToRemove
) external virtual onlyServiceManagerOwner validQuorumNumber(quorumNumber) {
uint256 indicesToRemoveLength = indicesToRemove.length;
require(indicesToRemoveLength > 0, "VoteWeigherBase.removeStrategiesConsideredAndMultipliers: no indices to remove provided");
for (uint256 i = 0; i < indicesToRemoveLength;) {
emit StrategyRemovedFromQuorum(quorumNumber, strategiesConsideredAndMultipliers[quorumNumber][indicesToRemove[i]].strategy);
emit StrategyMultiplierUpdated(quorumNumber, strategiesConsideredAndMultipliers[quorumNumber][indicesToRemove[i]].strategy, 0);
// remove strategy and its associated multiplier
strategiesConsideredAndMultipliers[quorumNumber][indicesToRemove[i]] = strategiesConsideredAndMultipliers[
quorumNumber
][strategiesConsideredAndMultipliers[quorumNumber].length - 1];
strategiesConsideredAndMultipliers[quorumNumber].pop();
unchecked {
++i;
}
}
}
/**
* @notice This function is used for modifying the weights of strategies that are already in the
* mapping strategiesConsideredAndMultipliers for a specific
* @param quorumNumber is the quorum number to change the strategy for
* @param strategyIndices are the indices of the strategies to change
* @param newMultipliers are the new multipliers for the strategies
*/
function modifyStrategyWeights(
uint8 quorumNumber,
uint256[] calldata strategyIndices,
uint96[] calldata newMultipliers
) external virtual onlyServiceManagerOwner validQuorumNumber(quorumNumber) {
uint256 numStrats = strategyIndices.length;
require(numStrats > 0, "VoteWeigherBase.modifyStrategyWeights: no strategy indices provided");
// sanity check on input lengths
require(newMultipliers.length == numStrats, "VoteWeigherBase.modifyStrategyWeights: input length mismatch");
for (uint256 i = 0; i < numStrats; ) {
// change the strategy's associated multiplier
strategiesConsideredAndMultipliers[quorumNumber][strategyIndices[i]].multiplier = newMultipliers[i];
emit StrategyMultiplierUpdated(quorumNumber, strategiesConsideredAndMultipliers[quorumNumber][strategyIndices[i]].strategy, newMultipliers[i]);
unchecked {
++i;
}
}
}
/*******************************************************************************
INTERNAL FUNCTIONS
*******************************************************************************/
/**
* @notice Creates a quorum with the given_strategiesConsideredAndMultipliers.
*/
function _createQuorum(
StrategyAndWeightingMultiplier[] memory _strategiesConsideredAndMultipliers
) internal {
uint16 quorumCountMem = quorumCount;
require(quorumCountMem < MAX_QUORUM_COUNT, "VoteWeigherBase._createQuorum: number of quorums cannot exceed MAX_QUORUM_COUNT");
uint8 quorumNumber = uint8(quorumCountMem);
// increment quorumCount
quorumCount = quorumCountMem + 1;
// add the strategies and their associated weights to the quorum
_addStrategiesConsideredAndMultipliers(quorumNumber, _strategiesConsideredAndMultipliers);
// emit event
emit QuorumCreated(quorumNumber);
}
/**
* @notice Adds `_newStrategiesConsideredAndMultipliers` 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 concious 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 _addStrategiesConsideredAndMultipliers(
uint8 quorumNumber,
StrategyAndWeightingMultiplier[] memory _newStrategiesConsideredAndMultipliers
) internal {
require(_newStrategiesConsideredAndMultipliers.length > 0, "VoteWeigherBase._addStrategiesConsideredAndMultipliers: no strategies provided");
uint256 numStratsToAdd = _newStrategiesConsideredAndMultipliers.length;
uint256 numStratsExisting = strategiesConsideredAndMultipliers[quorumNumber].length;
require(
numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH,
"VoteWeigherBase._addStrategiesConsideredAndMultipliers: exceed MAX_WEIGHING_FUNCTION_LENGTH"
);
for (uint256 i = 0; i < numStratsToAdd; ) {
// fairly gas-expensive internal loop to make sure that the *same* strategy cannot be added multiple times
for (uint256 j = 0; j < (numStratsExisting + i); ) {
require(
strategiesConsideredAndMultipliers[quorumNumber][j].strategy !=
_newStrategiesConsideredAndMultipliers[i].strategy,
"VoteWeigherBase._addStrategiesConsideredAndMultipliers: cannot add same strategy 2x"
);
unchecked {
++j;
}
}
require(
_newStrategiesConsideredAndMultipliers[i].multiplier > 0,
"VoteWeigherBase._addStrategiesConsideredAndMultipliers: cannot add strategy with zero weight"
);
strategiesConsideredAndMultipliers[quorumNumber].push(_newStrategiesConsideredAndMultipliers[i]);
emit StrategyAddedToQuorum(quorumNumber, _newStrategiesConsideredAndMultipliers[i].strategy);
emit StrategyMultiplierUpdated(
quorumNumber,
_newStrategiesConsideredAndMultipliers[i].strategy,
_newStrategiesConsideredAndMultipliers[i].multiplier
);
unchecked {
++i;
}
}
}
/*******************************************************************************
VIEW FUNCTIONS
*******************************************************************************/
/// @notice Returns the length of the dynamic array stored in `strategiesConsideredAndMultipliers[quorumNumber]`.
function strategiesConsideredAndMultipliersLength(uint8 quorumNumber) public view returns (uint256) {
return strategiesConsideredAndMultipliers[quorumNumber].length;
}
/// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber`
function strategyAndWeightingMultiplierForQuorumByIndex(
uint8 quorumNumber,
uint256 index
) public view returns (StrategyAndWeightingMultiplier memory)
{
return strategiesConsideredAndMultipliers[quorumNumber][index];
}
/**
* @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber.
* @dev reverts in the case that `quorumNumber` is greater than or equal to `quorumCount`
*/
function weightOfOperatorForQuorum(
uint8 quorumNumber,
address operator
) public virtual view validQuorumNumber(quorumNumber) returns (uint96) {
uint96 weight;
uint256 stratsLength = strategiesConsideredAndMultipliersLength(quorumNumber);
StrategyAndWeightingMultiplier memory strategyAndMultiplier;
for (uint256 i = 0; i < stratsLength;) {
// accessing i^th StrategyAndWeightingMultiplier struct for the quorumNumber
strategyAndMultiplier = strategiesConsideredAndMultipliers[quorumNumber][i];
// shares of the operator in the strategy
uint256 sharesAmount = delegation.operatorShares(operator, strategyAndMultiplier.strategy);
// add the weight from the shares for this strategy to the total weight
if (sharesAmount > 0) {
weight += uint96(sharesAmount * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR);
}
unchecked {
++i;
}
}
return weight;
}
}