Skip to content

Commit 71e82ee

Browse files
chore: fix tests to work with modified behavior (#378)
* chore: fix tests to work with modified behavior integration tests in particular are now slightly more flexible * fix: remove memory overwrite * docs: update dmgr docs --------- Co-authored-by: wadealexc <[email protected]>
1 parent fb73721 commit 71e82ee

8 files changed

+102
-62
lines changed

docs/core/DelegationManager.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -229,24 +229,24 @@ function undelegate(
229229
)
230230
external
231231
onlyWhenNotPaused(PAUSED_ENTER_WITHDRAWAL_QUEUE)
232-
returns (bytes32 withdrawalRoot)
232+
returns (bytes32[] memory withdrawalRoots)
233233
```
234234

235-
`undelegate` can be called by a Staker to undelegate themselves, or by a Staker's delegated Operator (or that Operator's `delegationApprover`). Undelegation (i) queues a withdrawal on behalf of the Staker for all their delegated shares, and (ii) decreases the Operator's delegated shares according to the amounts and strategies being withdrawn.
235+
`undelegate` can be called by a Staker to undelegate themselves, or by a Staker's delegated Operator (or that Operator's `delegationApprover`). Undelegation (i) queues withdrawals on behalf of the Staker for all their delegated shares, and (ii) decreases the Operator's delegated shares according to the amounts and strategies being withdrawn.
236236

237-
If the Staker has active shares in either the `EigenPodManager` or `StrategyManager`, they are removed while the withdrawal is in the queue.
237+
If the Staker has active shares in either the `EigenPodManager` or `StrategyManager`, they are removed while the withdrawal is in the queue - and an individual withdrawal is queued for each strategy removed.
238238

239-
The withdrawal can be completed by the Staker after `withdrawalDelayBlocks`, and does not require the Staker to "fully exit" from the system -- the Staker may choose to receive their shares back in full once the withdrawal is completed (see [`completeQueuedWithdrawal`](#completequeuedwithdrawal) for details).
239+
The withdrawals can be completed by the Staker after `withdrawalDelayBlocks`. This does not require the Staker to "fully exit" from the system -- the Staker may choose to receive their shares back in full once withdrawals are completed (see [`completeQueuedWithdrawal`](#completequeuedwithdrawal) for details).
240240

241241
Note that becoming an Operator is irreversible! Although Operators can withdraw, they cannot use this method to undelegate from themselves.
242242

243243
*Effects*:
244244
* Any shares held by the Staker in the `EigenPodManager` and `StrategyManager` are removed from the Operator's delegated shares.
245245
* The Staker is undelegated from the Operator
246246
* If the Staker has no delegatable shares, there is no withdrawal queued or further effects
247-
* A `Withdrawal` is queued for the Staker, tracking the strategies and shares being withdrawn
248-
* The Staker's withdrawal nonce is increased
249-
* The hash of the `Withdrawal` is marked as "pending"
247+
* For each strategy being withdrawn, a `Withdrawal` is queued for the Staker:
248+
* The Staker's withdrawal nonce is increased by 1 for each `Withdrawal`
249+
* The hash of each `Withdrawal` is marked as "pending"
250250
* See [`EigenPodManager.removeShares`](./EigenPodManager.md#eigenpodmanagerremoveshares)
251251
* See [`StrategyManager.removeShares`](./StrategyManager.md#removeshares)
252252

src/contracts/core/DelegationManager.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,12 @@ contract DelegationManager is Initializable, OwnableUpgradeable, Pausable, Deleg
242242
emit StakerUndelegated(staker, operator);
243243
delegatedTo[staker] = address(0);
244244

245-
// if no delegatable shares, return zero root, and don't queue a withdrawal
245+
// if no delegatable shares, return an empty array, and don't queue a withdrawal
246246
if (strategies.length == 0) {
247247
withdrawalRoots = new bytes32[](0);
248248
} else {
249249
withdrawalRoots = new bytes32[](strategies.length);
250-
for (uint i = 0; i < strategies.length; i++) {
250+
for (uint256 i = 0; i < strategies.length; i++) {
251251
IStrategy[] memory singleStrategy = new IStrategy[](1);
252252
uint256[] memory singleShare = new uint256[](1);
253253
singleStrategy[0] = strategies[i];

src/test/integration/IntegrationChecks.t.sol

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,16 @@ contract IntegrationCheckUtils is IntegrationBase {
5252
// ... check that each withdrawal was successfully enqueued, that the returned roots
5353
// match the hashes of each withdrawal, and that the staker and operator have
5454
// reduced shares.
55-
assert_AllWithdrawalsPending(withdrawalRoots, "staker withdrawals should now be pending");
56-
assert_ValidWithdrawalHashes(withdrawals, withdrawalRoots, "calculated withdrawals should match returned roots");
57-
assert_Snap_Added_QueuedWithdrawals(staker, withdrawals, "staker should have increased nonce by withdrawals.length");
58-
assert_Snap_Removed_OperatorShares(operator, strategies, shares, "failed to remove operator shares");
59-
assert_Snap_Removed_StakerShares(staker, strategies, shares, "failed to remove staker shares");
55+
assert_AllWithdrawalsPending(withdrawalRoots,
56+
"check_QueuedWithdrawal_State: staker withdrawals should now be pending");
57+
assert_ValidWithdrawalHashes(withdrawals, withdrawalRoots,
58+
"check_QueuedWithdrawal_State: calculated withdrawals should match returned roots");
59+
assert_Snap_Added_QueuedWithdrawals(staker, withdrawals,
60+
"check_QueuedWithdrawal_State: staker should have increased nonce by withdrawals.length");
61+
assert_Snap_Removed_OperatorShares(operator, strategies, shares,
62+
"check_QueuedWithdrawal_State: failed to remove operator shares");
63+
assert_Snap_Removed_StakerShares(staker, strategies, shares,
64+
"check_QueuedWithdrawal_State: failed to remove staker shares");
6065
}
6166

6267
function check_Undelegate_State(
@@ -72,13 +77,18 @@ contract IntegrationCheckUtils is IntegrationBase {
7277
// ... check that the staker is undelegated, all strategies from which the staker is deposited are unqeuued,
7378
// that the returned root matches the hashes for each strategy and share amounts, and that the staker
7479
// and operator have reduced shares
75-
assertEq(withdrawalRoots.length, 1, "should only be one withdrawal root");
76-
assertFalse(delegationManager.isDelegated(address(staker)), "staker should not be delegated");
77-
assert_ValidWithdrawalHashes(withdrawals, withdrawalRoots, "calculated withdrawl should match returned root");
78-
assert_AllWithdrawalsPending(withdrawalRoots, "stakers withdrawal should now be pending");
79-
assert_Snap_Added_QueuedWithdrawals(staker, withdrawals, "staker should have increased nonce by 1");
80-
assert_Snap_Removed_OperatorShares(operator, strategies, shares, "failed to remove operator shares");
81-
assert_Snap_Removed_StakerShares(staker, strategies, shares, "failed to remove staker shares");
80+
assertFalse(delegationManager.isDelegated(address(staker)),
81+
"check_Undelegate_State: staker should not be delegated");
82+
assert_ValidWithdrawalHashes(withdrawals, withdrawalRoots,
83+
"check_Undelegate_State: calculated withdrawl should match returned root");
84+
assert_AllWithdrawalsPending(withdrawalRoots,
85+
"check_Undelegate_State: stakers withdrawal should now be pending");
86+
assert_Snap_Added_QueuedWithdrawals(staker, withdrawals,
87+
"check_Undelegate_State: staker should have increased nonce by withdrawals.length");
88+
assert_Snap_Removed_OperatorShares(operator, strategies, shares,
89+
"check_Undelegate_State: failed to remove operator shares");
90+
assert_Snap_Removed_StakerShares(staker, strategies, shares,
91+
"check_Undelegate_State: failed to remove staker shares");
8292
}
8393

8494
function check_Withdrawal_AsTokens_State(

src/test/integration/User.t.sol

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -171,20 +171,26 @@ contract User is Test {
171171
function undelegate() public createSnapshot virtual returns(IDelegationManager.Withdrawal[] memory){
172172
emit log(_name(".undelegate"));
173173

174-
IDelegationManager.Withdrawal[] memory withdrawal = new IDelegationManager.Withdrawal[](1);
175-
withdrawal[0] = _getExpectedWithdrawalStructForStaker(address(this));
174+
IDelegationManager.Withdrawal[] memory expectedWithdrawals = _getExpectedWithdrawalStructsForStaker(address(this));
176175
delegationManager.undelegate(address(this));
177-
return withdrawal;
176+
177+
for (uint i = 0; i < expectedWithdrawals.length; i++) {
178+
emit log("expecting withdrawal:");
179+
emit log_named_uint("nonce: ", expectedWithdrawals[i].nonce);
180+
emit log_named_address("strat: ", address(expectedWithdrawals[i].strategies[0]));
181+
emit log_named_uint("shares: ", expectedWithdrawals[i].shares[0]);
182+
}
183+
184+
return expectedWithdrawals;
178185
}
179186

180187
/// @dev Force undelegate staker
181188
function forceUndelegate(User staker) public createSnapshot virtual returns(IDelegationManager.Withdrawal[] memory){
182189
emit log_named_string(_name(".forceUndelegate: "), staker.NAME());
183190

184-
IDelegationManager.Withdrawal[] memory withdrawal = new IDelegationManager.Withdrawal[](1);
185-
withdrawal[0] = _getExpectedWithdrawalStructForStaker(address(staker));
191+
IDelegationManager.Withdrawal[] memory expectedWithdrawals = _getExpectedWithdrawalStructsForStaker(address(staker));
186192
delegationManager.undelegate(address(staker));
187-
return withdrawal;
193+
return expectedWithdrawals;
188194
}
189195

190196
/// @dev Queues a single withdrawal for every share and strategy pair
@@ -317,20 +323,33 @@ contract User is Test {
317323
return abi.encodePacked(bytes1(uint8(1)), bytes11(0), address(pod));
318324
}
319325

326+
/// @notice Gets the expected withdrawals to be created when the staker is undelegated via a call to `DelegationManager.undelegate()`
320327
/// @notice Assumes staker and withdrawer are the same and that all strategies and shares are withdrawn
321-
function _getExpectedWithdrawalStructForStaker(address staker) internal view returns (IDelegationManager.Withdrawal memory) {
322-
(IStrategy[] memory strategies, uint[] memory shares)
328+
function _getExpectedWithdrawalStructsForStaker(address staker) internal returns (IDelegationManager.Withdrawal[] memory) {
329+
(IStrategy[] memory strategies, uint256[] memory shares)
323330
= delegationManager.getDelegatableShares(staker);
324331

325-
return IDelegationManager.Withdrawal({
326-
staker: staker,
327-
delegatedTo: delegationManager.delegatedTo(staker),
328-
withdrawer: staker,
329-
nonce: delegationManager.cumulativeWithdrawalsQueued(staker),
330-
startBlock: uint32(block.number),
331-
strategies: strategies,
332-
shares: shares
333-
});
332+
IDelegationManager.Withdrawal[] memory expectedWithdrawals = new IDelegationManager.Withdrawal[](strategies.length);
333+
address delegatedTo = delegationManager.delegatedTo(staker);
334+
uint256 nonce = delegationManager.cumulativeWithdrawalsQueued(staker);
335+
336+
for (uint256 i = 0; i < strategies.length; ++i) {
337+
IStrategy[] memory singleStrategy = new IStrategy[](1);
338+
uint256[] memory singleShares = new uint256[](1);
339+
singleStrategy[0] = strategies[i];
340+
singleShares[0] = shares[i];
341+
expectedWithdrawals[i] = IDelegationManager.Withdrawal({
342+
staker: staker,
343+
delegatedTo: delegatedTo,
344+
withdrawer: staker,
345+
nonce: (nonce + i),
346+
startBlock: uint32(block.number),
347+
strategies: singleStrategy,
348+
shares: singleShares
349+
});
350+
}
351+
352+
return expectedWithdrawals;
334353
}
335354

336355
function _name(string memory s) internal view returns (string memory) {

src/test/integration/tests/Deposit_Delegate_Queue_Complete.t.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ contract Integration_Deposit_Delegate_Queue_Complete is IntegrationCheckUtils {
5656
// Fast forward to when we can complete the withdrawal
5757
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
5858

59-
for (uint i = 0; i < withdrawals.length; i++) {
60-
uint[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].shares);
59+
for (uint256 i = 0; i < withdrawals.length; i++) {
60+
uint256[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].shares);
6161
IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]);
6262
check_Withdrawal_AsTokens_State(staker, operator, withdrawals[i], strategies, shares, tokens, expectedTokens);
6363
}
@@ -115,7 +115,7 @@ contract Integration_Deposit_Delegate_Queue_Complete is IntegrationCheckUtils {
115115
// Fast forward to when we can complete the withdrawal
116116
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
117117

118-
for (uint i = 0; i < withdrawals.length; i++) {
118+
for (uint256 i = 0; i < withdrawals.length; i++) {
119119
staker.completeWithdrawalAsShares(withdrawals[i]);
120120
check_Withdrawal_AsShares_State(staker, operator, withdrawals[i], strategies, shares);
121121
}
@@ -182,8 +182,8 @@ contract Integration_Deposit_Delegate_Queue_Complete is IntegrationCheckUtils {
182182
// 4. Complete withdrawals
183183
// Fast forward to when we can complete the withdrawal
184184
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
185-
for (uint i = 0; i < withdrawals.length; i++) {
186-
uint[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].shares);
185+
for (uint256 i = 0; i < withdrawals.length; i++) {
186+
uint256[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].shares);
187187
IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]);
188188
check_Withdrawal_AsTokens_State(staker, operator, withdrawals[i], withdrawStrats, withdrawShares, tokens, expectedTokens);
189189
}

src/test/integration/tests/Deposit_Delegate_Redelegate_Complete.t.sol

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ contract Integration_Deposit_Delegate_Redelegate_Complete is IntegrationCheckUti
5555
// 4. Complete withdrawal as shares
5656
// Fast forward to when we can complete the withdrawal
5757
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
58-
staker.completeWithdrawalAsShares(withdrawals[0]);
59-
check_Withdrawal_AsShares_Undelegated_State(staker, operator1, withdrawals[0], strategies, shares);
58+
for (uint256 i = 0; i < withdrawals.length; ++i) {
59+
staker.completeWithdrawalAsShares(withdrawals[i]);
60+
check_Withdrawal_AsShares_Undelegated_State(staker, operator1, withdrawals[i], withdrawals[i].strategies, withdrawals[i].shares);
61+
}
6062

6163
// 5. Delegate to a new operator
6264
staker.delegateTo(operator2);
@@ -76,7 +78,7 @@ contract Integration_Deposit_Delegate_Redelegate_Complete is IntegrationCheckUti
7678
for (uint i = 0; i < withdrawals.length; i++) {
7779
uint[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].shares);
7880
IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]);
79-
check_Withdrawal_AsTokens_State(staker, operator2, withdrawals[i], strategies, shares, tokens, expectedTokens);
81+
check_Withdrawal_AsTokens_State(staker, operator2, withdrawals[i], withdrawals[i].strategies, withdrawals[i].shares, tokens, expectedTokens);
8082
}
8183
}
8284

src/test/integration/tests/Deposit_Delegate_Undelegate_Complete.t.sol

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ contract Integration_Deposit_Delegate_Undelegate_Complete is IntegrationCheckUti
5454
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
5555

5656
// Complete withdrawal
57-
uint[] memory expectedTokens = _calculateExpectedTokens(withdrawals[0].strategies, withdrawals[0].shares);
58-
IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[0]);
59-
check_Withdrawal_AsTokens_State(staker, operator, withdrawals[0], strategies, shares, tokens, expectedTokens);
57+
for (uint256 i = 0; i < withdrawals.length; ++i) {
58+
uint[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].shares);
59+
IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]);
60+
check_Withdrawal_AsTokens_State(staker, operator, withdrawals[i], withdrawals[i].strategies, withdrawals[i].shares, tokens, expectedTokens);
61+
}
6062

6163
// Check Final State
6264
assert_HasNoDelegatableShares(staker, "staker should have withdrawn all shares");
@@ -110,8 +112,11 @@ contract Integration_Deposit_Delegate_Undelegate_Complete is IntegrationCheckUti
110112
// 4. Complete withdrawal
111113
// Fast forward to when we can complete the withdrawal
112114
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
113-
staker.completeWithdrawalAsShares(withdrawals[0]);
114-
check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[0], strategies, shares);
115+
for (uint256 i = 0; i < withdrawals.length; ++i) {
116+
staker.completeWithdrawalAsShares(withdrawals[i]);
117+
118+
check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[i], withdrawals[i].strategies, withdrawals[i].shares);
119+
}
115120

116121
// Check final state:
117122
assert_HasExpectedShares(staker, strategies, shares, "staker should have all original shares");
@@ -161,9 +166,11 @@ contract Integration_Deposit_Delegate_Undelegate_Complete is IntegrationCheckUti
161166
// Fast forward to when we can complete the withdrawal
162167
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
163168

164-
uint[] memory expectedTokens = _calculateExpectedTokens(withdrawals[0].strategies, withdrawals[0].shares);
165-
IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[0]);
166-
check_Withdrawal_AsTokens_State(staker, operator, withdrawals[0], strategies, shares, tokens, expectedTokens);
169+
for (uint256 i = 0; i < withdrawals.length; ++i) {
170+
uint[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].shares);
171+
IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]);
172+
check_Withdrawal_AsTokens_State(staker, operator, withdrawals[i], withdrawals[i].strategies, withdrawals[i].shares, tokens, expectedTokens);
173+
}
167174

168175
// Check Final State
169176
assert_HasNoDelegatableShares(staker, "staker should have withdrawn all shares");
@@ -212,8 +219,10 @@ contract Integration_Deposit_Delegate_Undelegate_Complete is IntegrationCheckUti
212219
// 4. Complete withdrawal
213220
// Fast forward to when we can complete the withdrawal
214221
cheats.roll(block.number + delegationManager.withdrawalDelayBlocks());
215-
staker.completeWithdrawalAsShares(withdrawals[0]);
216-
check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[0], strategies, shares);
222+
for (uint256 i = 0; i < withdrawals.length; ++i) {
223+
staker.completeWithdrawalAsShares(withdrawals[i]);
224+
check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[i], withdrawals[i].strategies, withdrawals[i].shares);
225+
}
217226

218227
// Check final state:
219228
assert_HasExpectedShares(staker, strategies, shares, "staker should have all original shares");

0 commit comments

Comments
 (0)