pallet_emission0/
lib.rs

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    /// Map of consensus members indexed by their keys. A consensus member is
42    /// any agent eligible for emissions in the next epoch. This means
43    /// unregistered agents will also receive emissions.
44    #[pallet::storage]
45    pub type ConsensusMembers<T: Config> =
46        StorageMap<_, Identity, AccountIdOf<T>, ConsensusMember<T>>;
47
48    /// Map of agents delegating weight control to other agents. Emissions
49    /// derived from weight delegation are taxed and the fees go the original
50    /// weight setter.
51    #[pallet::storage]
52    pub type WeightControlDelegation<T: Config> =
53        StorageMap<_, Identity, T::AccountId, T::AccountId>;
54
55    /// Percentage of issued tokens to be burned every epoch.
56    #[pallet::storage]
57    pub type EmissionRecyclingPercentage<T: Config> =
58        StorageValue<_, Percent, ValueQuery, T::DefaultEmissionRecyclingPercentage>;
59
60    /// Ratio between incentives and dividends on distribution. 50% means they
61    /// are distributed equally.
62    #[pallet::storage]
63    pub type IncentivesRatio<T: Config> =
64        StorageValue<_, Percent, ValueQuery, T::DefaultIncentivesRatio>;
65
66    /// Amount of tokens accumulated since the last epoch. This increases on
67    /// every block. See [`distribute::get_total_emission_per_block`].
68    #[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        /// Tokens emitted in an interval before halving the emissions in NANOs.
77        #[pallet::constant]
78        type HalvingInterval: Get<NonZeroU128>;
79
80        /// Max token supply in NANOs.
81        #[pallet::constant]
82        type MaxSupply: Get<NonZeroU128>;
83
84        /// Emissions per block in NANOs. Not taking into account halving and
85        /// recycling.
86        #[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        /// Agent tried setting more than 2 ^ 32 weights.
129        WeightSetTooLarge,
130
131        /// Tried setting weights for an agent that does not exist.
132        AgentIsNotRegistered,
133
134        /// Tried setting weights for itself.
135        CannotSetWeightsForSelf,
136
137        /// Tried setting weights while delegating weight control.
138        CannotSetWeightsWhileDelegating,
139
140        /// Tried delegating weight control to itself.
141        CannotDelegateWeightControlToSelf,
142
143        /// Tried regaining weight control without delegating it.
144        AgentIsNotDelegating,
145
146        /// Agent does not have enough stake to set weights.
147        NotEnoughStakeToSetWeights,
148
149        /// At the current state, agents cannot control their own weight.
150        WeightControlNotEnabled,
151    }
152
153    #[pallet::event]
154    #[pallet::generate_deposit(pub fn deposit_event)]
155    pub enum Event<T: Config> {
156        /// An agent set weights in the network.
157        WeightsSet(T::AccountId),
158        /// An agent gave weight control to the second agent.
159        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}