Skip to content

Commit

Permalink
feat: submit withdrawal base method
Browse files Browse the repository at this point in the history
  • Loading branch information
skhomuti committed Dec 28, 2023
1 parent bdf13f6 commit 3dbd0d0
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 36 deletions.
75 changes: 39 additions & 36 deletions src/CSModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ struct NodeOperator {
uint256 targetLimit;
bool isTargetLimitActive;
uint256 stuckPenaltyEndTimestamp;
uint256 totalExitedKeys;
uint256 totalAddedKeys;
uint256 totalWithdrawnKeys;
uint256 totalDepositedKeys;
uint256 totalVettedKeys;
uint256 stuckValidatorsCount;
uint256 refundedValidatorsCount;
uint256 totalExitedKeys; // @dev only increased
uint256 totalAddedKeys; // @dev only increased
uint256 totalWithdrawnKeys; // @dev only increased
uint256 totalDepositedKeys; // @dev only increased
uint256 totalVettedKeys; // @dev both increased and decreased
uint256 stuckValidatorsCount; // @dev both increased and decreased
uint256 refundedValidatorsCount; // @dev only increased
uint256 queueNonce;
}

Expand Down Expand Up @@ -82,6 +82,7 @@ contract CSModuleBase {
bool isTargetLimitActive,
uint256 targetValidatorsCount
);
event WithdrawalSubmitted(uint256 indexed validatorId, uint256 exitBalance);

event BatchEnqueued(
uint256 indexed nodeOperatorId,
Expand Down Expand Up @@ -132,6 +133,7 @@ contract CSModule is ICSModule, CSModuleBase {
// @dev max number of node operators is limited by uint64 due to Batch serialization in 32 bytes
// it seems to be enough
uint64 public constant MAX_NODE_OPERATORS_COUNT = type(uint64).max;
uint256 public constant DEPOSIT_SIZE = 32 ether;
bytes32 public constant SIGNING_KEYS_POSITION =
keccak256("lido.CommunityStakingModule.signingKeysPosition");

Expand Down Expand Up @@ -212,6 +214,7 @@ contract CSModule is ICSModule, CSModuleBase {
uint256 /* depositableValidatorsCount */
)
{
// TODO: need to be implemented properly
return (
_totalExitedValidators,
_totalDepositedValidators,
Expand Down Expand Up @@ -664,13 +667,14 @@ contract CSModule is ICSModule, CSModuleBase {
no.totalVettedKeys -
totalDepositedValidators;
if (no.isTargetLimitActive) {
depositableValidatorsCount = (totalExitedValidators +
targetValidatorsCount) <= no.totalVettedKeys
? 0
: Math.min(
totalExitedValidators + targetValidatorsCount,
no.totalVettedKeys
) - totalDepositedValidators;
uint256 activeValidatorsCount = no.totalDepositedKeys -
no.totalExitedKeys;
depositableValidatorsCount = Math.min(
targetValidatorsCount > activeValidatorsCount
? targetValidatorsCount - activeValidatorsCount
: 0,
depositableValidatorsCount
);
}
}

Expand Down Expand Up @@ -828,28 +832,6 @@ contract CSModule is ICSModule, CSModuleBase {
}
}

/// @notice Reports withdrawn validator for node operator
/// @param withdrawProof Withdraw proof
/// @param validatorId ID of the validator
/// @param nodeOperatorId ID of the node operator
/// @param withdrawnBalance Amount of withdrawn balance
function reportWithdrawnValidator(
bytes32[] memory withdrawProof,
uint256 validatorId,
uint256 nodeOperatorId,
uint256 withdrawnBalance
) external {
// TODO: implement me
}

/// @notice Triggers the node operator's unbonded validator to exit
function exitUnbondedValidator(
uint256 nodeOperatorId,
uint256 validatorId
) external {
// TODO: implement me
}

/// @notice Triggers the node operator's validator to exit by DAO decision
function unsafeExitValidator(
uint256 nodeOperatorId,
Expand Down Expand Up @@ -967,6 +949,7 @@ contract CSModule is ICSModule, CSModuleBase {

if (
no.isTargetLimitActive &&
// TODO: totalExited or totalWithdrawn?
vetKeysPointer > (no.totalExitedKeys + no.targetLimit)
) revert TargetLimitExceeded();
if (no.stuckValidatorsCount > 0) revert StuckKeysPresent();
Expand Down Expand Up @@ -1097,6 +1080,26 @@ contract CSModule is ICSModule, CSModuleBase {
_checkForOutOfBond(nodeOperatorId);
}

function submitWithdrawal(
bytes32 /*withdrawalProof*/,
uint256 nodeOperatorId,
uint256 validatorId,
uint256 exitBalance
) external onlyExistingNodeOperator(nodeOperatorId) {
// TODO: check for withdrawal proof
// TODO: consider asserting that withdrawn keys count is not higher than exited keys count
NodeOperator storage no = _nodeOperators[nodeOperatorId];

no.totalWithdrawnKeys += 1;

if (exitBalance < DEPOSIT_SIZE) {
accounting.penalize(nodeOperatorId, DEPOSIT_SIZE - exitBalance);
_checkForOutOfBond(nodeOperatorId);
}

emit WithdrawalSubmitted(validatorId, exitBalance);
}

/// @notice Called when withdrawal credentials changed by DAO
function onWithdrawalCredentialsChanged() external {
revert("NOT_IMPLEMENTED");
Expand Down
35 changes: 35 additions & 0 deletions test/CSModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1905,3 +1905,38 @@ contract CsmSettleELRewardsStealingPenalty is CSMCommon {
assertEq(lock.retentionUntil, 0);
}
}

contract CsmSubmitWithdrawal is CSMCommon {
function test_submitWithdrawal() public {
uint256 validatorId = 1;
uint256 noId = createNodeOperator();
csm.vetKeys(noId, 1);
csm.obtainDepositData(1, "");

vm.expectEmit(true, true, true, true, address(csm));
emit WithdrawalSubmitted(validatorId, csm.DEPOSIT_SIZE());
csm.submitWithdrawal("", noId, validatorId, csm.DEPOSIT_SIZE());

CSModule.NodeOperatorInfo memory no = csm.getNodeOperator(noId);
assertEq(no.totalWithdrawnValidators, 1);
}

function test_submitWithdrawal_lowExitBalance() public {
uint256 validatorId = 1;
uint256 noId = createNodeOperator();
uint256 depositSize = csm.DEPOSIT_SIZE();
csm.vetKeys(noId, 1);
csm.obtainDepositData(1, "");

vm.expectCall(
address(accounting),
abi.encodeWithSelector(accounting.penalize.selector, noId, 1 ether)
);
csm.submitWithdrawal("", noId, validatorId, depositSize - 1 ether);
}

function test_submitWithdrawal_RevertWhenNoNodeOperator() public {
vm.expectRevert(NodeOperatorDoesNotExist.selector);
csm.submitWithdrawal("", 0, 0, 0);
}
}

0 comments on commit 3dbd0d0

Please sign in to comment.