simd: '0123' title: Block Revenue Sharing authors: Justin Starry (Anza) category: Standard type: Core status: Review created: 2024-03-10
A new mechanism is proposed to allow validators to share part of their block revenue with their delegators. Commission rates from validator vote accounts will be used by the protocol to calculate post-commission rewards that will be automatically distributed to delegated stake accounts after an epoch is completed.
Delegated stake directly increases the number of blocks that a validator is allocated in an epoch leader schedule but the core protocol doesn't support diverting any of that extra revenue to stake delegators.
This proposal depends on the following previously accepted proposals:
SIMD-0180: Use Vote Account Address To Key Leader Schedule
Necessary for looking up a block producer's vote account
SIMD-0185: Vote Account v4
Introduces version 4 of the vote account state, which adds new fields for block revenue commission and pending delegation rewards
SIMD-0232: Custom Commission Collector Account
Necessary for looking up a block producer's commission collector account
SIMD-0291: Commssion Rate in Basis Points
Introduces a new instruction type for setting commission rates in basis points
The runtime could ensure that any distributed stake rewards get activated as well but it would require extra complexity in the protocol to support that feature. Instead, stakers will receive inactive SOL in their stake accounts that they will have to manage themselves. SIMD-0022 aims to make this experience better for stakers by allowing stake accounts to separately delegate any unstaked balance in their accounts.
Due to the lack of core protocol support for distributing block revenue to stakers, validators have developed their own solutions which are not enforced by the core protocol. For example, the Cogent validator diverts part of its fee revenue to NFT holders. But it's up the NFT holders to audit and hold Cogent accountable to a specific commission rate.
Another alternative is Jito's mechanism for block "tips" (not fees, but the idea is similar). Jito's validator implementation includes a tip distribution program which it instructs validator operators to divert all of their tips to but cannot enforce perfect compliance. It's up to stakers and the Jito team to audit compliance by validator operators. This mechanism requires trusting a third-party (in this case Jito) to calculate reward distribution in an accurate and fair manner. It also relies on using a merkle tree to distribute fees to all stake accounts and the distributed fees are not automatically staked in recipient stake accounts.
NA
After all transactions are processed in a block for a given leader, rather than collecting all block revenue into the validator identity account, the protocol will look up the block producer's vote account as described in SIMD-0180. Then it MUST check if the validator's vote account has specified a block revenue commission rate and collector addresses in the new vote account version described in SIMD-0185. As described in SIMD-0232, the latest block revenue commission rate and collector address MUST be loaded from the vote account state at the beginning of the previous epoch. This is the same vote account state used to build the leader schedule for the current epoch.
If the block revenue commission rate and collector account aren't set (e.g., the vote account state version has not been updated to v4 yet), all revenue will be collected into the validator's identity account as before. If the block revenue commission rate and collector account are specified, the rewards MUST be distributed according to the commission and delegator rewards collection sections below.
The commission amount MUST be calculated by first multiplying the amount of
revenue by the lesser of the vote account's block revenue commission rate or the
maximum of 10,000 basis points. Then use integer division to divide by
10,000 and discard the remainder. If the commission amount is non-zero, the
block revenue commission collector account MUST be loaded and checked for the
following conditions:
If the conditions are met, the commission amount MUST be deposited into the block revenue commission collector account. If either of these conditions is violated, the commission amount MUST be burned.
The delegator rewards amount MUST be calculated by subtracting the calculated commission from the block fee revenue. If the delegator rewards amount is non-zero, the vote account must be loaded and checked for the following conditions:
If the conditions are met, the delegator rewards amount MUST be added to the
vote state field pending_delegator_rewards and added to the balance of vote
account. If either of these conditions is violated, the delegator rewards amount
MUST be burned.
When calculating stake delegation rewards for a particular completed reward
epoch, construct a list of all vote accounts that were initialized at the
beginning of the reward epoch and had a non-zero active stake delegation. For
each vote account, retrieve its state at the end of the reward epoch and check
the pending_delegator_rewards field in its vote state. Let this value be P.
If P is non-zero, use it to calculate rewards for each of the stake accounts
delegated to the vote account as follows:
Sum all active stake delegated to the vote account during the reward epoch
epoch. Let this total be A.
For each individual stake account, multiply its active stake from the
reward epoch by P, and divide the result by A using integer division.
Discard any fractional lamports.
After calculating all individual stake rewards, sum them to obtain D, the
total distribution amount. Because of integer division, the full amount P may
not be distributed so compute the amount to be burned, B, as the difference
between P and D.
If no blocks in the epoch following the completed reward epoch have been
processed yet, subtract B from both the vote account’s lamport balance and its
pending_delegator_rewards field and store the updated vote account. Finally,
the burn amount B should also be deducted from the cluster capitalization.
The stake reward distribution amounts for each stake account calculated above can then be used to construct a list of stake reward entries which MUST be partitioned and distributed according to SIMD-0118.
When reward entries are used to distribute rewards pool funds during partitioned
rewards distribution, the delegated vote account for each rewarded stake account
must have its pending_delegator_rewards field and its balance deducted with
the amount of rewards distributed to keep capitalization consistent.
Since pending delegator rewards will be stored in the validator's vote account until distribution at the next epoch boundary, those funds will be unable to be withdrawn.
The Withdraw instruction must be modified so that if the balance indicated by
the pending_delegator_rewards field is non-zero, the vote account will no
longer be closeable by fully withdrawing funds. The withdrawable balance when
pending_delegator_rewards is non-zero will be equal to the vote account's
balance minus pending_delegator_rewards and the minimum rent exempt balance.
The UpdateCommissionBps instruction added in SIMD-0291 must be updated to
add support for updating the block revenue commission rate.
When the specified commission kind is CommissionKind::BlockRevenue, update the
block_revenue_commission_bps field instead of the previous behavior of
returning an InstructionError::InvalidInstructionData.
Note that the commission rate is allowed to be set and stored as any u16 value
but as detailed above, it will capped at 10,000 during the actual commission
calculation.
A new instruction for distributing lamports to stake delegators will be added to
the vote program with the enum discriminant value of 18u32 little endian
encoded in the first 4 bytes.
pub enum VoteInstruction {
/// # Account references
/// 0. `[WRITE]` Vote account to be updated with the deposit
/// 1. `[SIGNER, WRITE]` Source account for deposit funds
DepositDelegatorRewards { // 18u32
deposit: u64,
},
}
Perform the following checks:
InstructionError::NotEnoughAccountKeys0) fails to deserialize, return
InstructionError::InvalidAccountDataInstructionError::InvalidAccountDataThen the processor should perform a system transfer CPI of deposit lamports
from the source account (index 1) to the vote account. Lastly, increment the
pending_delegator_rewards value by deposit.
Stake delegators will receive additional income when delegating to validators
who adopt this new feature by setting a block revenue commission rate less than
the default of 100%.
NA
A feature gate will be used to enable block reward distribution at an epoch boundary.