Skip to content

Commit e3f6370

Browse files
gpsanantypatil12
authored andcommitted
feat: add share helpers (#964)
* feat: add share helpers * fix: add deposit scaling factor * fix: rebase
1 parent 25e5ce8 commit e3f6370

File tree

4 files changed

+174
-0
lines changed

4 files changed

+174
-0
lines changed

src/contracts/core/DelegationManager.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,25 @@ contract DelegationManager is
948948
}
949949
}
950950

951+
/// @inheritdoc IDelegationManager
952+
function convertToDepositShares(
953+
address staker,
954+
IStrategy[] memory strategies,
955+
uint256[] memory withdrawableShares
956+
) external view returns (uint256[] memory) {
957+
// Get the slashing factors for the staker/operator/strategies
958+
address operator = delegatedTo[staker];
959+
uint256[] memory slashingFactors = _getSlashingFactors(staker, operator, strategies);
960+
961+
// Calculate the deposit shares based on the slashing factor
962+
uint256[] memory depositShares = new uint256[](strategies.length);
963+
for (uint256 i = 0; i < strategies.length; ++i) {
964+
DepositScalingFactor memory dsf = _depositScalingFactor[staker][strategies[i]];
965+
depositShares[i] = dsf.calcDepositShares(withdrawableShares[i], slashingFactors[i]);
966+
}
967+
return depositShares;
968+
}
969+
951970
/// @inheritdoc IDelegationManager
952971
function calculateWithdrawalRoot(
953972
Withdrawal memory withdrawal

src/contracts/interfaces/IDelegationManager.sol

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,20 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele
481481
address staker
482482
) external view returns (Withdrawal[] memory withdrawals, uint256[][] memory shares);
483483

