Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
fe413cf
Era REward Manager
Ank4n Apr 1, 2026
ef58054
default impl staker reward calculator
Ank4n Apr 1, 2026
de58f7e
new pallet apis
Ank4n Apr 1, 2026
ec33c18
impls
Ank4n Apr 1, 2026
986390f
session rotation
Ank4n Apr 1, 2026
9c8e724
remove default impl for account id provider
Ank4n Apr 1, 2026
bf84f15
staking test compile
Ank4n Apr 1, 2026
d8ff407
integration test compiles
Ank4n Apr 1, 2026
87891e4
fmt
Ank4n Apr 1, 2026
eee0f27
fix tests minimal change
Ank4n Apr 1, 2026
2f74354
update EraPaid rustdoc
Ank4n Apr 1, 2026
6059429
fix all test
Ank4n Apr 1, 2026
1f76881
reward remainder can be removed
Ank4n Apr 1, 2026
ec5bdad
really remove RewardRemainder
Ank4n Apr 1, 2026
ea5bd79
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 1, 2026
ebe54cd
max commission tests
Ank4n Apr 1, 2026
c6e7a45
integrate runtime
Ank4n Apr 1, 2026
0ce1167
fmt
Ank4n Apr 1, 2026
2bf54f1
prdoc
Ank4n Apr 1, 2026
1bca7fc
zepter
Ank4n Apr 1, 2026
83a890e
taplo
Ank4n Apr 1, 2026
e839931
todo for dap bench
Ank4n Apr 1, 2026
d44646a
Update from github-actions[bot] running command 'bench --pallet palle…
github-actions[bot] Apr 2, 2026
7028a80
add missing bench for max commission
Ank4n Apr 2, 2026
04c002b
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 2, 2026
869a3b2
fix test-delegate-stake compile
Ank4n Apr 2, 2026
3283c72
dev weight for staking async
Ank4n Apr 2, 2026
4912afb
fix drip issuance bench
Ank4n Apr 2, 2026
5e17025
weights
Ank4n Apr 2, 2026
0a4040e
taplo
Ank4n Apr 2, 2026
dda4321
add dap bench to WAH
Ank4n Apr 2, 2026
07b2840
fix weight run with frame template
Ank4n Apr 2, 2026
1aa31ea
Update from github-actions[bot] running command 'bench --pallet palle…
github-actions[bot] Apr 2, 2026
8706c6c
ensure pot is created
Ank4n Apr 2, 2026
a811db6
disable legacy minting
Ank4n Apr 2, 2026
c939bc2
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 3, 2026
e77d44e
failing test
Ank4n Apr 3, 2026
cb16275
the fix
Ank4n Apr 3, 2026
e733237
remove staked rewards
Ank4n Apr 3, 2026
d6e23b8
feedback
Ank4n Apr 3, 2026
aa13771
fmt
Ank4n Apr 3, 2026
82aa08e
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 3, 2026
ff0de15
fix benchmarking compilation after removal of max_staked_rewards
sigurpol Apr 3, 2026
4dfa78c
Update from github-actions[bot] running command 'bench --pallet palle…
github-actions[bot] Apr 3, 2026
e83c03b
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 3, 2026
fd11b55
dap in kitchensink
Ank4n Apr 3, 2026
f0ca2a0
Update from github-actions[bot] running command 'bench --pallet palle…
github-actions[bot] Apr 3, 2026
6ed99ab
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 6, 2026
2456443
kusama compat
Ank4n Apr 6, 2026
d22f812
bench fix
Ank4n Apr 6, 2026
6ab8071
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 6, 2026
2b04c3a
fmt
Ank4n Apr 6, 2026
6f7b2b9
license file
Ank4n Apr 6, 2026
48e67bf
disable mint integrity test
Ank4n Apr 6, 2026
9bd2cb9
disable minting try state check
Ank4n Apr 6, 2026
a850691
minimise diff
Ank4n Apr 6, 2026
7b42139
fix nominator reward part
Ank4n Apr 6, 2026
1aa1d07
esnure nominator underpay is detected
Ank4n Apr 6, 2026
a123a1e
cosmetic
Ank4n Apr 6, 2026
e5e7120
update prdoc
Ank4n Apr 6, 2026
9c20c9a
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 7, 2026
864f440
guard against destroy if pot was never created
Ank4n Apr 7, 2026
39caae0
rename ValidatorMissingPayee to just MissingPayee
Ank4n Apr 7, 2026
6ededb6
add stash to missing payee
Ank4n Apr 7, 2026
2552fb6
fmt
Ank4n Apr 7, 2026
4007ee1
ensure if guard set, disable minting in config is always set
Ank4n Apr 7, 2026
520eddf
e2e tests
Ank4n Apr 7, 2026
c01c326
improve legacy -> dap mode test
Ank4n Apr 7, 2026
8b14681
fmt
Ank4n Apr 7, 2026
8f43b97
mock setup to switch impls
Ank4n Apr 7, 2026
14980ee
fmt
Ank4n Apr 7, 2026
45afe25
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 7, 2026
29c8ee5
papi e2e test
Ank4n Apr 8, 2026
0bfd62c
more assertions
Ank4n Apr 9, 2026
b71f5b3
hard assertion
Ank4n Apr 10, 2026
fd3cfe7
assert for era 0
Ank4n Apr 10, 2026
87f6e3f
assert on all events
Ank4n Apr 10, 2026
da0fb80
check all era events
Ank4n Apr 10, 2026
f85c6dc
assert on all events
Ank4n Apr 10, 2026
5be95b0
rename to IssuanceCurve
Ank4n Apr 10, 2026
f953b6d
fix comments
Ank4n Apr 10, 2026
0686742
fmt
Ank4n Apr 10, 2026
229300f
remove misleading hard-pressure curve comment
Ank4n Apr 10, 2026
032b4f2
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 10, 2026
e9bac74
pr feedbacks
Ank4n Apr 13, 2026
3b1ca1d
fmt
Ank4n Apr 13, 2026
eba58d9
debug assert exact
Ank4n Apr 13, 2026
407adc5
Merge branch 'master' into ankn-staker-reward-from-pot
Ank4n Apr 13, 2026
a24b233
combine enum for general and era pots
Ank4n Apr 13, 2026
b98a8a5
fix compile
Ank4n Apr 13, 2026
27941b4
fmt
Ank4n Apr 13, 2026
7c0fd96
update rustdoc for force apply commission
Ank4n Apr 13, 2026
dcf973b
emit missing payee in legacy path
Ank4n Apr 13, 2026
7715bb3
update ErasValidatorReward rustdoc
Ank4n Apr 13, 2026
769339d
fix test delegate stake mock
Ank4n Apr 13, 2026
7975c74
fix rustdoc
Ank4n Apr 13, 2026
a3a6045
dap mode in integration test
Ank4n Apr 13, 2026
282a851
fmt
Ank4n Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,35 @@ parameter_types! {
);
}

