Skip to main content

sp_staking/
budget.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Traits for inflation issuance and budget distribution.
19//!
20//! These traits define how new tokens are minted and distributed among budget recipients
21//! (e.g. staker rewards, validator incentives).
22
23use alloc::vec::Vec;
24use sp_runtime::BoundedVec;
25
26/// Maximum length of a budget key identifier.
27pub const MAX_BUDGET_KEY_LEN: u32 = 32;
28
29/// Identifier for a budget category in the inflation distribution system.
30///
31/// Each budget recipient (e.g. staker rewards, validator incentive) is identified
32/// by a unique key. Keys are bounded to [`MAX_BUDGET_KEY_LEN`] bytes.
33pub type BudgetKey = BoundedVec<u8, sp_core::ConstU32<MAX_BUDGET_KEY_LEN>>;
34
35/// Computes new token issuance for a given time period.
36///
37/// Unlike [`super::EraPayout`], this trait does not depend on staking state. Issuance is
38/// purely a function of total supply and elapsed time.
39pub trait IssuanceCurve<Balance> {
40	/// Compute how much new tokens to mint for the given period.
41	fn issue(total_issuance: Balance, elapsed_millis: u64) -> Balance;
42}
43
44impl<Balance: Default> IssuanceCurve<Balance> for () {
45	fn issue(_total_issuance: Balance, _elapsed_millis: u64) -> Balance {
46		Default::default()
47	}
48}
49
50/// A recipient of inflation budget.
51///
52/// Pallets that want a share of inflation implement this trait, providing a unique key
53/// and a pot account where minted funds are deposited.
54pub trait BudgetRecipient<AccountId> {
55	/// Unique identifier for this budget category.
56	fn budget_key() -> BudgetKey;
57	/// The account that receives minted inflation funds.
58	fn pot_account() -> AccountId;
59}
60
61/// Aggregates multiple [`BudgetRecipient`]s into a list.
62///
63/// Implemented for tuples of `BudgetRecipient` types, allowing runtime configuration like:
64/// ```ignore
65/// type BudgetRecipients = (StakerRewardRecipient, ValidatorIncentiveRecipient);
66/// ```
67pub trait BudgetRecipientList<AccountId> {
68	/// Collect all registered recipients as `(key, account)` pairs.
69	fn recipients() -> Vec<(BudgetKey, AccountId)>;
70}
71
72impl<AccountId> BudgetRecipientList<AccountId> for () {
73	fn recipients() -> Vec<(BudgetKey, AccountId)> {
74		Vec::new()
75	}
76}
77
78#[impl_trait_for_tuples::impl_for_tuples(1, 10)]
79#[tuple_types_custom_trait_bound(BudgetRecipient<AccountId>)]
80impl<AccountId> BudgetRecipientList<AccountId> for Tuple {
81	fn recipients() -> Vec<(BudgetKey, AccountId)> {
82		let mut v = Vec::new();
83		for_tuples!( #( v.push((Tuple::budget_key(), Tuple::pot_account())); )* );
84		debug_assert!(
85			{
86				let mut keys: Vec<_> = v.iter().map(|(k, _)| k.clone()).collect();
87				keys.sort();
88				keys.windows(2).all(|w| w[0] != w[1])
89			},
90			"Duplicate BudgetRecipient key detected"
91		);
92		v
93	}
94}
95
96#[cfg(test)]
97mod tests {
98	use super::*;
99
100	struct RecipientA;
101	impl BudgetRecipient<u64> for RecipientA {
102		fn budget_key() -> BudgetKey {
103			BudgetKey::truncate_from(b"alpha".to_vec())
104		}
105		fn pot_account() -> u64 {
106			1
107		}
108	}
109
110	struct RecipientB;
111	impl BudgetRecipient<u64> for RecipientB {
112		fn budget_key() -> BudgetKey {
113			BudgetKey::truncate_from(b"beta".to_vec())
114		}
115		fn pot_account() -> u64 {
116			2
117		}
118	}
119
120	// Duplicate key: same as RecipientA.
121	struct RecipientDuplicate;
122	impl BudgetRecipient<u64> for RecipientDuplicate {
123		fn budget_key() -> BudgetKey {
124			BudgetKey::truncate_from(b"alpha".to_vec())
125		}
126		fn pot_account() -> u64 {
127			3
128		}
129	}
130
131	#[test]
132	fn unique_keys_work() {
133		let recipients = <(RecipientA, RecipientB) as BudgetRecipientList<u64>>::recipients();
134		assert_eq!(recipients.len(), 2);
135	}
136
137	#[test]
138	#[should_panic(expected = "Duplicate BudgetRecipient key detected")]
139	fn duplicate_keys_panics() {
140		let _ = <(RecipientA, RecipientDuplicate) as BudgetRecipientList<u64>>::recipients();
141	}
142}