Skip to content

Commit 4cd8b13

Browse files
committed
test: integration w/ multiple stakers
1 parent 5c30eb1 commit 4cd8b13

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed

src/test/integration/IntegrationBase.t.sol

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,23 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
6969
return (staker, strategies, tokenBalances);
7070
}
7171

72+
/// @dev creates `numNewStakers` new stakers with random strategies and token balances
73+
function _newRandomStakers(uint numNewStakers) internal returns (User[] memory, IStrategy[][] memory, uint[][] memory) {
74+
User[] memory stakers = new User[](numNewStakers);
75+
IStrategy[][] memory strategies = new IStrategy[][](numNewStakers);
76+
uint[][] memory tokenBalances = new uint[][](numNewStakers);
77+
78+
for (uint i = 0; i < numNewStakers; i++) {
79+
(stakers[i], strategies[i], tokenBalances[i]) = _newRandomStaker();
80+
}
81+
82+
numStakers += numNewStakers;
83+
84+
return (stakers, strategies, tokenBalances);
85+
}
86+
87+
/// @dev Exactly like _newRandomStaker above but sets strategies and tokenBalances arrays to length 1
88+
/// to use a single strategy and balance
7289
function _newBasicStaker() internal returns (User, IStrategy[] memory, uint[] memory) {
7390
string memory stakerName;
7491

@@ -2307,6 +2324,21 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
23072324
return expectedShares;
23082325
}
23092326

2327+
/// @dev For some strategies/underlying token balances, calculate the expected shares received
2328+
/// Exact same as _calculateExpectedShares but for multiple stakers
2329+
function _calculateExpectedShares(
2330+
IStrategy[][] memory strategies,
2331+
uint[][] memory tokenBalances
2332+
) internal returns (uint[][] memory) {
2333+
uint[][] memory expectedShares = new uint[][](strategies.length);
2334+
2335+
for (uint i = 0; i < strategies.length; i++) {
2336+
expectedShares[i] = _calculateExpectedShares(strategies[i], tokenBalances[i]);
2337+
}
2338+
2339+
return expectedShares;
2340+
}
2341+
23102342
/// @dev For some strategies/underlying token balances, calculate the expected shares received
23112343
/// from depositing all tokens
23122344
function _calculateExpectedTokens(IStrategy[] memory strategies, uint[] memory shares) internal returns (uint[] memory) {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import "src/test/integration/IntegrationChecks.t.sol";
5+
import "src/test/integration/users/User.t.sol";
6+
7+
import "src/test/integration/utils/MultiStakersLib.sol";
8+
9+
contract Integration_MultiStaker_Deposit_Delegate_Queue_Complete is IntegrationCheckUtils {
10+
using MultiStakersLib for User[];
11+
12+
/*******************************************************************************
13+
FULL WITHDRAWALS
14+
*******************************************************************************/
15+
16+
// TODO: fix test
17+
/// Generates multiple random stakers and an operator. The stakers:
18+
/// 1. deposits all assets into strategies
19+
/// 2. delegates to an operator
20+
/// 3. queues a withdrawal for ALL shares
21+
/// 4. completes the queued withdrawal as tokens
22+
function testFuzz_deposit_delegate_queue_completeAsTokens(uint24 _random) public rand(_random) {
23+
/// 0. Create multiple random stakers with:
24+
// - some nonzero underlying token balances
25+
// - corresponding to a random subset of valid strategies (StrategyManager and/or EigenPodManager)
26+
//
27+
// ... check that the staker has no delegatable shares and isn't currently delegated
28+
29+
(
30+
User[] memory stakers,
31+
IStrategy[][] memory strategiesPerStaker,
32+
uint[][] memory tokenBalancesPerStaker
33+
) = _newRandomStakers({numNewStakers: _randUint(1, 10)});
34+
(User operator, ,) = _newRandomOperator();
35+
36+
uint[][] memory sharesPerStaker = _calculateExpectedShares(strategiesPerStaker, tokenBalancesPerStaker);
37+
38+
// 1. Deposit Into Strategies
39+
stakers.depositIntoEigenlayer(strategiesPerStaker, tokenBalancesPerStaker);
40+
// check_Deposit_State(staker, strategies, shares);
41+
42+
// 2. Delegate to an operator
43+
stakers.delegateTo(operator);
44+
// check_Delegation_State(staker, operator, strategies, shares);
45+
46+
// 3. Queue Withdrawals
47+
Withdrawal[][] memory withdrawals = stakers.queueWithdrawals(strategiesPerStaker, sharesPerStaker);
48+
49+
// 4. Complete withdrawal
50+
_rollBlocksForCompleteWithdrawals(withdrawals[0]);
51+
stakers.completeWithdrawalsAsTokens(withdrawals);
52+
}
53+
}

src/test/integration/users/User.t.sol

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
3434
using SlashingLib for *;
3535
using ArrayLib for *;
3636
using print for *;
37+
using EnumerableSet for EnumerableSet.AddressSet;
3738

3839
IStrategy constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0);
3940