/// Provides the initial `LastIssuanceTimestamp` for DAP migration.
pub struct DapLastIssuanceTimestamp;
impl frame_support::traits::Get<u64> for DapLastIssuanceTimestamp {
fn get() -> u64 {
pallet_staking_async::ActiveEra::<Runtime>::get()
.and_then(|era| era.start)
.unwrap_or(0)
}
}

/// Default budget: 85% staker rewards, 15% buffer.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's still a bit unclear how we are going to handle treasury (are we going to drain that account minus the expected budget for next period or what?) - but if this is meant to be an almost no-op replacement of current logic by end of May waiting for a followup referendum to set budget for stakers / incentive / buffer, then this is a breaking change. I am fine with that, and doesn't need to be solved here maybe. But a more "no-op" solution would be to already create a budget component for treasury and do 85% stakers - 15% treasury - 0% DAP as 1st step until the referendum.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also it's a bit unclear to me what we want to do for Kusama. For our mental sanity, it would be nicer to have just one code to maintain and so have also for Kusama configure DAP to mint and assign a budget etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, true this is not completely no-op. Its no-op for stakers. But treasury funds stop. I think this is fine as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn, I have ignored Kusama too long.

Okay, kusama's inflation still depends on total_staked, which IssuanceCurve (the new trait replacing EraPayout) doesn't know about.

