owned this note
owned this note
Published
Linked with GitHub
# IStakingModule interface
| Expecting approves | |
| ------------------------------ | --- |
| @george-avs | |
| @dgusakov | |
| @ztQfQNT3QamKRB2E2yC2hg (Max) | 💬 |
| @68uFj4WFReSxi8tiG4ZzUQ (Anna) | |
| @F4ever | ✅ |
✅ - approved
💬 - has unresolved comments
## 1. Context
The new validator exit mechanism on the EL will not be used regularly due to cost considerations but will serve as a guarantee that any validator can be exited if necessary. Given this, we consider it the node operator's responsibility to exit validators on time, and any violation of this policy will result in penalties.
A node operator may be penalized in two ways:
1. A fixed penalty per validator that the node operator failed to exit.
2. Compensation for the incurred cost of submitting a Triggerable Exit (TE) request.
## 2. Decision
We propose introducing two new methods in the interface, which will be sufficient for Staking Modules to determine validator/NO penalties.
### Handle Triggerable Exit
This method is called each time a validator's TE request is executed.
It is triggered only for validators that were requested to exit, but it could have any status on CL.
```solidity=
/**
* @notice Handles the triggerable exit events validator belonging to a specific node operator
* @dev This function is called when a validator is exited using the triggerable exit request on EL.
* @param _nodeOperatorIds The ID of the node operator
* @param publicKey Validaotor's public key.
* @param exitType The type of exit being performed.
* This parameter may be interpreted differently across various staking modules, depending on their specific implementation.
* @param withdrawalRequestPaidFee Fee amount paid to send withdrawal request on EL.
* @param validatorIds Array of the validator indexes from CL that were triggered exit.
*/
function onTriggerableExit(
uint256 _nodeOperatorId,
bytes calldata publicKey,
uint256 withdrawalRequestPaidFee,
uint256 exitType,
) external;
```
Parameter explanations:
- `exitType` – This can be defined differently for various staking modules. It is suggested to use this field to indicate whether the node operator should compensate for the TE fee or not. For example:
- 0 - Voluntary exit (no compensation expected).
- 1 - Forced exit (compensation expected).
- `withdrawalRequestPaidFee` – The current fee for submitting a TE request via the EIP-7002 contract.
### Handle Update Validators Exit Statuses
```solidity=
/**
* @notice Updates the exit-related state of validator belonging to a specific node operator.
* @dev This function records the timestamps associated with a validator's exit process,
* tracks when the exit was requested, the last known active_ongoing timestamp, and applies any withdrawal request fees.
* The data can be used for penalty calculations, tracking validator behavior, and enforcing exit policies.
* @param _nodeOperatorsId The ID of the node operator whose validators statuses being delivered.
* @param publicKey Validaotor's public key.
* @param stillActiveOngoingTimestamp The timestamp on which validators were still in active ongoing status. The timestamp of slot on which CL proof were prepared.
* @param eligableToExitInSec number of seconds that NO was eligable to exit validators, but didn't. Matched to the validators from the validatorIds array in the same order.
*/
function updateValidatorExitStatus(
uint256 _nodeOperatorsId,
bytes calldata publicKey,
uint256 stillActiveOngoingTimestamp,
uint256 eligableToExitInSec,
) external;
```
Parameter explanations:
- `stillActiveOngoingTimestamp` - will be used to determine whether TE should be compensated or not (e.g., by verifying that the TE creation timestamp is earlier than `stillActiveOngoingTimestamp`).
### Public methods for off-chain applications
#### Check if validator should be penalized in Prover bot
Since the methods `updateValidatorsExitStatuses` and `onTriggerableExits` are not designed to revert transactions, the bot must ensure that the transaction fees will not be wasted before submitting it.
```solidity=
/**
* @notice Determines whether a validator exit status should be updated and will have affect on Node Operator.
* @param _nodeOperatorId The ID of the node operator.
* @param publicKey Validaotor's public key.
* @param stillActiveOngoingTimestamp The last known timestamp when the validator was active.
* @param eligibleToExitInSec The number of seconds the validator was eligible to exit but did not.
* @return bool Returns true if contract should receive updated validator's status.
*/
function needsUpdateValidatorExitStatus(
uint256 _nodeOperatorsId,
bytes calldata publicKey,
uint256 stillActiveOngoingTimestamp,
uint256 eligableToExitInSec,
) view returns (bool);
```
#### Get exit timeframe to optimize requests in Prover bot.
The bot also requires to know the time after which a validator is considered late.
:::warning
Should we add here some pagination?
:::
```solidity=
/**
* @notice Returns the number of seconds after which a validator is considered late.
* @return The exit deadline threshold in seconds.
*/
function exitDeadlineThreshold(uint256 _nodeOperatorId) external view returns (uint256);
```
#### TE Request Creation Optimization
Should we consider adding a function for the TE bot that would preemptively revert a transaction if a TE request for the given validator has already been sent?
## Other Changes to Be Made
- Remove the `updateStuckValidatorsCount` method;
- Change description and deprecate `_stuckValidatorsCount` param in the `unsafeUpdateValidatorsCount` method;
- Change description and deprecate the `stuckValidatorsCount` and `stuckPenaltyEndTimestamp` params in the `getNodeOperatorSummary` method;
- Update the description for the `onExitedAndStuckValidatorsCountsUpdated` method;
## 3. Rationale
:::success
The following interface explanation is based on the current needs of the SMs. The described algorithm does not impose any strict implementation requirements on the module.
:::
We decided to proceed with Option B in the Curated Module's penalty system ([TW scope and alignment](https://www.notion.so/TW-alignment-on-critical-product-decisions-1aebf633d0c980f8b4eace3e16333ef2?pvs=4#1aebf633d0c98051a623cc5e2df990a5)). Implementing this feature requires the staking module to handle:
- Whether the node operator should compensate for a TE request.
- Whether a validator’s exit status should result in a penalty.

