Move era reward minting to DAP with era-based allocation in Staking#10844
Closed
Move era reward minting to DAP with era-based allocation in Staking#10844
Conversation
1 task
Contributor
Author
|
Update: Splitting this PR into smaller pieces:
A final PR stacked on #11527 (either this or new if too many git conflicts) will integrate everything together with the staking changes. What do you guys think @sigurpol @kianenigma ? |
Contributor
+1 for small PRs and a final one (this / new one ) to wire all together! Will try to review all the first 3 PRs Monday the latest |
github-merge-queue Bot
pushed a commit
that referenced
this pull request
Mar 31, 2026
## Context This trait is needed by `pallet-staking-async` to convert validator incentive holds into vesting schedules without coupling to a specific currency trait. Will be required by #10844 ## Changes - `VestedPayout<AccountId, Balance>`: callers specify total amount and duration only. The implementor handles per-block computation, block-number provider, and currency mechanism internally. Unlike the existing `VestedTransfer` trait, this is agnostic to both the old `Currency` and new `fungible` traits. - `pallet-vesting` implements the trait. Zero duration is treated as a liquid transfer (no schedule). --------- Co-authored-by: Paolo La Camera <paolo@parity.io>
github-merge-queue Bot
pushed a commit
that referenced
this pull request
Mar 31, 2026
…1513) # Overview Moves `EraPayout` trait to `sp-staking` and adds new traits for issuance, budget distribution, and reward calculation. Extracted changes from the main PR: #10844 Also see: #11512 ## Changes ### sp-staking - `EraPayout`: moved from `pallet-staking` and `pallet-staking-async` to `sp-staking`, eliminating the duplicate definitions. - `budget` module: stake independent traits: - `IssuanceCurve`: successor to `EraPayout`, computes issuance from total supply + elapsed time (no staking state dependency). - `BudgetRecipient` / `BudgetRecipientList`: register pot accounts for inflation distribution. Runtime wires as a tuple. - `BudgetKey`: bounded identifier for budget categories. - `StakerRewardCalculator`: trait for calculating validator incentive weights and staker reward splits. ### pallet-staking / pallet-staking-async - Removed local `EraPayout` definitions, re-exported from `sp-staking`. --------- Co-authored-by: Paolo La Camera <paolo@parity.io>
github-merge-queue Bot
pushed a commit
that referenced
this pull request
Mar 31, 2026
Stacked on #11513. Extracted from #10844. ## Overview Adds issuance drip and budget distribution to `pallet-dap`. DAP becomes a generic inflation engine: it mints new tokens on a configurable cadence and distributes them to registered budget recipients. No runtime behavior change: the existing chain configuration continues to mint via `EraPayout` trait in staking. DAP can be configured as a noop (cadence = 0 or empty budget). ## Changes ### pallet-dap - **Issuance drip**: `drip_issuance()` runs in `on_initialize`. Computes mint amount via `IssuanceCurve` (total issuance + elapsed time) and distributes to `BudgetRecipient`s per a governance-updatable `BudgetAllocation` map that must sum to 100%. - **Safety guards**: `MaxElapsedPerDrip` ceiling prevents over-minting if the chain stalls. First-block initialization skips drip to avoid minting for an unknown period. - **Buffer accounting**: buffer's share is deactivated on inflow (mint + `OnUnbalanced` slashes). - **`set_budget_allocation`** extrinsic (root-only): validates keys match registered recipients and percentages sum to exactly 100%. - **`BudgetRecipient` impl**: DAP exposes its buffer as a recipient (key: `"buffer"`). - **Migration**: `MigrateV1ToV2` seeds `LastIssuanceTimestamp` and `BudgetAllocation` for existing chains. Not wired up in WAH or other runtimes yet. ## TODOs - [x] Wire benchmark weights. ## In Later PR - Revert [these changes](9b388b2) --------- Co-authored-by: Paolo La Camera <paolo@parity.io>
lexnv
added a commit
that referenced
this pull request
Apr 1, 2026
Squashed commit of the following: commit 1b3ad0b Author: Alexandru Vasile <alexandru.vasile@parity.io> Date: Wed Apr 1 10:55:36 2026 +0000 Adjust metrics Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> commit 61d37db Author: Alexandru Vasile <alexandru.vasile@parity.io> Date: Wed Apr 1 09:58:38 2026 +0000 metrics: Bump some other buckets Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> commit ab2a221 Author: Alexandru Vasile <alexandru.vasile@parity.io> Date: Wed Apr 1 09:57:11 2026 +0000 collator-proto/metrics: Fix blindspot in collation fetch latency metrics Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> commit 0d6888a Author: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Wed Apr 1 00:18:21 2026 +0200 Implement Budget Distribution logic in pallet-dap (#11527) Stacked on #11513. Extracted from #10844. ## Overview Adds issuance drip and budget distribution to `pallet-dap`. DAP becomes a generic inflation engine: it mints new tokens on a configurable cadence and distributes them to registered budget recipients. No runtime behavior change: the existing chain configuration continues to mint via `EraPayout` trait in staking. DAP can be configured as a noop (cadence = 0 or empty budget). ## Changes ### pallet-dap - **Issuance drip**: `drip_issuance()` runs in `on_initialize`. Computes mint amount via `IssuanceCurve` (total issuance + elapsed time) and distributes to `BudgetRecipient`s per a governance-updatable `BudgetAllocation` map that must sum to 100%. - **Safety guards**: `MaxElapsedPerDrip` ceiling prevents over-minting if the chain stalls. First-block initialization skips drip to avoid minting for an unknown period. - **Buffer accounting**: buffer's share is deactivated on inflow (mint + `OnUnbalanced` slashes). - **`set_budget_allocation`** extrinsic (root-only): validates keys match registered recipients and percentages sum to exactly 100%. - **`BudgetRecipient` impl**: DAP exposes its buffer as a recipient (key: `"buffer"`). - **Migration**: `MigrateV1ToV2` seeds `LastIssuanceTimestamp` and `BudgetAllocation` for existing chains. Not wired up in WAH or other runtimes yet. ## TODOs - [x] Wire benchmark weights. ## In Later PR - Revert [these changes](9b388b2) --------- Co-authored-by: Paolo La Camera <paolo@parity.io> commit 8b53323 Author: Javier Viola <363911+pepoviola@users.noreply.github.com> Date: Tue Mar 31 21:26:58 2026 +0200 bump zombienet version to v0.4.9 (#11569) This version move the internal monitor to a diff log target, eliminating the current spam of tracing logs. Thx! --------- Co-authored-by: Bastian Köcher <git@kchr.de> commit 129a48a Author: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Tue Mar 31 19:06:21 2026 +0200 [Staking] Add issuance and budget traits in prep for Budget Split (#11513) # Overview Moves `EraPayout` trait to `sp-staking` and adds new traits for issuance, budget distribution, and reward calculation. Extracted changes from the main PR: #10844 Also see: #11512 ## Changes ### sp-staking - `EraPayout`: moved from `pallet-staking` and `pallet-staking-async` to `sp-staking`, eliminating the duplicate definitions. - `budget` module: stake independent traits: - `IssuanceCurve`: successor to `EraPayout`, computes issuance from total supply + elapsed time (no staking state dependency). - `BudgetRecipient` / `BudgetRecipientList`: register pot accounts for inflation distribution. Runtime wires as a tuple. - `BudgetKey`: bounded identifier for budget categories. - `StakerRewardCalculator`: trait for calculating validator incentive weights and staker reward splits. ### pallet-staking / pallet-staking-async - Removed local `EraPayout` definitions, re-exported from `sp-staking`. --------- Co-authored-by: Paolo La Camera <paolo@parity.io> commit 10a517e Author: Dmitry Markin <dmitry@markin.tech> Date: Tue Mar 31 18:37:01 2026 +0300 Implement `bitswap_v1_get` RPC method (#11521) Implement `bitswap_v1_get` RPC method according to the [spec](https://github.com/paritytech/json-rpc-interface-spec/blob/main/src/api/bitswap_v1_get.md). --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Lukasz Rubaszewski <117115317+lrubasze@users.noreply.github.com> commit 2c99855 Author: Paolo La Camera <paolo@parity.io> Date: Tue Mar 31 16:42:03 2026 +0200 Fix can_inc_consumer check blocking session key rotation in pallet_session (#11573) Check consumer capacity only when we actually increment the consumer count (first-time local registration or external-to-local transition), not on key rotation. Notes: 1. setting keys via `stakingRcClient` didn't suffer of this issue since by-pass `do_set_keys()` 2. setting and purging keys via session pallet directly on RC is deprecated and will be removed soon - but better safe than sorry till then --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> commit de0a245 Author: Luka Ciric <luka.ciric2106@gmail.com> Date: Tue Mar 31 16:39:48 2026 +0200 Raise offence queue eras bound limit (#11435) Fixes a bug where **OffenceQueueEras** bound (BondingDuration) was incorrect when `SlashDeferDuration=0`. The oldest reportable offence era formula allowed more eras than the bound could hold. --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
2 tasks
4 tasks
franciscoaguirre
pushed a commit
that referenced
this pull request
Apr 7, 2026
…1513) # Overview Moves `EraPayout` trait to `sp-staking` and adds new traits for issuance, budget distribution, and reward calculation. Extracted changes from the main PR: #10844 Also see: #11512 ## Changes ### sp-staking - `EraPayout`: moved from `pallet-staking` and `pallet-staking-async` to `sp-staking`, eliminating the duplicate definitions. - `budget` module: stake independent traits: - `IssuanceCurve`: successor to `EraPayout`, computes issuance from total supply + elapsed time (no staking state dependency). - `BudgetRecipient` / `BudgetRecipientList`: register pot accounts for inflation distribution. Runtime wires as a tuple. - `BudgetKey`: bounded identifier for budget categories. - `StakerRewardCalculator`: trait for calculating validator incentive weights and staker reward splits. ### pallet-staking / pallet-staking-async - Removed local `EraPayout` definitions, re-exported from `sp-staking`. --------- Co-authored-by: Paolo La Camera <paolo@parity.io>
github-merge-queue Bot
pushed a commit
that referenced
this pull request
Apr 13, 2026
…ts (#11616) Extracted from #10844. ## Overview This PR introduces dual-mode era rewards for `pallet-staking-async`: - **Non-minting mode** (`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. - **Legacy minting mode** (`DisableMinting = false`): `EraPayout` computes 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_rewards` transfers the accumulated balance into an era-specific pot. Payouts transfer from that pot. When an era expires past `HistoryDepth`, unclaimed funds are returned via `UnclaimedRewardHandler`. `DisableMintingGuard` is 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 any `OnUnbalanced` handler) **DAP** - Implement `IssuanceCurve` -- same as EraPayout except it does not do the reward-treasury split and returns one single amount. - register `DAP::buffer` and `Staking::StakerRewardRecipient` in `BudgetRecipients`. - Run `MigrateV1ToV2` to seed `LastIssuanceTimestamp` and `BudgetAllocation` - Pre-fund general staker pot with ED ### Legacy minting mode (Kusama and other non-polkadot runtimes) Same as before: `EraPayout::era_payout()` computes inflation from `total_staked`, `total_issuance`, and era duration. `MaxStakedRewards` caps the staker portion. Remainder goes to `RewardRemainder`. Tokens minted on payout. #### Runtime Changes **Staking Async** - `DisableMinting = ConstBool<false>` - `EraPayout` = same as before - `GeneralPots` / `EraPots` = `Seed<AnyPalletId>` (required to compile, never called) No DAP needed ## Config Changes **Added:** - `DisableMinting: Get<bool>`: compile-time constant controlling which mode. **Irreversible** once `true`. - `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 when `DisableMinting = 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 via `AdminOrigin` (StakingAdmin). ## WAH - `PolkadotIssuanceCurve` replaces old `EraPayout` impl. - DAP `MigrateV1ToV2` migration. ## TODO - [x] Run benchmark for staking and dap pallets. - [ ] Create issue to pre fund general staking pots with ED post merge. --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Paolo La Camera <paolo@parity.io>
pull Bot
pushed a commit
to cshein45/polkadot-sdk
that referenced
this pull request
Apr 21, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Update (Apr 1, 2026)
This PR is broken down into smaller ones:
Partially implements: https://hackmd.io/@jonasW3F/rkN6BXE2ex
Close #10487 .
Overview
This PR moves era reward minting logic from
pallet-staking-asynctopallet-dapas well as introducing budget allocation management to DAP and a separate validator self-stake incentive mechanism to staking.Changes
Reviewing in this order would help as well.
1. New Traits (sp-staking / frame-support)
InflationCurve— replacesEraPayout. Takes(total_issuance, elapsed_millis), no staking state dependency.BudgetRecipient/BudgetRecipientList— register pot accounts with DAP. Runtime wires as a tuple.UnclaimedRewardSink— singledeposit(source, amount) -> DispatchResultmethod. Implementor handles both the transfer and bookkeeping (e.g. deactivation).StakerRewardCalculator— commission-based staker split (replaces inline logic).VestedPayout(frame-support) — generic vested transfer using fungible traits.ValidatorIncentivePayout— adapter trait withImmediateIncentivePayoutandVestedIncentivePayoutimplementations.2. Inflation Drip and Budget Distribution (pallet-dap)
DAP is a generic inflation drip + distribution engine. It has no dependency on staking.
drip_inflation()runs inon_initialize. It computes how much to mint viaInflationCurve(total issuance + elapsed millis) and distributes to registeredBudgetRecipients according to aBoundedBTreeMap<BudgetKey, Perbill>that must sum to exactly 100%. Rounding dust is not minted. Usespallet_timestampfor time (validated by relay chain validators, see #9723).Safety guards:
MaxElapsedPerDrip— ceiling on elapsed time per drip. Prevents over-minting if AH gets stuck and comes back. Must be >InflationCadence(enforced byintegrity_test).LastInflationTimestampis 0, initializes without dripping. For existing chains,MigrateV1ToV2seeds this fromActiveEra.start.Buffer accounting:
InactiveIssuance) on every inflow — inflation mint, slash deposit (OnUnbalanced), and unclaimed reward transfers (UnclaimedRewardSink::deposit).withdraw_buffer,deposit_buffer) let governance move funds between buffer and other accounts (e.g. treasury), with symmetric reactivation/deactivation.3. Era Pot Lifecycle (staking-async)
DAP drips continuously into general pots. At era boundary (
end_era),EraRewardManager::snapshot_era_rewardstransfers accumulated balances from general pots into era-specific pot accounts (one for staker rewards, one for validator incentives). Era pots get provider refs to prevent reaping.When an era expires past
HistoryDepth,cleanup_eracallsUnclaimedRewardSink::depositto move remaining funds to the DAP buffer and drops the provider.DisableLegacyMintingErais set on first successful snapshot. After that, legacy mint-on-payout is blocked. This ensures a clean transition — old eras (before migration) still use legacy minting, new eras use era pots.4. Reward Payout (staking-async)
do_payout_stakers_by_pagechecks whether the era has a reward pot. If yes, transfers from the era pot. If no (old eras), falls back to legacy minting (unlessDisableLegacyMintingErablocks it).Staker reward split uses commission:
StakerRewardCalculator::calculate_staker_rewardcomputescommission_payout = commission * total_reward, splits the leftover proportionally by stake. Commission is capped byMaxCommission(governance-set storage item, defaults to 100%).5. Validator Self-Stake Incentive Curve
A separate reward track funded from the validator incentive pot. Each validator gets a weight based on their self-stake using a sqrt-based piecewise function:
w(s) = sqrt(s)w(s) = sqrt(T + k² × (s - T))— diminishing returns viaSelfStakeSlopeFactorWeights are stored in
ErasValidatorIncentiveduring era planning. At payout, each validator's incentive share =(their_weight / total_weight) * incentive_budget * page_stake_part. The incentive is prorated across pages proportional to each page's stake relative to the validator's total exposed stake.Config params (
OptimumSelfStake,HardCapSelfStake,SelfStakeSlopeFactor) are set viaset_validator_self_stake_incentive_config(root-only).Initial deployment sets validator incentive budget to 0% — no behavior change until governance explicitly enables it.
6. Vested Incentive Payout
Validator incentive rewards are held under
HoldReason::IncentiveVesting. On batch-boundary eras (era % BondingDuration == 0), accumulated holds are converted to a vesting schedule viapallet-vesting. The retroactive unlock fraction =BondingDuration / vesting_eras, with the remainder vesting linearly. IfVestingDurationis zero, payouts are liquid.Vesting uses relay chain block numbers (not parachain blocks) since parachain block times vary with elastic scaling.
This batching avoids exhausting
MaxVestingSchedules. With bonding duration of 28 eras and vesting duration of 1 year, we create ~13 vesting schedules per year (well within the 28 limit). If the vested transfer fails, funds keep accumulating under hold until the next batch boundary.Conversion triggers when
era % BondingDuration == 0AND all pages are claimed. If a validator claims their last page after the batch boundary, they wait until the next one. Funds remain held in the interim.Runtime Integration
Runtimes need to make the following changes:
InflationCurveimplementation topallet-dap::Config.BudgetRecipientswith(StakerRewardRecipient, ValidatorIncentiveRecipient).BudgetOriginfor governance-controlled budget allocation updates.ValidatorIncentivePayout,VestingDuration, andBlocksPerSession.Out of Scope
TODO
Open Questions