484+
/**
485+
* @notice Converts shares for a set of strategies to deposit shares, likely in order to input into `queueWithdrawals`
486+
* @param staker the staker to convert shares for
487+
* @param strategies the strategies to convert shares for
488+
* @param withdrawableShares the shares to convert
489+
* @return the deposit shares
490+
* @dev will be a few wei off due to rounding errors
491+
*/
492+
function convertToDepositShares(
493+
address staker,
494+
IStrategy[] memory strategies,
495+
uint256[] memory withdrawableShares
496+
) external view returns (uint256[] memory);
497+
484498
/// @notice Returns the keccak256 hash of `withdrawal`.
485499
function calculateWithdrawalRoot(
486500
Withdrawal memory withdrawal

src/contracts/libraries/SlashingLib.sol

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,17 @@ library SlashingLib {
159159
.mulWad(slashingFactor);
160160
}
161161

162+
function calcDepositShares(
163+
DepositScalingFactor memory dsf,
164+
uint256 withdrawableShares,
165+
uint256 slashingFactor
166+
) internal pure returns (uint256) {
167+
/// forgefmt: disable-next-item
168+
return withdrawableShares
169+
.divWad(dsf.scalingFactor())
170+
.divWad(slashingFactor);
171+
}
172+
162173
function calcSlashedAmount(
163174
uint256 operatorShares,
164175
uint256 prevMaxMagnitude,

src/test/unit/DelegationUnit.t.sol

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8192,3 +8192,133 @@ contract DelegationManagerUnitTests_Lifecycle is DelegationManagerUnitTests {
81928192
assertEq(delegationManager.operatorShares(newOperator, strategy), 0, "new operator shares should be unchanged");
81938193
}
81948194
}
8195+
8196+
contract DelegationManagerUnitTests_ConvertToDepositShares is DelegationManagerUnitTests {
8197+
using ArrayLib for *;
8198+
8199+
function test_convertToDepositShares_noSlashing() public {
8200+
IStrategy[] memory strategies = new IStrategy[](1);
8201+
strategies[0] = strategyMock;
8202+
uint256[] memory shares = uint256(100 ether).toArrayU256();
8203+
8204+
// Set the staker deposits in the strategies
8205+
strategyManagerMock.addDeposit(defaultStaker, strategies[0], shares[0]);
8206+
8207+
_checkDepositSharesConvertCorrectly(strategies, shares);
8208+
}
8209+
8210+
function test_convertToDepositShares_withSlashing() public {
8211+
IStrategy[] memory strategies = new IStrategy[](1);
8212+
strategies[0] = strategyMock;
8213+
uint256[] memory shares = uint256(100 ether).toArrayU256();
8214+
8215+
// Set the staker deposits in the strategies
8216+
strategyManagerMock.addDeposit(defaultStaker, strategies[0], shares[0]);
8217+
8218+
// register *this contract* as an operator
8219+
_registerOperatorWithBaseDetails(defaultOperator);
8220+
_delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator);
8221+
_setOperatorMagnitude(defaultOperator, strategyMock, WAD/3);
8222+
8223+
_checkDepositSharesConvertCorrectly(strategies, shares);
8224+
8225+
// queue and complete a withdrawal for half the deposit shares
8226+
(uint256[] memory withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, strategies);
8227+
_queueAndCompleteWithdrawalForSingleStrategy(strategies[0], shares[0] / 2);
8228+
8229+
// queued a withdrawal for half the deposit shares, and added back as withdrawable shares
8230+
shares[0] = shares[0] / 2 + withdrawableShares[0] / 2;
8231+
_checkDepositSharesConvertCorrectly(strategies, shares);
8232+
}
8233+
8234+
function test_convertToDepositShares_beaconChainETH() public {
8235+
IStrategy[] memory strategies = new IStrategy[](1);
8236+
strategies[0] = beaconChainETHStrategy;
8237+
uint256[] memory shares = uint256(100 ether).toArrayU256();
8238+
8239+
// Set the staker deposits in the strategies
8240+
eigenPodManagerMock.setPodOwnerShares(defaultStaker, int256(shares[0]));
8241+
8242+
uint256[] memory depositShares = delegationManager.convertToDepositShares(defaultStaker, strategies, shares);
8243+
assertEq(depositShares[0], shares[0], "deposit shares not converted correctly");
8244+
8245+
// delegate to an operator and slash
8246+
_registerOperatorWithBaseDetails(defaultOperator);
8247+
_delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator);
8248+
_setOperatorMagnitude(defaultOperator, beaconChainETHStrategy, WAD/3);
8249+
8250+
_checkDepositSharesConvertCorrectly(strategies, shares);
8251+
8252+
// slash on beacon chain by 1/3
8253+
_decreaseBeaconChainShares(defaultStaker, int256(shares[0]), shares[0]/3);
8254+
8255+
_checkDepositSharesConvertCorrectly(strategies, shares);
8256+
8257+
// queue and complete a withdrawal for half the deposit shares
8258+
(uint256[] memory withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, strategies);
8259+
_queueAndCompleteWithdrawalForSingleStrategy(strategies[0], shares[0] / 2);
8260+
8261+
// queued a withdrawal for half the deposit shares, and added back as withdrawable shares
8262+
shares[0] = shares[0] / 2 + withdrawableShares[0] / 2;
8263+
_checkDepositSharesConvertCorrectly(strategies, shares);
8264+
}
8265+
8266+
function _checkDepositSharesConvertCorrectly(IStrategy[] memory strategies, uint256[] memory expectedDepositShares) public {
8267+
(uint256[] memory withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, strategies);
8268+
// get the deposit shares
8269+
uint256[] memory depositShares = delegationManager.convertToDepositShares(defaultStaker, strategies, withdrawableShares);
8270+
8271+
for (uint256 i = 0; i < strategies.length; i++) {
8272+
assertApproxEqRel(
8273+
expectedDepositShares[i],
8274+
depositShares[i],
8275+
APPROX_REL_DIFF,
8276+
"deposit shares not converted correctly"
8277+
);
8278+
8279+
// make sure that the deposit shares are less than or equal to the shares,
8280+
// so this value is sane to input into `completeQueuedWithdrawals`
8281+
assertLe(
8282+
depositShares[i],
8283+
expectedDepositShares[i],
8284+
"deposit shares should be less than or equal to expected deposit shares"
8285+
);
8286+
}
8287+
8288+
// get the deposit shares
8289+
uint256[] memory oneThirdWithdrawableShares = new uint256[](strategies.length);
8290+
for (uint256 i = 0; i < strategies.length; i++) {
8291+
oneThirdWithdrawableShares[i] = withdrawableShares[i]/3;
8292+
}
8293+
uint256[] memory oneThirdDepositShares = delegationManager.convertToDepositShares(defaultStaker, strategies, oneThirdWithdrawableShares);
8294+
for (uint256 i = 0; i < strategies.length; i++) {
8295+
assertApproxEqRel(
8296+
expectedDepositShares[i]/3,
8297+
oneThirdDepositShares[i],
8298+
APPROX_REL_DIFF,
8299+
"deposit shares not converted correctly"
8300+
);
8301+
}
8302+
}
8303+
8304+
function _queueAndCompleteWithdrawalForSingleStrategy(IStrategy strategy, uint256 shares) public {
8305+
IStrategy[] memory strategies = new IStrategy[](1);
8306+
strategies[0] = strategy;
8307+
uint256[] memory depositShares = uint256(shares).toArrayU256();
8308+
8309+
(QueuedWithdrawalParams[] memory queuedWithdrawalParams, Withdrawal memory withdrawal, bytes32 withdrawalRoot) = _setUpQueueWithdrawalsSingleStrat({
8310+
staker: defaultStaker,
8311+
withdrawer: defaultStaker,
8312+
strategy: strategy,
8313+
depositSharesToWithdraw: shares
8314+
});
8315+
8316+
cheats.prank(defaultStaker);
8317+
delegationManager.queueWithdrawals(queuedWithdrawalParams);
8318+
8319+
cheats.roll(block.number + delegationManager.minWithdrawalDelayBlocks());
8320+
cheats.prank(defaultStaker);
8321+
delegationManager.completeQueuedWithdrawal(withdrawal, tokenMock.toArray(), false);
8322+
}
8323+
}
8324+

0 commit comments

Comments
 (0)