### **onTriggerableExits** hook received
This method notifies SM when a validator exit is triggered via an EL withdrawal request. The module should penalize the node operator only when it violates the policy.
Proposed algorithm:
1. If the validator has already exited via TE, return.
2. Record that a TE was triggered for the validator. The module also stores information on exit time, cost, and type.
3. If the exit type is voluntary, return.
4. If there is proof that the validator missed the deadline by more than `eligibleToExitInSec` and since `stillActiveOngoingTimestamp` did not passed `teCompensationWindowInSec`, penalize the node operator for the amount spent on the TE request.
:::warning
`teCompensationWindowInSec` is a parameter that defines the number of seconds during which proof of a missed deadline remains valid for a TE request, starting from the last recorded proof that the validator was not exiting. If the request is submitted after this period, additional proof with a later slot will be required to compel the node operator to compensate for the late TE request. This mechanism ensures that node operators are not unfairly penalized for validators that were late in the past but have already exited.
:::
### **updateValidatorsExitStatuses**
This method informs the SM about the exit status of a validator requested for exit via the VEB on the CL.
Proposed algorithm:
1. If an exit status record already exists, ensure that the new data is more recent than the previously recorded data (`eligibleToExitInSecNew` > `eligibleToExitInSecOld`). If the new data is older, return.
2. If `eligibleToExitInSec` exceeds the validator exit deadline, check whether the node operator has already been penalized for this validator. If not, apply the penalty.
3. If no TE exit record exists, return.
4. If TE has already been compensated, return.
5. If the TE timestamp is later than the validator's compensation window (`stillActiveOngoingTimestamp + teCompensationWindowInSec ≤ teTimestamp`), return.
6. Compensate the node operator for the amount recorded in the TE request record (refer to steps 3, 4 from previous section).
:::warning
This method can be called multiple times for the same validator to update the latest slot when it was active_ongoing, ensuring tracking if the validator remains still active beyond the initially recorded timestamp.
:::
## 4. References
- [ADR: Curated Module Implementation](https://hackmd.io/WH_60K4RQIaUZz-BwXMIWg)
- [Triggerable Withdrawals spec](https://hackmd.io/tMwct9BuQru47giIv4dVYw)
- [TW scope and alignment](https://www.notion.so/TW-alignment-on-critical-product-decisions-1aebf633d0c980f8b4eace3e16333ef2)