One option: make IssuanceCurve return two balances -- the first goes through DAP budget distribution, the second goes directly to treasury (DAP passes it through but can't override it).

For Polkadot: second part is 0, treasury gets its share via DAP budget allocation.
For Kusama: second part is the treasury portion, and DAP budget only handles the staker portion.

It's a bit messy but keeps backward compatibility. Wdyt? Any better ideas?

Copy link
Copy Markdown
Contributor

@sigurpol sigurpol Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a good solution in mind yet for Kusama 😢
Maybe, together with dual path at payout time, this PR could introduce a dual path (DAP-runtimes vs Kusama) for end_era too. So in "DAP mode", we snapshot the general pot -> we fund era_pot etc like you are doing now. For "legacy" mode, we fallback to old EraPayout + Reward reminder.
This maybe buy us a bit of time until we model Treasury budget component explicitly and maybe by then (and not now) we can make DAP work with it both for Polkadot (static) and for Kusama (dynamic - don't know how the dynamic part would be, something where Staking updates the budget allocation at each era boundary based on the staking ratio or so, but doesn't sound trivial at all).

Not sure if we want to tackle in a separate PR with the scope of being "Kusama" friendly -or in this one.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for treasury vs Polkadot, I am fine too but maybe needs some extra communication towards community (out of scope of this PR ofc) and we would need a budget component for treasury eventually

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserved the legacy mode.
So if EraPayout is set in pallet-staking-async (and DisableMinting set to false) then it should work as before with Staking managing inflation. Otherwise, the DAP flow. I have put integrity tests and try state checks to ensure its not easy to configure this wrongly. But please double check this. Bit messy to support both, but I guess this is the best path forward.

pub struct DefaultDapBudget;
impl frame_support::traits::Get<pallet_dap::BudgetAllocationMap> for DefaultDapBudget {
fn get() -> pallet_dap::BudgetAllocationMap {
use sp_runtime::Perbill;
use sp_staking::budget::BudgetRecipientList;

let recipients = <Runtime as pallet_dap::Config>::BudgetRecipients::recipients();
// [dap (buffer), StakerRewardRecipient]
let percentages = [Perbill::from_percent(15), Perbill::from_percent(85)];

let mut map = pallet_dap::BudgetAllocationMap::new();
for ((key, _), perbill) in recipients.into_iter().zip(percentages) {
let _ = map.try_insert(key, perbill);
}
map
}
}

/// Migrations to apply on runtime upgrade.
pub type Migrations = (
// v9420
Expand Down Expand Up @@ -1806,8 +1835,10 @@ pub type Migrations = (
// permanent
pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
// unreleased
// PSM: initialize first external asset (USDT) with fees and ceiling weight.
pallet_psm::migrations::v1::MigrateToV1<Runtime, PsmInitialConfig>,
pallet_dap::migrations::MigrateV1ToV2<Runtime, DapLastIssuanceTimestamp, DefaultDapBudget>,
);

/// Asset Hub Westend has some undecodable storage, delete it.
Expand Down Expand Up @@ -2011,6 +2042,7 @@ mod benches {
[pallet_bags_list, VoterList]
[pallet_balances, Balances]
[pallet_conviction_voting, ConvictionVoting]
[pallet_dap, Dap]
[pallet_election_provider_multi_block, MultiBlockElection]
[pallet_election_provider_multi_block::verifier, MultiBlockElectionVerifier]
[pallet_election_provider_multi_block::unsigned, MultiBlockElectionUnsigned]
Expand Down
48 changes: 28 additions & 20 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,29 +228,28 @@ impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
type WeightInfo = weights::pallet_bags_list::WeightInfo<Runtime>;
}

pub struct EraPayout;
impl pallet_staking_async::EraPayout<Balance> for EraPayout {
fn era_payout(
_total_staked: Balance,
_total_issuance: Balance,
era_duration_millis: u64,
) -> (Balance, Balance) {
parameter_types! {
pub const StakingPotsPalletId: PalletId = PalletId(*b"py/stkng");
Comment thread
Ank4n marked this conversation as resolved.
}

/// Westend inflation curve for DAP.
///
/// Same computation as the previous `EraPayout` but returns total emission.
/// The budget split (ex. 85/15 staker/treasury) is now handled by pallet-dap.
pub struct IssuanceCurve;
impl sp_staking::budget::IssuanceCurve<Balance> for IssuanceCurve {
fn issue(_total_issuance: Balance, elapsed_millis: u64) -> Balance {
const MILLISECONDS_PER_YEAR: u64 = (1000 * 3600 * 24 * 36525) / 100;
// A normal-sized era will have 1 / 365.25 here:
let relative_era_len =
FixedU128::from_rational(era_duration_millis.into(), MILLISECONDS_PER_YEAR.into());
let relative_period =
FixedU128::from_rational(elapsed_millis.into(), MILLISECONDS_PER_YEAR.into());

// Fixed total TI that we use as baseline for the issuance.
let fixed_total_issuance: i128 = 5_216_342_402_773_185_773;
let fixed_inflation_rate = FixedU128::from_rational(8, 100);
let yearly_emission = fixed_inflation_rate.saturating_mul_int(fixed_total_issuance);

let era_emission = relative_era_len.saturating_mul_int(yearly_emission);
// 15% to treasury, as per Polkadot ref 1139.
let to_treasury = FixedU128::from_rational(15, 100).saturating_mul_int(era_emission);
let to_stakers = era_emission.saturating_sub(to_treasury);

(to_stakers.saturated_into(), to_treasury.saturated_into())
let emission = relative_period.saturating_mul_int(yearly_emission);
emission.saturated_into()
}
}

Expand Down Expand Up @@ -287,7 +286,7 @@ impl pallet_staking_async::Config for Runtime {
type NominatorFastUnbondDuration = NominatorFastUnbondDuration;
type SlashDeferDuration = SlashDeferDuration;
type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
type EraPayout = EraPayout;
type EraPayout = ();
type MaxExposurePageSize = MaxExposurePageSize;
type ElectionProvider = MultiBlockElection;
type VoterList = VoterList;
Expand All @@ -301,6 +300,11 @@ impl pallet_staking_async::Config for Runtime {
type PlanningEraOffset = ConstU32<6>;
type RcClientInterface = StakingRcClient;
type MaxEraDuration = MaxEraDuration;
type DisableMinting = ConstBool<true>;
type UnclaimedRewardHandler = Dap;
type RewardPots = pallet_staking_async::Seed<StakingPotsPalletId>;
type StakerRewardCalculator =
pallet_staking_async::reward::DefaultStakerRewardCalculator<Runtime>;
type MaxPruningItems = MaxPruningItems;
type WeightInfo = weights::pallet_staking_async::WeightInfo<Runtime>;
}
Expand Down Expand Up @@ -353,9 +357,13 @@ parameter_types! {
impl pallet_dap::Config for Runtime {
type Currency = Balances;
type PalletId = DapPalletId;
/// Noop — DAP does not mint until budget drip is enabled.
type IssuanceCurve = ();
type BudgetRecipients = (pallet_dap::Pallet<Runtime>,);
type IssuanceCurve = IssuanceCurve;
type BudgetRecipients = (
pallet_dap::Pallet<Runtime>,
pallet_staking_async::StakerRewardRecipient<
pallet_staking_async::Seed<StakingPotsPalletId>,
>,
);
type Time = pallet_timestamp::Pallet<Runtime>;
type IssuanceCadence = IssuanceCadence;
type MaxElapsedPerDrip = MaxElapsedPerDrip;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Autogenerated weights for `pallet_dap`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2026-04-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `5851f50484f3`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024

// Executed Command:
// frame-omni-bencher
// v1
// benchmark
// pallet
// --extrinsic=*
// --runtime=target/production/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.wasm
// --pallet=pallet_dap
// --header=/__w/polkadot-sdk/polkadot-sdk/cumulus/file_header.txt
// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights
// --wasm-execution=compiled
// --steps=50
// --repeat=20
// --heap-pages=4096
// --no-storage-info
// --no-min-squares
// --no-median-slopes

#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]

use frame_support::{traits::Get, weights::Weight};
use core::marker::PhantomData;

/// Weight functions for `pallet_dap`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_dap::WeightInfo for WeightInfo<T> {
/// Storage: `Dap::BudgetAllocation` (r:0 w:1)
/// Proof: `Dap::BudgetAllocation` (`max_values`: Some(1), `max_size`: Some(593), added: 1088, mode: `MaxEncodedLen`)
fn set_budget_allocation() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 7_979_000 picoseconds.
Weight::from_parts(8_699_000, 0)
.saturating_add(Weight::from_parts(0, 0))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: `Timestamp::Now` (r:1 w:0)
/// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
/// Storage: `Dap::LastIssuanceTimestamp` (r:1 w:1)
/// Proof: `Dap::LastIssuanceTimestamp` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
/// Storage: `Dap::BudgetAllocation` (r:1 w:0)
/// Proof: `Dap::BudgetAllocation` (`max_values`: Some(1), `max_size`: Some(593), added: 1088, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:2 w:2)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn drip_issuance() -> Weight {
// Proof Size summary in bytes:
// Measured: `2541`
// Estimated: `6196`
// Minimum execution time: 62_672_000 picoseconds.
Weight::from_parts(65_724_000, 0)
.saturating_add(Weight::from_parts(0, 6196))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(3))
}
}
Loading
Loading