[Staking] Move reward minting to DAP; payouts from drip-funded era pots#11616
[Staking] Move reward minting to DAP; payouts from drip-funded era pots#11616
Conversation
|
/cmd bench --pallet pallet_staking_async |
|
Command "bench --pallet pallet_staking_async" has started 🚀 See logs here |
|
Command "bench --pallet pallet_staking_async" has finished ✅ See logs here DetailsSubweight results:
Command output:✅ Successful benchmarks of runtimes/pallets: |
| /// Creates an era pot account by adding a provider reference. | ||
| /// | ||
| /// Should only be called in non-minting mode (`DisableMinting = true`). | ||
| pub(crate) fn create(era: EraIndex, pot_type: EraPotType) -> T::AccountId { |
There was a problem hiding this comment.
| pub(crate) fn create(era: EraIndex, pot_type: EraPotType) -> T::AccountId { | |
| fn create(era: EraIndex, pot_type: EraPotType) -> T::AccountId { |
Should not be called anywhere else I presume?
There was a problem hiding this comment.
Its called in benchmarks.
| validator_total_reward: BalanceOf<T>, | ||
| validator_commission: Perbill, | ||
| validator_own_stake: BalanceOf<T>, | ||
| total_stake: BalanceOf<T>, |
There was a problem hiding this comment.
| total_stake: BalanceOf<T>, | |
| total_exposure: BalanceOf<T>, |
not to be mistaken with total stake that could mean something different (total % of DOTs staked)
kianenigma
left a comment
There was a problem hiding this comment.
In terms of review, I managed to get over this in one pass (to my own surprise)
What I think is missing is:
- View functions for pots, enabling UIs to display them easier
- Adding the budgets to the genesis of the test runtime, enabling more tests
- I manually tested that if we let the chain run for 4 eras, the following are correct: Pot accounts are created and populated. The
ErasValidatorRewardsis the right snapshot. calling a payout works. But this can be codified in a test as well.
- I manually tested that if we let the chain run for 4 eras, the following are correct: Pot accounts are created and populated. The
- For now I am using 2 pallet ids to resolve conflicting account ids.
My half finish work while reviewing this is:
https://github.com/paritytech/polkadot-sdk/compare/ankn-staker-reward-from-pot...kiz-dap3-review?expand=1
I made a simple UI for this in https://github.com/kianenigma/apps/tree/kiz-dap
Good point. I will add them in the following PR (#11651).
ahh very good point. I did it in unit tests but not in the integration tests. Let me do and see what fails.
There is this e2e drip test that runs for one era and verifies drips are continuous and in expected range, as well as the final snapshot reward is sensible. |
Extracted from paritytech#10844. Builds on paritytech#11616. Specs: https://hackmd.io/@jonasW3F/rkN6BXE2ex ## Overview Adds a separate validator self-stake incentive reward track. Validators receive an additional bonus from a dedicated incentive pot, proportional to their self-stake weight. Payout is flat liquid. Vesting implementation will be in a follow-up PR. ## Incentive Weight Curve Each validator's share of the incentive pot is determined by a piecewise sqrt function: - Below optimum: `w(s) = √s` - Between optimum and cap: `w(s) = √(T + k² × (s - T))` (diminishing returns) - Above cap: plateau Weights are calculated during era planning and stored per-validator. At payout, each validator's incentive = `(their_weight / total_weight) × era_incentive_budget`, prorated across pages. ## Changes **New storage:** `OptimumSelfStake`, `HardCapSelfStake`, `SelfStakeSlopeFactor`, `ErasValidatorIncentiveAllocation` (era budget snapshot), `ErasTotalValidatorWeight`, `ErasValidatorIncentive` (per-validator weights). **New extrinsic:** `set_validator_self_stake_incentive_config`: sets optimum, cap, and slope factor. Callable by StakingAdmin. Validates optimum ≤ cap. **New event:** `ValidatorIncentivePaid { era, validator_stash, dest, amount }` **Era lifecycle:** `end_era_dap` now snapshots both staker reward and incentive pots. Era planning calculates and stores incentive weights for each elected validator. Pruning cleans up `ErasValidatorIncentive` storage. **Payout:** `do_payout_stakers_by_page` calls `pay_validator_incentive_for_page` after staker rewards. Direct `Currency::transfer` from incentive era pot to payout account. ## TODO - [x] Run staking async bench - [x] Update integration test with self stake incentive setup. - [x] View Functions for Staking Pots. - [ ] Test with and improve on [Kian's UI](https://github.com/kianenigma/apps/tree/kiz-dap) --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Paolo La Camera <paolo@parity.io>
Extracted from #10844.
Overview
This PR introduces dual-mode era rewards for
pallet-staking-async:DisableMinting = true): Staking does not mint. An external source (e.g.pallet-dap) funds a general reward pot. Staking snapshots the pot into era-specific accounts at each era boundary. Payouts transfer from the era pot.DisableMinting = false):EraPayoutcomputes inflation, tokens are minted on-the-fly during payout. Kept for Kusama compatibility where inflation depends on the staking ratio.Switching from legacy to non-minting is a one-way migration.
How it works
Non-minting mode (Polkadot)
DAP drips inflation continuously into a general staker reward pot. At each era boundary,
EraRewardManager::snapshot_era_rewardstransfers the accumulated balance into an era-specific pot. Payouts transfer from that pot. When an era expires pastHistoryDepth, unclaimed funds are returned viaUnclaimedRewardHandler.DisableMintingGuardis set on the first successful snapshot as a payout-side safety net -- prevents minting for eras that should have pots.Runtime Changes
Staking Async
DisableMinting = ConstBool<true>EraPayout = ()(noop, never called)RewardRemainder = (),MaxEraDuration = ()GeneralPots/EraPots=Seed<StakingPotsPalletId>UnclaimedRewardHandler= DAP (or anyOnUnbalancedhandler)DAP
IssuanceCurve-- same as EraPayout except it does not do the reward-treasury split and returns one single amount.DAP::bufferandStaking::StakerRewardRecipientinBudgetRecipients.MigrateV1ToV2to seedLastIssuanceTimestampandBudgetAllocationLegacy minting mode (Kusama and other non-polkadot runtimes)
Same as before:
EraPayout::era_payout()computes inflation fromtotal_staked,total_issuance, and era duration.MaxStakedRewardscaps the staker portion. Remainder goes toRewardRemainder. Tokens minted on payout.Runtime Changes
Staking Async
DisableMinting = ConstBool<false>EraPayout= same as beforeGeneralPots/EraPots=Seed<AnyPalletId>(required to compile, never called)No DAP needed
Config Changes
Added:
DisableMinting: Get<bool>: compile-time constant controlling which mode. Irreversible oncetrue.UnclaimedRewardHandler: receives unclaimed era rewards during stale era cleanup.GeneralPots/EraPots: pot account providers for era reward bookkeeping.StakerRewardCalculator: Implementation for commission based reward-split between the validator and nominators.Preserved for legacy mode (Kusama):
EraPayout,RewardRemainder,MaxEraDuration,MaxStakedRewards: used only whenDisableMinting = false.New storage:
MaxCommission: upper bound on validator commission (defaults to 100%).DisableMintingGuard: safety guard: era from which legacy minting is permanently disabled on the payout side.New extrinsic:
set_max_commission: callable viaAdminOrigin(StakingAdmin).WAH
PolkadotIssuanceCurvereplaces oldEraPayoutimpl.MigrateV1ToV2migration.TODO