@@ -52,6 +53,10 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
5253

5354
string _NAME;
5455

56+
// User's delegated stakers if they are an operator
57+
// create an enumerableSet of stakers that have delegated to this user
58+
EnumerableSet.AddressSet internal delegatedStakers;
59+
5560
// User's EigenPod and each of their validator indices within that pod
5661
EigenPod public pod;
5762
uint40[] validators;
@@ -208,11 +213,16 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
208213
ISignatureUtilsMixinTypes.SignatureWithExpiry memory emptySig;
209214
delegationManager.delegateTo(address(operator), emptySig, bytes32(0));
210215
print.gasUsed();
216+
217+
// handle the delegating staker on the operator User contract
218+
// to manage the delegatedStakers enum set
219+
operator.handleDelegatingStakers(address(this));
211220
}
212221

213222
/// @dev Undelegate from operator
214223
function undelegate() public virtual createSnapshot returns (Withdrawal[] memory) {
215224
print.method("undelegate");
225+
User operator = User(payable(delegationManager.delegatedTo(address(this))));
216226

217227
Withdrawal[] memory expectedWithdrawals = _getExpectedWithdrawalStructsForStaker(address(this));
218228
_tryPrankAppointee_DelegationManager(IDelegationManager.undelegate.selector);
@@ -234,6 +244,10 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
234244
);
235245
}
236246

247+
// handle the undelegating staker on the operator User contract
248+
// to manage the delegatedStakers enum set
249+
operator.handleUndelegatingStaker(address(this));
250+
237251
return expectedWithdrawals;
238252
}
239253

@@ -242,6 +256,8 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
242256
User newOperator
243257
) public virtual createSnapshot returns (Withdrawal[] memory) {
244258
print.method("redelegate", newOperator.NAME_COLORED());
259+
User oldOperator = User(payable(delegationManager.delegatedTo(payable(address(this)))));
260+
245261
Withdrawal[] memory expectedWithdrawals = _getExpectedWithdrawalStructsForStaker(address(this));
246262
ISignatureUtilsMixinTypes.SignatureWithExpiry memory emptySig;
247263
_tryPrankAppointee_DelegationManager(IDelegationManager.redelegate.selector);
@@ -262,6 +278,12 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
262278
expectedWithdrawals[i].scaledShares[0]
263279
);
264280
}
281+
282+
// handle the redelegating staker on the operator User contract
283+
// to manage the delegatedStakers enum set
284+
oldOperator.handleUndelegatingStaker(address(this));
285+
newOperator.handleDelegatingStakers(address(this));
286+
265287
return expectedWithdrawals;
266288
}
267289

@@ -275,9 +297,35 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
275297
delegationManager.undelegate(address(staker));
276298
print.gasUsed();
277299

300+
// Operator is calling this function themselves
301+
// handle the undelegating staker on this contract to manage the delegatedStakers enum set
302+
handleUndelegatingStaker(address(staker));
303+
278304
return expectedWithdrawals;
279305
}
280306

