This EIP proposes a neutral effective balance (EB) design to ensure 0x01 (skimming) and 0x02 (compounding) validators receive equal consensus layer (CL) yields. Currently, validators that compound their balance have poor capital efficiency and lower yields as their idle balance (not contributing to EB) is 0.75 ETH, significantly higher than that of skimming validators which is near-zero. This disincentivizes stake consolidation which is critical to Ethereum's fast finality roadmap. Additionally, the current hysteresis rules allows 0x02 validators to game the hysteresis and withdraw down to 32.75 ETH while retaining a higher EB of 33 ETH, contradicting the goal of consolidation. This proposal sets the EB hysteresis upward threshold to the neutral +0.5 and the downward threshold to +0.25. To prevent gaming from operations like partial withdrawals, the EB is upon such operations reset to the floor of the balance, and a one-time temporary_upward_threshold is set. This threshold is derived from an integral calculation that ensures the average idle balance is exactly zero as the validator's balance compounds up to the next integer.
The roadmap for fast finality hinges on stake consolidation, where staking service providers (SSPs) transition from running 32-ETH validators with 0x01 credentials to compounding validators with 0x02 credentials. A roadblock to this transition is that 0x01 validators have close-to-ideal capital efficiency, whereas 0x02 validators have poor capital-efficiency during compounding. Furthermore, the optimal configuration for a 0x02 validator is to reduce its balance to 32.75 ETH and prevent it from compounding further. These incentives may lead Ethereum's fast-finality roadmap to be delayed. To prevent this, a neutral effective balance calculation is proposed, that sees all validator configurations earn the same CL yield.
A 0x02 validator that compounds its balance $b$ as intended will on average have 0.75 ETH of its balance not counting toward its effective balance (EB, or $E$ in equations). Specifically, with the current EB step bands, the idle remainder $r=b-E \in[0.25, 1.25)$ and $\bar{r}=(0.25+1.25)/2=0.75$. A 0x01 validator operating at 32 ETH has a much lower idle remainder, on average half the withdrawal sweep of $0.0235/2 = 0.01175$ ETH. A 32-ETH 0x01 validator will therefore earn around 2.3% more CL yield than a compounding 0x02 validator that grows from 32.25 ETH to 34.25 ETH over around two years.
A separate issue is that 0x02 validators can make a partial withdrawal down to 32.75 ETH when having any EB >33 ETH, leveraging the hysteresis to retain a 33 ETH EB. The surplus capital can then be deployed to increase the staking yield, running multiple 32.75-ETH validators. Assuming a 3% CL yield, recurring partial withdrawals deployed at 33 ETH to bring down the balance to 32.75 ETH, and ignoring the exit queue capital drag, the approach is more profitable than running skimming 2048-ETH validators under current base fees.
To uphold fairness while not disrupting the current operation of 0x01 validators, the following changes are made:
0x02 validator to on average have a $E=b$, just like a 32-ETH 0x01 validator.0x01 to 0x02} are prevented from gaming the protocol. The EB calculation is designed such that each UBO is treated neutrally: $E=b$. To achieve this, the following takes place after every UBO at epoch boundaries:temporary_upward_threshold ($t$) is added to the Validator Container.temporary_upward_threshold, after which temporary_upward_threshold is reset to 0.Change two existing constants:
| Constant | Value | 
|---|---|
| HYSTERESIS_DOWNWARD_MULTIPLIER | 3 | 
| HYSTERESIS_UPWARD_MULTIPLIER | 2 | 
Two new fields are added to the Validator Container:
class Validator(Container):
    ...
    temporary_upward_threshold: Gwei # Initiate to 0
    reset_eb: boolean    # Initiate to False
A new helper is added for initiating EB and temporary_upward_threshold after each UBO at epoch boundaries:
def initiate_effective_balance_and_threshold(v: Validator, balance: Gwei) -> None:
    v.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, get_max_effective_balance(v))
    offset = (EFFECTIVE_BALANCE_INCREMENT + (balance - v.effective_balance)**2 // EFFECTIVE_BALANCE_INCREMENT) // 2
    v.temporary_upward_threshold = v.effective_balance + offset
    v.reset_eb = False
The new helper is called before updating the EB, and the EB update accounts for the temporary_upward_threshold:
def process_effective_balance_updates(state: BeaconState) -> None:
    ...
    # After computing the UPWARD_THRESHOLD
    if validator.reset_eb:
        initiate_effective_balance_and_threshold(validator, balance)
        continue
    # Then, adjust the existing if-clause to account for the temporary_upward_threshold
    max_effective_balance = get_max_effective_balance(validator) 
    balance_floor = balance - balance % EFFECTIVE_BALANCE_INCREMENT
    if balance + DOWNWARD_THRESHOLD < validator.effective_balance:
        validator.effective_balance = min(balance_floor, max_effective_balance)
        validator.temporary_upward_threshold = 0
    elif (validator.effective_balance + UPWARD_THRESHOLD < balance 
        and balance >= validator.temporary_upward_threshold):
        new_eb = balance_floor
        if balance - balance_floor >= EFFECTIVE_BALANCE_INCREMENT // 2:
            new_eb = balance_floor + EFFECTIVE_BALANCE_INCREMENT
        validator.effective_balance = min(new_eb, max_effective_balance)
        validator.temporary_upward_threshold = 0
After each UBO, set reset_eb = True at the point specified in the below table:
| Type | Function name | After the following operation | _.reset_eb = True | 
|---|---|---|---|
| Creation | get_validator_from_deposit | constructing the Validator | validator | 
| Partial withdrawals | process_withdrawals | meeting the new if-clause | validator | 
| Switch to compounding | switch_to_compounding_validator | queue_excess_active_balance | validator | 
| Consolidation | process_pending_consolidations | crediting the target validator | target_validator | 
The new if-clause in process_withdrawals() is:
    # After this existing line
    decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
    # Add a new if-clause
    v = state.validators[withdrawal.validator_index]
    if v.exit_epoch == FAR_FUTURE_EPOCH and has_compounding_withdrawal_credential(v):
        v.reset_eb = True
To prevent gaming after a UBO, the EB is first reset to $E = \lfloor b_0 \rfloor$, where $b_0$ is the new balance. This creates an initial idle remainder $r_0 = b_0 - E$.
A temporary_upward_threshold ($t$) is then set to ensure the average idle balance is zero as the validator's balance compounds from $b_0$ to the next integer, $E+1$.
This threshold $t$ is calculated to solve the "zero-area" problem: the positive idle balance accumulated while $\text{EB}=E$ (from $b_0$ to $t$) must exactly cancel the negative idle balance accumulated while $\text{EB}=E+1$ (from $t$ to $E+1$). This is expressed by the integral equation:
$\int_{b_0}^{t}(b-E) db + \int_{t}^{E+1}(b-(E+1)) db =0.$
Solving for $t$ yields the formula used in the specification:
$t = E + \frac{1+r_0^2}{2},$
where $r_0 = b_0 - E$.
To the best of the authors' knowledge, there are no security risks associated with the proposal.
Copyright and related rights waived via CC0.