1#![cfg_attr(not(feature = "std"), no_std)]
2
3mod ext;
4pub mod migrations;
5
6pub(crate) use ext::*;
7pub use pallet::*;
8use pallet_emission0_api::Emission0Api;
9use polkadot_sdk::{
10 frame_support::{dispatch::DispatchResult, pallet_prelude::*, DefaultNoBound},
11 frame_system,
12 frame_system::pallet_prelude::OriginFor,
13 polkadot_sdk_frame::{self as frame, traits::Currency},
14 sp_runtime::Percent,
15};
16
17#[doc(hidden)]
18pub mod distribute;
19#[doc(hidden)]
20pub mod weight_control;
21
22pub mod benchmarking;
23pub mod weights;
24
25#[frame::pallet]
26pub mod pallet {
27 const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
28
29 use core::num::NonZeroU128;
30
31 use frame::prelude::BlockNumberFor;
32 use frame_system::ensure_signed;
33 use pallet_governance_api::GovernanceApi;
34 use pallet_permission0_api::{Permission0Api, Permission0EmissionApi};
35 use pallet_torus0_api::Torus0Api;
36 use polkadot_sdk::sp_std;
37 use weights::WeightInfo;
38
39 use super::*;
40
41 #[pallet::storage]
45 pub type ConsensusMembers<T: Config> =
46 StorageMap<_, Identity, AccountIdOf<T>, ConsensusMember<T>>;
47
48 #[pallet::storage]
52 pub type WeightControlDelegation<T: Config> =
53 StorageMap<_, Identity, T::AccountId, T::AccountId>;
54
55 #[pallet::storage]
57 pub type EmissionRecyclingPercentage<T: Config> =
58 StorageValue<_, Percent, ValueQuery, T::DefaultEmissionRecyclingPercentage>;
59
60 #[pallet::storage]
63 pub type IncentivesRatio<T: Config> =
64 StorageValue<_, Percent, ValueQuery, T::DefaultIncentivesRatio>;
65
66 #[pallet::storage]
69 pub type PendingEmission<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
70
71 #[pallet::config]
72 pub trait Config: polkadot_sdk::frame_system::Config {
73 type RuntimeEvent: From<Event<Self>>
74 + IsType<<Self as polkadot_sdk::frame_system::Config>::RuntimeEvent>;
75
76 #[pallet::constant]
78 type HalvingInterval: Get<NonZeroU128>;
79
80 #[pallet::constant]
82 type MaxSupply: Get<NonZeroU128>;
83
84 #[pallet::constant]
87 type BlockEmission: Get<u128>;
88
89 #[pallet::constant]
90 type DefaultEmissionRecyclingPercentage: Get<Percent>;
91
92 #[pallet::constant]
93 type DefaultIncentivesRatio: Get<Percent>;
94
95 type Currency: Currency<Self::AccountId, Balance = u128> + Send + Sync;
96
97 type Torus: Torus0Api<Self::AccountId, BalanceOf<Self>>;
98
99 type Governance: GovernanceApi<Self::AccountId>;
100
101 type Permission0: Permission0Api<OriginFor<Self>>
102 + Permission0EmissionApi<
103 Self::AccountId,
104 OriginFor<Self>,
105 BlockNumberFor<Self>,
106 BalanceOf<Self>,
107 NegativeImbalanceOf<Self>,
108 >;
109
110 type WeightInfo: WeightInfo;
111 }
112
113 #[pallet::pallet]
114 #[pallet::storage_version(STORAGE_VERSION)]
115 pub struct Pallet<T>(_);
116
117 #[pallet::hooks]
118 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
119 fn on_initialize(block_number: BlockNumberFor<T>) -> Weight {
120 distribute::distribute_emission::<T>(block_number);
121
122 Weight::zero()
123 }
124 }
125
126 #[pallet::error]
127 pub enum Error<T> {
128 WeightSetTooLarge,
130
131 AgentIsNotRegistered,
133
134 CannotSetWeightsForSelf,
136
137 CannotSetWeightsWhileDelegating,
139
140 CannotDelegateWeightControlToSelf,
142
143 AgentIsNotDelegating,
145
146 NotEnoughStakeToSetWeights,
148
149 WeightControlNotEnabled,
151 }
152
153 #[pallet::event]
154 #[pallet::generate_deposit(pub fn deposit_event)]
155 pub enum Event<T: Config> {
156 WeightsSet(T::AccountId),
158 DelegatedWeightControl(T::AccountId, T::AccountId),
160 }
161
162 #[pallet::call]
163 impl<T: Config> Pallet<T> {
164 #[pallet::call_index(0)]
165 #[pallet::weight((T::WeightInfo::set_weights(), DispatchClass::Normal, Pays::Yes))]
166 pub fn set_weights(
167 origin: OriginFor<T>,
168 weights: sp_std::vec::Vec<(AccountIdOf<T>, u16)>,
169 ) -> DispatchResult {
170 weight_control::set_weights::<T>(origin, weights)
171 }
172
173 #[pallet::call_index(1)]
174 #[pallet::weight((T::WeightInfo::delegate_weight_control(), DispatchClass::Normal, Pays::Yes))]
175 pub fn delegate_weight_control(
176 origin: OriginFor<T>,
177 target: AccountIdOf<T>,
178 ) -> DispatchResult {
179 let origin = ensure_signed(origin)?;
180 weight_control::delegate_weight_control::<T>(origin, target)
181 }
182
183 #[pallet::call_index(2)]
184 #[pallet::weight((T::WeightInfo::regain_weight_control(), DispatchClass::Normal, Pays::Yes))]
185 pub fn regain_weight_control(origin: OriginFor<T>) -> DispatchResult {
186 weight_control::regain_weight_control::<T>(origin)
187 }
188 }
189}
190
191pub type Weights<T> =
192 BoundedVec<(<T as frame_system::Config>::AccountId, u16), ConstU32<{ u32::MAX }>>;
193
194#[derive(CloneNoBound, DebugNoBound, DefaultNoBound, Decode, Encode, MaxEncodedLen, TypeInfo)]
195#[scale_info(skip_type_params(T))]
196pub struct ConsensusMember<T: Config> {
197 pub weights: Weights<T>,
198 pub last_incentives: u16,
199 pub last_dividends: u16,
200}
201
202impl<T: Config> ConsensusMember<T> {
203 pub fn update_weights(&mut self, weights: Weights<T>) {
204 self.weights = weights;
205 }
206}
207
208impl<T: Config> Emission0Api<T::AccountId> for Pallet<T> {
209 fn consensus_stats(
210 member: &T::AccountId,
211 ) -> Option<pallet_emission0_api::ConsensusMemberStats> {
212 ConsensusMembers::<T>::get(member).map(|member| {
213 pallet_emission0_api::ConsensusMemberStats {
214 dividends: member.last_dividends,
215 incentives: member.last_incentives,
216 }
217 })
218 }
219
220 fn delegate_weight_control(
221 delegator: &T::AccountId,
222 delegatee: &T::AccountId,
223 ) -> DispatchResult {
224 weight_control::delegate_weight_control::<T>(delegator.clone(), delegatee.clone())
225 }
226}