From 837331ec5fac1ed445d5cb0698259fe8e93ec79c Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Wed, 18 Sep 2024 18:08:59 +0300 Subject: [PATCH 1/4] adapt fees to decalred effective balance --- all.md | 31 ++-- contracts.md | 9 +- core.md | 30 ++-- ...dapt_fees_to_declared_effective_balance.md | 166 ++++++++++++++++++ 4 files changed, 203 insertions(+), 33 deletions(-) create mode 100644 sips/adapt_fees_to_declared_effective_balance.md diff --git a/all.md b/all.md index fa557a1..d27487d 100644 --- a/all.md +++ b/all.md @@ -1,17 +1,18 @@ ## All SIPS -| SIP # | Title | Status | -|---------------------------------------|-----------------------------|--------| -| [1](./sips/dkg.md) | DKG | open-for-discussion | -| [2](./sips/msg_struct_encoding.md) | Message struct and encoding | open-for-discussion | -| [3](./sips/qbft_sync.md) | QBFT Sync | open-for-discussion | -| [4](./sips/change_operator.md) | Change operators set | open-for-discussion | -| [5](./sips/ecies_share_encryption.md) | ECIES Share Encryption | open-for-discussion | -| [6](./sips/constant_qbft_timeout.md) | Constant QBFT timeout | open-for-discussion | -| [7](./sips/fork_support.md) | Fork Support | open-for-discussion | -| [8](./sips/pre_consensus_livness.md) | Pre-Consensus livness fix | open-for-discussion | -| [9](./sips/partial_signature_verification_aggregation.md) | Partial Signature Verification Aggregation | spec-merged | -| [10](./sips/qbft_drop_redundant_bls.md) | Drop redundant BLS in QBFT | spec-merged | -| [11](./sips/eliminate_bls.md) | Eliminate BLS out of QBFT and change message structure | spec-merged | -| [12](./sips/topic_by_committe_id.md) | Eliminate BLS out of QBFT and change message structure | spec-merged | -| [13](./sips/cluster_consensus.md) | Cluster-based consensus | spec-merged | +| SIP # | Title | Status | +| --------------------------------------------------------- | ------------------------------------------------------ | ------------------- | +| [1](./sips/dkg.md) | DKG | open-for-discussion | +| [2](./sips/msg_struct_encoding.md) | Message struct and encoding | open-for-discussion | +| [3](./sips/qbft_sync.md) | QBFT Sync | open-for-discussion | +| [4](./sips/change_operator.md) | Change operators set | open-for-discussion | +| [5](./sips/ecies_share_encryption.md) | ECIES Share Encryption | open-for-discussion | +| [6](./sips/constant_qbft_timeout.md) | Constant QBFT timeout | open-for-discussion | +| [7](./sips/fork_support.md) | Fork Support | open-for-discussion | +| [8](./sips/pre_consensus_livness.md) | Pre-Consensus livness fix | open-for-discussion | +| [9](./sips/partial_signature_verification_aggregation.md) | Partial Signature Verification Aggregation | spec-merged | +| [10](./sips/qbft_drop_redundant_bls.md) | Drop redundant BLS in QBFT | spec-merged | +| [11](./sips/eliminate_bls.md) | Eliminate BLS out of QBFT and change message structure | spec-merged | +| [12](./sips/topic_by_committe_id.md) | Eliminate BLS out of QBFT and change message structure | spec-merged | +| [13](./sips/cluster_consensus.md) | Cluster-based consensus | spec-merged | +| [14](./sips/adapt_fees_to_declared_effective_balance.md) | open-for-discussion | diff --git a/contracts.md b/contracts.md index 560956d..57523b0 100644 --- a/contracts.md +++ b/contracts.md @@ -1,6 +1,7 @@ ## Core -| SIP # | Title | Status | -|-------------------------------|-----------------------------|--------| -| [4](./sips/change_operator.md) | Change operators set | open-for-discussion | -| [5](./sips/ecies_share_encryption.md) | ECIES Share Encryption | open-for-discussion | \ No newline at end of file +| SIP # | Title | Status | +| -------------------------------------------------------- | ---------------------- | ------------------- | +| [4](./sips/change_operator.md) | Change operators set | open-for-discussion | +| [5](./sips/ecies_share_encryption.md) | ECIES Share Encryption | open-for-discussion | +| [14](./sips/adapt_fees_to_declared_effective_balance.md) | open-for-discussion | diff --git a/core.md b/core.md index 7bfed8d..01cef2c 100644 --- a/core.md +++ b/core.md @@ -1,16 +1,18 @@ ## Core -| SIP # | Title | Status | -|------------------------------------|-----------------------------|--------| -| [1](./sips/dkg.md) | DKG | open-for-discussion | -| [2](./sips/msg_struct_encoding.md) | Message struct and encoding | open-for-discussion | -| [3](./sips/qbft_sync.md) | QBFT Sync | open-for-discussion | -| [4](./sips/change_operator.md) | Change operators set | open-for-discussion | -| [5](./sips/ecies_share_encryption.md) | ECIES Share Encryption | open-for-discussion | -| [6](./sips/constant_qbft_timeout.md) | Constant QBFT timeout | open-for-discussion | -| [7](./sips/fork_support.md) | Fork Support | open-for-discussion | -| [8](./sips/pre_consensus_livness.md) | Pre-Consensus livness fix | open-for-discussion | -| [9](./sips/partial_signature_verification_aggregation.md) | Partial Signature Verification Aggregation | spec-merged | -| [10](./sips/qbft_drop_redundant_bls.md) | Drop redundant BLS in QBFT | spec-merged | -| [11](./sips/eliminate_bls.md) | Eliminate BLS out of QBFT and change message structure | spec-merged | -| [13](./sips/cluster_consensus.md) | Cluster-based consensus | spec-merged | +| SIP # | Title | Status | +| --------------------------------------------------------- | ------------------------------------------------------ | ------------------- | +| [1](./sips/dkg.md) | DKG | open-for-discussion | +| [2](./sips/msg_struct_encoding.md) | Message struct and encoding | open-for-discussion | +| [3](./sips/qbft_sync.md) | QBFT Sync | open-for-discussion | +| [4](./sips/change_operator.md) | Change operators set | open-for-discussion | +| [5](./sips/ecies_share_encryption.md) | ECIES Share Encryption | open-for-discussion | +| [6](./sips/constant_qbft_timeout.md) | Constant QBFT timeout | open-for-discussion | +| [7](./sips/fork_support.md) | Fork Support | open-for-discussion | +| [8](./sips/pre_consensus_livness.md) | Pre-Consensus livness fix | open-for-discussion | +| [9](./sips/partial_signature_verification_aggregation.md) | Partial Signature Verification Aggregation | spec-merged | +| [10](./sips/qbft_drop_redundant_bls.md) | Drop redundant BLS in QBFT | spec-merged | +| [11](./sips/eliminate_bls.md) | Eliminate BLS out of QBFT and change message structure | spec-merged | +| [13](./sips/cluster_consensus.md) | Cluster-based consensus | spec-merged | +| [14](./sips/adapt_fees_to_declared_effective_balance.md) | open-for-discussion | + diff --git a/sips/adapt_fees_to_declared_effective_balance.md b/sips/adapt_fees_to_declared_effective_balance.md new file mode 100644 index 0000000..9e5d22c --- /dev/null +++ b/sips/adapt_fees_to_declared_effective_balance.md @@ -0,0 +1,166 @@ +| Author | Title | Category | Status | Date | +|-------|----------------|--------------------------------------|---------|--------------------| +| Gal Rogozinski & Marco Tabasco | Adapt fees to declared effective balance | Contracts & Core | open-for-discussions |18.09.2024 | + + +## Summary + +Creates a mechanism to incentivize users to record the correct balance of their validator with the contract. +This balance will be used for operator and network fee calculations. + +## Motivation + +Operators on SSV Network charge validators a fee based on an expected workload. The expected workload is proportional to the the effective balance a validator has. Currently the effective balance is assumed to be a constant 32 ETH by the contract. With the introduction of [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) the effective balance of a validator could be as high as 2048 ETH. + +The same reasoning applies for the network fee charged to each validator registered. + + +## Rationale + +Do not perform duties if the validator's effective balance is greater by more than a deviation configured by the contract's owner. This will incentivize validators to always register the correct amount on the contract. Effective balance may grow due to compounding rewards and it is the user's responsibility to update their effective balance before their operators pause. Paused operators will resume when the effective balance is updated on the contract. + +## Specification + +### Contract +When registering new validators, add a new parameter to indicate the current effective balance of the validator being added. This will be used to update operators' [snapshots](https://github.com/ssvlabs/ssv-network/blob/583b7b7cb1c1abc5d4c3b13bafca59bf315113b6/contracts/interfaces/ISSVNetworkCore.sol#L10) with the right amounts, calculate the network fee applied to the validator, and emit the event with this information to allow the node confirm it. + + +#### Adapted functions + +```solidity +function registerValidator( + bytes calldata publicKey, + uint64[] memory operatorIds, + bytes calldata sharesData, + uint256 ethEffectiveBalance, \\ This is new + uint256 amount, + Cluster memory cluster +) external +``` +```solidity +function bulkRegisterValidator( + bytes[] memory publicKeys, + uint64[] memory operatorIds, + bytes[] calldata sharesData, + uint256[] ethEffectiveBalance, \\ This is new + uint256 amount, + Cluster memory cluster +) external +``` + + + +#### Adapted event +```solidity +event ValidatorAdded( + address indexed owner, + uint64[] operatorIds, + bytes publicKey, + bytes shares, + uint256 ethEffectiveBalance, //This is new + Cluster cluster); +``` + +### New functions + +```solidity +// Function to update the effective balance for a set of validators +function updateEthEffectiveBalance( + bytes[] memory publicKeys, + uint64[] memory operatorIds, + uint256[] newEffectiveBalance, + Cluster memory cluster +) external +``` +```solidity +// Function to update the maximum deviation allowed for the ETH +// balance declared by validator owners +// @dev Using 10000 to represent 2 digit precision: 100% = 10000, 10% = 1000 +// @dev Only the owner of the contract can perform the update +function updateAllowedBalanceDeviation(uint64 percentage) external onlyOwner +``` + +```solidity +// Function to get the maximum ETH balance deviation +function getAllowedBalanceDeviation() + external view returns(uint64 percentage) +``` + + +### New events + +```solidity +event ValidatorEffectiveBalanceUpdated( + address indexed owner, + uint64[] operatorIds, + bytes publicKey, + uint256 newEffectiveBalance, + Cluster cluster); +``` +```solidity +event AllowedBalanceDeviationUpdated(uint64 percentage); +``` + +### Current implementation + +Operator earnings formula since snapshot: + + +`(currentBlock - snapshotBlock) * operatorFee * operatorValidatorCount` + +### New implementation + +Earning for last snpashot + fees for one block + +`((currentBlock - snapshotBlock) * (snapshotTotalETH / 32) * operatorFee)` + +Where`snapshotTotalETH`: The total ETH declared by the validators managed by this operator. + + +#### Network fee +##### Current implementation +`(currentBlock - snapshotBlock) * networkFee * totalValidatorCount` + +##### New implementation +`(currentBlock - snapshotBlock) * (snapshotTotalETH / 32) * networkFee)` + + +#### User should always report effective balance when depositing SSVs + +```solidity +function deposit( + address clusterOwner, + uint64[] calldata operatorIds, + uint256 amount, + uint256 newEffectiveBalance, + Cluster memory cluster +) external +``` + +### Node + +Node should keep a table of the validator's effective balances reported by the contract's `AllowedDeviationUpdated` events. +This table is updated upon contract events reporting balance updates. + +When fetching duties for a validator, query the Eth node for its balance in the `Finalized` state. If the difference between the true balance and the effective balance reported by the user is larger than the allowed deviation do not execute the duty. + + +```python + def skipDuties(valPK ValidatorPublicKey, deviation_percentage float) + true_balance = GET /eth/v1/beacon/states/finalized/validator_balances?valPK + reported_eff_balance = getFromEvent(valPK) + true_balance = min(2048 ETH, true_balance) + # Skip duties if this statement hold + return RoundDown(true_balance) - reported_eff_balance > deviation_percentage * reported_eff_balance +``` + +A committee consensus process should only be skipped if all paritciapting validators should be skipped. + +Note: we use the actual balance and not effective balance since it is easy to query. + +## Issues and alternatives + +1. We do not track changing balance due to compounding... meaning users will pay a bit less then what they manage. So the deviation should be configured in a way that allow acceptable slippage while not degrading UX by forcing users to update theor balance too soon. +2. Are users incentivized to split validators? By splitting a 2048 eth validators to two validators, a validator enjoys compounding and pays a reduced fee. +3. There was a considered option to allow contract's owner to update the APR offered by Ethereum. Then estimate the correct fee in the presence of rewards compunding and allow a smaller balance deviation. This proposal favors simplicity. +4. Oracles can help calibrate the calculations of an exact fee... however using a 3rd party oracle will add more complexity and will probably be a paid service. \ No newline at end of file From 676547ae8905c136eb1d85c5ab2bb42698d938c3 Mon Sep 17 00:00:00 2001 From: Marco Tabasco Date: Tue, 22 Oct 2024 14:01:38 +0200 Subject: [PATCH 2/4] contracts:track maxeb per cluster --- ...dapt_fees_to_declared_effective_balance.md | 101 +++++++++++------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/sips/adapt_fees_to_declared_effective_balance.md b/sips/adapt_fees_to_declared_effective_balance.md index 9e5d22c..0157c35 100644 --- a/sips/adapt_fees_to_declared_effective_balance.md +++ b/sips/adapt_fees_to_declared_effective_balance.md @@ -21,58 +21,45 @@ Do not perform duties if the validator's effective balance is greater by more th ## Specification -### Contract -When registering new validators, add a new parameter to indicate the current effective balance of the validator being added. This will be used to update operators' [snapshots](https://github.com/ssvlabs/ssv-network/blob/583b7b7cb1c1abc5d4c3b13bafca59bf315113b6/contracts/interfaces/ISSVNetworkCore.sol#L10) with the right amounts, calculate the network fee applied to the validator, and emit the event with this information to allow the node confirm it. +### Contract - track maxeb per cluster +When registering new validators, add a new parameter to indicate the current effective balance of the total amount of validators being added. This will be used to update operators' [snapshots](https://github.com/ssvlabs/ssv-network/blob/583b7b7cb1c1abc5d4c3b13bafca59bf315113b6/contracts/interfaces/ISSVNetworkCore.sol#L10) with the right amounts, calculate the network fee applied to the cluster, and emit the event with this information to allow the node confirm it. #### Adapted functions -```solidity +```ts function registerValidator( bytes calldata publicKey, uint64[] memory operatorIds, bytes calldata sharesData, - uint256 ethEffectiveBalance, \\ This is new + uint256 clusterEB, // This is new uint256 amount, Cluster memory cluster ) external ``` -```solidity +```ts function bulkRegisterValidator( bytes[] memory publicKeys, uint64[] memory operatorIds, bytes[] calldata sharesData, - uint256[] ethEffectiveBalance, \\ This is new - uint256 amount, + uint256 clusterEB, // This is new + uint256 amount, // SSV Cluster memory cluster ) external ``` - -#### Adapted event -```solidity -event ValidatorAdded( - address indexed owner, - uint64[] operatorIds, - bytes publicKey, - bytes shares, - uint256 ethEffectiveBalance, //This is new - Cluster cluster); -``` - ### New functions -```solidity -// Function to update the effective balance for a set of validators +```ts +// Function to update the effective balance for a cluster function updateEthEffectiveBalance( - bytes[] memory publicKeys, uint64[] memory operatorIds, - uint256[] newEffectiveBalance, + uint256 clusterEB, // check lower type Cluster memory cluster ) external ``` -```solidity +```ts // Function to update the maximum deviation allowed for the ETH // balance declared by validator owners // @dev Using 10000 to represent 2 digit precision: 100% = 10000, 10% = 1000 @@ -80,24 +67,21 @@ function updateEthEffectiveBalance( function updateAllowedBalanceDeviation(uint64 percentage) external onlyOwner ``` -```solidity +```ts // Function to get the maximum ETH balance deviation function getAllowedBalanceDeviation() external view returns(uint64 percentage) ``` - ### New events - -```solidity -event ValidatorEffectiveBalanceUpdated( +```ts +event ebUpdated( address indexed owner, uint64[] operatorIds, - bytes publicKey, - uint256 newEffectiveBalance, - Cluster cluster); + uint256 clustereb); ``` -```solidity + +```ts event AllowedBalanceDeviationUpdated(uint64 percentage); ``` @@ -110,7 +94,7 @@ Operator earnings formula since snapshot: ### New implementation -Earning for last snpashot + fees for one block +Operator earnings for last snpashot + fees for one block. `((currentBlock - snapshotBlock) * (snapshotTotalETH / 32) * operatorFee)` @@ -127,16 +111,61 @@ Where`snapshotTotalETH`: The total ETH declared by the validators managed by thi #### User should always report effective balance when depositing SSVs -```solidity +```ts function deposit( address clusterOwner, uint64[] calldata operatorIds, uint256 amount, - uint256 newEffectiveBalance, + uint256 clusterEB, Cluster memory cluster ) external ``` +### Fee management migration +Each 32 ETH declared when registering new validators, removing, etc. can be taken as a fee unit of the current validators count for operators and network. + +These are the data structures to manage operator and network fees: +```ts +/// @notice Represents an SSV operator +struct Operator { + /// @dev The number of validators associated with this operator + uint32 validatorCount; + /// @dev The fee charged by the operator + uint64 fee; + ... +} + +/// @notice The count of validators governed by the DAO +uint32 daoValidatorCount; +/// @notice The current network fee value +uint64 networkFee; +``` + +Using the formulas described before to calculate operator and network earnings, we can conclude that: + +`(snapshotTotalETH / 32)` is a representation of the actual `Operator.validatorCount` and `daoValidatorCount`. So 32 ETH declared is 1 fee unit. + +##### Register validators example +Instead of using the number of keys being registered to update operators' and network snapshots and cluster metadata, the result of `declared maxEB / 32` will be used to calculate the fee units. + +##### Proposed naming changes +```ts +/// @notice Represents an SSV operator +struct Operator { + /// @dev The number of fee units associated with this operator + uint32 feeUnits; + /// @dev The fee charged by the operator + uint64 fee; + ... +} + +/// @notice The number of fee units governed by the DAO +uint32 daoFeeUnits; +/// @notice The current network fee value +uint64 networkFee; +``` + + ### Node Node should keep a table of the validator's effective balances reported by the contract's `AllowedDeviationUpdated` events. From e9fc1944f3d21e1936e5245e3ec2e90f85e1adba Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Wed, 23 Oct 2024 09:36:35 +0300 Subject: [PATCH 3/4] update node --- ...dapt_fees_to_declared_effective_balance.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sips/adapt_fees_to_declared_effective_balance.md b/sips/adapt_fees_to_declared_effective_balance.md index 0157c35..f13c39c 100644 --- a/sips/adapt_fees_to_declared_effective_balance.md +++ b/sips/adapt_fees_to_declared_effective_balance.md @@ -165,25 +165,28 @@ uint32 daoFeeUnits; uint64 networkFee; ``` - ### Node -Node should keep a table of the validator's effective balances reported by the contract's `AllowedDeviationUpdated` events. +Node should keep a table of the validator's effective balances reported by the contract's `ebUpdated` events. This table is updated upon contract events reporting balance updates. -When fetching duties for a validator, query the Eth node for its balance in the `Finalized` state. If the difference between the true balance and the effective balance reported by the user is larger than the allowed deviation do not execute the duty. +When fetching duties for a validator, query the Eth node for its cluster balance in the `Finalized` state. If the difference between the true balance and the effective balance reported by the user is larger than the allowed deviation do not execute the duty. ```python def skipDuties(valPK ValidatorPublicKey, deviation_percentage float) - true_balance = GET /eth/v1/beacon/states/finalized/validator_balances?valPK - reported_eff_balance = getFromEvent(valPK) - true_balance = min(2048 ETH, true_balance) + // can be found from validator added event + ownerID, opIDs, valPks = getClusterAccordingToValidator(valPk) + true_balances = GET /eth/v1/beacon/states/finalized/validator_balances?valPKs + cluster_balance = sum(true_balances) + //get from ebUpdated Event + reported_cluster_balance = getReportedClusterBalance(ownerID, opIDs) + eff_cluster_balance = min(2048 ETH*sizeof(valPks), cluster_balance) # Skip duties if this statement hold - return RoundDown(true_balance) - reported_eff_balance > deviation_percentage * reported_eff_balance + return RoundDown(eff_cluster_balance) - reported_cluster_balance > deviation_percentage * reported_cluster_balance ``` -A committee consensus process should only be skipped if all paritciapting validators should be skipped. +A committee consensus process should only be skipped if all participating validators should be skipped. Note: we use the actual balance and not effective balance since it is easy to query. From 6d17ea5d6709d8dd96715e9c6fd689bc30199d89 Mon Sep 17 00:00:00 2001 From: Marco Tabasco Date: Tue, 5 Nov 2024 18:44:51 +0100 Subject: [PATCH 4/4] introduce fee per ETH managed --- ...dapt_fees_to_declared_effective_balance.md | 254 ++++++++++++------ 1 file changed, 167 insertions(+), 87 deletions(-) diff --git a/sips/adapt_fees_to_declared_effective_balance.md b/sips/adapt_fees_to_declared_effective_balance.md index f13c39c..f7d9145 100644 --- a/sips/adapt_fees_to_declared_effective_balance.md +++ b/sips/adapt_fees_to_declared_effective_balance.md @@ -10,9 +10,9 @@ This balance will be used for operator and network fee calculations. ## Motivation -Operators on SSV Network charge validators a fee based on an expected workload. The expected workload is proportional to the the effective balance a validator has. Currently the effective balance is assumed to be a constant 32 ETH by the contract. With the introduction of [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) the effective balance of a validator could be as high as 2048 ETH. +Operators on SSV Network charge validators a fee based on an expected workload. The expected workload is proportional to the effective balance a validator has. Currently, the effective balance is assumed to be a constant 32 ETH by the contract. With the introduction of [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) the effective balance of a validator could be as high as 2048 ETH. -The same reasoning applies for the network fee charged to each validator registered. +The same reasoning applies to the network fee charged to each validator registered. ## Rationale @@ -21,149 +21,229 @@ Do not perform duties if the validator's effective balance is greater by more th ## Specification -### Contract - track maxeb per cluster +### Contract - Operators -When registering new validators, add a new parameter to indicate the current effective balance of the total amount of validators being added. This will be used to update operators' [snapshots](https://github.com/ssvlabs/ssv-network/blob/583b7b7cb1c1abc5d4c3b13bafca59bf315113b6/contracts/interfaces/ISSVNetworkCore.sol#L10) with the right amounts, calculate the network fee applied to the cluster, and emit the event with this information to allow the node confirm it. +Currently, operators set a fee (denominated in SSV) for the services provided to validator owners. This fee is charged per validator per block. + +An operator owner can change the fee of an operator following this [flow](https://docs.ssv.network/learn/operators/update-fee). + +The operator earnings are calculated on a cumulative basis, updating the operator balance when these events occur: + +- Operator removed +- Execute operator fee change +- Withdraw operator earnings +- Register validators +- Remove validators +- Liquidate cluster +- Reactivate cluster + +To calculate the operators' earnings, the number of validators managed is used along with the fee. With the introduction of EIP-7251, this math becomes obsolete. The operators' fees should consider the validators' declared effective balance to be fair. + +#### Summary of the changes +Validators now can deposit ETH in this range, with a precision of 1 Gwei (10^9 wei): + +`ETH deposited = 2048 - 32` + +An operator's fee now will be adjusted to handle the ETH effective balance declared by the validators it manages: the *fee per ETH managed model*. This fee is charged per block. + +#### Adapted data structures + +```c +struct Operator { + uint32 validatorCount; + uint64 ebManaged; // New property: effective balance managed + uint64 fee; // Now represents the fee per ETH managed per block + address owner; + bool whitelisted; + Snapshot snapshot; +} +``` + +#### Adapted formulas + +Operator earnings formula since snapshot: + +`current earnings = snapshotBalance + ((currentBlock - snapshotBlock) * fee * validatorCount)` + +New formula: + +`current earnings = snapshotBalance + ((currentBlock - snapshotBlock) * fee * ebManaged)` + +#### Functions involving operator fees (no signature changes) +```ts +function registerOperator( + bytes calldata publicKey, + uint256 fee, // fee per ETH managed per block + bool setPrivate +) external override returns (uint64 id) +``` + +```ts +function declareOperatorFee( + uint64 operatorId, + uint256 fee // fee per ETH managed per block +) external +``` + +```ts +function reduceOperatorFee( + uint64 operatorId, + uint256 fee // fee per ETH managed per block +) external +``` + +#### Fee management migration + +For all these cases, an ongoing migration process will be performed, meaning that there will not be an external process interacting with operators to change declared fees. + +##### Case 1: Public operators with a declared fee and `validatorCount = 0` +In this case, when new validators use these operators: +``` +1. Convert the current operator's fee value to the new fee per ETH managed. Taking into account the previous fee was declared for a 32 ETH deposited validators, the new fee can be calculated as: +new fee = current fee / 32 +2. Store the effective balance declared by the validator registration and update the operator's snapshot +``` +##### Case 2: Public operators with a declared fee and `validatorCount > 0` +For this case, when: +- Operator removed +- Declare operator fee change +- Withdraw operator earnings +- Register validators +- Remove validators +- Liquidate cluster +- Reactivate cluster + +``` +1. The operator's effective balance managed is calculated this way, taking into account the previous fee was declared for a 32 ETH deposited validators: + +ebManaged = 32 ether * validatorCount + +2. Convert the current operator's fee value to the new fee per ETH managed. The new fee can be calculated as: + +new fee = current fee / 32 + +3. Only when doing validators actions: Store the effective balance declared and update the operator's snapshot +``` + +### Contract - Validators +Validator owners deposit SSV tokens to cover network and operators' fees. This mechanism will be kept but now the total effective balance for all validators in the cluster should be passed as a parameter in these cases: +- Register validators +- Remove validators +- Liquidate cluster +- Reactivate cluster +- Withdraw cluster balance #### Adapted functions +When registering new validators, add a new parameter to indicate the current effective balance of the total amount of validators being added. This will be used to update operators' [snapshots](https://github.com/ssvlabs/ssv-network/blob/583b7b7cb1c1abc5d4c3b13bafca59bf315113b6/contracts/interfaces/ISSVNetworkCore.sol#L10) with the right amounts, calculate the network fee applied to the cluster, and emit the event with this information to allow the node to confirm it. + ```ts function registerValidator( bytes calldata publicKey, uint64[] memory operatorIds, bytes calldata sharesData, - uint256 clusterEB, // This is new + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster uint256 amount, Cluster memory cluster ) external -``` -```ts + function bulkRegisterValidator( bytes[] memory publicKeys, uint64[] memory operatorIds, bytes[] calldata sharesData, - uint256 clusterEB, // This is new + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster uint256 amount, // SSV Cluster memory cluster ) external -``` +function removeValidator( + bytes calldata publicKey, + uint64[] memory operatorIds, + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster + Cluster memory cluster +) external -### New functions +function bulkRemoveValidator( + bytes[] calldata publicKeys, + uint64[] memory operatorIds, + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster + Cluster memory cluster +) external + +function liquidate( + address clusterOwner, + uint64[] calldata operatorIds, + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster + Cluster memory cluster +) external + +function reactivate( + uint64[] calldata operatorIds, + uint256 amount, + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster + Cluster memory cluster +) external + +function withdraw( + uint64[] calldata operatorIds, + uint256 amount, + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster + Cluster memory cluster +) external +``` + +#### New functions ```ts // Function to update the effective balance for a cluster function updateEthEffectiveBalance( uint64[] memory operatorIds, - uint256 clusterEB, // check lower type + uint256 clusterEB, // declare ETH effective balance for all validators in the cluster Cluster memory cluster ) external -``` -```ts + // Function to update the maximum deviation allowed for the ETH // balance declared by validator owners // @dev Using 10000 to represent 2 digit precision: 100% = 10000, 10% = 1000 // @dev Only the owner of the contract can perform the update function updateAllowedBalanceDeviation(uint64 percentage) external onlyOwner -``` -```ts // Function to get the maximum ETH balance deviation function getAllowedBalanceDeviation() - external view returns(uint64 percentage) +external view +returns(uint64 percentage) ``` -### New events +#### New events ```ts event ebUpdated( address indexed owner, uint64[] operatorIds, uint256 clustereb); -``` -```ts event AllowedBalanceDeviationUpdated(uint64 percentage); ``` -### Current implementation +### Contract - Network fee -Operator earnings formula since snapshot: - - -`(currentBlock - snapshotBlock) * operatorFee * operatorValidatorCount` - -### New implementation - -Operator earnings for last snpashot + fees for one block. - -`((currentBlock - snapshotBlock) * (snapshotTotalETH / 32) * operatorFee)` - -Where`snapshotTotalETH`: The total ETH declared by the validators managed by this operator. +Similar to the fee management for operators, the network fee should be moved to the *fee per ETH managed* model. - -#### Network fee -##### Current implementation -`(currentBlock - snapshotBlock) * networkFee * totalValidatorCount` - -##### New implementation -`(currentBlock - snapshotBlock) * (snapshotTotalETH / 32) * networkFee)` - - -#### User should always report effective balance when depositing SSVs +#### New data structures ```ts -function deposit( - address clusterOwner, - uint64[] calldata operatorIds, - uint256 amount, - uint256 clusterEB, - Cluster memory cluster -) external +/// @notice The total declared ETH by validators in the network +uint64 daoEbManaged; ``` -### Fee management migration -Each 32 ETH declared when registering new validators, removing, etc. can be taken as a fee unit of the current validators count for operators and network. +Current formula to calculate network earnings: -These are the data structures to manage operator and network fees: -```ts -/// @notice Represents an SSV operator -struct Operator { - /// @dev The number of validators associated with this operator - uint32 validatorCount; - /// @dev The fee charged by the operator - uint64 fee; - ... -} +`network earnings = daoBalance + ((currentBlock - daoIndexBlockNumber) * networkFee * daoValidatorCount)` -/// @notice The count of validators governed by the DAO -uint32 daoValidatorCount; -/// @notice The current network fee value -uint64 networkFee; -``` +New formula: -Using the formulas described before to calculate operator and network earnings, we can conclude that: +`network earnings = daoBalance + ((currentBlock - daoIndexBlockNumber) * networkFee * daoEbManaged)` -`(snapshotTotalETH / 32)` is a representation of the actual `Operator.validatorCount` and `daoValidatorCount`. So 32 ETH declared is 1 fee unit. -##### Register validators example -Instead of using the number of keys being registered to update operators' and network snapshots and cluster metadata, the result of `declared maxEB / 32` will be used to calculate the fee units. - -##### Proposed naming changes -```ts -/// @notice Represents an SSV operator -struct Operator { - /// @dev The number of fee units associated with this operator - uint32 feeUnits; - /// @dev The fee charged by the operator - uint64 fee; - ... -} - -/// @notice The number of fee units governed by the DAO -uint32 daoFeeUnits; -/// @notice The current network fee value -uint64 networkFee; -``` ### Node