307+
/// @dev NOTE: do NOT call this function directly. This is simply implemented to manage the
308+
/// delegatedStakers enum set. This function is only meant to be called on the Operator User contract.
309+
function handleDelegatingStakers(address staker) public virtual {
310+
// check that the current User is an operator
311+
require(
312+
delegationManager.isOperator(address(this)),
313+
"User is not an operator"
314+
);
315+
delegatedStakers.add(staker);
316+
}
317+
318+
/// @dev NOTE: do NOT call this function directly. This is simply implemented to manage the
319+
/// delegatedStakers enum set. This function is only meant to be called on the Operator User contract.
320+
function handleUndelegatingStaker(address staker) public virtual {
321+
// check that the current User is an operator
322+
require(
323+
delegationManager.isOperator(address(this)),
324+
"User is not an operator"
325+
);
326+
delegatedStakers.remove(staker);
327+
}
328+
281329
/// @dev Queues a single withdrawal for every share and strategy pair
282330
function queueWithdrawals(
283331
IStrategy[] memory strategies,
@@ -713,6 +761,15 @@ contract User is Logger, IDelegationManagerTypes, IAllocationManagerTypes {
713761
return activeValidators;
714762
}
715763

764+
function getDelegatedStakers() public view returns (User[] memory) {
765+
address[] memory stakers = delegatedStakers.values();
766+
User[] memory users = new User[](stakers.length);
767+
for (uint256 i = 0; i < stakers.length; i++) {
768+
users[i] = User(payable(stakers[i]));
769+
}
770+
return users;
771+
}
772+
716773
function _tryPrankAppointee(
717774
address target,
718775
bytes4 selector
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import "src/test/integration/users/User.t.sol";
5+
6+
7+
/// @notice Contract that provides utility functions to reuse staker actions in User.t.sol
8+
// for multiple stakers at once.
9+
library MultiStakersLib {
10+
11+
function depositIntoEigenlayer(
12+
User[] memory stakers,
13+
IStrategy[][] memory strategies,
14+
uint[][] memory tokenBalances
15+
) internal {
16+
for (uint i = 0; i < stakers.length; i++) {
17+
stakers[i].depositIntoEigenlayer(strategies[i], tokenBalances[i]);
18+
}
19+
}
20+
21+
function delegateTo(
22+
User[] memory stakers,
23+
User operator
24+
) internal {
25+
for (uint i = 0; i < stakers.length; i++) {
26+
stakers[i].delegateTo(operator);
27+
}
28+
}
29+
30+
function undelegate(
31+
User[] memory stakers
32+
) internal {
33+
for (uint i = 0; i < stakers.length; i++) {
34+
stakers[i].undelegate();
35+
}
36+
}
37+
38+
function redelegate(
39+
User[] memory stakers,
40+
User newOperator
41+
) internal {
42+
for (uint i = 0; i < stakers.length; i++) {
43+
stakers[i].redelegate(newOperator);
44+
}
45+
}
46+
47+
function queueWithdrawals(
48+
User[] memory stakers,
49+
IStrategy[][] memory strategies,
50+
uint[][] memory shares
51+
) internal returns (IDelegationManagerTypes.Withdrawal[][] memory withdrawals) {
52+
withdrawals = new IDelegationManagerTypes.Withdrawal[][](stakers.length);
53+
for (uint i = 0; i < stakers.length; i++) {
54+
withdrawals[i] = stakers[i].queueWithdrawals(strategies[i], shares[i]);
55+
}
56+
}
57+
58+
function completeWithdrawalsAsTokens(
59+
User[] memory stakers,
60+
IDelegationManagerTypes.Withdrawal[][] memory withdrawals
61+
) internal {
62+
for (uint i = 0; i < stakers.length; i++) {
63+
stakers[i].completeWithdrawalsAsTokens(withdrawals[i]);
64+
}
65+
}
66+
67+
function completeWithdrawalsAsShares(
68+
User[] memory stakers,
69+
IDelegationManagerTypes.Withdrawal[][] memory withdrawals
70+
) internal {
71+
for (uint i = 0; i < stakers.length; i++) {
72+
stakers[i].completeWithdrawalsAsShares(withdrawals[i]);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)